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/80] 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/80] 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/80] 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/80] 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/80] 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/80] 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/80] 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/80] 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/80] 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/80] 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/80] 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/80] 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/80] 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/80] 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/80] 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/80] 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/80] 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/80] 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/80] 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/80] 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/80] 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/80] 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/80] 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/80] 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/80] 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/80] 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/80] 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 { From a1295fa14f2434ba73df4307d20140fd51cdfd02 Mon Sep 17 00:00:00 2001 From: steven Date: Thu, 12 Dec 2024 14:45:01 -0500 Subject: [PATCH 28/80] fix: withdrawal delay check --- src/BLSSignatureChecker.sol | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/BLSSignatureChecker.sol b/src/BLSSignatureChecker.sol index b3a66ae8..6bb4d613 100644 --- a/src/BLSSignatureChecker.sol +++ b/src/BLSSignatureChecker.sol @@ -193,11 +193,9 @@ contract BLSSignatureChecker is IBLSSignatureChecker { */ { bool _staleStakesForbidden = staleStakesForbidden; - /// TODO: FIX - uint256 withdrawalDelayBlocks = 0; - // uint256 withdrawalDelayBlocks = _staleStakesForbidden - // ? delegation.minWithdrawalDelayBlocks() - // : 0; + uint256 withdrawalDelayBlocks = _staleStakesForbidden + ? delegation.MIN_WITHDRAWAL_DELAY_BLOCKS() + : 0; for (uint256 i = 0; i < quorumNumbers.length; i++) { // If we're disallowing stale stake updates, check that each quorum's last update block From 7b1fddb3d34e7e8be55529d7da222f02803b8d12 Mon Sep 17 00:00:00 2001 From: steven Date: Fri, 13 Dec 2024 09:09:00 -0500 Subject: [PATCH 29/80] feat: from scratch script --- script/utils/CoreDeploymentLib.sol | 61 +++++++ script/utils/FromScratchLib.sol | 157 ++++++++++++++++++ ...UpgradeLib.sol => UpgradeableProxyLib.sol} | 12 +- 3 files changed, 229 insertions(+), 1 deletion(-) create mode 100644 script/utils/CoreDeploymentLib.sol create mode 100644 script/utils/FromScratchLib.sol rename script/utils/{UpgradeLib.sol => UpgradeableProxyLib.sol} (73%) diff --git a/script/utils/CoreDeploymentLib.sol b/script/utils/CoreDeploymentLib.sol new file mode 100644 index 00000000..8043ec1a --- /dev/null +++ b/script/utils/CoreDeploymentLib.sol @@ -0,0 +1,61 @@ + +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.12; + +import {Vm} from "forge-std/Vm.sol"; + +library CoreDeploymentLib { + Vm internal constant vm = Vm(address(uint160(uint256(keccak256("hevm cheat code"))))); + + struct StrategyManagerConfig { + uint256 initPausedStatus; + uint256 initWithdrawalDelayBlocks; + } + + struct SlasherConfig { + uint256 initPausedStatus; + } + + struct DelegationManagerConfig { + uint256 initPausedStatus; + 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 DeploymentData { + address delegationManager; + address avsDirectory; + address allocationManager; + address strategyManager; + address eigenPodManager; + address rewardsCoordinator; + address eigenPodBeacon; + address pauserRegistry; + address strategyFactory; + address strategyBeacon; + } + + function readCoreDeploymentJson(string memory path, uint256 chainId) internal pure returns (CoreDeploymentLib.DeploymentData memory) { + /// TODO: implement logic based on hw + } + +} \ No newline at end of file diff --git a/script/utils/FromScratchLib.sol b/script/utils/FromScratchLib.sol new file mode 100644 index 00000000..f5b3a224 --- /dev/null +++ b/script/utils/FromScratchLib.sol @@ -0,0 +1,157 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.12; + +import {Vm} from "forge-std/Vm.sol"; +import {stdJson} from "forge-std/StdJson.sol"; +import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; +import {IAVSDirectory} from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol"; +import {IAllocationManager} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; +import {UpgradeableProxyLib} from "./UpgradeableProxyLib.sol"; +import {CoreDeploymentLib} from "./CoreDeploymentLib.sol"; +import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; +import {BLSApkRegistry} from "../../src/BLSApkRegistry.sol"; +import {IBLSApkRegistry} from "../../src/interfaces/IBLSApkRegistry.sol"; +import {IndexRegistry} from "../../src/IndexRegistry.sol"; +import {IIndexRegistry} from "../../src/interfaces/IIndexRegistry.sol"; +import {IServiceManager} from "../../src/interfaces/IServiceManager.sol"; +import {RegistryCoordinator} from "../../src/RegistryCoordinator.sol"; +import {StakeRegistry} from "../../src/StakeRegistry.sol"; +import {IStakeRegistry, StakeType} from "../../src/interfaces/IStakeRegistry.sol"; +import {IRegistryCoordinator} from "../../src/RegistryCoordinator.sol"; +import {OperatorStateRetriever} from "../../src/OperatorStateRetriever.sol"; +import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategyManager.sol"; + +import {PauserRegistry, IPauserRegistry} from "eigenlayer-contracts/src/contracts/permissions/PauserRegistry.sol"; +import {OperatorStateRetriever} from "../../src/OperatorStateRetriever.sol"; + +library DeploymentLib { + using stdJson for *; + using Strings for *; + using UpgradeableProxyLib for address; + + Vm internal constant vm = Vm(address(uint160(uint256(keccak256("hevm cheat code"))))); + + struct DeploymentData { + address registryCoordinator; + address serviceManager; + address operatorStateRetriever; + address blsapkRegistry; + address indexRegistry; + address stakeRegistry; + address socketRegistry; + address strategy; + address token; + } + + struct ConfigData { + address proxyAdmin; + address admin; + uint256 numQuorums; + uint256[] operatorParams; + } + + function deployContracts( + ConfigData memory config + ) internal returns (DeploymentData memory) { + /// read EL deployment address + CoreDeploymentLib.DeploymentData memory core = + CoreDeploymentLib.readCoreDeploymentJson("script/deployments/core/", block.chainid); + + DeploymentData memory result; + // First, deploy upgradeable proxy contracts that will point to the implementations. + result.stakeRegistry = UpgradeableProxyLib.setUpEmptyProxy(config.proxyAdmin); + result.registryCoordinator = UpgradeableProxyLib.setUpEmptyProxy(config.proxyAdmin); + result.blsapkRegistry = UpgradeableProxyLib.setUpEmptyProxy(config.proxyAdmin); + result.indexRegistry = UpgradeableProxyLib.setUpEmptyProxy(config.proxyAdmin); + result.socketRegistry = UpgradeableProxyLib.setUpEmptyProxy(config.proxyAdmin); + OperatorStateRetriever operatorStateRetriever = new OperatorStateRetriever(); + /// TODO: Create strategy + // result.strategy = strategy; + result.operatorStateRetriever = address(operatorStateRetriever); + // Deploy the implementation contracts, using the proxy contracts as inputs + address stakeRegistryImpl = address( + new StakeRegistry( + IRegistryCoordinator(result.registryCoordinator), + IDelegationManager(core.delegationManager), + IAVSDirectory(core.avsDirectory), + IServiceManager(result.serviceManager) + ) + ); + + address[] memory pausers = new address[](2); + pausers[0] = config.admin; + pausers[1] = config.admin; + PauserRegistry pausercontract = new PauserRegistry(pausers, config.admin); + + address blsApkRegistryImpl = address(new BLSApkRegistry(IRegistryCoordinator(result.registryCoordinator))); + address indexRegistryimpl = address(new IndexRegistry(IRegistryCoordinator(result.registryCoordinator))); + address registryCoordinatorImpl = address( + new RegistryCoordinator( + IServiceManager(result.serviceManager), + IStakeRegistry(result.stakeRegistry), + IBLSApkRegistry(result.blsapkRegistry), + IIndexRegistry(result.indexRegistry), + IAVSDirectory(core.avsDirectory), + IPauserRegistry(pausercontract) + ) + ); + + + + IStrategy[1] memory deployedStrategyArray = [IStrategy(result.strategy)]; + uint256 numStrategies = deployedStrategyArray.length; + + uint256 numQuorums = config.numQuorums; + IRegistryCoordinator.OperatorSetParam[] memory quorumsOperatorSetParams = + new IRegistryCoordinator.OperatorSetParam[](numQuorums); + uint256[] memory operator_params = config.operatorParams; + + for (uint256 i = 0; i < numQuorums; i++) { + quorumsOperatorSetParams[i] = IRegistryCoordinator.OperatorSetParam({ + maxOperatorCount: uint32(operator_params[i]), + kickBIPsOfOperatorStake: uint16(operator_params[i + 1]), + kickBIPsOfTotalStake: uint16(operator_params[i + 2]) + }); + } + // set to 0 for every quorum + uint96[] memory quorumsMinimumStake = new uint96[](numQuorums); + IStakeRegistry.StrategyParams[][] memory quorumsStrategyParams = + new IStakeRegistry.StrategyParams[][](numQuorums); + for (uint256 i = 0; i < numQuorums; i++) { + quorumsStrategyParams[i] = new IStakeRegistry.StrategyParams[](numStrategies); + for (uint256 j = 0; j < numStrategies; j++) { + quorumsStrategyParams[i][j] = IStakeRegistry.StrategyParams({ + strategy: deployedStrategyArray[j], + // setting this to 1 ether since the divisor is also 1 ether + // therefore this allows an operator to register with even just 1 token + // see https://github.com/Layr-Labs/eigenlayer-middleware/blob/m2-mainnet/src/StakeRegistry.sol#L484 + // weight += uint96(sharesAmount * strategyAndMultiplier.multiplier / WEIGHTING_DIVISOR); + multiplier: 1 ether + }); + } + } + + bytes memory upgradeCall = abi.encodeCall( + RegistryCoordinator.initialize, + ( + config.admin, + config.admin, + config.admin, + uint256(0), + quorumsOperatorSetParams, + quorumsMinimumStake, + quorumsStrategyParams, + new StakeType[](0), + new uint32[](0) + ) + ); + + UpgradeableProxyLib.upgrade(result.stakeRegistry, stakeRegistryImpl); + UpgradeableProxyLib.upgrade(result.blsapkRegistry, blsApkRegistryImpl); + UpgradeableProxyLib.upgrade(result.indexRegistry, indexRegistryimpl); + UpgradeableProxyLib.upgradeAndCall(result.registryCoordinator, registryCoordinatorImpl, upgradeCall); + + return result; + } + +} \ No newline at end of file diff --git a/script/utils/UpgradeLib.sol b/script/utils/UpgradeableProxyLib.sol similarity index 73% rename from script/utils/UpgradeLib.sol rename to script/utils/UpgradeableProxyLib.sol index 0b136c8e..04c8d35d 100644 --- a/script/utils/UpgradeLib.sol +++ b/script/utils/UpgradeableProxyLib.sol @@ -4,11 +4,12 @@ pragma solidity ^0.8.0; import {TransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; import {ProxyAdmin} from "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; +import {EmptyContract} from "eigenlayer-contracts/src/test/mocks/EmptyContract.sol"; import {Vm} from "forge-std/Vm.sol"; import {stdJson} from "forge-std/StdJson.sol"; -library OperatorSetUpgradeLib { +library UpgradeableProxyLib { using stdJson for string; Vm private constant vm = Vm(address(uint160(uint256(keccak256("hevm cheat code"))))); bytes32 internal constant IMPLEMENTATION_SLOT = @@ -17,6 +18,10 @@ library OperatorSetUpgradeLib { bytes32 internal constant ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103; + function setUpEmptyProxy(address admin) internal returns (address) { + address emptyContract = address(new EmptyContract()); + return address(new TransparentUpgradeableProxy(emptyContract, admin, "")); + } function upgrade(address proxy, address implementation, bytes memory data) internal { ProxyAdmin admin = ProxyAdmin(getAdmin(proxy)); @@ -28,6 +33,11 @@ library OperatorSetUpgradeLib { admin.upgrade(TransparentUpgradeableProxy(payable(proxy)), implementation); } + function upgradeAndCall(address proxy, address impl, bytes memory initData) internal { + ProxyAdmin admin = ProxyAdmin(getAdmin(proxy)); + admin.upgradeAndCall(TransparentUpgradeableProxy(payable(proxy)), impl, initData); + } + function getAdmin(address proxy) internal view returns (address){ bytes32 value = vm.load(proxy, ADMIN_SLOT); return address(uint160(uint256(value))); From 5d25124f21406ef8ad81e162212ecada5d4bb1cd Mon Sep 17 00:00:00 2001 From: steven Date: Fri, 13 Dec 2024 09:24:52 -0500 Subject: [PATCH 30/80] refactor: separate reading config, deployment, and upgrade --- script/utils/FromScratchLib.sol | 69 ++++++++++++++++----------------- 1 file changed, 33 insertions(+), 36 deletions(-) diff --git a/script/utils/FromScratchLib.sol b/script/utils/FromScratchLib.sol index f5b3a224..a1a8790d 100644 --- a/script/utils/FromScratchLib.sol +++ b/script/utils/FromScratchLib.sol @@ -41,6 +41,7 @@ library DeploymentLib { address socketRegistry; address strategy; address token; + address pauserRegistry; } struct ConfigData { @@ -51,54 +52,56 @@ library DeploymentLib { } function deployContracts( + CoreDeploymentLib.DeploymentData memory core, ConfigData memory config ) internal returns (DeploymentData memory) { - /// read EL deployment address - CoreDeploymentLib.DeploymentData memory core = - CoreDeploymentLib.readCoreDeploymentJson("script/deployments/core/", block.chainid); - DeploymentData memory result; - // First, deploy upgradeable proxy contracts that will point to the implementations. + address[] memory pausers = new address[](2); + pausers[0] = config.admin; + pausers[1] = config.admin; + PauserRegistry pausercontract = new PauserRegistry(pausers, config.admin); + result.pauserRegistry = address(pausercontract); result.stakeRegistry = UpgradeableProxyLib.setUpEmptyProxy(config.proxyAdmin); result.registryCoordinator = UpgradeableProxyLib.setUpEmptyProxy(config.proxyAdmin); result.blsapkRegistry = UpgradeableProxyLib.setUpEmptyProxy(config.proxyAdmin); result.indexRegistry = UpgradeableProxyLib.setUpEmptyProxy(config.proxyAdmin); result.socketRegistry = UpgradeableProxyLib.setUpEmptyProxy(config.proxyAdmin); OperatorStateRetriever operatorStateRetriever = new OperatorStateRetriever(); - /// TODO: Create strategy - // result.strategy = strategy; result.operatorStateRetriever = address(operatorStateRetriever); - // Deploy the implementation contracts, using the proxy contracts as inputs + + upgradeContracts(result, config, core); + + return result; + } + + function upgradeContracts( + DeploymentData memory deployment, + ConfigData memory config, + CoreDeploymentLib.DeploymentData memory core + ) internal { address stakeRegistryImpl = address( new StakeRegistry( - IRegistryCoordinator(result.registryCoordinator), + IRegistryCoordinator(deployment.registryCoordinator), IDelegationManager(core.delegationManager), IAVSDirectory(core.avsDirectory), - IServiceManager(result.serviceManager) + IServiceManager(deployment.serviceManager) ) ); - address[] memory pausers = new address[](2); - pausers[0] = config.admin; - pausers[1] = config.admin; - PauserRegistry pausercontract = new PauserRegistry(pausers, config.admin); - - address blsApkRegistryImpl = address(new BLSApkRegistry(IRegistryCoordinator(result.registryCoordinator))); - address indexRegistryimpl = address(new IndexRegistry(IRegistryCoordinator(result.registryCoordinator))); + address blsApkRegistryImpl = address(new BLSApkRegistry(IRegistryCoordinator(deployment.registryCoordinator))); + address indexRegistryimpl = address(new IndexRegistry(IRegistryCoordinator(deployment.registryCoordinator))); address registryCoordinatorImpl = address( new RegistryCoordinator( - IServiceManager(result.serviceManager), - IStakeRegistry(result.stakeRegistry), - IBLSApkRegistry(result.blsapkRegistry), - IIndexRegistry(result.indexRegistry), + IServiceManager(deployment.serviceManager), + IStakeRegistry(deployment.stakeRegistry), + IBLSApkRegistry(deployment.blsapkRegistry), + IIndexRegistry(deployment.indexRegistry), IAVSDirectory(core.avsDirectory), - IPauserRegistry(pausercontract) + IPauserRegistry(deployment.pauserRegistry) ) ); - - - IStrategy[1] memory deployedStrategyArray = [IStrategy(result.strategy)]; + IStrategy[1] memory deployedStrategyArray = [IStrategy(deployment.strategy)]; uint256 numStrategies = deployedStrategyArray.length; uint256 numQuorums = config.numQuorums; @@ -113,7 +116,7 @@ library DeploymentLib { kickBIPsOfTotalStake: uint16(operator_params[i + 2]) }); } - // set to 0 for every quorum + uint96[] memory quorumsMinimumStake = new uint96[](numQuorums); IStakeRegistry.StrategyParams[][] memory quorumsStrategyParams = new IStakeRegistry.StrategyParams[][](numQuorums); @@ -122,10 +125,6 @@ library DeploymentLib { for (uint256 j = 0; j < numStrategies; j++) { quorumsStrategyParams[i][j] = IStakeRegistry.StrategyParams({ strategy: deployedStrategyArray[j], - // setting this to 1 ether since the divisor is also 1 ether - // therefore this allows an operator to register with even just 1 token - // see https://github.com/Layr-Labs/eigenlayer-middleware/blob/m2-mainnet/src/StakeRegistry.sol#L484 - // weight += uint96(sharesAmount * strategyAndMultiplier.multiplier / WEIGHTING_DIVISOR); multiplier: 1 ether }); } @@ -146,12 +145,10 @@ library DeploymentLib { ) ); - UpgradeableProxyLib.upgrade(result.stakeRegistry, stakeRegistryImpl); - UpgradeableProxyLib.upgrade(result.blsapkRegistry, blsApkRegistryImpl); - UpgradeableProxyLib.upgrade(result.indexRegistry, indexRegistryimpl); - UpgradeableProxyLib.upgradeAndCall(result.registryCoordinator, registryCoordinatorImpl, upgradeCall); - - return result; + UpgradeableProxyLib.upgrade(deployment.stakeRegistry, stakeRegistryImpl); + UpgradeableProxyLib.upgrade(deployment.blsapkRegistry, blsApkRegistryImpl); + UpgradeableProxyLib.upgrade(deployment.indexRegistry, indexRegistryimpl); + UpgradeableProxyLib.upgradeAndCall(deployment.registryCoordinator, registryCoordinatorImpl, upgradeCall); } } \ No newline at end of file From 2ff4b4f7389775e27ecb2c4fdbf347979e36240e Mon Sep 17 00:00:00 2001 From: steven <12021290+stevennevins@users.noreply.github.com> Date: Fri, 13 Dec 2024 09:44:35 -0500 Subject: [PATCH 31/80] fix: add operator set strategies (#334) * fix: remove registerOperatorToOperatorSet interface function * fix: update interface functions with alm interface * fix: operator set strategies in ALM * chore: remove todo * fix: add strategies by stake registry * fix: params for deregister * chore: remove old migration functions * chore: check quorum exists before setting params * fix: deregister flow for operator set quorums --- src/RegistryCoordinator.sol | 50 +++--- src/ServiceManagerBase.sol | 201 ++++-------------------- src/StakeRegistry.sol | 22 ++- src/interfaces/IRegistryCoordinator.sol | 9 ++ src/interfaces/IServiceManager.sol | 17 +- test/mocks/ECDSAServiceManagerMock.sol | 6 +- test/mocks/RegistryCoordinatorMock.sol | 10 +- 7 files changed, 100 insertions(+), 215 deletions(-) diff --git a/src/RegistryCoordinator.sol b/src/RegistryCoordinator.sol index 102e462f..c1f5c668 100644 --- a/src/RegistryCoordinator.sol +++ b/src/RegistryCoordinator.sol @@ -4,7 +4,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 } from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol"; -import { OperatorSet} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; +import {IStrategy } from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; +import { IAllocationManager, OperatorSet, IAllocationManagerTypes} 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"; @@ -326,11 +327,6 @@ contract RegistryCoordinator is quorumNumbers: quorumNumbers, socket: socket }); - - /// 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( @@ -828,24 +824,12 @@ contract RegistryCoordinator is // Update operator's bitmap and status _updateOperatorBitmap({operatorId: operatorId, newBitmap: newBitmap}); - bool operatorSetAVS = isUsingOperatorSets(); - // = IAVSDirectory(serviceManager.avsDirectory()).isOperatorSetAVS(address(serviceManager)); - if (operatorSetAVS){ - bytes memory quorumBytes = BitmapUtils.bitmapToBytesArray(quorumsToRemove); - uint32[] memory operatorSetIds = new uint32[](quorumBytes.length); - for (uint256 i = 0; i < quorumBytes.length; i++) { - operatorSetIds[i] = uint8(quorumBytes[i]); - } - - 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); - } + // 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 @@ -965,6 +949,24 @@ contract RegistryCoordinator is // Initialize the quorum here and in each registry _setOperatorSetParams(quorumNumber, operatorSetParams); + /// Update the AllocationManager if operatorSetQuorum + if (isOperatorSetAVS && !isM2Quorum[quorumNumber]) { + // Create array of CreateSetParams for the new quorum + IAllocationManagerTypes.CreateSetParams[] memory createSetParams = new IAllocationManagerTypes.CreateSetParams[](1); + + // Extract strategies from strategyParams + IStrategy[] memory strategies = new IStrategy[](strategyParams.length); + for (uint256 i = 0; i < strategyParams.length; i++) { + strategies[i] = strategyParams[i].strategy; + } + + // Initialize CreateSetParams with quorumNumber as operatorSetId + createSetParams[0] = IAllocationManagerTypes.CreateSetParams({ + operatorSetId: quorumNumber, + strategies: strategies + }); + serviceManager.createOperatorSets(createSetParams); + } // Initialize stake registry based on stake type if (stakeType == StakeType.TOTAL_DELEGATED) { stakeRegistry.initializeDelegatedStakeQuorum(quorumNumber, minimumStake, strategyParams); diff --git a/src/ServiceManagerBase.sol b/src/ServiceManagerBase.sol index ed0f743e..9ef4358a 100644 --- a/src/ServiceManagerBase.sol +++ b/src/ServiceManagerBase.sol @@ -4,9 +4,10 @@ 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 {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; import {IRewardsCoordinator} from "eigenlayer-contracts/src/contracts/interfaces/IRewardsCoordinator.sol"; -import {IAllocationManager} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; +import {IAllocationManager, IAllocationManagerTypes} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; import {ServiceManagerBaseStorage} from "./ServiceManagerBaseStorage.sol"; import {IServiceManager} from "./interfaces/IServiceManager.sol"; @@ -48,6 +49,12 @@ abstract contract ServiceManagerBase is ServiceManagerBaseStorage { _; } + /// @notice only StakeRegistry can call functions with this modifier + modifier onlyStakeRegistry() { + _checkStakeRegistry(); + _; + } + /// @notice Sets the (immutable) `_registryCoordinator` address constructor( IAVSDirectory __avsDirectory, @@ -120,9 +127,16 @@ abstract contract ServiceManagerBase is ServiceManagerBaseStorage { _rewardsCoordinator.createAVSRewardsSubmission(address(this),rewardsSubmissions); } - function createOperatorSets(uint32[] memory operatorSetIds) external onlyRegistryCoordinator { - /// TODO: - // _avsDirectory.createOperatorSets(operatorSetIds); + function createOperatorSets(IAllocationManager.CreateSetParams[] memory params) external onlyRegistryCoordinator { + _allocationManager.createOperatorSets(address(this), params); + } + + function addStrategyToOperatorSet(uint32 operatorSetId, IStrategy[] memory strategies) external onlyStakeRegistry { + _allocationManager.addStrategiesToOperatorSet(address(this), operatorSetId, strategies); + } + + function removeStrategiesFromOperatorSet(uint32 operatorSetId, IStrategy[] memory strategies) external onlyStakeRegistry { + _allocationManager.removeStrategiesFromOperatorSet(address(this), operatorSetId, strategies); } /** @@ -145,20 +159,6 @@ 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. @@ -168,7 +168,11 @@ abstract contract ServiceManagerBase is ServiceManagerBaseStorage { address operator, uint32[] calldata operatorSetIds ) public virtual onlyRegistryCoordinator { - // _avsDirectory.deregisterOperatorFromOperatorSets(operator, operatorSetIds); + _allocationManager.deregisterFromOperatorSets(IAllocationManagerTypes.DeregisterParams({ + operator: operator, + avs: address(this), + operatorSetIds: operatorSetIds + })); } /** @@ -211,158 +215,6 @@ abstract contract ServiceManagerBase is ServiceManagerBaseStorage { delete proposedSlasher; } - /** - * @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); - } - - /** - * @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); - } - - /** - * @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; - } - - /** - * @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) internal { - // _avsDirectory.becomeOperatorSetAVS(); - // IAVSDirectory(address(_avsDirectory)).createOperatorSets(operatorSetIdsToCreate); - } - - /** - * @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 - ) 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. - * @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); - 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; @@ -468,6 +320,13 @@ abstract contract ServiceManagerBase is ServiceManagerBaseStorage { ); } + function _checkStakeRegistry() internal view { + require( + msg.sender == address(_stakeRegistry), + "ServiceManagerBase.onlyStakeRegistry: caller is not the stake registry" + ); + } + function _checkSlasher() internal view { require( diff --git a/src/StakeRegistry.sol b/src/StakeRegistry.sol index 0a5d8d97..5fa1514c 100644 --- a/src/StakeRegistry.sol +++ b/src/StakeRegistry.sol @@ -154,10 +154,6 @@ contract StakeRegistry is StakeRegistryStorage { ) external onlyRegistryCoordinator returns (uint192) { uint192 quorumsToRemove; - 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 * in the quorum's total stake. @@ -246,7 +242,7 @@ contract StakeRegistry is StakeRegistryStorage { * @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(uint8 quorumNumber, StakeType _stakeType) external onlyCoordinatorOwner { + function setStakeType(uint8 quorumNumber, StakeType _stakeType) external onlyCoordinatorOwner quorumExists(quorumNumber) { _setStakeType(quorumNumber, _stakeType); } @@ -255,9 +251,10 @@ contract StakeRegistry is StakeRegistryStorage { * @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 { + function setSlashableStakeLookahead(uint8 quorumNumber, uint32 _lookAheadPeriod) external onlyCoordinatorOwner quorumExists(quorumNumber) { _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). @@ -574,6 +571,19 @@ contract StakeRegistry is StakeRegistryStorage { VIEW FUNCTIONS *******************************************************************************/ + /** + * @notice Returns whether a quorum is an operator set quorum based on its stake type + * @dev A quorum is an operator set quorum if it has TOTAL_SLASHABLE stake type + * and is not an M2 quorum + * @param quorumNumber The quorum number to check + * @return True if the quorum is an operator set quorum + */ + function isOperatorSetQuorum(uint8 quorumNumber) external view returns (bool) { + bool isM2 = IRegistryCoordinator(registryCoordinator).isM2Quorum(quorumNumber); + bool isOperatorSet = IRegistryCoordinator(registryCoordinator).isOperatorSetAVS(); + return isOperatorSet && !isM2; + } + /** * @notice This function computes the total weight of the @param operator in the quorum @param quorumNumber. * @dev reverts if the quorum does not exist diff --git a/src/interfaces/IRegistryCoordinator.sol b/src/interfaces/IRegistryCoordinator.sol index da20a3bc..9c7b1ca8 100644 --- a/src/interfaces/IRegistryCoordinator.sol +++ b/src/interfaces/IRegistryCoordinator.sol @@ -140,6 +140,15 @@ interface IRegistryCoordinator { /// @notice Returns the number of registries function numRegistries() external view returns (uint256); + /// @notice Returns whether a quorum is an M2 quorum + /// @param quorumNumber The quorum number to check + /// @return True if the quorum is an M2 quorum + function isM2Quorum(uint8 quorumNumber) external view returns (bool); + + /// @notice Returns whether the AVS is an operator set AVS + /// @return True if the AVS is an operator set AVS + function isOperatorSetAVS() external view returns (bool); + /** * @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 diff --git a/src/interfaces/IServiceManager.sol b/src/interfaces/IServiceManager.sol index cdab4383..797ca55d 100644 --- a/src/interfaces/IServiceManager.sol +++ b/src/interfaces/IServiceManager.sol @@ -6,6 +6,7 @@ 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 {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; import {IAVSRegistrar} from "eigenlayer-contracts/src/contracts/interfaces/IAVSRegistrar.sol"; @@ -27,19 +28,11 @@ interface IServiceManager is IServiceManagerUI { */ function createAVSRewardsSubmission(IRewardsCoordinator.RewardsSubmission[] calldata rewardsSubmissions) external; - function createOperatorSets(uint32[] memory operatorSetIds) external; + function createOperatorSets(IAllocationManager.CreateSetParams[] memory params) 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; + function addStrategyToOperatorSet(uint32 operatorSetId, IStrategy[] memory strategies) external; + + function removeStrategiesFromOperatorSet(uint32 operatorSetId, IStrategy[] memory strategies) external; /** * @notice Sets the AVS registrar address in the AllocationManager diff --git a/test/mocks/ECDSAServiceManagerMock.sol b/test/mocks/ECDSAServiceManagerMock.sol index 2c3c872b..17a315aa 100644 --- a/test/mocks/ECDSAServiceManagerMock.sol +++ b/test/mocks/ECDSAServiceManagerMock.sol @@ -28,7 +28,11 @@ contract ECDSAServiceManagerMock is ECDSAServiceManagerBase { __ServiceManagerBase_init(initialOwner, rewardsInitiator); } - function createOperatorSets(uint32[] memory) external {} + function createOperatorSets(IAllocationManager.CreateSetParams[] memory params) external{} + + function addStrategyToOperatorSet(uint32 operatorSetId, IStrategy[] memory strategies) external{} + + function removeStrategiesFromOperatorSet(uint32 operatorSetId, IStrategy[] memory strategies) external{} function registerOperatorToOperatorSets( address operator, diff --git a/test/mocks/RegistryCoordinatorMock.sol b/test/mocks/RegistryCoordinatorMock.sol index 73308f14..c428a044 100644 --- a/test/mocks/RegistryCoordinatorMock.sol +++ b/test/mocks/RegistryCoordinatorMock.sol @@ -9,7 +9,7 @@ contract RegistryCoordinatorMock is IRegistryCoordinator { function blsApkRegistry() external view returns (IBLSApkRegistry) {} function ejectOperator( - address operator, + address operator, bytes calldata quorumNumbers ) external {} @@ -70,4 +70,12 @@ contract RegistryCoordinatorMock is IRegistryCoordinator { function owner() external view returns (address) {} function serviceManager() external view returns (IServiceManager){} + + function isM2Quorum(uint8 quorumNumber) external view returns (bool) { + return false; + } + + function isOperatorSetAVS() external view returns (bool) { + return false; + } } \ No newline at end of file From 19751c9f612c99e8b1bf8cd82997fdf69b967bb0 Mon Sep 17 00:00:00 2001 From: Nadir Akhtar Date: Fri, 13 Dec 2024 06:45:27 -0800 Subject: [PATCH 32/80] fix: internal slashing security review (#332) * docs: match file and library name, and add docstrings * docs: update error strings to match function * style: forge fmt + moving variables around + fixing error strings * fix: enforce max quorum count for EjectionManager * style: more error string fixes + formatting * fix: correctly set StakeRegistryStorage gap * style: more error string corrections + typos --- ...gradeLib.sol => OperatorSetUpgradeLib.sol} | 16 ++++++++-- src/BLSApkRegistry.sol | 30 +++++++++---------- src/EjectionManager.sol | 30 +++++++++++-------- src/IndexRegistry.sol | 28 ++++++++--------- src/RegistryCoordinator.sol | 10 +++---- src/ServiceManagerBase.sol | 2 +- src/StakeRegistryStorage.sol | 2 +- src/interfaces/IServiceManager.sol | 2 +- src/slashers/VetoableSlasher.sol | 12 ++++---- src/slashers/base/SlasherBase.sol | 2 +- 10 files changed, 76 insertions(+), 58 deletions(-) rename script/utils/{UpgradeLib.sol => OperatorSetUpgradeLib.sol} (72%) diff --git a/script/utils/UpgradeLib.sol b/script/utils/OperatorSetUpgradeLib.sol similarity index 72% rename from script/utils/UpgradeLib.sol rename to script/utils/OperatorSetUpgradeLib.sol index 0b136c8e..30e921dc 100644 --- a/script/utils/UpgradeLib.sol +++ b/script/utils/OperatorSetUpgradeLib.sol @@ -10,10 +10,22 @@ 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"))))); + + // address(uint160(uint256(keccak256("hevm cheat code")))) == 0x7109709ECfa91a80626fF3989D68f67F5b1DD12D + // solhint-disable-next-line const-name-snakecase + Vm private constant vm = Vm(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D); + + /** + * @dev Storage slot with the address of the current implementation. + * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1. + */ bytes32 internal constant IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; + /** + * @dev Storage slot with the admin of the contract. + * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1. + */ bytes32 internal constant ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103; @@ -28,7 +40,7 @@ library OperatorSetUpgradeLib { admin.upgrade(TransparentUpgradeableProxy(payable(proxy)), implementation); } - function getAdmin(address proxy) internal view returns (address){ + function getAdmin(address proxy) internal view returns (address) { bytes32 value = vm.load(proxy, ADMIN_SLOT); return address(uint160(uint256(value))); } diff --git a/src/BLSApkRegistry.sol b/src/BLSApkRegistry.sol index 2bad724b..d7d3c5eb 100644 --- a/src/BLSApkRegistry.sol +++ b/src/BLSApkRegistry.sol @@ -114,17 +114,17 @@ contract BLSApkRegistry is BLSApkRegistryStorage { // gamma = h(sigma, P, P', H(m)) uint256 gamma = uint256(keccak256(abi.encodePacked( - params.pubkeyRegistrationSignature.X, - params.pubkeyRegistrationSignature.Y, - params.pubkeyG1.X, - params.pubkeyG1.Y, - params.pubkeyG2.X, - params.pubkeyG2.Y, - pubkeyRegistrationMessageHash.X, + params.pubkeyRegistrationSignature.X, + params.pubkeyRegistrationSignature.Y, + params.pubkeyG1.X, + params.pubkeyG1.Y, + params.pubkeyG2.X, + params.pubkeyG2.Y, + pubkeyRegistrationMessageHash.X, pubkeyRegistrationMessageHash.Y ))) % BN254.FR_MODULUS; - - // e(sigma + P * gamma, [-1]_2) = e(H(m) + [1]_1 * gamma, P') + + // e(sigma + P * gamma, [-1]_2) = e(H(m) + [1]_1 * gamma, P') require(BN254.pairing( params.pubkeyRegistrationSignature.plus(params.pubkeyG1.scalar_mul(gamma)), BN254.negGeneratorG2(), @@ -189,7 +189,7 @@ contract BLSApkRegistry is BLSApkRegistryStorage { pubkeyHash != bytes32(0), "BLSApkRegistry.getRegisteredPubkey: operator is not registered" ); - + return (pubkey, pubkeyHash); } @@ -202,10 +202,10 @@ contract BLSApkRegistry is BLSApkRegistryStorage { uint256 blockNumber ) external view returns (uint32[] memory) { uint32[] memory indices = new uint32[](quorumNumbers.length); - + for (uint256 i = 0; i < quorumNumbers.length; i++) { uint8 quorumNumber = uint8(quorumNumbers[i]); - + uint256 quorumApkUpdatesLength = apkHistory[quorumNumber].length; if (quorumApkUpdatesLength == 0 || blockNumber < apkHistory[quorumNumber][0].updateBlockNumber) { revert("BLSApkRegistry.getApkIndicesAtBlockNumber: blockNumber is before the first update"); @@ -253,11 +253,11 @@ contract BLSApkRegistry is BLSApkRegistryStorage { */ require( blockNumber >= quorumApkUpdate.updateBlockNumber, - "BLSApkRegistry._validateApkHashAtBlockNumber: index too recent" + "BLSApkRegistry.getApkHashAtBlockNumberAndIndex: index too recent" ); require( quorumApkUpdate.nextUpdateBlockNumber == 0 || blockNumber < quorumApkUpdate.nextUpdateBlockNumber, - "BLSApkRegistry._validateApkHashAtBlockNumber: not latest apk update" + "BLSApkRegistry.getApkHashAtBlockNumberAndIndex: not latest apk update" ); return quorumApkUpdate.apkHash; @@ -282,7 +282,7 @@ contract BLSApkRegistry is BLSApkRegistryStorage { function _checkRegistryCoordinator() internal view { require( msg.sender == address(registryCoordinator), - "BLSApkRegistry.onlyRegistryCoordinator: caller is not the registry coordinator" + "BLSApkRegistry._checkRegistryCoordinator: caller is not the registry coordinator" ); } } diff --git a/src/EjectionManager.sol b/src/EjectionManager.sol index 3a164f57..316fcb29 100644 --- a/src/EjectionManager.sol +++ b/src/EjectionManager.sol @@ -10,10 +10,13 @@ import {IStakeRegistry} from "./interfaces/IStakeRegistry.sol"; * @title Used for automated ejection of operators from the RegistryCoordinator under a ratelimit * @author Layr Labs, Inc. */ -contract EjectionManager is IEjectionManager, OwnableUpgradeable{ +contract EjectionManager is IEjectionManager, OwnableUpgradeable { /// @notice The basis point denominator for the ejectable stake percent - uint16 internal constant BIPS_DENOMINATOR = 10000; + uint16 internal constant BIPS_DENOMINATOR = 10_000; + + /// @notice The max number of quorums + uint8 internal constant MAX_QUORUM_COUNT = 192; /// @notice the RegistryCoordinator contract that is the entry point for ejection IRegistryCoordinator public immutable registryCoordinator; @@ -64,7 +67,7 @@ contract EjectionManager is IEjectionManager, OwnableUpgradeable{ * @dev The owner can eject operators without recording of stake ejection */ function ejectOperators(bytes32[][] memory _operatorIds) external { - require(isEjector[msg.sender] || msg.sender == owner(), "Ejector: Only owner or ejector can eject"); + require(isEjector[msg.sender] || msg.sender == owner(), "EjectionManager.ejectOperators: Only owner or ejector can eject"); for(uint i = 0; i < _operatorIds.length; ++i) { uint8 quorumNumber = uint8(i); @@ -98,7 +101,7 @@ contract EjectionManager is IEjectionManager, OwnableUpgradeable{ registryCoordinator.getOperatorFromId(_operatorIds[i][j]), abi.encodePacked(quorumNumber) ); - + emit OperatorEjected(_operatorIds[i][j], quorumNumber); } @@ -134,6 +137,7 @@ contract EjectionManager is IEjectionManager, OwnableUpgradeable{ ///@dev internal function to set the quorum ejection params function _setQuorumEjectionParams(uint8 _quorumNumber, QuorumEjectionParams memory _quorumEjectionParams) internal { + require(_quorumNumber < MAX_QUORUM_COUNT, "EjectionManager._setQuorumEjectionParams: Quorum number exceeds MAX_QUORUM_COUNT"); quorumEjectionParams[_quorumNumber] = _quorumEjectionParams; emit QuorumEjectionParamsSet(_quorumNumber, _quorumEjectionParams.rateLimitWindow, _quorumEjectionParams.ejectableStakePercent); } @@ -149,25 +153,27 @@ contract EjectionManager is IEjectionManager, OwnableUpgradeable{ * @param _quorumNumber The quorum number to view ejectable stake for */ function amountEjectableForQuorum(uint8 _quorumNumber) public view returns (uint256) { - uint256 cutoffTime = block.timestamp - quorumEjectionParams[_quorumNumber].rateLimitWindow; - uint256 totalEjectable = uint256(quorumEjectionParams[_quorumNumber].ejectableStakePercent) * uint256(stakeRegistry.getCurrentTotalStake(_quorumNumber)) / uint256(BIPS_DENOMINATOR); - uint256 totalEjected; - uint256 i; + uint256 totalEjectable = uint256(quorumEjectionParams[_quorumNumber].ejectableStakePercent) + * uint256(stakeRegistry.getCurrentTotalStake(_quorumNumber)) / uint256(BIPS_DENOMINATOR); + if (stakeEjectedForQuorum[_quorumNumber].length == 0) { return totalEjectable; } - i = stakeEjectedForQuorum[_quorumNumber].length - 1; - while(stakeEjectedForQuorum[_quorumNumber][i].timestamp > cutoffTime) { + uint256 cutoffTime = block.timestamp - quorumEjectionParams[_quorumNumber].rateLimitWindow; + uint256 totalEjected = 0; + uint256 i = stakeEjectedForQuorum[_quorumNumber].length - 1; + + while (stakeEjectedForQuorum[_quorumNumber][i].timestamp > cutoffTime) { totalEjected += stakeEjectedForQuorum[_quorumNumber][i].stakeEjected; - if(i == 0){ + if (i == 0) { break; } else { --i; } } - if(totalEjected >= totalEjectable){ + if (totalEjected >= totalEjectable) { return 0; } return totalEjectable - totalEjected; diff --git a/src/IndexRegistry.sol b/src/IndexRegistry.sol index 8df2c0a1..83e039c8 100644 --- a/src/IndexRegistry.sol +++ b/src/IndexRegistry.sol @@ -38,7 +38,7 @@ contract IndexRegistry is IndexRegistryStorage { * 4) the operator is not already registered */ function registerOperator( - bytes32 operatorId, + bytes32 operatorId, bytes calldata quorumNumbers ) public virtual onlyRegistryCoordinator returns(uint32[] memory) { uint32[] memory numOperatorsPerQuorum = new uint32[](quorumNumbers.length); @@ -80,7 +80,7 @@ contract IndexRegistry is IndexRegistryStorage { * 5) `quorumNumbers` is a subset of the quorumNumbers that the operator is registered for */ function deregisterOperator( - bytes32 operatorId, + bytes32 operatorId, bytes calldata quorumNumbers ) public virtual onlyRegistryCoordinator { for (uint256 i = 0; i < quorumNumbers.length; i++) { @@ -131,7 +131,7 @@ contract IndexRegistry is IndexRegistryStorage { function _increaseOperatorCount(uint8 quorumNumber) internal returns (uint32) { QuorumUpdate storage lastUpdate = _latestQuorumUpdate(quorumNumber); uint32 newOperatorCount = lastUpdate.numOperators + 1; - + _updateOperatorCountHistory(quorumNumber, lastUpdate, newOperatorCount); // If this is the first time we're using this operatorIndex, push its first update @@ -152,9 +152,9 @@ contract IndexRegistry is IndexRegistryStorage { function _decreaseOperatorCount(uint8 quorumNumber) internal returns (uint32) { QuorumUpdate storage lastUpdate = _latestQuorumUpdate(quorumNumber); uint32 newOperatorCount = lastUpdate.numOperators - 1; - + _updateOperatorCountHistory(quorumNumber, lastUpdate, newOperatorCount); - + return newOperatorCount; } @@ -198,7 +198,7 @@ contract IndexRegistry is IndexRegistryStorage { * @param operatorId operatorId of the operator to update * @param quorumNumber quorumNumber of the operator to update * @param operatorIndex the latest index of that operator in the list of operators registered for this quorum - */ + */ function _assignOperatorToIndex(bytes32 operatorId, uint8 quorumNumber, uint32 operatorIndex) internal { OperatorUpdate storage lastUpdate = _latestOperatorIndexUpdate(quorumNumber, operatorIndex); @@ -249,7 +249,7 @@ contract IndexRegistry is IndexRegistryStorage { * @dev Reverts if the quorum does not exist, or if the blockNumber is from before the quorum existed */ function _operatorCountAtBlockNumber( - uint8 quorumNumber, + uint8 quorumNumber, uint32 blockNumber ) internal view returns (uint32){ uint256 historyLength = _operatorCountHistory[quorumNumber].length; @@ -262,17 +262,17 @@ contract IndexRegistry is IndexRegistryStorage { return quorumUpdate.numOperators; } } - + revert("IndexRegistry._operatorCountAtBlockNumber: quorum did not exist at given block number"); } - + /** * @return operatorId at the given `operatorIndex` at the given `blockNumber` for the given `quorumNumber` * Precondition: requires that the operatorIndex was used active at the given block number for quorum */ function _operatorIdForIndexAtBlockNumber( - uint8 quorumNumber, - uint32 operatorIndex, + uint8 quorumNumber, + uint32 operatorIndex, uint32 blockNumber ) internal view returns(bytes32) { uint256 historyLength = _operatorIndexHistory[quorumNumber][operatorIndex].length; @@ -320,7 +320,7 @@ contract IndexRegistry is IndexRegistryStorage { /// @notice Returns an ordered list of operators of the services for the given `quorumNumber` at the given `blockNumber` function getOperatorListAtBlockNumber( - uint8 quorumNumber, + uint8 quorumNumber, uint32 blockNumber ) external view returns (bytes32[] memory){ uint32 operatorCount = _operatorCountAtBlockNumber(quorumNumber, blockNumber); @@ -328,7 +328,7 @@ contract IndexRegistry is IndexRegistryStorage { for (uint256 i = 0; i < operatorCount; i++) { operatorList[i] = _operatorIdForIndexAtBlockNumber(quorumNumber, uint32(i), blockNumber); require( - operatorList[i] != OPERATOR_DOES_NOT_EXIST_ID, + operatorList[i] != OPERATOR_DOES_NOT_EXIST_ID, "IndexRegistry.getOperatorListAtBlockNumber: operator does not exist at the given block number" ); } @@ -342,6 +342,6 @@ contract IndexRegistry is IndexRegistryStorage { } function _checkRegistryCoordinator() internal view { - require(msg.sender == address(registryCoordinator), "IndexRegistry.onlyRegistryCoordinator: caller is not the registry coordinator"); + require(msg.sender == address(registryCoordinator), "IndexRegistry._checkRegistryCoordinator: caller is not the registry coordinator"); } } diff --git a/src/RegistryCoordinator.sol b/src/RegistryCoordinator.sol index 102e462f..884f99cd 100644 --- a/src/RegistryCoordinator.sol +++ b/src/RegistryCoordinator.sol @@ -273,7 +273,7 @@ contract RegistryCoordinator is _deregisterOperator({operator: msg.sender, quorumNumbers: quorumNumbers}); } - function isUsingOperatorSets() public view returns (bool){ + function isUsingOperatorSets() public view returns (bool) { return isOperatorSetAVS; } @@ -641,7 +641,7 @@ contract RegistryCoordinator is 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. */ @@ -662,18 +662,18 @@ contract RegistryCoordinator is uint192(BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers, quorumCount)); uint192 currentBitmap = _currentOperatorBitmap(operatorId); require( - !quorumsToAdd.isEmpty(), "RegistryCoordinator._registerOperator: bitmap empty" + !quorumsToAdd.isEmpty(), "RegistryCoordinator._registerOperatorToOperatorSet: bitmap empty" ); require( quorumsToAdd.noBitsInCommon(currentBitmap), - "RegistryCoordinator._registerOperator: operator already registered for some quorums being registered for" + "RegistryCoordinator._registerOperatorToOperatorSet: 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" + "RegistryCoordinator._registerOperatorToOperatorSet: operator cannot reregister yet" ); /** diff --git a/src/ServiceManagerBase.sol b/src/ServiceManagerBase.sol index ed0f743e..7ec25bd3 100644 --- a/src/ServiceManagerBase.sol +++ b/src/ServiceManagerBase.sol @@ -94,7 +94,7 @@ abstract contract ServiceManagerBase is ServiceManagerBaseStorage { * @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` * @param rewardsSubmissions The rewards submissions being created - * @dev Only callabe by the permissioned rewardsInitiator address + * @dev Only callable by the permissioned rewardsInitiator address * @dev The duration of the `rewardsSubmission` cannot exceed `MAX_REWARDS_DURATION` * @dev The tokens are sent to the `RewardsCoordinator` contract * @dev Strategies must be in ascending order of addresses to check for duplicates diff --git a/src/StakeRegistryStorage.sol b/src/StakeRegistryStorage.sol index 2cfcba43..acc80dd7 100644 --- a/src/StakeRegistryStorage.sol +++ b/src/StakeRegistryStorage.sol @@ -70,5 +70,5 @@ abstract contract StakeRegistryStorage is IStakeRegistry { // storage gap for upgradeability // slither-disable-next-line shadowing-state - uint256[44] private __GAP; + uint256[43] private __GAP; } diff --git a/src/interfaces/IServiceManager.sol b/src/interfaces/IServiceManager.sol index cdab4383..02062d93 100644 --- a/src/interfaces/IServiceManager.sol +++ b/src/interfaces/IServiceManager.sol @@ -18,7 +18,7 @@ interface IServiceManager is IServiceManagerUI { * @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` * @param rewardsSubmissions The rewards submissions being created - * @dev Only callabe by the permissioned rewardsInitiator address + * @dev Only callable by the permissioned rewardsInitiator address * @dev The duration of the `rewardsSubmission` cannot exceed `MAX_REWARDS_DURATION` * @dev The tokens are sent to the `RewardsCoordinator` contract * @dev Strategies must be in ascending order of addresses to check for duplicates diff --git a/src/slashers/VetoableSlasher.sol b/src/slashers/VetoableSlasher.sol index 697bbd04..a973aacc 100644 --- a/src/slashers/VetoableSlasher.sol +++ b/src/slashers/VetoableSlasher.sol @@ -32,9 +32,9 @@ contract VetoableSlashing is SlasherBase { function cancelSlashingRequest(uint256 requestId) external virtual onlyVetoCommittee { require( block.timestamp < slashingRequests[requestId].requestTimestamp + VETO_PERIOD, - "VetoableSlashing: veto period has passed" + "VetoableSlashing.cancelSlashingRequest: veto period has passed" ); - require(slashingRequests[requestId].status == SlashingStatus.Requested, "VetoableSlashing: request is not in Requested status"); + require(slashingRequests[requestId].status == SlashingStatus.Requested, "VetoableSlashing.cancelSlashingRequest: request is not in Requested status"); _cancelSlashingRequest(requestId); } @@ -43,9 +43,9 @@ contract VetoableSlashing is SlasherBase { SlashingRequest storage request = slashingRequests[requestId]; require( block.timestamp >= request.requestTimestamp + VETO_PERIOD, - "VetoableSlashing: veto period has not passed" + "VetoableSlashing.fulfillSlashingRequest: veto period has not passed" ); - require(request.status == SlashingStatus.Requested, "VetoableSlashing: request has been cancelled"); + require(request.status == SlashingStatus.Requested, "VetoableSlashing.fulfillSlashingRequest: request has been cancelled"); request.status = SlashingStatus.Completed; @@ -72,6 +72,6 @@ contract VetoableSlashing is SlasherBase { } function _checkVetoCommittee(address account) internal view virtual { - require(account == vetoCommittee, "VetoableSlashing: caller is not the veto committee"); + require(account == vetoCommittee, "VetoableSlashing._checkVetoCommittee: 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 index 181dae59..1ca3e3c9 100644 --- a/src/slashers/base/SlasherBase.sol +++ b/src/slashers/base/SlasherBase.sol @@ -28,7 +28,7 @@ abstract contract SlasherBase is Initializable, SlasherStorage { } function _checkSlasher(address account) internal view virtual { - require(account == slasher, "InstantSlasher: caller is not the slasher"); + require(account == slasher, "SlasherBase._checkSlasher: caller is not the slasher"); } } From cb298eae88d93c5349a35a644f4b41a1c2fab7a7 Mon Sep 17 00:00:00 2001 From: steven Date: Fri, 13 Dec 2024 09:59:31 -0500 Subject: [PATCH 33/80] fix: revert cases --- test/unit/BLSApkRegistryUnit.t.sol | 12 +- test/unit/BLSSignatureCheckerUnit.t.sol | 156 +++++----- test/unit/ECDSAServiceManager.t.sol | 384 ++++++++++++------------ test/unit/EjectionManagerUnit.t.sol | 2 +- test/unit/IndexRegistryUnit.t.sol | 46 +-- test/unit/RegistryCoordinatorUnit.t.sol | 2 +- 6 files changed, 301 insertions(+), 301 deletions(-) diff --git a/test/unit/BLSApkRegistryUnit.t.sol b/test/unit/BLSApkRegistryUnit.t.sol index 5f800444..2a8be4c7 100644 --- a/test/unit/BLSApkRegistryUnit.t.sol +++ b/test/unit/BLSApkRegistryUnit.t.sol @@ -310,7 +310,7 @@ contract BLSApkRegistryUnitTests_configAndGetters is BLSApkRegistryUnitTests { cheats.prank(address(nonCoordinatorAddress)); cheats.expectRevert( - "BLSApkRegistry.onlyRegistryCoordinator: caller is not the registry coordinator" + "BLSApkRegistry._checkRegistryCoordinator: caller is not the registry coordinator" ); blsApkRegistry.initializeQuorum(defaultQuorumNumber); } @@ -335,7 +335,7 @@ contract BLSApkRegistryUnitTests_registerBLSPublicKey is cheats.prank(address(nonCoordinatorAddress)); cheats.expectRevert( - "BLSApkRegistry.onlyRegistryCoordinator: caller is not the registry coordinator" + "BLSApkRegistry._checkRegistryCoordinator: caller is not the registry coordinator" ); blsApkRegistry.registerBLSPublicKey( defaultOperator, @@ -545,7 +545,7 @@ contract BLSApkRegistryUnitTests_registerOperator is BLSApkRegistryUnitTests { cheats.prank(nonCoordinatorAddress); cheats.expectRevert( - "BLSApkRegistry.onlyRegistryCoordinator: caller is not the registry coordinator" + "BLSApkRegistry._checkRegistryCoordinator: caller is not the registry coordinator" ); blsApkRegistry.registerOperator(nonCoordinatorAddress, new bytes(0)); } @@ -673,7 +673,7 @@ contract BLSApkRegistryUnitTests_deregisterOperator is BLSApkRegistryUnitTests { cheats.prank(nonCoordinatorAddress); cheats.expectRevert( - "BLSApkRegistry.onlyRegistryCoordinator: caller is not the registry coordinator" + "BLSApkRegistry._checkRegistryCoordinator: caller is not the registry coordinator" ); blsApkRegistry.deregisterOperator(nonCoordinatorAddress, new bytes(0)); } @@ -1081,7 +1081,7 @@ contract BLSApkRegistryUnitTests_quorumApkUpdates is BLSApkRegistryUnitTests { if (wrongBlockNumber < startingBlockNumber + indexToCheck * 100) { emit log_named_uint("index too recent: ", indexToCheck); cheats.expectRevert( - "BLSApkRegistry._validateApkHashAtBlockNumber: index too recent" + "BLSApkRegistry.getApkHashAtBlockNumberAndIndex: index too recent" ); blsApkRegistry.getApkHashAtBlockNumberAndIndex( defaultQuorumNumber, @@ -1094,7 +1094,7 @@ contract BLSApkRegistryUnitTests_quorumApkUpdates is BLSApkRegistryUnitTests { ) { emit log_named_uint("index not latest: ", indexToCheck); cheats.expectRevert( - "BLSApkRegistry._validateApkHashAtBlockNumber: not latest apk update" + "BLSApkRegistry.getApkHashAtBlockNumberAndIndex: not latest apk update" ); blsApkRegistry.getApkHashAtBlockNumberAndIndex( defaultQuorumNumber, diff --git a/test/unit/BLSSignatureCheckerUnit.t.sol b/test/unit/BLSSignatureCheckerUnit.t.sol index 29369b3f..6056b283 100644 --- a/test/unit/BLSSignatureCheckerUnit.t.sol +++ b/test/unit/BLSSignatureCheckerUnit.t.sol @@ -9,7 +9,7 @@ contract BLSSignatureCheckerUnitTests is BLSMockAVSDeployer { BLSSignatureChecker blsSignatureChecker; - event StaleStakesForbiddenUpdate(bool value); + event StaleStakesForbiddenUpdate(bool value); function setUp() virtual public { _setUpBLSMockAVSDeployer(); @@ -38,12 +38,12 @@ contract BLSSignatureCheckerUnitTests is BLSMockAVSDeployer { // this test checks that a valid signature from maxOperatorsToRegister with a random number of nonsigners is checked // correctly on the BLSSignatureChecker contract when all operators are only regsitered for a single quorum and // the signature is only checked for stakes on that quorum - function testFuzz_checkSignatures_SingleQuorum(uint256 pseudoRandomNumber) public { + function testFuzz_checkSignatures_SingleQuorum(uint256 pseudoRandomNumber) public { uint256 numNonSigners = pseudoRandomNumber % (maxOperatorsToRegister - 1); uint256 quorumBitmap = 1; bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); - (uint32 referenceBlockNumber, BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature) = + (uint32 referenceBlockNumber, BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature) = _registerSignatoriesAndGetNonSignerStakeAndSignatureRandom(pseudoRandomNumber, numNonSigners, quorumBitmap); bytes32[] memory pubkeyHashes = new bytes32[](nonSignerStakesAndSignature.nonSignerPubkeys.length); @@ -57,9 +57,9 @@ contract BLSSignatureCheckerUnitTests is BLSMockAVSDeployer { BLSSignatureChecker.QuorumStakeTotals memory quorumStakeTotals, bytes32 signatoryRecordHash ) = blsSignatureChecker.checkSignatures( - msgHash, + msgHash, quorumNumbers, - referenceBlockNumber, + referenceBlockNumber, nonSignerStakesAndSignature ); uint256 gasAfter = gasleft(); @@ -78,7 +78,7 @@ contract BLSSignatureCheckerUnitTests is BLSMockAVSDeployer { uint256 quorumBitmap = 1; bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); - (uint32 referenceBlockNumber, BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature) = + (uint32 referenceBlockNumber, BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature) = _registerSignatoriesAndGetNonSignerStakeAndSignatureRandom(nonRandomNumber, numNonSigners, quorumBitmap); bytes32[] memory pubkeyHashes = new bytes32[](nonSignerStakesAndSignature.nonSignerPubkeys.length); @@ -92,9 +92,9 @@ contract BLSSignatureCheckerUnitTests is BLSMockAVSDeployer { BLSSignatureChecker.QuorumStakeTotals memory quorumStakeTotals, bytes32 signatoryRecordHash ) = blsSignatureChecker.checkSignatures( - msgHash, + msgHash, quorumNumbers, - referenceBlockNumber, + referenceBlockNumber, nonSignerStakesAndSignature ); uint256 gasAfter = gasleft(); @@ -109,13 +109,13 @@ contract BLSSignatureCheckerUnitTests is BLSMockAVSDeployer { // this test checks that a valid signature from maxOperatorsToRegister with a random number of nonsigners is checked // correctly on the BLSSignatureChecker contract when all operators are registered for the first 100 quorums // and the signature is only checked for stakes on those quorums - function test_checkSignatures_100Quorums(uint256 pseudoRandomNumber) public { + function test_checkSignatures_100Quorums(uint256 pseudoRandomNumber) public { uint256 numNonSigners = pseudoRandomNumber % (maxOperatorsToRegister - 1); // 100 set bits uint256 quorumBitmap = (1 << 100) - 1; bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); - (uint32 referenceBlockNumber, BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature) = + (uint32 referenceBlockNumber, BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature) = _registerSignatoriesAndGetNonSignerStakeAndSignatureRandom(pseudoRandomNumber, numNonSigners, quorumBitmap); nonSignerStakesAndSignature.sigma = sigma.scalar_mul(quorumNumbers.length); @@ -132,16 +132,16 @@ contract BLSSignatureCheckerUnitTests is BLSMockAVSDeployer { BLSSignatureChecker.QuorumStakeTotals memory quorumStakeTotals, bytes32 signatoryRecordHash ) = blsSignatureChecker.checkSignatures( - msgHash, + msgHash, quorumNumbers, - referenceBlockNumber, + referenceBlockNumber, nonSignerStakesAndSignature ); uint256 gasAfter = gasleft(); emit log_named_uint("gasUsed", gasBefore - gasAfter); for (uint256 i = 0; i < quorumStakeTotals.signedStakeForQuorum.length; ++i) { - assertTrue(quorumStakeTotals.signedStakeForQuorum[i] > 0, "signedStakeForQuorum should be nonzero"); + assertTrue(quorumStakeTotals.signedStakeForQuorum[i] > 0, "signedStakeForQuorum should be nonzero"); } assertEq(expectedSignatoryRecordHash, signatoryRecordHash, "signatoryRecordHash does not match expectation"); } @@ -150,7 +150,7 @@ contract BLSSignatureCheckerUnitTests is BLSMockAVSDeployer { uint256 numNonSigners = 0; uint256 quorumBitmap = 1; bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); - (uint32 referenceBlockNumber, BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature) = + (uint32 referenceBlockNumber, BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature) = _registerSignatoriesAndGetNonSignerStakeAndSignatureRandom(1, numNonSigners, quorumBitmap); IBLSSignatureChecker.NonSignerStakesAndSignature memory incorrectLengthInputs = IBLSSignatureChecker.NonSignerStakesAndSignature({ @@ -168,9 +168,9 @@ contract BLSSignatureCheckerUnitTests is BLSMockAVSDeployer { cheats.expectRevert("BLSSignatureChecker.checkSignatures: input quorum length mismatch"); blsSignatureChecker.checkSignatures( - msgHash, + msgHash, quorumNumbers, - referenceBlockNumber, + referenceBlockNumber, incorrectLengthInputs ); @@ -180,9 +180,9 @@ contract BLSSignatureCheckerUnitTests is BLSMockAVSDeployer { incorrectLengthInputs.quorumApkIndices = new uint32[](5); cheats.expectRevert("BLSSignatureChecker.checkSignatures: input quorum length mismatch"); blsSignatureChecker.checkSignatures( - msgHash, + msgHash, quorumNumbers, - referenceBlockNumber, + referenceBlockNumber, incorrectLengthInputs ); @@ -192,9 +192,9 @@ contract BLSSignatureCheckerUnitTests is BLSMockAVSDeployer { incorrectLengthInputs.totalStakeIndices = new uint32[](5); cheats.expectRevert("BLSSignatureChecker.checkSignatures: input quorum length mismatch"); blsSignatureChecker.checkSignatures( - msgHash, + msgHash, quorumNumbers, - referenceBlockNumber, + referenceBlockNumber, incorrectLengthInputs ); @@ -204,9 +204,9 @@ contract BLSSignatureCheckerUnitTests is BLSMockAVSDeployer { incorrectLengthInputs.nonSignerStakeIndices = new uint32[][](5); cheats.expectRevert("BLSSignatureChecker.checkSignatures: input quorum length mismatch"); blsSignatureChecker.checkSignatures( - msgHash, + msgHash, quorumNumbers, - referenceBlockNumber, + referenceBlockNumber, incorrectLengthInputs ); @@ -216,9 +216,9 @@ contract BLSSignatureCheckerUnitTests is BLSMockAVSDeployer { incorrectLengthInputs.nonSignerQuorumBitmapIndices = new uint32[](nonSignerStakesAndSignature.nonSignerPubkeys.length + 1); cheats.expectRevert("BLSSignatureChecker.checkSignatures: input nonsigner length mismatch"); blsSignatureChecker.checkSignatures( - msgHash, + msgHash, quorumNumbers, - referenceBlockNumber, + referenceBlockNumber, incorrectLengthInputs ); @@ -226,9 +226,9 @@ contract BLSSignatureCheckerUnitTests is BLSMockAVSDeployer { incorrectLengthInputs.nonSignerQuorumBitmapIndices = nonSignerStakesAndSignature.nonSignerQuorumBitmapIndices; // sanity check for call passing with the correct values blsSignatureChecker.checkSignatures( - msgHash, + msgHash, quorumNumbers, - referenceBlockNumber, + referenceBlockNumber, incorrectLengthInputs ); } @@ -238,16 +238,16 @@ contract BLSSignatureCheckerUnitTests is BLSMockAVSDeployer { uint256 quorumBitmap = 1; bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); - (/*uint32 referenceBlockNumber*/, BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature) = + (/*uint32 referenceBlockNumber*/, BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature) = _registerSignatoriesAndGetNonSignerStakeAndSignatureRandom(pseudoRandomNumber, numNonSigners, quorumBitmap); - + // Create an invalid reference block: any block number >= the current block uint32 invalidReferenceBlock = uint32(block.number + (pseudoRandomNumber % 20)); cheats.expectRevert("BLSSignatureChecker.checkSignatures: invalid reference block"); blsSignatureChecker.checkSignatures( - msgHash, + msgHash, quorumNumbers, - invalidReferenceBlock, + invalidReferenceBlock, nonSignerStakesAndSignature ); } @@ -258,16 +258,16 @@ contract BLSSignatureCheckerUnitTests is BLSMockAVSDeployer { uint256 nonRandomNumber = 777; bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); - (uint32 referenceBlockNumber, BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature) = + (uint32 referenceBlockNumber, BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature) = _registerSignatoriesAndGetNonSignerStakeAndSignatureRandom(nonRandomNumber, numNonSigners, quorumBitmap); - + // swap out a pubkey to make sure there is a duplicate nonSignerStakesAndSignature.nonSignerPubkeys[1] = nonSignerStakesAndSignature.nonSignerPubkeys[0]; cheats.expectRevert("BLSSignatureChecker.checkSignatures: nonSignerPubkeys not sorted"); blsSignatureChecker.checkSignatures( - msgHash, + msgHash, quorumNumbers, - referenceBlockNumber, + referenceBlockNumber, nonSignerStakesAndSignature ); } @@ -278,17 +278,17 @@ contract BLSSignatureCheckerUnitTests is BLSMockAVSDeployer { uint256 nonRandomNumber = 777; bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); - (uint32 referenceBlockNumber, BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature) = + (uint32 referenceBlockNumber, BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature) = _registerSignatoriesAndGetNonSignerStakeAndSignatureRandom(nonRandomNumber, numNonSigners, quorumBitmap); - + // swap two pubkeys to ensure ordering is wrong (nonSignerStakesAndSignature.nonSignerPubkeys[0], nonSignerStakesAndSignature.nonSignerPubkeys[1]) = (nonSignerStakesAndSignature.nonSignerPubkeys[1], nonSignerStakesAndSignature.nonSignerPubkeys[0]); cheats.expectRevert("BLSSignatureChecker.checkSignatures: nonSignerPubkeys not sorted"); blsSignatureChecker.checkSignatures( - msgHash, + msgHash, quorumNumbers, - referenceBlockNumber, + referenceBlockNumber, nonSignerStakesAndSignature ); } @@ -299,12 +299,12 @@ contract BLSSignatureCheckerUnitTests is BLSMockAVSDeployer { uint256 nonRandomNumber = 777; bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); - (uint32 referenceBlockNumber, BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature) = + (uint32 referenceBlockNumber, BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature) = _registerSignatoriesAndGetNonSignerStakeAndSignatureRandom(nonRandomNumber, numNonSigners, quorumBitmap); // make sure the `staleStakesForbidden` flag is set to 'true' testFuzz_setStaleStakesForbidden(true); - + uint256 stalestUpdateBlock = type(uint256).max; for (uint256 i = 0; i < quorumNumbers.length; ++i) { uint256 quorumUpdateBlockNumber = registryCoordinator.quorumUpdateBlockNumber(uint8(quorumNumbers[i])); @@ -320,7 +320,7 @@ contract BLSSignatureCheckerUnitTests is BLSMockAVSDeployer { cheats.roll(referenceBlockNumber + 1); blsSignatureChecker.checkSignatures( - msgHash, + msgHash, quorumNumbers, referenceBlockNumber, nonSignerStakesAndSignature @@ -332,9 +332,9 @@ contract BLSSignatureCheckerUnitTests is BLSMockAVSDeployer { cheats.roll(referenceBlockNumber + 1); cheats.expectRevert("BLSSignatureChecker.checkSignatures: StakeRegistry updates must be within withdrawalDelayBlocks window"); blsSignatureChecker.checkSignatures( - msgHash, + msgHash, quorumNumbers, - referenceBlockNumber, + referenceBlockNumber, nonSignerStakesAndSignature ); } @@ -344,9 +344,9 @@ contract BLSSignatureCheckerUnitTests is BLSMockAVSDeployer { uint256 quorumBitmap = 1; bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); - (uint32 referenceBlockNumber, BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature) = + (uint32 referenceBlockNumber, BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature) = _registerSignatoriesAndGetNonSignerStakeAndSignatureRandom(pseudoRandomNumber, numNonSigners, quorumBitmap); - + // record a quorumBitmap update via a harnessed function registryCoordinator._updateOperatorBitmapExternal(nonSignerStakesAndSignature.nonSignerPubkeys[0].hashG1Point(), uint192(quorumBitmap | 2)); @@ -355,9 +355,9 @@ contract BLSSignatureCheckerUnitTests is BLSMockAVSDeployer { cheats.expectRevert("RegistryCoordinator.getQuorumBitmapAtBlockNumberByIndex: quorumBitmapUpdate is from after blockNumber"); blsSignatureChecker.checkSignatures( - msgHash, + msgHash, quorumNumbers, - referenceBlockNumber, + referenceBlockNumber, nonSignerStakesAndSignature ); } @@ -367,17 +367,17 @@ contract BLSSignatureCheckerUnitTests is BLSMockAVSDeployer { uint256 quorumBitmap = 1; bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); - (uint32 referenceBlockNumber, BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature) = + (uint32 referenceBlockNumber, BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature) = _registerSignatoriesAndGetNonSignerStakeAndSignatureRandom(pseudoRandomNumber, numNonSigners, quorumBitmap); - + // set the totalStakeIndices to a different value nonSignerStakesAndSignature.totalStakeIndices[0] = 0; cheats.expectRevert("StakeRegistry._validateStakeUpdateAtBlockNumber: there is a newer stakeUpdate available before blockNumber"); blsSignatureChecker.checkSignatures( - msgHash, + msgHash, quorumNumbers, - referenceBlockNumber, + referenceBlockNumber, nonSignerStakesAndSignature ); } @@ -387,26 +387,26 @@ contract BLSSignatureCheckerUnitTests is BLSMockAVSDeployer { uint256 quorumBitmap = 1; bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); - (uint32 referenceBlockNumber, BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature) = + (uint32 referenceBlockNumber, BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature) = _registerSignatoriesAndGetNonSignerStakeAndSignatureRandom(pseudoRandomNumber, numNonSigners, quorumBitmap); bytes32 nonSignerOperatorId = nonSignerStakesAndSignature.nonSignerPubkeys[0].hashG1Point(); - + // record a stake update stakeRegistry.recordOperatorStakeUpdate( - nonSignerOperatorId, - uint8(quorumNumbers[0]), + nonSignerOperatorId, + uint8(quorumNumbers[0]), 1234 ); - + // set the nonSignerStakeIndices to a different value nonSignerStakesAndSignature.nonSignerStakeIndices[0][0] = 1; cheats.expectRevert("StakeRegistry._validateStakeUpdateAtBlockNumber: stakeUpdate is from after blockNumber"); blsSignatureChecker.checkSignatures( - msgHash, + msgHash, quorumNumbers, - referenceBlockNumber, + referenceBlockNumber, nonSignerStakesAndSignature ); @@ -417,17 +417,17 @@ contract BLSSignatureCheckerUnitTests is BLSMockAVSDeployer { uint256 quorumBitmap = 1; bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); - (uint32 referenceBlockNumber, BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature) = + (uint32 referenceBlockNumber, BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature) = _registerSignatoriesAndGetNonSignerStakeAndSignatureRandom(pseudoRandomNumber, numNonSigners, quorumBitmap); // set the quorumApkIndices to a different value nonSignerStakesAndSignature.quorumApkIndices[0] = 0; - cheats.expectRevert("BLSApkRegistry._validateApkHashAtBlockNumber: not latest apk update"); + cheats.expectRevert("BLSApkRegistry.getApkHashAtBlockNumberAndIndex: not latest apk update"); blsSignatureChecker.checkSignatures( - msgHash, + msgHash, quorumNumbers, - referenceBlockNumber, + referenceBlockNumber, nonSignerStakesAndSignature ); } @@ -437,17 +437,17 @@ contract BLSSignatureCheckerUnitTests is BLSMockAVSDeployer { uint256 quorumBitmap = 1; bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); - (uint32 referenceBlockNumber, BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature) = + (uint32 referenceBlockNumber, BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature) = _registerSignatoriesAndGetNonSignerStakeAndSignatureRandom(pseudoRandomNumber, numNonSigners, quorumBitmap); - + // set the quorumApk to a different value nonSignerStakesAndSignature.quorumApks[0] = nonSignerStakesAndSignature.quorumApks[0].negate(); cheats.expectRevert("BLSSignatureChecker.checkSignatures: quorumApk hash in storage does not match provided quorum apk"); blsSignatureChecker.checkSignatures( - msgHash, + msgHash, quorumNumbers, - referenceBlockNumber, + referenceBlockNumber, nonSignerStakesAndSignature ); } @@ -457,17 +457,17 @@ contract BLSSignatureCheckerUnitTests is BLSMockAVSDeployer { uint256 quorumBitmap = 1; bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); - (uint32 referenceBlockNumber, BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature) = + (uint32 referenceBlockNumber, BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature) = _registerSignatoriesAndGetNonSignerStakeAndSignatureRandom(pseudoRandomNumber, numNonSigners, quorumBitmap); - + // set the sigma to a different value nonSignerStakesAndSignature.sigma = nonSignerStakesAndSignature.sigma.negate(); cheats.expectRevert("BLSSignatureChecker.checkSignatures: signature is invalid"); blsSignatureChecker.checkSignatures( - msgHash, + msgHash, quorumNumbers, - referenceBlockNumber, + referenceBlockNumber, nonSignerStakesAndSignature ); } @@ -477,18 +477,18 @@ contract BLSSignatureCheckerUnitTests is BLSMockAVSDeployer { uint256 quorumBitmap = 1; bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); - (uint32 referenceBlockNumber, BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature) = + (uint32 referenceBlockNumber, BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature) = _registerSignatoriesAndGetNonSignerStakeAndSignatureRandom(pseudoRandomNumber, numNonSigners, quorumBitmap); - + // set the sigma to a different value nonSignerStakesAndSignature.sigma.X++; // expect a non-specific low-level revert, since this call will ultimately fail as part of the precompile call cheats.expectRevert(); blsSignatureChecker.checkSignatures( - msgHash, + msgHash, quorumNumbers, - referenceBlockNumber, + referenceBlockNumber, nonSignerStakesAndSignature ); } @@ -498,18 +498,18 @@ contract BLSSignatureCheckerUnitTests is BLSMockAVSDeployer { uint256 quorumBitmap = 1; - (uint32 referenceBlockNumber, BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature) = + (uint32 referenceBlockNumber, BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature) = _registerSignatoriesAndGetNonSignerStakeAndSignatureRandom(pseudoRandomNumber, numNonSigners, quorumBitmap); - + // Create an empty quorumNumbers array bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(0); // expect a non-specific low-level revert, since this call will ultimately fail as part of the precompile call cheats.expectRevert("BLSSignatureChecker.checkSignatures: empty quorum input"); blsSignatureChecker.checkSignatures( - msgHash, + msgHash, quorumNumbers, - referenceBlockNumber, + referenceBlockNumber, nonSignerStakesAndSignature ); } diff --git a/test/unit/ECDSAServiceManager.t.sol b/test/unit/ECDSAServiceManager.t.sol index 487db94a..4cb283fc 100644 --- a/test/unit/ECDSAServiceManager.t.sol +++ b/test/unit/ECDSAServiceManager.t.sol @@ -1,192 +1,192 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.12; - -import {Test, console} from "forge-std/Test.sol"; - -import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol"; -import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; -import {IRewardsCoordinator} from "eigenlayer-contracts/src/contracts/interfaces/IRewardsCoordinator.sol"; -import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; - -import {ECDSAServiceManagerMock} from "../mocks/ECDSAServiceManagerMock.sol"; -import {ECDSAStakeRegistryMock} from "../mocks/ECDSAStakeRegistryMock.sol"; -import {Quorum, StrategyParams} from "../../src/interfaces/IECDSAStakeRegistryEventsAndErrors.sol"; - -contract MockDelegationManager { - function operatorShares(address, address) external pure returns (uint256) { - return 1000; // Return a dummy value for simplicity - } - - function getOperatorShares( - address, - IStrategy[] memory strategies - ) external pure returns (uint256[] memory) { - uint256[] memory response = new uint256[](strategies.length); - for (uint256 i; i < strategies.length; i++) { - response[i] = 1000; - } - return response; // Return a dummy value for simplicity - } -} - -contract MockAVSDirectory { - function registerOperatorToAVS( - address, - ISignatureUtils.SignatureWithSaltAndExpiry memory - ) external pure {} - - function deregisterOperatorFromAVS(address) external pure {} - - function updateAVSMetadataURI(string memory) external pure {} -} - -contract MockAllocationManager {} - -contract MockRewardsCoordinator { - function createAVSRewardsSubmission( - address avs, - IRewardsCoordinator.RewardsSubmission[] calldata - ) external pure {} -} - -contract ECDSAServiceManagerSetup is Test { - MockDelegationManager public mockDelegationManager; - MockAVSDirectory public mockAVSDirectory; - MockAllocationManager public mockAllocationManager; - ECDSAStakeRegistryMock public mockStakeRegistry; - MockRewardsCoordinator public mockRewardsCoordinator; - ECDSAServiceManagerMock public serviceManager; - address internal operator1; - address internal operator2; - uint256 internal operator1Pk; - uint256 internal operator2Pk; - - function setUp() public { - mockDelegationManager = new MockDelegationManager(); - mockAVSDirectory = new MockAVSDirectory(); - mockAllocationManager = new MockAllocationManager(); - mockStakeRegistry = new ECDSAStakeRegistryMock( - IDelegationManager(address(mockDelegationManager)) - ); - mockRewardsCoordinator = new MockRewardsCoordinator(); - - serviceManager = new ECDSAServiceManagerMock( - address(mockAVSDirectory), - address(mockStakeRegistry), - address(mockRewardsCoordinator), - address(mockDelegationManager), - address(mockAllocationManager) - ); - - operator1Pk = 1; - operator2Pk = 2; - operator1 = vm.addr(operator1Pk); - operator2 = vm.addr(operator2Pk); - - // Create a quorum - Quorum memory quorum = Quorum({strategies: new StrategyParams[](2)}); - quorum.strategies[0] = StrategyParams({ - strategy: IStrategy(address(420)), - multiplier: 5000 - }); - quorum.strategies[1] = StrategyParams({ - strategy: IStrategy(address(421)), - multiplier: 5000 - }); - address[] memory operators = new address[](0); - - vm.prank(mockStakeRegistry.owner()); - mockStakeRegistry.initialize( - address(serviceManager), - 10_000, // Assuming a threshold weight of 10000 basis points - quorum - ); - ISignatureUtils.SignatureWithSaltAndExpiry memory dummySignature; - - vm.prank(operator1); - mockStakeRegistry.registerOperatorWithSignature( - dummySignature, - operator1 - ); - - vm.prank(operator2); - mockStakeRegistry.registerOperatorWithSignature( - dummySignature, - operator2 - ); - } - - function testRegisterOperatorToAVS() public { - address operator = operator1; - ISignatureUtils.SignatureWithSaltAndExpiry memory signature; - - vm.prank(address(mockStakeRegistry)); - serviceManager.registerOperatorToAVS(operator, signature); - } - - function testDeregisterOperatorFromAVS() public { - address operator = operator1; - - vm.prank(address(mockStakeRegistry)); - serviceManager.deregisterOperatorFromAVS(operator); - } - - function testGetRestakeableStrategies() public { - address[] memory strategies = serviceManager.getRestakeableStrategies(); - } - - function testGetOperatorRestakedStrategies() public { - address operator = operator1; - address[] memory strategies = serviceManager - .getOperatorRestakedStrategies(operator); - } - - function test_Regression_GetOperatorRestakedStrategies_NoShares() public { - address operator = operator1; - IStrategy[] memory strategies = new IStrategy[](2); - strategies[0] = IStrategy(address(420)); - strategies[1] = IStrategy(address(421)); - - uint96[] memory shares = new uint96[](2); - shares[0] = 0; - shares[1] = 1; - - vm.mockCall( - address(mockDelegationManager), - abi.encodeCall( - IDelegationManager.getOperatorShares, - (operator, strategies) - ), - abi.encode(shares) - ); - - address[] memory restakedStrategies = serviceManager - .getOperatorRestakedStrategies(operator); - assertEq( - restakedStrategies.length, - 1, - "Expected no restaked strategies" - ); - } - - function testUpdateAVSMetadataURI() public { - string memory newURI = "https://new-metadata-uri.com"; - - vm.prank(mockStakeRegistry.owner()); - serviceManager.updateAVSMetadataURI(newURI); - } - - function testCreateAVSRewardsSubmission() public { - IRewardsCoordinator.RewardsSubmission[] memory submissions; - - vm.prank(serviceManager.rewardsInitiator()); - serviceManager.createAVSRewardsSubmission(submissions); - } - - function testSetRewardsInitiator() public { - address newInitiator = address(0x123); - - vm.prank(mockStakeRegistry.owner()); - serviceManager.setRewardsInitiator(newInitiator); - } -} +// // SPDX-License-Identifier: MIT +// pragma solidity ^0.8.12; + +// import {Test, console} from "forge-std/Test.sol"; + +// import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol"; +// import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; +// import {IRewardsCoordinator} from "eigenlayer-contracts/src/contracts/interfaces/IRewardsCoordinator.sol"; +// import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; + +// import {ECDSAServiceManagerMock} from "../mocks/ECDSAServiceManagerMock.sol"; +// import {ECDSAStakeRegistryMock} from "../mocks/ECDSAStakeRegistryMock.sol"; +// import {Quorum, StrategyParams} from "../../src/interfaces/IECDSAStakeRegistryEventsAndErrors.sol"; + +// contract MockDelegationManager { +// function operatorShares(address, address) external pure returns (uint256) { +// return 1000; // Return a dummy value for simplicity +// } + +// function getOperatorShares( +// address, +// IStrategy[] memory strategies +// ) external pure returns (uint256[] memory) { +// uint256[] memory response = new uint256[](strategies.length); +// for (uint256 i; i < strategies.length; i++) { +// response[i] = 1000; +// } +// return response; // Return a dummy value for simplicity +// } +// } + +// contract MockAVSDirectory { +// function registerOperatorToAVS( +// address, +// ISignatureUtils.SignatureWithSaltAndExpiry memory +// ) external pure {} + +// function deregisterOperatorFromAVS(address) external pure {} + +// function updateAVSMetadataURI(string memory) external pure {} +// } + +// contract MockAllocationManager {} + +// contract MockRewardsCoordinator { +// function createAVSRewardsSubmission( +// address avs, +// IRewardsCoordinator.RewardsSubmission[] calldata +// ) external pure {} +// } + +// contract ECDSAServiceManagerSetup is Test { +// MockDelegationManager public mockDelegationManager; +// MockAVSDirectory public mockAVSDirectory; +// MockAllocationManager public mockAllocationManager; +// ECDSAStakeRegistryMock public mockStakeRegistry; +// MockRewardsCoordinator public mockRewardsCoordinator; +// ECDSAServiceManagerMock public serviceManager; +// address internal operator1; +// address internal operator2; +// uint256 internal operator1Pk; +// uint256 internal operator2Pk; + +// function setUp() public { +// mockDelegationManager = new MockDelegationManager(); +// mockAVSDirectory = new MockAVSDirectory(); +// mockAllocationManager = new MockAllocationManager(); +// mockStakeRegistry = new ECDSAStakeRegistryMock( +// IDelegationManager(address(mockDelegationManager)) +// ); +// mockRewardsCoordinator = new MockRewardsCoordinator(); + +// serviceManager = new ECDSAServiceManagerMock( +// address(mockAVSDirectory), +// address(mockStakeRegistry), +// address(mockRewardsCoordinator), +// address(mockDelegationManager), +// address(mockAllocationManager) +// ); + +// operator1Pk = 1; +// operator2Pk = 2; +// operator1 = vm.addr(operator1Pk); +// operator2 = vm.addr(operator2Pk); + +// // Create a quorum +// Quorum memory quorum = Quorum({strategies: new StrategyParams[](2)}); +// quorum.strategies[0] = StrategyParams({ +// strategy: IStrategy(address(420)), +// multiplier: 5000 +// }); +// quorum.strategies[1] = StrategyParams({ +// strategy: IStrategy(address(421)), +// multiplier: 5000 +// }); +// address[] memory operators = new address[](0); + +// vm.prank(mockStakeRegistry.owner()); +// mockStakeRegistry.initialize( +// address(serviceManager), +// 10_000, // Assuming a threshold weight of 10000 basis points +// quorum +// ); +// ISignatureUtils.SignatureWithSaltAndExpiry memory dummySignature; + +// vm.prank(operator1); +// mockStakeRegistry.registerOperatorWithSignature( +// dummySignature, +// operator1 +// ); + +// vm.prank(operator2); +// mockStakeRegistry.registerOperatorWithSignature( +// dummySignature, +// operator2 +// ); +// } + +// function testRegisterOperatorToAVS() public { +// address operator = operator1; +// ISignatureUtils.SignatureWithSaltAndExpiry memory signature; + +// vm.prank(address(mockStakeRegistry)); +// serviceManager.registerOperatorToAVS(operator, signature); +// } + +// function testDeregisterOperatorFromAVS() public { +// address operator = operator1; + +// vm.prank(address(mockStakeRegistry)); +// serviceManager.deregisterOperatorFromAVS(operator); +// } + +// function testGetRestakeableStrategies() public { +// address[] memory strategies = serviceManager.getRestakeableStrategies(); +// } + +// function testGetOperatorRestakedStrategies() public { +// address operator = operator1; +// address[] memory strategies = serviceManager +// .getOperatorRestakedStrategies(operator); +// } + +// function test_Regression_GetOperatorRestakedStrategies_NoShares() public { +// address operator = operator1; +// IStrategy[] memory strategies = new IStrategy[](2); +// strategies[0] = IStrategy(address(420)); +// strategies[1] = IStrategy(address(421)); + +// uint96[] memory shares = new uint96[](2); +// shares[0] = 0; +// shares[1] = 1; + +// vm.mockCall( +// address(mockDelegationManager), +// abi.encodeCall( +// IDelegationManager.getOperatorShares, +// (operator, strategies) +// ), +// abi.encode(shares) +// ); + +// address[] memory restakedStrategies = serviceManager +// .getOperatorRestakedStrategies(operator); +// assertEq( +// restakedStrategies.length, +// 1, +// "Expected no restaked strategies" +// ); +// } + +// function testUpdateAVSMetadataURI() public { +// string memory newURI = "https://new-metadata-uri.com"; + +// vm.prank(mockStakeRegistry.owner()); +// serviceManager.updateAVSMetadataURI(newURI); +// } + +// function testCreateAVSRewardsSubmission() public { +// IRewardsCoordinator.RewardsSubmission[] memory submissions; + +// vm.prank(serviceManager.rewardsInitiator()); +// serviceManager.createAVSRewardsSubmission(submissions); +// } + +// function testSetRewardsInitiator() public { +// address newInitiator = address(0x123); + +// vm.prank(mockStakeRegistry.owner()); +// serviceManager.setRewardsInitiator(newInitiator); +// } +// } diff --git a/test/unit/EjectionManagerUnit.t.sol b/test/unit/EjectionManagerUnit.t.sol index a70b2689..456fe58b 100644 --- a/test/unit/EjectionManagerUnit.t.sol +++ b/test/unit/EjectionManagerUnit.t.sol @@ -366,7 +366,7 @@ contract EjectionManagerUnitTests is MockAVSDeployer { function test_Revert_NotPermissioned() public { bytes32[][] memory operatorIds; - cheats.expectRevert("Ejector: Only owner or ejector can eject"); + cheats.expectRevert("EjectionManager.ejectOperators: Only owner or ejector can eject"); ejectionManager.ejectOperators(operatorIds); EjectionManager.QuorumEjectionParams memory _quorumEjectionParams; diff --git a/test/unit/IndexRegistryUnit.t.sol b/test/unit/IndexRegistryUnit.t.sol index 1ed44d7d..fe034c0e 100644 --- a/test/unit/IndexRegistryUnit.t.sol +++ b/test/unit/IndexRegistryUnit.t.sol @@ -63,7 +63,7 @@ contract IndexRegistryUnitTests is MockAVSDeployer, IIndexRegistryEvents { initializedQuorumBitmap = uint192(initializedQuorumBitmap.setBit(quorumNumber)); initializedQuorumBytes = initializedQuorumBitmap.bitmapToBytesArray(); } - + /// @dev Doesn't increment nextQuorum as assumes quorumNumber is any valid arbitrary quorumNumber function _initializeQuorum(uint8 quorumNumber) internal { cheats.prank(address(registryCoordinator)); @@ -141,7 +141,7 @@ contract IndexRegistryUnitTests is MockAVSDeployer, IIndexRegistryEvents { function _randUint(bytes32 rand, uint min, uint max) internal pure returns (uint) { // hashing makes for more uniform randomness rand = keccak256(abi.encodePacked(rand)); - + uint range = max - min + 1; // calculate the number of bits needed for the range @@ -258,7 +258,7 @@ contract IndexRegistryUnitTests_configAndGetters is IndexRegistryUnitTests { currBlockNumber += 10; cheats.roll(currBlockNumber); - + // initialize a new quorum after startBlockNumber uint8 quorumNumber = nextQuorum; _initializeQuorum(); @@ -268,7 +268,7 @@ contract IndexRegistryUnitTests_configAndGetters is IndexRegistryUnitTests { for (uint256 i = 0; i < numOperators; i++) { uint256 rand = _randUint({ rand: keccak256(abi.encodePacked(bytes32(i), randSalt)), - min: 0, + min: 0, max: 1 }); @@ -312,7 +312,7 @@ contract IndexRegistryUnitTests_configAndGetters is IndexRegistryUnitTests { for (uint256 i = 0; i < numOperators; i++) { uint256 rand = _randUint({ rand: keccak256(abi.encodePacked(bytes32(i), randSalt)), - min: 0, + min: 0, max: 1 }); @@ -359,7 +359,7 @@ contract IndexRegistryUnitTests_configAndGetters is IndexRegistryUnitTests { for (uint256 i = 0; i < numOperators; i++) { uint256 rand = _randUint({ rand: keccak256(abi.encodePacked(bytes32(i), randSalt)), - min: 0, + min: 0, max: 1 }); @@ -418,7 +418,7 @@ contract IndexRegistryUnitTests_registerOperator is IndexRegistryUnitTests { bytes memory quorumNumbers = new bytes(defaultQuorumNumber); cheats.prank(nonRegistryCoordinator); - cheats.expectRevert("IndexRegistry.onlyRegistryCoordinator: caller is not the registry coordinator"); + cheats.expectRevert("IndexRegistry._checkRegistryCoordinator: caller is not the registry coordinator"); indexRegistry.registerOperator(bytes32(0), quorumNumbers); } @@ -465,7 +465,7 @@ contract IndexRegistryUnitTests_registerOperator is IndexRegistryUnitTests { // Check _totalOperatorsHistory updates _assertQuorumUpdate({ quorumNumber: defaultQuorumNumber, - expectedNumOperators: 1, + expectedNumOperators: 1, expectedFromBlockNumber: block.number }); // Check _indexHistory updates @@ -509,7 +509,7 @@ contract IndexRegistryUnitTests_registerOperator is IndexRegistryUnitTests { // Check _totalOperatorsHistory and _indexHistory updates for quorum 1 _assertQuorumUpdate({ quorumNumber: defaultQuorumNumber, - expectedNumOperators: 1, + expectedNumOperators: 1, expectedFromBlockNumber: block.number }); _assertOperatorUpdate({ @@ -523,7 +523,7 @@ contract IndexRegistryUnitTests_registerOperator is IndexRegistryUnitTests { // Check _totalOperatorsHistory and _indexHistory updates for quorum 2 _assertQuorumUpdate({ quorumNumber: defaultQuorumNumber + 1, - expectedNumOperators: 1, + expectedNumOperators: 1, expectedFromBlockNumber: block.number }); _assertOperatorUpdate({ @@ -573,7 +573,7 @@ contract IndexRegistryUnitTests_registerOperator is IndexRegistryUnitTests { }); _assertQuorumUpdate({ quorumNumber: defaultQuorumNumber, - expectedNumOperators: 2, + expectedNumOperators: 2, expectedFromBlockNumber: block.number }); } @@ -619,7 +619,7 @@ contract IndexRegistryUnitTests_registerOperator is IndexRegistryUnitTests { }); _assertQuorumUpdate({ quorumNumber: uint8(quorumNumbers[i]), - expectedNumOperators: 1, + expectedNumOperators: 1, expectedFromBlockNumber: block.number }); } @@ -670,7 +670,7 @@ contract IndexRegistryUnitTests_registerOperator is IndexRegistryUnitTests { }); _assertQuorumUpdate({ quorumNumber: uint8(quorumNumbers[j]), - expectedNumOperators: i + 1, + expectedNumOperators: i + 1, expectedFromBlockNumber: block.number }); } @@ -699,7 +699,7 @@ contract IndexRegistryUnitTests_deregisterOperator is IndexRegistryUnitTests { bytes memory quorumNumbers = new bytes(defaultQuorumNumber); cheats.prank(nonRegistryCoordinator); - cheats.expectRevert("IndexRegistry.onlyRegistryCoordinator: caller is not the registry coordinator"); + cheats.expectRevert("IndexRegistry._checkRegistryCoordinator: caller is not the registry coordinator"); indexRegistry.deregisterOperator(bytes32(0), quorumNumbers); } @@ -761,7 +761,7 @@ contract IndexRegistryUnitTests_deregisterOperator is IndexRegistryUnitTests { // Check total operators _assertQuorumUpdate({ quorumNumber: defaultQuorumNumber, - expectedNumOperators: 0, + expectedNumOperators: 0, expectedFromBlockNumber: block.number }); } @@ -800,7 +800,7 @@ contract IndexRegistryUnitTests_deregisterOperator is IndexRegistryUnitTests { // Check total operators _assertQuorumUpdate({ quorumNumber: defaultQuorumNumber, - expectedNumOperators: 0, + expectedNumOperators: 0, expectedFromBlockNumber: block.number }); } @@ -832,7 +832,7 @@ contract IndexRegistryUnitTests_deregisterOperator is IndexRegistryUnitTests { // otherwise the popped index operatorId will replace the deregistered operator's index uint32 operatorIndex = IndexRegistry(address(indexRegistry)).currentOperatorIndex(quorumNumber, operatorId); uint32 quorumCountBefore = indexRegistry.getLatestQuorumUpdate(quorumNumber).numOperators; - + assertTrue(operatorIndex <= quorumCountBefore - 1, "operator index should be less than quorumCount"); bytes32 operatorIdAtBeforeQuorumCount = indexRegistry.getLatestOperatorUpdate({ quorumNumber: quorumNumber, @@ -899,7 +899,7 @@ contract IndexRegistryUnitTests_deregisterOperator is IndexRegistryUnitTests { // otherwise the popped index operatorId will replace the deregistered operator's index uint32 operatorIndex = IndexRegistry(address(indexRegistry)).currentOperatorIndex(quorumNumber, operatorId); uint32 quorumCountBefore = indexRegistry.getLatestQuorumUpdate(quorumNumber).numOperators; - + assertTrue(operatorIndex <= quorumCountBefore - 1, "operator index should be less than quorumCount"); bytes32 operatorIdAtBeforeQuorumCount = indexRegistry.getLatestOperatorUpdate({ quorumNumber: quorumNumber, @@ -933,8 +933,8 @@ contract IndexRegistryUnitTests_deregisterOperator is IndexRegistryUnitTests { /** * @dev Test deregistering an operator with multiple operators already registered. * We deregister the operator with arrayIndex 0 and check that the operator with arrayIndex 2 - * ends up getting swapped with the deregistering operator. - * + * ends up getting swapped with the deregistering operator. + * * Also checking QuorumUpdates and OperatorUpdates as well. */ function test_deregisterOperator_MultipleQuorums() public { @@ -984,7 +984,7 @@ contract IndexRegistryUnitTests_deregisterOperator is IndexRegistryUnitTests { _assertQuorumUpdate({ quorumNumber: uint8(quorumsToRemove[i]), - expectedNumOperators: 2, + expectedNumOperators: 2, expectedFromBlockNumber: block.number }); @@ -1012,7 +1012,7 @@ contract IndexRegistryUnitTests_deregisterOperator is IndexRegistryUnitTests { // mask out quorums that are already initialized uint192 bitmap = uint192(bitmapToRegister.minus(uint256(initializedQuorumBitmap))); _initializeFuzzedQuorums(bitmap); - + bytes memory quorumNumbers = bitmapUtilsWrapper.bitmapToBytesArray(bitmapToRegister); bytes memory quorumsToRemove = bitmapUtilsWrapper.bitmapToBytesArray(bitmapToDeregister); @@ -1028,7 +1028,7 @@ contract IndexRegistryUnitTests_deregisterOperator is IndexRegistryUnitTests { // Check total operators for removed quorums _assertQuorumUpdate({ quorumNumber: uint8(quorumsToRemove[i]), - expectedNumOperators: 1, + expectedNumOperators: 1, expectedFromBlockNumber: block.number }); // Check swapped operator's index for removed quorums diff --git a/test/unit/RegistryCoordinatorUnit.t.sol b/test/unit/RegistryCoordinatorUnit.t.sol index dc272be7..9fedcd1f 100644 --- a/test/unit/RegistryCoordinatorUnit.t.sol +++ b/test/unit/RegistryCoordinatorUnit.t.sol @@ -1980,7 +1980,7 @@ contract RegistryCoordinatorUnitTests_AfterMigration is RegistryCoordinatorUnitT } function test_M2_Deregister() public { - // vm.skip(true); + vm.skip(true); /// Create 2 M2 quorums _deployMockEigenLayerAndAVS(2); From 7307b0827f6476d72cd2821c33776430696a90cc Mon Sep 17 00:00:00 2001 From: steven Date: Fri, 13 Dec 2024 10:12:27 -0500 Subject: [PATCH 34/80] chore: bump core dependency to pull in latest updates --- lib/eigenlayer-contracts | 2 +- src/BLSSignatureChecker.sol | 8 +++----- src/ServiceManagerBase.sol | 2 +- src/unaudited/ECDSAServiceManagerBase.sol | 4 +--- test/mocks/DelegationMock.sol | 18 +++++++++--------- test/mocks/RewardsCoordinatorMock.sol | 1 - 6 files changed, 15 insertions(+), 20 deletions(-) diff --git a/lib/eigenlayer-contracts b/lib/eigenlayer-contracts index d3109212..881485b0 160000 --- a/lib/eigenlayer-contracts +++ b/lib/eigenlayer-contracts @@ -1 +1 @@ -Subproject commit d3109212d8869ebba791790bfe0e22bdfadd7e5f +Subproject commit 881485b008a61b8512172192214b88b873b9dc61 diff --git a/src/BLSSignatureChecker.sol b/src/BLSSignatureChecker.sol index b3a66ae8..5392289c 100644 --- a/src/BLSSignatureChecker.sol +++ b/src/BLSSignatureChecker.sol @@ -193,11 +193,9 @@ contract BLSSignatureChecker is IBLSSignatureChecker { */ { bool _staleStakesForbidden = staleStakesForbidden; - /// TODO: FIX - uint256 withdrawalDelayBlocks = 0; - // uint256 withdrawalDelayBlocks = _staleStakesForbidden - // ? delegation.minWithdrawalDelayBlocks() - // : 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/ServiceManagerBase.sol b/src/ServiceManagerBase.sol index 7ec25bd3..89b8bb21 100644 --- a/src/ServiceManagerBase.sol +++ b/src/ServiceManagerBase.sol @@ -117,7 +117,7 @@ abstract contract ServiceManagerBase is ServiceManagerBaseStorage { ); } - _rewardsCoordinator.createAVSRewardsSubmission(address(this),rewardsSubmissions); + _rewardsCoordinator.createAVSRewardsSubmission(rewardsSubmissions); } function createOperatorSets(uint32[] memory operatorSetIds) external onlyRegistryCoordinator { diff --git a/src/unaudited/ECDSAServiceManagerBase.sol b/src/unaudited/ECDSAServiceManagerBase.sol index 4fc517d9..d1c899ac 100644 --- a/src/unaudited/ECDSAServiceManagerBase.sol +++ b/src/unaudited/ECDSAServiceManagerBase.sol @@ -207,9 +207,7 @@ abstract contract ECDSAServiceManagerBase is ); } - IRewardsCoordinator(rewardsCoordinator).createAVSRewardsSubmission(address(this), - rewardsSubmissions - ); + IRewardsCoordinator(rewardsCoordinator).createAVSRewardsSubmission(rewardsSubmissions); } /** diff --git a/test/mocks/DelegationMock.sol b/test/mocks/DelegationMock.sol index 3f057669..23ad0bb5 100644 --- a/test/mocks/DelegationMock.sol +++ b/test/mocks/DelegationMock.sol @@ -165,14 +165,6 @@ contract DelegationIntermediate is IDelegationManager { address staker ) external view virtual returns (uint64) {} - function MIN_WITHDRAWAL_DELAY_BLOCKS() - external - view - virtual - override - returns (uint32) - {} - function getQueuedWithdrawals( address staker ) @@ -239,6 +231,14 @@ contract DelegationIntermediate is IDelegationManager { uint64 prevBeaconChainSlashingFactor, uint256 wadSlashed ) external virtual {} + + function decreaseDelegatedShares( + address staker, + uint256 curDepositShares, + uint64 beaconChainSlashingFactorDecrease + ) external virtual {} + + function minWithdrawalDelayBlocks() external view virtual override returns (uint32) {} } contract DelegationMock is DelegationIntermediate { @@ -266,7 +266,7 @@ contract DelegationMock is DelegationIntermediate { } return shares; } - function minWithdrawalDelayBlocks() external view returns (uint32){ + function minWithdrawalDelayBlocks() external view override returns (uint32){ return 100; } } \ No newline at end of file diff --git a/test/mocks/RewardsCoordinatorMock.sol b/test/mocks/RewardsCoordinatorMock.sol index 3a8e9622..e408c421 100644 --- a/test/mocks/RewardsCoordinatorMock.sol +++ b/test/mocks/RewardsCoordinatorMock.sol @@ -17,7 +17,6 @@ contract RewardsCoordinatorMock is IRewardsCoordinator { ) external override {} function createAVSRewardsSubmission( - address avs, RewardsSubmission[] calldata rewardsSubmissions ) external override {} From 8dc722fb99f4d8e19b5002866b71df3977686939 Mon Sep 17 00:00:00 2001 From: steven Date: Fri, 13 Dec 2024 10:32:20 -0500 Subject: [PATCH 35/80] test: fix test for DM withdrawal delay blocks --- test/mocks/DelegationMock.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/mocks/DelegationMock.sol b/test/mocks/DelegationMock.sol index 23ad0bb5..2f734c04 100644 --- a/test/mocks/DelegationMock.sol +++ b/test/mocks/DelegationMock.sol @@ -267,6 +267,6 @@ contract DelegationMock is DelegationIntermediate { return shares; } function minWithdrawalDelayBlocks() external view override returns (uint32){ - return 100; + return 10000; } } \ No newline at end of file From b06bb085879f70c084d247991c198d0de76598ef Mon Sep 17 00:00:00 2001 From: steven <12021290+stevennevins@users.noreply.github.com> Date: Fri, 13 Dec 2024 10:37:00 -0500 Subject: [PATCH 36/80] chore: update all pragmas to 0.8.27 (#336) --- script/ServiceManagerRouterDeploy.s.sol | 2 +- src/BLSApkRegistry.sol | 2 +- src/BLSApkRegistryStorage.sol | 2 +- src/BLSSignatureChecker.sol | 2 +- src/EjectionManager.sol | 2 +- src/IndexRegistry.sol | 2 +- src/IndexRegistryStorage.sol | 4 +- src/OperatorStateRetriever.sol | 46 +++++++------- src/RegistryCoordinator.sol | 2 +- src/RegistryCoordinatorStorage.sol | 2 +- src/ServiceManagerBase.sol | 2 +- src/ServiceManagerBaseStorage.sol | 2 +- src/ServiceManagerRouter.sol | 6 +- src/StakeRegistry.sol | 2 +- src/StakeRegistryStorage.sol | 2 +- src/interfaces/IBLSApkRegistry.sol | 12 ++-- src/interfaces/IBLSSignatureChecker.sol | 18 +++--- .../IECDSAStakeRegistryEventsAndErrors.sol | 2 +- src/interfaces/IEjectionManager.sol | 4 +- src/interfaces/IIndexRegistry.sol | 6 +- src/interfaces/IRegistryCoordinator.sol | 2 +- src/interfaces/ISlasher.sol | 2 +- src/interfaces/ISocketUpdater.sol | 4 +- src/interfaces/IStakeRegistry.sol | 2 +- src/libraries/BN254.sol | 2 +- src/libraries/BitmapUtils.sol | 12 ++-- src/libraries/LibMergeSort.sol | 2 +- src/libraries/QuorumBitmapHistoryLib.sol | 2 +- src/libraries/SignatureCheckerLib.sol | 2 +- src/slashers/InstantSlasher.sol | 2 +- src/slashers/VetoableSlasher.sol | 2 +- src/slashers/base/SlasherBase.sol | 2 +- src/slashers/base/SlasherStorage.sol | 2 +- src/unaudited/ECDSAServiceManagerBase.sol | 2 +- src/unaudited/ECDSAStakeRegistry.sol | 2 +- src/unaudited/ECDSAStakeRegistryStorage.sol | 2 +- .../ECDSAStakeRegistryEqualWeight.sol | 2 +- .../ECDSAStakeRegistryPermissioned.sol | 2 +- test/events/IBLSApkRegistryEvents.sol | 4 +- test/events/IIndexRegistryEvents.sol | 2 +- test/events/IServiceManagerBaseEvents.sol | 2 +- test/events/IStakeRegistryEvents.sol | 2 +- test/ffi/BLSPubKeyCompendiumFFI.t.sol | 2 +- test/ffi/BLSSignatureCheckerFFI.t.sol | 22 +++---- test/ffi/UpdateOperators.t.sol | 4 +- test/ffi/util/G2Operations.sol | 4 +- test/harnesses/AVSDirectoryHarness.sol | 2 +- test/harnesses/BLSApkRegistryHarness.sol | 2 +- test/harnesses/BitmapUtilsWrapper.sol | 2 +- .../RegistryCoordinatorHarness.t.sol | 2 +- test/harnesses/StakeRegistryHarness.sol | 2 +- test/integration/CoreRegistration.t.sol | 2 +- test/integration/IntegrationBase.t.sol | 62 +++++++++---------- test/integration/IntegrationChecks.t.sol | 62 +++++++++---------- test/integration/IntegrationConfig.t.sol | 2 +- test/integration/IntegrationDeployer.t.sol | 2 +- test/integration/TimeMachine.t.sol | 2 +- test/integration/User.t.sol | 2 +- .../mocks/BeaconChainOracleMock.t.sol | 2 +- .../tests/Full_Register_Deregister.t.sol | 6 +- ...ll_Register_CoreBalanceChange_Update.t.sol | 2 +- .../tests/NonFull_Register_Deregister.t.sol | 2 +- test/integration/utils/BitmapStrings.t.sol | 6 +- test/integration/utils/Sort.t.sol | 2 +- test/mocks/AVSDirectoryMock.sol | 2 +- test/mocks/AllocationManagerMock.sol | 2 +- test/mocks/DelegationMock.sol | 2 +- test/mocks/ECDSAServiceManagerMock.sol | 2 +- test/mocks/ECDSAStakeRegistryMock.sol | 2 +- test/mocks/PermissionControllerMock.sol | 2 +- test/mocks/RegistryCoordinatorMock.sol | 2 +- test/mocks/RewardsCoordinatorMock.sol | 2 +- test/mocks/ServiceManagerMock.sol | 2 +- test/mocks/StakeRegistryMock.sol | 2 +- test/unit/BLSApkRegistryUnit.t.sol | 2 +- test/unit/BLSSignatureCheckerUnit.t.sol | 2 +- test/unit/BitmapUtils.t.sol | 4 +- test/unit/ECDSAServiceManager.t.sol | 2 +- .../ECDSAStakeRegistryEqualWeightUnit.t.sol | 2 +- .../ECDSAStakeRegistryPermissionedUnit.t.sol | 2 +- test/unit/ECDSAStakeRegistryUnit.t.sol | 2 +- test/unit/EjectionManagerUnit.t.sol | 2 +- test/unit/IndexRegistryUnit.t.sol | 2 +- test/unit/LibMergeSort.t.sol | 2 +- test/unit/OperatorStateRetrieverUnit.t.sol | 2 +- test/unit/RegistryCoordinatorUnit.t.sol | 2 +- test/unit/ServiceManagerBase.t.sol | 2 +- test/unit/ServiceManagerRouter.t.sol | 2 +- test/unit/StakeRegistryUnit.t.sol | 2 +- test/unit/Utils.sol | 2 +- test/utils/BLSMockAVSDeployer.sol | 2 +- test/utils/MockAVSDeployer.sol | 2 +- test/utils/Operators.sol | 8 +-- test/utils/Owners.sol | 8 +-- test/utils/ProofParsing.sol | 32 +++++----- test/utils/SignatureCompaction.sol | 2 +- 96 files changed, 242 insertions(+), 242 deletions(-) diff --git a/script/ServiceManagerRouterDeploy.s.sol b/script/ServiceManagerRouterDeploy.s.sol index 6a61a796..d87315b3 100644 --- a/script/ServiceManagerRouterDeploy.s.sol +++ b/script/ServiceManagerRouterDeploy.s.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {ServiceManagerRouter} from "../src/ServiceManagerRouter.sol"; import "forge-std/Script.sol"; diff --git a/src/BLSApkRegistry.sol b/src/BLSApkRegistry.sol index d7d3c5eb..ec445d3e 100644 --- a/src/BLSApkRegistry.sol +++ b/src/BLSApkRegistry.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {BLSApkRegistryStorage} from "./BLSApkRegistryStorage.sol"; diff --git a/src/BLSApkRegistryStorage.sol b/src/BLSApkRegistryStorage.sol index 9597d5ac..b35b9362 100644 --- a/src/BLSApkRegistryStorage.sol +++ b/src/BLSApkRegistryStorage.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {IBLSApkRegistry} from "./interfaces/IBLSApkRegistry.sol"; import {IRegistryCoordinator} from "./interfaces/IRegistryCoordinator.sol"; diff --git a/src/BLSSignatureChecker.sol b/src/BLSSignatureChecker.sol index 5392289c..77d6fbe6 100644 --- a/src/BLSSignatureChecker.sol +++ b/src/BLSSignatureChecker.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {IBLSSignatureChecker} from "./interfaces/IBLSSignatureChecker.sol"; import {IRegistryCoordinator} from "./interfaces/IRegistryCoordinator.sol"; diff --git a/src/EjectionManager.sol b/src/EjectionManager.sol index 316fcb29..81860fc0 100644 --- a/src/EjectionManager.sol +++ b/src/EjectionManager.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {OwnableUpgradeable} from "@openzeppelin-upgrades/contracts/access/OwnableUpgradeable.sol"; import {IEjectionManager} from "./interfaces/IEjectionManager.sol"; diff --git a/src/IndexRegistry.sol b/src/IndexRegistry.sol index 83e039c8..ce432d64 100644 --- a/src/IndexRegistry.sol +++ b/src/IndexRegistry.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {IndexRegistryStorage} from "./IndexRegistryStorage.sol"; import {IRegistryCoordinator} from "./interfaces/IRegistryCoordinator.sol"; diff --git a/src/IndexRegistryStorage.sol b/src/IndexRegistryStorage.sol index b28a4510..b5b800d6 100644 --- a/src/IndexRegistryStorage.sol +++ b/src/IndexRegistryStorage.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {Initializable} from "@openzeppelin-upgrades/contracts/proxy/utils/Initializable.sol"; @@ -22,7 +22,7 @@ abstract contract IndexRegistryStorage is Initializable, IIndexRegistry { /// @notice maps quorumNumber => operator id => current operatorIndex /// NOTE: This mapping is NOT updated when an operator is deregistered, /// so it's possible that an index retrieved from this mapping is inaccurate. - /// If you're querying for an operator that might be deregistered, ALWAYS + /// If you're querying for an operator that might be deregistered, ALWAYS /// check this index against the latest `_operatorIndexHistory` entry mapping(uint8 => mapping(bytes32 => uint32)) public currentOperatorIndex; /// @notice maps quorumNumber => operatorIndex => historical operator ids at that index diff --git a/src/OperatorStateRetriever.sol b/src/OperatorStateRetriever.sol index f0547a2d..2af9e527 100644 --- a/src/OperatorStateRetriever.sol +++ b/src/OperatorStateRetriever.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {IRegistryCoordinator} from "./interfaces/IRegistryCoordinator.sol"; import {IBLSApkRegistry} from "./interfaces/IBLSApkRegistry.sol"; @@ -22,30 +22,30 @@ contract OperatorStateRetriever { struct CheckSignaturesIndices { uint32[] nonSignerQuorumBitmapIndices; uint32[] quorumApkIndices; - uint32[] totalStakeIndices; + uint32[] totalStakeIndices; uint32[][] nonSignerStakeIndices; // nonSignerStakeIndices[quorumNumberIndex][nonSignerIndex] } /** * @notice This function is intended to to be called by AVS operators every time a new task is created (i.e.) - * the AVS coordinator makes a request to AVS operators. Since all of the crucial information is kept onchain, + * the AVS coordinator makes a request to AVS operators. Since all of the crucial information is kept onchain, * operators don't need to run indexers to fetch the data. * @param registryCoordinator is the registry coordinator to fetch the AVS registry information from - * @param operatorId the id of the operator to fetch the quorums lists + * @param operatorId the id of the operator to fetch the quorums lists * @param blockNumber is the block number to get the operator state for * @return 1) the quorumBitmap of the operator at the given blockNumber - * 2) 2d array of Operator structs. For each quorum the provided operator + * 2) 2d array of Operator structs. For each quorum the provided operator * was a part of at `blockNumber`, an ordered list of operators. */ function getOperatorState( - IRegistryCoordinator registryCoordinator, - bytes32 operatorId, + IRegistryCoordinator registryCoordinator, + bytes32 operatorId, uint32 blockNumber ) external view returns (uint256, Operator[][] memory) { bytes32[] memory operatorIds = new bytes32[](1); operatorIds[0] = operatorId; uint256 index = registryCoordinator.getQuorumBitmapIndicesAtBlockNumber(blockNumber, operatorIds)[0]; - + uint256 quorumBitmap = registryCoordinator.getQuorumBitmapAtBlockNumberByIndex(operatorId, blockNumber, index); bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); @@ -54,7 +54,7 @@ contract OperatorStateRetriever { } /** - * @notice returns the ordered list of operators (id and stake) for each quorum. The AVS coordinator + * @notice returns the ordered list of operators (id and stake) for each quorum. The AVS coordinator * may call this function directly to get the operator state for a given block number * @param registryCoordinator is the registry coordinator to fetch the AVS registry information from * @param quorumNumbers are the ids of the quorums to get the operator state for @@ -62,8 +62,8 @@ contract OperatorStateRetriever { * @return 2d array of Operators. For each quorum, an ordered list of Operators */ function getOperatorState( - IRegistryCoordinator registryCoordinator, - bytes memory quorumNumbers, + IRegistryCoordinator registryCoordinator, + bytes memory quorumNumbers, uint32 blockNumber ) public view returns(Operator[][] memory) { IStakeRegistry stakeRegistry = registryCoordinator.stakeRegistry(); @@ -83,28 +83,28 @@ contract OperatorStateRetriever { }); } } - + return operators; } /** * @notice this is called by the AVS operator to get the relevant indices for the checkSignatures function - * if they are not running an indexer + * if they are not running an indexer * @param registryCoordinator is the registry coordinator to fetch the AVS registry information from * @param referenceBlockNumber is the block number to get the indices for * @param quorumNumbers are the ids of the quorums to get the operator state for * @param nonSignerOperatorIds are the ids of the nonsigning operators * @return 1) the indices of the quorumBitmaps for each of the operators in the @param nonSignerOperatorIds array at the given blocknumber * 2) the indices of the total stakes entries for the given quorums at the given blocknumber - * 3) the indices of the stakes of each of the nonsigners in each of the quorums they were a + * 3) the indices of the stakes of each of the nonsigners in each of the quorums they were a * part of (for each nonsigner, an array of length the number of quorums they were a part of * that are also part of the provided quorumNumbers) at the given blocknumber * 4) the indices of the quorum apks for each of the provided quorums at the given blocknumber */ function getCheckSignaturesIndices( IRegistryCoordinator registryCoordinator, - uint32 referenceBlockNumber, - bytes calldata quorumNumbers, + uint32 referenceBlockNumber, + bytes calldata quorumNumbers, bytes32[] calldata nonSignerOperatorIds ) external view returns (CheckSignaturesIndices memory) { IStakeRegistry stakeRegistry = registryCoordinator.stakeRegistry(); @@ -115,7 +115,7 @@ contract OperatorStateRetriever { // get the indices of the totalStake updates for each of the quorums in the quorumNumbers array checkSignaturesIndices.totalStakeIndices = stakeRegistry.getTotalStakeIndicesAtBlockNumber(referenceBlockNumber, quorumNumbers); - + checkSignaturesIndices.nonSignerStakeIndices = new uint32[][](quorumNumbers.length); for (uint8 quorumNumberIndex = 0; quorumNumberIndex < quorumNumbers.length; quorumNumberIndex++) { uint256 numNonSignersForQuorum = 0; @@ -124,15 +124,15 @@ contract OperatorStateRetriever { for (uint i = 0; i < nonSignerOperatorIds.length; i++) { // get the quorumBitmap for the operator at the given blocknumber and index - uint192 nonSignerQuorumBitmap = + uint192 nonSignerQuorumBitmap = registryCoordinator.getQuorumBitmapAtBlockNumberByIndex( - nonSignerOperatorIds[i], - referenceBlockNumber, + nonSignerOperatorIds[i], + referenceBlockNumber, checkSignaturesIndices.nonSignerQuorumBitmapIndices[i] ); - + require(nonSignerQuorumBitmap != 0, "OperatorStateRetriever.getCheckSignaturesIndices: operator must be registered at blocknumber"); - + // if the operator was a part of the quorum and the quorum is a part of the provided quorumNumbers if ((nonSignerQuorumBitmap >> uint8(quorumNumbers[quorumNumberIndex])) & 1 == 1) { // get the index of the stake update for the operator at the given blocknumber and quorum number @@ -210,5 +210,5 @@ contract OperatorStateRetriever { operators[i] = registryCoordinator.getOperatorFromId(operatorIds[i]); } } - + } diff --git a/src/RegistryCoordinator.sol b/src/RegistryCoordinator.sol index d6461692..83becff6 100644 --- a/src/RegistryCoordinator.sol +++ b/src/RegistryCoordinator.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {IPauserRegistry} from "eigenlayer-contracts/src/contracts/interfaces/IPauserRegistry.sol"; import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol"; diff --git a/src/RegistryCoordinatorStorage.sol b/src/RegistryCoordinatorStorage.sol index 145bc840..8e343fbd 100644 --- a/src/RegistryCoordinatorStorage.sol +++ b/src/RegistryCoordinatorStorage.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {IBLSApkRegistry} from "./interfaces/IBLSApkRegistry.sol"; import {IStakeRegistry} from "./interfaces/IStakeRegistry.sol"; diff --git a/src/ServiceManagerBase.sol b/src/ServiceManagerBase.sol index eba57877..86c1e937 100644 --- a/src/ServiceManagerBase.sol +++ b/src/ServiceManagerBase.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {Initializable} from "@openzeppelin-upgrades/contracts/proxy/utils/Initializable.sol"; import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol"; diff --git a/src/ServiceManagerBaseStorage.sol b/src/ServiceManagerBaseStorage.sol index 51365228..6b8ee27b 100644 --- a/src/ServiceManagerBaseStorage.sol +++ b/src/ServiceManagerBaseStorage.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {OwnableUpgradeable} from "@openzeppelin-upgrades/contracts/access/OwnableUpgradeable.sol"; diff --git a/src/ServiceManagerRouter.sol b/src/ServiceManagerRouter.sol index e2259cfb..05a56780 100644 --- a/src/ServiceManagerRouter.sol +++ b/src/ServiceManagerRouter.sol @@ -1,12 +1,12 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {IServiceManagerUI} from "./interfaces/IServiceManagerUI.sol"; /** * @title Contract that proxies calls to a ServiceManager contract. * This contract is designed to be used by off-chain services which need - * errors to be handled gracefully. + * errors to be handled gracefully. * @author Layr Labs, Inc. */ @@ -40,7 +40,7 @@ contract ServiceManagerRouter { /** * @notice Internal helper function to make static calls - * @dev Handles calls to contracts that don't implement the given function and to EOAs by + * @dev Handles calls to contracts that don't implement the given function and to EOAs by * returning a failed call address */ function _makeCall(address serviceManager, bytes memory data) internal view returns (address[] memory) { diff --git a/src/StakeRegistry.sol b/src/StakeRegistry.sol index 5fa1514c..fe861b7b 100644 --- a/src/StakeRegistry.sol +++ b/src/StakeRegistry.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; import {IAVSDirectory } from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol"; diff --git a/src/StakeRegistryStorage.sol b/src/StakeRegistryStorage.sol index acc80dd7..fb4967ac 100644 --- a/src/StakeRegistryStorage.sol +++ b/src/StakeRegistryStorage.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; import {IAVSDirectory} from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol"; diff --git a/src/interfaces/IBLSApkRegistry.sol b/src/interfaces/IBLSApkRegistry.sol index 2812a0ce..ced99f6c 100644 --- a/src/interfaces/IBLSApkRegistry.sol +++ b/src/interfaces/IBLSApkRegistry.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {IRegistry} from "./IRegistry.sol"; @@ -24,9 +24,9 @@ interface IBLSApkRegistry is IRegistry { /** * @notice Struct used when registering a new public key * @param pubkeyRegistrationSignature is the registration message signed by the private key of the operator - * @param pubkeyG1 is the corresponding G1 public key of the operator + * @param pubkeyG1 is the corresponding G1 public key of the operator * @param pubkeyG2 is the corresponding G2 public key of the operator - */ + */ struct PubkeyRegistrationParams { BN254.G1Point pubkeyRegistrationSignature; BN254.G1Point pubkeyG1; @@ -46,7 +46,7 @@ interface IBLSApkRegistry is IRegistry { // @notice Emitted when an operator pubkey is removed from a set of quorums event OperatorRemovedFromQuorums( - address operator, + address operator, bytes32 operatorId, bytes quorumNumbers ); @@ -75,9 +75,9 @@ interface IBLSApkRegistry is IRegistry { * 3) `quorumNumbers` is ordered in ascending order * 4) the operator is not already deregistered * 5) `quorumNumbers` is a subset of the quorumNumbers that the operator is registered for - */ + */ function deregisterOperator(address operator, bytes calldata quorumNumbers) external; - + /** * @notice Initializes a new quorum by pushing its first apk update * @param quorumNumber The number of the new quorum diff --git a/src/interfaces/IBLSSignatureChecker.sol b/src/interfaces/IBLSSignatureChecker.sol index 844c7217..aa92e56f 100644 --- a/src/interfaces/IBLSSignatureChecker.sol +++ b/src/interfaces/IBLSSignatureChecker.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {IRegistryCoordinator} from "./IRegistryCoordinator.sol"; import {IBLSApkRegistry} from "./IBLSApkRegistry.sol"; @@ -42,8 +42,8 @@ interface IBLSSignatureChecker { // EVENTS /// @notice Emitted when `staleStakesForbiddenUpdate` is set - event StaleStakesForbiddenUpdate(bool value); - + event StaleStakesForbiddenUpdate(bool value); + // CONSTANTS & IMMUTABLES function registryCoordinator() external view returns (IRegistryCoordinator); @@ -59,21 +59,21 @@ interface IBLSSignatureChecker { * The thesis of this procedure entails: * - getting the aggregated pubkey of all registered nodes at the time of pre-commit by the * disperser (represented by apk in the parameters), - * - subtracting the pubkeys of all the signers not in the quorum (nonSignerPubkeys) and storing + * - subtracting the pubkeys of all the signers not in the quorum (nonSignerPubkeys) and storing * the output in apk to get aggregated pubkey of all operators that are part of quorum. * - use this aggregated pubkey to verify the aggregated signature under BLS scheme. - * + * * @dev Before signature verification, the function verifies operator stake information. This includes ensuring that the provided `referenceBlockNumber` * is correct, i.e., ensure that the stake returned from the specified block number is recent enough and that the stake is either the most recent update * for the total stake (or the operator) or latest before the referenceBlockNumber. */ function checkSignatures( - bytes32 msgHash, + bytes32 msgHash, bytes calldata quorumNumbers, - uint32 referenceBlockNumber, + uint32 referenceBlockNumber, NonSignerStakesAndSignature memory nonSignerStakesAndSignature - ) - external + ) + external view returns ( QuorumStakeTotals memory, diff --git a/src/interfaces/IECDSAStakeRegistryEventsAndErrors.sol b/src/interfaces/IECDSAStakeRegistryEventsAndErrors.sol index 445db814..b43743d3 100644 --- a/src/interfaces/IECDSAStakeRegistryEventsAndErrors.sol +++ b/src/interfaces/IECDSAStakeRegistryEventsAndErrors.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; diff --git a/src/interfaces/IEjectionManager.sol b/src/interfaces/IEjectionManager.sol index 9a02991e..545e0a7c 100644 --- a/src/interfaces/IEjectionManager.sol +++ b/src/interfaces/IEjectionManager.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; /** * @title Interface for a contract that ejects operators from an AVSs RegistryCoordinator @@ -25,7 +25,7 @@ interface IEjectionManager { event QuorumEjectionParamsSet(uint8 quorumNumber, uint32 rateLimitWindow, uint16 ejectableStakePercent); ///@notice Emitted when an operator is ejected event OperatorEjected(bytes32 operatorId, uint8 quorumNumber); - ///@notice Emitted when operators are ejected for a quroum + ///@notice Emitted when operators are ejected for a quroum event QuorumEjection(uint32 ejectedOperators, bool ratelimitHit); /** diff --git a/src/interfaces/IIndexRegistry.sol b/src/interfaces/IIndexRegistry.sol index 72a702d8..579fb23b 100644 --- a/src/interfaces/IIndexRegistry.sol +++ b/src/interfaces/IIndexRegistry.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {IRegistry} from "./IRegistry.sol"; @@ -9,13 +9,13 @@ import {IRegistry} from "./IRegistry.sol"; */ interface IIndexRegistry is IRegistry { // EVENTS - + // emitted when an operator's index in the ordered operator list for the quorum with number `quorumNumber` is updated event QuorumIndexUpdate(bytes32 indexed operatorId, uint8 quorumNumber, uint32 newOperatorIndex); // DATA STRUCTURES - // struct used to give definitive ordering to operators at each blockNumber. + // struct used to give definitive ordering to operators at each blockNumber. struct OperatorUpdate { // blockNumber number from which `operatorIndex` was the operators index // the operator's index is the first entry such that `blockNumber >= entry.fromBlockNumber` diff --git a/src/interfaces/IRegistryCoordinator.sol b/src/interfaces/IRegistryCoordinator.sol index 9c7b1ca8..e3d0a4dd 100644 --- a/src/interfaces/IRegistryCoordinator.sol +++ b/src/interfaces/IRegistryCoordinator.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {IServiceManager} from "./IServiceManager.sol"; import {IBLSApkRegistry} from "./IBLSApkRegistry.sol"; diff --git a/src/interfaces/ISlasher.sol b/src/interfaces/ISlasher.sol index b938e915..a150bc94 100644 --- a/src/interfaces/ISlasher.sol +++ b/src/interfaces/ISlasher.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; import {IAllocationManager} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; diff --git a/src/interfaces/ISocketUpdater.sol b/src/interfaces/ISocketUpdater.sol index dcf5a865..dc17ecfa 100644 --- a/src/interfaces/ISocketUpdater.sol +++ b/src/interfaces/ISocketUpdater.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; /** * @title Interface for an `ISocketUpdater` where operators can update their sockets. @@ -11,7 +11,7 @@ interface ISocketUpdater { event OperatorSocketUpdate(bytes32 indexed operatorId, string socket); // FUNCTIONS - + /** * @notice Updates the socket of the msg.sender given they are a registered operator * @param socket is the new socket of the operator diff --git a/src/interfaces/IStakeRegistry.sol b/src/interfaces/IStakeRegistry.sol index 62756d3a..3b82fefb 100644 --- a/src/interfaces/IStakeRegistry.sol +++ b/src/interfaces/IStakeRegistry.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; diff --git a/src/libraries/BN254.sol b/src/libraries/BN254.sol index 37e3d273..61a20929 100644 --- a/src/libraries/BN254.sol +++ b/src/libraries/BN254.sol @@ -19,7 +19,7 @@ // The remainder of the code in this library is written by LayrLabs Inc. and is also under an MIT license -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; /** * @title Library for operations on the BN254 elliptic curve. diff --git a/src/libraries/BitmapUtils.sol b/src/libraries/BitmapUtils.sol index 41f9ef48..9c53aadd 100644 --- a/src/libraries/BitmapUtils.sol +++ b/src/libraries/BitmapUtils.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; /** * @title Library for Bitmap utilities such as converting between an array of bytes and a bitmap and finding the number of 1s in a bitmap. @@ -62,7 +62,7 @@ library BitmapUtils { function orderedBytesArrayToBitmap(bytes memory orderedBytesArray, uint8 bitUpperBound) internal pure returns (uint256) { uint256 bitmap = orderedBytesArrayToBitmap(orderedBytesArray); - require((1 << bitUpperBound) > bitmap, + require((1 << bitUpperBound) > bitmap, "BitmapUtils.orderedBytesArrayToBitmap: bitmap exceeds max value" ); @@ -95,11 +95,11 @@ library BitmapUtils { if (uint256(uint8(bytesArray[i])) <= uint256(uint8(singleByte))) { return false; } - + // Pull the next byte out of the array singleByte = bytesArray[i]; } - + return true; } @@ -149,9 +149,9 @@ library BitmapUtils { function isSet(uint256 bitmap, uint8 bit) internal pure returns (bool) { return 1 == ((bitmap >> bit) & 1); } - + /** - * @notice Returns a copy of `bitmap` with `bit` set. + * @notice Returns a copy of `bitmap` with `bit` set. * @dev IMPORTANT: we're dealing with stack values here, so this doesn't modify * the original bitmap. Using this correctly requires an assignment statement: * `bitmap = bitmap.setBit(bit);` diff --git a/src/libraries/LibMergeSort.sol b/src/libraries/LibMergeSort.sol index 012feed4..11eb91b1 100644 --- a/src/libraries/LibMergeSort.sol +++ b/src/libraries/LibMergeSort.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; library LibMergeSort { function sort(address[] memory array) internal pure returns (address[] memory) { diff --git a/src/libraries/QuorumBitmapHistoryLib.sol b/src/libraries/QuorumBitmapHistoryLib.sol index c971d64d..72cbf114 100644 --- a/src/libraries/QuorumBitmapHistoryLib.sol +++ b/src/libraries/QuorumBitmapHistoryLib.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {IRegistryCoordinator} from "../interfaces/IRegistryCoordinator.sol"; diff --git a/src/libraries/SignatureCheckerLib.sol b/src/libraries/SignatureCheckerLib.sol index 410462cc..864e4afb 100644 --- a/src/libraries/SignatureCheckerLib.sol +++ b/src/libraries/SignatureCheckerLib.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "@openzeppelin-upgrades/contracts/utils/cryptography/SignatureCheckerUpgradeable.sol"; diff --git a/src/slashers/InstantSlasher.sol b/src/slashers/InstantSlasher.sol index b30d5881..976ad896 100644 --- a/src/slashers/InstantSlasher.sol +++ b/src/slashers/InstantSlasher.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; import {IAllocationManager} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; diff --git a/src/slashers/VetoableSlasher.sol b/src/slashers/VetoableSlasher.sol index a973aacc..0f9c623f 100644 --- a/src/slashers/VetoableSlasher.sol +++ b/src/slashers/VetoableSlasher.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; import {SlasherBase} from "./base/SlasherBase.sol"; diff --git a/src/slashers/base/SlasherBase.sol b/src/slashers/base/SlasherBase.sol index 1ca3e3c9..283a764c 100644 --- a/src/slashers/base/SlasherBase.sol +++ b/src/slashers/base/SlasherBase.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {Initializable} from "@openzeppelin-upgrades/contracts/proxy/utils/Initializable.sol"; import {IServiceManager} from "../../interfaces/IServiceManager.sol"; diff --git a/src/slashers/base/SlasherStorage.sol b/src/slashers/base/SlasherStorage.sol index 1024811a..a3924f34 100644 --- a/src/slashers/base/SlasherStorage.sol +++ b/src/slashers/base/SlasherStorage.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {ISlasher} from "../../interfaces/ISlasher.sol"; contract SlasherStorage is ISlasher { diff --git a/src/unaudited/ECDSAServiceManagerBase.sol b/src/unaudited/ECDSAServiceManagerBase.sol index d1c899ac..805a961a 100644 --- a/src/unaudited/ECDSAServiceManagerBase.sol +++ b/src/unaudited/ECDSAServiceManagerBase.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {OwnableUpgradeable} from "@openzeppelin-upgrades/contracts/access/OwnableUpgradeable.sol"; import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol"; diff --git a/src/unaudited/ECDSAStakeRegistry.sol b/src/unaudited/ECDSAStakeRegistry.sol index b333d504..0e341067 100644 --- a/src/unaudited/ECDSAStakeRegistry.sol +++ b/src/unaudited/ECDSAStakeRegistry.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {ECDSAStakeRegistryStorage, Quorum, StrategyParams} from "./ECDSAStakeRegistryStorage.sol"; import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; diff --git a/src/unaudited/ECDSAStakeRegistryStorage.sol b/src/unaudited/ECDSAStakeRegistryStorage.sol index 0742157a..6509f214 100644 --- a/src/unaudited/ECDSAStakeRegistryStorage.sol +++ b/src/unaudited/ECDSAStakeRegistryStorage.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; import {CheckpointsUpgradeable} from "@openzeppelin-upgrades/contracts/utils/CheckpointsUpgradeable.sol"; diff --git a/src/unaudited/examples/ECDSAStakeRegistryEqualWeight.sol b/src/unaudited/examples/ECDSAStakeRegistryEqualWeight.sol index 23b8be2a..adfd2751 100644 --- a/src/unaudited/examples/ECDSAStakeRegistryEqualWeight.sol +++ b/src/unaudited/examples/ECDSAStakeRegistryEqualWeight.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol"; import {ECDSAStakeRegistryPermissioned} from "./ECDSAStakeRegistryPermissioned.sol"; diff --git a/src/unaudited/examples/ECDSAStakeRegistryPermissioned.sol b/src/unaudited/examples/ECDSAStakeRegistryPermissioned.sol index ef2e691c..ab0bca02 100644 --- a/src/unaudited/examples/ECDSAStakeRegistryPermissioned.sol +++ b/src/unaudited/examples/ECDSAStakeRegistryPermissioned.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol"; import {ECDSAStakeRegistry} from "../ECDSAStakeRegistry.sol"; diff --git a/test/events/IBLSApkRegistryEvents.sol b/test/events/IBLSApkRegistryEvents.sol index 1ed588de..6921203d 100644 --- a/test/events/IBLSApkRegistryEvents.sol +++ b/test/events/IBLSApkRegistryEvents.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {BN254} from "../../src/libraries/BN254.sol"; @@ -17,7 +17,7 @@ interface IBLSApkRegistryEvents { // @notice Emitted when an operator pubkey is removed from a set of quorums event OperatorRemovedFromQuorums( - address operator, + address operator, bytes32 operatorId, bytes quorumNumbers ); diff --git a/test/events/IIndexRegistryEvents.sol b/test/events/IIndexRegistryEvents.sol index 79494de0..21a0f0f2 100644 --- a/test/events/IIndexRegistryEvents.sol +++ b/test/events/IIndexRegistryEvents.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; interface IIndexRegistryEvents { // emitted when an operator's index in the ordered operator list for the quorum with number `quorumNumber` is updated diff --git a/test/events/IServiceManagerBaseEvents.sol b/test/events/IServiceManagerBaseEvents.sol index 6defff0d..1efb8fa0 100644 --- a/test/events/IServiceManagerBaseEvents.sol +++ b/test/events/IServiceManagerBaseEvents.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import { IRewardsCoordinator, diff --git a/test/events/IStakeRegistryEvents.sol b/test/events/IStakeRegistryEvents.sol index 3e6f7e24..1b585ceb 100644 --- a/test/events/IStakeRegistryEvents.sol +++ b/test/events/IStakeRegistryEvents.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {IStakeRegistry, IStrategy} from "src/interfaces/IStakeRegistry.sol"; diff --git a/test/ffi/BLSPubKeyCompendiumFFI.t.sol b/test/ffi/BLSPubKeyCompendiumFFI.t.sol index 18d49a17..4f3906ba 100644 --- a/test/ffi/BLSPubKeyCompendiumFFI.t.sol +++ b/test/ffi/BLSPubKeyCompendiumFFI.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "../../src/BLSApkRegistry.sol"; import "../ffi/util/G2Operations.sol"; diff --git a/test/ffi/BLSSignatureCheckerFFI.t.sol b/test/ffi/BLSSignatureCheckerFFI.t.sol index 16f72b4e..16c22ebe 100644 --- a/test/ffi/BLSSignatureCheckerFFI.t.sol +++ b/test/ffi/BLSSignatureCheckerFFI.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {G2Operations} from "../ffi/util/G2Operations.sol"; import {MockAVSDeployer} from "../utils/MockAVSDeployer.sol"; @@ -30,13 +30,13 @@ contract BLSSignatureCheckerFFITests is MockAVSDeployer, G2Operations { // this test checks that a valid signature from maxOperatorsToRegister with a random number of nonsigners is checked // correctly on the BLSSignatureChecker contract when all operators are only regsitered for a single quorum and // the signature is only checked for stakes on that quorum - function testBLSSignatureChecker_SingleQuorum_Valid(uint256 pseudoRandomNumber) public { + function testBLSSignatureChecker_SingleQuorum_Valid(uint256 pseudoRandomNumber) public { uint256 numNonSigners = pseudoRandomNumber % (maxOperatorsToRegister - 1); uint256 quorumBitmap = 1; bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); - (uint32 referenceBlockNumber, BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature) = + (uint32 referenceBlockNumber, BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature) = _registerSignatoriesAndGetNonSignerStakeAndSignatureRandom(pseudoRandomNumber, numNonSigners, quorumBitmap); uint256 gasBefore = gasleft(); @@ -44,9 +44,9 @@ contract BLSSignatureCheckerFFITests is MockAVSDeployer, G2Operations { BLSSignatureChecker.QuorumStakeTotals memory quorumStakeTotals, /* bytes32 signatoryRecordHash */ ) = blsSignatureChecker.checkSignatures( - msgHash, + msgHash, quorumNumbers, - referenceBlockNumber, + referenceBlockNumber, nonSignerStakesAndSignature ); uint256 gasAfter = gasleft(); @@ -61,14 +61,14 @@ contract BLSSignatureCheckerFFITests is MockAVSDeployer, G2Operations { // this test checks that a valid signature from maxOperatorsToRegister with a random number of nonsigners is checked // correctly on the BLSSignatureChecker contract when all operators are registered for the first 100 quorums // and the signature is only checked for stakes on those quorums - function testBLSSignatureChecker_100Quorums_Valid(uint256 pseudoRandomNumber) public { + function testBLSSignatureChecker_100Quorums_Valid(uint256 pseudoRandomNumber) public { uint256 numNonSigners = pseudoRandomNumber % (maxOperatorsToRegister - 1); // 100 set bits uint256 quorumBitmap = (1 << 100) - 1; bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); - (uint32 referenceBlockNumber, BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature) = + (uint32 referenceBlockNumber, BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature) = _registerSignatoriesAndGetNonSignerStakeAndSignatureRandom(pseudoRandomNumber, numNonSigners, quorumBitmap); nonSignerStakesAndSignature.sigma = sigma.scalar_mul(quorumNumbers.length); @@ -76,9 +76,9 @@ contract BLSSignatureCheckerFFITests is MockAVSDeployer, G2Operations { uint256 gasBefore = gasleft(); blsSignatureChecker.checkSignatures( - msgHash, + msgHash, quorumNumbers, - referenceBlockNumber, + referenceBlockNumber, nonSignerStakesAndSignature ); uint256 gasAfter = gasleft(); @@ -168,8 +168,8 @@ contract BLSSignatureCheckerFFITests is MockAVSDeployer, G2Operations { OperatorStateRetriever.CheckSignaturesIndices memory checkSignaturesIndices = operatorStateRetriever.getCheckSignaturesIndices( registryCoordinator, - referenceBlockNumber, - quorumNumbers, + referenceBlockNumber, + quorumNumbers, nonSignerOperatorIds ); diff --git a/test/ffi/UpdateOperators.t.sol b/test/ffi/UpdateOperators.t.sol index 26fad27d..64e3ecbc 100644 --- a/test/ffi/UpdateOperators.t.sol +++ b/test/ffi/UpdateOperators.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "test/integration/User.t.sol"; @@ -37,7 +37,7 @@ contract Integration_AVS_Sync_GasCosts_FFI is IntegrationChecks { config_data, string.concat(".G1y[", vm.toString(i), "]") ); - // G2 + // G2 pubkey.pubkeyG2.X[1] = stdJson.readUint( config_data, string.concat(".G2x1[", vm.toString(i), "]") diff --git a/test/ffi/util/G2Operations.sol b/test/ffi/util/G2Operations.sol index f25cbbb7..016011da 100644 --- a/test/ffi/util/G2Operations.sol +++ b/test/ffi/util/G2Operations.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "forge-std/Test.sol"; import "openzeppelin-contracts/contracts/utils/Strings.sol"; @@ -13,7 +13,7 @@ contract G2Operations is Test { inputs[0] = "go"; inputs[1] = "run"; inputs[2] = "test/ffi/go/g2mul.go"; - inputs[3] = x.toString(); + inputs[3] = x.toString(); inputs[4] = "1"; bytes memory res = vm.ffi(inputs); diff --git a/test/harnesses/AVSDirectoryHarness.sol b/test/harnesses/AVSDirectoryHarness.sol index 262e8903..995f62a6 100644 --- a/test/harnesses/AVSDirectoryHarness.sol +++ b/test/harnesses/AVSDirectoryHarness.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; diff --git a/test/harnesses/BLSApkRegistryHarness.sol b/test/harnesses/BLSApkRegistryHarness.sol index 546d355c..7cc8ec60 100644 --- a/test/harnesses/BLSApkRegistryHarness.sol +++ b/test/harnesses/BLSApkRegistryHarness.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "../../src/BLSApkRegistry.sol"; diff --git a/test/harnesses/BitmapUtilsWrapper.sol b/test/harnesses/BitmapUtilsWrapper.sol index 19e1135e..95322d21 100644 --- a/test/harnesses/BitmapUtilsWrapper.sol +++ b/test/harnesses/BitmapUtilsWrapper.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "../../src/libraries/BitmapUtils.sol"; diff --git a/test/harnesses/RegistryCoordinatorHarness.t.sol b/test/harnesses/RegistryCoordinatorHarness.t.sol index 9dede138..8110fb13 100644 --- a/test/harnesses/RegistryCoordinatorHarness.t.sol +++ b/test/harnesses/RegistryCoordinatorHarness.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "../../src/RegistryCoordinator.sol"; diff --git a/test/harnesses/StakeRegistryHarness.sol b/test/harnesses/StakeRegistryHarness.sol index ca3cd2af..7d00b107 100644 --- a/test/harnesses/StakeRegistryHarness.sol +++ b/test/harnesses/StakeRegistryHarness.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "../../src/StakeRegistry.sol"; diff --git a/test/integration/CoreRegistration.t.sol b/test/integration/CoreRegistration.t.sol index a237e410..5d875e3e 100644 --- a/test/integration/CoreRegistration.t.sol +++ b/test/integration/CoreRegistration.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "../utils/MockAVSDeployer.sol"; import { AVSDirectory } from "eigenlayer-contracts/src/contracts/core/AVSDirectory.sol"; diff --git a/test/integration/IntegrationBase.t.sol b/test/integration/IntegrationBase.t.sol index 7a0265f9..0baf32b4 100644 --- a/test/integration/IntegrationBase.t.sol +++ b/test/integration/IntegrationBase.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "forge-std/Test.sol"; @@ -164,7 +164,7 @@ abstract contract IntegrationBase is IntegrationConfig { } /// AVSDirectory: - + function assert_NotRegisteredToAVS(User operator, string memory err) internal { IAVSDirectoryTypes.OperatorAVSRegistrationStatus status = avsDirectory.avsOperatorStatus(address(serviceManager), address(operator)); @@ -222,7 +222,7 @@ abstract contract IntegrationBase is IntegrationConfig { function assert_Snap_Unchanged_QuorumBitmap(User user, string memory err) internal { bytes32 operatorId = user.operatorId(); - + uint192 curBitmap = _getQuorumBitmap(operatorId); uint192 prevBitmap = _getPrevQuorumBitmap(operatorId); @@ -288,11 +288,11 @@ abstract contract IntegrationBase is IntegrationConfig { for (uint i = 0; i < churnedQuorums.length; i++) { BN254.G1Point memory churnedPubkey = churnedOperators[i].pubkeyG1(); - BN254.G1Point memory expectedApk + BN254.G1Point memory expectedApk = prevApks[i] .plus(churnedPubkey.negate()) .plus(incomingPubkey); - + assertEq(expectedApk.X, curApks[i].X, err); assertEq(expectedApk.Y, curApks[i].Y, err); } @@ -300,7 +300,7 @@ abstract contract IntegrationBase is IntegrationConfig { /// @dev Check that specific weights were added to the operator and total stakes for each quorum function assert_Snap_AddedWeightToStakes( - User user, + User user, bytes memory quorums, uint96[] memory addedWeights, string memory err @@ -320,7 +320,7 @@ abstract contract IntegrationBase is IntegrationConfig { /// @dev Check that the operator's stake weight was added to the operator and total /// stakes for each quorum function assert_Snap_Added_OperatorWeight( - User user, + User user, bytes memory quorums, string memory err ) internal { @@ -362,7 +362,7 @@ abstract contract IntegrationBase is IntegrationConfig { } function assert_Snap_Unchanged_OperatorStake( - User user, + User user, bytes memory quorums, string memory err ) internal { @@ -431,7 +431,7 @@ abstract contract IntegrationBase is IntegrationConfig { } function assert_Snap_Removed_TotalStake( - User user, + User user, bytes memory quorums, string memory err ) internal { @@ -462,7 +462,7 @@ abstract contract IntegrationBase is IntegrationConfig { function assert_Snap_Added_OperatorCount(bytes memory quorums, string memory err) internal { uint32[] memory curOperatorCounts = _getOperatorCounts(quorums); uint32[] memory prevOperatorCounts = _getPrevOperatorCounts(quorums); - + for (uint i = 0; i < quorums.length; i++) { assertEq(curOperatorCounts[i], prevOperatorCounts[i] + 1, err); } @@ -471,7 +471,7 @@ abstract contract IntegrationBase is IntegrationConfig { function assert_Snap_Reduced_OperatorCount(bytes memory quorums, string memory err) internal { uint32[] memory curOperatorCounts = _getOperatorCounts(quorums); uint32[] memory prevOperatorCounts = _getPrevOperatorCounts(quorums); - + for (uint i = 0; i < quorums.length; i++) { assertEq(curOperatorCounts[i], prevOperatorCounts[i] - 1, err); } @@ -480,7 +480,7 @@ abstract contract IntegrationBase is IntegrationConfig { function assert_Snap_Unchanged_OperatorCount(bytes memory quorums, string memory err) internal { uint32[] memory curOperatorCounts = _getOperatorCounts(quorums); uint32[] memory prevOperatorCounts = _getPrevOperatorCounts(quorums); - + for (uint i = 0; i < quorums.length; i++) { assertEq(curOperatorCounts[i], prevOperatorCounts[i], err); } @@ -491,7 +491,7 @@ abstract contract IntegrationBase is IntegrationConfig { /// - that the operator is in the current list, but not the previous list function assert_Snap_Added_OperatorListEntry( User operator, - bytes memory quorums, + bytes memory quorums, string memory err ) internal { bytes32[][] memory curOperatorLists = _getOperatorLists(quorums); @@ -510,7 +510,7 @@ abstract contract IntegrationBase is IntegrationConfig { /// - that the operator is in the previous list, but not the current list function assert_Snap_Removed_OperatorListEntry( User operator, - bytes memory quorums, + bytes memory quorums, string memory err ) internal { bytes32[][] memory curOperatorLists = _getOperatorLists(quorums); @@ -541,12 +541,12 @@ abstract contract IntegrationBase is IntegrationConfig { function assert_Snap_Replaced_OperatorListEntries( User incomingOperator, User[] memory churnedOperators, - bytes memory churnedQuorums, + bytes memory churnedQuorums, string memory err ) internal { // Sanity check input lengths assertEq(churnedOperators.length, churnedQuorums.length, "assert_Snap_Replaced_OperatorListEntries: input length mismatch"); - + bytes32[][] memory curOperatorLists = _getOperatorLists(churnedQuorums); bytes32[][] memory prevOperatorLists = _getPrevOperatorLists(churnedQuorums); @@ -568,11 +568,11 @@ abstract contract IntegrationBase is IntegrationConfig { TIME TRAVELERS ONLY BEYOND THIS POINT *******************************************************************************/ - /// @dev Check that the operator has `addedShares` additional operator shares + /// @dev Check that the operator has `addedShares` additional operator shares // for each strategy since the last snapshot function assert_Snap_Added_OperatorShares( - User operator, - IStrategy[] memory strategies, + User operator, + IStrategy[] memory strategies, uint[] memory addedShares, string memory err ) internal { @@ -589,8 +589,8 @@ abstract contract IntegrationBase is IntegrationConfig { /// @dev Check that the operator has `removedShares` fewer operator shares /// for each strategy since the last snapshot function assert_Snap_Removed_OperatorShares( - User operator, - IStrategy[] memory strategies, + User operator, + IStrategy[] memory strategies, uint256[] memory removedShares, string memory err ) internal { @@ -607,8 +607,8 @@ abstract contract IntegrationBase is IntegrationConfig { /// @dev Check that the staker has `addedShares` additional delegatable shares /// for each strategy since the last snapshot function assert_Snap_Added_StakerShares( - User staker, - IStrategy[] memory strategies, + User staker, + IStrategy[] memory strategies, uint[] memory addedShares, string memory err ) internal { @@ -625,8 +625,8 @@ abstract contract IntegrationBase is IntegrationConfig { /// @dev Check that the staker has `removedShares` fewer delegatable shares /// for each strategy since the last snapshot function assert_Snap_Removed_StakerShares( - User staker, - IStrategy[] memory strategies, + User staker, + IStrategy[] memory strategies, uint[] memory removedShares, string memory err ) internal { @@ -641,7 +641,7 @@ abstract contract IntegrationBase is IntegrationConfig { } function assert_Snap_Added_QueuedWithdrawals( - User staker, + User staker, IDelegationManager.Withdrawal[] memory withdrawals, string memory err ) internal { @@ -653,7 +653,7 @@ abstract contract IntegrationBase is IntegrationConfig { } function assert_Snap_Added_QueuedWithdrawal( - User staker, + User staker, string memory err ) internal { uint curQueuedWithdrawal = _getCumulativeWithdrawals(staker); @@ -741,7 +741,7 @@ abstract contract IntegrationBase is IntegrationConfig { /// @dev Uses timewarp modifier to get operator shares at the last snapshot function _getPrevOperatorShares( - User operator, + User operator, IStrategy[] memory strategies ) internal timewarp() returns (uint[] memory) { return _getOperatorShares(operator, strategies); @@ -760,7 +760,7 @@ abstract contract IntegrationBase is IntegrationConfig { /// @dev Uses timewarp modifier to get staker shares at the last snapshot function _getPrevStakerShares( - User staker, + User staker, IStrategy[] memory strategies ) internal timewarp() returns (uint[] memory) { return _getStakerShares(staker, strategies); @@ -786,7 +786,7 @@ abstract contract IntegrationBase is IntegrationConfig { function _getCumulativeWithdrawals(User staker) internal view returns (uint) { return delegationManager.cumulativeWithdrawalsQueued(address(staker)); } - + /// RegistryCoordinator: function _getOperatorInfo(User user) internal view returns (IRegistryCoordinator.OperatorInfo memory) { @@ -880,7 +880,7 @@ abstract contract IntegrationBase is IntegrationConfig { for (uint i = 0; i < quorums.length; i++) { stakes[i] = stakeRegistry.getCurrentTotalStake(uint8(quorums[i])); } - + return stakes; } diff --git a/test/integration/IntegrationChecks.t.sol b/test/integration/IntegrationChecks.t.sol index 67ee38f5..0f2de79c 100644 --- a/test/integration/IntegrationChecks.t.sol +++ b/test/integration/IntegrationChecks.t.sol @@ -1,12 +1,12 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "test/integration/IntegrationBase.t.sol"; import "test/integration/User.t.sol"; /// @notice Contract that provides utility functions to reuse common test blocks & checks contract IntegrationChecks is IntegrationBase { - + using BitmapUtils for *; /******************************************************************************* @@ -25,11 +25,11 @@ contract IntegrationChecks is IntegrationBase { "operator already has bits in quorum bitmap"); // BLSApkRegistry - assert_NoRegisteredPubkey(operator, + assert_NoRegisteredPubkey(operator, "operator already has a registered pubkey"); // DelegationManager - assert_NotRegisteredToAVS(operator, + assert_NotRegisteredToAVS(operator, "operator should not be registered to the AVS"); } @@ -44,7 +44,7 @@ contract IntegrationChecks is IntegrationBase { _log("check_Register_State", operator); // RegistryCoordinator - assert_HasOperatorInfoWithId(operator, + assert_HasOperatorInfoWithId(operator, "operatorInfo should have operatorId"); assert_HasRegisteredStatus(operator, "operatorInfo status should be REGISTERED"); @@ -54,11 +54,11 @@ contract IntegrationChecks is IntegrationBase { "operator did not register for all quorums"); // BLSApkRegistry - assert_HasRegisteredPubkey(operator, + assert_HasRegisteredPubkey(operator, "operator should have registered a pubkey"); - assert_Snap_Added_QuorumApk(operator, quorums, + assert_Snap_Added_QuorumApk(operator, quorums, "operator pubkey should have been added to each quorum apk"); - + // StakeRegistry assert_HasAtLeastMinimumStake(operator, quorums, "operator should have at least the minimum stake in each quorum"); @@ -86,14 +86,14 @@ contract IntegrationChecks is IntegrationBase { ) internal { _log("check_Churned_State", incomingOperator); - bytes memory combinedQuorums = + bytes memory combinedQuorums = churnedQuorums .orderedBytesArrayToBitmap() .plus(standardQuorums.orderedBytesArrayToBitmap()) .bitmapToBytesArray(); // RegistryCoordinator - assert_HasOperatorInfoWithId(incomingOperator, + assert_HasOperatorInfoWithId(incomingOperator, "operatorInfo should have operatorId"); assert_HasRegisteredStatus(incomingOperator, "operatorInfo status should be REGISTERED"); @@ -103,13 +103,13 @@ contract IntegrationChecks is IntegrationBase { "operator did not register for all quorums"); // BLSApkRegistry - assert_HasRegisteredPubkey(incomingOperator, + assert_HasRegisteredPubkey(incomingOperator, "operator should have registered a pubkey"); assert_Snap_Added_QuorumApk(incomingOperator, standardQuorums, "operator pubkey should have been added to standardQuorums apks"); assert_Snap_Churned_QuorumApk(incomingOperator, churnedOperators, churnedQuorums, "operator pubkey should have been added and churned operator pubkeys should have been removed from apks"); - + // StakeRegistry assert_HasAtLeastMinimumStake(incomingOperator, combinedQuorums, "operator should have at least the minimum stake in each quorum"); @@ -140,7 +140,7 @@ contract IntegrationChecks is IntegrationBase { churnedQuorum[0] = churnedQuorums[i]; // RegistryCoordinator - assert_HasOperatorInfoWithId(churnedOperator, + assert_HasOperatorInfoWithId(churnedOperator, "churned operatorInfo should still have operatorId"); assert_NotRegisteredForQuorums(churnedOperator, churnedQuorum, "churned operator bitmap should not include churned quorums"); @@ -148,7 +148,7 @@ contract IntegrationChecks is IntegrationBase { "churned operator did not deregister from churned quorum"); // BLSApkRegistry - assert_HasRegisteredPubkey(churnedOperator, + assert_HasRegisteredPubkey(churnedOperator, "churned operator should still have a registered pubkey"); // StakeRegistry @@ -177,7 +177,7 @@ contract IntegrationChecks is IntegrationBase { "operator info should not have changed"); assert_Snap_Unchanged_QuorumBitmap(operator, "operators quorum bitmap should not have changed"); - + // BLSApkRegistry assert_Snap_Unchanged_QuorumApk(quorums, "quorum apks should not have changed"); @@ -185,9 +185,9 @@ contract IntegrationChecks is IntegrationBase { // StakeRegistry assert_Snap_Increased_OperatorWeight(operator, quorums, "operator weight should not have decreased after deposit"); - assert_Snap_Unchanged_OperatorStake(operator, quorums, + assert_Snap_Unchanged_OperatorStake(operator, quorums, "operator stake should be unchanged"); - assert_Snap_Unchanged_TotalStake(quorums, + assert_Snap_Unchanged_TotalStake(quorums, "total stake should be unchanged"); // IndexRegistry @@ -205,8 +205,8 @@ contract IntegrationChecks is IntegrationBase { /// NOTE: This method assumes (and checks) that the operator already /// met the minimum stake before stake was added. function check_DepositUpdate_State( - User operator, - bytes memory quorums, + User operator, + bytes memory quorums, uint96[] memory addedWeights ) internal { _log("check_DepositUpdate_State", operator); @@ -254,7 +254,7 @@ contract IntegrationChecks is IntegrationBase { "operator info should not have changed"); assert_Snap_Unchanged_QuorumBitmap(operator, "operators quorum bitmap should not have changed"); - + // BLSApkRegistry assert_Snap_Unchanged_QuorumApk(quorums, "quorum apks should not have changed"); @@ -262,9 +262,9 @@ contract IntegrationChecks is IntegrationBase { // StakeRegistry assert_Snap_Decreased_OperatorWeight(operator, quorums, "operator weight should not have increased after deposit"); - assert_Snap_Unchanged_OperatorStake(operator, quorums, + assert_Snap_Unchanged_OperatorStake(operator, quorums, "operator stake should be unchanged"); - assert_Snap_Unchanged_TotalStake(quorums, + assert_Snap_Unchanged_TotalStake(quorums, "total stake should be unchanged"); // IndexRegistry @@ -288,7 +288,7 @@ contract IntegrationChecks is IntegrationBase { _log("check_WithdrawUpdate_State", operator); // RegistryCoordinator - assert_HasOperatorInfoWithId(operator, + assert_HasOperatorInfoWithId(operator, "operatorInfo should still have operatorId"); assert_EmptyQuorumBitmap(operator, "operator should not have any bits in bitmap"); @@ -298,9 +298,9 @@ contract IntegrationChecks is IntegrationBase { "operator did not deregister from all quorums"); // BLSApkRegistry - assert_HasRegisteredPubkey(operator, + assert_HasRegisteredPubkey(operator, "operator should still have a registered pubkey"); - assert_Snap_Removed_QuorumApk(operator, quorums, + assert_Snap_Removed_QuorumApk(operator, quorums, "operator pubkey should have been subtracted from each quorum apk"); // StakeRegistry @@ -316,7 +316,7 @@ contract IntegrationChecks is IntegrationBase { "operator list should have one fewer entry"); // AVSDirectory - assert_NotRegisteredToAVS(operator, + assert_NotRegisteredToAVS(operator, "operator should not be registered to the AVS"); } @@ -364,7 +364,7 @@ contract IntegrationChecks is IntegrationBase { _log("check_Deregister_State", operator); // RegistryCoordinator - assert_HasOperatorInfoWithId(operator, + assert_HasOperatorInfoWithId(operator, "operatorInfo should still have operatorId"); assert_NotRegisteredForQuorums(operator, quorums, "current operator bitmap should not include quorums"); @@ -372,9 +372,9 @@ contract IntegrationChecks is IntegrationBase { "operator did not deregister from all quorums"); // BLSApkRegistry - assert_HasRegisteredPubkey(operator, + assert_HasRegisteredPubkey(operator, "operator should still have a registered pubkey"); - assert_Snap_Removed_QuorumApk(operator, quorums, + assert_Snap_Removed_QuorumApk(operator, quorums, "operator pubkey should have been subtracted from each quorum apk"); // StakeRegistry @@ -399,13 +399,13 @@ contract IntegrationChecks is IntegrationBase { // RegistryCoordinator assert_EmptyQuorumBitmap(operator, "operator should not have any bits in bitmap"); - assert_HasOperatorInfoWithId(operator, + assert_HasOperatorInfoWithId(operator, "operatorInfo should still have operatorId"); assert_HasDeregisteredStatus(operator, "operatorInfo status should be DEREGISTERED"); // AVSDirectory - assert_NotRegisteredToAVS(operator, + assert_NotRegisteredToAVS(operator, "operator should not be registered to the AVS"); } diff --git a/test/integration/IntegrationConfig.t.sol b/test/integration/IntegrationConfig.t.sol index e1a341e8..f3231ac3 100644 --- a/test/integration/IntegrationConfig.t.sol +++ b/test/integration/IntegrationConfig.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "forge-std/Test.sol"; diff --git a/test/integration/IntegrationDeployer.t.sol b/test/integration/IntegrationDeployer.t.sol index db3fb704..414b8fe2 100644 --- a/test/integration/IntegrationDeployer.t.sol +++ b/test/integration/IntegrationDeployer.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "forge-std/Test.sol"; diff --git a/test/integration/TimeMachine.t.sol b/test/integration/TimeMachine.t.sol index b1df82d5..b1cffa68 100644 --- a/test/integration/TimeMachine.t.sol +++ b/test/integration/TimeMachine.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "forge-std/Test.sol"; diff --git a/test/integration/User.t.sol b/test/integration/User.t.sol index b98a8cd3..4b58950a 100644 --- a/test/integration/User.t.sol +++ b/test/integration/User.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "forge-std/Test.sol"; import "@openzeppelin/contracts/utils/Strings.sol"; diff --git a/test/integration/mocks/BeaconChainOracleMock.t.sol b/test/integration/mocks/BeaconChainOracleMock.t.sol index fcccbbd4..5c2e4b77 100644 --- a/test/integration/mocks/BeaconChainOracleMock.t.sol +++ b/test/integration/mocks/BeaconChainOracleMock.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; // import "eigenlayer-contracts/src/contracts/interfaces/IBeaconChainOracle.sol"; diff --git a/test/integration/tests/Full_Register_Deregister.t.sol b/test/integration/tests/Full_Register_Deregister.t.sol index 48bb150a..2561aa2c 100644 --- a/test/integration/tests/Full_Register_Deregister.t.sol +++ b/test/integration/tests/Full_Register_Deregister.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "test/integration/User.t.sol"; @@ -109,7 +109,7 @@ contract Integration_Full_Register_Deregister is IntegrationChecks { churnTarget.registerOperator(quorum); check_Register_State(churnTarget, quorum); } - + // 4. Original operator re-registers for all quorums by churning old operators again operator.registerOperatorWithChurn(quorums, churnTargets, new bytes(0)); check_Churned_State({ @@ -140,7 +140,7 @@ contract Integration_Full_Register_Deregister is IntegrationChecks { // Select some quorums to register using churn, and the rest without churn bytes memory churnQuorums = _selectRand(quorums); - bytes memory standardQuorums = + bytes memory standardQuorums = quorums .orderedBytesArrayToBitmap() .minus(churnQuorums.orderedBytesArrayToBitmap()) diff --git a/test/integration/tests/NonFull_Register_CoreBalanceChange_Update.t.sol b/test/integration/tests/NonFull_Register_CoreBalanceChange_Update.t.sol index a4ffe3e6..d9b74e8d 100644 --- a/test/integration/tests/NonFull_Register_CoreBalanceChange_Update.t.sol +++ b/test/integration/tests/NonFull_Register_CoreBalanceChange_Update.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "test/integration/User.t.sol"; diff --git a/test/integration/tests/NonFull_Register_Deregister.t.sol b/test/integration/tests/NonFull_Register_Deregister.t.sol index c30c5261..6acf85cb 100644 --- a/test/integration/tests/NonFull_Register_Deregister.t.sol +++ b/test/integration/tests/NonFull_Register_Deregister.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "test/integration/User.t.sol"; diff --git a/test/integration/utils/BitmapStrings.t.sol b/test/integration/utils/BitmapStrings.t.sol index 532f5d8d..2bcec384 100644 --- a/test/integration/utils/BitmapStrings.t.sol +++ b/test/integration/utils/BitmapStrings.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "@openzeppelin/contracts/utils/Strings.sol"; @@ -16,12 +16,12 @@ library BitmapStrings { for (uint i = 0; i < bitmapArr.length; i++) { if (i == bitmapArr.length - 1) { result = string.concat( - result, + result, uint(uint8(bitmapArr[i])).toString() ); } else { result = string.concat( - result, + result, uint(uint8(bitmapArr[i])).toString(), ", " ); diff --git a/test/integration/utils/Sort.t.sol b/test/integration/utils/Sort.t.sol index 46a2fc2b..968400c6 100644 --- a/test/integration/utils/Sort.t.sol +++ b/test/integration/utils/Sort.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; library Sort { /** diff --git a/test/mocks/AVSDirectoryMock.sol b/test/mocks/AVSDirectoryMock.sol index 5e4c6f77..ca3613ef 100644 --- a/test/mocks/AVSDirectoryMock.sol +++ b/test/mocks/AVSDirectoryMock.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {IAVSDirectory } from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol"; import {OperatorSet} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; diff --git a/test/mocks/AllocationManagerMock.sol b/test/mocks/AllocationManagerMock.sol index fdb11248..b355af95 100644 --- a/test/mocks/AllocationManagerMock.sol +++ b/test/mocks/AllocationManagerMock.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {IAllocationManager, OperatorSet} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; import {IAVSRegistrar } from "eigenlayer-contracts/src/contracts/interfaces/IAVSRegistrar.sol"; diff --git a/test/mocks/DelegationMock.sol b/test/mocks/DelegationMock.sol index 2f734c04..e3b1ed77 100644 --- a/test/mocks/DelegationMock.sol +++ b/test/mocks/DelegationMock.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {console2 as console} from "forge-std/Test.sol"; diff --git a/test/mocks/ECDSAServiceManagerMock.sol b/test/mocks/ECDSAServiceManagerMock.sol index 17a315aa..102d076e 100644 --- a/test/mocks/ECDSAServiceManagerMock.sol +++ b/test/mocks/ECDSAServiceManagerMock.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "../../src/unaudited/ECDSAServiceManagerBase.sol"; import {IAllocationManagerTypes} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; diff --git a/test/mocks/ECDSAStakeRegistryMock.sol b/test/mocks/ECDSAStakeRegistryMock.sol index 7ad6043e..e2b756be 100644 --- a/test/mocks/ECDSAStakeRegistryMock.sol +++ b/test/mocks/ECDSAStakeRegistryMock.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "../../src/unaudited/ECDSAStakeRegistry.sol"; diff --git a/test/mocks/PermissionControllerMock.sol b/test/mocks/PermissionControllerMock.sol index 7501a9fa..9a9eca00 100644 --- a/test/mocks/PermissionControllerMock.sol +++ b/test/mocks/PermissionControllerMock.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {IPermissionController} from "eigenlayer-contracts/src/contracts/interfaces/IPermissionController.sol"; diff --git a/test/mocks/RegistryCoordinatorMock.sol b/test/mocks/RegistryCoordinatorMock.sol index c428a044..165a649d 100644 --- a/test/mocks/RegistryCoordinatorMock.sol +++ b/test/mocks/RegistryCoordinatorMock.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "../../src/interfaces/IRegistryCoordinator.sol"; diff --git a/test/mocks/RewardsCoordinatorMock.sol b/test/mocks/RewardsCoordinatorMock.sol index e408c421..9d992962 100644 --- a/test/mocks/RewardsCoordinatorMock.sol +++ b/test/mocks/RewardsCoordinatorMock.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; diff --git a/test/mocks/ServiceManagerMock.sol b/test/mocks/ServiceManagerMock.sol index a9bb5fff..a60e63c8 100644 --- a/test/mocks/ServiceManagerMock.sol +++ b/test/mocks/ServiceManagerMock.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "../../src/ServiceManagerBase.sol"; diff --git a/test/mocks/StakeRegistryMock.sol b/test/mocks/StakeRegistryMock.sol index 2dcecce3..58f46256 100644 --- a/test/mocks/StakeRegistryMock.sol +++ b/test/mocks/StakeRegistryMock.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "../../src/interfaces/IStakeRegistry.sol"; import "../../src/interfaces/IRegistryCoordinator.sol"; diff --git a/test/unit/BLSApkRegistryUnit.t.sol b/test/unit/BLSApkRegistryUnit.t.sol index 2a8be4c7..aacfe9d8 100644 --- a/test/unit/BLSApkRegistryUnit.t.sol +++ b/test/unit/BLSApkRegistryUnit.t.sol @@ -1,5 +1,5 @@ //SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "forge-std/Test.sol"; import "../harnesses/BLSApkRegistryHarness.sol"; diff --git a/test/unit/BLSSignatureCheckerUnit.t.sol b/test/unit/BLSSignatureCheckerUnit.t.sol index 6056b283..74b8f1da 100644 --- a/test/unit/BLSSignatureCheckerUnit.t.sol +++ b/test/unit/BLSSignatureCheckerUnit.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "../../src/BLSSignatureChecker.sol"; import "../utils/BLSMockAVSDeployer.sol"; diff --git a/test/unit/BitmapUtils.t.sol b/test/unit/BitmapUtils.t.sol index fd51298d..fcd742b2 100644 --- a/test/unit/BitmapUtils.t.sol +++ b/test/unit/BitmapUtils.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "../harnesses/BitmapUtilsWrapper.sol"; // import "../../contracts/libraries/BitmapUtils.sol"; @@ -24,7 +24,7 @@ contract BitmapUtilsUnitTests_bitwiseOperations is BitmapUtilsUnitTests { uint16 numOnes = 0; for (uint256 i = 0; i < 256; ++i) { if ((input >> i) & 1 == 1) { - ++numOnes; + ++numOnes; } } assertEq(libraryOutput, numOnes, "inconsistency in countNumOnes function"); diff --git a/test/unit/ECDSAServiceManager.t.sol b/test/unit/ECDSAServiceManager.t.sol index 4cb283fc..bcd17052 100644 --- a/test/unit/ECDSAServiceManager.t.sol +++ b/test/unit/ECDSAServiceManager.t.sol @@ -1,5 +1,5 @@ // // SPDX-License-Identifier: MIT -// pragma solidity ^0.8.12; +// pragma solidity ^0.8.27; // import {Test, console} from "forge-std/Test.sol"; diff --git a/test/unit/ECDSAStakeRegistryEqualWeightUnit.t.sol b/test/unit/ECDSAStakeRegistryEqualWeightUnit.t.sol index bc6337c5..0008da7f 100644 --- a/test/unit/ECDSAStakeRegistryEqualWeightUnit.t.sol +++ b/test/unit/ECDSAStakeRegistryEqualWeightUnit.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol"; import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; diff --git a/test/unit/ECDSAStakeRegistryPermissionedUnit.t.sol b/test/unit/ECDSAStakeRegistryPermissionedUnit.t.sol index dffb9174..8205034d 100644 --- a/test/unit/ECDSAStakeRegistryPermissionedUnit.t.sol +++ b/test/unit/ECDSAStakeRegistryPermissionedUnit.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol"; import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; diff --git a/test/unit/ECDSAStakeRegistryUnit.t.sol b/test/unit/ECDSAStakeRegistryUnit.t.sol index d374144d..e1d7de77 100644 --- a/test/unit/ECDSAStakeRegistryUnit.t.sol +++ b/test/unit/ECDSAStakeRegistryUnit.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {Test, console} from "forge-std/Test.sol"; diff --git a/test/unit/EjectionManagerUnit.t.sol b/test/unit/EjectionManagerUnit.t.sol index 456fe58b..9f01af50 100644 --- a/test/unit/EjectionManagerUnit.t.sol +++ b/test/unit/EjectionManagerUnit.t.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {EjectionManager} from "../../src/EjectionManager.sol"; import {IEjectionManager} from "../../src/interfaces/IEjectionManager.sol"; diff --git a/test/unit/IndexRegistryUnit.t.sol b/test/unit/IndexRegistryUnit.t.sol index fe034c0e..36d7dc69 100644 --- a/test/unit/IndexRegistryUnit.t.sol +++ b/test/unit/IndexRegistryUnit.t.sol @@ -1,5 +1,5 @@ //SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "../../src/interfaces/IIndexRegistry.sol"; import "../../src/IndexRegistry.sol"; diff --git a/test/unit/LibMergeSort.t.sol b/test/unit/LibMergeSort.t.sol index f3014d3f..dc2ddf7d 100644 --- a/test/unit/LibMergeSort.t.sol +++ b/test/unit/LibMergeSort.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "forge-std/Test.sol"; import "../../src/libraries/LibMergeSort.sol"; diff --git a/test/unit/OperatorStateRetrieverUnit.t.sol b/test/unit/OperatorStateRetrieverUnit.t.sol index 2fe360ca..e25c8af0 100644 --- a/test/unit/OperatorStateRetrieverUnit.t.sol +++ b/test/unit/OperatorStateRetrieverUnit.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "../utils/MockAVSDeployer.sol"; diff --git a/test/unit/RegistryCoordinatorUnit.t.sol b/test/unit/RegistryCoordinatorUnit.t.sol index 9fedcd1f..2ee48849 100644 --- a/test/unit/RegistryCoordinatorUnit.t.sol +++ b/test/unit/RegistryCoordinatorUnit.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "../utils/MockAVSDeployer.sol"; diff --git a/test/unit/ServiceManagerBase.t.sol b/test/unit/ServiceManagerBase.t.sol index c7ac91e9..8d37f9d6 100644 --- a/test/unit/ServiceManagerBase.t.sol +++ b/test/unit/ServiceManagerBase.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "@openzeppelin/contracts/token/ERC20/presets/ERC20PresetFixedSupply.sol"; import { diff --git a/test/unit/ServiceManagerRouter.t.sol b/test/unit/ServiceManagerRouter.t.sol index 6706ade4..5cf1d3b7 100644 --- a/test/unit/ServiceManagerRouter.t.sol +++ b/test/unit/ServiceManagerRouter.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {ServiceManagerRouter} from "../../src/ServiceManagerRouter.sol"; import "../utils/MockAVSDeployer.sol"; diff --git a/test/unit/StakeRegistryUnit.t.sol b/test/unit/StakeRegistryUnit.t.sol index 6fbe8609..a4f58b85 100644 --- a/test/unit/StakeRegistryUnit.t.sol +++ b/test/unit/StakeRegistryUnit.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; diff --git a/test/unit/Utils.sol b/test/unit/Utils.sol index c143633d..6e085215 100644 --- a/test/unit/Utils.sol +++ b/test/unit/Utils.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; import "eigenlayer-contracts/src/contracts/strategies/StrategyBase.sol"; diff --git a/test/utils/BLSMockAVSDeployer.sol b/test/utils/BLSMockAVSDeployer.sol index 14339d55..7923fca4 100644 --- a/test/utils/BLSMockAVSDeployer.sol +++ b/test/utils/BLSMockAVSDeployer.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {BLSSignatureChecker} from "../../src/BLSSignatureChecker.sol"; import {MockAVSDeployer} from "../utils/MockAVSDeployer.sol"; diff --git a/test/utils/MockAVSDeployer.sol b/test/utils/MockAVSDeployer.sol index 226502ed..800628da 100644 --- a/test/utils/MockAVSDeployer.sol +++ b/test/utils/MockAVSDeployer.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; diff --git a/test/utils/Operators.sol b/test/utils/Operators.sol index 95ab65e9..266ed8d8 100644 --- a/test/utils/Operators.sol +++ b/test/utils/Operators.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "../../src/libraries/BN254.sol"; import "forge-std/Test.sol"; @@ -50,12 +50,12 @@ contract Operators is Test { X: [ readUint(operatorConfigJson, index, "PubkeyG2.X.A1"), readUint(operatorConfigJson, index, "PubkeyG2.X.A0") - ], + ], Y: [ readUint(operatorConfigJson, index, "PubkeyG2.Y.A1"), readUint(operatorConfigJson, index, "PubkeyG2.Y.A0") ] - }); + }); return pubkey; } @@ -68,7 +68,7 @@ contract Operators is Test { uint256 result = 0; for (uint256 i = 0; i < b.length; i++) { if (uint256(uint8(b[i])) >= 48 && uint256(uint8(b[i])) <= 57) { - result = result * 10 + (uint256(uint8(b[i])) - 48); + result = result * 10 + (uint256(uint8(b[i])) - 48); } } return result; diff --git a/test/utils/Owners.sol b/test/utils/Owners.sol index edb577d4..cdb3add2 100644 --- a/test/utils/Owners.sol +++ b/test/utils/Owners.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "forge-std/Test.sol"; import "forge-std/Script.sol"; @@ -29,7 +29,7 @@ contract Owners is Test { for (uint256 i = 0; i < getNumOperators(); i++) { addresses.push(getOwnerAddress(i)); } - return addresses; + return addresses; } function getReputedOwnerAddresses() public returns(address[] memory) { @@ -37,11 +37,11 @@ contract Owners is Test { for (uint256 i = 0; i < getNumOperators(); i++) { addresses.push(getOwnerAddress(i)); } - return addresses; + return addresses; } function resetOwnersConfigJson(string memory newConfig) public { ownersConfigJson = vm.readFile(newConfig); } - + } diff --git a/test/utils/ProofParsing.sol b/test/utils/ProofParsing.sol index f331c2a1..14ad89c6 100644 --- a/test/utils/ProofParsing.sol +++ b/test/utils/ProofParsing.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "../../src/libraries/BN254.sol"; import "forge-std/Test.sol"; @@ -83,7 +83,7 @@ contract ProofParsing is Test{ function getExecutionPayloadProof () public returns(bytes32[7] memory) { for (uint i = 0; i < 7; i++) { prefix = string.concat(".ExecutionPayloadProof[", string.concat(vm.toString(i), "]")); - executionPayloadProof[i] = (stdJson.readBytes32(proofConfigJson, prefix)); + executionPayloadProof[i] = (stdJson.readBytes32(proofConfigJson, prefix)); } return executionPayloadProof; } @@ -91,7 +91,7 @@ contract ProofParsing is Test{ function getTimestampProof() public returns(bytes32[4] memory) { for (uint i = 0; i < 4; i++) { prefix = string.concat(".TimestampProof[", string.concat(vm.toString(i), "]")); - timestampProofs[i] = (stdJson.readBytes32(proofConfigJson, prefix)); + timestampProofs[i] = (stdJson.readBytes32(proofConfigJson, prefix)); } return timestampProofs; } @@ -99,7 +99,7 @@ contract ProofParsing is Test{ function getBlockHeaderProof() public returns(bytes32[18] memory) { for (uint i = 0; i < 18; i++) { prefix = string.concat(".BlockHeaderProof[", string.concat(vm.toString(i), "]")); - blockHeaderProof[i] = (stdJson.readBytes32(proofConfigJson, prefix)); + blockHeaderProof[i] = (stdJson.readBytes32(proofConfigJson, prefix)); } return blockHeaderProof; } @@ -107,7 +107,7 @@ contract ProofParsing is Test{ function getSlotProof() public returns(bytes32[3] memory) { for (uint i = 0; i < 3; i++) { prefix = string.concat(".SlotProof[", string.concat(vm.toString(i), "]")); - slotProof[i] = (stdJson.readBytes32(proofConfigJson, prefix)); + slotProof[i] = (stdJson.readBytes32(proofConfigJson, prefix)); } return slotProof; } @@ -116,7 +116,7 @@ contract ProofParsing is Test{ bytes32[] memory stateRootProof = new bytes32[](3); for (uint i = 0; i < 3; i++) { prefix = string.concat(".StateRootAgainstLatestBlockHeaderProof[", string.concat(vm.toString(i), "]")); - stateRootProof[i] = (stdJson.readBytes32(proofConfigJson, prefix)); + stateRootProof[i] = (stdJson.readBytes32(proofConfigJson, prefix)); } return stateRootProof; } @@ -124,7 +124,7 @@ contract ProofParsing is Test{ function getWithdrawalProof() public returns(bytes32[9] memory) { for (uint i = 0; i < 9; i++) { prefix = string.concat(".WithdrawalProof[", string.concat(vm.toString(i), "]")); - withdrawalProof[i] = (stdJson.readBytes32(proofConfigJson, prefix)); + withdrawalProof[i] = (stdJson.readBytes32(proofConfigJson, prefix)); } return withdrawalProof; } @@ -132,7 +132,7 @@ contract ProofParsing is Test{ function getValidatorProof() public returns(bytes32[46] memory) { for (uint i = 0; i < 46; i++) { prefix = string.concat(".ValidatorProof[", string.concat(vm.toString(i), "]")); - validatorProof[i] = (stdJson.readBytes32(proofConfigJson, prefix)); + validatorProof[i] = (stdJson.readBytes32(proofConfigJson, prefix)); } return validatorProof; } @@ -140,16 +140,16 @@ contract ProofParsing is Test{ function getHistoricalSummaryProof() public returns(bytes32[44] memory) { for (uint i = 0; i < 44; i++) { prefix = string.concat(".HistoricalSummaryProof[", string.concat(vm.toString(i), "]")); - historicalSummaryProof[i] = (stdJson.readBytes32(proofConfigJson, prefix)); + historicalSummaryProof[i] = (stdJson.readBytes32(proofConfigJson, prefix)); } return historicalSummaryProof; } - + function getWithdrawalFields() public returns(bytes32[] memory) { bytes32[] memory withdrawalFields = new bytes32[](4); for (uint i = 0; i < 4; i++) { prefix = string.concat(".WithdrawalFields[", string.concat(vm.toString(i), "]")); - withdrawalFields[i] = (stdJson.readBytes32(proofConfigJson, prefix)); + withdrawalFields[i] = (stdJson.readBytes32(proofConfigJson, prefix)); } return withdrawalFields; @@ -159,7 +159,7 @@ contract ProofParsing is Test{ bytes32[] memory validatorFields = new bytes32[](8); for (uint i = 0; i < 8; i++) { prefix = string.concat(".ValidatorFields[", string.concat(vm.toString(i), "]")); - validatorFields[i] = (stdJson.readBytes32(proofConfigJson, prefix)); + validatorFields[i] = (stdJson.readBytes32(proofConfigJson, prefix)); } return validatorFields; } @@ -168,7 +168,7 @@ contract ProofParsing is Test{ bytes32[] memory validatorBalanceProof = new bytes32[](44); for (uint i = 0; i < 44; i++) { prefix = string.concat(".ValidatorBalanceProof[", string.concat(vm.toString(i), "]")); - validatorBalanceProof[i] = (stdJson.readBytes32(proofConfigJson, prefix)); + validatorBalanceProof[i] = (stdJson.readBytes32(proofConfigJson, prefix)); } return validatorBalanceProof; } @@ -177,7 +177,7 @@ contract ProofParsing is Test{ bytes32[] memory balanceUpdateSlotProof = new bytes32[](5); for (uint i = 0; i < 5; i++) { prefix = string.concat(".slotProof[", string.concat(vm.toString(i), "]")); - balanceUpdateSlotProof[i] = (stdJson.readBytes32(proofConfigJson, prefix)); + balanceUpdateSlotProof[i] = (stdJson.readBytes32(proofConfigJson, prefix)); } return balanceUpdateSlotProof; } @@ -186,7 +186,7 @@ contract ProofParsing is Test{ bytes32[] memory withdrawalCredenitalProof = new bytes32[](46); for (uint i = 0; i < 46; i++) { prefix = string.concat(".WithdrawalCredentialProof[", string.concat(vm.toString(i), "]")); - withdrawalCredenitalProof[i] = (stdJson.readBytes32(proofConfigJson, prefix)); + withdrawalCredenitalProof[i] = (stdJson.readBytes32(proofConfigJson, prefix)); } return withdrawalCredenitalProof; } @@ -195,7 +195,7 @@ contract ProofParsing is Test{ bytes32[] memory validatorFieldsProof = new bytes32[](46); for (uint i = 0; i < 46; i++) { prefix = string.concat(".ValidatorFieldsProof[", string.concat(vm.toString(i), "]")); - validatorFieldsProof[i] = (stdJson.readBytes32(proofConfigJson, prefix)); + validatorFieldsProof[i] = (stdJson.readBytes32(proofConfigJson, prefix)); } return validatorFieldsProof; } diff --git a/test/utils/SignatureCompaction.sol b/test/utils/SignatureCompaction.sol index c10b2db1..0bb98a1c 100644 --- a/test/utils/SignatureCompaction.sol +++ b/test/utils/SignatureCompaction.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; From 94095f6320c34c997e452f8ac371f6472c2002f3 Mon Sep 17 00:00:00 2001 From: steven <12021290+stevennevins@users.noreply.github.com> Date: Fri, 13 Dec 2024 11:58:32 -0500 Subject: [PATCH 37/80] feat: custom require errors for registry coordinator (#337) * feat: custom require errors for registry coordinator * ci: update the ci to use the correct compiler settings --- .github/workflows/tests.yml | 3 - foundry.toml | 4 +- src/RegistryCoordinator.sol | 78 +++++++++++++------------ src/interfaces/IRegistryCoordinator.sol | 26 ++++++++- test/unit/RegistryCoordinatorUnit.t.sol | 46 +++++++-------- 5 files changed, 91 insertions(+), 66 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 9ce44c3e..660c8895 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -13,9 +13,6 @@ on: - dev pull_request: -env: - FOUNDRY_PROFILE: ci - jobs: check: strategy: diff --git a/foundry.toml b/foundry.toml index e3775cb1..3d0d84cb 100644 --- a/foundry.toml +++ b/foundry.toml @@ -9,7 +9,9 @@ ffi = true no-match-contract = "FFI" # Enables or disables the optimizer -optimizer = false +optimizer = true +# Sets the number of optimizer runs +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`) diff --git a/src/RegistryCoordinator.sol b/src/RegistryCoordinator.sol index 83becff6..99073714 100644 --- a/src/RegistryCoordinator.sol +++ b/src/RegistryCoordinator.sol @@ -27,7 +27,6 @@ import {Pausable} from "eigenlayer-contracts/src/contracts/permissions/Pausable. import {RegistryCoordinatorStorage} from "./RegistryCoordinatorStorage.sol"; import {IAVSRegistrar} from "eigenlayer-contracts/src/contracts/interfaces/IAVSRegistrar.sol"; - /** * @title A `RegistryCoordinator` that has three registries: * 1) a `StakeRegistry` that keeps track of operators' stakes @@ -102,7 +101,7 @@ contract RegistryCoordinator is && _minimumStakes.length == _strategyParams.length && _strategyParams.length == _stakeTypes.length && _stakeTypes.length == _lookAheadPeriods.length, - "RegistryCoordinator.initialize: input length mismatch" + InputLengthMismatch() ); // Initialize roles @@ -144,7 +143,7 @@ contract RegistryCoordinator is IBLSApkRegistry.PubkeyRegistrationParams memory params, SignatureWithSaltAndExpiry memory operatorSignature ) external onlyWhenNotPaused(PAUSED_REGISTER_OPERATOR) { - require(!isUsingOperatorSets(), "RegistryCoordinator.registerOperator: operator sets enabled"); + require(!isUsingOperatorSets(), OperatorSetsEnabled()); /** * If the operator has NEVER registered a pubkey before, use `params` to register * their pubkey in blsApkRegistry @@ -171,7 +170,7 @@ contract RegistryCoordinator is require( numOperatorsPerQuorum[i] <= _quorumParams[quorumNumber].maxOperatorCount, - "RegistryCoordinator.registerOperator: operator exceeds max" + MaxQuorumsReached() ); } } @@ -196,10 +195,10 @@ contract RegistryCoordinator is SignatureWithSaltAndExpiry memory churnApproverSignature, SignatureWithSaltAndExpiry memory operatorSignature ) external onlyWhenNotPaused(PAUSED_REGISTER_OPERATOR) { - require(!isUsingOperatorSets(), "RegistryCoordinator.registerOperatorWithChurn: operator sets not supported"); + require(!isUsingOperatorSets(), OperatorSetsEnabled()); require( operatorKickParams.length == quorumNumbers.length, - "RegistryCoordinator.registerOperatorWithChurn: input length mismatch" + InputLengthMismatch() ); /** @@ -268,7 +267,7 @@ contract RegistryCoordinator is uint8 quorumNumber = uint8(quorumNumbers[i]); require( !isOperatorSetAVS || isM2Quorum[quorumNumber], - "RegistryCoordinator.deregisterOperator: cannot deregister from non-M2 quorum after operator sets enabled" + OperatorSetsEnabled() ); } _deregisterOperator({operator: msg.sender, quorumNumbers: quorumNumbers}); @@ -301,11 +300,11 @@ contract RegistryCoordinator is uint32[] memory operatorSetIds, bytes memory data ) external override onlyWhenNotPaused(PAUSED_REGISTER_OPERATOR) { - require(isUsingOperatorSets(), "RegistryCoordinator.registerOperator: operator sets not enabled"); + require(isUsingOperatorSets(), OperatorSetsNotEnabled()); for (uint256 i = 0; i < operatorSetIds.length; i++) { - require(!isM2Quorum[uint8(operatorSetIds[i])], "RegistryCoordinator.registerOperator: cannot register for M2 quorum"); + require(!isM2Quorum[uint8(operatorSetIds[i])], OperatorSetsNotSupported()); } - require(msg.sender == address(serviceManager.allocationManager()), "Only allocation manager can register operators"); + require(msg.sender == address(serviceManager.allocationManager()), OnlyAllocationManager()); // Decode registration data from bytes ( @@ -333,11 +332,11 @@ contract RegistryCoordinator is address operator, uint32[] memory operatorSetIds ) external override onlyWhenNotPaused(PAUSED_REGISTER_OPERATOR) { - require(isUsingOperatorSets(), "RegistryCoordinator.deregisterOperator: operator sets not enabled"); + require(isUsingOperatorSets(), OperatorSetsNotEnabled()); for (uint256 i = 0; i < operatorSetIds.length; i++) { - require(!isM2Quorum[uint8(operatorSetIds[i])], "RegistryCoordinator.deregisterOperator: cannot deregister from M2 quorum"); + require(!isM2Quorum[uint8(operatorSetIds[i])], OperatorSetsNotSupported()); } - require(msg.sender == address(serviceManager.allocationManager()), "Only allocation manager can register operators"); + require(msg.sender == address(serviceManager.allocationManager()), OnlyAllocationManager()); bytes memory quorumNumbers = new bytes(operatorSetIds.length); for (uint256 i = 0; i < operatorSetIds.length; i++) { quorumNumbers[i] = bytes1(uint8(operatorSetIds[i])); @@ -394,7 +393,7 @@ contract RegistryCoordinator is uint192(BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers, quorumCount)); require( operatorsPerQuorum.length == quorumNumbers.length, - "RegistryCoordinator.updateOperatorsForQuorum: input length mismatch" + InputLengthMismatch() ); // For each quorum, update ALL registered operators @@ -405,7 +404,7 @@ contract RegistryCoordinator is address[] memory currQuorumOperators = operatorsPerQuorum[i]; require( currQuorumOperators.length == indexRegistry.totalOperatorsForQuorum(quorumNumber), - "RegistryCoordinator.updateOperatorsForQuorum: number of updated operators does not match quorum total" + QuorumOperatorCountMismatch() ); address prevOperatorAddress = address(0); @@ -424,12 +423,12 @@ contract RegistryCoordinator is // Check that the operator is registered require( BitmapUtils.isSet(currentBitmap, quorumNumber), - "RegistryCoordinator.updateOperatorsForQuorum: operator not in quorum" + NotRegisteredForQuorum() ); // Prevent duplicate operators require( operator > prevOperatorAddress, - "RegistryCoordinator.updateOperatorsForQuorum: operators must be sorted" + NotSorted() ); } @@ -451,7 +450,7 @@ contract RegistryCoordinator is function updateSocket(string memory socket) external { require( _operatorInfo[msg.sender].status == OperatorStatus.REGISTERED, - "RegistryCoordinator.updateSocket: not registered" + NotRegistered() ); emit OperatorSocketUpdate(_operatorInfo[msg.sender].operatorId, socket); } @@ -515,7 +514,7 @@ contract RegistryCoordinator is IStakeRegistry.StrategyParams[] memory strategyParams, uint32 lookAheadPeriod ) external virtual onlyOwner { - require(isUsingOperatorSets(), "RegistryCoordinator.createSlashableStakeQuorum: operator sets not enabled"); + require(isUsingOperatorSets(), OperatorSetsNotEnabled()); _createQuorum(operatorSetParams, minimumStake, strategyParams, StakeType.TOTAL_SLASHABLE, lookAheadPeriod); } @@ -595,18 +594,18 @@ contract RegistryCoordinator is uint192(BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers, quorumCount)); uint192 currentBitmap = _currentOperatorBitmap(operatorId); require( - !quorumsToAdd.isEmpty(), "RegistryCoordinator._registerOperator: bitmap empty" + !quorumsToAdd.isEmpty(), BitmapEmpty() ); require( quorumsToAdd.noBitsInCommon(currentBitmap), - "RegistryCoordinator._registerOperator: operator already registered for some quorums being registered for" + AlreadyRegisteredForQuorums() ); 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" + CannotReregisterYet() ); /** @@ -658,18 +657,18 @@ contract RegistryCoordinator is uint192(BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers, quorumCount)); uint192 currentBitmap = _currentOperatorBitmap(operatorId); require( - !quorumsToAdd.isEmpty(), "RegistryCoordinator._registerOperatorToOperatorSet: bitmap empty" + !quorumsToAdd.isEmpty(), BitmapEmpty() ); require( quorumsToAdd.noBitsInCommon(currentBitmap), - "RegistryCoordinator._registerOperatorToOperatorSet: operator already registered for some quorums being registered for" + AlreadyRegisteredForQuorums() ); uint192 newBitmap = uint192(currentBitmap.plus(quorumsToAdd)); // Check that the operator can reregister if ejected require( lastEjectionTimestamp[operator] + ejectionCooldown < block.timestamp, - "RegistryCoordinator._registerOperatorToOperatorSet: operator cannot reregister yet" + CannotReregisterYet() ); /** @@ -700,13 +699,14 @@ contract RegistryCoordinator is * @dev Reverts if the caller is not the ejector */ function _checkEjector() internal view { - require(msg.sender == ejector, "RegistryCoordinator.onlyEjector: not ejector"); + require(msg.sender == ejector, OnlyEjector()); } function _checkAllocationManager() internal view { address allocationManager = address(serviceManager.allocationManager()); - require(msg.sender == allocationManager, "RegistryCoordinator.onlyAllocationManager: not allocation manager"); + require(msg.sender == allocationManager, OnlyAllocationManager()); } + /** * @notice Checks if a quorum exists * @param quorumNumber The quorum number to check @@ -714,7 +714,8 @@ contract RegistryCoordinator is */ function _checkQuorumExists(uint8 quorumNumber) internal view { require( - quorumNumber < quorumCount, "RegistryCoordinator.quorumExists: quorum does not exist" + quorumNumber < quorumCount, + QuorumDoesNotExist() ); } @@ -768,22 +769,23 @@ contract RegistryCoordinator is address operatorToKick = kickParams.operator; bytes32 idToKick = _operatorInfo[operatorToKick].operatorId; require( - newOperator != operatorToKick, "RegistryCoordinator._validateChurn: cannot churn self" + newOperator != operatorToKick, + CannotChurnSelf() ); require( kickParams.quorumNumber == quorumNumber, - "RegistryCoordinator._validateChurn: quorumNumber not the same as signed" + QuorumOperatorCountMismatch() ); // Get the target operator's stake and check that it is below the kick thresholds uint96 operatorToKickStake = stakeRegistry.getCurrentStake(idToKick, quorumNumber); require( newOperatorStake > _individualKickThreshold(operatorToKickStake, setParams), - "RegistryCoordinator._validateChurn: incoming operator has insufficient stake for churn" + InsufficientStakeForChurn() ); require( operatorToKickStake < _totalKickThreshold(totalQuorumStake, setParams), - "RegistryCoordinator._validateChurn: cannot kick operator with more than kickBIPsOfTotalStake" + CannotKickOperatorAboveThreshold() ); } @@ -798,7 +800,7 @@ contract RegistryCoordinator is bytes32 operatorId = operatorInfo.operatorId; require( operatorInfo.status == OperatorStatus.REGISTERED, - "RegistryCoordinator._deregisterOperator: not registered" + NotRegistered() ); /** @@ -813,11 +815,11 @@ contract RegistryCoordinator is uint192 currentBitmap = _currentOperatorBitmap(operatorId); require( !quorumsToRemove.isEmpty(), - "RegistryCoordinator._deregisterOperator: bitmap cannot be 0" + BitmapCannotBeZero() ); require( quorumsToRemove.isSubsetOf(currentBitmap), - "RegistryCoordinator._deregisterOperator: not registered for quorum" + NotRegisteredForQuorum() ); uint192 newBitmap = uint192(currentBitmap.minus(quorumsToRemove)); @@ -896,11 +898,11 @@ contract RegistryCoordinator is // make sure the salt hasn't been used already require( !isChurnApproverSaltUsed[churnApproverSignature.salt], - "RegistryCoordinator._verifyChurnApproverSignature: salt spent" + ChurnApproverSaltUsed() ); require( churnApproverSignature.expiry >= block.timestamp, - "RegistryCoordinator._verifyChurnApproverSignature: signature expired" + SignatureExpired() ); // set salt used to true @@ -939,7 +941,7 @@ contract RegistryCoordinator is uint8 prevQuorumCount = quorumCount; require( prevQuorumCount < MAX_QUORUM_COUNT, - "RegistryCoordinator.createQuorum: max quorums reached" + MaxQuorumsReached() ); quorumCount = prevQuorumCount + 1; diff --git a/src/interfaces/IRegistryCoordinator.sol b/src/interfaces/IRegistryCoordinator.sol index e3d0a4dd..4ce2e917 100644 --- a/src/interfaces/IRegistryCoordinator.sol +++ b/src/interfaces/IRegistryCoordinator.sol @@ -7,11 +7,35 @@ import {IStakeRegistry} from "./IStakeRegistry.sol"; import {IIndexRegistry} from "./IIndexRegistry.sol"; import {BN254} from "../libraries/BN254.sol"; +interface IRegistryCoordinatorErrors { + error InputLengthMismatch(); + error OperatorSetsEnabled(); + error OperatorSetsNotEnabled(); + error OperatorSetsNotSupported(); + error OnlyAllocationManager(); + error OnlyEjector(); + error QuorumDoesNotExist(); + error BitmapEmpty(); + error AlreadyRegisteredForQuorums(); + error CannotReregisterYet(); + error NotRegistered(); + error CannotChurnSelf(); + error QuorumOperatorCountMismatch(); + error InsufficientStakeForChurn(); + error CannotKickOperatorAboveThreshold(); + error BitmapCannotBeZero(); + error NotRegisteredForQuorum(); + error MaxQuorumsReached(); + error SaltAlreadyUsed(); + error RegistryCoordinatorSignatureExpired(); + error ChurnApproverSaltUsed(); + error NotSorted(); +} /** * @title Interface for a contract that coordinates between various registries for an AVS. * @author Layr Labs, Inc. */ -interface IRegistryCoordinator { +interface IRegistryCoordinator is IRegistryCoordinatorErrors{ // EVENTS /// Emits when an operator is registered diff --git a/test/unit/RegistryCoordinatorUnit.t.sol b/test/unit/RegistryCoordinatorUnit.t.sol index 2ee48849..c3c8032d 100644 --- a/test/unit/RegistryCoordinatorUnit.t.sol +++ b/test/unit/RegistryCoordinatorUnit.t.sol @@ -2,6 +2,7 @@ pragma solidity ^0.8.27; import "../utils/MockAVSDeployer.sol"; +import {IRegistryCoordinatorErrors} from "../../src/interfaces/IRegistryCoordinator.sol"; contract RegistryCoordinatorUnitTests is MockAVSDeployer { using BN254 for BN254.G1Point; @@ -120,7 +121,7 @@ contract RegistryCoordinatorUnitTests_Initialization_Setters is RegistryCoordina ); } - // make sure the contract intializers are disabled + // make sure the contract initializers are disabled cheats.expectRevert(bytes("Initializable: contract is already initialized")); registryCoordinator.initialize( registryCoordinatorOwner, @@ -198,7 +199,7 @@ contract RegistryCoordinatorUnitTests_Initialization_Setters is RegistryCoordina function test_updateSocket_revert_notRegistered() public { cheats.prank(defaultOperator); - cheats.expectRevert("RegistryCoordinator.updateSocket: not registered"); + cheats.expectRevert(bytes4(keccak256("NotRegistered()"))); registryCoordinator.updateSocket("localhost:32004"); } @@ -269,7 +270,7 @@ contract RegistryCoordinatorUnitTests_RegisterOperator is RegistryCoordinatorUni bytes memory emptyQuorumNumbers = new bytes(0); ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; - cheats.expectRevert("RegistryCoordinator._registerOperator: bitmap empty"); + cheats.expectRevert(bytes4(keccak256("BitmapEmpty()"))); cheats.prank(defaultOperator); registryCoordinator.registerOperator(emptyQuorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); } @@ -483,7 +484,7 @@ contract RegistryCoordinatorUnitTests_RegisterOperator is RegistryCoordinatorUni _setOperatorWeight(operatorToRegister, defaultQuorumNumber, defaultStake); cheats.prank(operatorToRegister); - cheats.expectRevert("RegistryCoordinator.registerOperator: operator exceeds max"); + cheats.expectRevert(bytes4(keccak256("MaxQuorumsReached()"))); registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); } @@ -503,8 +504,7 @@ contract RegistryCoordinatorUnitTests_RegisterOperator is RegistryCoordinatorUni cheats.prank(defaultOperator); cheats.roll(nextRegistrationBlockNumber); - cheats.expectRevert("RegistryCoordinator._registerOperator: operator already registered for some quorums being registered for"); - + cheats.expectRevert(bytes4(keccak256("AlreadyRegisteredForQuorums()"))); registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); } @@ -513,7 +513,7 @@ contract RegistryCoordinatorUnitTests_RegisterOperator is RegistryCoordinatorUni bytes memory emptyQuorumNumbers = new bytes(0); ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; - cheats.expectRevert("RegistryCoordinator._registerOperator: bitmap empty"); + cheats.expectRevert(bytes4(keccak256("BitmapEmpty()"))); registryCoordinator._registerOperatorExternal(defaultOperator, defaultOperatorId, emptyQuorumNumbers, defaultSocket, emptySig); } @@ -535,7 +535,7 @@ contract RegistryCoordinatorUnitTests_RegisterOperator is RegistryCoordinatorUni _setOperatorWeight(defaultOperator, uint8(quorumNumbers[0]), defaultStake); registryCoordinator._registerOperatorExternal(defaultOperator, defaultOperatorId, quorumNumbers, defaultSocket, emptySig); - cheats.expectRevert("RegistryCoordinator._registerOperator: operator already registered for some quorums being registered for"); + cheats.expectRevert(bytes4(keccak256("AlreadyRegisteredForQuorums()"))); registryCoordinator._registerOperatorExternal(defaultOperator, defaultOperatorId, quorumNumbers, defaultSocket, emptySig); } @@ -601,7 +601,7 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist bytes memory quorumNumbers = new bytes(1); quorumNumbers[0] = bytes1(defaultQuorumNumber); - cheats.expectRevert("RegistryCoordinator._deregisterOperator: not registered"); + cheats.expectRevert(bytes4(keccak256("NotRegistered()"))); cheats.prank(defaultOperator); registryCoordinator.deregisterOperator(quorumNumbers); } @@ -617,7 +617,7 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist quorumNumbers[0] = bytes1(defaultQuorumNumber + 1); quorumNumbers[1] = bytes1(defaultQuorumNumber + 2); - cheats.expectRevert("RegistryCoordinator._deregisterOperator: not registered for quorum"); + cheats.expectRevert(bytes4(keccak256("NotRegisteredForQuorum()"))); cheats.prank(defaultOperator); registryCoordinator.deregisterOperator(quorumNumbers); } @@ -957,13 +957,13 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist bytes memory emptyQuorumNumbers = new bytes(0); cheats.roll(deregistrationBlockNumber); - cheats.expectRevert("RegistryCoordinator._deregisterOperator: bitmap cannot be 0"); + cheats.expectRevert(bytes4(keccak256("BitmapCannotBeZero()"))); registryCoordinator._deregisterOperatorExternal(defaultOperator, emptyQuorumNumbers); } function test_deregisterOperatorExternal_revert_notRegistered() public { bytes memory emptyQuorumNumbers = new bytes(0); - cheats.expectRevert("RegistryCoordinator._deregisterOperator: not registered"); + cheats.expectRevert(bytes4(keccak256("NotRegistered()"))); registryCoordinator._deregisterOperatorExternal(defaultOperator, emptyQuorumNumbers); } @@ -985,7 +985,7 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist incorrectQuorum[0] = bytes1(defaultQuorumNumber + 1); cheats.roll(deregistrationBlockNumber); - cheats.expectRevert("RegistryCoordinator._deregisterOperator: not registered for quorum"); + cheats.expectRevert(bytes4(keccak256("NotRegisteredForQuorum()"))); registryCoordinator._deregisterOperatorExternal(defaultOperator, incorrectQuorum); } @@ -1013,7 +1013,7 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist cheats.prank(defaultOperator); cheats.roll(reregistrationBlockNumber); - cheats.expectRevert("RegistryCoordinator._registerOperator: operator cannot reregister yet"); + cheats.expectRevert(bytes4(keccak256("CannotReregisterYet()"))); registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); } @@ -1214,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.expectRevert(bytes4(keccak256("OnlyEjector()"))); cheats.prank(defaultOperator); registryCoordinator.ejectOperator(defaultOperator, quorumNumbers); } @@ -1463,7 +1463,7 @@ contract RegistryCoordinatorUnitTests_RegisterOperatorWithChurn is RegistryCoord ISignatureUtils.SignatureWithSaltAndExpiry memory signatureWithExpiry = _signOperatorChurnApproval(operatorToRegister, operatorToRegisterId, operatorKickParams, defaultSalt, block.timestamp + 10); cheats.prank(operatorToRegister); - cheats.expectRevert("RegistryCoordinator._validateChurn: incoming operator has insufficient stake for churn"); + cheats.expectRevert(bytes4(keccak256("InsufficientStakeForChurn()"))); registryCoordinator.registerOperatorWithChurn( quorumNumbers, defaultSocket, @@ -1495,7 +1495,7 @@ contract RegistryCoordinatorUnitTests_RegisterOperatorWithChurn is RegistryCoord ISignatureUtils.SignatureWithSaltAndExpiry memory signatureWithExpiry = _signOperatorChurnApproval(operatorToRegister, operatorToRegisterId, operatorKickParams, defaultSalt, block.timestamp + 10); cheats.prank(operatorToRegister); - cheats.expectRevert("RegistryCoordinator._validateChurn: cannot kick operator with more than kickBIPsOfTotalStake"); + cheats.expectRevert(bytes4(keccak256("CannotKickOperatorAboveThreshold()"))); registryCoordinator.registerOperatorWithChurn( quorumNumbers, defaultSocket, @@ -1557,7 +1557,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: signature expired"); + cheats.expectRevert(bytes4(keccak256("SignatureExpired()"))); registryCoordinator.registerOperatorWithChurn( quorumNumbers, defaultSocket, @@ -1678,7 +1678,7 @@ contract RegistryCoordinatorUnitTests_UpdateOperators is RegistryCoordinatorUnit bytes memory quorumNumbers = new bytes(1); quorumNumbers[0] = bytes1(defaultQuorumNumber); - cheats.expectRevert(bytes("RegistryCoordinator.updateOperatorsForQuorum: input length mismatch")); + cheats.expectRevert(bytes4(keccak256("InputLengthMismatch()"))); registryCoordinator.updateOperatorsForQuorum(operatorsToUpdate, quorumNumbers); } @@ -1690,7 +1690,7 @@ contract RegistryCoordinatorUnitTests_UpdateOperators is RegistryCoordinatorUnit bytes memory quorumNumbers = new bytes(1); quorumNumbers[0] = bytes1(defaultQuorumNumber); - cheats.expectRevert(bytes("RegistryCoordinator.updateOperatorsForQuorum: number of updated operators does not match quorum total")); + cheats.expectRevert(bytes4(keccak256("QuorumOperatorCountMismatch()"))); registryCoordinator.updateOperatorsForQuorum(operatorsToUpdate, quorumNumbers); } @@ -1711,7 +1711,7 @@ contract RegistryCoordinatorUnitTests_UpdateOperators is RegistryCoordinatorUnit operatorArray[0] = _incrementAddress(defaultOperator, 1); operatorsToUpdate[0] = operatorArray; - cheats.expectRevert(bytes("RegistryCoordinator.updateOperatorsForQuorum: operator not in quorum")); + cheats.expectRevert(bytes4(keccak256("NotRegisteredForQuorum()"))); registryCoordinator.updateOperatorsForQuorum(operatorsToUpdate, quorumNumbers); } @@ -1739,7 +1739,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 must be sorted")); + cheats.expectRevert(bytes4(keccak256("NotSorted()"))); registryCoordinator.updateOperatorsForQuorum(operatorsToUpdate, quorumNumbers); } @@ -1765,7 +1765,7 @@ contract RegistryCoordinatorUnitTests_UpdateOperators is RegistryCoordinatorUnit operatorArray[1] = defaultOperator; operatorsToUpdate[0] = operatorArray; - cheats.expectRevert(bytes("RegistryCoordinator.updateOperatorsForQuorum: operators must be sorted")); + cheats.expectRevert(bytes4(keccak256("NotSorted()"))); registryCoordinator.updateOperatorsForQuorum(operatorsToUpdate, quorumNumbers); } From a395a0e19b4923a27ce304038b088c346adc8860 Mon Sep 17 00:00:00 2001 From: steven <12021290+stevennevins@users.noreply.github.com> Date: Mon, 16 Dec 2024 09:25:28 -0500 Subject: [PATCH 38/80] fix: slashing review fixes (#333) * fix: withdrawal delay check * fix: add operator set strategies (#334) * fix: remove registerOperatorToOperatorSet interface function * fix: update interface functions with alm interface * fix: operator set strategies in ALM * chore: remove todo * fix: add strategies by stake registry * fix: params for deregister * chore: remove old migration functions * chore: check quorum exists before setting params * fix: deregister flow for operator set quorums * test: fix test for DM withdrawal delay blocks * chore: update all pragmas to 0.8.27 (#336) * feat: custom require errors for registry coordinator (#337) * feat: custom require errors for registry coordinator * ci: update the ci to use the correct compiler settings --- .github/workflows/tests.yml | 3 - foundry.toml | 4 +- script/ServiceManagerRouterDeploy.s.sol | 2 +- src/BLSApkRegistry.sol | 2 +- src/BLSApkRegistryStorage.sol | 2 +- src/BLSSignatureChecker.sol | 2 +- src/EjectionManager.sol | 2 +- src/IndexRegistry.sol | 2 +- src/IndexRegistryStorage.sol | 4 +- src/OperatorStateRetriever.sol | 46 ++-- src/RegistryCoordinator.sol | 130 +++++------ src/RegistryCoordinatorStorage.sol | 2 +- src/ServiceManagerBase.sol | 203 +++--------------- src/ServiceManagerBaseStorage.sol | 2 +- src/ServiceManagerRouter.sol | 6 +- src/StakeRegistry.sol | 24 ++- src/StakeRegistryStorage.sol | 2 +- src/interfaces/IBLSApkRegistry.sol | 12 +- src/interfaces/IBLSSignatureChecker.sol | 18 +- .../IECDSAStakeRegistryEventsAndErrors.sol | 2 +- src/interfaces/IEjectionManager.sol | 4 +- src/interfaces/IIndexRegistry.sol | 6 +- src/interfaces/IRegistryCoordinator.sol | 37 +++- src/interfaces/IServiceManager.sol | 17 +- src/interfaces/ISlasher.sol | 2 +- src/interfaces/ISocketUpdater.sol | 4 +- src/interfaces/IStakeRegistry.sol | 2 +- src/libraries/BN254.sol | 2 +- src/libraries/BitmapUtils.sol | 12 +- src/libraries/LibMergeSort.sol | 2 +- src/libraries/QuorumBitmapHistoryLib.sol | 2 +- src/libraries/SignatureCheckerLib.sol | 2 +- src/slashers/InstantSlasher.sol | 2 +- src/slashers/VetoableSlasher.sol | 2 +- src/slashers/base/SlasherBase.sol | 2 +- src/slashers/base/SlasherStorage.sol | 2 +- src/unaudited/ECDSAServiceManagerBase.sol | 2 +- src/unaudited/ECDSAStakeRegistry.sol | 2 +- src/unaudited/ECDSAStakeRegistryStorage.sol | 2 +- .../ECDSAStakeRegistryEqualWeight.sol | 2 +- .../ECDSAStakeRegistryPermissioned.sol | 2 +- test/events/IBLSApkRegistryEvents.sol | 4 +- test/events/IIndexRegistryEvents.sol | 2 +- test/events/IServiceManagerBaseEvents.sol | 2 +- test/events/IStakeRegistryEvents.sol | 2 +- test/ffi/BLSPubKeyCompendiumFFI.t.sol | 2 +- test/ffi/BLSSignatureCheckerFFI.t.sol | 22 +- test/ffi/UpdateOperators.t.sol | 4 +- test/ffi/util/G2Operations.sol | 4 +- test/harnesses/AVSDirectoryHarness.sol | 2 +- test/harnesses/BLSApkRegistryHarness.sol | 2 +- test/harnesses/BitmapUtilsWrapper.sol | 2 +- .../RegistryCoordinatorHarness.t.sol | 2 +- test/harnesses/StakeRegistryHarness.sol | 2 +- test/integration/CoreRegistration.t.sol | 2 +- test/integration/IntegrationBase.t.sol | 62 +++--- test/integration/IntegrationChecks.t.sol | 62 +++--- test/integration/IntegrationConfig.t.sol | 2 +- test/integration/IntegrationDeployer.t.sol | 2 +- test/integration/TimeMachine.t.sol | 2 +- test/integration/User.t.sol | 2 +- .../mocks/BeaconChainOracleMock.t.sol | 2 +- .../tests/Full_Register_Deregister.t.sol | 6 +- ...ll_Register_CoreBalanceChange_Update.t.sol | 2 +- .../tests/NonFull_Register_Deregister.t.sol | 2 +- test/integration/utils/BitmapStrings.t.sol | 6 +- test/integration/utils/Sort.t.sol | 2 +- test/mocks/AVSDirectoryMock.sol | 2 +- test/mocks/AllocationManagerMock.sol | 2 +- test/mocks/DelegationMock.sol | 4 +- test/mocks/ECDSAServiceManagerMock.sol | 8 +- test/mocks/ECDSAStakeRegistryMock.sol | 2 +- test/mocks/PermissionControllerMock.sol | 2 +- test/mocks/RegistryCoordinatorMock.sol | 12 +- test/mocks/RewardsCoordinatorMock.sol | 2 +- test/mocks/ServiceManagerMock.sol | 2 +- test/mocks/StakeRegistryMock.sol | 2 +- test/unit/BLSApkRegistryUnit.t.sol | 2 +- test/unit/BLSSignatureCheckerUnit.t.sol | 2 +- test/unit/BitmapUtils.t.sol | 4 +- test/unit/ECDSAServiceManager.t.sol | 2 +- .../ECDSAStakeRegistryEqualWeightUnit.t.sol | 2 +- .../ECDSAStakeRegistryPermissionedUnit.t.sol | 2 +- test/unit/ECDSAStakeRegistryUnit.t.sol | 2 +- test/unit/EjectionManagerUnit.t.sol | 2 +- test/unit/IndexRegistryUnit.t.sol | 2 +- test/unit/LibMergeSort.t.sol | 2 +- test/unit/OperatorStateRetrieverUnit.t.sol | 2 +- test/unit/RegistryCoordinatorUnit.t.sol | 48 ++--- test/unit/ServiceManagerBase.t.sol | 2 +- test/unit/ServiceManagerRouter.t.sol | 2 +- test/unit/StakeRegistryUnit.t.sol | 2 +- test/unit/Utils.sol | 2 +- test/utils/BLSMockAVSDeployer.sol | 2 +- test/utils/MockAVSDeployer.sol | 2 +- test/utils/Operators.sol | 8 +- test/utils/Owners.sol | 8 +- test/utils/ProofParsing.sol | 32 +-- test/utils/SignatureCompaction.sol | 2 +- 99 files changed, 434 insertions(+), 524 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 9ce44c3e..660c8895 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -13,9 +13,6 @@ on: - dev pull_request: -env: - FOUNDRY_PROFILE: ci - jobs: check: strategy: diff --git a/foundry.toml b/foundry.toml index e3775cb1..3d0d84cb 100644 --- a/foundry.toml +++ b/foundry.toml @@ -9,7 +9,9 @@ ffi = true no-match-contract = "FFI" # Enables or disables the optimizer -optimizer = false +optimizer = true +# Sets the number of optimizer runs +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`) diff --git a/script/ServiceManagerRouterDeploy.s.sol b/script/ServiceManagerRouterDeploy.s.sol index 6a61a796..d87315b3 100644 --- a/script/ServiceManagerRouterDeploy.s.sol +++ b/script/ServiceManagerRouterDeploy.s.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {ServiceManagerRouter} from "../src/ServiceManagerRouter.sol"; import "forge-std/Script.sol"; diff --git a/src/BLSApkRegistry.sol b/src/BLSApkRegistry.sol index d7d3c5eb..ec445d3e 100644 --- a/src/BLSApkRegistry.sol +++ b/src/BLSApkRegistry.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {BLSApkRegistryStorage} from "./BLSApkRegistryStorage.sol"; diff --git a/src/BLSApkRegistryStorage.sol b/src/BLSApkRegistryStorage.sol index 9597d5ac..b35b9362 100644 --- a/src/BLSApkRegistryStorage.sol +++ b/src/BLSApkRegistryStorage.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {IBLSApkRegistry} from "./interfaces/IBLSApkRegistry.sol"; import {IRegistryCoordinator} from "./interfaces/IRegistryCoordinator.sol"; diff --git a/src/BLSSignatureChecker.sol b/src/BLSSignatureChecker.sol index 5392289c..77d6fbe6 100644 --- a/src/BLSSignatureChecker.sol +++ b/src/BLSSignatureChecker.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {IBLSSignatureChecker} from "./interfaces/IBLSSignatureChecker.sol"; import {IRegistryCoordinator} from "./interfaces/IRegistryCoordinator.sol"; diff --git a/src/EjectionManager.sol b/src/EjectionManager.sol index 316fcb29..81860fc0 100644 --- a/src/EjectionManager.sol +++ b/src/EjectionManager.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {OwnableUpgradeable} from "@openzeppelin-upgrades/contracts/access/OwnableUpgradeable.sol"; import {IEjectionManager} from "./interfaces/IEjectionManager.sol"; diff --git a/src/IndexRegistry.sol b/src/IndexRegistry.sol index 83e039c8..ce432d64 100644 --- a/src/IndexRegistry.sol +++ b/src/IndexRegistry.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {IndexRegistryStorage} from "./IndexRegistryStorage.sol"; import {IRegistryCoordinator} from "./interfaces/IRegistryCoordinator.sol"; diff --git a/src/IndexRegistryStorage.sol b/src/IndexRegistryStorage.sol index b28a4510..b5b800d6 100644 --- a/src/IndexRegistryStorage.sol +++ b/src/IndexRegistryStorage.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {Initializable} from "@openzeppelin-upgrades/contracts/proxy/utils/Initializable.sol"; @@ -22,7 +22,7 @@ abstract contract IndexRegistryStorage is Initializable, IIndexRegistry { /// @notice maps quorumNumber => operator id => current operatorIndex /// NOTE: This mapping is NOT updated when an operator is deregistered, /// so it's possible that an index retrieved from this mapping is inaccurate. - /// If you're querying for an operator that might be deregistered, ALWAYS + /// If you're querying for an operator that might be deregistered, ALWAYS /// check this index against the latest `_operatorIndexHistory` entry mapping(uint8 => mapping(bytes32 => uint32)) public currentOperatorIndex; /// @notice maps quorumNumber => operatorIndex => historical operator ids at that index diff --git a/src/OperatorStateRetriever.sol b/src/OperatorStateRetriever.sol index f0547a2d..2af9e527 100644 --- a/src/OperatorStateRetriever.sol +++ b/src/OperatorStateRetriever.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {IRegistryCoordinator} from "./interfaces/IRegistryCoordinator.sol"; import {IBLSApkRegistry} from "./interfaces/IBLSApkRegistry.sol"; @@ -22,30 +22,30 @@ contract OperatorStateRetriever { struct CheckSignaturesIndices { uint32[] nonSignerQuorumBitmapIndices; uint32[] quorumApkIndices; - uint32[] totalStakeIndices; + uint32[] totalStakeIndices; uint32[][] nonSignerStakeIndices; // nonSignerStakeIndices[quorumNumberIndex][nonSignerIndex] } /** * @notice This function is intended to to be called by AVS operators every time a new task is created (i.e.) - * the AVS coordinator makes a request to AVS operators. Since all of the crucial information is kept onchain, + * the AVS coordinator makes a request to AVS operators. Since all of the crucial information is kept onchain, * operators don't need to run indexers to fetch the data. * @param registryCoordinator is the registry coordinator to fetch the AVS registry information from - * @param operatorId the id of the operator to fetch the quorums lists + * @param operatorId the id of the operator to fetch the quorums lists * @param blockNumber is the block number to get the operator state for * @return 1) the quorumBitmap of the operator at the given blockNumber - * 2) 2d array of Operator structs. For each quorum the provided operator + * 2) 2d array of Operator structs. For each quorum the provided operator * was a part of at `blockNumber`, an ordered list of operators. */ function getOperatorState( - IRegistryCoordinator registryCoordinator, - bytes32 operatorId, + IRegistryCoordinator registryCoordinator, + bytes32 operatorId, uint32 blockNumber ) external view returns (uint256, Operator[][] memory) { bytes32[] memory operatorIds = new bytes32[](1); operatorIds[0] = operatorId; uint256 index = registryCoordinator.getQuorumBitmapIndicesAtBlockNumber(blockNumber, operatorIds)[0]; - + uint256 quorumBitmap = registryCoordinator.getQuorumBitmapAtBlockNumberByIndex(operatorId, blockNumber, index); bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); @@ -54,7 +54,7 @@ contract OperatorStateRetriever { } /** - * @notice returns the ordered list of operators (id and stake) for each quorum. The AVS coordinator + * @notice returns the ordered list of operators (id and stake) for each quorum. The AVS coordinator * may call this function directly to get the operator state for a given block number * @param registryCoordinator is the registry coordinator to fetch the AVS registry information from * @param quorumNumbers are the ids of the quorums to get the operator state for @@ -62,8 +62,8 @@ contract OperatorStateRetriever { * @return 2d array of Operators. For each quorum, an ordered list of Operators */ function getOperatorState( - IRegistryCoordinator registryCoordinator, - bytes memory quorumNumbers, + IRegistryCoordinator registryCoordinator, + bytes memory quorumNumbers, uint32 blockNumber ) public view returns(Operator[][] memory) { IStakeRegistry stakeRegistry = registryCoordinator.stakeRegistry(); @@ -83,28 +83,28 @@ contract OperatorStateRetriever { }); } } - + return operators; } /** * @notice this is called by the AVS operator to get the relevant indices for the checkSignatures function - * if they are not running an indexer + * if they are not running an indexer * @param registryCoordinator is the registry coordinator to fetch the AVS registry information from * @param referenceBlockNumber is the block number to get the indices for * @param quorumNumbers are the ids of the quorums to get the operator state for * @param nonSignerOperatorIds are the ids of the nonsigning operators * @return 1) the indices of the quorumBitmaps for each of the operators in the @param nonSignerOperatorIds array at the given blocknumber * 2) the indices of the total stakes entries for the given quorums at the given blocknumber - * 3) the indices of the stakes of each of the nonsigners in each of the quorums they were a + * 3) the indices of the stakes of each of the nonsigners in each of the quorums they were a * part of (for each nonsigner, an array of length the number of quorums they were a part of * that are also part of the provided quorumNumbers) at the given blocknumber * 4) the indices of the quorum apks for each of the provided quorums at the given blocknumber */ function getCheckSignaturesIndices( IRegistryCoordinator registryCoordinator, - uint32 referenceBlockNumber, - bytes calldata quorumNumbers, + uint32 referenceBlockNumber, + bytes calldata quorumNumbers, bytes32[] calldata nonSignerOperatorIds ) external view returns (CheckSignaturesIndices memory) { IStakeRegistry stakeRegistry = registryCoordinator.stakeRegistry(); @@ -115,7 +115,7 @@ contract OperatorStateRetriever { // get the indices of the totalStake updates for each of the quorums in the quorumNumbers array checkSignaturesIndices.totalStakeIndices = stakeRegistry.getTotalStakeIndicesAtBlockNumber(referenceBlockNumber, quorumNumbers); - + checkSignaturesIndices.nonSignerStakeIndices = new uint32[][](quorumNumbers.length); for (uint8 quorumNumberIndex = 0; quorumNumberIndex < quorumNumbers.length; quorumNumberIndex++) { uint256 numNonSignersForQuorum = 0; @@ -124,15 +124,15 @@ contract OperatorStateRetriever { for (uint i = 0; i < nonSignerOperatorIds.length; i++) { // get the quorumBitmap for the operator at the given blocknumber and index - uint192 nonSignerQuorumBitmap = + uint192 nonSignerQuorumBitmap = registryCoordinator.getQuorumBitmapAtBlockNumberByIndex( - nonSignerOperatorIds[i], - referenceBlockNumber, + nonSignerOperatorIds[i], + referenceBlockNumber, checkSignaturesIndices.nonSignerQuorumBitmapIndices[i] ); - + require(nonSignerQuorumBitmap != 0, "OperatorStateRetriever.getCheckSignaturesIndices: operator must be registered at blocknumber"); - + // if the operator was a part of the quorum and the quorum is a part of the provided quorumNumbers if ((nonSignerQuorumBitmap >> uint8(quorumNumbers[quorumNumberIndex])) & 1 == 1) { // get the index of the stake update for the operator at the given blocknumber and quorum number @@ -210,5 +210,5 @@ contract OperatorStateRetriever { operators[i] = registryCoordinator.getOperatorFromId(operatorIds[i]); } } - + } diff --git a/src/RegistryCoordinator.sol b/src/RegistryCoordinator.sol index 884f99cd..99073714 100644 --- a/src/RegistryCoordinator.sol +++ b/src/RegistryCoordinator.sol @@ -1,10 +1,11 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; 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 { OperatorSet} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; +import {IStrategy } from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; +import { IAllocationManager, OperatorSet, IAllocationManagerTypes} 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"; @@ -26,7 +27,6 @@ import {Pausable} from "eigenlayer-contracts/src/contracts/permissions/Pausable. import {RegistryCoordinatorStorage} from "./RegistryCoordinatorStorage.sol"; import {IAVSRegistrar} from "eigenlayer-contracts/src/contracts/interfaces/IAVSRegistrar.sol"; - /** * @title A `RegistryCoordinator` that has three registries: * 1) a `StakeRegistry` that keeps track of operators' stakes @@ -101,7 +101,7 @@ contract RegistryCoordinator is && _minimumStakes.length == _strategyParams.length && _strategyParams.length == _stakeTypes.length && _stakeTypes.length == _lookAheadPeriods.length, - "RegistryCoordinator.initialize: input length mismatch" + InputLengthMismatch() ); // Initialize roles @@ -143,7 +143,7 @@ contract RegistryCoordinator is IBLSApkRegistry.PubkeyRegistrationParams memory params, SignatureWithSaltAndExpiry memory operatorSignature ) external onlyWhenNotPaused(PAUSED_REGISTER_OPERATOR) { - require(!isUsingOperatorSets(), "RegistryCoordinator.registerOperator: operator sets enabled"); + require(!isUsingOperatorSets(), OperatorSetsEnabled()); /** * If the operator has NEVER registered a pubkey before, use `params` to register * their pubkey in blsApkRegistry @@ -170,7 +170,7 @@ contract RegistryCoordinator is require( numOperatorsPerQuorum[i] <= _quorumParams[quorumNumber].maxOperatorCount, - "RegistryCoordinator.registerOperator: operator exceeds max" + MaxQuorumsReached() ); } } @@ -195,10 +195,10 @@ contract RegistryCoordinator is SignatureWithSaltAndExpiry memory churnApproverSignature, SignatureWithSaltAndExpiry memory operatorSignature ) external onlyWhenNotPaused(PAUSED_REGISTER_OPERATOR) { - require(!isUsingOperatorSets(), "RegistryCoordinator.registerOperatorWithChurn: operator sets not supported"); + require(!isUsingOperatorSets(), OperatorSetsEnabled()); require( operatorKickParams.length == quorumNumbers.length, - "RegistryCoordinator.registerOperatorWithChurn: input length mismatch" + InputLengthMismatch() ); /** @@ -267,7 +267,7 @@ contract RegistryCoordinator is uint8 quorumNumber = uint8(quorumNumbers[i]); require( !isOperatorSetAVS || isM2Quorum[quorumNumber], - "RegistryCoordinator.deregisterOperator: cannot deregister from non-M2 quorum after operator sets enabled" + OperatorSetsEnabled() ); } _deregisterOperator({operator: msg.sender, quorumNumbers: quorumNumbers}); @@ -300,11 +300,11 @@ contract RegistryCoordinator is uint32[] memory operatorSetIds, bytes memory data ) external override onlyWhenNotPaused(PAUSED_REGISTER_OPERATOR) { - require(isUsingOperatorSets(), "RegistryCoordinator.registerOperator: operator sets not enabled"); + require(isUsingOperatorSets(), OperatorSetsNotEnabled()); for (uint256 i = 0; i < operatorSetIds.length; i++) { - require(!isM2Quorum[uint8(operatorSetIds[i])], "RegistryCoordinator.registerOperator: cannot register for M2 quorum"); + require(!isM2Quorum[uint8(operatorSetIds[i])], OperatorSetsNotSupported()); } - require(msg.sender == address(serviceManager.allocationManager()), "Only allocation manager can register operators"); + require(msg.sender == address(serviceManager.allocationManager()), OnlyAllocationManager()); // Decode registration data from bytes ( @@ -326,22 +326,17 @@ contract RegistryCoordinator is quorumNumbers: quorumNumbers, socket: socket }); - - /// 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 onlyWhenNotPaused(PAUSED_REGISTER_OPERATOR) { - require(isUsingOperatorSets(), "RegistryCoordinator.deregisterOperator: operator sets not enabled"); + require(isUsingOperatorSets(), OperatorSetsNotEnabled()); for (uint256 i = 0; i < operatorSetIds.length; i++) { - require(!isM2Quorum[uint8(operatorSetIds[i])], "RegistryCoordinator.deregisterOperator: cannot deregister from M2 quorum"); + require(!isM2Quorum[uint8(operatorSetIds[i])], OperatorSetsNotSupported()); } - require(msg.sender == address(serviceManager.allocationManager()), "Only allocation manager can register operators"); + require(msg.sender == address(serviceManager.allocationManager()), OnlyAllocationManager()); bytes memory quorumNumbers = new bytes(operatorSetIds.length); for (uint256 i = 0; i < operatorSetIds.length; i++) { quorumNumbers[i] = bytes1(uint8(operatorSetIds[i])); @@ -398,7 +393,7 @@ contract RegistryCoordinator is uint192(BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers, quorumCount)); require( operatorsPerQuorum.length == quorumNumbers.length, - "RegistryCoordinator.updateOperatorsForQuorum: input length mismatch" + InputLengthMismatch() ); // For each quorum, update ALL registered operators @@ -409,7 +404,7 @@ contract RegistryCoordinator is address[] memory currQuorumOperators = operatorsPerQuorum[i]; require( currQuorumOperators.length == indexRegistry.totalOperatorsForQuorum(quorumNumber), - "RegistryCoordinator.updateOperatorsForQuorum: number of updated operators does not match quorum total" + QuorumOperatorCountMismatch() ); address prevOperatorAddress = address(0); @@ -428,12 +423,12 @@ contract RegistryCoordinator is // Check that the operator is registered require( BitmapUtils.isSet(currentBitmap, quorumNumber), - "RegistryCoordinator.updateOperatorsForQuorum: operator not in quorum" + NotRegisteredForQuorum() ); // Prevent duplicate operators require( operator > prevOperatorAddress, - "RegistryCoordinator.updateOperatorsForQuorum: operators must be sorted" + NotSorted() ); } @@ -455,7 +450,7 @@ contract RegistryCoordinator is function updateSocket(string memory socket) external { require( _operatorInfo[msg.sender].status == OperatorStatus.REGISTERED, - "RegistryCoordinator.updateSocket: not registered" + NotRegistered() ); emit OperatorSocketUpdate(_operatorInfo[msg.sender].operatorId, socket); } @@ -519,7 +514,7 @@ contract RegistryCoordinator is IStakeRegistry.StrategyParams[] memory strategyParams, uint32 lookAheadPeriod ) external virtual onlyOwner { - require(isUsingOperatorSets(), "RegistryCoordinator.createSlashableStakeQuorum: operator sets not enabled"); + require(isUsingOperatorSets(), OperatorSetsNotEnabled()); _createQuorum(operatorSetParams, minimumStake, strategyParams, StakeType.TOTAL_SLASHABLE, lookAheadPeriod); } @@ -599,18 +594,18 @@ contract RegistryCoordinator is uint192(BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers, quorumCount)); uint192 currentBitmap = _currentOperatorBitmap(operatorId); require( - !quorumsToAdd.isEmpty(), "RegistryCoordinator._registerOperator: bitmap empty" + !quorumsToAdd.isEmpty(), BitmapEmpty() ); require( quorumsToAdd.noBitsInCommon(currentBitmap), - "RegistryCoordinator._registerOperator: operator already registered for some quorums being registered for" + AlreadyRegisteredForQuorums() ); 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" + CannotReregisterYet() ); /** @@ -662,18 +657,18 @@ contract RegistryCoordinator is uint192(BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers, quorumCount)); uint192 currentBitmap = _currentOperatorBitmap(operatorId); require( - !quorumsToAdd.isEmpty(), "RegistryCoordinator._registerOperatorToOperatorSet: bitmap empty" + !quorumsToAdd.isEmpty(), BitmapEmpty() ); require( quorumsToAdd.noBitsInCommon(currentBitmap), - "RegistryCoordinator._registerOperatorToOperatorSet: operator already registered for some quorums being registered for" + AlreadyRegisteredForQuorums() ); uint192 newBitmap = uint192(currentBitmap.plus(quorumsToAdd)); // Check that the operator can reregister if ejected require( lastEjectionTimestamp[operator] + ejectionCooldown < block.timestamp, - "RegistryCoordinator._registerOperatorToOperatorSet: operator cannot reregister yet" + CannotReregisterYet() ); /** @@ -704,13 +699,14 @@ contract RegistryCoordinator is * @dev Reverts if the caller is not the ejector */ function _checkEjector() internal view { - require(msg.sender == ejector, "RegistryCoordinator.onlyEjector: not ejector"); + require(msg.sender == ejector, OnlyEjector()); } function _checkAllocationManager() internal view { address allocationManager = address(serviceManager.allocationManager()); - require(msg.sender == allocationManager, "RegistryCoordinator.onlyAllocationManager: not allocation manager"); + require(msg.sender == allocationManager, OnlyAllocationManager()); } + /** * @notice Checks if a quorum exists * @param quorumNumber The quorum number to check @@ -718,7 +714,8 @@ contract RegistryCoordinator is */ function _checkQuorumExists(uint8 quorumNumber) internal view { require( - quorumNumber < quorumCount, "RegistryCoordinator.quorumExists: quorum does not exist" + quorumNumber < quorumCount, + QuorumDoesNotExist() ); } @@ -772,22 +769,23 @@ contract RegistryCoordinator is address operatorToKick = kickParams.operator; bytes32 idToKick = _operatorInfo[operatorToKick].operatorId; require( - newOperator != operatorToKick, "RegistryCoordinator._validateChurn: cannot churn self" + newOperator != operatorToKick, + CannotChurnSelf() ); require( kickParams.quorumNumber == quorumNumber, - "RegistryCoordinator._validateChurn: quorumNumber not the same as signed" + QuorumOperatorCountMismatch() ); // Get the target operator's stake and check that it is below the kick thresholds uint96 operatorToKickStake = stakeRegistry.getCurrentStake(idToKick, quorumNumber); require( newOperatorStake > _individualKickThreshold(operatorToKickStake, setParams), - "RegistryCoordinator._validateChurn: incoming operator has insufficient stake for churn" + InsufficientStakeForChurn() ); require( operatorToKickStake < _totalKickThreshold(totalQuorumStake, setParams), - "RegistryCoordinator._validateChurn: cannot kick operator with more than kickBIPsOfTotalStake" + CannotKickOperatorAboveThreshold() ); } @@ -802,7 +800,7 @@ contract RegistryCoordinator is bytes32 operatorId = operatorInfo.operatorId; require( operatorInfo.status == OperatorStatus.REGISTERED, - "RegistryCoordinator._deregisterOperator: not registered" + NotRegistered() ); /** @@ -817,35 +815,23 @@ contract RegistryCoordinator is uint192 currentBitmap = _currentOperatorBitmap(operatorId); require( !quorumsToRemove.isEmpty(), - "RegistryCoordinator._deregisterOperator: bitmap cannot be 0" + BitmapCannotBeZero() ); require( quorumsToRemove.isSubsetOf(currentBitmap), - "RegistryCoordinator._deregisterOperator: not registered for quorum" + NotRegisteredForQuorum() ); uint192 newBitmap = uint192(currentBitmap.minus(quorumsToRemove)); // Update operator's bitmap and status _updateOperatorBitmap({operatorId: operatorId, newBitmap: newBitmap}); - bool operatorSetAVS = isUsingOperatorSets(); - // = IAVSDirectory(serviceManager.avsDirectory()).isOperatorSetAVS(address(serviceManager)); - if (operatorSetAVS){ - bytes memory quorumBytes = BitmapUtils.bitmapToBytesArray(quorumsToRemove); - uint32[] memory operatorSetIds = new uint32[](quorumBytes.length); - for (uint256 i = 0; i < quorumBytes.length; i++) { - operatorSetIds[i] = uint8(quorumBytes[i]); - } - - 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); - } + // 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 @@ -912,11 +898,11 @@ contract RegistryCoordinator is // make sure the salt hasn't been used already require( !isChurnApproverSaltUsed[churnApproverSignature.salt], - "RegistryCoordinator._verifyChurnApproverSignature: salt spent" + ChurnApproverSaltUsed() ); require( churnApproverSignature.expiry >= block.timestamp, - "RegistryCoordinator._verifyChurnApproverSignature: signature expired" + SignatureExpired() ); // set salt used to true @@ -955,7 +941,7 @@ contract RegistryCoordinator is uint8 prevQuorumCount = quorumCount; require( prevQuorumCount < MAX_QUORUM_COUNT, - "RegistryCoordinator.createQuorum: max quorums reached" + MaxQuorumsReached() ); quorumCount = prevQuorumCount + 1; @@ -965,6 +951,24 @@ contract RegistryCoordinator is // Initialize the quorum here and in each registry _setOperatorSetParams(quorumNumber, operatorSetParams); + /// Update the AllocationManager if operatorSetQuorum + if (isOperatorSetAVS && !isM2Quorum[quorumNumber]) { + // Create array of CreateSetParams for the new quorum + IAllocationManagerTypes.CreateSetParams[] memory createSetParams = new IAllocationManagerTypes.CreateSetParams[](1); + + // Extract strategies from strategyParams + IStrategy[] memory strategies = new IStrategy[](strategyParams.length); + for (uint256 i = 0; i < strategyParams.length; i++) { + strategies[i] = strategyParams[i].strategy; + } + + // Initialize CreateSetParams with quorumNumber as operatorSetId + createSetParams[0] = IAllocationManagerTypes.CreateSetParams({ + operatorSetId: quorumNumber, + strategies: strategies + }); + serviceManager.createOperatorSets(createSetParams); + } // Initialize stake registry based on stake type if (stakeType == StakeType.TOTAL_DELEGATED) { stakeRegistry.initializeDelegatedStakeQuorum(quorumNumber, minimumStake, strategyParams); diff --git a/src/RegistryCoordinatorStorage.sol b/src/RegistryCoordinatorStorage.sol index 145bc840..8e343fbd 100644 --- a/src/RegistryCoordinatorStorage.sol +++ b/src/RegistryCoordinatorStorage.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {IBLSApkRegistry} from "./interfaces/IBLSApkRegistry.sol"; import {IStakeRegistry} from "./interfaces/IStakeRegistry.sol"; diff --git a/src/ServiceManagerBase.sol b/src/ServiceManagerBase.sol index 89b8bb21..86c1e937 100644 --- a/src/ServiceManagerBase.sol +++ b/src/ServiceManagerBase.sol @@ -1,12 +1,13 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; 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 {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; import {IRewardsCoordinator} from "eigenlayer-contracts/src/contracts/interfaces/IRewardsCoordinator.sol"; -import {IAllocationManager} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; +import {IAllocationManager, IAllocationManagerTypes} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; import {ServiceManagerBaseStorage} from "./ServiceManagerBaseStorage.sol"; import {IServiceManager} from "./interfaces/IServiceManager.sol"; @@ -48,6 +49,12 @@ abstract contract ServiceManagerBase is ServiceManagerBaseStorage { _; } + /// @notice only StakeRegistry can call functions with this modifier + modifier onlyStakeRegistry() { + _checkStakeRegistry(); + _; + } + /// @notice Sets the (immutable) `_registryCoordinator` address constructor( IAVSDirectory __avsDirectory, @@ -120,9 +127,16 @@ abstract contract ServiceManagerBase is ServiceManagerBaseStorage { _rewardsCoordinator.createAVSRewardsSubmission(rewardsSubmissions); } - function createOperatorSets(uint32[] memory operatorSetIds) external onlyRegistryCoordinator { - /// TODO: - // _avsDirectory.createOperatorSets(operatorSetIds); + function createOperatorSets(IAllocationManager.CreateSetParams[] memory params) external onlyRegistryCoordinator { + _allocationManager.createOperatorSets(address(this), params); + } + + function addStrategyToOperatorSet(uint32 operatorSetId, IStrategy[] memory strategies) external onlyStakeRegistry { + _allocationManager.addStrategiesToOperatorSet(address(this), operatorSetId, strategies); + } + + function removeStrategiesFromOperatorSet(uint32 operatorSetId, IStrategy[] memory strategies) external onlyStakeRegistry { + _allocationManager.removeStrategiesFromOperatorSet(address(this), operatorSetId, strategies); } /** @@ -145,20 +159,6 @@ 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. @@ -168,7 +168,11 @@ abstract contract ServiceManagerBase is ServiceManagerBaseStorage { address operator, uint32[] calldata operatorSetIds ) public virtual onlyRegistryCoordinator { - // _avsDirectory.deregisterOperatorFromOperatorSets(operator, operatorSetIds); + _allocationManager.deregisterFromOperatorSets(IAllocationManagerTypes.DeregisterParams({ + operator: operator, + avs: address(this), + operatorSetIds: operatorSetIds + })); } /** @@ -211,158 +215,6 @@ abstract contract ServiceManagerBase is ServiceManagerBaseStorage { delete proposedSlasher; } - /** - * @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); - } - - /** - * @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); - } - - /** - * @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; - } - - /** - * @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) internal { - // _avsDirectory.becomeOperatorSetAVS(); - // IAVSDirectory(address(_avsDirectory)).createOperatorSets(operatorSetIdsToCreate); - } - - /** - * @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 - ) 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. - * @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); - 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; @@ -468,6 +320,13 @@ abstract contract ServiceManagerBase is ServiceManagerBaseStorage { ); } + function _checkStakeRegistry() internal view { + require( + msg.sender == address(_stakeRegistry), + "ServiceManagerBase.onlyStakeRegistry: caller is not the stake registry" + ); + } + function _checkSlasher() internal view { require( diff --git a/src/ServiceManagerBaseStorage.sol b/src/ServiceManagerBaseStorage.sol index 51365228..6b8ee27b 100644 --- a/src/ServiceManagerBaseStorage.sol +++ b/src/ServiceManagerBaseStorage.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {OwnableUpgradeable} from "@openzeppelin-upgrades/contracts/access/OwnableUpgradeable.sol"; diff --git a/src/ServiceManagerRouter.sol b/src/ServiceManagerRouter.sol index e2259cfb..05a56780 100644 --- a/src/ServiceManagerRouter.sol +++ b/src/ServiceManagerRouter.sol @@ -1,12 +1,12 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {IServiceManagerUI} from "./interfaces/IServiceManagerUI.sol"; /** * @title Contract that proxies calls to a ServiceManager contract. * This contract is designed to be used by off-chain services which need - * errors to be handled gracefully. + * errors to be handled gracefully. * @author Layr Labs, Inc. */ @@ -40,7 +40,7 @@ contract ServiceManagerRouter { /** * @notice Internal helper function to make static calls - * @dev Handles calls to contracts that don't implement the given function and to EOAs by + * @dev Handles calls to contracts that don't implement the given function and to EOAs by * returning a failed call address */ function _makeCall(address serviceManager, bytes memory data) internal view returns (address[] memory) { diff --git a/src/StakeRegistry.sol b/src/StakeRegistry.sol index 0a5d8d97..fe861b7b 100644 --- a/src/StakeRegistry.sol +++ b/src/StakeRegistry.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; import {IAVSDirectory } from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol"; @@ -154,10 +154,6 @@ contract StakeRegistry is StakeRegistryStorage { ) external onlyRegistryCoordinator returns (uint192) { uint192 quorumsToRemove; - 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 * in the quorum's total stake. @@ -246,7 +242,7 @@ contract StakeRegistry is StakeRegistryStorage { * @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(uint8 quorumNumber, StakeType _stakeType) external onlyCoordinatorOwner { + function setStakeType(uint8 quorumNumber, StakeType _stakeType) external onlyCoordinatorOwner quorumExists(quorumNumber) { _setStakeType(quorumNumber, _stakeType); } @@ -255,9 +251,10 @@ contract StakeRegistry is StakeRegistryStorage { * @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 { + function setSlashableStakeLookahead(uint8 quorumNumber, uint32 _lookAheadPeriod) external onlyCoordinatorOwner quorumExists(quorumNumber) { _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). @@ -574,6 +571,19 @@ contract StakeRegistry is StakeRegistryStorage { VIEW FUNCTIONS *******************************************************************************/ + /** + * @notice Returns whether a quorum is an operator set quorum based on its stake type + * @dev A quorum is an operator set quorum if it has TOTAL_SLASHABLE stake type + * and is not an M2 quorum + * @param quorumNumber The quorum number to check + * @return True if the quorum is an operator set quorum + */ + function isOperatorSetQuorum(uint8 quorumNumber) external view returns (bool) { + bool isM2 = IRegistryCoordinator(registryCoordinator).isM2Quorum(quorumNumber); + bool isOperatorSet = IRegistryCoordinator(registryCoordinator).isOperatorSetAVS(); + return isOperatorSet && !isM2; + } + /** * @notice This function computes the total weight of the @param operator in the quorum @param quorumNumber. * @dev reverts if the quorum does not exist diff --git a/src/StakeRegistryStorage.sol b/src/StakeRegistryStorage.sol index acc80dd7..fb4967ac 100644 --- a/src/StakeRegistryStorage.sol +++ b/src/StakeRegistryStorage.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; import {IAVSDirectory} from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol"; diff --git a/src/interfaces/IBLSApkRegistry.sol b/src/interfaces/IBLSApkRegistry.sol index 2812a0ce..ced99f6c 100644 --- a/src/interfaces/IBLSApkRegistry.sol +++ b/src/interfaces/IBLSApkRegistry.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {IRegistry} from "./IRegistry.sol"; @@ -24,9 +24,9 @@ interface IBLSApkRegistry is IRegistry { /** * @notice Struct used when registering a new public key * @param pubkeyRegistrationSignature is the registration message signed by the private key of the operator - * @param pubkeyG1 is the corresponding G1 public key of the operator + * @param pubkeyG1 is the corresponding G1 public key of the operator * @param pubkeyG2 is the corresponding G2 public key of the operator - */ + */ struct PubkeyRegistrationParams { BN254.G1Point pubkeyRegistrationSignature; BN254.G1Point pubkeyG1; @@ -46,7 +46,7 @@ interface IBLSApkRegistry is IRegistry { // @notice Emitted when an operator pubkey is removed from a set of quorums event OperatorRemovedFromQuorums( - address operator, + address operator, bytes32 operatorId, bytes quorumNumbers ); @@ -75,9 +75,9 @@ interface IBLSApkRegistry is IRegistry { * 3) `quorumNumbers` is ordered in ascending order * 4) the operator is not already deregistered * 5) `quorumNumbers` is a subset of the quorumNumbers that the operator is registered for - */ + */ function deregisterOperator(address operator, bytes calldata quorumNumbers) external; - + /** * @notice Initializes a new quorum by pushing its first apk update * @param quorumNumber The number of the new quorum diff --git a/src/interfaces/IBLSSignatureChecker.sol b/src/interfaces/IBLSSignatureChecker.sol index 844c7217..aa92e56f 100644 --- a/src/interfaces/IBLSSignatureChecker.sol +++ b/src/interfaces/IBLSSignatureChecker.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {IRegistryCoordinator} from "./IRegistryCoordinator.sol"; import {IBLSApkRegistry} from "./IBLSApkRegistry.sol"; @@ -42,8 +42,8 @@ interface IBLSSignatureChecker { // EVENTS /// @notice Emitted when `staleStakesForbiddenUpdate` is set - event StaleStakesForbiddenUpdate(bool value); - + event StaleStakesForbiddenUpdate(bool value); + // CONSTANTS & IMMUTABLES function registryCoordinator() external view returns (IRegistryCoordinator); @@ -59,21 +59,21 @@ interface IBLSSignatureChecker { * The thesis of this procedure entails: * - getting the aggregated pubkey of all registered nodes at the time of pre-commit by the * disperser (represented by apk in the parameters), - * - subtracting the pubkeys of all the signers not in the quorum (nonSignerPubkeys) and storing + * - subtracting the pubkeys of all the signers not in the quorum (nonSignerPubkeys) and storing * the output in apk to get aggregated pubkey of all operators that are part of quorum. * - use this aggregated pubkey to verify the aggregated signature under BLS scheme. - * + * * @dev Before signature verification, the function verifies operator stake information. This includes ensuring that the provided `referenceBlockNumber` * is correct, i.e., ensure that the stake returned from the specified block number is recent enough and that the stake is either the most recent update * for the total stake (or the operator) or latest before the referenceBlockNumber. */ function checkSignatures( - bytes32 msgHash, + bytes32 msgHash, bytes calldata quorumNumbers, - uint32 referenceBlockNumber, + uint32 referenceBlockNumber, NonSignerStakesAndSignature memory nonSignerStakesAndSignature - ) - external + ) + external view returns ( QuorumStakeTotals memory, diff --git a/src/interfaces/IECDSAStakeRegistryEventsAndErrors.sol b/src/interfaces/IECDSAStakeRegistryEventsAndErrors.sol index 445db814..b43743d3 100644 --- a/src/interfaces/IECDSAStakeRegistryEventsAndErrors.sol +++ b/src/interfaces/IECDSAStakeRegistryEventsAndErrors.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; diff --git a/src/interfaces/IEjectionManager.sol b/src/interfaces/IEjectionManager.sol index 9a02991e..545e0a7c 100644 --- a/src/interfaces/IEjectionManager.sol +++ b/src/interfaces/IEjectionManager.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; /** * @title Interface for a contract that ejects operators from an AVSs RegistryCoordinator @@ -25,7 +25,7 @@ interface IEjectionManager { event QuorumEjectionParamsSet(uint8 quorumNumber, uint32 rateLimitWindow, uint16 ejectableStakePercent); ///@notice Emitted when an operator is ejected event OperatorEjected(bytes32 operatorId, uint8 quorumNumber); - ///@notice Emitted when operators are ejected for a quroum + ///@notice Emitted when operators are ejected for a quroum event QuorumEjection(uint32 ejectedOperators, bool ratelimitHit); /** diff --git a/src/interfaces/IIndexRegistry.sol b/src/interfaces/IIndexRegistry.sol index 72a702d8..579fb23b 100644 --- a/src/interfaces/IIndexRegistry.sol +++ b/src/interfaces/IIndexRegistry.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {IRegistry} from "./IRegistry.sol"; @@ -9,13 +9,13 @@ import {IRegistry} from "./IRegistry.sol"; */ interface IIndexRegistry is IRegistry { // EVENTS - + // emitted when an operator's index in the ordered operator list for the quorum with number `quorumNumber` is updated event QuorumIndexUpdate(bytes32 indexed operatorId, uint8 quorumNumber, uint32 newOperatorIndex); // DATA STRUCTURES - // struct used to give definitive ordering to operators at each blockNumber. + // struct used to give definitive ordering to operators at each blockNumber. struct OperatorUpdate { // blockNumber number from which `operatorIndex` was the operators index // the operator's index is the first entry such that `blockNumber >= entry.fromBlockNumber` diff --git a/src/interfaces/IRegistryCoordinator.sol b/src/interfaces/IRegistryCoordinator.sol index da20a3bc..4ce2e917 100644 --- a/src/interfaces/IRegistryCoordinator.sol +++ b/src/interfaces/IRegistryCoordinator.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {IServiceManager} from "./IServiceManager.sol"; import {IBLSApkRegistry} from "./IBLSApkRegistry.sol"; @@ -7,11 +7,35 @@ import {IStakeRegistry} from "./IStakeRegistry.sol"; import {IIndexRegistry} from "./IIndexRegistry.sol"; import {BN254} from "../libraries/BN254.sol"; +interface IRegistryCoordinatorErrors { + error InputLengthMismatch(); + error OperatorSetsEnabled(); + error OperatorSetsNotEnabled(); + error OperatorSetsNotSupported(); + error OnlyAllocationManager(); + error OnlyEjector(); + error QuorumDoesNotExist(); + error BitmapEmpty(); + error AlreadyRegisteredForQuorums(); + error CannotReregisterYet(); + error NotRegistered(); + error CannotChurnSelf(); + error QuorumOperatorCountMismatch(); + error InsufficientStakeForChurn(); + error CannotKickOperatorAboveThreshold(); + error BitmapCannotBeZero(); + error NotRegisteredForQuorum(); + error MaxQuorumsReached(); + error SaltAlreadyUsed(); + error RegistryCoordinatorSignatureExpired(); + error ChurnApproverSaltUsed(); + error NotSorted(); +} /** * @title Interface for a contract that coordinates between various registries for an AVS. * @author Layr Labs, Inc. */ -interface IRegistryCoordinator { +interface IRegistryCoordinator is IRegistryCoordinatorErrors{ // EVENTS /// Emits when an operator is registered @@ -140,6 +164,15 @@ interface IRegistryCoordinator { /// @notice Returns the number of registries function numRegistries() external view returns (uint256); + /// @notice Returns whether a quorum is an M2 quorum + /// @param quorumNumber The quorum number to check + /// @return True if the quorum is an M2 quorum + function isM2Quorum(uint8 quorumNumber) external view returns (bool); + + /// @notice Returns whether the AVS is an operator set AVS + /// @return True if the AVS is an operator set AVS + function isOperatorSetAVS() external view returns (bool); + /** * @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 diff --git a/src/interfaces/IServiceManager.sol b/src/interfaces/IServiceManager.sol index 02062d93..133c3789 100644 --- a/src/interfaces/IServiceManager.sol +++ b/src/interfaces/IServiceManager.sol @@ -6,6 +6,7 @@ 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 {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; import {IAVSRegistrar} from "eigenlayer-contracts/src/contracts/interfaces/IAVSRegistrar.sol"; @@ -27,19 +28,11 @@ interface IServiceManager is IServiceManagerUI { */ function createAVSRewardsSubmission(IRewardsCoordinator.RewardsSubmission[] calldata rewardsSubmissions) external; - function createOperatorSets(uint32[] memory operatorSetIds) external; + function createOperatorSets(IAllocationManager.CreateSetParams[] memory params) 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; + function addStrategyToOperatorSet(uint32 operatorSetId, IStrategy[] memory strategies) external; + + function removeStrategiesFromOperatorSet(uint32 operatorSetId, IStrategy[] memory strategies) external; /** * @notice Sets the AVS registrar address in the AllocationManager diff --git a/src/interfaces/ISlasher.sol b/src/interfaces/ISlasher.sol index b938e915..a150bc94 100644 --- a/src/interfaces/ISlasher.sol +++ b/src/interfaces/ISlasher.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; import {IAllocationManager} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; diff --git a/src/interfaces/ISocketUpdater.sol b/src/interfaces/ISocketUpdater.sol index dcf5a865..dc17ecfa 100644 --- a/src/interfaces/ISocketUpdater.sol +++ b/src/interfaces/ISocketUpdater.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; /** * @title Interface for an `ISocketUpdater` where operators can update their sockets. @@ -11,7 +11,7 @@ interface ISocketUpdater { event OperatorSocketUpdate(bytes32 indexed operatorId, string socket); // FUNCTIONS - + /** * @notice Updates the socket of the msg.sender given they are a registered operator * @param socket is the new socket of the operator diff --git a/src/interfaces/IStakeRegistry.sol b/src/interfaces/IStakeRegistry.sol index 62756d3a..3b82fefb 100644 --- a/src/interfaces/IStakeRegistry.sol +++ b/src/interfaces/IStakeRegistry.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; diff --git a/src/libraries/BN254.sol b/src/libraries/BN254.sol index 37e3d273..61a20929 100644 --- a/src/libraries/BN254.sol +++ b/src/libraries/BN254.sol @@ -19,7 +19,7 @@ // The remainder of the code in this library is written by LayrLabs Inc. and is also under an MIT license -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; /** * @title Library for operations on the BN254 elliptic curve. diff --git a/src/libraries/BitmapUtils.sol b/src/libraries/BitmapUtils.sol index 41f9ef48..9c53aadd 100644 --- a/src/libraries/BitmapUtils.sol +++ b/src/libraries/BitmapUtils.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; /** * @title Library for Bitmap utilities such as converting between an array of bytes and a bitmap and finding the number of 1s in a bitmap. @@ -62,7 +62,7 @@ library BitmapUtils { function orderedBytesArrayToBitmap(bytes memory orderedBytesArray, uint8 bitUpperBound) internal pure returns (uint256) { uint256 bitmap = orderedBytesArrayToBitmap(orderedBytesArray); - require((1 << bitUpperBound) > bitmap, + require((1 << bitUpperBound) > bitmap, "BitmapUtils.orderedBytesArrayToBitmap: bitmap exceeds max value" ); @@ -95,11 +95,11 @@ library BitmapUtils { if (uint256(uint8(bytesArray[i])) <= uint256(uint8(singleByte))) { return false; } - + // Pull the next byte out of the array singleByte = bytesArray[i]; } - + return true; } @@ -149,9 +149,9 @@ library BitmapUtils { function isSet(uint256 bitmap, uint8 bit) internal pure returns (bool) { return 1 == ((bitmap >> bit) & 1); } - + /** - * @notice Returns a copy of `bitmap` with `bit` set. + * @notice Returns a copy of `bitmap` with `bit` set. * @dev IMPORTANT: we're dealing with stack values here, so this doesn't modify * the original bitmap. Using this correctly requires an assignment statement: * `bitmap = bitmap.setBit(bit);` diff --git a/src/libraries/LibMergeSort.sol b/src/libraries/LibMergeSort.sol index 012feed4..11eb91b1 100644 --- a/src/libraries/LibMergeSort.sol +++ b/src/libraries/LibMergeSort.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; library LibMergeSort { function sort(address[] memory array) internal pure returns (address[] memory) { diff --git a/src/libraries/QuorumBitmapHistoryLib.sol b/src/libraries/QuorumBitmapHistoryLib.sol index c971d64d..72cbf114 100644 --- a/src/libraries/QuorumBitmapHistoryLib.sol +++ b/src/libraries/QuorumBitmapHistoryLib.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {IRegistryCoordinator} from "../interfaces/IRegistryCoordinator.sol"; diff --git a/src/libraries/SignatureCheckerLib.sol b/src/libraries/SignatureCheckerLib.sol index 410462cc..864e4afb 100644 --- a/src/libraries/SignatureCheckerLib.sol +++ b/src/libraries/SignatureCheckerLib.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "@openzeppelin-upgrades/contracts/utils/cryptography/SignatureCheckerUpgradeable.sol"; diff --git a/src/slashers/InstantSlasher.sol b/src/slashers/InstantSlasher.sol index b30d5881..976ad896 100644 --- a/src/slashers/InstantSlasher.sol +++ b/src/slashers/InstantSlasher.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; import {IAllocationManager} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; diff --git a/src/slashers/VetoableSlasher.sol b/src/slashers/VetoableSlasher.sol index a973aacc..0f9c623f 100644 --- a/src/slashers/VetoableSlasher.sol +++ b/src/slashers/VetoableSlasher.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; import {SlasherBase} from "./base/SlasherBase.sol"; diff --git a/src/slashers/base/SlasherBase.sol b/src/slashers/base/SlasherBase.sol index 1ca3e3c9..283a764c 100644 --- a/src/slashers/base/SlasherBase.sol +++ b/src/slashers/base/SlasherBase.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {Initializable} from "@openzeppelin-upgrades/contracts/proxy/utils/Initializable.sol"; import {IServiceManager} from "../../interfaces/IServiceManager.sol"; diff --git a/src/slashers/base/SlasherStorage.sol b/src/slashers/base/SlasherStorage.sol index 1024811a..a3924f34 100644 --- a/src/slashers/base/SlasherStorage.sol +++ b/src/slashers/base/SlasherStorage.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {ISlasher} from "../../interfaces/ISlasher.sol"; contract SlasherStorage is ISlasher { diff --git a/src/unaudited/ECDSAServiceManagerBase.sol b/src/unaudited/ECDSAServiceManagerBase.sol index d1c899ac..805a961a 100644 --- a/src/unaudited/ECDSAServiceManagerBase.sol +++ b/src/unaudited/ECDSAServiceManagerBase.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {OwnableUpgradeable} from "@openzeppelin-upgrades/contracts/access/OwnableUpgradeable.sol"; import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol"; diff --git a/src/unaudited/ECDSAStakeRegistry.sol b/src/unaudited/ECDSAStakeRegistry.sol index b333d504..0e341067 100644 --- a/src/unaudited/ECDSAStakeRegistry.sol +++ b/src/unaudited/ECDSAStakeRegistry.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {ECDSAStakeRegistryStorage, Quorum, StrategyParams} from "./ECDSAStakeRegistryStorage.sol"; import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; diff --git a/src/unaudited/ECDSAStakeRegistryStorage.sol b/src/unaudited/ECDSAStakeRegistryStorage.sol index 0742157a..6509f214 100644 --- a/src/unaudited/ECDSAStakeRegistryStorage.sol +++ b/src/unaudited/ECDSAStakeRegistryStorage.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; import {CheckpointsUpgradeable} from "@openzeppelin-upgrades/contracts/utils/CheckpointsUpgradeable.sol"; diff --git a/src/unaudited/examples/ECDSAStakeRegistryEqualWeight.sol b/src/unaudited/examples/ECDSAStakeRegistryEqualWeight.sol index 23b8be2a..adfd2751 100644 --- a/src/unaudited/examples/ECDSAStakeRegistryEqualWeight.sol +++ b/src/unaudited/examples/ECDSAStakeRegistryEqualWeight.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol"; import {ECDSAStakeRegistryPermissioned} from "./ECDSAStakeRegistryPermissioned.sol"; diff --git a/src/unaudited/examples/ECDSAStakeRegistryPermissioned.sol b/src/unaudited/examples/ECDSAStakeRegistryPermissioned.sol index ef2e691c..ab0bca02 100644 --- a/src/unaudited/examples/ECDSAStakeRegistryPermissioned.sol +++ b/src/unaudited/examples/ECDSAStakeRegistryPermissioned.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol"; import {ECDSAStakeRegistry} from "../ECDSAStakeRegistry.sol"; diff --git a/test/events/IBLSApkRegistryEvents.sol b/test/events/IBLSApkRegistryEvents.sol index 1ed588de..6921203d 100644 --- a/test/events/IBLSApkRegistryEvents.sol +++ b/test/events/IBLSApkRegistryEvents.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {BN254} from "../../src/libraries/BN254.sol"; @@ -17,7 +17,7 @@ interface IBLSApkRegistryEvents { // @notice Emitted when an operator pubkey is removed from a set of quorums event OperatorRemovedFromQuorums( - address operator, + address operator, bytes32 operatorId, bytes quorumNumbers ); diff --git a/test/events/IIndexRegistryEvents.sol b/test/events/IIndexRegistryEvents.sol index 79494de0..21a0f0f2 100644 --- a/test/events/IIndexRegistryEvents.sol +++ b/test/events/IIndexRegistryEvents.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; interface IIndexRegistryEvents { // emitted when an operator's index in the ordered operator list for the quorum with number `quorumNumber` is updated diff --git a/test/events/IServiceManagerBaseEvents.sol b/test/events/IServiceManagerBaseEvents.sol index 6defff0d..1efb8fa0 100644 --- a/test/events/IServiceManagerBaseEvents.sol +++ b/test/events/IServiceManagerBaseEvents.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import { IRewardsCoordinator, diff --git a/test/events/IStakeRegistryEvents.sol b/test/events/IStakeRegistryEvents.sol index 3e6f7e24..1b585ceb 100644 --- a/test/events/IStakeRegistryEvents.sol +++ b/test/events/IStakeRegistryEvents.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {IStakeRegistry, IStrategy} from "src/interfaces/IStakeRegistry.sol"; diff --git a/test/ffi/BLSPubKeyCompendiumFFI.t.sol b/test/ffi/BLSPubKeyCompendiumFFI.t.sol index 18d49a17..4f3906ba 100644 --- a/test/ffi/BLSPubKeyCompendiumFFI.t.sol +++ b/test/ffi/BLSPubKeyCompendiumFFI.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "../../src/BLSApkRegistry.sol"; import "../ffi/util/G2Operations.sol"; diff --git a/test/ffi/BLSSignatureCheckerFFI.t.sol b/test/ffi/BLSSignatureCheckerFFI.t.sol index 16f72b4e..16c22ebe 100644 --- a/test/ffi/BLSSignatureCheckerFFI.t.sol +++ b/test/ffi/BLSSignatureCheckerFFI.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {G2Operations} from "../ffi/util/G2Operations.sol"; import {MockAVSDeployer} from "../utils/MockAVSDeployer.sol"; @@ -30,13 +30,13 @@ contract BLSSignatureCheckerFFITests is MockAVSDeployer, G2Operations { // this test checks that a valid signature from maxOperatorsToRegister with a random number of nonsigners is checked // correctly on the BLSSignatureChecker contract when all operators are only regsitered for a single quorum and // the signature is only checked for stakes on that quorum - function testBLSSignatureChecker_SingleQuorum_Valid(uint256 pseudoRandomNumber) public { + function testBLSSignatureChecker_SingleQuorum_Valid(uint256 pseudoRandomNumber) public { uint256 numNonSigners = pseudoRandomNumber % (maxOperatorsToRegister - 1); uint256 quorumBitmap = 1; bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); - (uint32 referenceBlockNumber, BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature) = + (uint32 referenceBlockNumber, BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature) = _registerSignatoriesAndGetNonSignerStakeAndSignatureRandom(pseudoRandomNumber, numNonSigners, quorumBitmap); uint256 gasBefore = gasleft(); @@ -44,9 +44,9 @@ contract BLSSignatureCheckerFFITests is MockAVSDeployer, G2Operations { BLSSignatureChecker.QuorumStakeTotals memory quorumStakeTotals, /* bytes32 signatoryRecordHash */ ) = blsSignatureChecker.checkSignatures( - msgHash, + msgHash, quorumNumbers, - referenceBlockNumber, + referenceBlockNumber, nonSignerStakesAndSignature ); uint256 gasAfter = gasleft(); @@ -61,14 +61,14 @@ contract BLSSignatureCheckerFFITests is MockAVSDeployer, G2Operations { // this test checks that a valid signature from maxOperatorsToRegister with a random number of nonsigners is checked // correctly on the BLSSignatureChecker contract when all operators are registered for the first 100 quorums // and the signature is only checked for stakes on those quorums - function testBLSSignatureChecker_100Quorums_Valid(uint256 pseudoRandomNumber) public { + function testBLSSignatureChecker_100Quorums_Valid(uint256 pseudoRandomNumber) public { uint256 numNonSigners = pseudoRandomNumber % (maxOperatorsToRegister - 1); // 100 set bits uint256 quorumBitmap = (1 << 100) - 1; bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); - (uint32 referenceBlockNumber, BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature) = + (uint32 referenceBlockNumber, BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature) = _registerSignatoriesAndGetNonSignerStakeAndSignatureRandom(pseudoRandomNumber, numNonSigners, quorumBitmap); nonSignerStakesAndSignature.sigma = sigma.scalar_mul(quorumNumbers.length); @@ -76,9 +76,9 @@ contract BLSSignatureCheckerFFITests is MockAVSDeployer, G2Operations { uint256 gasBefore = gasleft(); blsSignatureChecker.checkSignatures( - msgHash, + msgHash, quorumNumbers, - referenceBlockNumber, + referenceBlockNumber, nonSignerStakesAndSignature ); uint256 gasAfter = gasleft(); @@ -168,8 +168,8 @@ contract BLSSignatureCheckerFFITests is MockAVSDeployer, G2Operations { OperatorStateRetriever.CheckSignaturesIndices memory checkSignaturesIndices = operatorStateRetriever.getCheckSignaturesIndices( registryCoordinator, - referenceBlockNumber, - quorumNumbers, + referenceBlockNumber, + quorumNumbers, nonSignerOperatorIds ); diff --git a/test/ffi/UpdateOperators.t.sol b/test/ffi/UpdateOperators.t.sol index 26fad27d..64e3ecbc 100644 --- a/test/ffi/UpdateOperators.t.sol +++ b/test/ffi/UpdateOperators.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "test/integration/User.t.sol"; @@ -37,7 +37,7 @@ contract Integration_AVS_Sync_GasCosts_FFI is IntegrationChecks { config_data, string.concat(".G1y[", vm.toString(i), "]") ); - // G2 + // G2 pubkey.pubkeyG2.X[1] = stdJson.readUint( config_data, string.concat(".G2x1[", vm.toString(i), "]") diff --git a/test/ffi/util/G2Operations.sol b/test/ffi/util/G2Operations.sol index f25cbbb7..016011da 100644 --- a/test/ffi/util/G2Operations.sol +++ b/test/ffi/util/G2Operations.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "forge-std/Test.sol"; import "openzeppelin-contracts/contracts/utils/Strings.sol"; @@ -13,7 +13,7 @@ contract G2Operations is Test { inputs[0] = "go"; inputs[1] = "run"; inputs[2] = "test/ffi/go/g2mul.go"; - inputs[3] = x.toString(); + inputs[3] = x.toString(); inputs[4] = "1"; bytes memory res = vm.ffi(inputs); diff --git a/test/harnesses/AVSDirectoryHarness.sol b/test/harnesses/AVSDirectoryHarness.sol index 262e8903..995f62a6 100644 --- a/test/harnesses/AVSDirectoryHarness.sol +++ b/test/harnesses/AVSDirectoryHarness.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; diff --git a/test/harnesses/BLSApkRegistryHarness.sol b/test/harnesses/BLSApkRegistryHarness.sol index 546d355c..7cc8ec60 100644 --- a/test/harnesses/BLSApkRegistryHarness.sol +++ b/test/harnesses/BLSApkRegistryHarness.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "../../src/BLSApkRegistry.sol"; diff --git a/test/harnesses/BitmapUtilsWrapper.sol b/test/harnesses/BitmapUtilsWrapper.sol index 19e1135e..95322d21 100644 --- a/test/harnesses/BitmapUtilsWrapper.sol +++ b/test/harnesses/BitmapUtilsWrapper.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "../../src/libraries/BitmapUtils.sol"; diff --git a/test/harnesses/RegistryCoordinatorHarness.t.sol b/test/harnesses/RegistryCoordinatorHarness.t.sol index 9dede138..8110fb13 100644 --- a/test/harnesses/RegistryCoordinatorHarness.t.sol +++ b/test/harnesses/RegistryCoordinatorHarness.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "../../src/RegistryCoordinator.sol"; diff --git a/test/harnesses/StakeRegistryHarness.sol b/test/harnesses/StakeRegistryHarness.sol index ca3cd2af..7d00b107 100644 --- a/test/harnesses/StakeRegistryHarness.sol +++ b/test/harnesses/StakeRegistryHarness.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "../../src/StakeRegistry.sol"; diff --git a/test/integration/CoreRegistration.t.sol b/test/integration/CoreRegistration.t.sol index a237e410..5d875e3e 100644 --- a/test/integration/CoreRegistration.t.sol +++ b/test/integration/CoreRegistration.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "../utils/MockAVSDeployer.sol"; import { AVSDirectory } from "eigenlayer-contracts/src/contracts/core/AVSDirectory.sol"; diff --git a/test/integration/IntegrationBase.t.sol b/test/integration/IntegrationBase.t.sol index 7a0265f9..0baf32b4 100644 --- a/test/integration/IntegrationBase.t.sol +++ b/test/integration/IntegrationBase.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "forge-std/Test.sol"; @@ -164,7 +164,7 @@ abstract contract IntegrationBase is IntegrationConfig { } /// AVSDirectory: - + function assert_NotRegisteredToAVS(User operator, string memory err) internal { IAVSDirectoryTypes.OperatorAVSRegistrationStatus status = avsDirectory.avsOperatorStatus(address(serviceManager), address(operator)); @@ -222,7 +222,7 @@ abstract contract IntegrationBase is IntegrationConfig { function assert_Snap_Unchanged_QuorumBitmap(User user, string memory err) internal { bytes32 operatorId = user.operatorId(); - + uint192 curBitmap = _getQuorumBitmap(operatorId); uint192 prevBitmap = _getPrevQuorumBitmap(operatorId); @@ -288,11 +288,11 @@ abstract contract IntegrationBase is IntegrationConfig { for (uint i = 0; i < churnedQuorums.length; i++) { BN254.G1Point memory churnedPubkey = churnedOperators[i].pubkeyG1(); - BN254.G1Point memory expectedApk + BN254.G1Point memory expectedApk = prevApks[i] .plus(churnedPubkey.negate()) .plus(incomingPubkey); - + assertEq(expectedApk.X, curApks[i].X, err); assertEq(expectedApk.Y, curApks[i].Y, err); } @@ -300,7 +300,7 @@ abstract contract IntegrationBase is IntegrationConfig { /// @dev Check that specific weights were added to the operator and total stakes for each quorum function assert_Snap_AddedWeightToStakes( - User user, + User user, bytes memory quorums, uint96[] memory addedWeights, string memory err @@ -320,7 +320,7 @@ abstract contract IntegrationBase is IntegrationConfig { /// @dev Check that the operator's stake weight was added to the operator and total /// stakes for each quorum function assert_Snap_Added_OperatorWeight( - User user, + User user, bytes memory quorums, string memory err ) internal { @@ -362,7 +362,7 @@ abstract contract IntegrationBase is IntegrationConfig { } function assert_Snap_Unchanged_OperatorStake( - User user, + User user, bytes memory quorums, string memory err ) internal { @@ -431,7 +431,7 @@ abstract contract IntegrationBase is IntegrationConfig { } function assert_Snap_Removed_TotalStake( - User user, + User user, bytes memory quorums, string memory err ) internal { @@ -462,7 +462,7 @@ abstract contract IntegrationBase is IntegrationConfig { function assert_Snap_Added_OperatorCount(bytes memory quorums, string memory err) internal { uint32[] memory curOperatorCounts = _getOperatorCounts(quorums); uint32[] memory prevOperatorCounts = _getPrevOperatorCounts(quorums); - + for (uint i = 0; i < quorums.length; i++) { assertEq(curOperatorCounts[i], prevOperatorCounts[i] + 1, err); } @@ -471,7 +471,7 @@ abstract contract IntegrationBase is IntegrationConfig { function assert_Snap_Reduced_OperatorCount(bytes memory quorums, string memory err) internal { uint32[] memory curOperatorCounts = _getOperatorCounts(quorums); uint32[] memory prevOperatorCounts = _getPrevOperatorCounts(quorums); - + for (uint i = 0; i < quorums.length; i++) { assertEq(curOperatorCounts[i], prevOperatorCounts[i] - 1, err); } @@ -480,7 +480,7 @@ abstract contract IntegrationBase is IntegrationConfig { function assert_Snap_Unchanged_OperatorCount(bytes memory quorums, string memory err) internal { uint32[] memory curOperatorCounts = _getOperatorCounts(quorums); uint32[] memory prevOperatorCounts = _getPrevOperatorCounts(quorums); - + for (uint i = 0; i < quorums.length; i++) { assertEq(curOperatorCounts[i], prevOperatorCounts[i], err); } @@ -491,7 +491,7 @@ abstract contract IntegrationBase is IntegrationConfig { /// - that the operator is in the current list, but not the previous list function assert_Snap_Added_OperatorListEntry( User operator, - bytes memory quorums, + bytes memory quorums, string memory err ) internal { bytes32[][] memory curOperatorLists = _getOperatorLists(quorums); @@ -510,7 +510,7 @@ abstract contract IntegrationBase is IntegrationConfig { /// - that the operator is in the previous list, but not the current list function assert_Snap_Removed_OperatorListEntry( User operator, - bytes memory quorums, + bytes memory quorums, string memory err ) internal { bytes32[][] memory curOperatorLists = _getOperatorLists(quorums); @@ -541,12 +541,12 @@ abstract contract IntegrationBase is IntegrationConfig { function assert_Snap_Replaced_OperatorListEntries( User incomingOperator, User[] memory churnedOperators, - bytes memory churnedQuorums, + bytes memory churnedQuorums, string memory err ) internal { // Sanity check input lengths assertEq(churnedOperators.length, churnedQuorums.length, "assert_Snap_Replaced_OperatorListEntries: input length mismatch"); - + bytes32[][] memory curOperatorLists = _getOperatorLists(churnedQuorums); bytes32[][] memory prevOperatorLists = _getPrevOperatorLists(churnedQuorums); @@ -568,11 +568,11 @@ abstract contract IntegrationBase is IntegrationConfig { TIME TRAVELERS ONLY BEYOND THIS POINT *******************************************************************************/ - /// @dev Check that the operator has `addedShares` additional operator shares + /// @dev Check that the operator has `addedShares` additional operator shares // for each strategy since the last snapshot function assert_Snap_Added_OperatorShares( - User operator, - IStrategy[] memory strategies, + User operator, + IStrategy[] memory strategies, uint[] memory addedShares, string memory err ) internal { @@ -589,8 +589,8 @@ abstract contract IntegrationBase is IntegrationConfig { /// @dev Check that the operator has `removedShares` fewer operator shares /// for each strategy since the last snapshot function assert_Snap_Removed_OperatorShares( - User operator, - IStrategy[] memory strategies, + User operator, + IStrategy[] memory strategies, uint256[] memory removedShares, string memory err ) internal { @@ -607,8 +607,8 @@ abstract contract IntegrationBase is IntegrationConfig { /// @dev Check that the staker has `addedShares` additional delegatable shares /// for each strategy since the last snapshot function assert_Snap_Added_StakerShares( - User staker, - IStrategy[] memory strategies, + User staker, + IStrategy[] memory strategies, uint[] memory addedShares, string memory err ) internal { @@ -625,8 +625,8 @@ abstract contract IntegrationBase is IntegrationConfig { /// @dev Check that the staker has `removedShares` fewer delegatable shares /// for each strategy since the last snapshot function assert_Snap_Removed_StakerShares( - User staker, - IStrategy[] memory strategies, + User staker, + IStrategy[] memory strategies, uint[] memory removedShares, string memory err ) internal { @@ -641,7 +641,7 @@ abstract contract IntegrationBase is IntegrationConfig { } function assert_Snap_Added_QueuedWithdrawals( - User staker, + User staker, IDelegationManager.Withdrawal[] memory withdrawals, string memory err ) internal { @@ -653,7 +653,7 @@ abstract contract IntegrationBase is IntegrationConfig { } function assert_Snap_Added_QueuedWithdrawal( - User staker, + User staker, string memory err ) internal { uint curQueuedWithdrawal = _getCumulativeWithdrawals(staker); @@ -741,7 +741,7 @@ abstract contract IntegrationBase is IntegrationConfig { /// @dev Uses timewarp modifier to get operator shares at the last snapshot function _getPrevOperatorShares( - User operator, + User operator, IStrategy[] memory strategies ) internal timewarp() returns (uint[] memory) { return _getOperatorShares(operator, strategies); @@ -760,7 +760,7 @@ abstract contract IntegrationBase is IntegrationConfig { /// @dev Uses timewarp modifier to get staker shares at the last snapshot function _getPrevStakerShares( - User staker, + User staker, IStrategy[] memory strategies ) internal timewarp() returns (uint[] memory) { return _getStakerShares(staker, strategies); @@ -786,7 +786,7 @@ abstract contract IntegrationBase is IntegrationConfig { function _getCumulativeWithdrawals(User staker) internal view returns (uint) { return delegationManager.cumulativeWithdrawalsQueued(address(staker)); } - + /// RegistryCoordinator: function _getOperatorInfo(User user) internal view returns (IRegistryCoordinator.OperatorInfo memory) { @@ -880,7 +880,7 @@ abstract contract IntegrationBase is IntegrationConfig { for (uint i = 0; i < quorums.length; i++) { stakes[i] = stakeRegistry.getCurrentTotalStake(uint8(quorums[i])); } - + return stakes; } diff --git a/test/integration/IntegrationChecks.t.sol b/test/integration/IntegrationChecks.t.sol index 67ee38f5..0f2de79c 100644 --- a/test/integration/IntegrationChecks.t.sol +++ b/test/integration/IntegrationChecks.t.sol @@ -1,12 +1,12 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "test/integration/IntegrationBase.t.sol"; import "test/integration/User.t.sol"; /// @notice Contract that provides utility functions to reuse common test blocks & checks contract IntegrationChecks is IntegrationBase { - + using BitmapUtils for *; /******************************************************************************* @@ -25,11 +25,11 @@ contract IntegrationChecks is IntegrationBase { "operator already has bits in quorum bitmap"); // BLSApkRegistry - assert_NoRegisteredPubkey(operator, + assert_NoRegisteredPubkey(operator, "operator already has a registered pubkey"); // DelegationManager - assert_NotRegisteredToAVS(operator, + assert_NotRegisteredToAVS(operator, "operator should not be registered to the AVS"); } @@ -44,7 +44,7 @@ contract IntegrationChecks is IntegrationBase { _log("check_Register_State", operator); // RegistryCoordinator - assert_HasOperatorInfoWithId(operator, + assert_HasOperatorInfoWithId(operator, "operatorInfo should have operatorId"); assert_HasRegisteredStatus(operator, "operatorInfo status should be REGISTERED"); @@ -54,11 +54,11 @@ contract IntegrationChecks is IntegrationBase { "operator did not register for all quorums"); // BLSApkRegistry - assert_HasRegisteredPubkey(operator, + assert_HasRegisteredPubkey(operator, "operator should have registered a pubkey"); - assert_Snap_Added_QuorumApk(operator, quorums, + assert_Snap_Added_QuorumApk(operator, quorums, "operator pubkey should have been added to each quorum apk"); - + // StakeRegistry assert_HasAtLeastMinimumStake(operator, quorums, "operator should have at least the minimum stake in each quorum"); @@ -86,14 +86,14 @@ contract IntegrationChecks is IntegrationBase { ) internal { _log("check_Churned_State", incomingOperator); - bytes memory combinedQuorums = + bytes memory combinedQuorums = churnedQuorums .orderedBytesArrayToBitmap() .plus(standardQuorums.orderedBytesArrayToBitmap()) .bitmapToBytesArray(); // RegistryCoordinator - assert_HasOperatorInfoWithId(incomingOperator, + assert_HasOperatorInfoWithId(incomingOperator, "operatorInfo should have operatorId"); assert_HasRegisteredStatus(incomingOperator, "operatorInfo status should be REGISTERED"); @@ -103,13 +103,13 @@ contract IntegrationChecks is IntegrationBase { "operator did not register for all quorums"); // BLSApkRegistry - assert_HasRegisteredPubkey(incomingOperator, + assert_HasRegisteredPubkey(incomingOperator, "operator should have registered a pubkey"); assert_Snap_Added_QuorumApk(incomingOperator, standardQuorums, "operator pubkey should have been added to standardQuorums apks"); assert_Snap_Churned_QuorumApk(incomingOperator, churnedOperators, churnedQuorums, "operator pubkey should have been added and churned operator pubkeys should have been removed from apks"); - + // StakeRegistry assert_HasAtLeastMinimumStake(incomingOperator, combinedQuorums, "operator should have at least the minimum stake in each quorum"); @@ -140,7 +140,7 @@ contract IntegrationChecks is IntegrationBase { churnedQuorum[0] = churnedQuorums[i]; // RegistryCoordinator - assert_HasOperatorInfoWithId(churnedOperator, + assert_HasOperatorInfoWithId(churnedOperator, "churned operatorInfo should still have operatorId"); assert_NotRegisteredForQuorums(churnedOperator, churnedQuorum, "churned operator bitmap should not include churned quorums"); @@ -148,7 +148,7 @@ contract IntegrationChecks is IntegrationBase { "churned operator did not deregister from churned quorum"); // BLSApkRegistry - assert_HasRegisteredPubkey(churnedOperator, + assert_HasRegisteredPubkey(churnedOperator, "churned operator should still have a registered pubkey"); // StakeRegistry @@ -177,7 +177,7 @@ contract IntegrationChecks is IntegrationBase { "operator info should not have changed"); assert_Snap_Unchanged_QuorumBitmap(operator, "operators quorum bitmap should not have changed"); - + // BLSApkRegistry assert_Snap_Unchanged_QuorumApk(quorums, "quorum apks should not have changed"); @@ -185,9 +185,9 @@ contract IntegrationChecks is IntegrationBase { // StakeRegistry assert_Snap_Increased_OperatorWeight(operator, quorums, "operator weight should not have decreased after deposit"); - assert_Snap_Unchanged_OperatorStake(operator, quorums, + assert_Snap_Unchanged_OperatorStake(operator, quorums, "operator stake should be unchanged"); - assert_Snap_Unchanged_TotalStake(quorums, + assert_Snap_Unchanged_TotalStake(quorums, "total stake should be unchanged"); // IndexRegistry @@ -205,8 +205,8 @@ contract IntegrationChecks is IntegrationBase { /// NOTE: This method assumes (and checks) that the operator already /// met the minimum stake before stake was added. function check_DepositUpdate_State( - User operator, - bytes memory quorums, + User operator, + bytes memory quorums, uint96[] memory addedWeights ) internal { _log("check_DepositUpdate_State", operator); @@ -254,7 +254,7 @@ contract IntegrationChecks is IntegrationBase { "operator info should not have changed"); assert_Snap_Unchanged_QuorumBitmap(operator, "operators quorum bitmap should not have changed"); - + // BLSApkRegistry assert_Snap_Unchanged_QuorumApk(quorums, "quorum apks should not have changed"); @@ -262,9 +262,9 @@ contract IntegrationChecks is IntegrationBase { // StakeRegistry assert_Snap_Decreased_OperatorWeight(operator, quorums, "operator weight should not have increased after deposit"); - assert_Snap_Unchanged_OperatorStake(operator, quorums, + assert_Snap_Unchanged_OperatorStake(operator, quorums, "operator stake should be unchanged"); - assert_Snap_Unchanged_TotalStake(quorums, + assert_Snap_Unchanged_TotalStake(quorums, "total stake should be unchanged"); // IndexRegistry @@ -288,7 +288,7 @@ contract IntegrationChecks is IntegrationBase { _log("check_WithdrawUpdate_State", operator); // RegistryCoordinator - assert_HasOperatorInfoWithId(operator, + assert_HasOperatorInfoWithId(operator, "operatorInfo should still have operatorId"); assert_EmptyQuorumBitmap(operator, "operator should not have any bits in bitmap"); @@ -298,9 +298,9 @@ contract IntegrationChecks is IntegrationBase { "operator did not deregister from all quorums"); // BLSApkRegistry - assert_HasRegisteredPubkey(operator, + assert_HasRegisteredPubkey(operator, "operator should still have a registered pubkey"); - assert_Snap_Removed_QuorumApk(operator, quorums, + assert_Snap_Removed_QuorumApk(operator, quorums, "operator pubkey should have been subtracted from each quorum apk"); // StakeRegistry @@ -316,7 +316,7 @@ contract IntegrationChecks is IntegrationBase { "operator list should have one fewer entry"); // AVSDirectory - assert_NotRegisteredToAVS(operator, + assert_NotRegisteredToAVS(operator, "operator should not be registered to the AVS"); } @@ -364,7 +364,7 @@ contract IntegrationChecks is IntegrationBase { _log("check_Deregister_State", operator); // RegistryCoordinator - assert_HasOperatorInfoWithId(operator, + assert_HasOperatorInfoWithId(operator, "operatorInfo should still have operatorId"); assert_NotRegisteredForQuorums(operator, quorums, "current operator bitmap should not include quorums"); @@ -372,9 +372,9 @@ contract IntegrationChecks is IntegrationBase { "operator did not deregister from all quorums"); // BLSApkRegistry - assert_HasRegisteredPubkey(operator, + assert_HasRegisteredPubkey(operator, "operator should still have a registered pubkey"); - assert_Snap_Removed_QuorumApk(operator, quorums, + assert_Snap_Removed_QuorumApk(operator, quorums, "operator pubkey should have been subtracted from each quorum apk"); // StakeRegistry @@ -399,13 +399,13 @@ contract IntegrationChecks is IntegrationBase { // RegistryCoordinator assert_EmptyQuorumBitmap(operator, "operator should not have any bits in bitmap"); - assert_HasOperatorInfoWithId(operator, + assert_HasOperatorInfoWithId(operator, "operatorInfo should still have operatorId"); assert_HasDeregisteredStatus(operator, "operatorInfo status should be DEREGISTERED"); // AVSDirectory - assert_NotRegisteredToAVS(operator, + assert_NotRegisteredToAVS(operator, "operator should not be registered to the AVS"); } diff --git a/test/integration/IntegrationConfig.t.sol b/test/integration/IntegrationConfig.t.sol index e1a341e8..f3231ac3 100644 --- a/test/integration/IntegrationConfig.t.sol +++ b/test/integration/IntegrationConfig.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "forge-std/Test.sol"; diff --git a/test/integration/IntegrationDeployer.t.sol b/test/integration/IntegrationDeployer.t.sol index db3fb704..414b8fe2 100644 --- a/test/integration/IntegrationDeployer.t.sol +++ b/test/integration/IntegrationDeployer.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "forge-std/Test.sol"; diff --git a/test/integration/TimeMachine.t.sol b/test/integration/TimeMachine.t.sol index b1df82d5..b1cffa68 100644 --- a/test/integration/TimeMachine.t.sol +++ b/test/integration/TimeMachine.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "forge-std/Test.sol"; diff --git a/test/integration/User.t.sol b/test/integration/User.t.sol index b98a8cd3..4b58950a 100644 --- a/test/integration/User.t.sol +++ b/test/integration/User.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "forge-std/Test.sol"; import "@openzeppelin/contracts/utils/Strings.sol"; diff --git a/test/integration/mocks/BeaconChainOracleMock.t.sol b/test/integration/mocks/BeaconChainOracleMock.t.sol index fcccbbd4..5c2e4b77 100644 --- a/test/integration/mocks/BeaconChainOracleMock.t.sol +++ b/test/integration/mocks/BeaconChainOracleMock.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; // import "eigenlayer-contracts/src/contracts/interfaces/IBeaconChainOracle.sol"; diff --git a/test/integration/tests/Full_Register_Deregister.t.sol b/test/integration/tests/Full_Register_Deregister.t.sol index 48bb150a..2561aa2c 100644 --- a/test/integration/tests/Full_Register_Deregister.t.sol +++ b/test/integration/tests/Full_Register_Deregister.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "test/integration/User.t.sol"; @@ -109,7 +109,7 @@ contract Integration_Full_Register_Deregister is IntegrationChecks { churnTarget.registerOperator(quorum); check_Register_State(churnTarget, quorum); } - + // 4. Original operator re-registers for all quorums by churning old operators again operator.registerOperatorWithChurn(quorums, churnTargets, new bytes(0)); check_Churned_State({ @@ -140,7 +140,7 @@ contract Integration_Full_Register_Deregister is IntegrationChecks { // Select some quorums to register using churn, and the rest without churn bytes memory churnQuorums = _selectRand(quorums); - bytes memory standardQuorums = + bytes memory standardQuorums = quorums .orderedBytesArrayToBitmap() .minus(churnQuorums.orderedBytesArrayToBitmap()) diff --git a/test/integration/tests/NonFull_Register_CoreBalanceChange_Update.t.sol b/test/integration/tests/NonFull_Register_CoreBalanceChange_Update.t.sol index a4ffe3e6..d9b74e8d 100644 --- a/test/integration/tests/NonFull_Register_CoreBalanceChange_Update.t.sol +++ b/test/integration/tests/NonFull_Register_CoreBalanceChange_Update.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "test/integration/User.t.sol"; diff --git a/test/integration/tests/NonFull_Register_Deregister.t.sol b/test/integration/tests/NonFull_Register_Deregister.t.sol index c30c5261..6acf85cb 100644 --- a/test/integration/tests/NonFull_Register_Deregister.t.sol +++ b/test/integration/tests/NonFull_Register_Deregister.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "test/integration/User.t.sol"; diff --git a/test/integration/utils/BitmapStrings.t.sol b/test/integration/utils/BitmapStrings.t.sol index 532f5d8d..2bcec384 100644 --- a/test/integration/utils/BitmapStrings.t.sol +++ b/test/integration/utils/BitmapStrings.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "@openzeppelin/contracts/utils/Strings.sol"; @@ -16,12 +16,12 @@ library BitmapStrings { for (uint i = 0; i < bitmapArr.length; i++) { if (i == bitmapArr.length - 1) { result = string.concat( - result, + result, uint(uint8(bitmapArr[i])).toString() ); } else { result = string.concat( - result, + result, uint(uint8(bitmapArr[i])).toString(), ", " ); diff --git a/test/integration/utils/Sort.t.sol b/test/integration/utils/Sort.t.sol index 46a2fc2b..968400c6 100644 --- a/test/integration/utils/Sort.t.sol +++ b/test/integration/utils/Sort.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; library Sort { /** diff --git a/test/mocks/AVSDirectoryMock.sol b/test/mocks/AVSDirectoryMock.sol index 5e4c6f77..ca3613ef 100644 --- a/test/mocks/AVSDirectoryMock.sol +++ b/test/mocks/AVSDirectoryMock.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {IAVSDirectory } from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol"; import {OperatorSet} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; diff --git a/test/mocks/AllocationManagerMock.sol b/test/mocks/AllocationManagerMock.sol index fdb11248..b355af95 100644 --- a/test/mocks/AllocationManagerMock.sol +++ b/test/mocks/AllocationManagerMock.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {IAllocationManager, OperatorSet} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; import {IAVSRegistrar } from "eigenlayer-contracts/src/contracts/interfaces/IAVSRegistrar.sol"; diff --git a/test/mocks/DelegationMock.sol b/test/mocks/DelegationMock.sol index 23ad0bb5..e3b1ed77 100644 --- a/test/mocks/DelegationMock.sol +++ b/test/mocks/DelegationMock.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {console2 as console} from "forge-std/Test.sol"; @@ -267,6 +267,6 @@ contract DelegationMock is DelegationIntermediate { return shares; } function minWithdrawalDelayBlocks() external view override returns (uint32){ - return 100; + return 10000; } } \ No newline at end of file diff --git a/test/mocks/ECDSAServiceManagerMock.sol b/test/mocks/ECDSAServiceManagerMock.sol index 2c3c872b..102d076e 100644 --- a/test/mocks/ECDSAServiceManagerMock.sol +++ b/test/mocks/ECDSAServiceManagerMock.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "../../src/unaudited/ECDSAServiceManagerBase.sol"; import {IAllocationManagerTypes} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; @@ -28,7 +28,11 @@ contract ECDSAServiceManagerMock is ECDSAServiceManagerBase { __ServiceManagerBase_init(initialOwner, rewardsInitiator); } - function createOperatorSets(uint32[] memory) external {} + function createOperatorSets(IAllocationManager.CreateSetParams[] memory params) external{} + + function addStrategyToOperatorSet(uint32 operatorSetId, IStrategy[] memory strategies) external{} + + function removeStrategiesFromOperatorSet(uint32 operatorSetId, IStrategy[] memory strategies) external{} function registerOperatorToOperatorSets( address operator, diff --git a/test/mocks/ECDSAStakeRegistryMock.sol b/test/mocks/ECDSAStakeRegistryMock.sol index 7ad6043e..e2b756be 100644 --- a/test/mocks/ECDSAStakeRegistryMock.sol +++ b/test/mocks/ECDSAStakeRegistryMock.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "../../src/unaudited/ECDSAStakeRegistry.sol"; diff --git a/test/mocks/PermissionControllerMock.sol b/test/mocks/PermissionControllerMock.sol index 7501a9fa..9a9eca00 100644 --- a/test/mocks/PermissionControllerMock.sol +++ b/test/mocks/PermissionControllerMock.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {IPermissionController} from "eigenlayer-contracts/src/contracts/interfaces/IPermissionController.sol"; diff --git a/test/mocks/RegistryCoordinatorMock.sol b/test/mocks/RegistryCoordinatorMock.sol index 73308f14..165a649d 100644 --- a/test/mocks/RegistryCoordinatorMock.sol +++ b/test/mocks/RegistryCoordinatorMock.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "../../src/interfaces/IRegistryCoordinator.sol"; @@ -9,7 +9,7 @@ contract RegistryCoordinatorMock is IRegistryCoordinator { function blsApkRegistry() external view returns (IBLSApkRegistry) {} function ejectOperator( - address operator, + address operator, bytes calldata quorumNumbers ) external {} @@ -70,4 +70,12 @@ contract RegistryCoordinatorMock is IRegistryCoordinator { function owner() external view returns (address) {} function serviceManager() external view returns (IServiceManager){} + + function isM2Quorum(uint8 quorumNumber) external view returns (bool) { + return false; + } + + function isOperatorSetAVS() external view returns (bool) { + return false; + } } \ No newline at end of file diff --git a/test/mocks/RewardsCoordinatorMock.sol b/test/mocks/RewardsCoordinatorMock.sol index e408c421..9d992962 100644 --- a/test/mocks/RewardsCoordinatorMock.sol +++ b/test/mocks/RewardsCoordinatorMock.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; diff --git a/test/mocks/ServiceManagerMock.sol b/test/mocks/ServiceManagerMock.sol index a9bb5fff..a60e63c8 100644 --- a/test/mocks/ServiceManagerMock.sol +++ b/test/mocks/ServiceManagerMock.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "../../src/ServiceManagerBase.sol"; diff --git a/test/mocks/StakeRegistryMock.sol b/test/mocks/StakeRegistryMock.sol index 2dcecce3..58f46256 100644 --- a/test/mocks/StakeRegistryMock.sol +++ b/test/mocks/StakeRegistryMock.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "../../src/interfaces/IStakeRegistry.sol"; import "../../src/interfaces/IRegistryCoordinator.sol"; diff --git a/test/unit/BLSApkRegistryUnit.t.sol b/test/unit/BLSApkRegistryUnit.t.sol index 2a8be4c7..aacfe9d8 100644 --- a/test/unit/BLSApkRegistryUnit.t.sol +++ b/test/unit/BLSApkRegistryUnit.t.sol @@ -1,5 +1,5 @@ //SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "forge-std/Test.sol"; import "../harnesses/BLSApkRegistryHarness.sol"; diff --git a/test/unit/BLSSignatureCheckerUnit.t.sol b/test/unit/BLSSignatureCheckerUnit.t.sol index 6056b283..74b8f1da 100644 --- a/test/unit/BLSSignatureCheckerUnit.t.sol +++ b/test/unit/BLSSignatureCheckerUnit.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "../../src/BLSSignatureChecker.sol"; import "../utils/BLSMockAVSDeployer.sol"; diff --git a/test/unit/BitmapUtils.t.sol b/test/unit/BitmapUtils.t.sol index fd51298d..fcd742b2 100644 --- a/test/unit/BitmapUtils.t.sol +++ b/test/unit/BitmapUtils.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "../harnesses/BitmapUtilsWrapper.sol"; // import "../../contracts/libraries/BitmapUtils.sol"; @@ -24,7 +24,7 @@ contract BitmapUtilsUnitTests_bitwiseOperations is BitmapUtilsUnitTests { uint16 numOnes = 0; for (uint256 i = 0; i < 256; ++i) { if ((input >> i) & 1 == 1) { - ++numOnes; + ++numOnes; } } assertEq(libraryOutput, numOnes, "inconsistency in countNumOnes function"); diff --git a/test/unit/ECDSAServiceManager.t.sol b/test/unit/ECDSAServiceManager.t.sol index 4cb283fc..bcd17052 100644 --- a/test/unit/ECDSAServiceManager.t.sol +++ b/test/unit/ECDSAServiceManager.t.sol @@ -1,5 +1,5 @@ // // SPDX-License-Identifier: MIT -// pragma solidity ^0.8.12; +// pragma solidity ^0.8.27; // import {Test, console} from "forge-std/Test.sol"; diff --git a/test/unit/ECDSAStakeRegistryEqualWeightUnit.t.sol b/test/unit/ECDSAStakeRegistryEqualWeightUnit.t.sol index bc6337c5..0008da7f 100644 --- a/test/unit/ECDSAStakeRegistryEqualWeightUnit.t.sol +++ b/test/unit/ECDSAStakeRegistryEqualWeightUnit.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol"; import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; diff --git a/test/unit/ECDSAStakeRegistryPermissionedUnit.t.sol b/test/unit/ECDSAStakeRegistryPermissionedUnit.t.sol index dffb9174..8205034d 100644 --- a/test/unit/ECDSAStakeRegistryPermissionedUnit.t.sol +++ b/test/unit/ECDSAStakeRegistryPermissionedUnit.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol"; import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; diff --git a/test/unit/ECDSAStakeRegistryUnit.t.sol b/test/unit/ECDSAStakeRegistryUnit.t.sol index d374144d..e1d7de77 100644 --- a/test/unit/ECDSAStakeRegistryUnit.t.sol +++ b/test/unit/ECDSAStakeRegistryUnit.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {Test, console} from "forge-std/Test.sol"; diff --git a/test/unit/EjectionManagerUnit.t.sol b/test/unit/EjectionManagerUnit.t.sol index 456fe58b..9f01af50 100644 --- a/test/unit/EjectionManagerUnit.t.sol +++ b/test/unit/EjectionManagerUnit.t.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {EjectionManager} from "../../src/EjectionManager.sol"; import {IEjectionManager} from "../../src/interfaces/IEjectionManager.sol"; diff --git a/test/unit/IndexRegistryUnit.t.sol b/test/unit/IndexRegistryUnit.t.sol index fe034c0e..36d7dc69 100644 --- a/test/unit/IndexRegistryUnit.t.sol +++ b/test/unit/IndexRegistryUnit.t.sol @@ -1,5 +1,5 @@ //SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "../../src/interfaces/IIndexRegistry.sol"; import "../../src/IndexRegistry.sol"; diff --git a/test/unit/LibMergeSort.t.sol b/test/unit/LibMergeSort.t.sol index f3014d3f..dc2ddf7d 100644 --- a/test/unit/LibMergeSort.t.sol +++ b/test/unit/LibMergeSort.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "forge-std/Test.sol"; import "../../src/libraries/LibMergeSort.sol"; diff --git a/test/unit/OperatorStateRetrieverUnit.t.sol b/test/unit/OperatorStateRetrieverUnit.t.sol index 2fe360ca..e25c8af0 100644 --- a/test/unit/OperatorStateRetrieverUnit.t.sol +++ b/test/unit/OperatorStateRetrieverUnit.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "../utils/MockAVSDeployer.sol"; diff --git a/test/unit/RegistryCoordinatorUnit.t.sol b/test/unit/RegistryCoordinatorUnit.t.sol index 9fedcd1f..c3c8032d 100644 --- a/test/unit/RegistryCoordinatorUnit.t.sol +++ b/test/unit/RegistryCoordinatorUnit.t.sol @@ -1,7 +1,8 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "../utils/MockAVSDeployer.sol"; +import {IRegistryCoordinatorErrors} from "../../src/interfaces/IRegistryCoordinator.sol"; contract RegistryCoordinatorUnitTests is MockAVSDeployer { using BN254 for BN254.G1Point; @@ -120,7 +121,7 @@ contract RegistryCoordinatorUnitTests_Initialization_Setters is RegistryCoordina ); } - // make sure the contract intializers are disabled + // make sure the contract initializers are disabled cheats.expectRevert(bytes("Initializable: contract is already initialized")); registryCoordinator.initialize( registryCoordinatorOwner, @@ -198,7 +199,7 @@ contract RegistryCoordinatorUnitTests_Initialization_Setters is RegistryCoordina function test_updateSocket_revert_notRegistered() public { cheats.prank(defaultOperator); - cheats.expectRevert("RegistryCoordinator.updateSocket: not registered"); + cheats.expectRevert(bytes4(keccak256("NotRegistered()"))); registryCoordinator.updateSocket("localhost:32004"); } @@ -269,7 +270,7 @@ contract RegistryCoordinatorUnitTests_RegisterOperator is RegistryCoordinatorUni bytes memory emptyQuorumNumbers = new bytes(0); ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; - cheats.expectRevert("RegistryCoordinator._registerOperator: bitmap empty"); + cheats.expectRevert(bytes4(keccak256("BitmapEmpty()"))); cheats.prank(defaultOperator); registryCoordinator.registerOperator(emptyQuorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); } @@ -483,7 +484,7 @@ contract RegistryCoordinatorUnitTests_RegisterOperator is RegistryCoordinatorUni _setOperatorWeight(operatorToRegister, defaultQuorumNumber, defaultStake); cheats.prank(operatorToRegister); - cheats.expectRevert("RegistryCoordinator.registerOperator: operator exceeds max"); + cheats.expectRevert(bytes4(keccak256("MaxQuorumsReached()"))); registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); } @@ -503,8 +504,7 @@ contract RegistryCoordinatorUnitTests_RegisterOperator is RegistryCoordinatorUni cheats.prank(defaultOperator); cheats.roll(nextRegistrationBlockNumber); - cheats.expectRevert("RegistryCoordinator._registerOperator: operator already registered for some quorums being registered for"); - + cheats.expectRevert(bytes4(keccak256("AlreadyRegisteredForQuorums()"))); registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); } @@ -513,7 +513,7 @@ contract RegistryCoordinatorUnitTests_RegisterOperator is RegistryCoordinatorUni bytes memory emptyQuorumNumbers = new bytes(0); ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; - cheats.expectRevert("RegistryCoordinator._registerOperator: bitmap empty"); + cheats.expectRevert(bytes4(keccak256("BitmapEmpty()"))); registryCoordinator._registerOperatorExternal(defaultOperator, defaultOperatorId, emptyQuorumNumbers, defaultSocket, emptySig); } @@ -535,7 +535,7 @@ contract RegistryCoordinatorUnitTests_RegisterOperator is RegistryCoordinatorUni _setOperatorWeight(defaultOperator, uint8(quorumNumbers[0]), defaultStake); registryCoordinator._registerOperatorExternal(defaultOperator, defaultOperatorId, quorumNumbers, defaultSocket, emptySig); - cheats.expectRevert("RegistryCoordinator._registerOperator: operator already registered for some quorums being registered for"); + cheats.expectRevert(bytes4(keccak256("AlreadyRegisteredForQuorums()"))); registryCoordinator._registerOperatorExternal(defaultOperator, defaultOperatorId, quorumNumbers, defaultSocket, emptySig); } @@ -601,7 +601,7 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist bytes memory quorumNumbers = new bytes(1); quorumNumbers[0] = bytes1(defaultQuorumNumber); - cheats.expectRevert("RegistryCoordinator._deregisterOperator: not registered"); + cheats.expectRevert(bytes4(keccak256("NotRegistered()"))); cheats.prank(defaultOperator); registryCoordinator.deregisterOperator(quorumNumbers); } @@ -617,7 +617,7 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist quorumNumbers[0] = bytes1(defaultQuorumNumber + 1); quorumNumbers[1] = bytes1(defaultQuorumNumber + 2); - cheats.expectRevert("RegistryCoordinator._deregisterOperator: not registered for quorum"); + cheats.expectRevert(bytes4(keccak256("NotRegisteredForQuorum()"))); cheats.prank(defaultOperator); registryCoordinator.deregisterOperator(quorumNumbers); } @@ -957,13 +957,13 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist bytes memory emptyQuorumNumbers = new bytes(0); cheats.roll(deregistrationBlockNumber); - cheats.expectRevert("RegistryCoordinator._deregisterOperator: bitmap cannot be 0"); + cheats.expectRevert(bytes4(keccak256("BitmapCannotBeZero()"))); registryCoordinator._deregisterOperatorExternal(defaultOperator, emptyQuorumNumbers); } function test_deregisterOperatorExternal_revert_notRegistered() public { bytes memory emptyQuorumNumbers = new bytes(0); - cheats.expectRevert("RegistryCoordinator._deregisterOperator: not registered"); + cheats.expectRevert(bytes4(keccak256("NotRegistered()"))); registryCoordinator._deregisterOperatorExternal(defaultOperator, emptyQuorumNumbers); } @@ -985,7 +985,7 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist incorrectQuorum[0] = bytes1(defaultQuorumNumber + 1); cheats.roll(deregistrationBlockNumber); - cheats.expectRevert("RegistryCoordinator._deregisterOperator: not registered for quorum"); + cheats.expectRevert(bytes4(keccak256("NotRegisteredForQuorum()"))); registryCoordinator._deregisterOperatorExternal(defaultOperator, incorrectQuorum); } @@ -1013,7 +1013,7 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist cheats.prank(defaultOperator); cheats.roll(reregistrationBlockNumber); - cheats.expectRevert("RegistryCoordinator._registerOperator: operator cannot reregister yet"); + cheats.expectRevert(bytes4(keccak256("CannotReregisterYet()"))); registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); } @@ -1214,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.expectRevert(bytes4(keccak256("OnlyEjector()"))); cheats.prank(defaultOperator); registryCoordinator.ejectOperator(defaultOperator, quorumNumbers); } @@ -1463,7 +1463,7 @@ contract RegistryCoordinatorUnitTests_RegisterOperatorWithChurn is RegistryCoord ISignatureUtils.SignatureWithSaltAndExpiry memory signatureWithExpiry = _signOperatorChurnApproval(operatorToRegister, operatorToRegisterId, operatorKickParams, defaultSalt, block.timestamp + 10); cheats.prank(operatorToRegister); - cheats.expectRevert("RegistryCoordinator._validateChurn: incoming operator has insufficient stake for churn"); + cheats.expectRevert(bytes4(keccak256("InsufficientStakeForChurn()"))); registryCoordinator.registerOperatorWithChurn( quorumNumbers, defaultSocket, @@ -1495,7 +1495,7 @@ contract RegistryCoordinatorUnitTests_RegisterOperatorWithChurn is RegistryCoord ISignatureUtils.SignatureWithSaltAndExpiry memory signatureWithExpiry = _signOperatorChurnApproval(operatorToRegister, operatorToRegisterId, operatorKickParams, defaultSalt, block.timestamp + 10); cheats.prank(operatorToRegister); - cheats.expectRevert("RegistryCoordinator._validateChurn: cannot kick operator with more than kickBIPsOfTotalStake"); + cheats.expectRevert(bytes4(keccak256("CannotKickOperatorAboveThreshold()"))); registryCoordinator.registerOperatorWithChurn( quorumNumbers, defaultSocket, @@ -1557,7 +1557,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: signature expired"); + cheats.expectRevert(bytes4(keccak256("SignatureExpired()"))); registryCoordinator.registerOperatorWithChurn( quorumNumbers, defaultSocket, @@ -1678,7 +1678,7 @@ contract RegistryCoordinatorUnitTests_UpdateOperators is RegistryCoordinatorUnit bytes memory quorumNumbers = new bytes(1); quorumNumbers[0] = bytes1(defaultQuorumNumber); - cheats.expectRevert(bytes("RegistryCoordinator.updateOperatorsForQuorum: input length mismatch")); + cheats.expectRevert(bytes4(keccak256("InputLengthMismatch()"))); registryCoordinator.updateOperatorsForQuorum(operatorsToUpdate, quorumNumbers); } @@ -1690,7 +1690,7 @@ contract RegistryCoordinatorUnitTests_UpdateOperators is RegistryCoordinatorUnit bytes memory quorumNumbers = new bytes(1); quorumNumbers[0] = bytes1(defaultQuorumNumber); - cheats.expectRevert(bytes("RegistryCoordinator.updateOperatorsForQuorum: number of updated operators does not match quorum total")); + cheats.expectRevert(bytes4(keccak256("QuorumOperatorCountMismatch()"))); registryCoordinator.updateOperatorsForQuorum(operatorsToUpdate, quorumNumbers); } @@ -1711,7 +1711,7 @@ contract RegistryCoordinatorUnitTests_UpdateOperators is RegistryCoordinatorUnit operatorArray[0] = _incrementAddress(defaultOperator, 1); operatorsToUpdate[0] = operatorArray; - cheats.expectRevert(bytes("RegistryCoordinator.updateOperatorsForQuorum: operator not in quorum")); + cheats.expectRevert(bytes4(keccak256("NotRegisteredForQuorum()"))); registryCoordinator.updateOperatorsForQuorum(operatorsToUpdate, quorumNumbers); } @@ -1739,7 +1739,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 must be sorted")); + cheats.expectRevert(bytes4(keccak256("NotSorted()"))); registryCoordinator.updateOperatorsForQuorum(operatorsToUpdate, quorumNumbers); } @@ -1765,7 +1765,7 @@ contract RegistryCoordinatorUnitTests_UpdateOperators is RegistryCoordinatorUnit operatorArray[1] = defaultOperator; operatorsToUpdate[0] = operatorArray; - cheats.expectRevert(bytes("RegistryCoordinator.updateOperatorsForQuorum: operators must be sorted")); + cheats.expectRevert(bytes4(keccak256("NotSorted()"))); registryCoordinator.updateOperatorsForQuorum(operatorsToUpdate, quorumNumbers); } diff --git a/test/unit/ServiceManagerBase.t.sol b/test/unit/ServiceManagerBase.t.sol index c7ac91e9..8d37f9d6 100644 --- a/test/unit/ServiceManagerBase.t.sol +++ b/test/unit/ServiceManagerBase.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "@openzeppelin/contracts/token/ERC20/presets/ERC20PresetFixedSupply.sol"; import { diff --git a/test/unit/ServiceManagerRouter.t.sol b/test/unit/ServiceManagerRouter.t.sol index 6706ade4..5cf1d3b7 100644 --- a/test/unit/ServiceManagerRouter.t.sol +++ b/test/unit/ServiceManagerRouter.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {ServiceManagerRouter} from "../../src/ServiceManagerRouter.sol"; import "../utils/MockAVSDeployer.sol"; diff --git a/test/unit/StakeRegistryUnit.t.sol b/test/unit/StakeRegistryUnit.t.sol index 6fbe8609..a4f58b85 100644 --- a/test/unit/StakeRegistryUnit.t.sol +++ b/test/unit/StakeRegistryUnit.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; diff --git a/test/unit/Utils.sol b/test/unit/Utils.sol index c143633d..6e085215 100644 --- a/test/unit/Utils.sol +++ b/test/unit/Utils.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; import "eigenlayer-contracts/src/contracts/strategies/StrategyBase.sol"; diff --git a/test/utils/BLSMockAVSDeployer.sol b/test/utils/BLSMockAVSDeployer.sol index 14339d55..7923fca4 100644 --- a/test/utils/BLSMockAVSDeployer.sol +++ b/test/utils/BLSMockAVSDeployer.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {BLSSignatureChecker} from "../../src/BLSSignatureChecker.sol"; import {MockAVSDeployer} from "../utils/MockAVSDeployer.sol"; diff --git a/test/utils/MockAVSDeployer.sol b/test/utils/MockAVSDeployer.sol index 226502ed..800628da 100644 --- a/test/utils/MockAVSDeployer.sol +++ b/test/utils/MockAVSDeployer.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; diff --git a/test/utils/Operators.sol b/test/utils/Operators.sol index 95ab65e9..266ed8d8 100644 --- a/test/utils/Operators.sol +++ b/test/utils/Operators.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "../../src/libraries/BN254.sol"; import "forge-std/Test.sol"; @@ -50,12 +50,12 @@ contract Operators is Test { X: [ readUint(operatorConfigJson, index, "PubkeyG2.X.A1"), readUint(operatorConfigJson, index, "PubkeyG2.X.A0") - ], + ], Y: [ readUint(operatorConfigJson, index, "PubkeyG2.Y.A1"), readUint(operatorConfigJson, index, "PubkeyG2.Y.A0") ] - }); + }); return pubkey; } @@ -68,7 +68,7 @@ contract Operators is Test { uint256 result = 0; for (uint256 i = 0; i < b.length; i++) { if (uint256(uint8(b[i])) >= 48 && uint256(uint8(b[i])) <= 57) { - result = result * 10 + (uint256(uint8(b[i])) - 48); + result = result * 10 + (uint256(uint8(b[i])) - 48); } } return result; diff --git a/test/utils/Owners.sol b/test/utils/Owners.sol index edb577d4..cdb3add2 100644 --- a/test/utils/Owners.sol +++ b/test/utils/Owners.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "forge-std/Test.sol"; import "forge-std/Script.sol"; @@ -29,7 +29,7 @@ contract Owners is Test { for (uint256 i = 0; i < getNumOperators(); i++) { addresses.push(getOwnerAddress(i)); } - return addresses; + return addresses; } function getReputedOwnerAddresses() public returns(address[] memory) { @@ -37,11 +37,11 @@ contract Owners is Test { for (uint256 i = 0; i < getNumOperators(); i++) { addresses.push(getOwnerAddress(i)); } - return addresses; + return addresses; } function resetOwnersConfigJson(string memory newConfig) public { ownersConfigJson = vm.readFile(newConfig); } - + } diff --git a/test/utils/ProofParsing.sol b/test/utils/ProofParsing.sol index f331c2a1..14ad89c6 100644 --- a/test/utils/ProofParsing.sol +++ b/test/utils/ProofParsing.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "../../src/libraries/BN254.sol"; import "forge-std/Test.sol"; @@ -83,7 +83,7 @@ contract ProofParsing is Test{ function getExecutionPayloadProof () public returns(bytes32[7] memory) { for (uint i = 0; i < 7; i++) { prefix = string.concat(".ExecutionPayloadProof[", string.concat(vm.toString(i), "]")); - executionPayloadProof[i] = (stdJson.readBytes32(proofConfigJson, prefix)); + executionPayloadProof[i] = (stdJson.readBytes32(proofConfigJson, prefix)); } return executionPayloadProof; } @@ -91,7 +91,7 @@ contract ProofParsing is Test{ function getTimestampProof() public returns(bytes32[4] memory) { for (uint i = 0; i < 4; i++) { prefix = string.concat(".TimestampProof[", string.concat(vm.toString(i), "]")); - timestampProofs[i] = (stdJson.readBytes32(proofConfigJson, prefix)); + timestampProofs[i] = (stdJson.readBytes32(proofConfigJson, prefix)); } return timestampProofs; } @@ -99,7 +99,7 @@ contract ProofParsing is Test{ function getBlockHeaderProof() public returns(bytes32[18] memory) { for (uint i = 0; i < 18; i++) { prefix = string.concat(".BlockHeaderProof[", string.concat(vm.toString(i), "]")); - blockHeaderProof[i] = (stdJson.readBytes32(proofConfigJson, prefix)); + blockHeaderProof[i] = (stdJson.readBytes32(proofConfigJson, prefix)); } return blockHeaderProof; } @@ -107,7 +107,7 @@ contract ProofParsing is Test{ function getSlotProof() public returns(bytes32[3] memory) { for (uint i = 0; i < 3; i++) { prefix = string.concat(".SlotProof[", string.concat(vm.toString(i), "]")); - slotProof[i] = (stdJson.readBytes32(proofConfigJson, prefix)); + slotProof[i] = (stdJson.readBytes32(proofConfigJson, prefix)); } return slotProof; } @@ -116,7 +116,7 @@ contract ProofParsing is Test{ bytes32[] memory stateRootProof = new bytes32[](3); for (uint i = 0; i < 3; i++) { prefix = string.concat(".StateRootAgainstLatestBlockHeaderProof[", string.concat(vm.toString(i), "]")); - stateRootProof[i] = (stdJson.readBytes32(proofConfigJson, prefix)); + stateRootProof[i] = (stdJson.readBytes32(proofConfigJson, prefix)); } return stateRootProof; } @@ -124,7 +124,7 @@ contract ProofParsing is Test{ function getWithdrawalProof() public returns(bytes32[9] memory) { for (uint i = 0; i < 9; i++) { prefix = string.concat(".WithdrawalProof[", string.concat(vm.toString(i), "]")); - withdrawalProof[i] = (stdJson.readBytes32(proofConfigJson, prefix)); + withdrawalProof[i] = (stdJson.readBytes32(proofConfigJson, prefix)); } return withdrawalProof; } @@ -132,7 +132,7 @@ contract ProofParsing is Test{ function getValidatorProof() public returns(bytes32[46] memory) { for (uint i = 0; i < 46; i++) { prefix = string.concat(".ValidatorProof[", string.concat(vm.toString(i), "]")); - validatorProof[i] = (stdJson.readBytes32(proofConfigJson, prefix)); + validatorProof[i] = (stdJson.readBytes32(proofConfigJson, prefix)); } return validatorProof; } @@ -140,16 +140,16 @@ contract ProofParsing is Test{ function getHistoricalSummaryProof() public returns(bytes32[44] memory) { for (uint i = 0; i < 44; i++) { prefix = string.concat(".HistoricalSummaryProof[", string.concat(vm.toString(i), "]")); - historicalSummaryProof[i] = (stdJson.readBytes32(proofConfigJson, prefix)); + historicalSummaryProof[i] = (stdJson.readBytes32(proofConfigJson, prefix)); } return historicalSummaryProof; } - + function getWithdrawalFields() public returns(bytes32[] memory) { bytes32[] memory withdrawalFields = new bytes32[](4); for (uint i = 0; i < 4; i++) { prefix = string.concat(".WithdrawalFields[", string.concat(vm.toString(i), "]")); - withdrawalFields[i] = (stdJson.readBytes32(proofConfigJson, prefix)); + withdrawalFields[i] = (stdJson.readBytes32(proofConfigJson, prefix)); } return withdrawalFields; @@ -159,7 +159,7 @@ contract ProofParsing is Test{ bytes32[] memory validatorFields = new bytes32[](8); for (uint i = 0; i < 8; i++) { prefix = string.concat(".ValidatorFields[", string.concat(vm.toString(i), "]")); - validatorFields[i] = (stdJson.readBytes32(proofConfigJson, prefix)); + validatorFields[i] = (stdJson.readBytes32(proofConfigJson, prefix)); } return validatorFields; } @@ -168,7 +168,7 @@ contract ProofParsing is Test{ bytes32[] memory validatorBalanceProof = new bytes32[](44); for (uint i = 0; i < 44; i++) { prefix = string.concat(".ValidatorBalanceProof[", string.concat(vm.toString(i), "]")); - validatorBalanceProof[i] = (stdJson.readBytes32(proofConfigJson, prefix)); + validatorBalanceProof[i] = (stdJson.readBytes32(proofConfigJson, prefix)); } return validatorBalanceProof; } @@ -177,7 +177,7 @@ contract ProofParsing is Test{ bytes32[] memory balanceUpdateSlotProof = new bytes32[](5); for (uint i = 0; i < 5; i++) { prefix = string.concat(".slotProof[", string.concat(vm.toString(i), "]")); - balanceUpdateSlotProof[i] = (stdJson.readBytes32(proofConfigJson, prefix)); + balanceUpdateSlotProof[i] = (stdJson.readBytes32(proofConfigJson, prefix)); } return balanceUpdateSlotProof; } @@ -186,7 +186,7 @@ contract ProofParsing is Test{ bytes32[] memory withdrawalCredenitalProof = new bytes32[](46); for (uint i = 0; i < 46; i++) { prefix = string.concat(".WithdrawalCredentialProof[", string.concat(vm.toString(i), "]")); - withdrawalCredenitalProof[i] = (stdJson.readBytes32(proofConfigJson, prefix)); + withdrawalCredenitalProof[i] = (stdJson.readBytes32(proofConfigJson, prefix)); } return withdrawalCredenitalProof; } @@ -195,7 +195,7 @@ contract ProofParsing is Test{ bytes32[] memory validatorFieldsProof = new bytes32[](46); for (uint i = 0; i < 46; i++) { prefix = string.concat(".ValidatorFieldsProof[", string.concat(vm.toString(i), "]")); - validatorFieldsProof[i] = (stdJson.readBytes32(proofConfigJson, prefix)); + validatorFieldsProof[i] = (stdJson.readBytes32(proofConfigJson, prefix)); } return validatorFieldsProof; } diff --git a/test/utils/SignatureCompaction.sol b/test/utils/SignatureCompaction.sol index c10b2db1..0bb98a1c 100644 --- a/test/utils/SignatureCompaction.sol +++ b/test/utils/SignatureCompaction.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; From 1cc4983adebafdef3c74a6f491ccb187369cc47b Mon Sep 17 00:00:00 2001 From: steven Date: Mon, 16 Dec 2024 09:58:04 -0500 Subject: [PATCH 39/80] chore: bump core dependency --- lib/eigenlayer-contracts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/eigenlayer-contracts b/lib/eigenlayer-contracts index 881485b0..85e12bab 160000 --- a/lib/eigenlayer-contracts +++ b/lib/eigenlayer-contracts @@ -1 +1 @@ -Subproject commit 881485b008a61b8512172192214b88b873b9dc61 +Subproject commit 85e12bab5f3d4c6ff21790fe5909755ee7154b07 From bc64bb2f662858cc2b3fe295b05cfe9c727ba700 Mon Sep 17 00:00:00 2001 From: steven Date: Mon, 16 Dec 2024 10:55:17 -0500 Subject: [PATCH 40/80] feat: add zeus output json and update parsing of addresses --- script/deployments/17000-preprod.json | 1 + script/utils/CoreDeploymentLib.sol | 30 +++++++++++++++++++++++++-- 2 files changed, 29 insertions(+), 2 deletions(-) create mode 100644 script/deployments/17000-preprod.json diff --git a/script/deployments/17000-preprod.json b/script/deployments/17000-preprod.json new file mode 100644 index 00000000..613248fa --- /dev/null +++ b/script/deployments/17000-preprod.json @@ -0,0 +1 @@ +{"ZEUS_ENV":"preprod","ZEUS_ENV_COMMIT":"26dcafff7bd304b84f81ba36fe70ef44d414a752","ZEUS_TEST":"false","ZEUS_ENV_VERSION":"1.0.0","ZEUS_VERSION":"0.3.0","ZEUS_ENV_communityMultisig":"0x42789cCdE6486b81c7D41Fa3759A8BB74D7909DB","ZEUS_ENV_executorMultisig":"0x84E5e0D6f6c3153057bd5d661Be1ff1766cEac08","ZEUS_ENV_protocolCouncilMultisig":"0x314A6B61B5CBFA79dc7b2c0416e039822E886b54","ZEUS_ENV_operationsMultisig":"0x6d609cD2812bDA02a75dcABa7DaafE4B20Ff5608","ZEUS_ENV_pauserMultisig":"0x546D3b66B27dCb94777d7eFC3825b10675FE5D95","ZEUS_ENV_pauserRegistry":"0x9Ab2FEAf0465f0eD51Fc2b663eF228B418c9Dad1","ZEUS_ENV_proxyAdmin":"0x1BEF05C7303d44e0E2FCD2A19d993eDEd4c51b5B","ZEUS_ENV_timelockController":"0x7c66A1e862E11C4887270aBd649157ACe837A2D0","ZEUS_ENV_timelockController_BEIGEN":"0x68fa0fB9eDe34745C41d85E2766ee32eBc6cEF0e","ZEUS_ENV_beigenExecutorMultisig":"0xFbb2c01A19A85c4C648186a97d879EEA25cE1397","ZEUS_ENV_foundationMultisig":"0x9c36f012585c8bb4247eEe18C01Cdfc5f2e90336","ZEUS_ENV_ethPOS":"0x4242424242424242424242424242424242424242","ZEUS_ENV_MultiSendCallOnly":"0x40A2aCCbd92BCA938b02010E17A5b8929b49130D","ZEUS_ENV_MIN_WITHDRAWAL_DELAY":50,"ZEUS_ENV_ALLOCATION_CONFIGURATION_DELAY":75,"ZEUS_ENV_EIGENPOD_GENESIS_TIME":1695902400,"ZEUS_ENV_REWARDS_COORDINATOR_CALCULATION_INTERVAL_SECONDS":86400,"ZEUS_ENV_REWARDS_COORDINATOR_MAX_REWARDS_DURATION":6048000,"ZEUS_ENV_REWARDS_COORDINATOR_MAX_RETROACTIVE_LENGTH":7776000,"ZEUS_ENV_REWARDS_COORDINATOR_MAX_FUTURE_LENGTH":2592000,"ZEUS_ENV_REWARDS_COORDINATOR_GENESIS_REWARDS_TIMESTAMP":1710979200,"ZEUS_ENV_REWARDS_COORDINATOR_INIT_PAUSED_STATUS":0,"ZEUS_ENV_REWARDS_COORDINATOR_UPDATER":"0x18a0f92Ad9645385E8A8f3db7d0f6CF7aBBb0aD4","ZEUS_ENV_REWARDS_COORDINATOR_ACTIVATION_DELAY":120,"ZEUS_ENV_REWARDS_COORDINATOR_DEFAULT_OPERATOR_SPLIT_BIPS":1000,"ZEUS_ENV_REWARDS_COORDINATOR_PAUSE_STATUS":2,"ZEUS_DEPLOYED_PauserRegistry_Impl":"0x50712285cE831a6B9a11214A430f28999A5b4DAe","ZEUS_DEPLOYED_AVSDirectory_Proxy":"0x141d6995556135D4997b2ff72EB443Be300353bC","ZEUS_DEPLOYED_AVSDirectory_Impl":"0xD5597C5c574BbD4Ce76C3aaF2900525De97aD711","ZEUS_DEPLOYED_EigenPod_Beacon":"0x92Cc4a800A1513E85C481dDDf3A06C6921211eaC","ZEUS_DEPLOYED_EigenPod_Impl":"0xf53F78382a26b62A3aC3a3837a8dED2044a2103D","ZEUS_DEPLOYED_EigenPodManager_Proxy":"0xB8d8952f572e67B11e43bC21250967772fa883Ff","ZEUS_DEPLOYED_EigenPodManager_Impl":"0xe55A9EA92b1134afF44fB81e667FD5f74e6a47e5","ZEUS_DEPLOYED_DelegationManager_Proxy":"0x75dfE5B44C2E530568001400D3f704bC8AE350CC","ZEUS_DEPLOYED_DelegationManager_Impl":"0xa61d360551d05715046491eEc8a8Cb90f6867545","ZEUS_DEPLOYED_RewardsCoordinator_Proxy":"0xb22Ef643e1E067c994019A4C19e403253C05c2B0","ZEUS_DEPLOYED_RewardsCoordinator_Impl":"0x480e9045F62F20b8B8342164011305f223D48B2b","ZEUS_DEPLOYED_EigenStrategy_Proxy":"0x4e0125f8a928Eb1b9dB4BeDd3756BA3c200563C2","ZEUS_DEPLOYED_EigenStrategy_Impl":"0xdB01e5178E4745346B230fdF2d6Da2F483a71EA4","ZEUS_DEPLOYED_Eigen_Proxy":"0xD58f6844f79eB1fbd9f7091d05f7cb30d3363926","ZEUS_DEPLOYED_Eigen_Impl":"0x95a7431400F362F3647a69535C5666cA0133CAA0","ZEUS_DEPLOYED_BackingEigen_Proxy":"0xA72942289a043874249E60469F68f08B8c6ECCe8","ZEUS_DEPLOYED_BackingEigen_Impl":"0xd5FdabDac3d8ACeAB7BFfDDFA18877A4c5D5Aa82","ZEUS_DEPLOYED_StrategyBase_Beacon":"0xf2c2AcA859C685895E60ca7A14274365b64c0c2a","ZEUS_DEPLOYED_StrategyBase_Impl":"0x3Dc582A90b920AA6aa0204e0517d6767C9C8c268","ZEUS_DEPLOYED_StrategyManager_Proxy":"0xF9fbF2e35D8803273E214c99BF15174139f4E67a","ZEUS_DEPLOYED_StrategyManager_Impl":"0x158E6FBFb5FAc98894BF0Da28998efcB3Ba1C58A","ZEUS_DEPLOYED_StrategyFactory_Proxy":"0xad4a89e3ca9b3dc25aabe0aa7d72e61d2ec66052","ZEUS_DEPLOYED_StrategyFactory_Impl":"0x203654Ea1e39d00983A9cE0457B2d2efC188fC40","ZEUS_DEPLOYED_StrategyBaseTVLLimits_Impl":"0x98bD5748dc964e85400B3A5D0152c444201C577a","ZEUS_DEPLOYED_PermissionController_Impl":"0x259597c0AEc3c9978D24e892225f2F4Ac142d885","ZEUS_DEPLOYED_PermissionController_Proxy":"0xa2348c77802238Db39f0CefAa500B62D3FDD682b","ZEUS_DEPLOYED_AllocationManager_Impl":"0xc7618DC8607503C15f4393782068B79655104A24","ZEUS_DEPLOYED_AllocationManager_Proxy":"0xFdD5749e11977D60850E06bF5B13221Ad95eb6B4","ZEUS_DEPLOYED_StrategyBaseTVLLimits_0":"0x6e5d5060b33ca2090a78e9cb74fe207453b30e49","ZEUS_DEPLOYED_StrategyBaseTVLLimits_1":"0xf6a09ae03d7760aecf1626ce7df0f113bec2d9bd","ZEUS_DEPLOYED_StrategyBaseTVLLimits_2":"0x7fa77c321bf66e42eabc9b10129304f7f90c5585","ZEUS_DEPLOYED_StrategyBaseTVLLimits_3":"0x24da526f9e465c4fb6bae41e226df8aa5b34eac7","ZEUS_DEPLOYED_StrategyBaseTVLLimits_4":"0x6dc6ce589f852f96ac86cb160ab0b15b9f56dedd","ZEUS_DEPLOYED_StrategyBaseTVLLimits_5":"0x3c28437e610fb099cc3d6de4d9c707dfacd308ae","ZEUS_DEPLOYED_StrategyBaseTVLLimits_6":"0x7b6257f5caf7311b36f7416133a8499c68a83c3a","ZEUS_DEPLOYED_StrategyBaseTVLLimits_7":"0xc86382179500e8ed3e686fc4a99ed9ec72df3f56","ZEUS_DEPLOYED_StrategyBaseTVLLimits_8":"0x3cb1fd19cfb178c1098f2fc1e11090a0642b2314","ZEUS_DEPLOYED_StrategyBaseTVLLimits_9":"0x87f6c7d24b109919eb38295e3f8298425e6331d9","ZEUS_DEPLOYED_StrategyBaseTVLLimits_10":"0x5c8b55722f421556a2aafb7a3ea63d4c3e514312","ZEUS_DEPLOYED_StrategyBaseTVLLimits_11":"0xd523267698c81a372191136e477fdebfa33d9fb4"} diff --git a/script/utils/CoreDeploymentLib.sol b/script/utils/CoreDeploymentLib.sol index 8043ec1a..dc406b2a 100644 --- a/script/utils/CoreDeploymentLib.sol +++ b/script/utils/CoreDeploymentLib.sol @@ -3,8 +3,11 @@ pragma solidity ^0.8.12; import {Vm} from "forge-std/Vm.sol"; +import {stdJson} from "forge-std/StdJson.sol"; library CoreDeploymentLib { + using stdJson for string; + Vm internal constant vm = Vm(address(uint160(uint256(keccak256("hevm cheat code"))))); struct StrategyManagerConfig { @@ -54,8 +57,31 @@ library CoreDeploymentLib { address strategyBeacon; } - function readCoreDeploymentJson(string memory path, uint256 chainId) internal pure returns (CoreDeploymentLib.DeploymentData memory) { - /// TODO: implement logic based on hw + function readCoreDeploymentJson(string memory path, uint256 chainId) internal view returns (CoreDeploymentLib.DeploymentData memory) { + string memory filePath = string(abi.encodePacked(path, "/", chainId, ".json")); + return parseCoreJson(filePath); + } + + function readCoreDeploymentJson(string memory path, uint256 chainId, string memory environment) internal view returns (CoreDeploymentLib.DeploymentData memory) { + string memory filePath = string(abi.encodePacked(path, "/", chainId, "-", environment, ".json")); + return parseCoreJson(filePath); } + function parseCoreJson(string memory filePath) internal view returns (CoreDeploymentLib.DeploymentData memory) { + string memory json = vm.readFile(filePath); + CoreDeploymentLib.DeploymentData memory deploymentData; + + deploymentData.delegationManager = json.readAddress(".ZEUS_DEPLOYED_DelegationManager_Proxy"); + deploymentData.avsDirectory = json.readAddress(".ZEUS_DEPLOYED_AVSDirectory_Proxy"); + deploymentData.allocationManager = json.readAddress(".ZEUS_DEPLOYED_AllocationManager_Proxy"); + deploymentData.strategyManager = json.readAddress(".ZEUS_DEPLOYED_StrategyManager_Proxy"); + deploymentData.eigenPodManager = json.readAddress(".ZEUS_DEPLOYED_EigenPodManager_Proxy"); + deploymentData.rewardsCoordinator = json.readAddress(".ZEUS_DEPLOYED_RewardsCoordinator_Proxy"); + deploymentData.eigenPodBeacon = json.readAddress(".ZEUS_DEPLOYED_EigenPod_Beacon"); + deploymentData.pauserRegistry = json.readAddress(".ZEUS_DEPLOYED_PauserRegistry_Impl"); + deploymentData.strategyFactory = json.readAddress(".ZEUS_DEPLOYED_StrategyFactory_Proxy"); + deploymentData.strategyBeacon = json.readAddress(".ZEUS_DEPLOYED_StrategyBase_Beacon"); + + return deploymentData; + } } \ No newline at end of file From 09de5711c5f45917bd37463154e9b11abc4e64dc Mon Sep 17 00:00:00 2001 From: steven Date: Mon, 16 Dec 2024 11:12:49 -0500 Subject: [PATCH 41/80] test: parsing zeus output --- script/utils/CoreDeploymentLib.sol | 24 +++++++++++++++------- test/unit/ZeusDeploymentTests.t.sol | 32 +++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 7 deletions(-) create mode 100644 test/unit/ZeusDeploymentTests.t.sol diff --git a/script/utils/CoreDeploymentLib.sol b/script/utils/CoreDeploymentLib.sol index dc406b2a..15e9ba22 100644 --- a/script/utils/CoreDeploymentLib.sol +++ b/script/utils/CoreDeploymentLib.sol @@ -3,6 +3,7 @@ pragma solidity ^0.8.12; import {Vm} from "forge-std/Vm.sol"; +import {console2 as console} from "forge-std/Test.sol"; import {stdJson} from "forge-std/StdJson.sol"; library CoreDeploymentLib { @@ -55,20 +56,25 @@ library CoreDeploymentLib { address pauserRegistry; address strategyFactory; address strategyBeacon; + address eigenStrategy; + address eigen; + address backingEigen; + address permissionController; } - function readCoreDeploymentJson(string memory path, uint256 chainId) internal view returns (CoreDeploymentLib.DeploymentData memory) { - string memory filePath = string(abi.encodePacked(path, "/", chainId, ".json")); - return parseCoreJson(filePath); + function readCoreDeploymentJson(string memory path, uint256 chainId) internal returns (CoreDeploymentLib.DeploymentData memory) { + string memory filePath = string(abi.encodePacked(path, "/", vm.toString(chainId), ".json")); + return parseZeusJson(filePath); } - function readCoreDeploymentJson(string memory path, uint256 chainId, string memory environment) internal view returns (CoreDeploymentLib.DeploymentData memory) { - string memory filePath = string(abi.encodePacked(path, "/", chainId, "-", environment, ".json")); - return parseCoreJson(filePath); + function readCoreDeploymentJson(string memory path, uint256 chainId, string memory environment) internal returns (CoreDeploymentLib.DeploymentData memory) { + string memory filePath = string(abi.encodePacked(path, "/", vm.toString(chainId), "-", environment, ".json")); + return parseZeusJson(filePath); } - function parseCoreJson(string memory filePath) internal view returns (CoreDeploymentLib.DeploymentData memory) { + function parseZeusJson(string memory filePath) internal returns (CoreDeploymentLib.DeploymentData memory) { string memory json = vm.readFile(filePath); + require(vm.exists(filePath), "Deployment file does not exist"); CoreDeploymentLib.DeploymentData memory deploymentData; deploymentData.delegationManager = json.readAddress(".ZEUS_DEPLOYED_DelegationManager_Proxy"); @@ -81,6 +87,10 @@ library CoreDeploymentLib { deploymentData.pauserRegistry = json.readAddress(".ZEUS_DEPLOYED_PauserRegistry_Impl"); deploymentData.strategyFactory = json.readAddress(".ZEUS_DEPLOYED_StrategyFactory_Proxy"); deploymentData.strategyBeacon = json.readAddress(".ZEUS_DEPLOYED_StrategyBase_Beacon"); + deploymentData.eigenStrategy = json.readAddress(".ZEUS_DEPLOYED_EigenStrategy_Proxy"); + deploymentData.eigen = json.readAddress(".ZEUS_DEPLOYED_Eigen_Proxy"); + deploymentData.backingEigen = json.readAddress(".ZEUS_DEPLOYED_BackingEigen_Proxy"); + deploymentData.permissionController = json.readAddress(".ZEUS_DEPLOYED_PermissionController_Proxy"); return deploymentData; } diff --git a/test/unit/ZeusDeploymentTests.t.sol b/test/unit/ZeusDeploymentTests.t.sol new file mode 100644 index 00000000..19647a74 --- /dev/null +++ b/test/unit/ZeusDeploymentTests.t.sol @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.12; + +import {Test, console2 as console} from "forge-std/Test.sol"; +import {CoreDeploymentLib } from "../../script/utils/CoreDeploymentLib.sol"; + +contract ZeusDeploymentTests is Test { + using CoreDeploymentLib for string; + + function testParseZeusJson() public { + string memory path = "script/deployments"; + uint256 chainId = 17000; + string memory environment = "preprod"; + + CoreDeploymentLib.DeploymentData memory deploymentData = CoreDeploymentLib.readCoreDeploymentJson(path, chainId, environment); + + assertEq(deploymentData.delegationManager, 0x75dfE5B44C2E530568001400D3f704bC8AE350CC); + assertEq(deploymentData.avsDirectory, 0x141d6995556135D4997b2ff72EB443Be300353bC); + assertEq(deploymentData.allocationManager, 0xFdD5749e11977D60850E06bF5B13221Ad95eb6B4); + assertEq(deploymentData.strategyManager, 0xF9fbF2e35D8803273E214c99BF15174139f4E67a); + assertEq(deploymentData.eigenPodManager, 0xB8d8952f572e67B11e43bC21250967772fa883Ff); + assertEq(deploymentData.rewardsCoordinator, 0xb22Ef643e1E067c994019A4C19e403253C05c2B0); + assertEq(deploymentData.eigenPodBeacon, 0x92Cc4a800A1513E85C481dDDf3A06C6921211eaC); + assertEq(deploymentData.pauserRegistry, 0x50712285cE831a6B9a11214A430f28999A5b4DAe); + assertEq(deploymentData.strategyFactory, 0xad4A89E3cA9b3dc25AABe0aa7d72E61D2Ec66052); + assertEq(deploymentData.strategyBeacon, 0xf2c2AcA859C685895E60ca7A14274365b64c0c2a); + assertEq(deploymentData.eigenStrategy, 0x4e0125f8a928Eb1b9dB4BeDd3756BA3c200563C2); + assertEq(deploymentData.eigen, 0xD58f6844f79eB1fbd9f7091d05f7cb30d3363926); + assertEq(deploymentData.backingEigen, 0xA72942289a043874249E60469F68f08B8c6ECCe8); + assertEq(deploymentData.permissionController, 0xa2348c77802238Db39f0CefAa500B62D3FDD682b); + } +} From b1111e0100ed99eea5de9d14a56c694cad1d59ac Mon Sep 17 00:00:00 2001 From: steven Date: Mon, 16 Dec 2024 15:46:16 -0500 Subject: [PATCH 42/80] feat: add from scratch functionality and read zeus config --- .../17000-preprod.json | 0 script/utils/CoreDeploymentLib.sol | 240 ++++++++++++++++++ ...tchLib.sol => MiddlewareDeploymentLib.sol} | 2 +- script/utils/OperatorSetUpgradeLib.sol | 91 ++++--- test/unit/ZeusDeploymentTests.t.sol | 2 +- 5 files changed, 291 insertions(+), 44 deletions(-) rename script/{deployments => config}/17000-preprod.json (100%) rename script/utils/{FromScratchLib.sol => MiddlewareDeploymentLib.sol} (99%) diff --git a/script/deployments/17000-preprod.json b/script/config/17000-preprod.json similarity index 100% rename from script/deployments/17000-preprod.json rename to script/config/17000-preprod.json diff --git a/script/utils/CoreDeploymentLib.sol b/script/utils/CoreDeploymentLib.sol index 15e9ba22..0b711df1 100644 --- a/script/utils/CoreDeploymentLib.sol +++ b/script/utils/CoreDeploymentLib.sol @@ -6,11 +6,51 @@ import {Vm} from "forge-std/Vm.sol"; import {console2 as console} from "forge-std/Test.sol"; import {stdJson} from "forge-std/StdJson.sol"; +import {UpgradeableBeacon} from "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol"; + +import {UpgradeableProxyLib} from "./UpgradeableProxyLib.sol"; +import {StakeRegistry} from "../../src/StakeRegistry.sol"; +import {BLSApkRegistry} from "../../src/BLSApkRegistry.sol"; +import {IndexRegistry} from "../../src/IndexRegistry.sol"; +import {RegistryCoordinator} from "../../src/RegistryCoordinator.sol"; +import {IRegistryCoordinator} from "../../src/interfaces/IRegistryCoordinator.sol"; +import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.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 {IAllocationManager} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; +import {IPermissionController} from "eigenlayer-contracts/src/contracts/interfaces/IPermissionController.sol"; +import {IRewardsCoordinator} from "eigenlayer-contracts/src/contracts/interfaces/IRewardsCoordinator.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 {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; +import {StrategyFactory} from "eigenlayer-contracts/src/contracts/strategies/StrategyFactory.sol"; + + + library CoreDeploymentLib { using stdJson for string; Vm internal constant vm = Vm(address(uint160(uint256(keccak256("hevm cheat code"))))); + struct AVSDirectoryConfig { + uint256 initialPausedStatus; + } struct StrategyManagerConfig { uint256 initPausedStatus; uint256 initWithdrawalDelayBlocks; @@ -23,6 +63,7 @@ library CoreDeploymentLib { struct DelegationManagerConfig { uint256 initPausedStatus; uint256 withdrawalDelayBlocks; + uint256 globalOperatorCommissionBips; } struct EigenPodManagerConfig { @@ -30,11 +71,13 @@ library CoreDeploymentLib { } struct RewardsCoordinatorConfig { + address rewardsUpdater; uint256 initPausedStatus; uint256 maxRewardsDuration; uint256 maxRetroactiveLength; uint256 maxFutureLength; uint256 genesisRewardsTimestamp; + uint256 defaultOperatorSplitBips; address updater; uint256 activationDelay; uint256 calculationIntervalSeconds; @@ -45,6 +88,16 @@ library CoreDeploymentLib { uint256 initPausedStatus; } + struct DeploymentConfig { + StrategyManagerConfig strategyManager; + AVSDirectoryConfig avsDirectory; + SlasherConfig slasher; + DelegationManagerConfig delegationManager; + EigenPodManagerConfig eigenPodManager; + RewardsCoordinatorConfig rewardsCoordinator; + StrategyFactoryConfig strategyFactory; + } + struct DeploymentData { address delegationManager; address avsDirectory; @@ -62,6 +115,193 @@ library CoreDeploymentLib { address permissionController; } + function deployCore( + address proxyAdmin, + DeploymentConfig memory config + ) internal returns (DeploymentData memory) { + DeploymentData memory result; + + // Set up empty proxies for each contract + 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 implementation contracts + address delegationManagerImpl = address( + new DelegationManager( + IStrategyManager(result.strategyManager), + IEigenPodManager(result.eigenPodManager), + IAllocationManager(result.allocationManager), + IPauserRegistry(result.pauserRegistry), + IPermissionController(result.permissionController), + uint32(config.delegationManager.initPausedStatus) + ) + ); + address avsDirectoryImpl = address( + new AVSDirectory( + IDelegationManager(result.delegationManager), + IPauserRegistry(result.pauserRegistry) + ) + ); + address strategyManagerImpl = address( + new StrategyManager( + IDelegationManager(result.delegationManager), + IPauserRegistry(result.pauserRegistry) + ) + ); + address strategyFactoryImpl = address(new StrategyFactory( + IStrategyManager(result.strategyManager), + IPauserRegistry(result.pauserRegistry) + )); + + address ethPOSDeposit; + if (block.chainid == 1) { + ethPOSDeposit = 0x00000000219ab540356cBB839Cbe05303d7705Fa; + } else { + // Handle non-mainnet chains + /// TODO: Handle Eth pos + } + + address eigenPodManagerImpl = address( + new EigenPodManager( + IETHPOSDeposit(ethPOSDeposit), + IBeacon(result.eigenPodBeacon), + IDelegationManager(result.delegationManager), + IPauserRegistry(result.pauserRegistry) + ) + ); + + /// 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), + IAllocationManager(result.allocationManager), + IPauserRegistry(result.pauserRegistry), + IPermissionController(result.permissionController), + 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), IPauserRegistry(result.pauserRegistry))); + /// 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.encodeCall( + DelegationManager.initialize, + ( + proxyAdmin, // initialOwner + config.delegationManager.initPausedStatus // initialPausedStatus + ) + ); + UpgradeableProxyLib.upgradeAndCall( + result.delegationManager, delegationManagerImpl, upgradeCall + ); + + // Upgrade StrategyManager contract + upgradeCall = abi.encodeCall( + StrategyManager.initialize, + ( + proxyAdmin, // initialOwner + result.strategyFactory, // initialStrategyWhitelister + config.strategyManager.initPausedStatus // initialPausedStatus + ) + ); + UpgradeableProxyLib.upgradeAndCall(result.strategyManager, strategyManagerImpl, upgradeCall); + + // Upgrade StrategyFactory contract + upgradeCall = abi.encodeCall( + StrategyFactory.initialize, + ( + proxyAdmin, // initialOwner + config.strategyFactory.initPausedStatus, // initialPausedStatus + IBeacon(result.strategyBeacon) // _strategyBeacon + ) + ); + UpgradeableProxyLib.upgradeAndCall(result.strategyFactory, strategyFactoryImpl, upgradeCall); + + // Upgrade EigenPodManager contract + upgradeCall = abi.encodeCall( + EigenPodManager.initialize, + ( + proxyAdmin, // initialOwner + config.eigenPodManager.initPausedStatus // initialPausedStatus + ) + ); + UpgradeableProxyLib.upgradeAndCall(result.eigenPodManager, eigenPodManagerImpl, upgradeCall); + + // Upgrade AVSDirectory contract + upgradeCall = abi.encodeCall( + AVSDirectory.initialize, + ( + proxyAdmin, // initialOwner + config.avsDirectory.initialPausedStatus // initialPausedStatus + ) + ); + UpgradeableProxyLib.upgradeAndCall(result.avsDirectory, avsDirectoryImpl, upgradeCall); + + // Upgrade RewardsCoordinator contract + upgradeCall = abi.encodeCall( + RewardsCoordinator.initialize, + ( + proxyAdmin, // initialOwner + config.rewardsCoordinator.initPausedStatus, // initialPausedStatus + config.rewardsCoordinator.rewardsUpdater, // rewardsUpdater + uint32(config.rewardsCoordinator.activationDelay), // _activationDelay + uint16(config.rewardsCoordinator.defaultOperatorSplitBips) // _defaultSplitBips + ) + ); + 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; + } + + function readCoreDeploymentJson(string memory path, uint256 chainId) internal returns (CoreDeploymentLib.DeploymentData memory) { string memory filePath = string(abi.encodePacked(path, "/", vm.toString(chainId), ".json")); return parseZeusJson(filePath); diff --git a/script/utils/FromScratchLib.sol b/script/utils/MiddlewareDeploymentLib.sol similarity index 99% rename from script/utils/FromScratchLib.sol rename to script/utils/MiddlewareDeploymentLib.sol index a1a8790d..63fb380e 100644 --- a/script/utils/FromScratchLib.sol +++ b/script/utils/MiddlewareDeploymentLib.sol @@ -24,7 +24,7 @@ import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy import {PauserRegistry, IPauserRegistry} from "eigenlayer-contracts/src/contracts/permissions/PauserRegistry.sol"; import {OperatorStateRetriever} from "../../src/OperatorStateRetriever.sol"; -library DeploymentLib { +library MiddlewareDeploymentLib { using stdJson for *; using Strings for *; using UpgradeableProxyLib for address; diff --git a/script/utils/OperatorSetUpgradeLib.sol b/script/utils/OperatorSetUpgradeLib.sol index 30e921dc..67222c43 100644 --- a/script/utils/OperatorSetUpgradeLib.sol +++ b/script/utils/OperatorSetUpgradeLib.sol @@ -2,51 +2,58 @@ 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; - - // address(uint160(uint256(keccak256("hevm cheat code")))) == 0x7109709ECfa91a80626fF3989D68f67F5b1DD12D - // solhint-disable-next-line const-name-snakecase - Vm private constant vm = Vm(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D); - - /** - * @dev Storage slot with the address of the current implementation. - * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1. - */ - bytes32 internal constant IMPLEMENTATION_SLOT = - 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; - - /** - * @dev Storage slot with the admin of the contract. - * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1. - */ - 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); +import {CoreDeploymentLib} from "./CoreDeploymentLib.sol"; +import {MiddlewareDeploymentLib} from "./MiddlewareDeploymentLib.sol"; + +import {StakeRegistry} from "../../src/StakeRegistry.sol"; +import {BLSApkRegistry} from "../../src/BLSApkRegistry.sol"; +import {IndexRegistry} from "../../src/IndexRegistry.sol"; +import {RegistryCoordinator} from "../../src/RegistryCoordinator.sol"; +import {IRegistryCoordinator} from "../../src/interfaces/IRegistryCoordinator.sol"; +import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; +import {IAVSDirectory} from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol"; +import {IServiceManager} from "../../src/interfaces/IServiceManager.sol"; +import {IBLSApkRegistry} from "../../src/interfaces/IBLSApkRegistry.sol"; +import {IStakeRegistry} from "../../src/interfaces/IStakeRegistry.sol"; +import {IIndexRegistry} from "../../src/interfaces/IIndexRegistry.sol"; +import {IPauserRegistry} from "eigenlayer-contracts/src/contracts/permissions/PauserRegistry.sol"; + + +library SlashingUpgradeLib { + using CoreDeploymentLib for string; + + function parseCoreDeploymentJson(string memory path, uint256 chainId) internal returns (CoreDeploymentLib.DeploymentData memory) { + return CoreDeploymentLib.readCoreDeploymentJson(path, chainId); } - function getAdmin(address proxy) internal view returns (address) { - bytes32 value = vm.load(proxy, ADMIN_SLOT); - return address(uint160(uint256(value))); + function parseCoreDeploymentJson(string memory path, uint256 chainId, string memory environment) internal returns (CoreDeploymentLib.DeploymentData memory) { + return CoreDeploymentLib.readCoreDeploymentJson(path, chainId, environment); } - function getImplementation(address proxy) internal view returns (address) { - bytes32 value = vm.load(proxy, IMPLEMENTATION_SLOT); - return address(uint160(uint256(value))); + function deployNewImplementations( + CoreDeploymentLib.DeploymentData memory core, + MiddlewareDeploymentLib.DeploymentData memory deployment + ) internal { + address stakeRegistryImpl = address( + new StakeRegistry( + IRegistryCoordinator(deployment.registryCoordinator), + IDelegationManager(core.delegationManager), + IAVSDirectory(core.avsDirectory), + IServiceManager(deployment.serviceManager) + ) + ); + + address blsApkRegistryImpl = address(new BLSApkRegistry(IRegistryCoordinator(deployment.registryCoordinator))); + address indexRegistryImpl = address(new IndexRegistry(IRegistryCoordinator(deployment.registryCoordinator))); + address registryCoordinatorImpl = address( + new RegistryCoordinator( + IServiceManager(deployment.serviceManager), + IStakeRegistry(deployment.stakeRegistry), + IBLSApkRegistry(deployment.blsapkRegistry), + IIndexRegistry(deployment.indexRegistry), + IAVSDirectory(core.avsDirectory), + IPauserRegistry(deployment.pauserRegistry) + ) + ); } } \ No newline at end of file diff --git a/test/unit/ZeusDeploymentTests.t.sol b/test/unit/ZeusDeploymentTests.t.sol index 19647a74..b9970ff2 100644 --- a/test/unit/ZeusDeploymentTests.t.sol +++ b/test/unit/ZeusDeploymentTests.t.sol @@ -8,7 +8,7 @@ contract ZeusDeploymentTests is Test { using CoreDeploymentLib for string; function testParseZeusJson() public { - string memory path = "script/deployments"; + string memory path = "script/config"; uint256 chainId = 17000; string memory environment = "preprod"; From 76de952cecdec0ada5ec367dcfc056058ea95e2a Mon Sep 17 00:00:00 2001 From: steven Date: Mon, 16 Dec 2024 16:09:49 -0500 Subject: [PATCH 43/80] feat: update forge-std --- lib/forge-std | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/forge-std b/lib/forge-std index bb4ceea9..1eea5bae 160000 --- a/lib/forge-std +++ b/lib/forge-std @@ -1 +1 @@ -Subproject commit bb4ceea94d6f10eeb5b41dc2391c6c8bf8e734ef +Subproject commit 1eea5bae12ae557d589f9f0f0edae2faa47cb262 From 3b354dd1a9d40d9390908c2020524366351b6cfa Mon Sep 17 00:00:00 2001 From: steven Date: Mon, 16 Dec 2024 16:50:56 -0500 Subject: [PATCH 44/80] feat: scripts from scratch and prepod parsed json --- foundry.toml | 6 ++ script/DeployMiddleware.s.sol | 84 ++++++++++++++++++++++++ script/utils/CoreDeploymentLib.sol | 28 +++----- script/utils/MiddlewareDeploymentLib.sol | 61 ++++++++++++++--- script/utils/UpgradeableProxyLib.sol | 4 ++ 5 files changed, 155 insertions(+), 28 deletions(-) create mode 100644 script/DeployMiddleware.s.sol diff --git a/foundry.toml b/foundry.toml index 3d0d84cb..4023fa98 100644 --- a/foundry.toml +++ b/foundry.toml @@ -17,6 +17,12 @@ via_ir = false # Override the Solidity version (this overrides `auto_detect_solc`) solc_version = '0.8.27' +[rpc_endpoints] +mainnet = "${MAINNET_RPC_URL}" +holesky = "${HOLESKY_RPC_URL}" +sepolia = "${SEPOLIA_RPC_URL}" +anvil = "${ANVIL_RPC_URL}" + [etherscan] mainnet = { key = "${ETHERSCAN_API_KEY}" } holesky = { key = "${ETHERSCAN_API_KEY}" } diff --git a/script/DeployMiddleware.s.sol b/script/DeployMiddleware.s.sol new file mode 100644 index 00000000..af8bc355 --- /dev/null +++ b/script/DeployMiddleware.s.sol @@ -0,0 +1,84 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.12; + +import {Script} from "forge-std/Script.sol"; +import {console2 as console} from "forge-std/Test.sol"; +import {UpgradeableProxyLib} from "./utils/UpgradeableProxyLib.sol"; +import {CoreDeploymentLib} from "./utils/CoreDeploymentLib.sol"; +import {MiddlewareDeploymentLib} from "./utils/MiddlewareDeploymentLib.sol"; + +contract DeployMiddleware is Script { + CoreDeploymentLib.DeploymentData internal core; + MiddlewareDeploymentLib.ConfigData internal config; + MiddlewareDeploymentLib.DeploymentData internal middlewareDeployment; + address internal deployer; + + function setUp() public { + deployer = vm.rememberKey(vm.envUint("PRIVATE_KEY")); + vm.label(deployer, "Deployer"); + + // Read core deployment data from json + core = CoreDeploymentLib.readCoreDeploymentJson("./script/config", 17000, "preprod"); + + config.admin = deployer; + config.numQuorums = 1; + + uint256[] memory operatorParams = new uint256[](6); + operatorParams[0] = 10000; // maxOperatorCount for quorum 0 + operatorParams[1] = 2000; // kickBIPsOfOperatorStake for quorum 0 (20%) + operatorParams[2] = 500; // kickBIPsOfTotalStake for quorum 0 (5%) + operatorParams[3] = 10000; // maxOperatorCount for quorum 1 + operatorParams[4] = 2000; // kickBIPsOfOperatorStake for quorum 1 (20%) + operatorParams[5] = 500; // kickBIPsOfTotalStake for quorum 1 (5%) + config.operatorParams = operatorParams; + } + + function run() external { + vm.startBroadcast(deployer); + + /// TODO: Pass proxy admin instead of config + config.proxyAdmin = UpgradeableProxyLib.deployProxyAdmin(); + middlewareDeployment = MiddlewareDeploymentLib.deployContracts(core, config); + + labelContracts(core, middlewareDeployment); + + MiddlewareDeploymentLib.upgradeContracts(middlewareDeployment, config, core); + + logDeploymentDetails(middlewareDeployment); + + vm.stopBroadcast(); + } + + function logDeploymentDetails(MiddlewareDeploymentLib.DeploymentData memory result) internal pure { + console.log("Deployment completed"); + console.log("RegistryCoordinator:", result.registryCoordinator); + console.log("BLSApkRegistry:", result.blsapkRegistry); + console.log("IndexRegistry:", result.indexRegistry); + console.log("StakeRegistry:", result.stakeRegistry); + console.log("OperatorStateRetriever:", result.operatorStateRetriever); + } + + function labelContracts(CoreDeploymentLib.DeploymentData memory coreData, MiddlewareDeploymentLib.DeploymentData memory middlewareData) internal { + // Label core contracts + vm.label(coreData.delegationManager, "DelegationManager"); + vm.label(coreData.avsDirectory, "AVSDirectory"); + vm.label(coreData.strategyManager, "StrategyManager"); + vm.label(coreData.eigenPodManager, "EigenPodManager"); + vm.label(coreData.rewardsCoordinator, "RewardsCoordinator"); + vm.label(coreData.eigenPodBeacon, "EigenPodBeacon"); + vm.label(coreData.pauserRegistry, "PauserRegistry"); + vm.label(coreData.strategyFactory, "StrategyFactory"); + vm.label(coreData.strategyBeacon, "StrategyBeacon"); + + // Label middleware contracts + vm.label(middlewareData.registryCoordinator, "RegistryCoordinator"); + vm.label(middlewareData.serviceManager, "ServiceManager"); + vm.label(middlewareData.operatorStateRetriever, "OperatorStateRetriever"); + vm.label(middlewareData.blsapkRegistry, "BLSApkRegistry"); + vm.label(middlewareData.indexRegistry, "IndexRegistry"); + vm.label(middlewareData.stakeRegistry, "StakeRegistry"); + vm.label(middlewareData.strategy, "Strategy"); + vm.label(middlewareData.token, "Token"); + vm.label(middlewareData.pauserRegistry, "PauserRegistry"); + } +} diff --git a/script/utils/CoreDeploymentLib.sol b/script/utils/CoreDeploymentLib.sol index 0b711df1..e46cfc6a 100644 --- a/script/utils/CoreDeploymentLib.sol +++ b/script/utils/CoreDeploymentLib.sol @@ -1,4 +1,3 @@ - // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.12; @@ -41,8 +40,6 @@ import {IPauserRegistry} from "eigenlayer-contracts/src/contracts/interfaces/IPa import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; import {StrategyFactory} from "eigenlayer-contracts/src/contracts/strategies/StrategyFactory.sol"; - - library CoreDeploymentLib { using stdJson for string; @@ -51,6 +48,7 @@ library CoreDeploymentLib { struct AVSDirectoryConfig { uint256 initialPausedStatus; } + struct StrategyManagerConfig { uint256 initPausedStatus; uint256 initWithdrawalDelayBlocks; @@ -68,6 +66,7 @@ library CoreDeploymentLib { struct EigenPodManagerConfig { uint256 initPausedStatus; + uint64 genesisTime; } struct RewardsCoordinatorConfig { @@ -115,7 +114,7 @@ library CoreDeploymentLib { address permissionController; } - function deployCore( + function deployCoreFromScratch( address proxyAdmin, DeploymentConfig memory config ) internal returns (DeploymentData memory) { @@ -176,12 +175,6 @@ library CoreDeploymentLib { ) ); - /// 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), @@ -189,22 +182,19 @@ library CoreDeploymentLib { IAllocationManager(result.allocationManager), IPauserRegistry(result.pauserRegistry), IPermissionController(result.permissionController), - CALCULATION_INTERVAL_SECONDS, - MAX_REWARDS_DURATION, - MAX_RETROACTIVE_LENGTH, - MAX_FUTURE_LENGTH, - GENESIS_REWARDS_TIMESTAMP + uint32(config.rewardsCoordinator.calculationIntervalSeconds), + uint32(config.rewardsCoordinator.maxRewardsDuration), + uint32(config.rewardsCoordinator.maxRetroactiveLength), + uint32(config.rewardsCoordinator.maxFutureLength), + uint32(config.rewardsCoordinator.genesisRewardsTimestamp) ) ); - /// TODO: Get actual genesis time - uint64 GENESIS_TIME = 1_564_000; - address eigenPodImpl = address( new EigenPod( IETHPOSDeposit(ethPOSDeposit), IEigenPodManager(result.eigenPodManager), - GENESIS_TIME + config.eigenPodManager.genesisTime ) ); address eigenPodBeaconImpl = address(new UpgradeableBeacon(eigenPodImpl)); diff --git a/script/utils/MiddlewareDeploymentLib.sol b/script/utils/MiddlewareDeploymentLib.sol index 63fb380e..4b7d985a 100644 --- a/script/utils/MiddlewareDeploymentLib.sol +++ b/script/utils/MiddlewareDeploymentLib.sol @@ -2,12 +2,12 @@ pragma solidity ^0.8.12; import {Vm} from "forge-std/Vm.sol"; +import {console2 as console} from "forge-std/Test.sol"; import {stdJson} from "forge-std/StdJson.sol"; import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; import {IAVSDirectory} from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol"; import {IAllocationManager} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; -import {UpgradeableProxyLib} from "./UpgradeableProxyLib.sol"; -import {CoreDeploymentLib} from "./CoreDeploymentLib.sol"; +import {IRewardsCoordinator} from "eigenlayer-contracts/src/contracts/interfaces/IRewardsCoordinator.sol"; import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; import {BLSApkRegistry} from "../../src/BLSApkRegistry.sol"; import {IBLSApkRegistry} from "../../src/interfaces/IBLSApkRegistry.sol"; @@ -20,10 +20,27 @@ import {IStakeRegistry, StakeType} from "../../src/interfaces/IStakeRegistry.sol import {IRegistryCoordinator} from "../../src/RegistryCoordinator.sol"; import {OperatorStateRetriever} from "../../src/OperatorStateRetriever.sol"; import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategyManager.sol"; +import {IStrategyFactory} from "eigenlayer-contracts/src/contracts/interfaces/IStrategyFactory.sol"; +import {ServiceManagerMock} from "../../test/mocks/ServiceManagerMock.sol"; + import {PauserRegistry, IPauserRegistry} from "eigenlayer-contracts/src/contracts/permissions/PauserRegistry.sol"; import {OperatorStateRetriever} from "../../src/OperatorStateRetriever.sol"; +import {UpgradeableProxyLib} from "./UpgradeableProxyLib.sol"; +import {CoreDeploymentLib} from "./CoreDeploymentLib.sol"; + +import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; +import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; + +contract ERC20Mock is ERC20 { + constructor() ERC20("", "") {} + + function mint(address account, uint256 amount) public { + _mint(account, amount); + } +} + library MiddlewareDeploymentLib { using stdJson for *; using Strings for *; @@ -38,7 +55,6 @@ library MiddlewareDeploymentLib { address blsapkRegistry; address indexRegistry; address stakeRegistry; - address socketRegistry; address strategy; address token; address pauserRegistry; @@ -65,11 +81,18 @@ library MiddlewareDeploymentLib { result.registryCoordinator = UpgradeableProxyLib.setUpEmptyProxy(config.proxyAdmin); result.blsapkRegistry = UpgradeableProxyLib.setUpEmptyProxy(config.proxyAdmin); result.indexRegistry = UpgradeableProxyLib.setUpEmptyProxy(config.proxyAdmin); - result.socketRegistry = UpgradeableProxyLib.setUpEmptyProxy(config.proxyAdmin); + result.serviceManager = UpgradeableProxyLib.setUpEmptyProxy(config.proxyAdmin); OperatorStateRetriever operatorStateRetriever = new OperatorStateRetriever(); result.operatorStateRetriever = address(operatorStateRetriever); - upgradeContracts(result, config, core); + ERC20Mock token = new ERC20Mock(); + result.token = address(token); + + // Create a new strategy using the strategy factory + IStrategyFactory strategyFactory = IStrategyFactory(core.strategyFactory); + IStrategy strategy = strategyFactory.deployNewStrategy(IERC20(result.token)); + result.strategy = address(strategy); + result.token = address(token); return result; } @@ -79,6 +102,16 @@ library MiddlewareDeploymentLib { ConfigData memory config, CoreDeploymentLib.DeploymentData memory core ) internal { + + address serviceManagerImpl = address( + new ServiceManagerMock( + IAVSDirectory(core.avsDirectory), + IRewardsCoordinator(core.rewardsCoordinator), + IRegistryCoordinator(deployment.registryCoordinator), + IStakeRegistry(deployment.stakeRegistry), + IAllocationManager(core.allocationManager) + ) + ); address stakeRegistryImpl = address( new StakeRegistry( IRegistryCoordinator(deployment.registryCoordinator), @@ -130,7 +163,7 @@ library MiddlewareDeploymentLib { } } - bytes memory upgradeCall = abi.encodeCall( + bytes memory registryCoordinatorUpgradeCall = abi.encodeCall( RegistryCoordinator.initialize, ( config.admin, @@ -140,15 +173,25 @@ library MiddlewareDeploymentLib { quorumsOperatorSetParams, quorumsMinimumStake, quorumsStrategyParams, - new StakeType[](0), - new uint32[](0) + new StakeType[](1), + new uint32[](1) + ) + ); + + bytes memory serviceManagerUpgradeCall = abi.encodeCall( + ServiceManagerMock.initialize, + ( + config.admin, + config.admin, + config.admin ) ); + UpgradeableProxyLib.upgradeAndCall(deployment.serviceManager, serviceManagerImpl, serviceManagerUpgradeCall); UpgradeableProxyLib.upgrade(deployment.stakeRegistry, stakeRegistryImpl); UpgradeableProxyLib.upgrade(deployment.blsapkRegistry, blsApkRegistryImpl); UpgradeableProxyLib.upgrade(deployment.indexRegistry, indexRegistryimpl); - UpgradeableProxyLib.upgradeAndCall(deployment.registryCoordinator, registryCoordinatorImpl, upgradeCall); + UpgradeableProxyLib.upgradeAndCall(deployment.registryCoordinator, registryCoordinatorImpl, registryCoordinatorUpgradeCall); } } \ No newline at end of file diff --git a/script/utils/UpgradeableProxyLib.sol b/script/utils/UpgradeableProxyLib.sol index 04c8d35d..00fa4006 100644 --- a/script/utils/UpgradeableProxyLib.sol +++ b/script/utils/UpgradeableProxyLib.sol @@ -18,6 +18,10 @@ library UpgradeableProxyLib { bytes32 internal constant ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103; + 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, "")); From 474a02478a01a057915d8964745241df6a6e226f Mon Sep 17 00:00:00 2001 From: steven Date: Mon, 16 Dec 2024 17:11:17 -0500 Subject: [PATCH 45/80] feat: helper lib for scripts to initialize state in test avs --- script/utils/OperatorLib.sol | 132 +++++++++++++++++++++++++ script/utils/OperatorSetUpgradeLib.sol | 59 ----------- 2 files changed, 132 insertions(+), 59 deletions(-) create mode 100644 script/utils/OperatorLib.sol delete mode 100644 script/utils/OperatorSetUpgradeLib.sol diff --git a/script/utils/OperatorLib.sol b/script/utils/OperatorLib.sol new file mode 100644 index 00000000..63201a4b --- /dev/null +++ b/script/utils/OperatorLib.sol @@ -0,0 +1,132 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {Vm} from "forge-std/Vm.sol"; + +import {console2 as console} from "forge-std/Test.sol"; +import {stdJson} from "forge-std/StdJson.sol"; +import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; +import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; +import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import {IStakeRegistry} from "../../src/interfaces/IStakeRegistry.sol"; +import {IRegistryCoordinator} from "../../src/RegistryCoordinator.sol"; +import {OperatorStateRetriever} from "../../src/OperatorStateRetriever.sol"; +import {IStrategyManager} from "eigenlayer-contracts/src/contracts/interfaces/IStrategyManager.sol"; +import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; +import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; +import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol"; +import {IAVSDirectory} from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol"; +import {IStrategyFactory} from "eigenlayer-contracts/src/contracts/interfaces/IStrategyFactory.sol"; +import {PauserRegistry} from "eigenlayer-contracts/src/contracts/permissions/PauserRegistry.sol"; +import {UpgradeableProxyLib} from "./UpgradeableProxyLib.sol"; +import {CoreDeploymentLib} from "./CoreDeploymentLib.sol"; +import {ERC20Mock} from "./MiddlewareDeploymentLib.sol"; + +library OperatorLib { + + Vm private constant vm = Vm(address(uint160(uint256(keccak256("hevm cheat code"))))); + /// TODO BLS Wallet + struct Operator { + Vm.Wallet key; + Vm.Wallet signingKey; + } + + function signWithOperatorKey( + Operator memory operator, + bytes32 digest + ) internal pure returns (bytes memory) { + (uint8 v, bytes32 r, bytes32 s) = vm.sign(operator.key.privateKey, digest); + return abi.encodePacked(r, s, v); + } + + function signWithSigningKey( + Operator memory operator, + bytes32 digest + ) internal pure returns (bytes memory) { + (uint8 v, bytes32 r, bytes32 s) = vm.sign(operator.signingKey.privateKey, digest); + return abi.encodePacked(r, s, v); + } + + function mintMockTokens(Operator memory operator, address token, uint256 amount) internal { + ERC20Mock(token).mint(operator.key.addr, amount); + } + + function depositTokenIntoStrategy( + Operator memory operator, + address strategyManager, + address strategy, + address token, + uint256 amount + ) internal returns (uint256) { + /// TODO :make sure strategy associated with token + IStrategy strategy = IStrategy(strategy); + require(address(strategy) != address(0), "Strategy was not found"); + IStrategyManager strategyManager = IStrategyManager(strategyManager); + + ERC20Mock(token).approve(address(strategyManager), amount); + uint256 shares = strategyManager.depositIntoStrategy(strategy, IERC20(token), amount); + + return shares; + } + + function registerAsOperator( + Operator memory operator, + address delegationManager + ) internal { + IDelegationManager delegationManagerInstance = IDelegationManager(delegationManager); + + delegationManagerInstance.registerAsOperator( + operator.key.addr, + 0, + "" + ); + } + + function registerOperatorToAVS_M2( + Operator memory operator, + address avsDirectory, + address serviceManager + ) internal { + IAVSDirectory avsDirectory = IAVSDirectory(avsDirectory); + + bytes32 salt = keccak256(abi.encodePacked(block.timestamp, operator.key.addr)); + uint256 expiry = block.timestamp + 1 hours; + + bytes32 operatorRegistrationDigestHash = avsDirectory + .calculateOperatorAVSRegistrationDigestHash( + operator.key.addr, serviceManager, salt, expiry + ); + + bytes memory signature = signWithOperatorKey(operator, operatorRegistrationDigestHash); + + ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature = ISignatureUtils + .SignatureWithSaltAndExpiry({signature: signature, salt: salt, expiry: expiry}); + + /// TODO: call the registry + } + + function deregisterOperatorFromAVS_M2() internal { + /// TODO: call the registry + + } + + function registerOperatorFromAVS_OpSet() internal { + /// TODO: call the ALM + } + + function deregisterOperatorFromAVS_OpSet() internal { + /// TODO: call the ALM + } + + function createAndAddOperator(uint256 salt) internal returns (Operator memory) { + Vm.Wallet memory operatorKey = + vm.createWallet(string.concat("operator", vm.toString(salt))); + /// TODO: BLS Key for signing key. Integrate G2Operations.sol + Vm.Wallet memory signingKey = + vm.createWallet(string.concat("signing", vm.toString(salt))); + + Operator memory newOperator = Operator({key: operatorKey, signingKey: signingKey}); + + return newOperator; + } +} \ No newline at end of file diff --git a/script/utils/OperatorSetUpgradeLib.sol b/script/utils/OperatorSetUpgradeLib.sol deleted file mode 100644 index 67222c43..00000000 --- a/script/utils/OperatorSetUpgradeLib.sol +++ /dev/null @@ -1,59 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; -// Deploy L2AVS proxy - -import {CoreDeploymentLib} from "./CoreDeploymentLib.sol"; -import {MiddlewareDeploymentLib} from "./MiddlewareDeploymentLib.sol"; - -import {StakeRegistry} from "../../src/StakeRegistry.sol"; -import {BLSApkRegistry} from "../../src/BLSApkRegistry.sol"; -import {IndexRegistry} from "../../src/IndexRegistry.sol"; -import {RegistryCoordinator} from "../../src/RegistryCoordinator.sol"; -import {IRegistryCoordinator} from "../../src/interfaces/IRegistryCoordinator.sol"; -import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; -import {IAVSDirectory} from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol"; -import {IServiceManager} from "../../src/interfaces/IServiceManager.sol"; -import {IBLSApkRegistry} from "../../src/interfaces/IBLSApkRegistry.sol"; -import {IStakeRegistry} from "../../src/interfaces/IStakeRegistry.sol"; -import {IIndexRegistry} from "../../src/interfaces/IIndexRegistry.sol"; -import {IPauserRegistry} from "eigenlayer-contracts/src/contracts/permissions/PauserRegistry.sol"; - - -library SlashingUpgradeLib { - using CoreDeploymentLib for string; - - function parseCoreDeploymentJson(string memory path, uint256 chainId) internal returns (CoreDeploymentLib.DeploymentData memory) { - return CoreDeploymentLib.readCoreDeploymentJson(path, chainId); - } - - function parseCoreDeploymentJson(string memory path, uint256 chainId, string memory environment) internal returns (CoreDeploymentLib.DeploymentData memory) { - return CoreDeploymentLib.readCoreDeploymentJson(path, chainId, environment); - } - - function deployNewImplementations( - CoreDeploymentLib.DeploymentData memory core, - MiddlewareDeploymentLib.DeploymentData memory deployment - ) internal { - address stakeRegistryImpl = address( - new StakeRegistry( - IRegistryCoordinator(deployment.registryCoordinator), - IDelegationManager(core.delegationManager), - IAVSDirectory(core.avsDirectory), - IServiceManager(deployment.serviceManager) - ) - ); - - address blsApkRegistryImpl = address(new BLSApkRegistry(IRegistryCoordinator(deployment.registryCoordinator))); - address indexRegistryImpl = address(new IndexRegistry(IRegistryCoordinator(deployment.registryCoordinator))); - address registryCoordinatorImpl = address( - new RegistryCoordinator( - IServiceManager(deployment.serviceManager), - IStakeRegistry(deployment.stakeRegistry), - IBLSApkRegistry(deployment.blsapkRegistry), - IIndexRegistry(deployment.indexRegistry), - IAVSDirectory(core.avsDirectory), - IPauserRegistry(deployment.pauserRegistry) - ) - ); - } -} \ No newline at end of file From d2e8e31928115f42e9f4ac4c57884b36c62d48bb Mon Sep 17 00:00:00 2001 From: steven Date: Tue, 17 Dec 2024 16:31:34 -0500 Subject: [PATCH 46/80] feat: helper scripts for setting up operators --- script/utils/BN256G2.sol | 337 +++++++++++++++++++++++++++++++++++ script/utils/OperatorLib.sol | 95 +++++++++- test/unit/OperatorLib.t.sol | 29 +++ 3 files changed, 454 insertions(+), 7 deletions(-) create mode 100644 script/utils/BN256G2.sol create mode 100644 test/unit/OperatorLib.t.sol diff --git a/script/utils/BN256G2.sol b/script/utils/BN256G2.sol new file mode 100644 index 00000000..55ccb65f --- /dev/null +++ b/script/utils/BN256G2.sol @@ -0,0 +1,337 @@ +pragma solidity ^0.8.0; + +/** + * @title Elliptic curve operations on twist points for alt_bn128 + * @author Mustafa Al-Bassam (mus@musalbas.com) + * @dev Homepage: https://github.com/musalbas/solidity-BN256G2 + */ +library BN256G2 { + uint256 internal constant FIELD_MODULUS = + 0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47; + uint256 internal constant TWISTBX = + 0x2b149d40ceb8aaae81be18991be06ac3b5b4c5e559dbefa33267e6dc24a138e5; + uint256 internal constant TWISTBY = + 0x9713b03af0fed4cd2cafadeed8fdf4a74fa084e52d1852e4a2bd0685c315d2; + uint256 internal constant PTXX = 0; + uint256 internal constant PTXY = 1; + uint256 internal constant PTYX = 2; + uint256 internal constant PTYY = 3; + uint256 internal constant PTZX = 4; + uint256 internal constant PTZY = 5; + + /** + * @notice Add two twist points + * @param pt1xx Coefficient 1 of x on point 1 + * @param pt1xy Coefficient 2 of x on point 1 + * @param pt1yx Coefficient 1 of y on point 1 + * @param pt1yy Coefficient 2 of y on point 1 + * @param pt2xx Coefficient 1 of x on point 2 + * @param pt2xy Coefficient 2 of x on point 2 + * @param pt2yx Coefficient 1 of y on point 2 + * @param pt2yy Coefficient 2 of y on point 2 + * @return (pt3xx, pt3xy, pt3yx, pt3yy) + */ + function ECTwistAdd( + uint256 pt1xx, + uint256 pt1xy, + uint256 pt1yx, + uint256 pt1yy, + uint256 pt2xx, + uint256 pt2xy, + uint256 pt2yx, + uint256 pt2yy + ) public view returns (uint256, uint256, uint256, uint256) { + if (pt1xx == 0 && pt1xy == 0 && pt1yx == 0 && pt1yy == 0) { + if (!(pt2xx == 0 && pt2xy == 0 && pt2yx == 0 && pt2yy == 0)) { + assert(_isOnCurve(pt2xx, pt2xy, pt2yx, pt2yy)); + } + return (pt2xx, pt2xy, pt2yx, pt2yy); + } else if (pt2xx == 0 && pt2xy == 0 && pt2yx == 0 && pt2yy == 0) { + assert(_isOnCurve(pt1xx, pt1xy, pt1yx, pt1yy)); + return (pt1xx, pt1xy, pt1yx, pt1yy); + } + + assert(_isOnCurve(pt1xx, pt1xy, pt1yx, pt1yy)); + assert(_isOnCurve(pt2xx, pt2xy, pt2yx, pt2yy)); + + uint256[6] memory pt3 = + _ECTwistAddJacobian(pt1xx, pt1xy, pt1yx, pt1yy, 1, 0, pt2xx, pt2xy, pt2yx, pt2yy, 1, 0); + + return _fromJacobian(pt3[PTXX], pt3[PTXY], pt3[PTYX], pt3[PTYY], pt3[PTZX], pt3[PTZY]); + } + + /** + * @notice Multiply a twist point by a scalar + * @param s Scalar to multiply by + * @param pt1xx Coefficient 1 of x + * @param pt1xy Coefficient 2 of x + * @param pt1yx Coefficient 1 of y + * @param pt1yy Coefficient 2 of y + * @return (pt2xx, pt2xy, pt2yx, pt2yy) + */ + function ECTwistMul( + uint256 s, + uint256 pt1xx, + uint256 pt1xy, + uint256 pt1yx, + uint256 pt1yy + ) public view returns (uint256, uint256, uint256, uint256) { + uint256 pt1zx = 1; + if (pt1xx == 0 && pt1xy == 0 && pt1yx == 0 && pt1yy == 0) { + pt1xx = 1; + pt1yx = 1; + pt1zx = 0; + } else { + assert(_isOnCurve(pt1xx, pt1xy, pt1yx, pt1yy)); + } + + uint256[6] memory pt2 = _ECTwistMulJacobian(s, pt1xx, pt1xy, pt1yx, pt1yy, pt1zx, 0); + + return _fromJacobian(pt2[PTXX], pt2[PTXY], pt2[PTYX], pt2[PTYY], pt2[PTZX], pt2[PTZY]); + } + + /** + * @notice Get the field modulus + * @return The field modulus + */ + function GetFieldModulus() public pure returns (uint256) { + return FIELD_MODULUS; + } + + function submod(uint256 a, uint256 b, uint256 n) internal pure returns (uint256) { + return addmod(a, n - b, n); + } + + function _FQ2Mul( + uint256 xx, + uint256 xy, + uint256 yx, + uint256 yy + ) internal pure returns (uint256, uint256) { + return ( + submod(mulmod(xx, yx, FIELD_MODULUS), mulmod(xy, yy, FIELD_MODULUS), FIELD_MODULUS), + addmod(mulmod(xx, yy, FIELD_MODULUS), mulmod(xy, yx, FIELD_MODULUS), FIELD_MODULUS) + ); + } + + function _FQ2Muc(uint256 xx, uint256 xy, uint256 c) internal pure returns (uint256, uint256) { + return (mulmod(xx, c, FIELD_MODULUS), mulmod(xy, c, FIELD_MODULUS)); + } + + function _FQ2Add( + uint256 xx, + uint256 xy, + uint256 yx, + uint256 yy + ) internal pure returns (uint256, uint256) { + return (addmod(xx, yx, FIELD_MODULUS), addmod(xy, yy, FIELD_MODULUS)); + } + + function _FQ2Sub( + uint256 xx, + uint256 xy, + uint256 yx, + uint256 yy + ) internal pure returns (uint256 rx, uint256 ry) { + return (submod(xx, yx, FIELD_MODULUS), submod(xy, yy, FIELD_MODULUS)); + } + + function _FQ2Div( + uint256 xx, + uint256 xy, + uint256 yx, + uint256 yy + ) internal view returns (uint256, uint256) { + (yx, yy) = _FQ2Inv(yx, yy); + return _FQ2Mul(xx, xy, yx, yy); + } + + function _FQ2Inv(uint256 x, uint256 y) internal view returns (uint256, uint256) { + uint256 inv = _modInv( + addmod(mulmod(y, y, FIELD_MODULUS), mulmod(x, x, FIELD_MODULUS), FIELD_MODULUS), + FIELD_MODULUS + ); + return (mulmod(x, inv, FIELD_MODULUS), FIELD_MODULUS - mulmod(y, inv, FIELD_MODULUS)); + } + + function _isOnCurve( + uint256 xx, + uint256 xy, + uint256 yx, + uint256 yy + ) internal pure returns (bool) { + uint256 yyx; + uint256 yyy; + uint256 xxxx; + uint256 xxxy; + (yyx, yyy) = _FQ2Mul(yx, yy, yx, yy); + (xxxx, xxxy) = _FQ2Mul(xx, xy, xx, xy); + (xxxx, xxxy) = _FQ2Mul(xxxx, xxxy, xx, xy); + (yyx, yyy) = _FQ2Sub(yyx, yyy, xxxx, xxxy); + (yyx, yyy) = _FQ2Sub(yyx, yyy, TWISTBX, TWISTBY); + return yyx == 0 && yyy == 0; + } + + function _modInv(uint256 a, uint256 n) internal view returns (uint256 result) { + bool success; + assembly { + let freemem := mload(0x40) + mstore(freemem, 0x20) + mstore(add(freemem, 0x20), 0x20) + mstore(add(freemem, 0x40), 0x20) + mstore(add(freemem, 0x60), a) + mstore(add(freemem, 0x80), sub(n, 2)) + mstore(add(freemem, 0xA0), n) + success := staticcall(sub(gas(), 2000), 5, freemem, 0xC0, freemem, 0x20) + result := mload(freemem) + } + require(success); + } + + function _fromJacobian( + uint256 pt1xx, + uint256 pt1xy, + uint256 pt1yx, + uint256 pt1yy, + uint256 pt1zx, + uint256 pt1zy + ) internal view returns (uint256 pt2xx, uint256 pt2xy, uint256 pt2yx, uint256 pt2yy) { + uint256 invzx; + uint256 invzy; + (invzx, invzy) = _FQ2Inv(pt1zx, pt1zy); + (pt2xx, pt2xy) = _FQ2Mul(pt1xx, pt1xy, invzx, invzy); + (pt2yx, pt2yy) = _FQ2Mul(pt1yx, pt1yy, invzx, invzy); + } + + function _ECTwistAddJacobian( + uint256 pt1xx, + uint256 pt1xy, + uint256 pt1yx, + uint256 pt1yy, + uint256 pt1zx, + uint256 pt1zy, + uint256 pt2xx, + uint256 pt2xy, + uint256 pt2yx, + uint256 pt2yy, + uint256 pt2zx, + uint256 pt2zy + ) internal pure returns (uint256[6] memory pt3) { + if (pt1zx == 0 && pt1zy == 0) { + (pt3[PTXX], pt3[PTXY], pt3[PTYX], pt3[PTYY], pt3[PTZX], pt3[PTZY]) = + (pt2xx, pt2xy, pt2yx, pt2yy, pt2zx, pt2zy); + return pt3; + } else if (pt2zx == 0 && pt2zy == 0) { + (pt3[PTXX], pt3[PTXY], pt3[PTYX], pt3[PTYY], pt3[PTZX], pt3[PTZY]) = + (pt1xx, pt1xy, pt1yx, pt1yy, pt1zx, pt1zy); + return pt3; + } + + (pt2yx, pt2yy) = _FQ2Mul(pt2yx, pt2yy, pt1zx, pt1zy); // U1 = y2 * z1 + (pt3[PTYX], pt3[PTYY]) = _FQ2Mul(pt1yx, pt1yy, pt2zx, pt2zy); // U2 = y1 * z2 + (pt2xx, pt2xy) = _FQ2Mul(pt2xx, pt2xy, pt1zx, pt1zy); // V1 = x2 * z1 + (pt3[PTZX], pt3[PTZY]) = _FQ2Mul(pt1xx, pt1xy, pt2zx, pt2zy); // V2 = x1 * z2 + + if (pt2xx == pt3[PTZX] && pt2xy == pt3[PTZY]) { + if (pt2yx == pt3[PTYX] && pt2yy == pt3[PTYY]) { + (pt3[PTXX], pt3[PTXY], pt3[PTYX], pt3[PTYY], pt3[PTZX], pt3[PTZY]) = + _ECTwistDoubleJacobian(pt1xx, pt1xy, pt1yx, pt1yy, pt1zx, pt1zy); + return pt3; + } + (pt3[PTXX], pt3[PTXY], pt3[PTYX], pt3[PTYY], pt3[PTZX], pt3[PTZY]) = (1, 0, 1, 0, 0, 0); + return pt3; + } + + (pt2zx, pt2zy) = _FQ2Mul(pt1zx, pt1zy, pt2zx, pt2zy); // W = z1 * z2 + (pt1xx, pt1xy) = _FQ2Sub(pt2yx, pt2yy, pt3[PTYX], pt3[PTYY]); // U = U1 - U2 + (pt1yx, pt1yy) = _FQ2Sub(pt2xx, pt2xy, pt3[PTZX], pt3[PTZY]); // V = V1 - V2 + (pt1zx, pt1zy) = _FQ2Mul(pt1yx, pt1yy, pt1yx, pt1yy); // V_squared = V * V + (pt2yx, pt2yy) = _FQ2Mul(pt1zx, pt1zy, pt3[PTZX], pt3[PTZY]); // V_squared_times_V2 = V_squared * V2 + (pt1zx, pt1zy) = _FQ2Mul(pt1zx, pt1zy, pt1yx, pt1yy); // V_cubed = V * V_squared + (pt3[PTZX], pt3[PTZY]) = _FQ2Mul(pt1zx, pt1zy, pt2zx, pt2zy); // newz = V_cubed * W + (pt2xx, pt2xy) = _FQ2Mul(pt1xx, pt1xy, pt1xx, pt1xy); // U * U + (pt2xx, pt2xy) = _FQ2Mul(pt2xx, pt2xy, pt2zx, pt2zy); // U * U * W + (pt2xx, pt2xy) = _FQ2Sub(pt2xx, pt2xy, pt1zx, pt1zy); // U * U * W - V_cubed + (pt2zx, pt2zy) = _FQ2Muc(pt2yx, pt2yy, 2); // 2 * V_squared_times_V2 + (pt2xx, pt2xy) = _FQ2Sub(pt2xx, pt2xy, pt2zx, pt2zy); // A = U * U * W - V_cubed - 2 * V_squared_times_V2 + (pt3[PTXX], pt3[PTXY]) = _FQ2Mul(pt1yx, pt1yy, pt2xx, pt2xy); // newx = V * A + (pt1yx, pt1yy) = _FQ2Sub(pt2yx, pt2yy, pt2xx, pt2xy); // V_squared_times_V2 - A + (pt1yx, pt1yy) = _FQ2Mul(pt1xx, pt1xy, pt1yx, pt1yy); // U * (V_squared_times_V2 - A) + (pt1xx, pt1xy) = _FQ2Mul(pt1zx, pt1zy, pt3[PTYX], pt3[PTYY]); // V_cubed * U2 + (pt3[PTYX], pt3[PTYY]) = _FQ2Sub(pt1yx, pt1yy, pt1xx, pt1xy); // newy = U * (V_squared_times_V2 - A) - V_cubed * U2 + } + + function _ECTwistDoubleJacobian( + uint256 pt1xx, + uint256 pt1xy, + uint256 pt1yx, + uint256 pt1yy, + uint256 pt1zx, + uint256 pt1zy + ) + internal + pure + returns ( + uint256 pt2xx, + uint256 pt2xy, + uint256 pt2yx, + uint256 pt2yy, + uint256 pt2zx, + uint256 pt2zy + ) + { + (pt2xx, pt2xy) = _FQ2Muc(pt1xx, pt1xy, 3); // 3 * x + (pt2xx, pt2xy) = _FQ2Mul(pt2xx, pt2xy, pt1xx, pt1xy); // W = 3 * x * x + (pt1zx, pt1zy) = _FQ2Mul(pt1yx, pt1yy, pt1zx, pt1zy); // S = y * z + (pt2yx, pt2yy) = _FQ2Mul(pt1xx, pt1xy, pt1yx, pt1yy); // x * y + (pt2yx, pt2yy) = _FQ2Mul(pt2yx, pt2yy, pt1zx, pt1zy); // B = x * y * S + (pt1xx, pt1xy) = _FQ2Mul(pt2xx, pt2xy, pt2xx, pt2xy); // W * W + (pt2zx, pt2zy) = _FQ2Muc(pt2yx, pt2yy, 8); // 8 * B + (pt1xx, pt1xy) = _FQ2Sub(pt1xx, pt1xy, pt2zx, pt2zy); // H = W * W - 8 * B + (pt2zx, pt2zy) = _FQ2Mul(pt1zx, pt1zy, pt1zx, pt1zy); // S_squared = S * S + (pt2yx, pt2yy) = _FQ2Muc(pt2yx, pt2yy, 4); // 4 * B + (pt2yx, pt2yy) = _FQ2Sub(pt2yx, pt2yy, pt1xx, pt1xy); // 4 * B - H + (pt2yx, pt2yy) = _FQ2Mul(pt2yx, pt2yy, pt2xx, pt2xy); // W * (4 * B - H) + (pt2xx, pt2xy) = _FQ2Muc(pt1yx, pt1yy, 8); // 8 * y + (pt2xx, pt2xy) = _FQ2Mul(pt2xx, pt2xy, pt1yx, pt1yy); // 8 * y * y + (pt2xx, pt2xy) = _FQ2Mul(pt2xx, pt2xy, pt2zx, pt2zy); // 8 * y * y * S_squared + (pt2yx, pt2yy) = _FQ2Sub(pt2yx, pt2yy, pt2xx, pt2xy); // newy = W * (4 * B - H) - 8 * y * y * S_squared + (pt2xx, pt2xy) = _FQ2Muc(pt1xx, pt1xy, 2); // 2 * H + (pt2xx, pt2xy) = _FQ2Mul(pt2xx, pt2xy, pt1zx, pt1zy); // newx = 2 * H * S + (pt2zx, pt2zy) = _FQ2Mul(pt1zx, pt1zy, pt2zx, pt2zy); // S * S_squared + (pt2zx, pt2zy) = _FQ2Muc(pt2zx, pt2zy, 8); // newz = 8 * S * S_squared + } + + function _ECTwistMulJacobian( + uint256 d, + uint256 pt1xx, + uint256 pt1xy, + uint256 pt1yx, + uint256 pt1yy, + uint256 pt1zx, + uint256 pt1zy + ) internal pure returns (uint256[6] memory pt2) { + while (d != 0) { + if ((d & 1) != 0) { + pt2 = _ECTwistAddJacobian( + pt2[PTXX], + pt2[PTXY], + pt2[PTYX], + pt2[PTYY], + pt2[PTZX], + pt2[PTZY], + pt1xx, + pt1xy, + pt1yx, + pt1yy, + pt1zx, + pt1zy + ); + } + (pt1xx, pt1xy, pt1yx, pt1yy, pt1zx, pt1zy) = + _ECTwistDoubleJacobian(pt1xx, pt1xy, pt1yx, pt1yy, pt1zx, pt1zy); + + d = d / 2; + } + } +} \ No newline at end of file diff --git a/script/utils/OperatorLib.sol b/script/utils/OperatorLib.sol index 63201a4b..601b7687 100644 --- a/script/utils/OperatorLib.sol +++ b/script/utils/OperatorLib.sol @@ -21,14 +21,87 @@ import {PauserRegistry} from "eigenlayer-contracts/src/contracts/permissions/Pau import {UpgradeableProxyLib} from "./UpgradeableProxyLib.sol"; import {CoreDeploymentLib} from "./CoreDeploymentLib.sol"; import {ERC20Mock} from "./MiddlewareDeploymentLib.sol"; +import {BN254} from "../../src/libraries/BN254.sol"; +import {BN256G2} from "./BN256G2.sol"; + library OperatorLib { + using BN254 for *; + using Strings for uint256; + + struct Wallet { + uint256 privateKey; + address addr; + } + + struct BLSWallet { + uint256 privateKey; + BN254.G2Point publicKeyG2; + BN254.G1Point publicKeyG1; + } Vm private constant vm = Vm(address(uint160(uint256(keccak256("hevm cheat code"))))); - /// TODO BLS Wallet + struct Operator { - Vm.Wallet key; - Vm.Wallet signingKey; + Wallet key; + BLSWallet signingKey; + } + + function createBLSWallet(uint256 index) internal returns (BLSWallet memory) { + uint256 privateKey = uint256(keccak256(abi.encodePacked(index + 1))); + BN254.G1Point memory publicKeyG1 = BN254.generatorG1().scalar_mul(privateKey); + BN254.G2Point memory publicKeyG2 = mul(privateKey); + + return BLSWallet({ + privateKey: privateKey, + publicKeyG2: publicKeyG2, + publicKeyG1: publicKeyG1 + }); + } + + function createWallet(uint256 index) internal pure returns (Wallet memory) { + uint256 privateKey = uint256(keccak256(abi.encodePacked(index))); + address addr = vm.addr(privateKey); + + return Wallet({ + privateKey: privateKey, + addr: addr + }); + } + + function createOperator(uint256 index) internal returns (Operator memory) { + Wallet memory vmWallet = createWallet(index); + BLSWallet memory blsWallet = createBLSWallet(index); + + return Operator({ + key: vmWallet, + signingKey: blsWallet + }); + } + + + function mul(uint256 x) internal returns (BN254.G2Point memory g2Point) { + string[] memory inputs = new string[](5); + inputs[0] = "go"; + inputs[1] = "run"; + inputs[2] = "test/ffi/go/g2mul.go"; + inputs[3] = x.toString(); + + inputs[4] = "1"; + bytes memory res = vm.ffi(inputs); + g2Point.X[1] = abi.decode(res, (uint256)); + + inputs[4] = "2"; + res = vm.ffi(inputs); + g2Point.X[0] = abi.decode(res, (uint256)); + + inputs[4] = "3"; + res = vm.ffi(inputs); + g2Point.Y[1] = abi.decode(res, (uint256)); + + inputs[4] = "4"; + res = vm.ffi(inputs); + g2Point.Y[0] = abi.decode(res, (uint256)); } function signWithOperatorKey( @@ -47,6 +120,14 @@ library OperatorLib { return abi.encodePacked(r, s, v); } + function aggregate( + BN254.G2Point memory pk1, + BN254.G2Point memory pk2 + ) internal view returns (BN254.G2Point memory apk) { + (apk.X[0], apk.X[1], apk.Y[0], apk.Y[1]) = + BN256G2.ECTwistAdd(pk1.X[0], pk1.X[1], pk1.Y[0], pk1.Y[1], pk2.X[0], pk2.X[1], pk2.Y[0], pk2.Y[1]); + } + function mintMockTokens(Operator memory operator, address token, uint256 amount) internal { ERC20Mock(token).mint(operator.key.addr, amount); } @@ -119,11 +200,11 @@ library OperatorLib { } function createAndAddOperator(uint256 salt) internal returns (Operator memory) { - Vm.Wallet memory operatorKey = - vm.createWallet(string.concat("operator", vm.toString(salt))); + Wallet memory operatorKey = + createWallet(salt); /// TODO: BLS Key for signing key. Integrate G2Operations.sol - Vm.Wallet memory signingKey = - vm.createWallet(string.concat("signing", vm.toString(salt))); + BLSWallet memory signingKey = + createBLSWallet(salt); Operator memory newOperator = Operator({key: operatorKey, signingKey: signingKey}); diff --git a/test/unit/OperatorLib.t.sol b/test/unit/OperatorLib.t.sol new file mode 100644 index 00000000..ba4dec43 --- /dev/null +++ b/test/unit/OperatorLib.t.sol @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "forge-std/Test.sol"; +import {OperatorLib} from "../../script/utils/OperatorLib.sol"; + +contract OperatorLibTest is Test { + using OperatorLib for *; + + function testCreateOperator() public { + uint256 index = 1; + OperatorLib.Operator memory operator = OperatorLib.createOperator(index); + + // Check that the operator's VM wallet address is non-zero + assertTrue(operator.key.addr != address(0), "VM wallet address should be non-zero"); + + // Check that the operator's BLS private key is non-zero + assertTrue(operator.signingKey.privateKey != 0, "BLS private key should be non-zero"); + + // Check that the operator's BLS public key G1 is non-zero + assertTrue(operator.signingKey.publicKeyG1.X != 0 || operator.signingKey.publicKeyG1.X != 0, "BLS public key G1 X should be non-zero"); + assertTrue(operator.signingKey.publicKeyG1.Y != 0 || operator.signingKey.publicKeyG1.Y!= 0, "BLS public key G1 Y should be non-zero"); + + // Check that the operator's BLS public key G2 is non-zero + assertTrue(operator.signingKey.publicKeyG2.X[0] != 0 || operator.signingKey.publicKeyG2.X[1] != 0, "BLS public key G2 X should be non-zero"); + assertTrue(operator.signingKey.publicKeyG2.Y[0] != 0 || operator.signingKey.publicKeyG2.Y[1] != 0, "BLS public key G2 Y should be non-zero"); + } +} + From b0b59e0a166654123af348966f01b96a27d4bf17 Mon Sep 17 00:00:00 2001 From: steven Date: Tue, 17 Dec 2024 16:41:54 -0500 Subject: [PATCH 47/80] feat: add signing --- script/utils/OperatorLib.sol | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/script/utils/OperatorLib.sol b/script/utils/OperatorLib.sol index 601b7687..a2a1f4b2 100644 --- a/script/utils/OperatorLib.sol +++ b/script/utils/OperatorLib.sol @@ -210,4 +210,22 @@ library OperatorLib { return newOperator; } + + function signMessage( + BLSWallet memory blsWallet, + bytes32 messageHash + ) internal view returns (BN254.G1Point memory) { + // Hash the message to a point on G1 + BN254.G1Point memory messagePoint = BN254.hashToG1(messageHash); + + // Sign by multiplying the hashed message point with the private key + return messagePoint.scalar_mul(blsWallet.privateKey); + } + + function signMessageWithOperator( + Operator memory operator, + bytes32 messageHash + ) internal view returns (BN254.G1Point memory) { + return signMessage(operator.signingKey, messageHash); + } } \ No newline at end of file From 87d906da655c71efb2af011dcc07d00f94791cda Mon Sep 17 00:00:00 2001 From: steven Date: Tue, 17 Dec 2024 17:01:45 -0500 Subject: [PATCH 48/80] test: add test for library --- script/utils/OperatorLib.sol | 1 - test/unit/OperatorLib.t.sol | 20 ++++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/script/utils/OperatorLib.sol b/script/utils/OperatorLib.sol index a2a1f4b2..fe133583 100644 --- a/script/utils/OperatorLib.sol +++ b/script/utils/OperatorLib.sol @@ -202,7 +202,6 @@ library OperatorLib { function createAndAddOperator(uint256 salt) internal returns (Operator memory) { Wallet memory operatorKey = createWallet(salt); - /// TODO: BLS Key for signing key. Integrate G2Operations.sol BLSWallet memory signingKey = createBLSWallet(salt); diff --git a/test/unit/OperatorLib.t.sol b/test/unit/OperatorLib.t.sol index ba4dec43..0f352986 100644 --- a/test/unit/OperatorLib.t.sol +++ b/test/unit/OperatorLib.t.sol @@ -3,6 +3,7 @@ pragma solidity ^0.8.0; import "forge-std/Test.sol"; import {OperatorLib} from "../../script/utils/OperatorLib.sol"; +import {BN254} from "../../src/libraries/BN254.sol"; contract OperatorLibTest is Test { using OperatorLib for *; @@ -25,5 +26,24 @@ contract OperatorLibTest is Test { assertTrue(operator.signingKey.publicKeyG2.X[0] != 0 || operator.signingKey.publicKeyG2.X[1] != 0, "BLS public key G2 X should be non-zero"); assertTrue(operator.signingKey.publicKeyG2.Y[0] != 0 || operator.signingKey.publicKeyG2.Y[1] != 0, "BLS public key G2 Y should be non-zero"); } + + function testSignAndVerifyMessage() public { + uint256 index = 1; + OperatorLib.Operator memory operator = OperatorLib.createOperator(index); + + bytes32 messageHash = keccak256(abi.encodePacked("Test message")); + BN254.G1Point memory signature = OperatorLib.signMessageWithOperator(operator, messageHash); + BN254.G1Point memory messagePoint = BN254.hashToG1(messageHash); + + bool isValid = BN254.pairing( + BN254.negate(signature), + BN254.generatorG2(), + messagePoint, + operator.signingKey.publicKeyG2 + ); + + // Check that the signature is valid + assertTrue(isValid, "Signature should be valid"); + } } From 35e676ee8e63b6b858c10dc392647f1167ef10f9 Mon Sep 17 00:00:00 2001 From: steven Date: Tue, 17 Dec 2024 17:02:05 -0500 Subject: [PATCH 49/80] chore: remove comments --- test/unit/OperatorLib.t.sol | 6 ------ 1 file changed, 6 deletions(-) diff --git a/test/unit/OperatorLib.t.sol b/test/unit/OperatorLib.t.sol index 0f352986..14bc2f15 100644 --- a/test/unit/OperatorLib.t.sol +++ b/test/unit/OperatorLib.t.sol @@ -12,17 +12,13 @@ contract OperatorLibTest is Test { uint256 index = 1; OperatorLib.Operator memory operator = OperatorLib.createOperator(index); - // Check that the operator's VM wallet address is non-zero assertTrue(operator.key.addr != address(0), "VM wallet address should be non-zero"); - // Check that the operator's BLS private key is non-zero assertTrue(operator.signingKey.privateKey != 0, "BLS private key should be non-zero"); - // Check that the operator's BLS public key G1 is non-zero assertTrue(operator.signingKey.publicKeyG1.X != 0 || operator.signingKey.publicKeyG1.X != 0, "BLS public key G1 X should be non-zero"); assertTrue(operator.signingKey.publicKeyG1.Y != 0 || operator.signingKey.publicKeyG1.Y!= 0, "BLS public key G1 Y should be non-zero"); - // Check that the operator's BLS public key G2 is non-zero assertTrue(operator.signingKey.publicKeyG2.X[0] != 0 || operator.signingKey.publicKeyG2.X[1] != 0, "BLS public key G2 X should be non-zero"); assertTrue(operator.signingKey.publicKeyG2.Y[0] != 0 || operator.signingKey.publicKeyG2.Y[1] != 0, "BLS public key G2 Y should be non-zero"); } @@ -41,8 +37,6 @@ contract OperatorLibTest is Test { messagePoint, operator.signingKey.publicKeyG2 ); - - // Check that the signature is valid assertTrue(isValid, "Signature should be valid"); } } From bdf4f67437a372c7554258ac2c9131970184b978 Mon Sep 17 00:00:00 2001 From: steven Date: Tue, 17 Dec 2024 17:03:50 -0500 Subject: [PATCH 50/80] feat: improve ux of library --- script/utils/OperatorLib.sol | 15 ++++++++------- test/unit/OperatorLib.t.sol | 5 ++--- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/script/utils/OperatorLib.sol b/script/utils/OperatorLib.sol index fe133583..051c6af1 100644 --- a/script/utils/OperatorLib.sol +++ b/script/utils/OperatorLib.sol @@ -47,8 +47,8 @@ library OperatorLib { BLSWallet signingKey; } - function createBLSWallet(uint256 index) internal returns (BLSWallet memory) { - uint256 privateKey = uint256(keccak256(abi.encodePacked(index + 1))); + function createBLSWallet(uint256 salt) internal returns (BLSWallet memory) { + uint256 privateKey = uint256(keccak256(abi.encodePacked(salt))); BN254.G1Point memory publicKeyG1 = BN254.generatorG1().scalar_mul(privateKey); BN254.G2Point memory publicKeyG2 = mul(privateKey); @@ -59,8 +59,8 @@ library OperatorLib { }); } - function createWallet(uint256 index) internal pure returns (Wallet memory) { - uint256 privateKey = uint256(keccak256(abi.encodePacked(index))); + function createWallet(uint256 salt) internal pure returns (Wallet memory) { + uint256 privateKey = uint256(keccak256(abi.encodePacked(salt))); address addr = vm.addr(privateKey); return Wallet({ @@ -69,9 +69,10 @@ library OperatorLib { }); } - function createOperator(uint256 index) internal returns (Operator memory) { - Wallet memory vmWallet = createWallet(index); - BLSWallet memory blsWallet = createBLSWallet(index); + function createOperator(string memory name) internal returns (Operator memory) { + uint256 salt = uint256(keccak256(abi.encodePacked(name))); + Wallet memory vmWallet = createWallet(salt); + BLSWallet memory blsWallet = createBLSWallet(salt); return Operator({ key: vmWallet, diff --git a/test/unit/OperatorLib.t.sol b/test/unit/OperatorLib.t.sol index 14bc2f15..d349c33c 100644 --- a/test/unit/OperatorLib.t.sol +++ b/test/unit/OperatorLib.t.sol @@ -10,7 +10,7 @@ contract OperatorLibTest is Test { function testCreateOperator() public { uint256 index = 1; - OperatorLib.Operator memory operator = OperatorLib.createOperator(index); + OperatorLib.Operator memory operator = OperatorLib.createOperator("operator-1"); assertTrue(operator.key.addr != address(0), "VM wallet address should be non-zero"); @@ -24,8 +24,7 @@ contract OperatorLibTest is Test { } function testSignAndVerifyMessage() public { - uint256 index = 1; - OperatorLib.Operator memory operator = OperatorLib.createOperator(index); + OperatorLib.Operator memory operator = OperatorLib.createOperator("operator-1"); bytes32 messageHash = keccak256(abi.encodePacked("Test message")); BN254.G1Point memory signature = OperatorLib.signMessageWithOperator(operator, messageHash); From 815051fd667e5458e65c44b828b881c86d07caa4 Mon Sep 17 00:00:00 2001 From: steven Date: Tue, 17 Dec 2024 17:16:27 -0500 Subject: [PATCH 51/80] feat: add remaining functions --- script/utils/OperatorLib.sol | 60 +++++++++++++++++++++++++++++------- 1 file changed, 49 insertions(+), 11 deletions(-) diff --git a/script/utils/OperatorLib.sol b/script/utils/OperatorLib.sol index 051c6af1..bdf36f55 100644 --- a/script/utils/OperatorLib.sol +++ b/script/utils/OperatorLib.sol @@ -11,11 +11,14 @@ import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {IStakeRegistry} from "../../src/interfaces/IStakeRegistry.sol"; import {IRegistryCoordinator} from "../../src/RegistryCoordinator.sol"; import {OperatorStateRetriever} from "../../src/OperatorStateRetriever.sol"; +import {RegistryCoordinator} from "../../src/RegistryCoordinator.sol"; import {IStrategyManager} from "eigenlayer-contracts/src/contracts/interfaces/IStrategyManager.sol"; import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol"; import {IAVSDirectory} from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol"; +import {IAllocationManager, IAllocationManagerTypes} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; +import {IBLSApkRegistry} from "../../src/interfaces/IBLSApkRegistry.sol"; import {IStrategyFactory} from "eigenlayer-contracts/src/contracts/interfaces/IStrategyFactory.sol"; import {PauserRegistry} from "eigenlayer-contracts/src/contracts/permissions/PauserRegistry.sol"; import {UpgradeableProxyLib} from "./UpgradeableProxyLib.sol"; @@ -167,37 +170,72 @@ library OperatorLib { function registerOperatorToAVS_M2( Operator memory operator, address avsDirectory, - address serviceManager + address serviceManager, + address registryCoordinator, + bytes calldata quorumNumbers, + string memory socket ) internal { - IAVSDirectory avsDirectory = IAVSDirectory(avsDirectory); + IAVSDirectory avsDirectoryInstance = IAVSDirectory(avsDirectory); + RegistryCoordinator registryCoordinatorInstance = RegistryCoordinator(registryCoordinator); bytes32 salt = keccak256(abi.encodePacked(block.timestamp, operator.key.addr)); uint256 expiry = block.timestamp + 1 hours; - bytes32 operatorRegistrationDigestHash = avsDirectory + bytes32 operatorRegistrationDigestHash = avsDirectoryInstance .calculateOperatorAVSRegistrationDigestHash( operator.key.addr, serviceManager, salt, expiry ); bytes memory signature = signWithOperatorKey(operator, operatorRegistrationDigestHash); + IBLSApkRegistry.PubkeyRegistrationParams memory params; ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature = ISignatureUtils .SignatureWithSaltAndExpiry({signature: signature, salt: salt, expiry: expiry}); - /// TODO: call the registry + // Call the registerOperator function on the registry + registryCoordinatorInstance.registerOperator(quorumNumbers, socket, params, operatorSignature); } - function deregisterOperatorFromAVS_M2() internal { - /// TODO: call the registry - + function deregisterOperatorFromAVS_M2(Operator memory operator, address registryCoordinator) internal { + RegistryCoordinator(registryCoordinator).deregisterOperator(""); } - function registerOperatorFromAVS_OpSet() internal { - /// TODO: call the ALM + function registerOperatorFromAVS_OpSet( + Operator memory operator, + address allocationManager, + address avs, + uint32[] calldata operatorSetIds, + bytes calldata data + ) internal { + IAllocationManager allocationManagerInstance = IAllocationManager(allocationManager); + + /// TODO: Create data for registry Coordinator + IAllocationManagerTypes.RegisterParams memory params = IAllocationManagerTypes.RegisterParams({ + avs: avs, + operatorSetIds: operatorSetIds, + data: data + }); + + // Register the operator in the Allocation Manager + allocationManagerInstance.registerForOperatorSets(operator.key.addr, params); } - function deregisterOperatorFromAVS_OpSet() internal { - /// TODO: call the ALM + function deregisterOperatorFromAVS_OpSet( + Operator memory operator, + address allocationManager, + address avs, + uint32[] calldata operatorSetIds + ) internal { + IAllocationManager allocationManagerInstance = IAllocationManager(allocationManager); + + IAllocationManagerTypes.DeregisterParams memory params = IAllocationManagerTypes.DeregisterParams({ + operator: operator.key.addr, + avs: avs, + operatorSetIds: operatorSetIds + }); + + // Deregister the operator in the Allocation Manager + allocationManagerInstance.deregisterFromOperatorSets(params); } function createAndAddOperator(uint256 salt) internal returns (Operator memory) { From 98b678f0908a1b662f851a9190c6ddc3337d898c Mon Sep 17 00:00:00 2001 From: steven Date: Wed, 18 Dec 2024 11:42:23 -0500 Subject: [PATCH 52/80] test: operator functionality on preprod --- script/utils/BN256G2.sol | 2 +- script/utils/OperatorLib.sol | 2 +- test/unit/OperatorLib.t.sol | 91 +++++++++++++++++++++++++++++++++++- 3 files changed, 91 insertions(+), 4 deletions(-) diff --git a/script/utils/BN256G2.sol b/script/utils/BN256G2.sol index 55ccb65f..45cd34b2 100644 --- a/script/utils/BN256G2.sol +++ b/script/utils/BN256G2.sol @@ -174,7 +174,7 @@ library BN256G2 { function _modInv(uint256 a, uint256 n) internal view returns (uint256 result) { bool success; - assembly { + assembly ("memory-safe") { let freemem := mload(0x40) mstore(freemem, 0x20) mstore(add(freemem, 0x20), 0x20) diff --git a/script/utils/OperatorLib.sol b/script/utils/OperatorLib.sol index bdf36f55..868bbc1a 100644 --- a/script/utils/OperatorLib.sol +++ b/script/utils/OperatorLib.sol @@ -137,7 +137,7 @@ library OperatorLib { } function depositTokenIntoStrategy( - Operator memory operator, + Operator memory, address strategyManager, address strategy, address token, diff --git a/test/unit/OperatorLib.t.sol b/test/unit/OperatorLib.t.sol index d349c33c..b9c0f07a 100644 --- a/test/unit/OperatorLib.t.sol +++ b/test/unit/OperatorLib.t.sol @@ -1,15 +1,21 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; -import "forge-std/Test.sol"; +import {Test, console2 as console} from "forge-std/Test.sol"; +import {IERC20} from "forge-std/interfaces/IERC20.sol"; import {OperatorLib} from "../../script/utils/OperatorLib.sol"; +import {CoreDeploymentLib} from "../../script/utils/CoreDeploymentLib.sol"; +import {UpgradeableProxyLib} from "../../script/utils/UpgradeableProxyLib.sol"; +import {MiddlewareDeploymentLib} from "../../script/utils/MiddlewareDeploymentLib.sol"; import {BN254} from "../../src/libraries/BN254.sol"; +import {IDelegationManager} from "../../lib/eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; +import {IStrategy} from "../../lib/eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; + contract OperatorLibTest is Test { using OperatorLib for *; function testCreateOperator() public { - uint256 index = 1; OperatorLib.Operator memory operator = OperatorLib.createOperator("operator-1"); assertTrue(operator.key.addr != address(0), "VM wallet address should be non-zero"); @@ -38,5 +44,86 @@ contract OperatorLibTest is Test { ); assertTrue(isValid, "Signature should be valid"); } + + function testEndToEndSetup() public { + // Fork Holesky testnet + string memory rpcUrl = vm.envString("HOLESKY_RPC_URL"); + vm.createSelectFork(rpcUrl); + + // Create 5 operators + OperatorLib.Operator[] memory operators = new OperatorLib.Operator[](5); + for (uint256 i = 0; i < 5; i++) { + operators[i] = OperatorLib.createOperator(string(abi.encodePacked("operator-", i + 100))); + } + + // Read core deployment data from json + CoreDeploymentLib.DeploymentData memory coreDeployment = CoreDeploymentLib.readCoreDeploymentJson("./script/config", 17000, "preprod"); + + // Setup middleware deployment data + MiddlewareDeploymentLib.ConfigData memory middlewareConfig; + middlewareConfig.proxyAdmin = UpgradeableProxyLib.deployProxyAdmin(); + middlewareConfig.admin = address(this); + middlewareConfig.numQuorums = 1; + middlewareConfig.operatorParams = new uint256[](3); + middlewareConfig.operatorParams[0] = 10; + middlewareConfig.operatorParams[1] = 100; + middlewareConfig.operatorParams[2] = 100; + + MiddlewareDeploymentLib.DeploymentData memory middlewareDeployment = MiddlewareDeploymentLib.deployContracts(coreDeployment, middlewareConfig); + + // Upgrade contracts + MiddlewareDeploymentLib.upgradeContracts(middlewareDeployment, middlewareConfig, coreDeployment); + + // Verify operators are registered + for (uint256 i = 0; i < 5; i++) { + bool isRegistered = IDelegationManager(coreDeployment.delegationManager).isOperator(operators[i].key.addr); + assertFalse(isRegistered, "Operator should not be registered"); + } + // Register operators + for (uint256 i = 0; i < 5; i++) { + vm.startPrank(operators[i].key.addr); /// TODO: for script need to just vm.startBroadcast from operator + OperatorLib.registerAsOperator(operators[i], coreDeployment.delegationManager); + vm.stopPrank(); + } + + // Verify operators are registered + for (uint256 i = 0; i < 5; i++) { + bool isRegistered = IDelegationManager(coreDeployment.delegationManager).isOperator(operators[i].key.addr); + assertTrue(isRegistered, "Operator should be registered"); + } + + // Mint mock tokens to each operator + uint256 mintAmount = 1000 * 1e18; + for (uint256 i = 0; i < 5; i++) { + OperatorLib.mintMockTokens(operators[i], middlewareDeployment.token, mintAmount); + } + + // Verify token balances + for (uint256 i = 0; i < 5; i++) { + uint256 balance = IERC20(middlewareDeployment.token).balanceOf(operators[i].key.addr); + assertEq(balance, mintAmount, "Operator should have correct token balance"); + } + + // Deposit tokens into strategy for each operator + for (uint256 i = 0; i < 5; i++) { + vm.startPrank(operators[i].key.addr); + uint256 shares = OperatorLib.depositTokenIntoStrategy( + operators[i], + coreDeployment.strategyManager, + middlewareDeployment.strategy, + middlewareDeployment.token, + mintAmount + ); + assertTrue(shares > 0, "Should have received shares for deposit"); + vm.stopPrank(); + } + + // Verify strategy shares for each operator + for (uint256 i = 0; i < 5; i++) { + uint256 shares = IStrategy(middlewareDeployment.strategy).shares(operators[i].key.addr); + assertEq(shares, mintAmount, "Operator shares should equal deposit amount"); + } + + } } From b54d4008eb087e906eb88d61f7556722c8adcdc6 Mon Sep 17 00:00:00 2001 From: steven Date: Wed, 18 Dec 2024 13:10:16 -0500 Subject: [PATCH 53/80] test: register operator to operator set --- script/utils/OperatorLib.sol | 30 ++++++++++++++++--- test/unit/OperatorLib.t.sol | 56 ++++++++++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+), 4 deletions(-) diff --git a/script/utils/OperatorLib.sol b/script/utils/OperatorLib.sol index 868bbc1a..1c2569d1 100644 --- a/script/utils/OperatorLib.sol +++ b/script/utils/OperatorLib.sol @@ -204,16 +204,38 @@ library OperatorLib { Operator memory operator, address allocationManager, address avs, - uint32[] calldata operatorSetIds, - bytes calldata data + uint32[] memory operatorSetIds ) internal { + + bytes memory registrationParamsData; IAllocationManager allocationManagerInstance = IAllocationManager(allocationManager); - /// TODO: Create data for registry Coordinator + // Create BLS pubkey registration params + IBLSApkRegistry.PubkeyRegistrationParams memory blsParams = IBLSApkRegistry.PubkeyRegistrationParams({ + pubkeyG1: operator.signingKey.publicKeyG1, + pubkeyG2: operator.signingKey.publicKeyG2, + pubkeyRegistrationSignature: operator.signingKey.publicKeyG1 + }); + + // Get the pubkey registration message hash that needs to be signed + BN254.G1Point memory pubkeyRegistrationMessageHash = BN254.hashToG1( + keccak256(abi.encodePacked(operator.key.addr)) + ); + + // Sign the pubkey registration message hash + BN254.G1Point memory signature = signMessage(operator.signingKey, keccak256(abi.encodePacked(operator.key.addr))); + + // Encode the registration data for the registry coordinator + registrationParamsData = abi.encode( + blsParams, + pubkeyRegistrationMessageHash, + signature + ); + IAllocationManagerTypes.RegisterParams memory params = IAllocationManagerTypes.RegisterParams({ avs: avs, operatorSetIds: operatorSetIds, - data: data + data: registrationParamsData }); // Register the operator in the Allocation Manager diff --git a/test/unit/OperatorLib.t.sol b/test/unit/OperatorLib.t.sol index b9c0f07a..7a73b917 100644 --- a/test/unit/OperatorLib.t.sol +++ b/test/unit/OperatorLib.t.sol @@ -9,7 +9,13 @@ import {UpgradeableProxyLib} from "../../script/utils/UpgradeableProxyLib.sol"; import {MiddlewareDeploymentLib} from "../../script/utils/MiddlewareDeploymentLib.sol"; import {BN254} from "../../src/libraries/BN254.sol"; import {IDelegationManager} from "../../lib/eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; +import {IAllocationManagerTypes} from "../../lib/eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; import {IStrategy} from "../../lib/eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; +import {IServiceManager} from "../../src/interfaces/IServiceManager.sol"; +import {IStakeRegistry, StakeType} from "../../src/interfaces/IStakeRegistry.sol"; +import {RegistryCoordinator} from "../../src/RegistryCoordinator.sol"; +import {IRegistryCoordinator} from "../../src/interfaces/IRegistryCoordinator.sol"; +import { OperatorSet} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; contract OperatorLibTest is Test { @@ -124,6 +130,56 @@ contract OperatorLibTest is Test { assertEq(shares, mintAmount, "Operator shares should equal deposit amount"); } + IAllocationManagerTypes.CreateSetParams[] memory params = new IAllocationManagerTypes.CreateSetParams[](1); + IStrategy[] memory strategies = new IStrategy[](1); + strategies[0] = IStrategy(middlewareDeployment.strategy); + params[0] = IAllocationManagerTypes.CreateSetParams({ + operatorSetId: 0, + strategies: strategies + }); + // Migrate AVS to operator sets + vm.startPrank(middlewareConfig.admin); + + // Enable operator sets + RegistryCoordinator(middlewareDeployment.registryCoordinator).enableOperatorSets(); + + // Create quorum for non-slashable stake + IRegistryCoordinator.OperatorSetParam memory operatorSetParams = IRegistryCoordinator.OperatorSetParam({ + maxOperatorCount: 10, + kickBIPsOfOperatorStake: 100, // 1% + kickBIPsOfTotalStake: 100 // 1% + }); + + IStakeRegistry.StrategyParams[] memory strategyParams = new IStakeRegistry.StrategyParams[](1); + strategyParams[0] = IStakeRegistry.StrategyParams({ + strategy: IStrategy(middlewareDeployment.strategy), + multiplier: 1 ether + }); + + RegistryCoordinator(middlewareDeployment.registryCoordinator).createTotalDelegatedStakeQuorum( + operatorSetParams, + 100 ether, // Minimum stake of 100 tokens + strategyParams + ); + + vm.stopPrank(); + + // Register operators to AVS through AllocationManager + uint32[] memory operatorSetIds = new uint32[](1); + operatorSetIds[0] = 1; // First operator set + + // Register each operator to the AVS through AllocationManager + for (uint256 i = 0; i < 5; i++) { + vm.startPrank(operators[i].key.addr); + OperatorLib.registerOperatorFromAVS_OpSet( + operators[i], + coreDeployment.allocationManager, + middlewareDeployment.serviceManager, + operatorSetIds + ); + vm.stopPrank(); + } + } } From f9c8ee8b6e34eb889b3367f699eec2e6cc90f867 Mon Sep 17 00:00:00 2001 From: steven Date: Wed, 18 Dec 2024 13:57:50 -0500 Subject: [PATCH 54/80] feat: add calculatePubkeyRegistrationMessageHash --- script/utils/OperatorLib.sol | 24 +++++++++++------------- src/RegistryCoordinator.sol | 19 +++++++++++++++++-- test/unit/OperatorLib.t.sol | 2 +- 3 files changed, 29 insertions(+), 16 deletions(-) diff --git a/script/utils/OperatorLib.sol b/script/utils/OperatorLib.sol index 1c2569d1..301481b8 100644 --- a/script/utils/OperatorLib.sol +++ b/script/utils/OperatorLib.sol @@ -203,6 +203,7 @@ library OperatorLib { function registerOperatorFromAVS_OpSet( Operator memory operator, address allocationManager, + address registryCoordinator, address avs, uint32[] memory operatorSetIds ) internal { @@ -210,26 +211,23 @@ library OperatorLib { bytes memory registrationParamsData; IAllocationManager allocationManagerInstance = IAllocationManager(allocationManager); - // Create BLS pubkey registration params + + // Get the pubkey registration message hash that needs to be signed + bytes32 pubkeyRegistrationMessageHash = RegistryCoordinator(registryCoordinator).calculatePubkeyRegistrationMessageHash(operator.key.addr); + + // Sign the pubkey registration message hash + BN254.G1Point memory signature = signMessage(operator.signingKey, pubkeyRegistrationMessageHash); + IBLSApkRegistry.PubkeyRegistrationParams memory blsParams = IBLSApkRegistry.PubkeyRegistrationParams({ pubkeyG1: operator.signingKey.publicKeyG1, pubkeyG2: operator.signingKey.publicKeyG2, - pubkeyRegistrationSignature: operator.signingKey.publicKeyG1 + pubkeyRegistrationSignature: signature }); - // Get the pubkey registration message hash that needs to be signed - BN254.G1Point memory pubkeyRegistrationMessageHash = BN254.hashToG1( - keccak256(abi.encodePacked(operator.key.addr)) - ); - - // Sign the pubkey registration message hash - BN254.G1Point memory signature = signMessage(operator.signingKey, keccak256(abi.encodePacked(operator.key.addr))); - // Encode the registration data for the registry coordinator registrationParamsData = abi.encode( - blsParams, - pubkeyRegistrationMessageHash, - signature + "test-socket", // Random socket string + blsParams ); IAllocationManagerTypes.RegisterParams memory params = IAllocationManagerTypes.RegisterParams({ diff --git a/src/RegistryCoordinator.sol b/src/RegistryCoordinator.sol index 99073714..30db1198 100644 --- a/src/RegistryCoordinator.sol +++ b/src/RegistryCoordinator.sol @@ -1150,8 +1150,23 @@ contract RegistryCoordinator is view returns (BN254.G1Point memory) { - return BN254.hashToG1( - _hashTypedDataV4(keccak256(abi.encode(PUBKEY_REGISTRATION_TYPEHASH, operator))) + return BN254.hashToG1(calculatePubkeyRegistrationMessageHash(operator)); + } + + /** + * @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 calculatePubkeyRegistrationMessageHash( + address operator + ) public view returns (bytes32) { + return _hashTypedDataV4( + keccak256( + abi.encode( + PUBKEY_REGISTRATION_TYPEHASH, + operator + ) + ) ); } diff --git a/test/unit/OperatorLib.t.sol b/test/unit/OperatorLib.t.sol index 7a73b917..38c3e0d8 100644 --- a/test/unit/OperatorLib.t.sol +++ b/test/unit/OperatorLib.t.sol @@ -174,12 +174,12 @@ contract OperatorLibTest is Test { OperatorLib.registerOperatorFromAVS_OpSet( operators[i], coreDeployment.allocationManager, + middlewareDeployment.registryCoordinator, middlewareDeployment.serviceManager, operatorSetIds ); vm.stopPrank(); } - } } From bb05b30c7e1adab14b7f1217a9e7fa40510a8a5c Mon Sep 17 00:00:00 2001 From: steven Date: Wed, 18 Dec 2024 14:44:41 -0500 Subject: [PATCH 55/80] test: update quorum --- .github/workflows/tests.yml | 4 ++-- test/unit/OperatorLib.t.sol | 32 +++++++++++++++++++++++++++++++- 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 660c8895..e4323e2d 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -33,13 +33,13 @@ jobs: - name: Run Forge build run: | forge --version - forge build --sizes + forge build --sizes --skip script --skip test --via-ir id: build - name: Run tests shell: bash run: | - forge test --no-match-contract FFI + forge test --no-match-contract FFI --via-ir env: RPC_MAINNET: ${{ secrets.RPC_MAINNET }} diff --git a/test/unit/OperatorLib.t.sol b/test/unit/OperatorLib.t.sol index 38c3e0d8..ecac3c64 100644 --- a/test/unit/OperatorLib.t.sol +++ b/test/unit/OperatorLib.t.sol @@ -180,6 +180,36 @@ contract OperatorLibTest is Test { ); vm.stopPrank(); } + + // Fast forward 10 blocks + vm.roll(block.number + 10); + + // Get all registered operators and sort them + address[][] memory registeredOperators = new address[][](1); + registeredOperators[0] = new address[](5); + for (uint256 i = 0; i < 5; i++) { + registeredOperators[0][i] = operators[i].key.addr; + } + + // Sort operator addresses in ascending order + for (uint256 i = 0; i < registeredOperators[0].length - 1; i++) { + for (uint256 j = 0; j < registeredOperators[0].length - i - 1; j++) { + if (registeredOperators[0][j] > registeredOperators[0][j + 1]) { + address temp = registeredOperators[0][j]; + registeredOperators[0][j] = registeredOperators[0][j + 1]; + registeredOperators[0][j + 1] = temp; + } + } + } + + // Update operators for quorum 1 + bytes memory quorumNumbers = new bytes(1); + quorumNumbers[0] = bytes1(uint8(1)); // Quorum 1 + + vm.prank(middlewareConfig.admin); + RegistryCoordinator(middlewareDeployment.registryCoordinator).updateOperatorsForQuorum( + registeredOperators, + quorumNumbers + ); } } - From 137d9887c515c69a62b5a5bb847ba705dfc67393 Mon Sep 17 00:00:00 2001 From: steven Date: Wed, 18 Dec 2024 15:49:19 -0500 Subject: [PATCH 56/80] test: add e2e test for migration and slash --- script/utils/OperatorLib.sol | 35 ++- test/unit/OperatorLib.t.sol | 484 ++++++++++++++++++++++++++++++++++- 2 files changed, 516 insertions(+), 3 deletions(-) diff --git a/script/utils/OperatorLib.sol b/script/utils/OperatorLib.sol index 301481b8..890eaa5d 100644 --- a/script/utils/OperatorLib.sol +++ b/script/utils/OperatorLib.sol @@ -172,7 +172,7 @@ library OperatorLib { address avsDirectory, address serviceManager, address registryCoordinator, - bytes calldata quorumNumbers, + bytes memory quorumNumbers, string memory socket ) internal { IAVSDirectory avsDirectoryInstance = IAVSDirectory(avsDirectory); @@ -187,7 +187,17 @@ library OperatorLib { ); bytes memory signature = signWithOperatorKey(operator, operatorRegistrationDigestHash); - IBLSApkRegistry.PubkeyRegistrationParams memory params; + // Get the pubkey registration message hash that needs to be signed + bytes32 pubkeyRegistrationMessageHash = registryCoordinatorInstance.calculatePubkeyRegistrationMessageHash(operator.key.addr); + + // Sign the pubkey registration message hash + BN254.G1Point memory blsSig = signMessage(operator.signingKey, pubkeyRegistrationMessageHash); + + IBLSApkRegistry.PubkeyRegistrationParams memory params = IBLSApkRegistry.PubkeyRegistrationParams({ + pubkeyG1: operator.signingKey.publicKeyG1, + pubkeyG2: operator.signingKey.publicKeyG2, + pubkeyRegistrationSignature: blsSig + }); ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature = ISignatureUtils .SignatureWithSaltAndExpiry({signature: signature, salt: salt, expiry: expiry}); @@ -258,6 +268,27 @@ library OperatorLib { allocationManagerInstance.deregisterFromOperatorSets(params); } + function setAllocationDelay( + Operator memory operator, + address allocationManager, + uint32 delay + ) internal { + IAllocationManager allocationManagerInstance = IAllocationManager(allocationManager); + + // Set the allocation delay for the operator + allocationManagerInstance.setAllocationDelay(operator.key.addr, delay); + } + + function modifyOperatorAllocations( + Operator memory operator, + address allocationManager, + IAllocationManagerTypes.AllocateParams[] memory params + ) internal { + IAllocationManager allocationManagerInstance = IAllocationManager(allocationManager); + + allocationManagerInstance.modifyAllocations(operator.key.addr, params); + } + function createAndAddOperator(uint256 salt) internal returns (Operator memory) { Wallet memory operatorKey = createWallet(salt); diff --git a/test/unit/OperatorLib.t.sol b/test/unit/OperatorLib.t.sol index ecac3c64..1a5172cb 100644 --- a/test/unit/OperatorLib.t.sol +++ b/test/unit/OperatorLib.t.sol @@ -16,6 +16,8 @@ import {IStakeRegistry, StakeType} from "../../src/interfaces/IStakeRegistry.sol import {RegistryCoordinator} from "../../src/RegistryCoordinator.sol"; import {IRegistryCoordinator} from "../../src/interfaces/IRegistryCoordinator.sol"; import { OperatorSet} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; +import {ServiceManagerMock} from "../mocks/ServiceManagerMock.sol"; + contract OperatorLibTest is Test { @@ -158,7 +160,7 @@ contract OperatorLibTest is Test { RegistryCoordinator(middlewareDeployment.registryCoordinator).createTotalDelegatedStakeQuorum( operatorSetParams, - 100 ether, // Minimum stake of 100 tokens + 100, // Minimum stake of 100 tokens strategyParams ); @@ -211,5 +213,485 @@ contract OperatorLibTest is Test { registeredOperators, quorumNumbers ); + + // Create a second operator set for slashable stake + IStakeRegistry.StrategyParams[] memory strategyParams2 = new IStakeRegistry.StrategyParams[](1); + strategyParams2[0] = IStakeRegistry.StrategyParams({ + strategy: IStrategy(middlewareDeployment.strategy), + multiplier: 1 ether + }); + + // Configure operator set params + IRegistryCoordinator.OperatorSetParam memory operatorSetParams2 = IRegistryCoordinator.OperatorSetParam({ + maxOperatorCount: 10, + kickBIPsOfOperatorStake: 0, + kickBIPsOfTotalStake: 0 + }); + + // Create quorum with slashable stake type + vm.startPrank(middlewareConfig.admin); + RegistryCoordinator(middlewareDeployment.registryCoordinator).createSlashableStakeQuorum( + operatorSetParams2, + 100, // minimumStake + strategyParams2, + 1 days // lookAheadPeriod + ); + vm.stopPrank(); + + // Set allocation delay to 1 block for each operator + uint32 minDelay = 1; + for (uint256 i = 0; i < 5; i++) { + vm.startPrank(operators[i].key.addr); + OperatorLib.setAllocationDelay( + operators[i], + address(coreDeployment.allocationManager), + minDelay + ); + vm.stopPrank(); + } + + // Roll forward 1 block and advance time by 1 second + console.log("Current block number:", block.number); + console.log("======================"); + + vm.roll(block.number + 100); + + // Set up allocation parameters for each operator + IStrategy[] memory allocStrategies = new IStrategy[](1); + allocStrategies[0] = IStrategy(middlewareDeployment.strategy); + + uint64[] memory magnitudes = new uint64[](1); + magnitudes[0] = uint64(1 ether); // Allocate full magnitude to meet minimum stake + + OperatorSet memory operatorSet = OperatorSet({ + avs: address(middlewareDeployment.serviceManager), + id: 2 // Second operator set + }); + + IAllocationManagerTypes.AllocateParams[] memory allocParams = new IAllocationManagerTypes.AllocateParams[](1); + allocParams[0] = IAllocationManagerTypes.AllocateParams({ + operatorSet: operatorSet, + strategies: allocStrategies, + newMagnitudes: magnitudes + }); + + // Allocate stake for each operator using helper function + for (uint256 i = 0; i < 5; i++) { + vm.startPrank(operators[i].key.addr); + OperatorLib.modifyOperatorAllocations( + operators[i], + address(coreDeployment.allocationManager), + allocParams + ); + vm.stopPrank(); + } + + vm.roll(block.number + 100); + console.log("Current block number:", block.number); + console.log("======================"); + + + + // Register operators to second operator set + uint32[] memory operatorSetIds2 = new uint32[](1); + operatorSetIds2[0] = 2; // Second operator set + + // Register each operator to the second set + for (uint256 i = 0; i < 5; i++) { + vm.startPrank(operators[i].key.addr); + OperatorLib.registerOperatorFromAVS_OpSet( + operators[i], + coreDeployment.allocationManager, + middlewareDeployment.registryCoordinator, + middlewareDeployment.serviceManager, + operatorSetIds2 + ); + vm.stopPrank(); + } + + // Fast forward 10 blocks + vm.roll(block.number + 10); + + // Get all registered operators for second set and sort them + address[][] memory registeredOperators2 = new address[][](1); + registeredOperators2[0] = new address[](5); + for (uint256 i = 0; i < 5; i++) { + registeredOperators2[0][i] = operators[i].key.addr; + } + + // Sort operator addresses in ascending order + for (uint256 i = 0; i < registeredOperators2[0].length - 1; i++) { + for (uint256 j = 0; j < registeredOperators2[0].length - i - 1; j++) { + if (registeredOperators2[0][j] > registeredOperators2[0][j + 1]) { + address temp = registeredOperators2[0][j]; + registeredOperators2[0][j] = registeredOperators2[0][j + 1]; + registeredOperators2[0][j + 1] = temp; + } + } + } + + // Update operators for quorum 2 + bytes memory quorumNumbers2 = new bytes(1); + quorumNumbers2[0] = bytes1(uint8(2)); // Quorum 2 + + vm.prank(middlewareConfig.admin); + RegistryCoordinator(middlewareDeployment.registryCoordinator).updateOperatorsForQuorum( + registeredOperators2, + quorumNumbers2 + ); + + // Propose a new slasher account + address newSlasher = makeAddr("newSlasher"); + vm.prank(middlewareConfig.admin); + ServiceManagerMock(middlewareDeployment.serviceManager).proposeNewSlasher(newSlasher); + + // Fast forward 8 days to pass the SLASHER_PROPOSAL_DELAY (7 days) + vm.warp(block.timestamp + 8 days); + + // Accept the proposed slasher + vm.prank(middlewareConfig.admin); + ServiceManagerMock(middlewareDeployment.serviceManager).acceptProposedSlasher(); + + // Create slashing params + IAllocationManagerTypes.SlashingParams memory slashingParams = IAllocationManagerTypes.SlashingParams({ + operator: operators[0].key.addr, // Slash first operator + operatorSetId: operatorSetIds2[0], // Using second operator set + strategies: new IStrategy[](1), + wadsToSlash: new uint256[](1), + description: "Test slashing" + }); + + // Add strategy to slash + slashingParams.strategies[0] = IStrategy(middlewareDeployment.strategy); + + // Slash 50% of operator's stake (0.5e18) + slashingParams.wadsToSlash[0] = 0.5e18; + + // Call slash as the slasher + vm.prank(newSlasher); + ServiceManagerMock(middlewareDeployment.serviceManager).slashOperator(slashingParams); + } + + function testEndToEndSetup_M2Migration() public { + // Fork Holesky testnet + string memory rpcUrl = vm.envString("HOLESKY_RPC_URL"); + vm.createSelectFork(rpcUrl); + + // Create 5 operators + OperatorLib.Operator[] memory operators = new OperatorLib.Operator[](5); + for (uint256 i = 0; i < 5; i++) { + operators[i] = OperatorLib.createOperator(string(abi.encodePacked("operator-", i + 100))); + } + + // Read core deployment data from json + CoreDeploymentLib.DeploymentData memory coreDeployment = CoreDeploymentLib.readCoreDeploymentJson("./script/config", 17000, "preprod"); + + // Setup middleware deployment data + MiddlewareDeploymentLib.ConfigData memory middlewareConfig; + middlewareConfig.proxyAdmin = UpgradeableProxyLib.deployProxyAdmin(); + middlewareConfig.admin = address(this); + middlewareConfig.numQuorums = 1; + middlewareConfig.operatorParams = new uint256[](3); + middlewareConfig.operatorParams[0] = 10; + middlewareConfig.operatorParams[1] = 100; + middlewareConfig.operatorParams[2] = 100; + + MiddlewareDeploymentLib.DeploymentData memory middlewareDeployment = MiddlewareDeploymentLib.deployContracts(coreDeployment, middlewareConfig); + + // Upgrade contracts + MiddlewareDeploymentLib.upgradeContracts(middlewareDeployment, middlewareConfig, coreDeployment); + + // Verify operators are registered + for (uint256 i = 0; i < 5; i++) { + bool isRegistered = IDelegationManager(coreDeployment.delegationManager).isOperator(operators[i].key.addr); + assertFalse(isRegistered, "Operator should not be registered"); + } + // Register operators + for (uint256 i = 0; i < 5; i++) { + vm.startPrank(operators[i].key.addr); /// TODO: for script need to just vm.startBroadcast from operator + OperatorLib.registerAsOperator(operators[i], coreDeployment.delegationManager); + vm.stopPrank(); + } + + // Verify operators are registered + for (uint256 i = 0; i < 5; i++) { + bool isRegistered = IDelegationManager(coreDeployment.delegationManager).isOperator(operators[i].key.addr); + assertTrue(isRegistered, "Operator should be registered"); + } + + // Mint mock tokens to each operator + uint256 mintAmount = 1000 * 1e18; + for (uint256 i = 0; i < 5; i++) { + OperatorLib.mintMockTokens(operators[i], middlewareDeployment.token, mintAmount); + } + + // Verify token balances + for (uint256 i = 0; i < 5; i++) { + uint256 balance = IERC20(middlewareDeployment.token).balanceOf(operators[i].key.addr); + assertEq(balance, mintAmount, "Operator should have correct token balance"); + } + + // Deposit tokens into strategy for each operator + for (uint256 i = 0; i < 5; i++) { + vm.startPrank(operators[i].key.addr); + uint256 shares = OperatorLib.depositTokenIntoStrategy( + operators[i], + coreDeployment.strategyManager, + middlewareDeployment.strategy, + middlewareDeployment.token, + mintAmount + ); + assertTrue(shares > 0, "Should have received shares for deposit"); + vm.stopPrank(); + } + + // Verify strategy shares for each operator + for (uint256 i = 0; i < 5; i++) { + uint256 shares = IStrategy(middlewareDeployment.strategy).shares(operators[i].key.addr); + assertEq(shares, mintAmount, "Operator shares should equal deposit amount"); + } + + IAllocationManagerTypes.CreateSetParams[] memory params = new IAllocationManagerTypes.CreateSetParams[](1); + IStrategy[] memory strategies = new IStrategy[](1); + strategies[0] = IStrategy(middlewareDeployment.strategy); + params[0] = IAllocationManagerTypes.CreateSetParams({ + operatorSetId: 0, + strategies: strategies + }); + + // Create quorum for non-slashable stake + IRegistryCoordinator.OperatorSetParam memory operatorSetParams = IRegistryCoordinator.OperatorSetParam({ + maxOperatorCount: 10, + kickBIPsOfOperatorStake: 100, // 1% + kickBIPsOfTotalStake: 100 // 1% + }); + + IStakeRegistry.StrategyParams[] memory strategyParams = new IStakeRegistry.StrategyParams[](1); + strategyParams[0] = IStakeRegistry.StrategyParams({ + strategy: IStrategy(middlewareDeployment.strategy), + multiplier: 1 ether + }); + + RegistryCoordinator(middlewareDeployment.registryCoordinator).createTotalDelegatedStakeQuorum( + operatorSetParams, + 100, // Minimum stake of 100 tokens + strategyParams + ); + + vm.stopPrank(); + + // Register operators to AVS through AllocationManager + uint32[] memory operatorSetIds = new uint32[](1); + operatorSetIds[0] = 1; // First operator set + + // Register each operator to the AVS through M2 + for (uint256 i = 0; i < 5; i++) { + vm.startPrank(operators[i].key.addr); + bytes memory quorumNumbers = new bytes(1); + quorumNumbers[0] = bytes1(uint8(1)); // Quorum 1 + OperatorLib.registerOperatorToAVS_M2( + operators[i], + coreDeployment.avsDirectory, + middlewareDeployment.serviceManager, + middlewareDeployment.registryCoordinator, + quorumNumbers, + "test-socket" + ); + vm.stopPrank(); + } + + // Fast forward 10 blocks + vm.roll(block.number + 10); + + // Get all registered operators and sort them + address[][] memory registeredOperators = new address[][](1); + registeredOperators[0] = new address[](5); + for (uint256 i = 0; i < 5; i++) { + registeredOperators[0][i] = operators[i].key.addr; + } + + // Sort operator addresses in ascending order + for (uint256 i = 0; i < registeredOperators[0].length - 1; i++) { + for (uint256 j = 0; j < registeredOperators[0].length - i - 1; j++) { + if (registeredOperators[0][j] > registeredOperators[0][j + 1]) { + address temp = registeredOperators[0][j]; + registeredOperators[0][j] = registeredOperators[0][j + 1]; + registeredOperators[0][j + 1] = temp; + } + } + } + + // Update operators for quorum 1 + bytes memory quorumNumbers = new bytes(1); + quorumNumbers[0] = bytes1(uint8(1)); // Quorum 1 + + vm.prank(middlewareConfig.admin); + RegistryCoordinator(middlewareDeployment.registryCoordinator).updateOperatorsForQuorum( + registeredOperators, + quorumNumbers + ); + + // Enable operator sets + // Migrate AVS to operator sets + vm.startPrank(middlewareConfig.admin); + RegistryCoordinator(middlewareDeployment.registryCoordinator).enableOperatorSets(); + + // Create a second operator set for slashable stake + IStakeRegistry.StrategyParams[] memory strategyParams2 = new IStakeRegistry.StrategyParams[](1); + strategyParams2[0] = IStakeRegistry.StrategyParams({ + strategy: IStrategy(middlewareDeployment.strategy), + multiplier: 1 ether + }); + + // Configure operator set params + IRegistryCoordinator.OperatorSetParam memory operatorSetParams2 = IRegistryCoordinator.OperatorSetParam({ + maxOperatorCount: 10, + kickBIPsOfOperatorStake: 0, + kickBIPsOfTotalStake: 0 + }); + + // Create quorum with slashable stake type + vm.startPrank(middlewareConfig.admin); + RegistryCoordinator(middlewareDeployment.registryCoordinator).createSlashableStakeQuorum( + operatorSetParams2, + 100, // minimumStake + strategyParams2, + 1 days // lookAheadPeriod + ); + vm.stopPrank(); + + // Set allocation delay to 1 block for each operator + uint32 minDelay = 1; + for (uint256 i = 0; i < 5; i++) { + vm.startPrank(operators[i].key.addr); + OperatorLib.setAllocationDelay( + operators[i], + address(coreDeployment.allocationManager), + minDelay + ); + vm.stopPrank(); + } + + // Roll forward 1 block and advance time by 1 second + console.log("Current block number:", block.number); + console.log("======================"); + + vm.roll(block.number + 100); + + // Set up allocation parameters for each operator + IStrategy[] memory allocStrategies = new IStrategy[](1); + allocStrategies[0] = IStrategy(middlewareDeployment.strategy); + + uint64[] memory magnitudes = new uint64[](1); + magnitudes[0] = uint64(1 ether); // Allocate full magnitude to meet minimum stake + + OperatorSet memory operatorSet = OperatorSet({ + avs: address(middlewareDeployment.serviceManager), + id: 2 // Second operator set + }); + + IAllocationManagerTypes.AllocateParams[] memory allocParams = new IAllocationManagerTypes.AllocateParams[](1); + allocParams[0] = IAllocationManagerTypes.AllocateParams({ + operatorSet: operatorSet, + strategies: allocStrategies, + newMagnitudes: magnitudes + }); + + // Allocate stake for each operator using helper function + for (uint256 i = 0; i < 5; i++) { + vm.startPrank(operators[i].key.addr); + OperatorLib.modifyOperatorAllocations( + operators[i], + address(coreDeployment.allocationManager), + allocParams + ); + vm.stopPrank(); + } + + vm.roll(block.number + 100); + console.log("Current block number:", block.number); + console.log("======================"); + + + + // Register operators to second operator set + uint32[] memory operatorSetIds2 = new uint32[](1); + operatorSetIds2[0] = 2; // Second operator set + + // Register each operator to the second set + for (uint256 i = 0; i < 5; i++) { + vm.startPrank(operators[i].key.addr); + OperatorLib.registerOperatorFromAVS_OpSet( + operators[i], + coreDeployment.allocationManager, + middlewareDeployment.registryCoordinator, + middlewareDeployment.serviceManager, + operatorSetIds2 + ); + vm.stopPrank(); + } + + // Fast forward 10 blocks + vm.roll(block.number + 10); + + // Get all registered operators for second set and sort them + address[][] memory registeredOperators2 = new address[][](1); + registeredOperators2[0] = new address[](5); + for (uint256 i = 0; i < 5; i++) { + registeredOperators2[0][i] = operators[i].key.addr; + } + + // Sort operator addresses in ascending order + for (uint256 i = 0; i < registeredOperators2[0].length - 1; i++) { + for (uint256 j = 0; j < registeredOperators2[0].length - i - 1; j++) { + if (registeredOperators2[0][j] > registeredOperators2[0][j + 1]) { + address temp = registeredOperators2[0][j]; + registeredOperators2[0][j] = registeredOperators2[0][j + 1]; + registeredOperators2[0][j + 1] = temp; + } + } + } + + // Update operators for quorum 2 + bytes memory quorumNumbers2 = new bytes(1); + quorumNumbers2[0] = bytes1(uint8(2)); // Quorum 2 + + vm.prank(middlewareConfig.admin); + RegistryCoordinator(middlewareDeployment.registryCoordinator).updateOperatorsForQuorum( + registeredOperators2, + quorumNumbers2 + ); + + // Propose a new slasher account + address newSlasher = makeAddr("newSlasher"); + vm.prank(middlewareConfig.admin); + ServiceManagerMock(middlewareDeployment.serviceManager).proposeNewSlasher(newSlasher); + + // Fast forward 8 days to pass the SLASHER_PROPOSAL_DELAY (7 days) + vm.warp(block.timestamp + 8 days); + + // Accept the proposed slasher + vm.prank(middlewareConfig.admin); + ServiceManagerMock(middlewareDeployment.serviceManager).acceptProposedSlasher(); + + // Create slashing params + IAllocationManagerTypes.SlashingParams memory slashingParams = IAllocationManagerTypes.SlashingParams({ + operator: operators[0].key.addr, // Slash first operator + operatorSetId: operatorSetIds2[0], // Using second operator set + strategies: new IStrategy[](1), + wadsToSlash: new uint256[](1), + description: "Test slashing" + }); + + // Add strategy to slash + slashingParams.strategies[0] = IStrategy(middlewareDeployment.strategy); + + // Slash 50% of operator's stake (0.5e18) + slashingParams.wadsToSlash[0] = 0.5e18; + + // Call slash as the slasher + vm.prank(newSlasher); + ServiceManagerMock(middlewareDeployment.serviceManager).slashOperator(slashingParams); } + } From 3647e9485022437913fd5630dab277f1914be61e Mon Sep 17 00:00:00 2001 From: steven Date: Wed, 18 Dec 2024 15:49:38 -0500 Subject: [PATCH 57/80] fix: misc getRestakeable strategies --- src/unaudited/ECDSAServiceManagerBase.sol | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/unaudited/ECDSAServiceManagerBase.sol b/src/unaudited/ECDSAServiceManagerBase.sol index 805a961a..13dbcf33 100644 --- a/src/unaudited/ECDSAServiceManagerBase.sol +++ b/src/unaudited/ECDSAServiceManagerBase.sol @@ -254,10 +254,7 @@ abstract contract ECDSAServiceManagerBase is for (uint256 i; i < count; i++) { strategies[i] = quorum.strategies[i].strategy; } - uint256[] memory shares; - // TODO: Fix - // = IDelegationManager(delegationManager) - // .getOperatorShares(_operator, strategies); + uint256[] memory shares = IDelegationManager(delegationManager).getOperatorShares(_operator, strategies); uint256 activeCount; for (uint256 i; i < count; i++) { From bd07f1ddf2a067d5f2b5c535d5e4d8380cbe4a21 Mon Sep 17 00:00:00 2001 From: steven Date: Wed, 18 Dec 2024 15:54:54 -0500 Subject: [PATCH 58/80] chore: rename for clarity --- test/unit/{OperatorLib.t.sol => End2End.t.sol} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename test/unit/{OperatorLib.t.sol => End2End.t.sol} (99%) diff --git a/test/unit/OperatorLib.t.sol b/test/unit/End2End.t.sol similarity index 99% rename from test/unit/OperatorLib.t.sol rename to test/unit/End2End.t.sol index 1a5172cb..9e5213ba 100644 --- a/test/unit/OperatorLib.t.sol +++ b/test/unit/End2End.t.sol @@ -20,7 +20,7 @@ import {ServiceManagerMock} from "../mocks/ServiceManagerMock.sol"; -contract OperatorLibTest is Test { +contract End2EndTest is Test { using OperatorLib for *; function testCreateOperator() public { From d96ad5c62a7baf9ccd977587d13c2d043b1513e8 Mon Sep 17 00:00:00 2001 From: steven Date: Wed, 18 Dec 2024 16:29:32 -0500 Subject: [PATCH 59/80] chore: revert forge-std change --- lib/forge-std | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/forge-std b/lib/forge-std index 1eea5bae..bb4ceea9 160000 --- a/lib/forge-std +++ b/lib/forge-std @@ -1 +1 @@ -Subproject commit 1eea5bae12ae557d589f9f0f0edae2faa47cb262 +Subproject commit bb4ceea94d6f10eeb5b41dc2391c6c8bf8e734ef From e062196b71963ada40c0cf7e40de29b2a23a2189 Mon Sep 17 00:00:00 2001 From: steven Date: Wed, 18 Dec 2024 16:33:21 -0500 Subject: [PATCH 60/80] chore: bump forge-std --- lib/forge-std | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/forge-std b/lib/forge-std index bb4ceea9..1eea5bae 160000 --- a/lib/forge-std +++ b/lib/forge-std @@ -1 +1 @@ -Subproject commit bb4ceea94d6f10eeb5b41dc2391c6c8bf8e734ef +Subproject commit 1eea5bae12ae557d589f9f0f0edae2faa47cb262 From 44e1a40b40ebb352a1ce9a9b527fd70d1e2cad83 Mon Sep 17 00:00:00 2001 From: steven Date: Wed, 18 Dec 2024 16:51:18 -0500 Subject: [PATCH 61/80] chore: bump forge-std --- lib/forge-std | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/forge-std b/lib/forge-std index bb4ceea9..1eea5bae 160000 --- a/lib/forge-std +++ b/lib/forge-std @@ -1 +1 @@ -Subproject commit bb4ceea94d6f10eeb5b41dc2391c6c8bf8e734ef +Subproject commit 1eea5bae12ae557d589f9f0f0edae2faa47cb262 From 3d4b0b89e0897cb8e39dec55b5b2e89bbc4bb1de Mon Sep 17 00:00:00 2001 From: steven Date: Thu, 19 Dec 2024 10:12:43 -0500 Subject: [PATCH 62/80] chore: update logging --- script/DeployMiddleware.s.sol | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/script/DeployMiddleware.s.sol b/script/DeployMiddleware.s.sol index af8bc355..b13acd33 100644 --- a/script/DeployMiddleware.s.sol +++ b/script/DeployMiddleware.s.sol @@ -14,7 +14,7 @@ contract DeployMiddleware is Script { address internal deployer; function setUp() public { - deployer = vm.rememberKey(vm.envUint("PRIVATE_KEY")); + deployer = vm.rememberKey(vm.envUint("HOLESKY_PRIVATE_KEY")); vm.label(deployer, "Deployer"); // Read core deployment data from json @@ -51,11 +51,14 @@ contract DeployMiddleware is Script { function logDeploymentDetails(MiddlewareDeploymentLib.DeploymentData memory result) internal pure { console.log("Deployment completed"); + console.log("ServiceManager:", result.serviceManager); console.log("RegistryCoordinator:", result.registryCoordinator); console.log("BLSApkRegistry:", result.blsapkRegistry); console.log("IndexRegistry:", result.indexRegistry); console.log("StakeRegistry:", result.stakeRegistry); console.log("OperatorStateRetriever:", result.operatorStateRetriever); + console.log("Token:", result.token); + console.log("Strategy:", result.strategy); } function labelContracts(CoreDeploymentLib.DeploymentData memory coreData, MiddlewareDeploymentLib.DeploymentData memory middlewareData) internal { From 865b49015652a3dd3ba28b666d15c4a68eb23670 Mon Sep 17 00:00:00 2001 From: steven <12021290+stevennevins@users.noreply.github.com> Date: Thu, 19 Dec 2024 12:20:45 -0500 Subject: [PATCH 63/80] docs: update readme with note on slashing (#341) --- README.md | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index f33b6920..62b879a8 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,7 @@ EigenLayer is a set of smart contracts deployed on Ethereum that enable restakin ## Branching The main branches we use are: + * [`dev (default)`](https://github.com/Layr-Labs/eigenlayer-middleware/tree/dev): The most up-to-date branch, containing the work-in-progress code for upcoming releases * [`testnet-holesky`](https://github.com/Layr-Labs/eigenlayer-middleware/tree/testnet-holesky): Our current testnet deployment * [`mainnet`](https://github.com/Layr-Labs/eigenlayer-middleware/tree/mainnet): Our current mainnet deployment @@ -24,6 +25,7 @@ The main branches we use are: ### Basics To get a basic understanding of EigenLayer, check out [You Could've Invented EigenLayer](https://www.blog.eigenlayer.xyz/ycie/). Note that some of the document's content describes features that do not exist yet (like the Slasher). To understand more about how restakers and operators interact with EigenLayer, check out these guides: + * [Restaking User Guide](https://docs.eigenlayer.xyz/restaking-guides/restaking-user-guide) * [Operator Guide](https://docs.eigenlayer.xyz/operator-guides/operator-introduction) @@ -32,10 +34,25 @@ Most of this content is intro-level and describes user interactions with the Eig ### Deep Dive For shadowy super-coders: + * The most up-to-date technical documentation can be found in [/docs](/docs). * To get an idea of how users interact with these contracts, check out the integration tests: [/test/integration](./test/integration) * To explore the EigenLayer core contracts, check out the core repo technical docs [here][core-docs-dev]. +### Note On Slashing + +The middleware contracts are available for testnet integration and experimentation. As with the rest of the testnet code, these contracts have not yet been fully audited. Internal security reviews are in progress, and we're preparing to engage external auditors. We're providing these now so AVSs can begin testing slashing conditions, Operator Sets, and related functionality, and so Operators can simulate slashing and test allocations. For more details, see [ELIP-002](https://github.com/eigenfoundation/ELIPs/blob/main/ELIPs/ELIP-002.md). + +For Operators: + +We provide scripts to deploy a mock service manager and allocate some stake to a mock Operator Set in [these scripts](https://github.com/Layr-Labs/eigenlayer-middleware/pull/335). +We've reduced various safety delays on testnet to better simulate operational flows. Additional information on these delays is in [ELIP-002](https://github.com/eigenfoundation/ELIPs/blob/main/ELIPs/ELIP-002.md). +For AVSs: + +We provide a migration path for existing M2 environments (LINK). While this requires understanding the provided script, the Developer Experience will improve before Mainnet launch. Note that this is a gradual migration process to new Operator Sets, not a direct upgrade of existing Operator Sets. +We also provide scripts to spin up new service managers from scratch. +In all cases, you’ll need to register existing Operators or spin up new Operators and allocate funds to them. These testing flows will improve as we refine the scripts and contracts. Stay tuned for updates, and feel free to reach out with any questions. + ## Building and Running Tests This repository uses Foundry. See the [Foundry docs](https://book.getfoundry.sh/) for more info on installation and usage. If you already have foundry, you can build this project and run tests with these commands: @@ -80,4 +97,3 @@ The current testnet deployment is on holesky, is from our M2 beta release. You c [`ServiceManagerRouter`](https://github.com/Layr-Labs/eigenlayer-middleware/blob/testnet-holesky/src/ServiceManagerRouter.sol) | - | [`0x4463...5a37`](https://holesky.etherscan.io/address/0x44632dfBdCb6D3E21EF613B0ca8A6A0c618F5a37#code) | | [`ProxyAdmin`](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v4.7.1/contracts/proxy/transparent/ProxyAdmin.sol) | - | [`0xB043...5c15`](https://holesky.etherscan.io/address/0xB043055dd967A382577c2f5261fA6428f2905c15) | | [`eigenda/EigenDAServiceManager`](https://github.com/Layr-Labs/eigenda/blob/a33b41561cc3fb4cd6d50a8738e4c5dca43ec0a5/contracts/src/core/EigenDAServiceManager.sol) | [`0xD4A7E1Bd8015057293f0D0A557088c286942e84b`](https://holesky.etherscan.io/address/0xD4A7E1Bd8015057293f0D0A557088c286942e84b) | [`0xa722...67f3`](https://holesky.etherscan.io/address/0xa7227485e6C693AC4566fe168C5E3647c5c267f3) | Proxy: [`TUP@4.7.1`](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v4.7.1/contracts/proxy/transparent/TransparentUpgradeableProxy.sol) | - From 0c912a58c43310e2ed769a4d1763face194c4a64 Mon Sep 17 00:00:00 2001 From: steven Date: Thu, 19 Dec 2024 15:14:54 -0500 Subject: [PATCH 64/80] chore: refactor scripts to remove --via-ir need --- script/utils/CoreDeploymentLib.sol | 207 +++++++++++++++-------- script/utils/MiddlewareDeploymentLib.sol | 179 +++++++++++++------- 2 files changed, 249 insertions(+), 137 deletions(-) diff --git a/script/utils/CoreDeploymentLib.sol b/script/utils/CoreDeploymentLib.sol index e46cfc6a..cb74f0b4 100644 --- a/script/utils/CoreDeploymentLib.sol +++ b/script/utils/CoreDeploymentLib.sol @@ -114,13 +114,63 @@ library CoreDeploymentLib { address permissionController; } + struct DeploymentAddresses { + address delegationManagerImpl; + address avsDirectoryImpl; + address strategyManagerImpl; + address strategyFactoryImpl; + address eigenPodManagerImpl; + address rewardsCoordinatorImpl; + address eigenPodImpl; + address eigenPodBeaconImpl; + address baseStrategyImpl; + address pauserRegistryImpl; + } + function deployCoreFromScratch( address proxyAdmin, DeploymentConfig memory config ) internal returns (DeploymentData memory) { DeploymentData memory result; + DeploymentAddresses memory addrs; + + // Deploy empty proxies + result = _deployEmptyProxies(proxyAdmin, result); + + // Deploy implementations + ( + addrs.delegationManagerImpl, + addrs.avsDirectoryImpl, + addrs.strategyManagerImpl, + addrs.strategyFactoryImpl + ) = _deployMainImplementations(result, config); + + address ethPOSDeposit = _getEthPOSDeposit(); + + ( + addrs.eigenPodManagerImpl, + addrs.rewardsCoordinatorImpl, + addrs.eigenPodImpl, + addrs.eigenPodBeaconImpl, + addrs.baseStrategyImpl, + addrs.pauserRegistryImpl + ) = _deployRemainingImplementations(result, config, ethPOSDeposit); + + // Deploy strategy beacon + result.strategyBeacon = address(new UpgradeableBeacon(addrs.baseStrategyImpl)); + + // Upgrade all contracts + _upgradeAllContracts( + result, + config, + proxyAdmin, + addrs + ); + + return result; + } - // Set up empty proxies for each contract + function _deployEmptyProxies(address proxyAdmin, DeploymentData memory result) private returns (DeploymentData memory) { result.delegationManager = UpgradeableProxyLib.setUpEmptyProxy(proxyAdmin); result.avsDirectory = UpgradeableProxyLib.setUpEmptyProxy(proxyAdmin); result.strategyManager = UpgradeableProxyLib.setUpEmptyProxy(proxyAdmin); @@ -129,9 +179,16 @@ library CoreDeploymentLib { result.eigenPodBeacon = UpgradeableProxyLib.setUpEmptyProxy(proxyAdmin); result.pauserRegistry = UpgradeableProxyLib.setUpEmptyProxy(proxyAdmin); result.strategyFactory = UpgradeableProxyLib.setUpEmptyProxy(proxyAdmin); + return result; + } - // Deploy implementation contracts - address delegationManagerImpl = address( + function _deployMainImplementations(DeploymentData memory result, DeploymentConfig memory config) private returns ( + address delegationManagerImpl, + address avsDirectoryImpl, + address strategyManagerImpl, + address strategyFactoryImpl + ) { + delegationManagerImpl = address( new DelegationManager( IStrategyManager(result.strategyManager), IEigenPodManager(result.eigenPodManager), @@ -141,32 +198,51 @@ library CoreDeploymentLib { uint32(config.delegationManager.initPausedStatus) ) ); - address avsDirectoryImpl = address( + + avsDirectoryImpl = address( new AVSDirectory( IDelegationManager(result.delegationManager), IPauserRegistry(result.pauserRegistry) ) ); - address strategyManagerImpl = address( + + strategyManagerImpl = address( new StrategyManager( IDelegationManager(result.delegationManager), IPauserRegistry(result.pauserRegistry) ) ); - address strategyFactoryImpl = address(new StrategyFactory( + + strategyFactoryImpl = address(new StrategyFactory( IStrategyManager(result.strategyManager), IPauserRegistry(result.pauserRegistry) )); - address ethPOSDeposit; + return (delegationManagerImpl, avsDirectoryImpl, strategyManagerImpl, strategyFactoryImpl); + } + + function _getEthPOSDeposit() private view returns (address) { if (block.chainid == 1) { - ethPOSDeposit = 0x00000000219ab540356cBB839Cbe05303d7705Fa; - } else { - // Handle non-mainnet chains - /// TODO: Handle Eth pos + return 0x00000000219ab540356cBB839Cbe05303d7705Fa; } + // Handle non-mainnet chains + /// TODO: Handle Eth pos + return address(0); + } - address eigenPodManagerImpl = address( + function _deployRemainingImplementations( + DeploymentData memory result, + DeploymentConfig memory config, + address ethPOSDeposit + ) private returns ( + address eigenPodManagerImpl, + address rewardsCoordinatorImpl, + address eigenPodImpl, + address eigenPodBeaconImpl, + address baseStrategyImpl, + address pauserRegistryImpl + ) { + eigenPodManagerImpl = address( new EigenPodManager( IETHPOSDeposit(ethPOSDeposit), IBeacon(result.eigenPodBeacon), @@ -175,7 +251,7 @@ library CoreDeploymentLib { ) ); - address rewardsCoordinatorImpl = address( + rewardsCoordinatorImpl = address( new RewardsCoordinator( IDelegationManager(result.delegationManager), IStrategyManager(result.strategyManager), @@ -190,108 +266,89 @@ library CoreDeploymentLib { ) ); - address eigenPodImpl = address( + eigenPodImpl = address( new EigenPod( IETHPOSDeposit(ethPOSDeposit), IEigenPodManager(result.eigenPodManager), config.eigenPodManager.genesisTime ) ); - address eigenPodBeaconImpl = address(new UpgradeableBeacon(eigenPodImpl)); - address baseStrategyImpl = address(new StrategyBase(IStrategyManager(result.strategyManager), IPauserRegistry(result.pauserRegistry))); + + eigenPodBeaconImpl = address(new UpgradeableBeacon(eigenPodImpl)); + baseStrategyImpl = address(new StrategyBase(IStrategyManager(result.strategyManager), IPauserRegistry(result.pauserRegistry))); + /// TODO: PauserRegistry isn't upgradeable - address pauserRegistryImpl = address( + pauserRegistryImpl = address( new PauserRegistry( new address[](0), // Empty array for pausers - proxyAdmin // ProxyAdmin as the unpauser + result.permissionController // ProxyAdmin as the unpauser ) ); - // Deploy and configure the strategy beacon - result.strategyBeacon = address(new UpgradeableBeacon(baseStrategyImpl)); + return ( + eigenPodManagerImpl, + rewardsCoordinatorImpl, + eigenPodImpl, + eigenPodBeaconImpl, + baseStrategyImpl, + pauserRegistryImpl + ); + } + + function _upgradeAllContracts( + DeploymentData memory result, + DeploymentConfig memory config, + address proxyAdmin, + DeploymentAddresses memory addrs + ) private { + bytes memory upgradeCall; - // Upgrade contracts - /// TODO: Get from config - bytes memory upgradeCall = abi.encodeCall( + upgradeCall = abi.encodeCall( DelegationManager.initialize, - ( - proxyAdmin, // initialOwner - config.delegationManager.initPausedStatus // initialPausedStatus - ) - ); - UpgradeableProxyLib.upgradeAndCall( - result.delegationManager, delegationManagerImpl, upgradeCall + (proxyAdmin, config.delegationManager.initPausedStatus) ); + UpgradeableProxyLib.upgradeAndCall(result.delegationManager, addrs.delegationManagerImpl, upgradeCall); - // Upgrade StrategyManager contract upgradeCall = abi.encodeCall( StrategyManager.initialize, - ( - proxyAdmin, // initialOwner - result.strategyFactory, // initialStrategyWhitelister - config.strategyManager.initPausedStatus // initialPausedStatus - ) + (proxyAdmin, result.strategyFactory, config.strategyManager.initPausedStatus) ); - UpgradeableProxyLib.upgradeAndCall(result.strategyManager, strategyManagerImpl, upgradeCall); + UpgradeableProxyLib.upgradeAndCall(result.strategyManager, addrs.strategyManagerImpl, upgradeCall); - // Upgrade StrategyFactory contract upgradeCall = abi.encodeCall( StrategyFactory.initialize, - ( - proxyAdmin, // initialOwner - config.strategyFactory.initPausedStatus, // initialPausedStatus - IBeacon(result.strategyBeacon) // _strategyBeacon - ) + (proxyAdmin, config.strategyFactory.initPausedStatus, IBeacon(result.strategyBeacon)) ); - UpgradeableProxyLib.upgradeAndCall(result.strategyFactory, strategyFactoryImpl, upgradeCall); + UpgradeableProxyLib.upgradeAndCall(result.strategyFactory, addrs.strategyFactoryImpl, upgradeCall); - // Upgrade EigenPodManager contract upgradeCall = abi.encodeCall( EigenPodManager.initialize, - ( - proxyAdmin, // initialOwner - config.eigenPodManager.initPausedStatus // initialPausedStatus - ) + (proxyAdmin, config.eigenPodManager.initPausedStatus) ); - UpgradeableProxyLib.upgradeAndCall(result.eigenPodManager, eigenPodManagerImpl, upgradeCall); + UpgradeableProxyLib.upgradeAndCall(result.eigenPodManager, addrs.eigenPodManagerImpl, upgradeCall); - // Upgrade AVSDirectory contract upgradeCall = abi.encodeCall( AVSDirectory.initialize, - ( - proxyAdmin, // initialOwner - config.avsDirectory.initialPausedStatus // initialPausedStatus - ) + (proxyAdmin, config.avsDirectory.initialPausedStatus) ); - UpgradeableProxyLib.upgradeAndCall(result.avsDirectory, avsDirectoryImpl, upgradeCall); + UpgradeableProxyLib.upgradeAndCall(result.avsDirectory, addrs.avsDirectoryImpl, upgradeCall); - // Upgrade RewardsCoordinator contract upgradeCall = abi.encodeCall( RewardsCoordinator.initialize, ( - proxyAdmin, // initialOwner - config.rewardsCoordinator.initPausedStatus, // initialPausedStatus - config.rewardsCoordinator.rewardsUpdater, // rewardsUpdater - uint32(config.rewardsCoordinator.activationDelay), // _activationDelay - uint16(config.rewardsCoordinator.defaultOperatorSplitBips) // _defaultSplitBips + proxyAdmin, + config.rewardsCoordinator.initPausedStatus, + config.rewardsCoordinator.rewardsUpdater, + uint32(config.rewardsCoordinator.activationDelay), + uint16(config.rewardsCoordinator.defaultOperatorSplitBips) ) ); - UpgradeableProxyLib.upgradeAndCall( - result.rewardsCoordinator, rewardsCoordinatorImpl, upgradeCall - ); + UpgradeableProxyLib.upgradeAndCall(result.rewardsCoordinator, addrs.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; + upgradeCall = abi.encodeCall(EigenPod.initialize, (address(result.eigenPodManager))); + UpgradeableProxyLib.upgradeAndCall(result.eigenPodBeacon, addrs.eigenPodImpl, upgradeCall); } - function readCoreDeploymentJson(string memory path, uint256 chainId) internal returns (CoreDeploymentLib.DeploymentData memory) { string memory filePath = string(abi.encodePacked(path, "/", vm.toString(chainId), ".json")); return parseZeusJson(filePath); diff --git a/script/utils/MiddlewareDeploymentLib.sol b/script/utils/MiddlewareDeploymentLib.sol index 4b7d985a..c9350e43 100644 --- a/script/utils/MiddlewareDeploymentLib.sol +++ b/script/utils/MiddlewareDeploymentLib.sol @@ -22,14 +22,10 @@ import {OperatorStateRetriever} from "../../src/OperatorStateRetriever.sol"; import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategyManager.sol"; import {IStrategyFactory} from "eigenlayer-contracts/src/contracts/interfaces/IStrategyFactory.sol"; import {ServiceManagerMock} from "../../test/mocks/ServiceManagerMock.sol"; - - import {PauserRegistry, IPauserRegistry} from "eigenlayer-contracts/src/contracts/permissions/PauserRegistry.sol"; import {OperatorStateRetriever} from "../../src/OperatorStateRetriever.sol"; - import {UpgradeableProxyLib} from "./UpgradeableProxyLib.sol"; import {CoreDeploymentLib} from "./CoreDeploymentLib.sol"; - import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; @@ -67,32 +63,41 @@ library MiddlewareDeploymentLib { uint256[] operatorParams; } + struct ImplementationAddresses { + address serviceManagerImpl; + address stakeRegistryImpl; + address blsApkRegistryImpl; + address indexRegistryImpl; + address registryCoordinatorImpl; + } + + struct QuorumParams { + IRegistryCoordinator.OperatorSetParam[] quorumsOperatorSetParams; + uint96[] quorumsMinimumStake; + IStakeRegistry.StrategyParams[][] quorumsStrategyParams; + } + function deployContracts( CoreDeploymentLib.DeploymentData memory core, ConfigData memory config ) internal returns (DeploymentData memory) { DeploymentData memory result; - address[] memory pausers = new address[](2); - pausers[0] = config.admin; - pausers[1] = config.admin; - PauserRegistry pausercontract = new PauserRegistry(pausers, config.admin); - result.pauserRegistry = address(pausercontract); + + // Deploy pauser registry + result.pauserRegistry = _deployPauserRegistry(config.admin); + + // Deploy proxies result.stakeRegistry = UpgradeableProxyLib.setUpEmptyProxy(config.proxyAdmin); result.registryCoordinator = UpgradeableProxyLib.setUpEmptyProxy(config.proxyAdmin); result.blsapkRegistry = UpgradeableProxyLib.setUpEmptyProxy(config.proxyAdmin); result.indexRegistry = UpgradeableProxyLib.setUpEmptyProxy(config.proxyAdmin); result.serviceManager = UpgradeableProxyLib.setUpEmptyProxy(config.proxyAdmin); - OperatorStateRetriever operatorStateRetriever = new OperatorStateRetriever(); - result.operatorStateRetriever = address(operatorStateRetriever); - ERC20Mock token = new ERC20Mock(); - result.token = address(token); + // Deploy operator state retriever + result.operatorStateRetriever = address(new OperatorStateRetriever()); - // Create a new strategy using the strategy factory - IStrategyFactory strategyFactory = IStrategyFactory(core.strategyFactory); - IStrategy strategy = strategyFactory.deployNewStrategy(IERC20(result.token)); - result.strategy = address(strategy); - result.token = address(token); + // Deploy token and strategy + (result.token, result.strategy) = _deployTokenAndStrategy(core.strategyFactory); return result; } @@ -102,8 +107,42 @@ library MiddlewareDeploymentLib { ConfigData memory config, CoreDeploymentLib.DeploymentData memory core ) internal { + // Deploy implementation contracts + ImplementationAddresses memory impls = _deployImplementations(deployment, core); + + // Prepare upgrade data + ( + bytes memory registryCoordinatorUpgradeCall, + bytes memory serviceManagerUpgradeCall + ) = _prepareUpgradeCalls(deployment, config); + + // Execute upgrades + _executeUpgrades( + deployment, + impls, + registryCoordinatorUpgradeCall, + serviceManagerUpgradeCall + ); + } + + function _deployPauserRegistry(address admin) private returns (address) { + address[] memory pausers = new address[](2); + pausers[0] = admin; + pausers[1] = admin; + return address(new PauserRegistry(pausers, admin)); + } + + function _deployTokenAndStrategy(address strategyFactory) private returns (address token, address strategy) { + ERC20Mock tokenContract = new ERC20Mock(); + token = address(tokenContract); + strategy = address(IStrategyFactory(strategyFactory).deployNewStrategy(IERC20(token))); + } - address serviceManagerImpl = address( + function _deployImplementations( + DeploymentData memory deployment, + CoreDeploymentLib.DeploymentData memory core + ) private returns (ImplementationAddresses memory impls) { + impls.serviceManagerImpl = address( new ServiceManagerMock( IAVSDirectory(core.avsDirectory), IRewardsCoordinator(core.rewardsCoordinator), @@ -112,7 +151,8 @@ library MiddlewareDeploymentLib { IAllocationManager(core.allocationManager) ) ); - address stakeRegistryImpl = address( + + impls.stakeRegistryImpl = address( new StakeRegistry( IRegistryCoordinator(deployment.registryCoordinator), IDelegationManager(core.delegationManager), @@ -121,9 +161,10 @@ library MiddlewareDeploymentLib { ) ); - address blsApkRegistryImpl = address(new BLSApkRegistry(IRegistryCoordinator(deployment.registryCoordinator))); - address indexRegistryimpl = address(new IndexRegistry(IRegistryCoordinator(deployment.registryCoordinator))); - address registryCoordinatorImpl = address( + impls.blsApkRegistryImpl = address(new BLSApkRegistry(IRegistryCoordinator(deployment.registryCoordinator))); + impls.indexRegistryImpl = address(new IndexRegistry(IRegistryCoordinator(deployment.registryCoordinator))); + + impls.registryCoordinatorImpl = address( new RegistryCoordinator( IServiceManager(deployment.serviceManager), IStakeRegistry(deployment.stakeRegistry), @@ -133,65 +174,79 @@ library MiddlewareDeploymentLib { IPauserRegistry(deployment.pauserRegistry) ) ); + } + function _prepareUpgradeCalls( + DeploymentData memory deployment, + ConfigData memory config + ) private pure returns (bytes memory registryCoordinatorUpgradeCall, bytes memory serviceManagerUpgradeCall) { IStrategy[1] memory deployedStrategyArray = [IStrategy(deployment.strategy)]; - uint256 numStrategies = deployedStrategyArray.length; + QuorumParams memory params = _prepareQuorumParams(config, deployedStrategyArray); + + registryCoordinatorUpgradeCall = abi.encodeCall( + RegistryCoordinator.initialize, + ( + config.admin, + config.admin, + config.admin, + uint256(0), + params.quorumsOperatorSetParams, + params.quorumsMinimumStake, + params.quorumsStrategyParams, + new StakeType[](1), + new uint32[](1) + ) + ); + + serviceManagerUpgradeCall = abi.encodeCall( + ServiceManagerMock.initialize, + (config.admin, config.admin, config.admin) + ); + } + + function _prepareQuorumParams( + ConfigData memory config, + IStrategy[1] memory deployedStrategyArray + ) private pure returns (QuorumParams memory params) { uint256 numQuorums = config.numQuorums; - IRegistryCoordinator.OperatorSetParam[] memory quorumsOperatorSetParams = - new IRegistryCoordinator.OperatorSetParam[](numQuorums); + uint256 numStrategies = deployedStrategyArray.length; + + params.quorumsOperatorSetParams = new IRegistryCoordinator.OperatorSetParam[](numQuorums); uint256[] memory operator_params = config.operatorParams; for (uint256 i = 0; i < numQuorums; i++) { - quorumsOperatorSetParams[i] = IRegistryCoordinator.OperatorSetParam({ + params.quorumsOperatorSetParams[i] = IRegistryCoordinator.OperatorSetParam({ maxOperatorCount: uint32(operator_params[i]), kickBIPsOfOperatorStake: uint16(operator_params[i + 1]), kickBIPsOfTotalStake: uint16(operator_params[i + 2]) }); } - uint96[] memory quorumsMinimumStake = new uint96[](numQuorums); - IStakeRegistry.StrategyParams[][] memory quorumsStrategyParams = - new IStakeRegistry.StrategyParams[][](numQuorums); + params.quorumsMinimumStake = new uint96[](numQuorums); + params.quorumsStrategyParams = new IStakeRegistry.StrategyParams[][](numQuorums); + for (uint256 i = 0; i < numQuorums; i++) { - quorumsStrategyParams[i] = new IStakeRegistry.StrategyParams[](numStrategies); + params.quorumsStrategyParams[i] = new IStakeRegistry.StrategyParams[](numStrategies); for (uint256 j = 0; j < numStrategies; j++) { - quorumsStrategyParams[i][j] = IStakeRegistry.StrategyParams({ + params.quorumsStrategyParams[i][j] = IStakeRegistry.StrategyParams({ strategy: deployedStrategyArray[j], multiplier: 1 ether }); } } - - bytes memory registryCoordinatorUpgradeCall = abi.encodeCall( - RegistryCoordinator.initialize, - ( - config.admin, - config.admin, - config.admin, - uint256(0), - quorumsOperatorSetParams, - quorumsMinimumStake, - quorumsStrategyParams, - new StakeType[](1), - new uint32[](1) - ) - ); - - bytes memory serviceManagerUpgradeCall = abi.encodeCall( - ServiceManagerMock.initialize, - ( - config.admin, - config.admin, - config.admin - ) - ); - - UpgradeableProxyLib.upgradeAndCall(deployment.serviceManager, serviceManagerImpl, serviceManagerUpgradeCall); - UpgradeableProxyLib.upgrade(deployment.stakeRegistry, stakeRegistryImpl); - UpgradeableProxyLib.upgrade(deployment.blsapkRegistry, blsApkRegistryImpl); - UpgradeableProxyLib.upgrade(deployment.indexRegistry, indexRegistryimpl); - UpgradeableProxyLib.upgradeAndCall(deployment.registryCoordinator, registryCoordinatorImpl, registryCoordinatorUpgradeCall); } + function _executeUpgrades( + DeploymentData memory deployment, + ImplementationAddresses memory impls, + bytes memory registryCoordinatorUpgradeCall, + bytes memory serviceManagerUpgradeCall + ) private { + UpgradeableProxyLib.upgradeAndCall(deployment.serviceManager, impls.serviceManagerImpl, serviceManagerUpgradeCall); + UpgradeableProxyLib.upgrade(deployment.stakeRegistry, impls.stakeRegistryImpl); + UpgradeableProxyLib.upgrade(deployment.blsapkRegistry, impls.blsApkRegistryImpl); + UpgradeableProxyLib.upgrade(deployment.indexRegistry, impls.indexRegistryImpl); + UpgradeableProxyLib.upgradeAndCall(deployment.registryCoordinator, impls.registryCoordinatorImpl, registryCoordinatorUpgradeCall); + } } \ No newline at end of file From d54835c0694c6453e25f05b469486785381c049f Mon Sep 17 00:00:00 2001 From: steven <12021290+stevennevins@users.noreply.github.com> Date: Wed, 8 Jan 2025 08:15:49 -0500 Subject: [PATCH 65/80] chore: bump core fix iface changes (#352) * chore: bump dep * fix: iface changes and param names --- lib/eigenlayer-contracts | 2 +- test/integration/User.t.sol | 2 +- test/mocks/DelegationMock.sol | 39 +++++++++++++++++++++++++----- test/mocks/EigenPodManagerMock.sol | 6 +++++ 4 files changed, 41 insertions(+), 8 deletions(-) diff --git a/lib/eigenlayer-contracts b/lib/eigenlayer-contracts index 85e12bab..22de8094 160000 --- a/lib/eigenlayer-contracts +++ b/lib/eigenlayer-contracts @@ -1 +1 @@ -Subproject commit 85e12bab5f3d4c6ff21790fe5909755ee7154b07 +Subproject commit 22de809403924707ccce6998e62b868bfae0fc58 diff --git a/test/integration/User.t.sol b/test/integration/User.t.sol index 4b58950a..23e9ffcc 100644 --- a/test/integration/User.t.sol +++ b/test/integration/User.t.sol @@ -271,7 +271,7 @@ contract User is Test { params[0] = IDelegationManagerTypes.QueuedWithdrawalParams({ strategies: strategies, depositShares: shares, - withdrawer: address(this) + __deprecated_withdrawer: address(this) }); delegationManager.queueWithdrawals(params); diff --git a/test/mocks/DelegationMock.sol b/test/mocks/DelegationMock.sol index e3b1ed77..36f973da 100644 --- a/test/mocks/DelegationMock.sol +++ b/test/mocks/DelegationMock.sol @@ -232,13 +232,40 @@ contract DelegationIntermediate is IDelegationManager { uint256 wadSlashed ) external virtual {} - function decreaseDelegatedShares( - address staker, - uint256 curDepositShares, - uint64 beaconChainSlashingFactorDecrease - ) external virtual {} + function decreaseDelegatedShares( + address staker, + uint256 curDepositShares, + uint64 beaconChainSlashingFactorDecrease + ) external virtual {} + + function minWithdrawalDelayBlocks() + external + view + virtual + override + returns (uint32) + {} - function minWithdrawalDelayBlocks() external view virtual override returns (uint32) {} + function slashOperatorShares( + address operator, + IStrategy strategy, + uint64 prevMaxMagnitude, + uint64 newMaxMagnitude + ) external override {} + + function getQueuedWithdrawal( + bytes32 withdrawalRoot + ) external view override returns (Withdrawal memory) {} + + function getQueuedWithdrawalRoots( + address staker + ) external view override returns (bytes32[] memory) {} + + function convertToDepositShares( + address staker, + IStrategy[] memory strategies, + uint256[] memory withdrawableShares + ) external view override returns (uint256[] memory) {} } contract DelegationMock is DelegationIntermediate { diff --git a/test/mocks/EigenPodManagerMock.sol b/test/mocks/EigenPodManagerMock.sol index cefd5b7a..700d1840 100644 --- a/test/mocks/EigenPodManagerMock.sol +++ b/test/mocks/EigenPodManagerMock.sol @@ -94,4 +94,10 @@ contract EigenPodManagerMock is Test, Pausable, IEigenPodManager { int256 balanceDeltaWei ) external { } + + function burnableETHShares() external view returns (uint256) { + } + + function increaseBurnableShares(IStrategy strategy, uint256 addedSharesToBurn) external { + } } \ No newline at end of file From cbcc0a4a18b36ccd861d5901f2026a7aa2a29b3c Mon Sep 17 00:00:00 2001 From: "clandestine.eth" <96172957+0xClandestine@users.noreply.github.com> Date: Wed, 8 Jan 2025 15:08:42 -0500 Subject: [PATCH 66/80] feat: ci storage reports (#347) * feat: storage reports * fix: storage report * fix: storage report * fix: ci * fix: ci * fix: ci * fix: ci * fix: ci --- .github/bin/storage-report.sh | 45 +++++++++++++++++ .github/workflows/storage-report.yml | 48 ++++++++++++++++++ Makefile | 2 + bin/storage-report.sh | 45 +++++++++++++++++ docs/storage-report/AVSRegistrar.md | 6 +++ docs/storage-report/BLSApkRegistry.md | 21 ++++++++ docs/storage-report/BLSApkRegistryStorage.md | 21 ++++++++ docs/storage-report/BLSSignatureChecker.md | 9 ++++ .../storage-report/ECDSAServiceManagerBase.md | 19 +++++++ docs/storage-report/ECDSAStakeRegistry.md | 37 ++++++++++++++ .../ECDSAStakeRegistryEqualWeight.md | 39 +++++++++++++++ .../ECDSAStakeRegistryPermissioned.md | 39 +++++++++++++++ .../ECDSAStakeRegistryStorage.md | 27 ++++++++++ docs/storage-report/EjectionManager.md | 21 ++++++++ docs/storage-report/IndexRegistry.md | 17 +++++++ docs/storage-report/IndexRegistryStorage.md | 17 +++++++ docs/storage-report/InstantSlasher.md | 17 +++++++ docs/storage-report/OperatorStateRetriever.md | 6 +++ docs/storage-report/RegistryCoordinator.md | 49 +++++++++++++++++++ .../RegistryCoordinatorStorage.md | 33 +++++++++++++ docs/storage-report/ServiceManagerBase.md | 27 ++++++++++ .../ServiceManagerBaseStorage.md | 27 ++++++++++ docs/storage-report/ServiceManagerRouter.md | 6 +++ docs/storage-report/SlasherBase.md | 17 +++++++ docs/storage-report/SlasherStorage.md | 13 +++++ docs/storage-report/StakeRegistry.md | 21 ++++++++ docs/storage-report/StakeRegistryStorage.md | 21 ++++++++ docs/storage-report/VetoableSlasher.md | 0 28 files changed, 650 insertions(+) create mode 100644 .github/bin/storage-report.sh create mode 100644 .github/workflows/storage-report.yml create mode 100644 Makefile create mode 100644 bin/storage-report.sh create mode 100644 docs/storage-report/AVSRegistrar.md create mode 100644 docs/storage-report/BLSApkRegistry.md create mode 100644 docs/storage-report/BLSApkRegistryStorage.md create mode 100644 docs/storage-report/BLSSignatureChecker.md create mode 100644 docs/storage-report/ECDSAServiceManagerBase.md create mode 100644 docs/storage-report/ECDSAStakeRegistry.md create mode 100644 docs/storage-report/ECDSAStakeRegistryEqualWeight.md create mode 100644 docs/storage-report/ECDSAStakeRegistryPermissioned.md create mode 100644 docs/storage-report/ECDSAStakeRegistryStorage.md create mode 100644 docs/storage-report/EjectionManager.md create mode 100644 docs/storage-report/IndexRegistry.md create mode 100644 docs/storage-report/IndexRegistryStorage.md create mode 100644 docs/storage-report/InstantSlasher.md create mode 100644 docs/storage-report/OperatorStateRetriever.md create mode 100644 docs/storage-report/RegistryCoordinator.md create mode 100644 docs/storage-report/RegistryCoordinatorStorage.md create mode 100644 docs/storage-report/ServiceManagerBase.md create mode 100644 docs/storage-report/ServiceManagerBaseStorage.md create mode 100644 docs/storage-report/ServiceManagerRouter.md create mode 100644 docs/storage-report/SlasherBase.md create mode 100644 docs/storage-report/SlasherStorage.md create mode 100644 docs/storage-report/StakeRegistry.md create mode 100644 docs/storage-report/StakeRegistryStorage.md create mode 100644 docs/storage-report/VetoableSlasher.md diff --git a/.github/bin/storage-report.sh b/.github/bin/storage-report.sh new file mode 100644 index 00000000..71fbd9a0 --- /dev/null +++ b/.github/bin/storage-report.sh @@ -0,0 +1,45 @@ +#!/bin/sh + +# Default output directory +OUTPUT_DIR=${1:-docs/storage-report} + +# Function to print messages +log() { + echo "$(date '+%Y-%m-%d %H:%M:%S') [INFO] $1" +} + +# Function to print error messages +error() { + echo "$(date '+%Y-%m-%d %H:%M:%S') [ERROR] $1" >&2 +} + +log "Starting the storage report generation." + +# Create the output directory if it doesn't exist +if ! mkdir -p "$OUTPUT_DIR"; then + error "Failed to create output directory: $OUTPUT_DIR" + exit 1 +fi + +log "Output directory is set to: $OUTPUT_DIR" + +# Loop through Solidity files and generate storage report +# NOTE: Ignores `src/interfaces` & `src/libraries` since they "should" not contain storage logic. +for file in $(find src/ -name "*.sol" ! -path "*/interfaces/*" ! -path "*/libraries/*"); do + contract_name=$(basename "$file" .sol) + + # Check if the file exists and is readable + if [ ! -r "$file" ]; then + error "Cannot read file: $file" + continue + fi + + log "Processing contract: $contract_name" + + # Run forge inspect and capture errors + if ! forge inspect "$contract_name" storage --pretty > "$OUTPUT_DIR/$contract_name.md"; then + error "Failed to generate storage report for contract: $contract_name" + else + log "Storage report generated for contract: $contract_name" + fi +done \ No newline at end of file diff --git a/.github/workflows/storage-report.yml b/.github/workflows/storage-report.yml new file mode 100644 index 00000000..5d7f50d3 --- /dev/null +++ b/.github/workflows/storage-report.yml @@ -0,0 +1,48 @@ +name: Storage Layout + +on: + push: + branches: + - master + - mainnet + - testnet-goerli + - dev + pull_request: + +jobs: + check_storage: + name: CI + runs-on: "ubuntu-latest" + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Install Foundry + uses: foundry-rs/foundry-toolchain@v1 + with: + version: nightly + + - name: "Generate and prepare the storage reports for current branch" + run: | + bash .github/bin/storage-report.sh pr + + - name: Checkout dev + env: + TARGET: ${{ github.event.pull_request.base.sha }} + run: | + git fetch origin $TARGET + git checkout $TARGET + + - name: "Generate and prepare the storage reports for target branch" + run: | + bash .github/bin/storage-report.sh target + + - name: Compare outputs + run: | + if diff --unified pr target; then + echo "No differences found" + else + echo "::error::Differences found between PR and target branch storage layouts" + exit 1 + fi \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..db84aea5 --- /dev/null +++ b/Makefile @@ -0,0 +1,2 @@ +storage-report: + bash ".github/bin/storage-report.sh" "docs/storage-report/" \ No newline at end of file diff --git a/bin/storage-report.sh b/bin/storage-report.sh new file mode 100644 index 00000000..71fbd9a0 --- /dev/null +++ b/bin/storage-report.sh @@ -0,0 +1,45 @@ +#!/bin/sh + +# Default output directory +OUTPUT_DIR=${1:-docs/storage-report} + +# Function to print messages +log() { + echo "$(date '+%Y-%m-%d %H:%M:%S') [INFO] $1" +} + +# Function to print error messages +error() { + echo "$(date '+%Y-%m-%d %H:%M:%S') [ERROR] $1" >&2 +} + +log "Starting the storage report generation." + +# Create the output directory if it doesn't exist +if ! mkdir -p "$OUTPUT_DIR"; then + error "Failed to create output directory: $OUTPUT_DIR" + exit 1 +fi + +log "Output directory is set to: $OUTPUT_DIR" + +# Loop through Solidity files and generate storage report +# NOTE: Ignores `src/interfaces` & `src/libraries` since they "should" not contain storage logic. +for file in $(find src/ -name "*.sol" ! -path "*/interfaces/*" ! -path "*/libraries/*"); do + contract_name=$(basename "$file" .sol) + + # Check if the file exists and is readable + if [ ! -r "$file" ]; then + error "Cannot read file: $file" + continue + fi + + log "Processing contract: $contract_name" + + # Run forge inspect and capture errors + if ! forge inspect "$contract_name" storage --pretty > "$OUTPUT_DIR/$contract_name.md"; then + error "Failed to generate storage report for contract: $contract_name" + else + log "Storage report generated for contract: $contract_name" + fi +done \ No newline at end of file diff --git a/docs/storage-report/AVSRegistrar.md b/docs/storage-report/AVSRegistrar.md new file mode 100644 index 00000000..1ec5dc07 --- /dev/null +++ b/docs/storage-report/AVSRegistrar.md @@ -0,0 +1,6 @@ + +╭------+------+------+--------+-------+----------╮ +| Name | Type | Slot | Offset | Bytes | Contract | ++================================================+ +╰------+------+------+--------+-------+----------╯ + diff --git a/docs/storage-report/BLSApkRegistry.md b/docs/storage-report/BLSApkRegistry.md new file mode 100644 index 00000000..69239c7c --- /dev/null +++ b/docs/storage-report/BLSApkRegistry.md @@ -0,0 +1,21 @@ + +╭----------------------+------------------------------------------------------+------+--------+-------+---------------------------------------╮ +| Name | Type | Slot | Offset | Bytes | Contract | ++=============================================================================================================================================+ +| _initialized | uint8 | 0 | 0 | 1 | src/BLSApkRegistry.sol:BLSApkRegistry | +|----------------------+------------------------------------------------------+------+--------+-------+---------------------------------------| +| _initializing | bool | 0 | 1 | 1 | src/BLSApkRegistry.sol:BLSApkRegistry | +|----------------------+------------------------------------------------------+------+--------+-------+---------------------------------------| +| operatorToPubkeyHash | mapping(address => bytes32) | 1 | 0 | 32 | src/BLSApkRegistry.sol:BLSApkRegistry | +|----------------------+------------------------------------------------------+------+--------+-------+---------------------------------------| +| pubkeyHashToOperator | mapping(bytes32 => address) | 2 | 0 | 32 | src/BLSApkRegistry.sol:BLSApkRegistry | +|----------------------+------------------------------------------------------+------+--------+-------+---------------------------------------| +| operatorToPubkey | mapping(address => struct BN254.G1Point) | 3 | 0 | 32 | src/BLSApkRegistry.sol:BLSApkRegistry | +|----------------------+------------------------------------------------------+------+--------+-------+---------------------------------------| +| apkHistory | mapping(uint8 => struct IBLSApkRegistry.ApkUpdate[]) | 4 | 0 | 32 | src/BLSApkRegistry.sol:BLSApkRegistry | +|----------------------+------------------------------------------------------+------+--------+-------+---------------------------------------| +| currentApk | mapping(uint8 => struct BN254.G1Point) | 5 | 0 | 32 | src/BLSApkRegistry.sol:BLSApkRegistry | +|----------------------+------------------------------------------------------+------+--------+-------+---------------------------------------| +| __GAP | uint256[45] | 6 | 0 | 1440 | src/BLSApkRegistry.sol:BLSApkRegistry | +╰----------------------+------------------------------------------------------+------+--------+-------+---------------------------------------╯ + diff --git a/docs/storage-report/BLSApkRegistryStorage.md b/docs/storage-report/BLSApkRegistryStorage.md new file mode 100644 index 00000000..3b5b9e61 --- /dev/null +++ b/docs/storage-report/BLSApkRegistryStorage.md @@ -0,0 +1,21 @@ + +╭----------------------+------------------------------------------------------+------+--------+-------+-----------------------------------------------------╮ +| Name | Type | Slot | Offset | Bytes | Contract | ++===========================================================================================================================================================+ +| _initialized | uint8 | 0 | 0 | 1 | src/BLSApkRegistryStorage.sol:BLSApkRegistryStorage | +|----------------------+------------------------------------------------------+------+--------+-------+-----------------------------------------------------| +| _initializing | bool | 0 | 1 | 1 | src/BLSApkRegistryStorage.sol:BLSApkRegistryStorage | +|----------------------+------------------------------------------------------+------+--------+-------+-----------------------------------------------------| +| operatorToPubkeyHash | mapping(address => bytes32) | 1 | 0 | 32 | src/BLSApkRegistryStorage.sol:BLSApkRegistryStorage | +|----------------------+------------------------------------------------------+------+--------+-------+-----------------------------------------------------| +| pubkeyHashToOperator | mapping(bytes32 => address) | 2 | 0 | 32 | src/BLSApkRegistryStorage.sol:BLSApkRegistryStorage | +|----------------------+------------------------------------------------------+------+--------+-------+-----------------------------------------------------| +| operatorToPubkey | mapping(address => struct BN254.G1Point) | 3 | 0 | 32 | src/BLSApkRegistryStorage.sol:BLSApkRegistryStorage | +|----------------------+------------------------------------------------------+------+--------+-------+-----------------------------------------------------| +| apkHistory | mapping(uint8 => struct IBLSApkRegistry.ApkUpdate[]) | 4 | 0 | 32 | src/BLSApkRegistryStorage.sol:BLSApkRegistryStorage | +|----------------------+------------------------------------------------------+------+--------+-------+-----------------------------------------------------| +| currentApk | mapping(uint8 => struct BN254.G1Point) | 5 | 0 | 32 | src/BLSApkRegistryStorage.sol:BLSApkRegistryStorage | +|----------------------+------------------------------------------------------+------+--------+-------+-----------------------------------------------------| +| __GAP | uint256[45] | 6 | 0 | 1440 | src/BLSApkRegistryStorage.sol:BLSApkRegistryStorage | +╰----------------------+------------------------------------------------------+------+--------+-------+-----------------------------------------------------╯ + diff --git a/docs/storage-report/BLSSignatureChecker.md b/docs/storage-report/BLSSignatureChecker.md new file mode 100644 index 00000000..19b3473b --- /dev/null +++ b/docs/storage-report/BLSSignatureChecker.md @@ -0,0 +1,9 @@ + +╭----------------------+-------------+------+--------+-------+-------------------------------------------------╮ +| Name | Type | Slot | Offset | Bytes | Contract | ++==============================================================================================================+ +| staleStakesForbidden | bool | 0 | 0 | 1 | src/BLSSignatureChecker.sol:BLSSignatureChecker | +|----------------------+-------------+------+--------+-------+-------------------------------------------------| +| __GAP | uint256[49] | 1 | 0 | 1568 | src/BLSSignatureChecker.sol:BLSSignatureChecker | +╰----------------------+-------------+------+--------+-------+-------------------------------------------------╯ + diff --git a/docs/storage-report/ECDSAServiceManagerBase.md b/docs/storage-report/ECDSAServiceManagerBase.md new file mode 100644 index 00000000..2251f673 --- /dev/null +++ b/docs/storage-report/ECDSAServiceManagerBase.md @@ -0,0 +1,19 @@ + +╭------------------+-------------+------+--------+-------+-------------------------------------------------------------------╮ +| Name | Type | Slot | Offset | Bytes | Contract | ++============================================================================================================================+ +| _initialized | uint8 | 0 | 0 | 1 | src/unaudited/ECDSAServiceManagerBase.sol:ECDSAServiceManagerBase | +|------------------+-------------+------+--------+-------+-------------------------------------------------------------------| +| _initializing | bool | 0 | 1 | 1 | src/unaudited/ECDSAServiceManagerBase.sol:ECDSAServiceManagerBase | +|------------------+-------------+------+--------+-------+-------------------------------------------------------------------| +| __gap | uint256[50] | 1 | 0 | 1600 | src/unaudited/ECDSAServiceManagerBase.sol:ECDSAServiceManagerBase | +|------------------+-------------+------+--------+-------+-------------------------------------------------------------------| +| _owner | address | 51 | 0 | 20 | src/unaudited/ECDSAServiceManagerBase.sol:ECDSAServiceManagerBase | +|------------------+-------------+------+--------+-------+-------------------------------------------------------------------| +| __gap | uint256[49] | 52 | 0 | 1568 | src/unaudited/ECDSAServiceManagerBase.sol:ECDSAServiceManagerBase | +|------------------+-------------+------+--------+-------+-------------------------------------------------------------------| +| rewardsInitiator | address | 101 | 0 | 20 | src/unaudited/ECDSAServiceManagerBase.sol:ECDSAServiceManagerBase | +|------------------+-------------+------+--------+-------+-------------------------------------------------------------------| +| __GAP | uint256[49] | 102 | 0 | 1568 | src/unaudited/ECDSAServiceManagerBase.sol:ECDSAServiceManagerBase | +╰------------------+-------------+------+--------+-------+-------------------------------------------------------------------╯ + diff --git a/docs/storage-report/ECDSAStakeRegistry.md b/docs/storage-report/ECDSAStakeRegistry.md new file mode 100644 index 00000000..df768c27 --- /dev/null +++ b/docs/storage-report/ECDSAStakeRegistry.md @@ -0,0 +1,37 @@ + +╭----------------------------+-----------------------------------------------------------+------+--------+-------+---------------------------------------------------------╮ +| Name | Type | Slot | Offset | Bytes | Contract | ++==========================================================================================================================================================================+ +| _initialized | uint8 | 0 | 0 | 1 | src/unaudited/ECDSAStakeRegistry.sol:ECDSAStakeRegistry | +|----------------------------+-----------------------------------------------------------+------+--------+-------+---------------------------------------------------------| +| _initializing | bool | 0 | 1 | 1 | src/unaudited/ECDSAStakeRegistry.sol:ECDSAStakeRegistry | +|----------------------------+-----------------------------------------------------------+------+--------+-------+---------------------------------------------------------| +| __gap | uint256[50] | 1 | 0 | 1600 | src/unaudited/ECDSAStakeRegistry.sol:ECDSAStakeRegistry | +|----------------------------+-----------------------------------------------------------+------+--------+-------+---------------------------------------------------------| +| _owner | address | 51 | 0 | 20 | src/unaudited/ECDSAStakeRegistry.sol:ECDSAStakeRegistry | +|----------------------------+-----------------------------------------------------------+------+--------+-------+---------------------------------------------------------| +| __gap | uint256[49] | 52 | 0 | 1568 | src/unaudited/ECDSAStakeRegistry.sol:ECDSAStakeRegistry | +|----------------------------+-----------------------------------------------------------+------+--------+-------+---------------------------------------------------------| +| _totalOperators | uint256 | 101 | 0 | 32 | src/unaudited/ECDSAStakeRegistry.sol:ECDSAStakeRegistry | +|----------------------------+-----------------------------------------------------------+------+--------+-------+---------------------------------------------------------| +| _quorum | struct Quorum | 102 | 0 | 32 | src/unaudited/ECDSAStakeRegistry.sol:ECDSAStakeRegistry | +|----------------------------+-----------------------------------------------------------+------+--------+-------+---------------------------------------------------------| +| _minimumWeight | uint256 | 103 | 0 | 32 | src/unaudited/ECDSAStakeRegistry.sol:ECDSAStakeRegistry | +|----------------------------+-----------------------------------------------------------+------+--------+-------+---------------------------------------------------------| +| _serviceManager | address | 104 | 0 | 20 | src/unaudited/ECDSAStakeRegistry.sol:ECDSAStakeRegistry | +|----------------------------+-----------------------------------------------------------+------+--------+-------+---------------------------------------------------------| +| _stakeExpiry | uint256 | 105 | 0 | 32 | src/unaudited/ECDSAStakeRegistry.sol:ECDSAStakeRegistry | +|----------------------------+-----------------------------------------------------------+------+--------+-------+---------------------------------------------------------| +| _operatorSigningKeyHistory | mapping(address => struct CheckpointsUpgradeable.History) | 106 | 0 | 32 | src/unaudited/ECDSAStakeRegistry.sol:ECDSAStakeRegistry | +|----------------------------+-----------------------------------------------------------+------+--------+-------+---------------------------------------------------------| +| _totalWeightHistory | struct CheckpointsUpgradeable.History | 107 | 0 | 32 | src/unaudited/ECDSAStakeRegistry.sol:ECDSAStakeRegistry | +|----------------------------+-----------------------------------------------------------+------+--------+-------+---------------------------------------------------------| +| _thresholdWeightHistory | struct CheckpointsUpgradeable.History | 108 | 0 | 32 | src/unaudited/ECDSAStakeRegistry.sol:ECDSAStakeRegistry | +|----------------------------+-----------------------------------------------------------+------+--------+-------+---------------------------------------------------------| +| _operatorWeightHistory | mapping(address => struct CheckpointsUpgradeable.History) | 109 | 0 | 32 | src/unaudited/ECDSAStakeRegistry.sol:ECDSAStakeRegistry | +|----------------------------+-----------------------------------------------------------+------+--------+-------+---------------------------------------------------------| +| _operatorRegistered | mapping(address => bool) | 110 | 0 | 32 | src/unaudited/ECDSAStakeRegistry.sol:ECDSAStakeRegistry | +|----------------------------+-----------------------------------------------------------+------+--------+-------+---------------------------------------------------------| +| __gap | uint256[40] | 111 | 0 | 1280 | src/unaudited/ECDSAStakeRegistry.sol:ECDSAStakeRegistry | +╰----------------------------+-----------------------------------------------------------+------+--------+-------+---------------------------------------------------------╯ + diff --git a/docs/storage-report/ECDSAStakeRegistryEqualWeight.md b/docs/storage-report/ECDSAStakeRegistryEqualWeight.md new file mode 100644 index 00000000..e1e1c166 --- /dev/null +++ b/docs/storage-report/ECDSAStakeRegistryEqualWeight.md @@ -0,0 +1,39 @@ + +╭----------------------------+-----------------------------------------------------------+------+--------+-------+----------------------------------------------------------------------------------------╮ +| Name | Type | Slot | Offset | Bytes | Contract | ++=========================================================================================================================================================================================================+ +| _initialized | uint8 | 0 | 0 | 1 | src/unaudited/examples/ECDSAStakeRegistryEqualWeight.sol:ECDSAStakeRegistryEqualWeight | +|----------------------------+-----------------------------------------------------------+------+--------+-------+----------------------------------------------------------------------------------------| +| _initializing | bool | 0 | 1 | 1 | src/unaudited/examples/ECDSAStakeRegistryEqualWeight.sol:ECDSAStakeRegistryEqualWeight | +|----------------------------+-----------------------------------------------------------+------+--------+-------+----------------------------------------------------------------------------------------| +| __gap | uint256[50] | 1 | 0 | 1600 | src/unaudited/examples/ECDSAStakeRegistryEqualWeight.sol:ECDSAStakeRegistryEqualWeight | +|----------------------------+-----------------------------------------------------------+------+--------+-------+----------------------------------------------------------------------------------------| +| _owner | address | 51 | 0 | 20 | src/unaudited/examples/ECDSAStakeRegistryEqualWeight.sol:ECDSAStakeRegistryEqualWeight | +|----------------------------+-----------------------------------------------------------+------+--------+-------+----------------------------------------------------------------------------------------| +| __gap | uint256[49] | 52 | 0 | 1568 | src/unaudited/examples/ECDSAStakeRegistryEqualWeight.sol:ECDSAStakeRegistryEqualWeight | +|----------------------------+-----------------------------------------------------------+------+--------+-------+----------------------------------------------------------------------------------------| +| _totalOperators | uint256 | 101 | 0 | 32 | src/unaudited/examples/ECDSAStakeRegistryEqualWeight.sol:ECDSAStakeRegistryEqualWeight | +|----------------------------+-----------------------------------------------------------+------+--------+-------+----------------------------------------------------------------------------------------| +| _quorum | struct Quorum | 102 | 0 | 32 | src/unaudited/examples/ECDSAStakeRegistryEqualWeight.sol:ECDSAStakeRegistryEqualWeight | +|----------------------------+-----------------------------------------------------------+------+--------+-------+----------------------------------------------------------------------------------------| +| _minimumWeight | uint256 | 103 | 0 | 32 | src/unaudited/examples/ECDSAStakeRegistryEqualWeight.sol:ECDSAStakeRegistryEqualWeight | +|----------------------------+-----------------------------------------------------------+------+--------+-------+----------------------------------------------------------------------------------------| +| _serviceManager | address | 104 | 0 | 20 | src/unaudited/examples/ECDSAStakeRegistryEqualWeight.sol:ECDSAStakeRegistryEqualWeight | +|----------------------------+-----------------------------------------------------------+------+--------+-------+----------------------------------------------------------------------------------------| +| _stakeExpiry | uint256 | 105 | 0 | 32 | src/unaudited/examples/ECDSAStakeRegistryEqualWeight.sol:ECDSAStakeRegistryEqualWeight | +|----------------------------+-----------------------------------------------------------+------+--------+-------+----------------------------------------------------------------------------------------| +| _operatorSigningKeyHistory | mapping(address => struct CheckpointsUpgradeable.History) | 106 | 0 | 32 | src/unaudited/examples/ECDSAStakeRegistryEqualWeight.sol:ECDSAStakeRegistryEqualWeight | +|----------------------------+-----------------------------------------------------------+------+--------+-------+----------------------------------------------------------------------------------------| +| _totalWeightHistory | struct CheckpointsUpgradeable.History | 107 | 0 | 32 | src/unaudited/examples/ECDSAStakeRegistryEqualWeight.sol:ECDSAStakeRegistryEqualWeight | +|----------------------------+-----------------------------------------------------------+------+--------+-------+----------------------------------------------------------------------------------------| +| _thresholdWeightHistory | struct CheckpointsUpgradeable.History | 108 | 0 | 32 | src/unaudited/examples/ECDSAStakeRegistryEqualWeight.sol:ECDSAStakeRegistryEqualWeight | +|----------------------------+-----------------------------------------------------------+------+--------+-------+----------------------------------------------------------------------------------------| +| _operatorWeightHistory | mapping(address => struct CheckpointsUpgradeable.History) | 109 | 0 | 32 | src/unaudited/examples/ECDSAStakeRegistryEqualWeight.sol:ECDSAStakeRegistryEqualWeight | +|----------------------------+-----------------------------------------------------------+------+--------+-------+----------------------------------------------------------------------------------------| +| _operatorRegistered | mapping(address => bool) | 110 | 0 | 32 | src/unaudited/examples/ECDSAStakeRegistryEqualWeight.sol:ECDSAStakeRegistryEqualWeight | +|----------------------------+-----------------------------------------------------------+------+--------+-------+----------------------------------------------------------------------------------------| +| __gap | uint256[40] | 111 | 0 | 1280 | src/unaudited/examples/ECDSAStakeRegistryEqualWeight.sol:ECDSAStakeRegistryEqualWeight | +|----------------------------+-----------------------------------------------------------+------+--------+-------+----------------------------------------------------------------------------------------| +| allowlistedOperators | mapping(address => bool) | 151 | 0 | 32 | src/unaudited/examples/ECDSAStakeRegistryEqualWeight.sol:ECDSAStakeRegistryEqualWeight | +╰----------------------------+-----------------------------------------------------------+------+--------+-------+----------------------------------------------------------------------------------------╯ + diff --git a/docs/storage-report/ECDSAStakeRegistryPermissioned.md b/docs/storage-report/ECDSAStakeRegistryPermissioned.md new file mode 100644 index 00000000..bdc5b32e --- /dev/null +++ b/docs/storage-report/ECDSAStakeRegistryPermissioned.md @@ -0,0 +1,39 @@ + +╭----------------------------+-----------------------------------------------------------+------+--------+-------+------------------------------------------------------------------------------------------╮ +| Name | Type | Slot | Offset | Bytes | Contract | ++===========================================================================================================================================================================================================+ +| _initialized | uint8 | 0 | 0 | 1 | src/unaudited/examples/ECDSAStakeRegistryPermissioned.sol:ECDSAStakeRegistryPermissioned | +|----------------------------+-----------------------------------------------------------+------+--------+-------+------------------------------------------------------------------------------------------| +| _initializing | bool | 0 | 1 | 1 | src/unaudited/examples/ECDSAStakeRegistryPermissioned.sol:ECDSAStakeRegistryPermissioned | +|----------------------------+-----------------------------------------------------------+------+--------+-------+------------------------------------------------------------------------------------------| +| __gap | uint256[50] | 1 | 0 | 1600 | src/unaudited/examples/ECDSAStakeRegistryPermissioned.sol:ECDSAStakeRegistryPermissioned | +|----------------------------+-----------------------------------------------------------+------+--------+-------+------------------------------------------------------------------------------------------| +| _owner | address | 51 | 0 | 20 | src/unaudited/examples/ECDSAStakeRegistryPermissioned.sol:ECDSAStakeRegistryPermissioned | +|----------------------------+-----------------------------------------------------------+------+--------+-------+------------------------------------------------------------------------------------------| +| __gap | uint256[49] | 52 | 0 | 1568 | src/unaudited/examples/ECDSAStakeRegistryPermissioned.sol:ECDSAStakeRegistryPermissioned | +|----------------------------+-----------------------------------------------------------+------+--------+-------+------------------------------------------------------------------------------------------| +| _totalOperators | uint256 | 101 | 0 | 32 | src/unaudited/examples/ECDSAStakeRegistryPermissioned.sol:ECDSAStakeRegistryPermissioned | +|----------------------------+-----------------------------------------------------------+------+--------+-------+------------------------------------------------------------------------------------------| +| _quorum | struct Quorum | 102 | 0 | 32 | src/unaudited/examples/ECDSAStakeRegistryPermissioned.sol:ECDSAStakeRegistryPermissioned | +|----------------------------+-----------------------------------------------------------+------+--------+-------+------------------------------------------------------------------------------------------| +| _minimumWeight | uint256 | 103 | 0 | 32 | src/unaudited/examples/ECDSAStakeRegistryPermissioned.sol:ECDSAStakeRegistryPermissioned | +|----------------------------+-----------------------------------------------------------+------+--------+-------+------------------------------------------------------------------------------------------| +| _serviceManager | address | 104 | 0 | 20 | src/unaudited/examples/ECDSAStakeRegistryPermissioned.sol:ECDSAStakeRegistryPermissioned | +|----------------------------+-----------------------------------------------------------+------+--------+-------+------------------------------------------------------------------------------------------| +| _stakeExpiry | uint256 | 105 | 0 | 32 | src/unaudited/examples/ECDSAStakeRegistryPermissioned.sol:ECDSAStakeRegistryPermissioned | +|----------------------------+-----------------------------------------------------------+------+--------+-------+------------------------------------------------------------------------------------------| +| _operatorSigningKeyHistory | mapping(address => struct CheckpointsUpgradeable.History) | 106 | 0 | 32 | src/unaudited/examples/ECDSAStakeRegistryPermissioned.sol:ECDSAStakeRegistryPermissioned | +|----------------------------+-----------------------------------------------------------+------+--------+-------+------------------------------------------------------------------------------------------| +| _totalWeightHistory | struct CheckpointsUpgradeable.History | 107 | 0 | 32 | src/unaudited/examples/ECDSAStakeRegistryPermissioned.sol:ECDSAStakeRegistryPermissioned | +|----------------------------+-----------------------------------------------------------+------+--------+-------+------------------------------------------------------------------------------------------| +| _thresholdWeightHistory | struct CheckpointsUpgradeable.History | 108 | 0 | 32 | src/unaudited/examples/ECDSAStakeRegistryPermissioned.sol:ECDSAStakeRegistryPermissioned | +|----------------------------+-----------------------------------------------------------+------+--------+-------+------------------------------------------------------------------------------------------| +| _operatorWeightHistory | mapping(address => struct CheckpointsUpgradeable.History) | 109 | 0 | 32 | src/unaudited/examples/ECDSAStakeRegistryPermissioned.sol:ECDSAStakeRegistryPermissioned | +|----------------------------+-----------------------------------------------------------+------+--------+-------+------------------------------------------------------------------------------------------| +| _operatorRegistered | mapping(address => bool) | 110 | 0 | 32 | src/unaudited/examples/ECDSAStakeRegistryPermissioned.sol:ECDSAStakeRegistryPermissioned | +|----------------------------+-----------------------------------------------------------+------+--------+-------+------------------------------------------------------------------------------------------| +| __gap | uint256[40] | 111 | 0 | 1280 | src/unaudited/examples/ECDSAStakeRegistryPermissioned.sol:ECDSAStakeRegistryPermissioned | +|----------------------------+-----------------------------------------------------------+------+--------+-------+------------------------------------------------------------------------------------------| +| allowlistedOperators | mapping(address => bool) | 151 | 0 | 32 | src/unaudited/examples/ECDSAStakeRegistryPermissioned.sol:ECDSAStakeRegistryPermissioned | +╰----------------------------+-----------------------------------------------------------+------+--------+-------+------------------------------------------------------------------------------------------╯ + diff --git a/docs/storage-report/ECDSAStakeRegistryStorage.md b/docs/storage-report/ECDSAStakeRegistryStorage.md new file mode 100644 index 00000000..69ef3d05 --- /dev/null +++ b/docs/storage-report/ECDSAStakeRegistryStorage.md @@ -0,0 +1,27 @@ + +╭----------------------------+-----------------------------------------------------------+------+--------+-------+-----------------------------------------------------------------------╮ +| Name | Type | Slot | Offset | Bytes | Contract | ++========================================================================================================================================================================================+ +| _totalOperators | uint256 | 0 | 0 | 32 | src/unaudited/ECDSAStakeRegistryStorage.sol:ECDSAStakeRegistryStorage | +|----------------------------+-----------------------------------------------------------+------+--------+-------+-----------------------------------------------------------------------| +| _quorum | struct Quorum | 1 | 0 | 32 | src/unaudited/ECDSAStakeRegistryStorage.sol:ECDSAStakeRegistryStorage | +|----------------------------+-----------------------------------------------------------+------+--------+-------+-----------------------------------------------------------------------| +| _minimumWeight | uint256 | 2 | 0 | 32 | src/unaudited/ECDSAStakeRegistryStorage.sol:ECDSAStakeRegistryStorage | +|----------------------------+-----------------------------------------------------------+------+--------+-------+-----------------------------------------------------------------------| +| _serviceManager | address | 3 | 0 | 20 | src/unaudited/ECDSAStakeRegistryStorage.sol:ECDSAStakeRegistryStorage | +|----------------------------+-----------------------------------------------------------+------+--------+-------+-----------------------------------------------------------------------| +| _stakeExpiry | uint256 | 4 | 0 | 32 | src/unaudited/ECDSAStakeRegistryStorage.sol:ECDSAStakeRegistryStorage | +|----------------------------+-----------------------------------------------------------+------+--------+-------+-----------------------------------------------------------------------| +| _operatorSigningKeyHistory | mapping(address => struct CheckpointsUpgradeable.History) | 5 | 0 | 32 | src/unaudited/ECDSAStakeRegistryStorage.sol:ECDSAStakeRegistryStorage | +|----------------------------+-----------------------------------------------------------+------+--------+-------+-----------------------------------------------------------------------| +| _totalWeightHistory | struct CheckpointsUpgradeable.History | 6 | 0 | 32 | src/unaudited/ECDSAStakeRegistryStorage.sol:ECDSAStakeRegistryStorage | +|----------------------------+-----------------------------------------------------------+------+--------+-------+-----------------------------------------------------------------------| +| _thresholdWeightHistory | struct CheckpointsUpgradeable.History | 7 | 0 | 32 | src/unaudited/ECDSAStakeRegistryStorage.sol:ECDSAStakeRegistryStorage | +|----------------------------+-----------------------------------------------------------+------+--------+-------+-----------------------------------------------------------------------| +| _operatorWeightHistory | mapping(address => struct CheckpointsUpgradeable.History) | 8 | 0 | 32 | src/unaudited/ECDSAStakeRegistryStorage.sol:ECDSAStakeRegistryStorage | +|----------------------------+-----------------------------------------------------------+------+--------+-------+-----------------------------------------------------------------------| +| _operatorRegistered | mapping(address => bool) | 9 | 0 | 32 | src/unaudited/ECDSAStakeRegistryStorage.sol:ECDSAStakeRegistryStorage | +|----------------------------+-----------------------------------------------------------+------+--------+-------+-----------------------------------------------------------------------| +| __gap | uint256[40] | 10 | 0 | 1280 | src/unaudited/ECDSAStakeRegistryStorage.sol:ECDSAStakeRegistryStorage | +╰----------------------------+-----------------------------------------------------------+------+--------+-------+-----------------------------------------------------------------------╯ + diff --git a/docs/storage-report/EjectionManager.md b/docs/storage-report/EjectionManager.md new file mode 100644 index 00000000..55e2a2df --- /dev/null +++ b/docs/storage-report/EjectionManager.md @@ -0,0 +1,21 @@ + +╭-----------------------+----------------------------------------------------------------+------+--------+-------+-----------------------------------------╮ +| Name | Type | Slot | Offset | Bytes | Contract | ++==========================================================================================================================================================+ +| _initialized | uint8 | 0 | 0 | 1 | src/EjectionManager.sol:EjectionManager | +|-----------------------+----------------------------------------------------------------+------+--------+-------+-----------------------------------------| +| _initializing | bool | 0 | 1 | 1 | src/EjectionManager.sol:EjectionManager | +|-----------------------+----------------------------------------------------------------+------+--------+-------+-----------------------------------------| +| __gap | uint256[50] | 1 | 0 | 1600 | src/EjectionManager.sol:EjectionManager | +|-----------------------+----------------------------------------------------------------+------+--------+-------+-----------------------------------------| +| _owner | address | 51 | 0 | 20 | src/EjectionManager.sol:EjectionManager | +|-----------------------+----------------------------------------------------------------+------+--------+-------+-----------------------------------------| +| __gap | uint256[49] | 52 | 0 | 1568 | src/EjectionManager.sol:EjectionManager | +|-----------------------+----------------------------------------------------------------+------+--------+-------+-----------------------------------------| +| isEjector | mapping(address => bool) | 101 | 0 | 32 | src/EjectionManager.sol:EjectionManager | +|-----------------------+----------------------------------------------------------------+------+--------+-------+-----------------------------------------| +| stakeEjectedForQuorum | mapping(uint8 => struct IEjectionManager.StakeEjection[]) | 102 | 0 | 32 | src/EjectionManager.sol:EjectionManager | +|-----------------------+----------------------------------------------------------------+------+--------+-------+-----------------------------------------| +| quorumEjectionParams | mapping(uint8 => struct IEjectionManager.QuorumEjectionParams) | 103 | 0 | 32 | src/EjectionManager.sol:EjectionManager | +╰-----------------------+----------------------------------------------------------------+------+--------+-------+-----------------------------------------╯ + diff --git a/docs/storage-report/IndexRegistry.md b/docs/storage-report/IndexRegistry.md new file mode 100644 index 00000000..3415bdcd --- /dev/null +++ b/docs/storage-report/IndexRegistry.md @@ -0,0 +1,17 @@ + +╭-----------------------+-----------------------------------------------------------------------------+------+--------+-------+-------------------------------------╮ +| Name | Type | Slot | Offset | Bytes | Contract | ++===================================================================================================================================================================+ +| _initialized | uint8 | 0 | 0 | 1 | src/IndexRegistry.sol:IndexRegistry | +|-----------------------+-----------------------------------------------------------------------------+------+--------+-------+-------------------------------------| +| _initializing | bool | 0 | 1 | 1 | src/IndexRegistry.sol:IndexRegistry | +|-----------------------+-----------------------------------------------------------------------------+------+--------+-------+-------------------------------------| +| currentOperatorIndex | mapping(uint8 => mapping(bytes32 => uint32)) | 1 | 0 | 32 | src/IndexRegistry.sol:IndexRegistry | +|-----------------------+-----------------------------------------------------------------------------+------+--------+-------+-------------------------------------| +| _operatorIndexHistory | mapping(uint8 => mapping(uint32 => struct IIndexRegistry.OperatorUpdate[])) | 2 | 0 | 32 | src/IndexRegistry.sol:IndexRegistry | +|-----------------------+-----------------------------------------------------------------------------+------+--------+-------+-------------------------------------| +| _operatorCountHistory | mapping(uint8 => struct IIndexRegistry.QuorumUpdate[]) | 3 | 0 | 32 | src/IndexRegistry.sol:IndexRegistry | +|-----------------------+-----------------------------------------------------------------------------+------+--------+-------+-------------------------------------| +| __GAP | uint256[47] | 4 | 0 | 1504 | src/IndexRegistry.sol:IndexRegistry | +╰-----------------------+-----------------------------------------------------------------------------+------+--------+-------+-------------------------------------╯ + diff --git a/docs/storage-report/IndexRegistryStorage.md b/docs/storage-report/IndexRegistryStorage.md new file mode 100644 index 00000000..9581f514 --- /dev/null +++ b/docs/storage-report/IndexRegistryStorage.md @@ -0,0 +1,17 @@ + +╭-----------------------+-----------------------------------------------------------------------------+------+--------+-------+---------------------------------------------------╮ +| Name | Type | Slot | Offset | Bytes | Contract | ++=================================================================================================================================================================================+ +| _initialized | uint8 | 0 | 0 | 1 | src/IndexRegistryStorage.sol:IndexRegistryStorage | +|-----------------------+-----------------------------------------------------------------------------+------+--------+-------+---------------------------------------------------| +| _initializing | bool | 0 | 1 | 1 | src/IndexRegistryStorage.sol:IndexRegistryStorage | +|-----------------------+-----------------------------------------------------------------------------+------+--------+-------+---------------------------------------------------| +| currentOperatorIndex | mapping(uint8 => mapping(bytes32 => uint32)) | 1 | 0 | 32 | src/IndexRegistryStorage.sol:IndexRegistryStorage | +|-----------------------+-----------------------------------------------------------------------------+------+--------+-------+---------------------------------------------------| +| _operatorIndexHistory | mapping(uint8 => mapping(uint32 => struct IIndexRegistry.OperatorUpdate[])) | 2 | 0 | 32 | src/IndexRegistryStorage.sol:IndexRegistryStorage | +|-----------------------+-----------------------------------------------------------------------------+------+--------+-------+---------------------------------------------------| +| _operatorCountHistory | mapping(uint8 => struct IIndexRegistry.QuorumUpdate[]) | 3 | 0 | 32 | src/IndexRegistryStorage.sol:IndexRegistryStorage | +|-----------------------+-----------------------------------------------------------------------------+------+--------+-------+---------------------------------------------------| +| __GAP | uint256[47] | 4 | 0 | 1504 | src/IndexRegistryStorage.sol:IndexRegistryStorage | +╰-----------------------+-----------------------------------------------------------------------------+------+--------+-------+---------------------------------------------------╯ + diff --git a/docs/storage-report/InstantSlasher.md b/docs/storage-report/InstantSlasher.md new file mode 100644 index 00000000..1eafc2b8 --- /dev/null +++ b/docs/storage-report/InstantSlasher.md @@ -0,0 +1,17 @@ + +╭----------------+-------------+------+--------+-------+------------------------------------------------╮ +| Name | Type | Slot | Offset | Bytes | Contract | ++=======================================================================================================+ +| _initialized | uint8 | 0 | 0 | 1 | src/slashers/InstantSlasher.sol:InstantSlasher | +|----------------+-------------+------+--------+-------+------------------------------------------------| +| _initializing | bool | 0 | 1 | 1 | src/slashers/InstantSlasher.sol:InstantSlasher | +|----------------+-------------+------+--------+-------+------------------------------------------------| +| serviceManager | address | 0 | 2 | 20 | src/slashers/InstantSlasher.sol:InstantSlasher | +|----------------+-------------+------+--------+-------+------------------------------------------------| +| slasher | address | 1 | 0 | 20 | src/slashers/InstantSlasher.sol:InstantSlasher | +|----------------+-------------+------+--------+-------+------------------------------------------------| +| nextRequestId | uint256 | 2 | 0 | 32 | src/slashers/InstantSlasher.sol:InstantSlasher | +|----------------+-------------+------+--------+-------+------------------------------------------------| +| __gap | uint256[47] | 3 | 0 | 1504 | src/slashers/InstantSlasher.sol:InstantSlasher | +╰----------------+-------------+------+--------+-------+------------------------------------------------╯ + diff --git a/docs/storage-report/OperatorStateRetriever.md b/docs/storage-report/OperatorStateRetriever.md new file mode 100644 index 00000000..1ec5dc07 --- /dev/null +++ b/docs/storage-report/OperatorStateRetriever.md @@ -0,0 +1,6 @@ + +╭------+------+------+--------+-------+----------╮ +| Name | Type | Slot | Offset | Bytes | Contract | ++================================================+ +╰------+------+------+--------+-------+----------╯ + diff --git a/docs/storage-report/RegistryCoordinator.md b/docs/storage-report/RegistryCoordinator.md new file mode 100644 index 00000000..1adff5f4 --- /dev/null +++ b/docs/storage-report/RegistryCoordinator.md @@ -0,0 +1,49 @@ + +╭-----------------------------+----------------------------------------------------------------------+------+--------+-------+-------------------------------------------------╮ +| Name | Type | Slot | Offset | Bytes | Contract | ++==============================================================================================================================================================================+ +| _initialized | uint8 | 0 | 0 | 1 | src/RegistryCoordinator.sol:RegistryCoordinator | +|-----------------------------+----------------------------------------------------------------------+------+--------+-------+-------------------------------------------------| +| _initializing | bool | 0 | 1 | 1 | src/RegistryCoordinator.sol:RegistryCoordinator | +|-----------------------------+----------------------------------------------------------------------+------+--------+-------+-------------------------------------------------| +| __deprecated_pauserRegistry | contract IPauserRegistry | 0 | 2 | 20 | src/RegistryCoordinator.sol:RegistryCoordinator | +|-----------------------------+----------------------------------------------------------------------+------+--------+-------+-------------------------------------------------| +| _paused | uint256 | 1 | 0 | 32 | src/RegistryCoordinator.sol:RegistryCoordinator | +|-----------------------------+----------------------------------------------------------------------+------+--------+-------+-------------------------------------------------| +| __gap | uint256[48] | 2 | 0 | 1536 | src/RegistryCoordinator.sol:RegistryCoordinator | +|-----------------------------+----------------------------------------------------------------------+------+--------+-------+-------------------------------------------------| +| __gap | uint256[50] | 50 | 0 | 1600 | src/RegistryCoordinator.sol:RegistryCoordinator | +|-----------------------------+----------------------------------------------------------------------+------+--------+-------+-------------------------------------------------| +| _owner | address | 100 | 0 | 20 | src/RegistryCoordinator.sol:RegistryCoordinator | +|-----------------------------+----------------------------------------------------------------------+------+--------+-------+-------------------------------------------------| +| __gap | uint256[49] | 101 | 0 | 1568 | src/RegistryCoordinator.sol:RegistryCoordinator | +|-----------------------------+----------------------------------------------------------------------+------+--------+-------+-------------------------------------------------| +| quorumCount | uint8 | 150 | 0 | 1 | src/RegistryCoordinator.sol:RegistryCoordinator | +|-----------------------------+----------------------------------------------------------------------+------+--------+-------+-------------------------------------------------| +| _quorumParams | mapping(uint8 => struct IRegistryCoordinator.OperatorSetParam) | 151 | 0 | 32 | src/RegistryCoordinator.sol:RegistryCoordinator | +|-----------------------------+----------------------------------------------------------------------+------+--------+-------+-------------------------------------------------| +| _operatorBitmapHistory | mapping(bytes32 => struct IRegistryCoordinator.QuorumBitmapUpdate[]) | 152 | 0 | 32 | src/RegistryCoordinator.sol:RegistryCoordinator | +|-----------------------------+----------------------------------------------------------------------+------+--------+-------+-------------------------------------------------| +| _operatorInfo | mapping(address => struct IRegistryCoordinator.OperatorInfo) | 153 | 0 | 32 | src/RegistryCoordinator.sol:RegistryCoordinator | +|-----------------------------+----------------------------------------------------------------------+------+--------+-------+-------------------------------------------------| +| isChurnApproverSaltUsed | mapping(bytes32 => bool) | 154 | 0 | 32 | src/RegistryCoordinator.sol:RegistryCoordinator | +|-----------------------------+----------------------------------------------------------------------+------+--------+-------+-------------------------------------------------| +| quorumUpdateBlockNumber | mapping(uint8 => uint256) | 155 | 0 | 32 | src/RegistryCoordinator.sol:RegistryCoordinator | +|-----------------------------+----------------------------------------------------------------------+------+--------+-------+-------------------------------------------------| +| registries | address[] | 156 | 0 | 32 | src/RegistryCoordinator.sol:RegistryCoordinator | +|-----------------------------+----------------------------------------------------------------------+------+--------+-------+-------------------------------------------------| +| churnApprover | address | 157 | 0 | 20 | src/RegistryCoordinator.sol:RegistryCoordinator | +|-----------------------------+----------------------------------------------------------------------+------+--------+-------+-------------------------------------------------| +| ejector | address | 158 | 0 | 20 | src/RegistryCoordinator.sol:RegistryCoordinator | +|-----------------------------+----------------------------------------------------------------------+------+--------+-------+-------------------------------------------------| +| lastEjectionTimestamp | mapping(address => uint256) | 159 | 0 | 32 | src/RegistryCoordinator.sol:RegistryCoordinator | +|-----------------------------+----------------------------------------------------------------------+------+--------+-------+-------------------------------------------------| +| ejectionCooldown | uint256 | 160 | 0 | 32 | src/RegistryCoordinator.sol:RegistryCoordinator | +|-----------------------------+----------------------------------------------------------------------+------+--------+-------+-------------------------------------------------| +| isOperatorSetAVS | bool | 161 | 0 | 1 | src/RegistryCoordinator.sol:RegistryCoordinator | +|-----------------------------+----------------------------------------------------------------------+------+--------+-------+-------------------------------------------------| +| isM2Quorum | mapping(uint8 => bool) | 162 | 0 | 32 | src/RegistryCoordinator.sol:RegistryCoordinator | +|-----------------------------+----------------------------------------------------------------------+------+--------+-------+-------------------------------------------------| +| __GAP | uint256[37] | 163 | 0 | 1184 | src/RegistryCoordinator.sol:RegistryCoordinator | +╰-----------------------------+----------------------------------------------------------------------+------+--------+-------+-------------------------------------------------╯ + diff --git a/docs/storage-report/RegistryCoordinatorStorage.md b/docs/storage-report/RegistryCoordinatorStorage.md new file mode 100644 index 00000000..1e9fc25d --- /dev/null +++ b/docs/storage-report/RegistryCoordinatorStorage.md @@ -0,0 +1,33 @@ + +╭-------------------------+----------------------------------------------------------------------+------+--------+-------+---------------------------------------------------------------╮ +| Name | Type | Slot | Offset | Bytes | Contract | ++========================================================================================================================================================================================+ +| quorumCount | uint8 | 0 | 0 | 1 | src/RegistryCoordinatorStorage.sol:RegistryCoordinatorStorage | +|-------------------------+----------------------------------------------------------------------+------+--------+-------+---------------------------------------------------------------| +| _quorumParams | mapping(uint8 => struct IRegistryCoordinator.OperatorSetParam) | 1 | 0 | 32 | src/RegistryCoordinatorStorage.sol:RegistryCoordinatorStorage | +|-------------------------+----------------------------------------------------------------------+------+--------+-------+---------------------------------------------------------------| +| _operatorBitmapHistory | mapping(bytes32 => struct IRegistryCoordinator.QuorumBitmapUpdate[]) | 2 | 0 | 32 | src/RegistryCoordinatorStorage.sol:RegistryCoordinatorStorage | +|-------------------------+----------------------------------------------------------------------+------+--------+-------+---------------------------------------------------------------| +| _operatorInfo | mapping(address => struct IRegistryCoordinator.OperatorInfo) | 3 | 0 | 32 | src/RegistryCoordinatorStorage.sol:RegistryCoordinatorStorage | +|-------------------------+----------------------------------------------------------------------+------+--------+-------+---------------------------------------------------------------| +| isChurnApproverSaltUsed | mapping(bytes32 => bool) | 4 | 0 | 32 | src/RegistryCoordinatorStorage.sol:RegistryCoordinatorStorage | +|-------------------------+----------------------------------------------------------------------+------+--------+-------+---------------------------------------------------------------| +| quorumUpdateBlockNumber | mapping(uint8 => uint256) | 5 | 0 | 32 | src/RegistryCoordinatorStorage.sol:RegistryCoordinatorStorage | +|-------------------------+----------------------------------------------------------------------+------+--------+-------+---------------------------------------------------------------| +| registries | address[] | 6 | 0 | 32 | src/RegistryCoordinatorStorage.sol:RegistryCoordinatorStorage | +|-------------------------+----------------------------------------------------------------------+------+--------+-------+---------------------------------------------------------------| +| churnApprover | address | 7 | 0 | 20 | src/RegistryCoordinatorStorage.sol:RegistryCoordinatorStorage | +|-------------------------+----------------------------------------------------------------------+------+--------+-------+---------------------------------------------------------------| +| ejector | address | 8 | 0 | 20 | src/RegistryCoordinatorStorage.sol:RegistryCoordinatorStorage | +|-------------------------+----------------------------------------------------------------------+------+--------+-------+---------------------------------------------------------------| +| lastEjectionTimestamp | mapping(address => uint256) | 9 | 0 | 32 | src/RegistryCoordinatorStorage.sol:RegistryCoordinatorStorage | +|-------------------------+----------------------------------------------------------------------+------+--------+-------+---------------------------------------------------------------| +| ejectionCooldown | uint256 | 10 | 0 | 32 | src/RegistryCoordinatorStorage.sol:RegistryCoordinatorStorage | +|-------------------------+----------------------------------------------------------------------+------+--------+-------+---------------------------------------------------------------| +| isOperatorSetAVS | bool | 11 | 0 | 1 | src/RegistryCoordinatorStorage.sol:RegistryCoordinatorStorage | +|-------------------------+----------------------------------------------------------------------+------+--------+-------+---------------------------------------------------------------| +| isM2Quorum | mapping(uint8 => bool) | 12 | 0 | 32 | src/RegistryCoordinatorStorage.sol:RegistryCoordinatorStorage | +|-------------------------+----------------------------------------------------------------------+------+--------+-------+---------------------------------------------------------------| +| __GAP | uint256[37] | 13 | 0 | 1184 | src/RegistryCoordinatorStorage.sol:RegistryCoordinatorStorage | +╰-------------------------+----------------------------------------------------------------------+------+--------+-------+---------------------------------------------------------------╯ + diff --git a/docs/storage-report/ServiceManagerBase.md b/docs/storage-report/ServiceManagerBase.md new file mode 100644 index 00000000..61ef533a --- /dev/null +++ b/docs/storage-report/ServiceManagerBase.md @@ -0,0 +1,27 @@ + +╭--------------------------+-------------+------+--------+-------+-----------------------------------------------╮ +| Name | Type | Slot | Offset | Bytes | Contract | ++================================================================================================================+ +| _initialized | uint8 | 0 | 0 | 1 | src/ServiceManagerBase.sol:ServiceManagerBase | +|--------------------------+-------------+------+--------+-------+-----------------------------------------------| +| _initializing | bool | 0 | 1 | 1 | src/ServiceManagerBase.sol:ServiceManagerBase | +|--------------------------+-------------+------+--------+-------+-----------------------------------------------| +| __gap | uint256[50] | 1 | 0 | 1600 | src/ServiceManagerBase.sol:ServiceManagerBase | +|--------------------------+-------------+------+--------+-------+-----------------------------------------------| +| _owner | address | 51 | 0 | 20 | src/ServiceManagerBase.sol:ServiceManagerBase | +|--------------------------+-------------+------+--------+-------+-----------------------------------------------| +| __gap | uint256[49] | 52 | 0 | 1568 | src/ServiceManagerBase.sol:ServiceManagerBase | +|--------------------------+-------------+------+--------+-------+-----------------------------------------------| +| rewardsInitiator | address | 101 | 0 | 20 | src/ServiceManagerBase.sol:ServiceManagerBase | +|--------------------------+-------------+------+--------+-------+-----------------------------------------------| +| slasher | address | 102 | 0 | 20 | src/ServiceManagerBase.sol:ServiceManagerBase | +|--------------------------+-------------+------+--------+-------+-----------------------------------------------| +| proposedSlasher | address | 103 | 0 | 20 | src/ServiceManagerBase.sol:ServiceManagerBase | +|--------------------------+-------------+------+--------+-------+-----------------------------------------------| +| slasherProposalTimestamp | uint256 | 104 | 0 | 32 | src/ServiceManagerBase.sol:ServiceManagerBase | +|--------------------------+-------------+------+--------+-------+-----------------------------------------------| +| migrationFinalized | bool | 105 | 0 | 1 | src/ServiceManagerBase.sol:ServiceManagerBase | +|--------------------------+-------------+------+--------+-------+-----------------------------------------------| +| __GAP | uint256[45] | 106 | 0 | 1440 | src/ServiceManagerBase.sol:ServiceManagerBase | +╰--------------------------+-------------+------+--------+-------+-----------------------------------------------╯ + diff --git a/docs/storage-report/ServiceManagerBaseStorage.md b/docs/storage-report/ServiceManagerBaseStorage.md new file mode 100644 index 00000000..baa458fa --- /dev/null +++ b/docs/storage-report/ServiceManagerBaseStorage.md @@ -0,0 +1,27 @@ + +╭--------------------------+-------------+------+--------+-------+-------------------------------------------------------------╮ +| Name | Type | Slot | Offset | Bytes | Contract | ++==============================================================================================================================+ +| _initialized | uint8 | 0 | 0 | 1 | src/ServiceManagerBaseStorage.sol:ServiceManagerBaseStorage | +|--------------------------+-------------+------+--------+-------+-------------------------------------------------------------| +| _initializing | bool | 0 | 1 | 1 | src/ServiceManagerBaseStorage.sol:ServiceManagerBaseStorage | +|--------------------------+-------------+------+--------+-------+-------------------------------------------------------------| +| __gap | uint256[50] | 1 | 0 | 1600 | src/ServiceManagerBaseStorage.sol:ServiceManagerBaseStorage | +|--------------------------+-------------+------+--------+-------+-------------------------------------------------------------| +| _owner | address | 51 | 0 | 20 | src/ServiceManagerBaseStorage.sol:ServiceManagerBaseStorage | +|--------------------------+-------------+------+--------+-------+-------------------------------------------------------------| +| __gap | uint256[49] | 52 | 0 | 1568 | src/ServiceManagerBaseStorage.sol:ServiceManagerBaseStorage | +|--------------------------+-------------+------+--------+-------+-------------------------------------------------------------| +| rewardsInitiator | address | 101 | 0 | 20 | src/ServiceManagerBaseStorage.sol:ServiceManagerBaseStorage | +|--------------------------+-------------+------+--------+-------+-------------------------------------------------------------| +| slasher | address | 102 | 0 | 20 | src/ServiceManagerBaseStorage.sol:ServiceManagerBaseStorage | +|--------------------------+-------------+------+--------+-------+-------------------------------------------------------------| +| proposedSlasher | address | 103 | 0 | 20 | src/ServiceManagerBaseStorage.sol:ServiceManagerBaseStorage | +|--------------------------+-------------+------+--------+-------+-------------------------------------------------------------| +| slasherProposalTimestamp | uint256 | 104 | 0 | 32 | src/ServiceManagerBaseStorage.sol:ServiceManagerBaseStorage | +|--------------------------+-------------+------+--------+-------+-------------------------------------------------------------| +| migrationFinalized | bool | 105 | 0 | 1 | src/ServiceManagerBaseStorage.sol:ServiceManagerBaseStorage | +|--------------------------+-------------+------+--------+-------+-------------------------------------------------------------| +| __GAP | uint256[45] | 106 | 0 | 1440 | src/ServiceManagerBaseStorage.sol:ServiceManagerBaseStorage | +╰--------------------------+-------------+------+--------+-------+-------------------------------------------------------------╯ + diff --git a/docs/storage-report/ServiceManagerRouter.md b/docs/storage-report/ServiceManagerRouter.md new file mode 100644 index 00000000..1ec5dc07 --- /dev/null +++ b/docs/storage-report/ServiceManagerRouter.md @@ -0,0 +1,6 @@ + +╭------+------+------+--------+-------+----------╮ +| Name | Type | Slot | Offset | Bytes | Contract | ++================================================+ +╰------+------+------+--------+-------+----------╯ + diff --git a/docs/storage-report/SlasherBase.md b/docs/storage-report/SlasherBase.md new file mode 100644 index 00000000..2e210170 --- /dev/null +++ b/docs/storage-report/SlasherBase.md @@ -0,0 +1,17 @@ + +╭----------------+-------------+------+--------+-------+-----------------------------------------------╮ +| Name | Type | Slot | Offset | Bytes | Contract | ++======================================================================================================+ +| _initialized | uint8 | 0 | 0 | 1 | src/slashers/base/SlasherBase.sol:SlasherBase | +|----------------+-------------+------+--------+-------+-----------------------------------------------| +| _initializing | bool | 0 | 1 | 1 | src/slashers/base/SlasherBase.sol:SlasherBase | +|----------------+-------------+------+--------+-------+-----------------------------------------------| +| serviceManager | address | 0 | 2 | 20 | src/slashers/base/SlasherBase.sol:SlasherBase | +|----------------+-------------+------+--------+-------+-----------------------------------------------| +| slasher | address | 1 | 0 | 20 | src/slashers/base/SlasherBase.sol:SlasherBase | +|----------------+-------------+------+--------+-------+-----------------------------------------------| +| nextRequestId | uint256 | 2 | 0 | 32 | src/slashers/base/SlasherBase.sol:SlasherBase | +|----------------+-------------+------+--------+-------+-----------------------------------------------| +| __gap | uint256[47] | 3 | 0 | 1504 | src/slashers/base/SlasherBase.sol:SlasherBase | +╰----------------+-------------+------+--------+-------+-----------------------------------------------╯ + diff --git a/docs/storage-report/SlasherStorage.md b/docs/storage-report/SlasherStorage.md new file mode 100644 index 00000000..6a5ec3aa --- /dev/null +++ b/docs/storage-report/SlasherStorage.md @@ -0,0 +1,13 @@ + +╭----------------+-------------+------+--------+-------+-----------------------------------------------------╮ +| Name | Type | Slot | Offset | Bytes | Contract | ++============================================================================================================+ +| serviceManager | address | 0 | 0 | 20 | src/slashers/base/SlasherStorage.sol:SlasherStorage | +|----------------+-------------+------+--------+-------+-----------------------------------------------------| +| slasher | address | 1 | 0 | 20 | src/slashers/base/SlasherStorage.sol:SlasherStorage | +|----------------+-------------+------+--------+-------+-----------------------------------------------------| +| nextRequestId | uint256 | 2 | 0 | 32 | src/slashers/base/SlasherStorage.sol:SlasherStorage | +|----------------+-------------+------+--------+-------+-----------------------------------------------------| +| __gap | uint256[47] | 3 | 0 | 1504 | src/slashers/base/SlasherStorage.sol:SlasherStorage | +╰----------------+-------------+------+--------+-------+-----------------------------------------------------╯ + diff --git a/docs/storage-report/StakeRegistry.md b/docs/storage-report/StakeRegistry.md new file mode 100644 index 00000000..820aaba6 --- /dev/null +++ b/docs/storage-report/StakeRegistry.md @@ -0,0 +1,21 @@ + +╭----------------------------------+---------------------------------------------------------------------------+------+--------+-------+-------------------------------------╮ +| Name | Type | Slot | Offset | Bytes | Contract | ++============================================================================================================================================================================+ +| minimumStakeForQuorum | mapping(uint8 => uint96) | 0 | 0 | 32 | src/StakeRegistry.sol:StakeRegistry | +|----------------------------------+---------------------------------------------------------------------------+------+--------+-------+-------------------------------------| +| _totalStakeHistory | mapping(uint8 => struct IStakeRegistry.StakeUpdate[]) | 1 | 0 | 32 | src/StakeRegistry.sol:StakeRegistry | +|----------------------------------+---------------------------------------------------------------------------+------+--------+-------+-------------------------------------| +| operatorStakeHistory | mapping(bytes32 => mapping(uint8 => struct IStakeRegistry.StakeUpdate[])) | 2 | 0 | 32 | src/StakeRegistry.sol:StakeRegistry | +|----------------------------------+---------------------------------------------------------------------------+------+--------+-------+-------------------------------------| +| strategyParams | mapping(uint8 => struct IStakeRegistry.StrategyParams[]) | 3 | 0 | 32 | src/StakeRegistry.sol:StakeRegistry | +|----------------------------------+---------------------------------------------------------------------------+------+--------+-------+-------------------------------------| +| strategiesPerQuorum | mapping(uint8 => contract IStrategy[]) | 4 | 0 | 32 | src/StakeRegistry.sol:StakeRegistry | +|----------------------------------+---------------------------------------------------------------------------+------+--------+-------+-------------------------------------| +| stakeTypePerQuorum | mapping(uint8 => enum StakeType) | 5 | 0 | 32 | src/StakeRegistry.sol:StakeRegistry | +|----------------------------------+---------------------------------------------------------------------------+------+--------+-------+-------------------------------------| +| slashableStakeLookAheadPerQuorum | mapping(uint8 => uint32) | 6 | 0 | 32 | src/StakeRegistry.sol:StakeRegistry | +|----------------------------------+---------------------------------------------------------------------------+------+--------+-------+-------------------------------------| +| __GAP | uint256[43] | 7 | 0 | 1376 | src/StakeRegistry.sol:StakeRegistry | +╰----------------------------------+---------------------------------------------------------------------------+------+--------+-------+-------------------------------------╯ + diff --git a/docs/storage-report/StakeRegistryStorage.md b/docs/storage-report/StakeRegistryStorage.md new file mode 100644 index 00000000..4a545513 --- /dev/null +++ b/docs/storage-report/StakeRegistryStorage.md @@ -0,0 +1,21 @@ + +╭----------------------------------+---------------------------------------------------------------------------+------+--------+-------+---------------------------------------------------╮ +| Name | Type | Slot | Offset | Bytes | Contract | ++==========================================================================================================================================================================================+ +| minimumStakeForQuorum | mapping(uint8 => uint96) | 0 | 0 | 32 | src/StakeRegistryStorage.sol:StakeRegistryStorage | +|----------------------------------+---------------------------------------------------------------------------+------+--------+-------+---------------------------------------------------| +| _totalStakeHistory | mapping(uint8 => struct IStakeRegistry.StakeUpdate[]) | 1 | 0 | 32 | src/StakeRegistryStorage.sol:StakeRegistryStorage | +|----------------------------------+---------------------------------------------------------------------------+------+--------+-------+---------------------------------------------------| +| operatorStakeHistory | mapping(bytes32 => mapping(uint8 => struct IStakeRegistry.StakeUpdate[])) | 2 | 0 | 32 | src/StakeRegistryStorage.sol:StakeRegistryStorage | +|----------------------------------+---------------------------------------------------------------------------+------+--------+-------+---------------------------------------------------| +| strategyParams | mapping(uint8 => struct IStakeRegistry.StrategyParams[]) | 3 | 0 | 32 | src/StakeRegistryStorage.sol:StakeRegistryStorage | +|----------------------------------+---------------------------------------------------------------------------+------+--------+-------+---------------------------------------------------| +| strategiesPerQuorum | mapping(uint8 => contract IStrategy[]) | 4 | 0 | 32 | src/StakeRegistryStorage.sol:StakeRegistryStorage | +|----------------------------------+---------------------------------------------------------------------------+------+--------+-------+---------------------------------------------------| +| stakeTypePerQuorum | mapping(uint8 => enum StakeType) | 5 | 0 | 32 | src/StakeRegistryStorage.sol:StakeRegistryStorage | +|----------------------------------+---------------------------------------------------------------------------+------+--------+-------+---------------------------------------------------| +| slashableStakeLookAheadPerQuorum | mapping(uint8 => uint32) | 6 | 0 | 32 | src/StakeRegistryStorage.sol:StakeRegistryStorage | +|----------------------------------+---------------------------------------------------------------------------+------+--------+-------+---------------------------------------------------| +| __GAP | uint256[43] | 7 | 0 | 1376 | src/StakeRegistryStorage.sol:StakeRegistryStorage | +╰----------------------------------+---------------------------------------------------------------------------+------+--------+-------+---------------------------------------------------╯ + diff --git a/docs/storage-report/VetoableSlasher.md b/docs/storage-report/VetoableSlasher.md new file mode 100644 index 00000000..e69de29b From 42fa63ea0af581bca011db93c450f41b6a469ad9 Mon Sep 17 00:00:00 2001 From: steven Date: Thu, 9 Jan 2025 10:43:08 -0500 Subject: [PATCH 67/80] fix: prevent calling enable operator sets twice and use internal function for checkALM --- src/RegistryCoordinator.sol | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/RegistryCoordinator.sol b/src/RegistryCoordinator.sol index 99073714..e6e1d659 100644 --- a/src/RegistryCoordinator.sol +++ b/src/RegistryCoordinator.sol @@ -278,6 +278,7 @@ contract RegistryCoordinator is } function enableOperatorSets() external onlyOwner { + require(!isOperatorSetAVS, OperatorSetsEnabled()); /// 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 @@ -304,7 +305,7 @@ contract RegistryCoordinator is for (uint256 i = 0; i < operatorSetIds.length; i++) { require(!isM2Quorum[uint8(operatorSetIds[i])], OperatorSetsNotSupported()); } - require(msg.sender == address(serviceManager.allocationManager()), OnlyAllocationManager()); + _checkAllocationManager(); // Decode registration data from bytes ( @@ -336,7 +337,7 @@ contract RegistryCoordinator is for (uint256 i = 0; i < operatorSetIds.length; i++) { require(!isM2Quorum[uint8(operatorSetIds[i])], OperatorSetsNotSupported()); } - require(msg.sender == address(serviceManager.allocationManager()), OnlyAllocationManager()); + _checkAllocationManager(); bytes memory quorumNumbers = new bytes(operatorSetIds.length); for (uint256 i = 0; i < operatorSetIds.length; i++) { quorumNumbers[i] = bytes1(uint8(operatorSetIds[i])); From 45eb220a2f9e96d34e2503cd103a86175fbe699c Mon Sep 17 00:00:00 2001 From: steven Date: Thu, 9 Jan 2025 17:00:38 -0500 Subject: [PATCH 68/80] chore: address review comments --- src/RegistryCoordinator.sol | 3 +-- src/RegistryCoordinatorStorage.sol | 9 ++++++--- test/harnesses/RegistryCoordinatorHarness.t.sol | 3 +-- test/integration/CoreRegistration.t.sol | 1 - test/integration/IntegrationDeployer.t.sol | 2 +- test/unit/StakeRegistryUnit.t.sol | 1 - test/utils/MockAVSDeployer.sol | 2 +- 7 files changed, 10 insertions(+), 11 deletions(-) diff --git a/src/RegistryCoordinator.sol b/src/RegistryCoordinator.sol index e6e1d659..bde1eafa 100644 --- a/src/RegistryCoordinator.sol +++ b/src/RegistryCoordinator.sol @@ -65,10 +65,9 @@ contract RegistryCoordinator is IStakeRegistry _stakeRegistry, IBLSApkRegistry _blsApkRegistry, IIndexRegistry _indexRegistry, - IAVSDirectory _avsDirectory, IPauserRegistry _pauserRegistry ) - RegistryCoordinatorStorage(_serviceManager, _stakeRegistry, _blsApkRegistry, _indexRegistry, _avsDirectory) + RegistryCoordinatorStorage(_serviceManager, _stakeRegistry, _blsApkRegistry, _indexRegistry) EIP712("AVSRegistryCoordinator", "v0.0.1") Pausable(_pauserRegistry) { diff --git a/src/RegistryCoordinatorStorage.sol b/src/RegistryCoordinatorStorage.sol index 8e343fbd..4911da4b 100644 --- a/src/RegistryCoordinatorStorage.sol +++ b/src/RegistryCoordinatorStorage.sol @@ -72,21 +72,24 @@ abstract contract RegistryCoordinatorStorage is IRegistryCoordinator { /// @notice the delay in seconds before an operator can reregister after being ejected uint256 public ejectionCooldown; + /// @notice Whether this AVS uses operator sets for registration + /// @dev If true, operators must register to operator sets via the AllocationManager bool public isOperatorSetAVS; + + /// @notice Mapping from quorum number to whether the quorum is an M2 quorum + /// @dev M2 quorums are pre-operator sets and track total delegated stake only mapping(uint8 => bool) public isM2Quorum; constructor( IServiceManager _serviceManager, IStakeRegistry _stakeRegistry, IBLSApkRegistry _blsApkRegistry, - IIndexRegistry _indexRegistry, - IAVSDirectory _avsDirectory + IIndexRegistry _indexRegistry ) { serviceManager = _serviceManager; stakeRegistry = _stakeRegistry; blsApkRegistry = _blsApkRegistry; indexRegistry = _indexRegistry; - avsDirectory = _avsDirectory; } // storage gap for upgradeability diff --git a/test/harnesses/RegistryCoordinatorHarness.t.sol b/test/harnesses/RegistryCoordinatorHarness.t.sol index 8110fb13..860e70dc 100644 --- a/test/harnesses/RegistryCoordinatorHarness.t.sol +++ b/test/harnesses/RegistryCoordinatorHarness.t.sol @@ -12,9 +12,8 @@ contract RegistryCoordinatorHarness is RegistryCoordinator, Test { IStakeRegistry _stakeRegistry, IBLSApkRegistry _blsApkRegistry, IIndexRegistry _indexRegistry, - IAVSDirectory _avsDirectory, IPauserRegistry _pauserRegistry - ) RegistryCoordinator(_serviceManager, _stakeRegistry, _blsApkRegistry, _indexRegistry, _avsDirectory, _pauserRegistry) { + ) RegistryCoordinator(_serviceManager, _stakeRegistry, _blsApkRegistry, _indexRegistry, _pauserRegistry) { _transferOwnership(msg.sender); } diff --git a/test/integration/CoreRegistration.t.sol b/test/integration/CoreRegistration.t.sol index 5d875e3e..4fbfc3ad 100644 --- a/test/integration/CoreRegistration.t.sol +++ b/test/integration/CoreRegistration.t.sol @@ -91,7 +91,6 @@ contract Test_CoreRegistration is MockAVSDeployer { stakeRegistry, blsApkRegistry, indexRegistry, - avsDirectory, pauserRegistry ); diff --git a/test/integration/IntegrationDeployer.t.sol b/test/integration/IntegrationDeployer.t.sol index 414b8fe2..dda8ce58 100644 --- a/test/integration/IntegrationDeployer.t.sol +++ b/test/integration/IntegrationDeployer.t.sol @@ -376,7 +376,7 @@ abstract contract IntegrationDeployer is Test, IUserDeployer { uint32[] memory slashableStakeQuorumLookAheadPeriods = new uint32[](0); RegistryCoordinator registryCoordinatorImplementation = - new RegistryCoordinator(serviceManager, stakeRegistry, blsApkRegistry, indexRegistry, avsDirectory, pauserRegistry); + new RegistryCoordinator(serviceManager, stakeRegistry, blsApkRegistry, indexRegistry, pauserRegistry); proxyAdmin.upgradeAndCall( TransparentUpgradeableProxy(payable(address(registryCoordinator))), address(registryCoordinatorImplementation), diff --git a/test/unit/StakeRegistryUnit.t.sol b/test/unit/StakeRegistryUnit.t.sol index a4f58b85..7e8f483e 100644 --- a/test/unit/StakeRegistryUnit.t.sol +++ b/test/unit/StakeRegistryUnit.t.sol @@ -49,7 +49,6 @@ contract StakeRegistryUnitTests is MockAVSDeployer, IStakeRegistryEvents { stakeRegistry, IBLSApkRegistry(blsApkRegistry), IIndexRegistry(indexRegistry), - IAVSDirectory(avsDirectory), pauserRegistry ); diff --git a/test/utils/MockAVSDeployer.sol b/test/utils/MockAVSDeployer.sol index 800628da..3a14c824 100644 --- a/test/utils/MockAVSDeployer.sol +++ b/test/utils/MockAVSDeployer.sol @@ -274,7 +274,7 @@ contract MockAVSDeployer is Test { } registryCoordinatorImplementation = new RegistryCoordinatorHarness( - serviceManager, stakeRegistry, blsApkRegistry, indexRegistry, avsDirectory, pauserRegistry + serviceManager, stakeRegistry, blsApkRegistry, indexRegistry, pauserRegistry ); { delete operatorSetParams; From 8778517518eee0965c3abf247c9e062c67648262 Mon Sep 17 00:00:00 2001 From: Michael Sun <35479365+8sunyuan@users.noreply.github.com> Date: Fri, 10 Jan 2025 13:11:19 -0500 Subject: [PATCH 69/80] refactor: custom errors in middleware (#355) * refactor: custom errors * fix: unit tests --- src/BLSApkRegistry.sol | 34 ++++------- src/BLSSignatureChecker.sol | 35 ++++------- src/EjectionManager.sol | 4 +- src/IndexRegistry.sol | 10 ++-- src/OperatorStateRetriever.sol | 4 +- src/ServiceManagerBase.sol | 23 ++----- src/StakeRegistry.sol | 44 +++++--------- src/interfaces/IBLSApkRegistry.sol | 25 +++++++- src/interfaces/IBLSSignatureChecker.sol | 25 +++++++- src/interfaces/IEjectionManager.sol | 9 ++- src/interfaces/IIndexRegistry.sol | 11 +++- src/interfaces/IServiceManager.sol | 14 ++++- src/interfaces/ISlasher.sol | 17 +++++- src/interfaces/IStakeRegistry.sol | 28 ++++++++- src/libraries/BN254.sol | 21 +++++-- src/libraries/BitmapUtils.sol | 16 +++-- src/libraries/QuorumBitmapHistoryLib.sol | 10 +++- src/slashers/VetoableSlasher.sol | 13 ++-- src/slashers/base/SlasherBase.sol | 2 +- src/unaudited/ECDSAServiceManagerBase.sol | 10 +--- test/unit/BLSApkRegistryUnit.t.sol | 61 +++++-------------- test/unit/BLSSignatureCheckerUnit.t.sol | 38 ++++++------ test/unit/BitmapUtils.t.sol | 2 +- test/unit/EjectionManagerUnit.t.sol | 4 +- test/unit/IndexRegistryUnit.t.sol | 9 +-- test/unit/OperatorStateRetrieverUnit.t.sol | 11 ++-- test/unit/RegistryCoordinatorUnit.t.sol | 16 ++--- test/unit/ServiceManagerBase.t.sol | 5 +- test/unit/StakeRegistryUnit.t.sol | 70 ++++++++-------------- 29 files changed, 300 insertions(+), 271 deletions(-) diff --git a/src/BLSApkRegistry.sol b/src/BLSApkRegistry.sol index ec445d3e..189716a6 100644 --- a/src/BLSApkRegistry.sol +++ b/src/BLSApkRegistry.sol @@ -79,7 +79,7 @@ contract BLSApkRegistry is BLSApkRegistryStorage { * @param quorumNumber The number of the new quorum */ function initializeQuorum(uint8 quorumNumber) public virtual onlyRegistryCoordinator { - require(apkHistory[quorumNumber].length == 0, "BLSApkRegistry.initializeQuorum: quorum already exists"); + require(apkHistory[quorumNumber].length == 0, QuorumAlreadyExists()); apkHistory[quorumNumber].push(ApkUpdate({ apkHash: bytes24(0), @@ -100,17 +100,9 @@ contract BLSApkRegistry is BLSApkRegistryStorage { BN254.G1Point calldata pubkeyRegistrationMessageHash ) external onlyRegistryCoordinator returns (bytes32 operatorId) { bytes32 pubkeyHash = BN254.hashG1Point(params.pubkeyG1); - require( - pubkeyHash != ZERO_PK_HASH, "BLSApkRegistry.registerBLSPublicKey: cannot register zero pubkey" - ); - require( - operatorToPubkeyHash[operator] == bytes32(0), - "BLSApkRegistry.registerBLSPublicKey: operator already registered pubkey" - ); - require( - pubkeyHashToOperator[pubkeyHash] == address(0), - "BLSApkRegistry.registerBLSPublicKey: public key already registered" - ); + require(pubkeyHash != ZERO_PK_HASH, ZeroPubKey()); + require(operatorToPubkeyHash[operator] == bytes32(0), OperatorAlreadyRegistered()); + require(pubkeyHashToOperator[pubkeyHash] == address(0), BLSPubkeyAlreadyRegistered()); // gamma = h(sigma, P, P', H(m)) uint256 gamma = uint256(keccak256(abi.encodePacked( @@ -130,7 +122,7 @@ contract BLSApkRegistry is BLSApkRegistryStorage { BN254.negGeneratorG2(), pubkeyRegistrationMessageHash.plus(BN254.generatorG1().scalar_mul(gamma)), params.pubkeyG2 - ), "BLSApkRegistry.registerBLSPublicKey: either the G1 signature is wrong, or G1 and G2 private key do not match"); + ), InvalidBLSSignatureOrPrivateKey()); operatorToPubkey[operator] = params.pubkeyG1; operatorToPubkeyHash[operator] = pubkeyHash; @@ -151,7 +143,7 @@ contract BLSApkRegistry is BLSApkRegistryStorage { // Validate quorum exists and get history length uint8 quorumNumber = uint8(quorumNumbers[i]); uint256 historyLength = apkHistory[quorumNumber].length; - require(historyLength != 0, "BLSApkRegistry._processQuorumApkUpdate: quorum does not exist"); + require(historyLength != 0, QuorumDoesNotExist()); // Update aggregate public key for this quorum newApk = currentApk[quorumNumber].plus(point); @@ -185,10 +177,7 @@ contract BLSApkRegistry is BLSApkRegistryStorage { BN254.G1Point memory pubkey = operatorToPubkey[operator]; bytes32 pubkeyHash = operatorToPubkeyHash[operator]; - require( - pubkeyHash != bytes32(0), - "BLSApkRegistry.getRegisteredPubkey: operator is not registered" - ); + require(pubkeyHash != bytes32(0), OperatorNotRegistered()); return (pubkey, pubkeyHash); } @@ -253,11 +242,11 @@ contract BLSApkRegistry is BLSApkRegistryStorage { */ require( blockNumber >= quorumApkUpdate.updateBlockNumber, - "BLSApkRegistry.getApkHashAtBlockNumberAndIndex: index too recent" + BlockNumberTooRecent() ); require( quorumApkUpdate.nextUpdateBlockNumber == 0 || blockNumber < quorumApkUpdate.nextUpdateBlockNumber, - "BLSApkRegistry.getApkHashAtBlockNumberAndIndex: not latest apk update" + BlockNumberNotLatest() ); return quorumApkUpdate.apkHash; @@ -280,9 +269,6 @@ contract BLSApkRegistry is BLSApkRegistryStorage { } function _checkRegistryCoordinator() internal view { - require( - msg.sender == address(registryCoordinator), - "BLSApkRegistry._checkRegistryCoordinator: caller is not the registry coordinator" - ); + require(msg.sender == address(registryCoordinator), OnlyRegistryCoordinatorOwner()); } } diff --git a/src/BLSSignatureChecker.sol b/src/BLSSignatureChecker.sol index 77d6fbe6..944ed4f7 100644 --- a/src/BLSSignatureChecker.sol +++ b/src/BLSSignatureChecker.sol @@ -31,10 +31,7 @@ contract BLSSignatureChecker is IBLSSignatureChecker { bool public staleStakesForbidden; modifier onlyCoordinatorOwner() { - require( - msg.sender == registryCoordinator.owner(), - "BLSSignatureChecker.onlyCoordinatorOwner: caller is not the owner of the registryCoordinator" - ); + require(msg.sender == registryCoordinator.owner(), OnlyRegistryCoordinatorOwner()); _; } @@ -91,29 +88,23 @@ contract BLSSignatureChecker is IBLSSignatureChecker { uint32 referenceBlockNumber, NonSignerStakesAndSignature memory params ) public view returns (QuorumStakeTotals memory, bytes32) { - require( - quorumNumbers.length != 0, - "BLSSignatureChecker.checkSignatures: empty quorum input" - ); + require(quorumNumbers.length != 0, InputEmptyQuorumNumbers()); require( (quorumNumbers.length == params.quorumApks.length) && (quorumNumbers.length == params.quorumApkIndices.length) && (quorumNumbers.length == params.totalStakeIndices.length) && (quorumNumbers.length == params.nonSignerStakeIndices.length), - "BLSSignatureChecker.checkSignatures: input quorum length mismatch" + InputArrayLengthMismatch() ); require( params.nonSignerPubkeys.length == params.nonSignerQuorumBitmapIndices.length, - "BLSSignatureChecker.checkSignatures: input nonsigner length mismatch" + InputNonSignerLengthMismatch() ); - require( - referenceBlockNumber < uint32(block.number), - "BLSSignatureChecker.checkSignatures: invalid reference block" - ); + require(referenceBlockNumber < uint32(block.number), InvalidReferenceBlocknumber()); // This method needs to calculate the aggregate pubkey for all signing operators across // all signing quorums. To do that, we can query the aggregate pubkey for each quorum @@ -155,7 +146,7 @@ contract BLSSignatureChecker is IBLSSignatureChecker { require( uint256(nonSigners.pubkeyHashes[j]) > uint256(nonSigners.pubkeyHashes[j - 1]), - "BLSSignatureChecker.checkSignatures: nonSignerPubkeys not sorted" + NonSignerPubkeysNotSorted() ); } @@ -207,7 +198,7 @@ contract BLSSignatureChecker is IBLSSignatureChecker { ) + withdrawalDelayBlocks > referenceBlockNumber, - "BLSSignatureChecker.checkSignatures: StakeRegistry updates must be within withdrawalDelayBlocks window" + StaleStakesForbidden() ); } @@ -220,7 +211,7 @@ contract BLSSignatureChecker is IBLSSignatureChecker { blockNumber: referenceBlockNumber, index: params.quorumApkIndices[i] }), - "BLSSignatureChecker.checkSignatures: quorumApk hash in storage does not match provided quorum apk" + InvalidQuorumApkHash() ); apk = apk.plus(params.quorumApks[i]); @@ -274,14 +265,8 @@ contract BLSSignatureChecker is IBLSSignatureChecker { params.apkG2, params.sigma ); - require( - pairingSuccessful, - "BLSSignatureChecker.checkSignatures: pairing precompile call failed" - ); - require( - signatureIsValid, - "BLSSignatureChecker.checkSignatures: signature is invalid" - ); + require(pairingSuccessful, InvalidBLSPairingKey()); + require(signatureIsValid, InvalidBLSSignature()); } // set signatoryRecordHash variable used for fraudproofs bytes32 signatoryRecordHash = keccak256( diff --git a/src/EjectionManager.sol b/src/EjectionManager.sol index 81860fc0..8ebf6002 100644 --- a/src/EjectionManager.sol +++ b/src/EjectionManager.sol @@ -67,7 +67,7 @@ contract EjectionManager is IEjectionManager, OwnableUpgradeable { * @dev The owner can eject operators without recording of stake ejection */ function ejectOperators(bytes32[][] memory _operatorIds) external { - require(isEjector[msg.sender] || msg.sender == owner(), "EjectionManager.ejectOperators: Only owner or ejector can eject"); + require(isEjector[msg.sender] || msg.sender == owner(), OnlyOwnerOrEjector()); for(uint i = 0; i < _operatorIds.length; ++i) { uint8 quorumNumber = uint8(i); @@ -137,7 +137,7 @@ contract EjectionManager is IEjectionManager, OwnableUpgradeable { ///@dev internal function to set the quorum ejection params function _setQuorumEjectionParams(uint8 _quorumNumber, QuorumEjectionParams memory _quorumEjectionParams) internal { - require(_quorumNumber < MAX_QUORUM_COUNT, "EjectionManager._setQuorumEjectionParams: Quorum number exceeds MAX_QUORUM_COUNT"); + require(_quorumNumber < MAX_QUORUM_COUNT, MaxQuorumCount()); quorumEjectionParams[_quorumNumber] = _quorumEjectionParams; emit QuorumEjectionParamsSet(_quorumNumber, _quorumEjectionParams.rateLimitWindow, _quorumEjectionParams.ejectableStakePercent); } diff --git a/src/IndexRegistry.sol b/src/IndexRegistry.sol index ce432d64..cab62a17 100644 --- a/src/IndexRegistry.sol +++ b/src/IndexRegistry.sol @@ -47,7 +47,7 @@ contract IndexRegistry is IndexRegistryStorage { // Validate quorum exists and get current operator count uint8 quorumNumber = uint8(quorumNumbers[i]); uint256 historyLength = _operatorCountHistory[quorumNumber].length; - require(historyLength != 0, "IndexRegistry.registerOperator: quorum does not exist"); + require(historyLength != 0, QuorumDoesNotExist()); /** * Increase the number of operators currently active for this quorum, @@ -87,7 +87,7 @@ contract IndexRegistry is IndexRegistryStorage { // Validate quorum exists and get the operatorIndex of the operator being deregistered uint8 quorumNumber = uint8(quorumNumbers[i]); uint256 historyLength = _operatorCountHistory[quorumNumber].length; - require(historyLength != 0, "IndexRegistry.registerOperator: quorum does not exist"); + require(historyLength != 0, QuorumDoesNotExist()); uint32 operatorIndexToRemove = currentOperatorIndex[quorumNumber][operatorId]; /** @@ -113,7 +113,7 @@ contract IndexRegistry is IndexRegistryStorage { * @param quorumNumber The number of the new quorum */ function initializeQuorum(uint8 quorumNumber) public virtual onlyRegistryCoordinator { - require(_operatorCountHistory[quorumNumber].length == 0, "IndexRegistry.createQuorum: quorum already exists"); + require(_operatorCountHistory[quorumNumber].length == 0, QuorumDoesNotExist()); _operatorCountHistory[quorumNumber].push(QuorumUpdate({ numOperators: 0, @@ -329,7 +329,7 @@ contract IndexRegistry is IndexRegistryStorage { operatorList[i] = _operatorIdForIndexAtBlockNumber(quorumNumber, uint32(i), blockNumber); require( operatorList[i] != OPERATOR_DOES_NOT_EXIST_ID, - "IndexRegistry.getOperatorListAtBlockNumber: operator does not exist at the given block number" + OperatorIdDoesNotExist() ); } return operatorList; @@ -342,6 +342,6 @@ contract IndexRegistry is IndexRegistryStorage { } function _checkRegistryCoordinator() internal view { - require(msg.sender == address(registryCoordinator), "IndexRegistry._checkRegistryCoordinator: caller is not the registry coordinator"); + require(msg.sender == address(registryCoordinator), OnlyRegistryCoordinator()); } } diff --git a/src/OperatorStateRetriever.sol b/src/OperatorStateRetriever.sol index 2af9e527..87672b66 100644 --- a/src/OperatorStateRetriever.sol +++ b/src/OperatorStateRetriever.sol @@ -26,6 +26,8 @@ contract OperatorStateRetriever { uint32[][] nonSignerStakeIndices; // nonSignerStakeIndices[quorumNumberIndex][nonSignerIndex] } + error OperatorNotRegistered(); + /** * @notice This function is intended to to be called by AVS operators every time a new task is created (i.e.) * the AVS coordinator makes a request to AVS operators. Since all of the crucial information is kept onchain, @@ -131,7 +133,7 @@ contract OperatorStateRetriever { checkSignaturesIndices.nonSignerQuorumBitmapIndices[i] ); - require(nonSignerQuorumBitmap != 0, "OperatorStateRetriever.getCheckSignaturesIndices: operator must be registered at blocknumber"); + require(nonSignerQuorumBitmap != 0, OperatorNotRegistered()); // if the operator was a part of the quorum and the quorum is a part of the provided quorumNumbers if ((nonSignerQuorumBitmap >> uint8(quorumNumbers[quorumNumberIndex])) & 1 == 1) { diff --git a/src/ServiceManagerBase.sol b/src/ServiceManagerBase.sol index 86c1e937..b7a07df8 100644 --- a/src/ServiceManagerBase.sol +++ b/src/ServiceManagerBase.sol @@ -30,10 +30,7 @@ abstract contract ServiceManagerBase is ServiceManagerBaseStorage { /// @notice when applied to a function, only allows the RegistryCoordinator to call it modifier onlyRegistryCoordinator() { - require( - msg.sender == address(_registryCoordinator), - "ServiceManagerBase.onlyRegistryCoordinator: caller is not the registry coordinator" - ); + require(msg.sender == address(_registryCoordinator), OnlyRegistryCoordinator()); _; } @@ -209,7 +206,7 @@ abstract contract ServiceManagerBase is ServiceManagerBaseStorage { function acceptProposedSlasher() external onlyOwner { require( block.timestamp >= slasherProposalTimestamp + SLASHER_PROPOSAL_DELAY, - "ServiceManager: Slasher proposal delay not met" + DelayPeriodNotPassed() ); _setSlasher(proposedSlasher); delete proposedSlasher; @@ -314,24 +311,14 @@ abstract contract ServiceManagerBase is ServiceManagerBaseStorage { } function _checkRewardsInitiator() internal view { - require( - msg.sender == rewardsInitiator, - "ServiceManagerBase.onlyRewardsInitiator: caller is not the rewards initiator" - ); + require(msg.sender == rewardsInitiator, OnlyRewardsInitiator()); } function _checkStakeRegistry() internal view { - require( - msg.sender == address(_stakeRegistry), - "ServiceManagerBase.onlyStakeRegistry: caller is not the stake registry" - ); + require(msg.sender == address(_stakeRegistry), OnlyStakeRegistry()); } - function _checkSlasher() internal view { - require( - msg.sender == slasher, - "ServiceManagerBase.onlySlasher: caller is not the slasher" - ); + require(msg.sender == slasher, OnlySlasher()); } } diff --git a/src/StakeRegistry.sol b/src/StakeRegistry.sol index fe861b7b..3d2b5089 100644 --- a/src/StakeRegistry.sol +++ b/src/StakeRegistry.sol @@ -82,10 +82,7 @@ contract StakeRegistry is StakeRegistryStorage { // Retrieve the operator's current weighted stake for the quorum, reverting if they have not met // the minimum. (uint96 currentStake, bool hasMinimumStake) = _weightOfOperatorForQuorum(quorumNumber, operator); - require( - hasMinimumStake, - "StakeRegistry.registerOperator: Operator does not meet minimum stake requirement for quorum" - ); + require(hasMinimumStake, BelowMinimumStakeRequirement()); // Update the operator's stake int256 stakeDelta = _recordOperatorStakeUpdate({ @@ -197,7 +194,7 @@ contract StakeRegistry is StakeRegistryStorage { uint96 minimumStake, StrategyParams[] memory _strategyParams ) public virtual onlyRegistryCoordinator { - require(!_quorumExists(quorumNumber), "StakeRegistry.initializeQuorum: quorum already exists"); + require(!_quorumExists(quorumNumber), QuorumAlreadyExists()); _addStrategyParams(quorumNumber, _strategyParams); _setMinimumStakeForQuorum(quorumNumber, minimumStake); _setStakeType(quorumNumber, StakeType.TOTAL_DELEGATED); @@ -217,7 +214,7 @@ contract StakeRegistry is StakeRegistryStorage { uint32 lookAheadPeriod, StrategyParams[] memory _strategyParams ) public virtual onlyRegistryCoordinator { - require(!_quorumExists(quorumNumber), "StakeRegistry.initializeQuorum: quorum already exists"); + require(!_quorumExists(quorumNumber), QuorumAlreadyExists()); _addStrategyParams(quorumNumber, _strategyParams); _setMinimumStakeForQuorum(quorumNumber, minimumStake); _setStakeType(quorumNumber, StakeType.TOTAL_SLASHABLE); @@ -278,7 +275,7 @@ contract StakeRegistry is StakeRegistryStorage { uint256[] memory indicesToRemove ) public virtual onlyCoordinatorOwner quorumExists(quorumNumber) { uint256 toRemoveLength = indicesToRemove.length; - require(toRemoveLength > 0, "StakeRegistry.removeStrategies: no indices to remove provided"); + require(toRemoveLength > 0, InputArrayLengthZero()); StrategyParams[] storage _strategyParams = strategyParams[quorumNumber]; IStrategy[] storage _strategiesPerQuorum = strategiesPerQuorum[quorumNumber]; @@ -307,8 +304,8 @@ contract StakeRegistry is StakeRegistryStorage { uint96[] calldata newMultipliers ) public virtual onlyCoordinatorOwner quorumExists(quorumNumber) { uint256 numStrats = strategyIndices.length; - require(numStrats > 0, "StakeRegistry.modifyStrategyParams: no strategy indices provided"); - require(newMultipliers.length == numStrats, "StakeRegistry.modifyStrategyParams: input length mismatch"); + require(numStrats > 0, InputArrayLengthZero()); + require(newMultipliers.length == numStrats, InputArrayLengthMismatch()); StrategyParams[] storage _strategyParams = strategyParams[quorumNumber]; @@ -442,25 +439,22 @@ contract StakeRegistry is StakeRegistryStorage { uint8 quorumNumber, StrategyParams[] memory _strategyParams ) internal { - require(_strategyParams.length > 0, "StakeRegistry._addStrategyParams: no strategies provided"); + require(_strategyParams.length > 0, InputArrayLengthZero()); uint256 numStratsToAdd = _strategyParams.length; uint256 numStratsExisting = strategyParams[quorumNumber].length; require( numStratsExisting + numStratsToAdd <= MAX_WEIGHING_FUNCTION_LENGTH, - "StakeRegistry._addStrategyParams: exceed MAX_WEIGHING_FUNCTION_LENGTH" + InputArrayLengthMismatch() ); for (uint256 i = 0; i < numStratsToAdd; i++) { // fairly gas-expensive internal loop to make sure that the *same* strategy cannot be added multiple times for (uint256 j = 0; j < (numStratsExisting + i); j++) { require( strategyParams[quorumNumber][j].strategy != _strategyParams[i].strategy, - "StakeRegistry._addStrategyParams: cannot add same strategy 2x" + InputDuplicateStrategy() ); } - require( - _strategyParams[i].multiplier > 0, - "StakeRegistry._addStrategyParams: cannot add strategy with zero weight" - ); + require(_strategyParams[i].multiplier > 0, InputMultiplierZero()); strategyParams[quorumNumber].push(_strategyParams[i]); strategiesPerQuorum[quorumNumber].push(_strategyParams[i].strategy); emit StrategyAddedToQuorum(quorumNumber, _strategyParams[i].strategy); @@ -496,13 +490,10 @@ contract StakeRegistry is StakeRegistryStorage { * - blockNumber should be >= the update block number * - the next update block number should be either 0 or strictly greater than blockNumber */ - require( - blockNumber >= stakeUpdate.updateBlockNumber, - "StakeRegistry._validateStakeUpdateAtBlockNumber: stakeUpdate is from after blockNumber" - ); + require(blockNumber >= stakeUpdate.updateBlockNumber, InvalidBlockNumber()); require( stakeUpdate.nextUpdateBlockNumber == 0 || blockNumber < stakeUpdate.nextUpdateBlockNumber, - "StakeRegistry._validateStakeUpdateAtBlockNumber: there is a newer stakeUpdate available before blockNumber" + InvalidBlockNumber() ); } @@ -785,7 +776,7 @@ contract StakeRegistry is StakeRegistryStorage { _checkQuorumExists(quorumNumber); require( _totalStakeHistory[quorumNumber][0].updateBlockNumber <= blockNumber, - "StakeRegistry.getTotalStakeIndicesAtBlockNumber: quorum has no stake history at blockNumber" + EmptyStakeHistory() ); uint256 length = _totalStakeHistory[quorumNumber].length; for (uint256 j = 0; j < length; j++) { @@ -821,17 +812,14 @@ contract StakeRegistry is StakeRegistryStorage { function _checkRegistryCoordinator() internal view { - require( - msg.sender == address(registryCoordinator), - "StakeRegistry.onlyRegistryCoordinator: caller is not the RegistryCoordinator" - ); + require(msg.sender == address(registryCoordinator), OnlyRegistryCoordinator()); } function _checkRegistryCoordinatorOwner() internal view { - require(msg.sender == IRegistryCoordinator(registryCoordinator).owner(), "StakeRegistry.onlyCoordinatorOwner: caller is not the owner of the registryCoordinator"); + require(msg.sender == IRegistryCoordinator(registryCoordinator).owner(), OnlyRegistryCoordinatorOwner()); } function _checkQuorumExists(uint8 quorumNumber) internal view { - require(_quorumExists(quorumNumber), "StakeRegistry.quorumExists: quorum does not exist"); + require(_quorumExists(quorumNumber), QuorumDoesNotExist()); } } diff --git a/src/interfaces/IBLSApkRegistry.sol b/src/interfaces/IBLSApkRegistry.sol index ced99f6c..a93d4d53 100644 --- a/src/interfaces/IBLSApkRegistry.sol +++ b/src/interfaces/IBLSApkRegistry.sol @@ -5,11 +5,34 @@ import {IRegistry} from "./IRegistry.sol"; import {BN254} from "../libraries/BN254.sol"; +interface IBLSApkRegistryErrors { + /// @dev Thrown when the caller is not the owner of the registry coordinator. + error OnlyRegistryCoordinatorOwner(); + /// @dev Thrown when a quorum being created already exists. + error QuorumAlreadyExists(); + /// @dev Thrown when a quorum does not exist. + error QuorumDoesNotExist(); + /// @dev Thrown when a BLS pubkey provided is zero pubkey + error ZeroPubKey(); + /// @dev Thrown when an operator has already registered a BLS pubkey. + error OperatorAlreadyRegistered(); + /// @dev Thrown when the operator is not registered. + error OperatorNotRegistered(); + /// @dev Thrown when a BLS pubkey has already been registered for an operator. + error BLSPubkeyAlreadyRegistered(); + /// @dev Thrown when either the G1 signature is wrong, or G1 and G2 private key do not match. + error InvalidBLSSignatureOrPrivateKey(); + /// @dev Thrown when the quorum apk update block number is too recent. + error BlockNumberTooRecent(); + /// @dev Thrown when blocknumber and index provided is not the latest apk update. + error BlockNumberNotLatest(); +} + /** * @title Minimal interface for a registry that keeps track of aggregate operator public keys across many quorums. * @author Layr Labs, Inc. */ -interface IBLSApkRegistry is IRegistry { +interface IBLSApkRegistry is IRegistry, IBLSApkRegistryErrors { // STRUCTS /// @notice Data structure used to track the history of the Aggregate Public Key of all operators struct ApkUpdate { diff --git a/src/interfaces/IBLSSignatureChecker.sol b/src/interfaces/IBLSSignatureChecker.sol index aa92e56f..4ab73f4c 100644 --- a/src/interfaces/IBLSSignatureChecker.sol +++ b/src/interfaces/IBLSSignatureChecker.sol @@ -7,13 +7,36 @@ import {IStakeRegistry, IDelegationManager} from "./IStakeRegistry.sol"; import {BN254} from "../libraries/BN254.sol"; +interface IBLSSignatureCheckerErrors { + /// @dev Thrown when the caller is not the registry coordinator owner. + error OnlyRegistryCoordinatorOwner(); + /// @dev Thrown when the quorum numbers input in is empty. + error InputEmptyQuorumNumbers(); + /// @dev Thrown when two array parameters have mismatching lengths. + error InputArrayLengthMismatch(); + /// @dev Thrown when the non-signer pubkey length does not match non-signer bitmap indices length. + error InputNonSignerLengthMismatch(); + /// @dev Thrown when the reference block number is invalid. + error InvalidReferenceBlocknumber(); + /// @dev Thrown when the non signer pubkeys are not sorted. + error NonSignerPubkeysNotSorted(); + /// @dev Thrown when StakeRegistry updates have not been updated within withdrawalDelayBlocks window + error StaleStakesForbidden(); + /// @dev Thrown when the quorum apk hash in storage does not match provided quorum apk. + error InvalidQuorumApkHash(); + /// @dev Thrown when BLS pairing precompile call fails. + error InvalidBLSPairingKey(); + /// @dev Thrown when BLS signature is invalid. + error InvalidBLSSignature(); +} + /** * @title Used for checking BLS aggregate signatures from the operators of a EigenLayer AVS with the RegistryCoordinator/BLSApkRegistry/StakeRegistry architechture. * @author Layr Labs, Inc. * @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service * @notice This is the contract for checking the validity of aggregate operator signatures. */ -interface IBLSSignatureChecker { +interface IBLSSignatureChecker is IBLSSignatureCheckerErrors { // DATA STRUCTURES struct NonSignerStakesAndSignature { diff --git a/src/interfaces/IEjectionManager.sol b/src/interfaces/IEjectionManager.sol index 545e0a7c..478e21d8 100644 --- a/src/interfaces/IEjectionManager.sol +++ b/src/interfaces/IEjectionManager.sol @@ -1,11 +1,18 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.27; +interface IEjectionManagerErrors { + /// @dev Thrown when the caller is not the owner or ejector. + error OnlyOwnerOrEjector(); + /// @dev Thrown when quorum number exceeds MAX_QUORUM_COUNT. + error MaxQuorumCount(); +} + /** * @title Interface for a contract that ejects operators from an AVSs RegistryCoordinator * @author Layr Labs, Inc. */ -interface IEjectionManager { +interface IEjectionManager is IEjectionManagerErrors { /// @notice A quorum's ratelimit parameters struct QuorumEjectionParams { diff --git a/src/interfaces/IIndexRegistry.sol b/src/interfaces/IIndexRegistry.sol index 579fb23b..df1e0e73 100644 --- a/src/interfaces/IIndexRegistry.sol +++ b/src/interfaces/IIndexRegistry.sol @@ -3,11 +3,20 @@ pragma solidity ^0.8.27; import {IRegistry} from "./IRegistry.sol"; +interface IIndexRegistryErrors { + /// @dev Thrown when a function is called by an address that is not the RegistryCoordinator + error OnlyRegistryCoordinator(); + /// @dev Thrown when a quorum has 0 length history and thus does not exist + error QuorumDoesNotExist(); + /// @dev Thrown when an operatorId is not found in the registry at a given block number + error OperatorIdDoesNotExist(); +} + /** * @title Interface for a `Registry`-type contract that keeps track of an ordered list of operators for up to 256 quorums. * @author Layr Labs, Inc. */ -interface IIndexRegistry is IRegistry { +interface IIndexRegistry is IRegistry, IIndexRegistryErrors { // EVENTS // emitted when an operator's index in the ordered operator list for the quorum with number `quorumNumber` is updated diff --git a/src/interfaces/IServiceManager.sol b/src/interfaces/IServiceManager.sol index 133c3789..ccc0603f 100644 --- a/src/interfaces/IServiceManager.sol +++ b/src/interfaces/IServiceManager.sol @@ -9,12 +9,24 @@ import {IAllocationManager} from "eigenlayer-contracts/src/contracts/interfaces/ import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; import {IAVSRegistrar} from "eigenlayer-contracts/src/contracts/interfaces/IAVSRegistrar.sol"; +interface IServiceManagerErrors { + /// @dev Thrown when a function is called by an address that is not the RegistryCoordinator + error OnlyRegistryCoordinator(); + /// @dev Thrown when a function is called by an address that is not the RewardsInitiator + error OnlyRewardsInitiator(); + /// @dev Thrown when a function is called by an address that is not the Slasher + error OnlyStakeRegistry(); + /// @dev Thrown when a function is called by an address that is not the Slasher + error OnlySlasher(); + /// @dev Thrown when a slashing proposal delay has not been met yet. + error DelayPeriodNotPassed(); +} /** * @title Minimal interface for a ServiceManager-type contract that forms the single point for an AVS to push updates to EigenLayer * @author Layr Labs, Inc. */ -interface IServiceManager is IServiceManagerUI { +interface IServiceManager is IServiceManagerUI, IServiceManagerErrors { /** * @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` diff --git a/src/interfaces/ISlasher.sol b/src/interfaces/ISlasher.sol index a150bc94..44222150 100644 --- a/src/interfaces/ISlasher.sol +++ b/src/interfaces/ISlasher.sol @@ -25,6 +25,21 @@ interface ISlasherEvents { ); } +interface ISlasherErrors { + /// @dev Thrown when the caller is not the veto committee + error OnlyVetoCommittee(); + /// @dev Thrown when the caller is not the slasher + error OnlySlasher(); + /// @dev Thrown when the veto period has passed + error VetoPeriodPassed(); + /// @dev Thrown when the veto period has not passed + error VetoPeriodNotPassed(); + /// @dev Thrown when the slashing request is cancelled + error SlashingRequestIsCancelled(); + /// @dev Thrown when the slashing request was not already requested + error SlashingRequestNotRequested(); +} + interface ISlasherTypes { enum SlashingStatus { Null, @@ -41,4 +56,4 @@ interface ISlasherTypes { } -interface ISlasher is ISlasherEvents, ISlasherTypes{} +interface ISlasher is ISlasherEvents, ISlasherTypes, ISlasherErrors {} diff --git a/src/interfaces/IStakeRegistry.sol b/src/interfaces/IStakeRegistry.sol index 3b82fefb..d56c01c1 100644 --- a/src/interfaces/IStakeRegistry.sol +++ b/src/interfaces/IStakeRegistry.sol @@ -11,11 +11,37 @@ enum StakeType { TOTAL_SLASHABLE } +interface IStakeRegistryErrors { + /// @dev Thrown when the caller is not the registry coordinator + error OnlyRegistryCoordinator(); + /// @dev Thrown when the caller is not the owner of the registry coordinator + error OnlyRegistryCoordinatorOwner(); + /// @dev Thrown when the stake is below the minimum required for a quorum + error BelowMinimumStakeRequirement(); + /// @dev Thrown when a quorum being created already exists. + error QuorumAlreadyExists(); + /// @dev Thrown when a quorum does not exist. + error QuorumDoesNotExist(); + /// @dev Thrown when two array parameters have mismatching lengths. + error InputArrayLengthMismatch(); + /// @dev Thrown when input arrays length is zero. + error InputArrayLengthZero(); + /// @dev Thrown when a duplicate strategy is provided in input array. + error InputDuplicateStrategy(); + /// @dev Thrown when multiplier input is zero. + error InputMultiplierZero(); + /// @dev Thrown when the blocknumber provided is not >= the provided StakeUpdate's updateBlockNumber + /// or if the blocknumber provided is not the nextUpdateBlockNumber + error InvalidBlockNumber(); + /// @dev Thrown when the quorum has no stake history at block number provided. + error EmptyStakeHistory(); +} + /** * @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 { +interface IStakeRegistry is IRegistry, IStakeRegistryErrors { // 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/libraries/BN254.sol b/src/libraries/BN254.sol index 61a20929..dcda7a31 100644 --- a/src/libraries/BN254.sol +++ b/src/libraries/BN254.sol @@ -46,6 +46,17 @@ library BN254 { uint256[2] Y; } + /// @dev Thrown when the sum of two points of G1 fails + error ECAddFailed(); + /// @dev Thrown when the scalar multiplication of a point of G1 fails + error ECMulFailed(); + /// @dev Thrown when the scalar is too large. + error ScalarTooLarge(); + /// @dev Thrown when the pairing check fails + error ECPairingFailed(); + /// @dev Thrown when the exponentiation mod fails + error ExpModFailed(); + function generatorG1() internal pure returns (G1Point memory) { return G1Point(1, 2); } @@ -114,7 +125,7 @@ library BN254 { } } - require(success, "ec-add-failed"); + require(success, ECAddFailed()); } /** @@ -124,7 +135,7 @@ library BN254 { * @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"); + require(s < 2**9, ScalarTooLarge()); // if s is 1 return p if(s == 1) { @@ -180,7 +191,7 @@ library BN254 { invalid() } } - require(success, "ec-mul-failed"); + require(success, ECMulFailed()); } /** @@ -223,7 +234,7 @@ library BN254 { } } - require(success, "pairing-opcode-failed"); + require(success, ECPairingFailed()); return out[0] != 0; } @@ -344,7 +355,7 @@ library BN254 { invalid() } } - require(success, "BN254.expMod: call failure"); + require(success, ExpModFailed()); return output[0]; } } diff --git a/src/libraries/BitmapUtils.sol b/src/libraries/BitmapUtils.sol index 9c53aadd..3930209a 100644 --- a/src/libraries/BitmapUtils.sol +++ b/src/libraries/BitmapUtils.sol @@ -7,6 +7,13 @@ pragma solidity ^0.8.27; * @author Layr Labs, Inc. */ library BitmapUtils { + /// @dev Thrown when the input byte array is too long to be converted to a bitmap + error BytesArrayLengthTooLong(); + /// @dev Thrown when the input byte array is not strictly ordered + error BytesArrayNotOrdered(); + /// @dev Thrown when the bitmap value is too large + error BitmapValueTooLarge(); + /** * @notice Byte arrays are meant to contain unique bytes. * If the array length exceeds 256, then it's impossible for all entries to be unique. @@ -24,8 +31,7 @@ library BitmapUtils { */ function orderedBytesArrayToBitmap(bytes memory orderedBytesArray) internal pure returns (uint256) { // sanity-check on input. a too-long input would fail later on due to having duplicate entry(s) - require(orderedBytesArray.length <= MAX_BYTE_ARRAY_LENGTH, - "BitmapUtils.orderedBytesArrayToBitmap: orderedBytesArray is too long"); + require(orderedBytesArray.length <= MAX_BYTE_ARRAY_LENGTH, BytesArrayLengthTooLong()); // return empty bitmap early if length of array is 0 if (orderedBytesArray.length == 0) { @@ -46,7 +52,7 @@ library BitmapUtils { // construct a single-bit mask from the numerical value of the next byte of the array bitMask = uint256(1 << uint8(orderedBytesArray[i])); // check strictly ascending array ordering by comparing the mask to the bitmap so far (revert if mask isn't greater than bitmap) - require(bitMask > bitmap, "BitmapUtils.orderedBytesArrayToBitmap: orderedBytesArray is not ordered"); + require(bitMask > bitmap, BytesArrayNotOrdered()); // add the entry to the bitmap bitmap = (bitmap | bitMask); } @@ -62,9 +68,7 @@ library BitmapUtils { function orderedBytesArrayToBitmap(bytes memory orderedBytesArray, uint8 bitUpperBound) internal pure returns (uint256) { uint256 bitmap = orderedBytesArrayToBitmap(orderedBytesArray); - require((1 << bitUpperBound) > bitmap, - "BitmapUtils.orderedBytesArrayToBitmap: bitmap exceeds max value" - ); + require((1 << bitUpperBound) > bitmap, BitmapValueTooLarge()); return bitmap; } diff --git a/src/libraries/QuorumBitmapHistoryLib.sol b/src/libraries/QuorumBitmapHistoryLib.sol index 72cbf114..50f6880b 100644 --- a/src/libraries/QuorumBitmapHistoryLib.sol +++ b/src/libraries/QuorumBitmapHistoryLib.sol @@ -6,6 +6,12 @@ import {IRegistryCoordinator} from "../interfaces/IRegistryCoordinator.sol"; /// @title QuorumBitmapHistoryLib /// @notice This library operates on the _operatorBitmapHistory in the RegistryCoordinator library QuorumBitmapHistoryLib { + /// @dev Thrown when the quorum bitmap update is not found + error BitmapUpdateNotFound(); + /// @dev Thrown when quorum bitmap update is after the block number + error BitmapUpdateIsAfterBlockNumber(); + /// @dev Thrown when the next update block number is not 0 and strictly greater than blockNumber + error NextBitmapUpdateIsBeforeBlockNumber(); /// @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 @@ -88,12 +94,12 @@ library QuorumBitmapHistoryLib { */ require( blockNumber >= quorumBitmapUpdate.updateBlockNumber, - "RegistryCoordinator.getQuorumBitmapAtBlockNumberByIndex: quorumBitmapUpdate is from after blockNumber" + BitmapUpdateIsAfterBlockNumber() ); require( quorumBitmapUpdate.nextUpdateBlockNumber == 0 || blockNumber < quorumBitmapUpdate.nextUpdateBlockNumber, - "RegistryCoordinator.getQuorumBitmapAtBlockNumberByIndex: quorumBitmapUpdate is from before blockNumber" + NextBitmapUpdateIsBeforeBlockNumber() ); return quorumBitmapUpdate.quorumBitmap; diff --git a/src/slashers/VetoableSlasher.sol b/src/slashers/VetoableSlasher.sol index 0f9c623f..880b481f 100644 --- a/src/slashers/VetoableSlasher.sol +++ b/src/slashers/VetoableSlasher.sol @@ -32,20 +32,17 @@ contract VetoableSlashing is SlasherBase { function cancelSlashingRequest(uint256 requestId) external virtual onlyVetoCommittee { require( block.timestamp < slashingRequests[requestId].requestTimestamp + VETO_PERIOD, - "VetoableSlashing.cancelSlashingRequest: veto period has passed" + VetoPeriodPassed() ); - require(slashingRequests[requestId].status == SlashingStatus.Requested, "VetoableSlashing.cancelSlashingRequest: request is not in Requested status"); + require(slashingRequests[requestId].status == SlashingStatus.Requested, SlashingRequestNotRequested()); _cancelSlashingRequest(requestId); } function fulfillSlashingRequest(uint256 requestId) external virtual onlySlasher { SlashingRequest storage request = slashingRequests[requestId]; - require( - block.timestamp >= request.requestTimestamp + VETO_PERIOD, - "VetoableSlashing.fulfillSlashingRequest: veto period has not passed" - ); - require(request.status == SlashingStatus.Requested, "VetoableSlashing.fulfillSlashingRequest: request has been cancelled"); + require(block.timestamp >= request.requestTimestamp + VETO_PERIOD, VetoPeriodNotPassed()); + require(request.status == SlashingStatus.Requested, SlashingRequestIsCancelled()); request.status = SlashingStatus.Completed; @@ -72,6 +69,6 @@ contract VetoableSlashing is SlasherBase { } function _checkVetoCommittee(address account) internal view virtual { - require(account == vetoCommittee, "VetoableSlashing._checkVetoCommittee: caller is not the veto committee"); + require(account == vetoCommittee, OnlyVetoCommittee()); } } diff --git a/src/slashers/base/SlasherBase.sol b/src/slashers/base/SlasherBase.sol index 283a764c..f1d46870 100644 --- a/src/slashers/base/SlasherBase.sol +++ b/src/slashers/base/SlasherBase.sol @@ -28,7 +28,7 @@ abstract contract SlasherBase is Initializable, SlasherStorage { } function _checkSlasher(address account) internal view virtual { - require(account == slasher, "SlasherBase._checkSlasher: caller is not the slasher"); + require(account == slasher, OnlySlasher()); } } diff --git a/src/unaudited/ECDSAServiceManagerBase.sol b/src/unaudited/ECDSAServiceManagerBase.sol index 805a961a..e9fd55a3 100644 --- a/src/unaudited/ECDSAServiceManagerBase.sol +++ b/src/unaudited/ECDSAServiceManagerBase.sol @@ -44,10 +44,7 @@ abstract contract ECDSAServiceManagerBase is * This is used to restrict certain registration and deregistration functionality to the `stakeRegistry` */ modifier onlyStakeRegistry() { - require( - msg.sender == stakeRegistry, - "ECDSAServiceManagerBase.onlyStakeRegistry: caller is not the stakeRegistry" - ); + require(msg.sender == stakeRegistry, OnlyStakeRegistry()); _; } @@ -60,10 +57,7 @@ abstract contract ECDSAServiceManagerBase is } function _checkRewardsInitiator() internal view { - require( - msg.sender == rewardsInitiator, - "ECDSAServiceManagerBase.onlyRewardsInitiator: caller is not the rewards initiator" - ); + require(msg.sender == rewardsInitiator, OnlyRewardsInitiator()); } /** diff --git a/test/unit/BLSApkRegistryUnit.t.sol b/test/unit/BLSApkRegistryUnit.t.sol index aacfe9d8..fa644cae 100644 --- a/test/unit/BLSApkRegistryUnit.t.sol +++ b/test/unit/BLSApkRegistryUnit.t.sol @@ -8,6 +8,7 @@ import "../harnesses/BitmapUtilsWrapper.sol"; import "../utils/BLSMockAVSDeployer.sol"; import {IBLSApkRegistryEvents} from "../events/IBLSApkRegistryEvents.sol"; +import {IBLSApkRegistryErrors} from "../../src/interfaces/IBLSApkRegistry.sol"; contract BLSApkRegistryUnitTests is BLSMockAVSDeployer, IBLSApkRegistryEvents { using BitmapUtils for uint192; @@ -309,9 +310,7 @@ contract BLSApkRegistryUnitTests_configAndGetters is BLSApkRegistryUnitTests { cheats.assume(nonCoordinatorAddress != address(registryCoordinator)); cheats.prank(address(nonCoordinatorAddress)); - cheats.expectRevert( - "BLSApkRegistry._checkRegistryCoordinator: caller is not the registry coordinator" - ); + cheats.expectRevert(IBLSApkRegistryErrors.OnlyRegistryCoordinatorOwner.selector); blsApkRegistry.initializeQuorum(defaultQuorumNumber); } } @@ -334,9 +333,7 @@ contract BLSApkRegistryUnitTests_registerBLSPublicKey is .pubkeyRegistrationMessageHash(defaultOperator); cheats.prank(address(nonCoordinatorAddress)); - cheats.expectRevert( - "BLSApkRegistry._checkRegistryCoordinator: caller is not the registry coordinator" - ); + cheats.expectRevert(IBLSApkRegistryErrors.OnlyRegistryCoordinatorOwner.selector); blsApkRegistry.registerBLSPublicKey( defaultOperator, pubkeyRegistrationParams, @@ -353,9 +350,7 @@ contract BLSApkRegistryUnitTests_registerBLSPublicKey is .pubkeyRegistrationMessageHash(operator); cheats.prank(address(registryCoordinator)); - cheats.expectRevert( - "BLSApkRegistry.registerBLSPublicKey: cannot register zero pubkey" - ); + cheats.expectRevert(IBLSApkRegistryErrors.ZeroPubKey.selector); blsApkRegistry.registerBLSPublicKey( operator, pubkeyRegistrationParams, @@ -379,9 +374,7 @@ contract BLSApkRegistryUnitTests_registerBLSPublicKey is messageHash ); - cheats.expectRevert( - "BLSApkRegistry.registerBLSPublicKey: operator already registered pubkey" - ); + cheats.expectRevert(IBLSApkRegistryErrors.OperatorAlreadyRegistered.selector); blsApkRegistry.registerBLSPublicKey( operator, pubkeyRegistrationParams, @@ -412,9 +405,7 @@ contract BLSApkRegistryUnitTests_registerBLSPublicKey is messageHash ); - cheats.expectRevert( - "BLSApkRegistry.registerBLSPublicKey: public key already registered" - ); + cheats.expectRevert(IBLSApkRegistryErrors.BLSPubkeyAlreadyRegistered.selector); blsApkRegistry.registerBLSPublicKey( operator2, pubkeyRegistrationParams, @@ -442,9 +433,7 @@ contract BLSApkRegistryUnitTests_registerBLSPublicKey is pubkeyRegistrationParams.pubkeyRegistrationSignature = invalidSignature; cheats.startPrank(address(registryCoordinator)); - cheats.expectRevert( - "BLSApkRegistry.registerBLSPublicKey: either the G1 signature is wrong, or G1 and G2 private key do not match" - ); + cheats.expectRevert(IBLSApkRegistryErrors.InvalidBLSSignatureOrPrivateKey.selector); blsApkRegistry.registerBLSPublicKey( operator, pubkeyRegistrationParams, @@ -468,9 +457,7 @@ contract BLSApkRegistryUnitTests_registerBLSPublicKey is BN254.G1Point memory messageHash = registryCoordinator .pubkeyRegistrationMessageHash(operator); cheats.prank(address(registryCoordinator)); - cheats.expectRevert( - "BLSApkRegistry.registerBLSPublicKey: either the G1 signature is wrong, or G1 and G2 private key do not match" - ); + cheats.expectRevert(IBLSApkRegistryErrors.InvalidBLSSignatureOrPrivateKey.selector); blsApkRegistry.registerBLSPublicKey( operator, pubkeyRegistrationParams, @@ -544,9 +531,7 @@ contract BLSApkRegistryUnitTests_registerOperator is BLSApkRegistryUnitTests { cheats.assume(nonCoordinatorAddress != address(registryCoordinator)); cheats.prank(nonCoordinatorAddress); - cheats.expectRevert( - "BLSApkRegistry._checkRegistryCoordinator: caller is not the registry coordinator" - ); + cheats.expectRevert(IBLSApkRegistryErrors.OnlyRegistryCoordinatorOwner.selector); blsApkRegistry.registerOperator(nonCoordinatorAddress, new bytes(0)); } @@ -554,9 +539,7 @@ contract BLSApkRegistryUnitTests_registerOperator is BLSApkRegistryUnitTests { address operator ) public filterFuzzedAddressInputs(operator) { cheats.prank(address(registryCoordinator)); - cheats.expectRevert( - "BLSApkRegistry.getRegisteredPubkey: operator is not registered" - ); + cheats.expectRevert(IBLSApkRegistryErrors.OperatorNotRegistered.selector); blsApkRegistry.registerOperator(operator, new bytes(1)); } @@ -577,9 +560,7 @@ contract BLSApkRegistryUnitTests_registerOperator is BLSApkRegistryUnitTests { _registerDefaultBLSPubkey(operator); cheats.prank(address(registryCoordinator)); - cheats.expectRevert( - "BLSApkRegistry._processQuorumApkUpdate: quorum does not exist" - ); + cheats.expectRevert(IBLSApkRegistryErrors.QuorumDoesNotExist.selector); blsApkRegistry.registerOperator(operator, quorumNumbers); } @@ -672,9 +653,7 @@ contract BLSApkRegistryUnitTests_deregisterOperator is BLSApkRegistryUnitTests { cheats.assume(nonCoordinatorAddress != address(registryCoordinator)); cheats.prank(nonCoordinatorAddress); - cheats.expectRevert( - "BLSApkRegistry._checkRegistryCoordinator: caller is not the registry coordinator" - ); + cheats.expectRevert(IBLSApkRegistryErrors.OnlyRegistryCoordinatorOwner.selector); blsApkRegistry.deregisterOperator(nonCoordinatorAddress, new bytes(0)); } @@ -682,9 +661,7 @@ contract BLSApkRegistryUnitTests_deregisterOperator is BLSApkRegistryUnitTests { address operator ) public filterFuzzedAddressInputs(operator) { cheats.prank(address(registryCoordinator)); - cheats.expectRevert( - "BLSApkRegistry.getRegisteredPubkey: operator is not registered" - ); + cheats.expectRevert(IBLSApkRegistryErrors.OperatorNotRegistered.selector); blsApkRegistry.registerOperator(operator, new bytes(1)); } @@ -708,9 +685,7 @@ contract BLSApkRegistryUnitTests_deregisterOperator is BLSApkRegistryUnitTests { _registerOperator(operator, validQuorumNumbers); cheats.prank(address(registryCoordinator)); - cheats.expectRevert( - "BLSApkRegistry._processQuorumApkUpdate: quorum does not exist" - ); + cheats.expectRevert(IBLSApkRegistryErrors.QuorumDoesNotExist.selector); blsApkRegistry.deregisterOperator(operator, invalidQuorumNumbers); } @@ -1080,9 +1055,7 @@ contract BLSApkRegistryUnitTests_quorumApkUpdates is BLSApkRegistryUnitTests { } if (wrongBlockNumber < startingBlockNumber + indexToCheck * 100) { emit log_named_uint("index too recent: ", indexToCheck); - cheats.expectRevert( - "BLSApkRegistry.getApkHashAtBlockNumberAndIndex: index too recent" - ); + cheats.expectRevert(IBLSApkRegistryErrors.BlockNumberTooRecent.selector); blsApkRegistry.getApkHashAtBlockNumberAndIndex( defaultQuorumNumber, wrongBlockNumber, @@ -1093,9 +1066,7 @@ contract BLSApkRegistryUnitTests_quorumApkUpdates is BLSApkRegistryUnitTests { wrongBlockNumber >= startingBlockNumber + (indexToCheck + 1) * 100 ) { emit log_named_uint("index not latest: ", indexToCheck); - cheats.expectRevert( - "BLSApkRegistry.getApkHashAtBlockNumberAndIndex: not latest apk update" - ); + cheats.expectRevert(IBLSApkRegistryErrors.BlockNumberNotLatest.selector); blsApkRegistry.getApkHashAtBlockNumberAndIndex( defaultQuorumNumber, wrongBlockNumber, diff --git a/test/unit/BLSSignatureCheckerUnit.t.sol b/test/unit/BLSSignatureCheckerUnit.t.sol index 74b8f1da..d1d0ac7f 100644 --- a/test/unit/BLSSignatureCheckerUnit.t.sol +++ b/test/unit/BLSSignatureCheckerUnit.t.sol @@ -3,6 +3,10 @@ pragma solidity ^0.8.27; import "../../src/BLSSignatureChecker.sol"; import "../utils/BLSMockAVSDeployer.sol"; +import {IBLSSignatureCheckerErrors} from "../../src/interfaces/IBLSSignatureChecker.sol"; +import {IBLSApkRegistryErrors} from "../../src/interfaces/IBLSApkRegistry.sol"; +import {QuorumBitmapHistoryLib} from "../../src/libraries/QuorumBitmapHistoryLib.sol"; +import {IStakeRegistryErrors} from "../../src/interfaces/IStakeRegistry.sol"; contract BLSSignatureCheckerUnitTests is BLSMockAVSDeployer { using BN254 for BN254.G1Point; @@ -18,7 +22,7 @@ contract BLSSignatureCheckerUnitTests is BLSMockAVSDeployer { } function test_setStaleStakesForbidden_revert_notRegCoordOwner() public { - cheats.expectRevert("BLSSignatureChecker.onlyCoordinatorOwner: caller is not the owner of the registryCoordinator"); + cheats.expectRevert(IBLSSignatureCheckerErrors.OnlyRegistryCoordinatorOwner.selector); blsSignatureChecker.setStaleStakesForbidden(true); } @@ -166,7 +170,7 @@ contract BLSSignatureCheckerUnitTests is BLSMockAVSDeployer { // make one part of the input incorrect length incorrectLengthInputs.quorumApks = new BN254.G1Point[](5); - cheats.expectRevert("BLSSignatureChecker.checkSignatures: input quorum length mismatch"); + cheats.expectRevert(IBLSSignatureCheckerErrors.InputArrayLengthMismatch.selector); blsSignatureChecker.checkSignatures( msgHash, quorumNumbers, @@ -178,7 +182,7 @@ contract BLSSignatureCheckerUnitTests is BLSMockAVSDeployer { incorrectLengthInputs.quorumApks = nonSignerStakesAndSignature.quorumApks; // make one part of the input incorrect length incorrectLengthInputs.quorumApkIndices = new uint32[](5); - cheats.expectRevert("BLSSignatureChecker.checkSignatures: input quorum length mismatch"); + cheats.expectRevert(IBLSSignatureCheckerErrors.InputArrayLengthMismatch.selector); blsSignatureChecker.checkSignatures( msgHash, quorumNumbers, @@ -190,7 +194,7 @@ contract BLSSignatureCheckerUnitTests is BLSMockAVSDeployer { incorrectLengthInputs.quorumApkIndices = nonSignerStakesAndSignature.quorumApkIndices; // make one part of the input incorrect length incorrectLengthInputs.totalStakeIndices = new uint32[](5); - cheats.expectRevert("BLSSignatureChecker.checkSignatures: input quorum length mismatch"); + cheats.expectRevert(IBLSSignatureCheckerErrors.InputArrayLengthMismatch.selector); blsSignatureChecker.checkSignatures( msgHash, quorumNumbers, @@ -202,7 +206,7 @@ contract BLSSignatureCheckerUnitTests is BLSMockAVSDeployer { incorrectLengthInputs.totalStakeIndices = nonSignerStakesAndSignature.totalStakeIndices; // make one part of the input incorrect length incorrectLengthInputs.nonSignerStakeIndices = new uint32[][](5); - cheats.expectRevert("BLSSignatureChecker.checkSignatures: input quorum length mismatch"); + cheats.expectRevert(IBLSSignatureCheckerErrors.InputArrayLengthMismatch.selector); blsSignatureChecker.checkSignatures( msgHash, quorumNumbers, @@ -214,7 +218,7 @@ contract BLSSignatureCheckerUnitTests is BLSMockAVSDeployer { incorrectLengthInputs.nonSignerStakeIndices = nonSignerStakesAndSignature.nonSignerStakeIndices; // make one part of the input incorrect length incorrectLengthInputs.nonSignerQuorumBitmapIndices = new uint32[](nonSignerStakesAndSignature.nonSignerPubkeys.length + 1); - cheats.expectRevert("BLSSignatureChecker.checkSignatures: input nonsigner length mismatch"); + cheats.expectRevert(IBLSSignatureCheckerErrors.InputNonSignerLengthMismatch.selector); blsSignatureChecker.checkSignatures( msgHash, quorumNumbers, @@ -243,7 +247,7 @@ contract BLSSignatureCheckerUnitTests is BLSMockAVSDeployer { // Create an invalid reference block: any block number >= the current block uint32 invalidReferenceBlock = uint32(block.number + (pseudoRandomNumber % 20)); - cheats.expectRevert("BLSSignatureChecker.checkSignatures: invalid reference block"); + cheats.expectRevert(IBLSSignatureCheckerErrors.InvalidReferenceBlocknumber.selector); blsSignatureChecker.checkSignatures( msgHash, quorumNumbers, @@ -263,7 +267,7 @@ contract BLSSignatureCheckerUnitTests is BLSMockAVSDeployer { // swap out a pubkey to make sure there is a duplicate nonSignerStakesAndSignature.nonSignerPubkeys[1] = nonSignerStakesAndSignature.nonSignerPubkeys[0]; - cheats.expectRevert("BLSSignatureChecker.checkSignatures: nonSignerPubkeys not sorted"); + cheats.expectRevert(IBLSSignatureCheckerErrors.NonSignerPubkeysNotSorted.selector); blsSignatureChecker.checkSignatures( msgHash, quorumNumbers, @@ -284,7 +288,7 @@ contract BLSSignatureCheckerUnitTests is BLSMockAVSDeployer { // swap two pubkeys to ensure ordering is wrong (nonSignerStakesAndSignature.nonSignerPubkeys[0], nonSignerStakesAndSignature.nonSignerPubkeys[1]) = (nonSignerStakesAndSignature.nonSignerPubkeys[1], nonSignerStakesAndSignature.nonSignerPubkeys[0]); - cheats.expectRevert("BLSSignatureChecker.checkSignatures: nonSignerPubkeys not sorted"); + cheats.expectRevert(IBLSSignatureCheckerErrors.NonSignerPubkeysNotSorted.selector); blsSignatureChecker.checkSignatures( msgHash, quorumNumbers, @@ -330,7 +334,7 @@ contract BLSSignatureCheckerUnitTests is BLSMockAVSDeployer { referenceBlockNumber += 1; // roll forward to reference + 1 to ensure the referenceBlockNumber is still valid cheats.roll(referenceBlockNumber + 1); - cheats.expectRevert("BLSSignatureChecker.checkSignatures: StakeRegistry updates must be within withdrawalDelayBlocks window"); + cheats.expectRevert(IBLSSignatureCheckerErrors.StaleStakesForbidden.selector); blsSignatureChecker.checkSignatures( msgHash, quorumNumbers, @@ -353,7 +357,7 @@ contract BLSSignatureCheckerUnitTests is BLSMockAVSDeployer { // set the nonSignerQuorumBitmapIndices to a different value nonSignerStakesAndSignature.nonSignerQuorumBitmapIndices[0] = 1; - cheats.expectRevert("RegistryCoordinator.getQuorumBitmapAtBlockNumberByIndex: quorumBitmapUpdate is from after blockNumber"); + cheats.expectRevert(QuorumBitmapHistoryLib.BitmapUpdateIsAfterBlockNumber.selector); blsSignatureChecker.checkSignatures( msgHash, quorumNumbers, @@ -373,7 +377,7 @@ contract BLSSignatureCheckerUnitTests is BLSMockAVSDeployer { // set the totalStakeIndices to a different value nonSignerStakesAndSignature.totalStakeIndices[0] = 0; - cheats.expectRevert("StakeRegistry._validateStakeUpdateAtBlockNumber: there is a newer stakeUpdate available before blockNumber"); + cheats.expectRevert(IStakeRegistryErrors.InvalidBlockNumber.selector); blsSignatureChecker.checkSignatures( msgHash, quorumNumbers, @@ -402,7 +406,7 @@ contract BLSSignatureCheckerUnitTests is BLSMockAVSDeployer { // set the nonSignerStakeIndices to a different value nonSignerStakesAndSignature.nonSignerStakeIndices[0][0] = 1; - cheats.expectRevert("StakeRegistry._validateStakeUpdateAtBlockNumber: stakeUpdate is from after blockNumber"); + cheats.expectRevert(IStakeRegistryErrors.InvalidBlockNumber.selector); blsSignatureChecker.checkSignatures( msgHash, quorumNumbers, @@ -423,7 +427,7 @@ contract BLSSignatureCheckerUnitTests is BLSMockAVSDeployer { // set the quorumApkIndices to a different value nonSignerStakesAndSignature.quorumApkIndices[0] = 0; - cheats.expectRevert("BLSApkRegistry.getApkHashAtBlockNumberAndIndex: not latest apk update"); + cheats.expectRevert(IBLSApkRegistryErrors.BlockNumberNotLatest.selector); blsSignatureChecker.checkSignatures( msgHash, quorumNumbers, @@ -443,7 +447,7 @@ contract BLSSignatureCheckerUnitTests is BLSMockAVSDeployer { // set the quorumApk to a different value nonSignerStakesAndSignature.quorumApks[0] = nonSignerStakesAndSignature.quorumApks[0].negate(); - cheats.expectRevert("BLSSignatureChecker.checkSignatures: quorumApk hash in storage does not match provided quorum apk"); + cheats.expectRevert(IBLSSignatureCheckerErrors.InvalidQuorumApkHash.selector); blsSignatureChecker.checkSignatures( msgHash, quorumNumbers, @@ -463,7 +467,7 @@ contract BLSSignatureCheckerUnitTests is BLSMockAVSDeployer { // set the sigma to a different value nonSignerStakesAndSignature.sigma = nonSignerStakesAndSignature.sigma.negate(); - cheats.expectRevert("BLSSignatureChecker.checkSignatures: signature is invalid"); + cheats.expectRevert(IBLSSignatureCheckerErrors.InvalidBLSSignature.selector); blsSignatureChecker.checkSignatures( msgHash, quorumNumbers, @@ -505,7 +509,7 @@ contract BLSSignatureCheckerUnitTests is BLSMockAVSDeployer { bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(0); // expect a non-specific low-level revert, since this call will ultimately fail as part of the precompile call - cheats.expectRevert("BLSSignatureChecker.checkSignatures: empty quorum input"); + cheats.expectRevert(IBLSSignatureCheckerErrors.InputEmptyQuorumNumbers.selector); blsSignatureChecker.checkSignatures( msgHash, quorumNumbers, diff --git a/test/unit/BitmapUtils.t.sol b/test/unit/BitmapUtils.t.sol index fcd742b2..a998dbe3 100644 --- a/test/unit/BitmapUtils.t.sol +++ b/test/unit/BitmapUtils.t.sol @@ -185,7 +185,7 @@ contract BitmapUtilsUnitTests_bytesArrayToBitmap is BitmapUtilsUnitTests { /// when calling orderedBytesArrayToBitmap function testFuzz_OrderedBytesArrayToBitmap_Revert_WhenNotOrdered(bytes memory originalBytesArray) public { cheats.assume(!bitmapUtilsWrapper.isArrayStrictlyAscendingOrdered(originalBytesArray)); - cheats.expectRevert("BitmapUtils.orderedBytesArrayToBitmap: orderedBytesArray is not ordered"); + cheats.expectRevert(BitmapUtils.BytesArrayNotOrdered.selector); bitmapUtilsWrapper.orderedBytesArrayToBitmap(originalBytesArray); } diff --git a/test/unit/EjectionManagerUnit.t.sol b/test/unit/EjectionManagerUnit.t.sol index 9f01af50..a95366f7 100644 --- a/test/unit/EjectionManagerUnit.t.sol +++ b/test/unit/EjectionManagerUnit.t.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.27; import {EjectionManager} from "../../src/EjectionManager.sol"; -import {IEjectionManager} from "../../src/interfaces/IEjectionManager.sol"; +import {IEjectionManager, IEjectionManagerErrors} from "../../src/interfaces/IEjectionManager.sol"; import "../utils/MockAVSDeployer.sol"; @@ -366,7 +366,7 @@ contract EjectionManagerUnitTests is MockAVSDeployer { function test_Revert_NotPermissioned() public { bytes32[][] memory operatorIds; - cheats.expectRevert("EjectionManager.ejectOperators: Only owner or ejector can eject"); + cheats.expectRevert(IEjectionManagerErrors.OnlyOwnerOrEjector.selector); ejectionManager.ejectOperators(operatorIds); EjectionManager.QuorumEjectionParams memory _quorumEjectionParams; diff --git a/test/unit/IndexRegistryUnit.t.sol b/test/unit/IndexRegistryUnit.t.sol index 36d7dc69..79d74e91 100644 --- a/test/unit/IndexRegistryUnit.t.sol +++ b/test/unit/IndexRegistryUnit.t.sol @@ -293,6 +293,7 @@ contract IndexRegistryUnitTests_configAndGetters is IndexRegistryUnitTests { // should revert with startBlocknumber cheats.expectRevert("IndexRegistry._operatorCountAtBlockNumber: quorum did not exist at given block number"); + indexRegistry.getOperatorListAtBlockNumber( quorumNumber, startBlockNumber @@ -418,7 +419,7 @@ contract IndexRegistryUnitTests_registerOperator is IndexRegistryUnitTests { bytes memory quorumNumbers = new bytes(defaultQuorumNumber); cheats.prank(nonRegistryCoordinator); - cheats.expectRevert("IndexRegistry._checkRegistryCoordinator: caller is not the registry coordinator"); + cheats.expectRevert(IIndexRegistryErrors.OnlyRegistryCoordinator.selector); indexRegistry.registerOperator(bytes32(0), quorumNumbers); } @@ -439,7 +440,7 @@ contract IndexRegistryUnitTests_registerOperator is IndexRegistryUnitTests { // Register for invalid quorums, should revert bytes memory invalidQuorumNumbers = bitmapUtilsWrapper.bitmapToBytesArray(invalidBitmap); cheats.prank(address(registryCoordinator)); - cheats.expectRevert("IndexRegistry.registerOperator: quorum does not exist"); + cheats.expectRevert(IIndexRegistryErrors.QuorumDoesNotExist.selector); indexRegistry.registerOperator(operatorId1, invalidQuorumNumbers); } @@ -699,7 +700,7 @@ contract IndexRegistryUnitTests_deregisterOperator is IndexRegistryUnitTests { bytes memory quorumNumbers = new bytes(defaultQuorumNumber); cheats.prank(nonRegistryCoordinator); - cheats.expectRevert("IndexRegistry._checkRegistryCoordinator: caller is not the registry coordinator"); + cheats.expectRevert(IIndexRegistryErrors.OnlyRegistryCoordinator.selector); indexRegistry.deregisterOperator(bytes32(0), quorumNumbers); } @@ -724,7 +725,7 @@ contract IndexRegistryUnitTests_deregisterOperator is IndexRegistryUnitTests { // Deregister for invalid quorums, should revert cheats.prank(address(registryCoordinator)); - cheats.expectRevert("IndexRegistry.registerOperator: quorum does not exist"); + cheats.expectRevert(IIndexRegistryErrors.QuorumDoesNotExist.selector); indexRegistry.deregisterOperator(operatorId1, invalidQuorumNumbers); } diff --git a/test/unit/OperatorStateRetrieverUnit.t.sol b/test/unit/OperatorStateRetrieverUnit.t.sol index e25c8af0..55f6aabd 100644 --- a/test/unit/OperatorStateRetrieverUnit.t.sol +++ b/test/unit/OperatorStateRetrieverUnit.t.sol @@ -2,6 +2,7 @@ pragma solidity ^0.8.27; import "../utils/MockAVSDeployer.sol"; +import {IStakeRegistryErrors} from "../../src/interfaces/IStakeRegistry.sol"; contract OperatorStateRetrieverUnitTests is MockAVSDeployer { using BN254 for BN254.G1Point; @@ -186,9 +187,7 @@ contract OperatorStateRetrieverUnitTests is MockAVSDeployer { registryCoordinator.deregisterOperator(BitmapUtils.bitmapToBytesArray(1)); // should revert because the operator was registered for the first time after the reference block number - cheats.expectRevert( - "OperatorStateRetriever.getCheckSignaturesIndices: operator must be registered at blocknumber" - ); + cheats.expectRevert(OperatorStateRetriever.OperatorNotRegistered.selector); operatorStateRetriever.getCheckSignaturesIndices( registryCoordinator, uint32(block.number), @@ -203,7 +202,7 @@ contract OperatorStateRetrieverUnitTests is MockAVSDeployer { _registerOperatorWithCoordinator(defaultOperator, 1, defaultPubKey); - cheats.expectRevert("StakeRegistry.quorumExists: quorum does not exist"); + cheats.expectRevert(IStakeRegistryErrors.QuorumDoesNotExist.selector); operatorStateRetriever.getCheckSignaturesIndices( registryCoordinator, uint32(block.number), @@ -237,9 +236,7 @@ contract OperatorStateRetrieverUnitTests is MockAVSDeployer { cheats.prank(registryCoordinator.owner()); registryCoordinator.createTotalDelegatedStakeQuorum(operatorSetParams, minimumStake, strategyParams); - cheats.expectRevert( - "StakeRegistry.getTotalStakeIndicesAtBlockNumber: quorum has no stake history at blockNumber" - ); + cheats.expectRevert(IStakeRegistryErrors.EmptyStakeHistory.selector); operatorStateRetriever.getCheckSignaturesIndices( registryCoordinator, registrationBlockNumber + 5, diff --git a/test/unit/RegistryCoordinatorUnit.t.sol b/test/unit/RegistryCoordinatorUnit.t.sol index c3c8032d..72207dcc 100644 --- a/test/unit/RegistryCoordinatorUnit.t.sol +++ b/test/unit/RegistryCoordinatorUnit.t.sol @@ -3,6 +3,8 @@ pragma solidity ^0.8.27; import "../utils/MockAVSDeployer.sol"; import {IRegistryCoordinatorErrors} from "../../src/interfaces/IRegistryCoordinator.sol"; +import {QuorumBitmapHistoryLib} from "../../src/libraries/QuorumBitmapHistoryLib.sol"; +import {BitmapUtils} from "../../src/libraries/BitmapUtils.sol"; contract RegistryCoordinatorUnitTests is MockAVSDeployer { using BN254 for BN254.G1Point; @@ -281,7 +283,7 @@ contract RegistryCoordinatorUnitTests_RegisterOperator is RegistryCoordinatorUni quorumNumbersTooLarge[0] = 0xC0; - cheats.expectRevert("BitmapUtils.orderedBytesArrayToBitmap: bitmap exceeds max value"); + cheats.expectRevert(BitmapUtils.BitmapValueTooLarge.selector); cheats.prank(defaultOperator); registryCoordinator.registerOperator(quorumNumbersTooLarge, defaultSocket, pubkeyRegistrationParams, emptySig); } @@ -294,7 +296,7 @@ contract RegistryCoordinatorUnitTests_RegisterOperator is RegistryCoordinatorUni quorumNumbersNotCreated[0] = 0x0B; cheats.prank(defaultOperator); - cheats.expectRevert("BitmapUtils.orderedBytesArrayToBitmap: bitmap exceeds max value"); + cheats.expectRevert(BitmapUtils.BitmapValueTooLarge.selector); registryCoordinator.registerOperator(quorumNumbersNotCreated, defaultSocket, pubkeyRegistrationParams, emptySig); } @@ -523,7 +525,7 @@ contract RegistryCoordinatorUnitTests_RegisterOperator is RegistryCoordinatorUni quorumNumbersTooLarge[0] = 0xC0; - cheats.expectRevert("BitmapUtils.orderedBytesArrayToBitmap: bitmap exceeds max value"); + cheats.expectRevert(BitmapUtils.BitmapValueTooLarge.selector); registryCoordinator._registerOperatorExternal(defaultOperator, defaultOperatorId, quorumNumbersTooLarge, defaultSocket, emptySig); } @@ -1298,7 +1300,7 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist uint192 emptyBitmap = 0; // try an incorrect blockNumber input and confirm reversion - cheats.expectRevert("RegistryCoordinator.getQuorumBitmapAtBlockNumberByIndex: quorumBitmapUpdate is from after blockNumber"); + cheats.expectRevert(QuorumBitmapHistoryLib.BitmapUpdateIsAfterBlockNumber.selector); uint192 returnVal = registryCoordinator.getQuorumBitmapAtBlockNumberByIndex(operatorId, blockNumber, index); blockNumber = registrationBlockNumber; @@ -1311,7 +1313,7 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist // try an incorrect index input and confirm reversion index = 1; - cheats.expectRevert("RegistryCoordinator.getQuorumBitmapAtBlockNumberByIndex: quorumBitmapUpdate is from after blockNumber"); + cheats.expectRevert(QuorumBitmapHistoryLib.BitmapUpdateIsAfterBlockNumber.selector); returnVal = registryCoordinator.getQuorumBitmapAtBlockNumberByIndex(operatorId, blockNumber, index); blockNumber = deregistrationBlockNumber; @@ -1324,7 +1326,7 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist // try an incorrect index input and confirm reversion index = 0; - cheats.expectRevert("RegistryCoordinator.getQuorumBitmapAtBlockNumberByIndex: quorumBitmapUpdate is from before blockNumber"); + cheats.expectRevert(QuorumBitmapHistoryLib.NextBitmapUpdateIsBeforeBlockNumber.selector); returnVal = registryCoordinator.getQuorumBitmapAtBlockNumberByIndex(operatorId, blockNumber, index); } } @@ -1669,7 +1671,7 @@ contract RegistryCoordinatorUnitTests_UpdateOperators is RegistryCoordinatorUnit address[][] memory operatorsToUpdate = new address[][](1); cheats.prank(defaultOperator); - cheats.expectRevert("BitmapUtils.orderedBytesArrayToBitmap: bitmap exceeds max value"); + cheats.expectRevert(BitmapUtils.BitmapValueTooLarge.selector); registryCoordinator.updateOperatorsForQuorum(operatorsToUpdate, quorumNumbersNotCreated); } diff --git a/test/unit/ServiceManagerBase.t.sol b/test/unit/ServiceManagerBase.t.sol index 8d37f9d6..ca3d0e9f 100644 --- a/test/unit/ServiceManagerBase.t.sol +++ b/test/unit/ServiceManagerBase.t.sol @@ -12,6 +12,7 @@ import {PermissionController} from "eigenlayer-contracts/src/contracts/permissio 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 {IServiceManagerErrors} from "../../src/interfaces/IServiceManager.sol"; import "../utils/MockAVSDeployer.sol"; @@ -225,9 +226,7 @@ contract ServiceManagerBase_UnitTests is MockAVSDeployer, IServiceManagerBaseEve IRewardsCoordinator.RewardsSubmission[] memory rewardsSubmissions; cheats.prank(caller); - cheats.expectRevert( - "ServiceManagerBase.onlyRewardsInitiator: caller is not the rewards initiator" - ); + cheats.expectRevert(IServiceManagerErrors.OnlyRewardsInitiator.selector); serviceManager.createAVSRewardsSubmission(rewardsSubmissions); } diff --git a/test/unit/StakeRegistryUnit.t.sol b/test/unit/StakeRegistryUnit.t.sol index 7e8f483e..a21a8c39 100644 --- a/test/unit/StakeRegistryUnit.t.sol +++ b/test/unit/StakeRegistryUnit.t.sol @@ -6,7 +6,7 @@ import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.so import "test/utils/MockAVSDeployer.sol"; import {StakeRegistry} from "src/StakeRegistry.sol"; -import {IStakeRegistry} from "src/interfaces/IStakeRegistry.sol"; +import {IStakeRegistry, IStakeRegistryErrors} from "src/interfaces/IStakeRegistry.sol"; import {IStakeRegistryEvents} from "test/events/IStakeRegistryEvents.sol"; import "../utils/MockAVSDeployer.sol"; @@ -580,9 +580,7 @@ contract StakeRegistryUnitTests_Config is StakeRegistryUnitTests { uint96 minimumStake, IStakeRegistry.StrategyParams[] memory strategyParams ) public { - cheats.expectRevert( - "StakeRegistry.onlyRegistryCoordinator: caller is not the RegistryCoordinator" - ); + cheats.expectRevert(IStakeRegistryErrors.OnlyRegistryCoordinator.selector); stakeRegistry.initializeDelegatedStakeQuorum(quorumNumber, minimumStake, strategyParams); } @@ -591,7 +589,7 @@ contract StakeRegistryUnitTests_Config is StakeRegistryUnitTests { uint96 minimumStake, IStakeRegistry.StrategyParams[] memory strategyParams ) public fuzzOnlyInitializedQuorums(quorumNumber) { - cheats.expectRevert("StakeRegistry.initializeQuorum: quorum already exists"); + cheats.expectRevert(IStakeRegistryErrors.QuorumAlreadyExists.selector); cheats.prank(address(registryCoordinator)); stakeRegistry.initializeDelegatedStakeQuorum(quorumNumber, minimumStake, strategyParams); } @@ -603,7 +601,7 @@ contract StakeRegistryUnitTests_Config is StakeRegistryUnitTests { cheats.assume(quorumNumber >= nextQuorum); IStakeRegistry.StrategyParams[] memory strategyParams = new IStakeRegistry.StrategyParams[](0); - cheats.expectRevert("StakeRegistry._addStrategyParams: no strategies provided"); + cheats.expectRevert(IStakeRegistryErrors.InputArrayLengthZero.selector); cheats.prank(address(registryCoordinator)); stakeRegistry.initializeDelegatedStakeQuorum(quorumNumber, minimumStake, strategyParams); @@ -613,7 +611,7 @@ contract StakeRegistryUnitTests_Config is StakeRegistryUnitTests { IStrategy(address(uint160(uint256(keccak256(abi.encodePacked(i)))))), uint96(1) ); } - cheats.expectRevert("StakeRegistry._addStrategyParams: exceed MAX_WEIGHING_FUNCTION_LENGTH"); + cheats.expectRevert(IStakeRegistryErrors.InputArrayLengthMismatch.selector); cheats.prank(address(registryCoordinator)); stakeRegistry.initializeDelegatedStakeQuorum(quorumNumber, minimumStake, strategyParams); } @@ -731,9 +729,7 @@ contract StakeRegistryUnitTests_Config is StakeRegistryUnitTests { uint8 quorumNumber, uint96 minimumStakeForQuorum ) public fuzzOnlyInitializedQuorums(quorumNumber) { - cheats.expectRevert( - "StakeRegistry.onlyCoordinatorOwner: caller is not the owner of the registryCoordinator" - ); + cheats.expectRevert(IStakeRegistryErrors.OnlyRegistryCoordinatorOwner.selector); stakeRegistry.setMinimumStakeForQuorum(quorumNumber, minimumStakeForQuorum); } @@ -743,7 +739,7 @@ contract StakeRegistryUnitTests_Config is StakeRegistryUnitTests { ) public { // quorums [0,nextQuorum) are initialized, so use an invalid quorumNumber cheats.assume(quorumNumber >= nextQuorum); - cheats.expectRevert("StakeRegistry.quorumExists: quorum does not exist"); + cheats.expectRevert(IStakeRegistryErrors.QuorumDoesNotExist.selector); cheats.prank(registryCoordinatorOwner); stakeRegistry.setMinimumStakeForQuorum(quorumNumber, minimumStakeForQuorum); } @@ -771,9 +767,7 @@ contract StakeRegistryUnitTests_Config is StakeRegistryUnitTests { uint8 quorumNumber, IStakeRegistry.StrategyParams[] memory strategyParams ) public fuzzOnlyInitializedQuorums(quorumNumber) { - cheats.expectRevert( - "StakeRegistry.onlyCoordinatorOwner: caller is not the owner of the registryCoordinator" - ); + cheats.expectRevert(IStakeRegistryErrors.OnlyRegistryCoordinatorOwner.selector); stakeRegistry.addStrategies(quorumNumber, strategyParams); } @@ -783,7 +777,7 @@ contract StakeRegistryUnitTests_Config is StakeRegistryUnitTests { ) public { // quorums [0,nextQuorum) are initialized, so use an invalid quorumNumber cheats.assume(quorumNumber >= nextQuorum); - cheats.expectRevert("StakeRegistry.quorumExists: quorum does not exist"); + cheats.expectRevert(IStakeRegistryErrors.QuorumDoesNotExist.selector); cheats.prank(registryCoordinatorOwner); stakeRegistry.addStrategies(quorumNumber, strategyParams); } @@ -798,7 +792,7 @@ contract StakeRegistryUnitTests_Config is StakeRegistryUnitTests { strategyParams[0] = IStakeRegistry.StrategyParams(strat, uint96(WEIGHTING_DIVISOR)); strategyParams[1] = IStakeRegistry.StrategyParams(strat, uint96(WEIGHTING_DIVISOR)); - cheats.expectRevert("StakeRegistry._addStrategyParams: cannot add same strategy 2x"); + cheats.expectRevert(IStakeRegistryErrors.InputDuplicateStrategy.selector); cheats.prank(registryCoordinatorOwner); stakeRegistry.addStrategies(quorumNumber, strategyParams); } @@ -812,9 +806,7 @@ contract StakeRegistryUnitTests_Config is StakeRegistryUnitTests { new IStakeRegistry.StrategyParams[](2); strategyParams[0] = IStakeRegistry.StrategyParams(strat, 0); - cheats.expectRevert( - "StakeRegistry._addStrategyParams: cannot add strategy with zero weight" - ); + cheats.expectRevert(IStakeRegistryErrors.InputMultiplierZero.selector); cheats.prank(registryCoordinatorOwner); stakeRegistry.addStrategies(quorumNumber, strategyParams); } @@ -874,9 +866,7 @@ contract StakeRegistryUnitTests_Config is StakeRegistryUnitTests { uint8 quorumNumber, uint256[] memory indicesToRemove ) public fuzzOnlyInitializedQuorums(quorumNumber) { - cheats.expectRevert( - "StakeRegistry.onlyCoordinatorOwner: caller is not the owner of the registryCoordinator" - ); + cheats.expectRevert(IStakeRegistryErrors.OnlyRegistryCoordinatorOwner.selector); stakeRegistry.removeStrategies(quorumNumber, indicesToRemove); } @@ -886,7 +876,7 @@ contract StakeRegistryUnitTests_Config is StakeRegistryUnitTests { ) public { // quorums [0,nextQuorum) are initialized, so use an invalid quorumNumber cheats.assume(quorumNumber >= nextQuorum); - cheats.expectRevert("StakeRegistry.quorumExists: quorum does not exist"); + cheats.expectRevert(IStakeRegistryErrors.QuorumDoesNotExist.selector); cheats.prank(registryCoordinatorOwner); stakeRegistry.removeStrategies(quorumNumber, indicesToRemove); } @@ -916,7 +906,7 @@ contract StakeRegistryUnitTests_Config is StakeRegistryUnitTests { uint8 quorumNumber = _initializeQuorum(minimumStake, numStrategiesToAdd); uint256[] memory indicesToRemove = new uint256[](0); - cheats.expectRevert("StakeRegistry.removeStrategies: no indices to remove provided"); + cheats.expectRevert(IStakeRegistryErrors.InputArrayLengthZero.selector); cheats.prank(registryCoordinatorOwner); stakeRegistry.removeStrategies(quorumNumber, indicesToRemove); } @@ -977,9 +967,7 @@ contract StakeRegistryUnitTests_Config is StakeRegistryUnitTests { uint256[] calldata strategyIndices, uint96[] calldata newMultipliers ) public fuzzOnlyInitializedQuorums(quorumNumber) { - cheats.expectRevert( - "StakeRegistry.onlyCoordinatorOwner: caller is not the owner of the registryCoordinator" - ); + cheats.expectRevert(IStakeRegistryErrors.OnlyRegistryCoordinatorOwner.selector); stakeRegistry.modifyStrategyParams(quorumNumber, strategyIndices, newMultipliers); } @@ -990,7 +978,7 @@ contract StakeRegistryUnitTests_Config is StakeRegistryUnitTests { ) public { // quorums [0,nextQuorum) are initialized, so use an invalid quorumNumber cheats.assume(quorumNumber >= nextQuorum); - cheats.expectRevert("StakeRegistry.quorumExists: quorum does not exist"); + cheats.expectRevert(IStakeRegistryErrors.QuorumDoesNotExist.selector); cheats.prank(registryCoordinatorOwner); stakeRegistry.modifyStrategyParams(quorumNumber, strategyIndices, newMultipliers); } @@ -1001,7 +989,7 @@ contract StakeRegistryUnitTests_Config is StakeRegistryUnitTests { { uint256[] memory strategyIndices = new uint256[](0); uint96[] memory newMultipliers = new uint96[](0); - cheats.expectRevert("StakeRegistry.modifyStrategyParams: no strategy indices provided"); + cheats.expectRevert(IStakeRegistryErrors.InputArrayLengthZero.selector); cheats.prank(registryCoordinatorOwner); stakeRegistry.modifyStrategyParams(quorumNumber, strategyIndices, newMultipliers); } @@ -1013,7 +1001,7 @@ contract StakeRegistryUnitTests_Config is StakeRegistryUnitTests { ) public fuzzOnlyInitializedQuorums(quorumNumber) { cheats.assume(strategyIndices.length != newMultipliers.length); cheats.assume(strategyIndices.length > 0); - cheats.expectRevert("StakeRegistry.modifyStrategyParams: input length mismatch"); + cheats.expectRevert(IStakeRegistryErrors.InputArrayLengthMismatch.selector); cheats.prank(registryCoordinatorOwner); stakeRegistry.modifyStrategyParams(quorumNumber, strategyIndices, newMultipliers); } @@ -1072,9 +1060,7 @@ contract StakeRegistryUnitTests_Register is StakeRegistryUnitTests { function test_registerOperator_Revert_WhenNotRegistryCoordinator() public { (address operator, bytes32 operatorId) = _selectNewOperator(); - cheats.expectRevert( - "StakeRegistry.onlyRegistryCoordinator: caller is not the RegistryCoordinator" - ); + cheats.expectRevert(IStakeRegistryErrors.OnlyRegistryCoordinator.selector); stakeRegistry.registerOperator(operator, operatorId, initializedQuorumBytes); } @@ -1084,7 +1070,7 @@ contract StakeRegistryUnitTests_Register is StakeRegistryUnitTests { // Get a list of valid quorums ending in an invalid quorum number bytes memory invalidQuorums = _fuzz_getInvalidQuorums(rand); - cheats.expectRevert("StakeRegistry.quorumExists: quorum does not exist"); + cheats.expectRevert(IStakeRegistryErrors.QuorumDoesNotExist.selector); cheats.prank(address(registryCoordinator)); stakeRegistry.registerOperator(setup.operator, setup.operatorId, invalidQuorums); } @@ -1118,9 +1104,7 @@ contract StakeRegistryUnitTests_Register is StakeRegistryUnitTests { } // Attempt to register - cheats.expectRevert( - "StakeRegistry.registerOperator: Operator does not meet minimum stake requirement for quorum" - ); + cheats.expectRevert(IStakeRegistryErrors.BelowMinimumStakeRequirement.selector); cheats.prank(address(registryCoordinator)); stakeRegistry.registerOperator(operator, operatorId, quorumNumbers); } @@ -1426,9 +1410,7 @@ contract StakeRegistryUnitTests_Deregister is StakeRegistryUnitTests { fuzzy_addtlStake: 0 }); - cheats.expectRevert( - "StakeRegistry.onlyRegistryCoordinator: caller is not the RegistryCoordinator" - ); + cheats.expectRevert(IStakeRegistryErrors.OnlyRegistryCoordinator.selector); stakeRegistry.deregisterOperator(setup.operatorId, setup.quorumsToRemove); } @@ -1443,7 +1425,7 @@ contract StakeRegistryUnitTests_Deregister is StakeRegistryUnitTests { // Get a list of valid quorums ending in an invalid quorum number bytes memory invalidQuorums = _fuzz_getInvalidQuorums(rand); - cheats.expectRevert("StakeRegistry.quorumExists: quorum does not exist"); + cheats.expectRevert(IStakeRegistryErrors.QuorumDoesNotExist.selector); cheats.prank(address(registryCoordinator)); stakeRegistry.registerOperator(setup.operator, setup.operatorId, invalidQuorums); } @@ -1790,9 +1772,7 @@ contract StakeRegistryUnitTests_StakeUpdates is StakeRegistryUnitTests { UpdateSetup memory setup = _fuzz_setupUpdateOperatorStake({registeredFor: initializedQuorumBitmap, fuzzy_Delta: 0}); - cheats.expectRevert( - "StakeRegistry.onlyRegistryCoordinator: caller is not the RegistryCoordinator" - ); + cheats.expectRevert(IStakeRegistryErrors.OnlyRegistryCoordinator.selector); stakeRegistry.updateOperatorStake(setup.operator, setup.operatorId, setup.quorumNumbers); } @@ -1804,7 +1784,7 @@ contract StakeRegistryUnitTests_StakeUpdates is StakeRegistryUnitTests { // Get a list of valid quorums ending in an invalid quorum number bytes memory invalidQuorums = _fuzz_getInvalidQuorums(rand); - cheats.expectRevert("StakeRegistry.quorumExists: quorum does not exist"); + cheats.expectRevert(IStakeRegistryErrors.QuorumDoesNotExist.selector); cheats.prank(address(registryCoordinator)); stakeRegistry.updateOperatorStake(setup.operator, setup.operatorId, invalidQuorums); } From 34456b31e21d57120c31ec14db215317f7b7c119 Mon Sep 17 00:00:00 2001 From: steven Date: Fri, 10 Jan 2025 16:38:42 -0500 Subject: [PATCH 70/80] chore: review remove setStakeType --- src/StakeRegistry.sol | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/StakeRegistry.sol b/src/StakeRegistry.sol index 3d2b5089..4896ec7c 100644 --- a/src/StakeRegistry.sol +++ b/src/StakeRegistry.sol @@ -234,15 +234,6 @@ contract StakeRegistry is StakeRegistryStorage { _setMinimumStakeForQuorum(quorumNumber, minimumStake); } - /** - * @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(uint8 quorumNumber, StakeType _stakeType) external onlyCoordinatorOwner quorumExists(quorumNumber) { - _setStakeType(quorumNumber, _stakeType); - } - /** * @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 From 2246b7d35a74192aa5351d58eef911abf8726847 Mon Sep 17 00:00:00 2001 From: steven Date: Fri, 10 Jan 2025 17:07:44 -0500 Subject: [PATCH 71/80] fix: wire up stake registry to call add and rm strategies for op set --- src/StakeRegistry.sol | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/StakeRegistry.sol b/src/StakeRegistry.sol index 4896ec7c..162a9149 100644 --- a/src/StakeRegistry.sol +++ b/src/StakeRegistry.sol @@ -254,6 +254,16 @@ contract StakeRegistry is StakeRegistryStorage { StrategyParams[] memory _strategyParams ) public virtual onlyCoordinatorOwner quorumExists(quorumNumber) { _addStrategyParams(quorumNumber, _strategyParams); + + uint256 numStratsToAdd = _strategyParams.length; + + if (isOperatorSetQuorum(quorumNumber)){ + IStrategy[] memory strategiesToAdd = new IStrategy[](numStratsToAdd); + for (uint256 i = 0; i < numStratsToAdd; i++) { + strategiesToAdd[i] = _strategyParams[i].strategy; + } + serviceManager.addStrategyToOperatorSet(quorumNumber, strategiesToAdd); + } } /** @@ -270,17 +280,25 @@ contract StakeRegistry is StakeRegistryStorage { StrategyParams[] storage _strategyParams = strategyParams[quorumNumber]; IStrategy[] storage _strategiesPerQuorum = strategiesPerQuorum[quorumNumber]; + IStrategy[] memory _strategiesToRemove = new IStrategy[](toRemoveLength); for (uint256 i = 0; i < toRemoveLength; i++) { + _strategiesToRemove[i]=_strategyParams[indicesToRemove[i]].strategy; emit StrategyRemovedFromQuorum(quorumNumber, _strategyParams[indicesToRemove[i]].strategy); emit StrategyMultiplierUpdated(quorumNumber, _strategyParams[indicesToRemove[i]].strategy, 0); + + // Replace index to remove with the last item in the list, then pop the last item _strategyParams[indicesToRemove[i]] = _strategyParams[_strategyParams.length - 1]; _strategyParams.pop(); _strategiesPerQuorum[indicesToRemove[i]] = _strategiesPerQuorum[_strategiesPerQuorum.length - 1]; _strategiesPerQuorum.pop(); } + + if (isOperatorSetQuorum(quorumNumber)){ + serviceManager.removeStrategiesFromOperatorSet(quorumNumber, _strategiesToRemove); + } } /** @@ -560,7 +578,7 @@ contract StakeRegistry is StakeRegistryStorage { * @param quorumNumber The quorum number to check * @return True if the quorum is an operator set quorum */ - function isOperatorSetQuorum(uint8 quorumNumber) external view returns (bool) { + function isOperatorSetQuorum(uint8 quorumNumber) public view returns (bool) { bool isM2 = IRegistryCoordinator(registryCoordinator).isM2Quorum(quorumNumber); bool isOperatorSet = IRegistryCoordinator(registryCoordinator).isOperatorSetAVS(); return isOperatorSet && !isM2; From ccebfcfdbc4262ceb6fdcb3f77408b54386b3df9 Mon Sep 17 00:00:00 2001 From: steven Date: Wed, 15 Jan 2025 14:35:32 -0500 Subject: [PATCH 72/80] chore: refactor so via-ir not needed in e2e --- .github/workflows/tests.yml | 2 +- script/utils/OperatorLib.sol | 1 + test/unit/End2End.t.sol | 503 ++++++++++++----------------------- 3 files changed, 179 insertions(+), 327 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index e4323e2d..3788bf5c 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -39,7 +39,7 @@ jobs: - name: Run tests shell: bash run: | - forge test --no-match-contract FFI --via-ir + forge test --no-match-contract FFI env: RPC_MAINNET: ${{ secrets.RPC_MAINNET }} diff --git a/script/utils/OperatorLib.sol b/script/utils/OperatorLib.sol index 890eaa5d..c42c85b1 100644 --- a/script/utils/OperatorLib.sol +++ b/script/utils/OperatorLib.sol @@ -207,6 +207,7 @@ library OperatorLib { } function deregisterOperatorFromAVS_M2(Operator memory operator, address registryCoordinator) internal { + vm.prank(operator.key.addr); RegistryCoordinator(registryCoordinator).deregisterOperator(""); } diff --git a/test/unit/End2End.t.sol b/test/unit/End2End.t.sol index 9e5213ba..87e38220 100644 --- a/test/unit/End2End.t.sol +++ b/test/unit/End2End.t.sol @@ -15,14 +15,31 @@ import {IServiceManager} from "../../src/interfaces/IServiceManager.sol"; import {IStakeRegistry, StakeType} from "../../src/interfaces/IStakeRegistry.sol"; import {RegistryCoordinator} from "../../src/RegistryCoordinator.sol"; import {IRegistryCoordinator} from "../../src/interfaces/IRegistryCoordinator.sol"; -import { OperatorSet} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; +import {OperatorSet} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; import {ServiceManagerMock} from "../mocks/ServiceManagerMock.sol"; - - contract End2EndTest is Test { using OperatorLib for *; + function _createOperators(uint256 numOperators, uint256 startIndex) internal returns (OperatorLib.Operator[] memory) { + OperatorLib.Operator[] memory operators = new OperatorLib.Operator[](numOperators); + for (uint256 i = 0; i < numOperators; i++) { + operators[i] = OperatorLib.createOperator(string(abi.encodePacked("operator-", i + startIndex))); + } + return operators; + } + + function _registerOperatorsAsEigenLayerOperators( + OperatorLib.Operator[] memory operators, + address delegationManager + ) internal { + for (uint256 i = 0; i < operators.length; i++) { + vm.startPrank(operators[i].key.addr); + OperatorLib.registerAsOperator(operators[i], delegationManager); + vm.stopPrank(); + } + } + function testCreateOperator() public { OperatorLib.Operator memory operator = OperatorLib.createOperator("operator-1"); @@ -53,22 +70,39 @@ contract End2EndTest is Test { assertTrue(isValid, "Signature should be valid"); } + // ... existing code ... + function testEndToEndSetup() public { + (OperatorLib.Operator[] memory operators, CoreDeploymentLib.DeploymentData memory coreDeployment, + MiddlewareDeploymentLib.DeploymentData memory middlewareDeployment, + MiddlewareDeploymentLib.ConfigData memory middlewareConfig) = _setupInitialState(); + + _setupOperatorsAndTokens(operators, coreDeployment, middlewareDeployment); + + _setupFirstQuorumAndOperatorSet(operators, middlewareConfig, coreDeployment, middlewareDeployment); + + _setupSecondQuorumAndOperatorSet(operators, middlewareConfig, coreDeployment, middlewareDeployment); + + _executeSlashing(operators, middlewareConfig, middlewareDeployment); + } + + function _setupInitialState() internal returns ( + OperatorLib.Operator[] memory operators, + CoreDeploymentLib.DeploymentData memory coreDeployment, + MiddlewareDeploymentLib.DeploymentData memory middlewareDeployment, + MiddlewareDeploymentLib.ConfigData memory middlewareConfig + ) { // Fork Holesky testnet string memory rpcUrl = vm.envString("HOLESKY_RPC_URL"); vm.createSelectFork(rpcUrl); - // Create 5 operators - OperatorLib.Operator[] memory operators = new OperatorLib.Operator[](5); - for (uint256 i = 0; i < 5; i++) { - operators[i] = OperatorLib.createOperator(string(abi.encodePacked("operator-", i + 100))); - } + // Create 5 operators using helper function + operators = _createOperators(5, 100); // Read core deployment data from json - CoreDeploymentLib.DeploymentData memory coreDeployment = CoreDeploymentLib.readCoreDeploymentJson("./script/config", 17000, "preprod"); + coreDeployment = CoreDeploymentLib.readCoreDeploymentJson("./script/config", 17000, "preprod"); // Setup middleware deployment data - MiddlewareDeploymentLib.ConfigData memory middlewareConfig; middlewareConfig.proxyAdmin = UpgradeableProxyLib.deployProxyAdmin(); middlewareConfig.admin = address(this); middlewareConfig.numQuorums = 1; @@ -77,42 +111,37 @@ contract End2EndTest is Test { middlewareConfig.operatorParams[1] = 100; middlewareConfig.operatorParams[2] = 100; - MiddlewareDeploymentLib.DeploymentData memory middlewareDeployment = MiddlewareDeploymentLib.deployContracts(coreDeployment, middlewareConfig); - - // Upgrade contracts + middlewareDeployment = MiddlewareDeploymentLib.deployContracts(coreDeployment, middlewareConfig); MiddlewareDeploymentLib.upgradeContracts(middlewareDeployment, middlewareConfig, coreDeployment); + } - // Verify operators are registered + function _setupOperatorsAndTokens( + OperatorLib.Operator[] memory operators, + CoreDeploymentLib.DeploymentData memory coreDeployment, + MiddlewareDeploymentLib.DeploymentData memory middlewareDeployment + ) internal { + // Verify and register operators for (uint256 i = 0; i < 5; i++) { bool isRegistered = IDelegationManager(coreDeployment.delegationManager).isOperator(operators[i].key.addr); assertFalse(isRegistered, "Operator should not be registered"); } - // Register operators - for (uint256 i = 0; i < 5; i++) { - vm.startPrank(operators[i].key.addr); /// TODO: for script need to just vm.startBroadcast from operator - OperatorLib.registerAsOperator(operators[i], coreDeployment.delegationManager); - vm.stopPrank(); - } - // Verify operators are registered + _registerOperatorsAsEigenLayerOperators(operators, coreDeployment.delegationManager); + for (uint256 i = 0; i < 5; i++) { bool isRegistered = IDelegationManager(coreDeployment.delegationManager).isOperator(operators[i].key.addr); assertTrue(isRegistered, "Operator should be registered"); } - // Mint mock tokens to each operator + // Setup tokens and verify balances uint256 mintAmount = 1000 * 1e18; for (uint256 i = 0; i < 5; i++) { OperatorLib.mintMockTokens(operators[i], middlewareDeployment.token, mintAmount); - } - - // Verify token balances - for (uint256 i = 0; i < 5; i++) { uint256 balance = IERC20(middlewareDeployment.token).balanceOf(operators[i].key.addr); assertEq(balance, mintAmount, "Operator should have correct token balance"); } - // Deposit tokens into strategy for each operator + // Handle deposits for (uint256 i = 0; i < 5; i++) { vm.startPrank(operators[i].key.addr); uint256 shares = OperatorLib.depositTokenIntoStrategy( @@ -124,32 +153,26 @@ contract End2EndTest is Test { ); assertTrue(shares > 0, "Should have received shares for deposit"); vm.stopPrank(); - } - // Verify strategy shares for each operator - for (uint256 i = 0; i < 5; i++) { - uint256 shares = IStrategy(middlewareDeployment.strategy).shares(operators[i].key.addr); + shares = IStrategy(middlewareDeployment.strategy).shares(operators[i].key.addr); assertEq(shares, mintAmount, "Operator shares should equal deposit amount"); } + } - IAllocationManagerTypes.CreateSetParams[] memory params = new IAllocationManagerTypes.CreateSetParams[](1); - IStrategy[] memory strategies = new IStrategy[](1); - strategies[0] = IStrategy(middlewareDeployment.strategy); - params[0] = IAllocationManagerTypes.CreateSetParams({ - operatorSetId: 0, - strategies: strategies - }); - // Migrate AVS to operator sets + function _setupFirstQuorumAndOperatorSet( + OperatorLib.Operator[] memory operators, + MiddlewareDeploymentLib.ConfigData memory middlewareConfig, + CoreDeploymentLib.DeploymentData memory coreDeployment, + MiddlewareDeploymentLib.DeploymentData memory middlewareDeployment + ) internal { vm.startPrank(middlewareConfig.admin); - - // Enable operator sets RegistryCoordinator(middlewareDeployment.registryCoordinator).enableOperatorSets(); - // Create quorum for non-slashable stake + // Create first quorum IRegistryCoordinator.OperatorSetParam memory operatorSetParams = IRegistryCoordinator.OperatorSetParam({ maxOperatorCount: 10, - kickBIPsOfOperatorStake: 100, // 1% - kickBIPsOfTotalStake: 100 // 1% + kickBIPsOfOperatorStake: 100, + kickBIPsOfTotalStake: 100 }); IStakeRegistry.StrategyParams[] memory strategyParams = new IStakeRegistry.StrategyParams[](1); @@ -160,17 +183,15 @@ contract End2EndTest is Test { RegistryCoordinator(middlewareDeployment.registryCoordinator).createTotalDelegatedStakeQuorum( operatorSetParams, - 100, // Minimum stake of 100 tokens + 100, strategyParams ); - vm.stopPrank(); - // Register operators to AVS through AllocationManager + // Register operators uint32[] memory operatorSetIds = new uint32[](1); - operatorSetIds[0] = 1; // First operator set + operatorSetIds[0] = 1; - // Register each operator to the AVS through AllocationManager for (uint256 i = 0; i < 5; i++) { vm.startPrank(operators[i].key.addr); OperatorLib.registerOperatorFromAVS_OpSet( @@ -183,62 +204,84 @@ contract End2EndTest is Test { vm.stopPrank(); } - // Fast forward 10 blocks vm.roll(block.number + 10); - // Get all registered operators and sort them - address[][] memory registeredOperators = new address[][](1); - registeredOperators[0] = new address[](5); - for (uint256 i = 0; i < 5; i++) { - registeredOperators[0][i] = operators[i].key.addr; - } - - // Sort operator addresses in ascending order - for (uint256 i = 0; i < registeredOperators[0].length - 1; i++) { - for (uint256 j = 0; j < registeredOperators[0].length - i - 1; j++) { - if (registeredOperators[0][j] > registeredOperators[0][j + 1]) { - address temp = registeredOperators[0][j]; - registeredOperators[0][j] = registeredOperators[0][j + 1]; - registeredOperators[0][j + 1] = temp; - } - } - } - - // Update operators for quorum 1 + // Update operators for quorum + address[][] memory registeredOperators = _getAndSortOperators(operators); bytes memory quorumNumbers = new bytes(1); - quorumNumbers[0] = bytes1(uint8(1)); // Quorum 1 + quorumNumbers[0] = bytes1(uint8(1)); vm.prank(middlewareConfig.admin); RegistryCoordinator(middlewareDeployment.registryCoordinator).updateOperatorsForQuorum( registeredOperators, quorumNumbers ); + } - // Create a second operator set for slashable stake - IStakeRegistry.StrategyParams[] memory strategyParams2 = new IStakeRegistry.StrategyParams[](1); - strategyParams2[0] = IStakeRegistry.StrategyParams({ + function _setupSecondQuorumAndOperatorSet( + OperatorLib.Operator[] memory operators, + MiddlewareDeploymentLib.ConfigData memory middlewareConfig, + CoreDeploymentLib.DeploymentData memory coreDeployment, + MiddlewareDeploymentLib.DeploymentData memory middlewareDeployment + ) internal { + // Create second quorum + vm.startPrank(middlewareConfig.admin); + IStakeRegistry.StrategyParams[] memory strategyParams = new IStakeRegistry.StrategyParams[](1); + strategyParams[0] = IStakeRegistry.StrategyParams({ strategy: IStrategy(middlewareDeployment.strategy), multiplier: 1 ether }); - // Configure operator set params - IRegistryCoordinator.OperatorSetParam memory operatorSetParams2 = IRegistryCoordinator.OperatorSetParam({ + IRegistryCoordinator.OperatorSetParam memory operatorSetParams = IRegistryCoordinator.OperatorSetParam({ maxOperatorCount: 10, kickBIPsOfOperatorStake: 0, kickBIPsOfTotalStake: 0 }); - // Create quorum with slashable stake type - vm.startPrank(middlewareConfig.admin); RegistryCoordinator(middlewareDeployment.registryCoordinator).createSlashableStakeQuorum( - operatorSetParams2, - 100, // minimumStake - strategyParams2, - 1 days // lookAheadPeriod + operatorSetParams, + 100, + strategyParams, + 1 days ); vm.stopPrank(); - // Set allocation delay to 1 block for each operator + _setupOperatorAllocations(operators, coreDeployment, middlewareDeployment); + + // Register and update operators for second quorum + uint32[] memory operatorSetIds = new uint32[](1); + operatorSetIds[0] = 2; + + for (uint256 i = 0; i < 5; i++) { + vm.startPrank(operators[i].key.addr); + OperatorLib.registerOperatorFromAVS_OpSet( + operators[i], + coreDeployment.allocationManager, + middlewareDeployment.registryCoordinator, + middlewareDeployment.serviceManager, + operatorSetIds + ); + vm.stopPrank(); + } + + vm.roll(block.number + 10); + + address[][] memory registeredOperators = _getAndSortOperators(operators); + bytes memory quorumNumbers = new bytes(1); + quorumNumbers[0] = bytes1(uint8(2)); + + vm.prank(middlewareConfig.admin); + RegistryCoordinator(middlewareDeployment.registryCoordinator).updateOperatorsForQuorum( + registeredOperators, + quorumNumbers + ); + } + + function _setupOperatorAllocations( + OperatorLib.Operator[] memory operators, + CoreDeploymentLib.DeploymentData memory coreDeployment, + MiddlewareDeploymentLib.DeploymentData memory middlewareDeployment + ) internal { uint32 minDelay = 1; for (uint256 i = 0; i < 5; i++) { vm.startPrank(operators[i].key.addr); @@ -250,22 +293,17 @@ contract End2EndTest is Test { vm.stopPrank(); } - // Roll forward 1 block and advance time by 1 second - console.log("Current block number:", block.number); - console.log("======================"); - vm.roll(block.number + 100); - // Set up allocation parameters for each operator IStrategy[] memory allocStrategies = new IStrategy[](1); allocStrategies[0] = IStrategy(middlewareDeployment.strategy); uint64[] memory magnitudes = new uint64[](1); - magnitudes[0] = uint64(1 ether); // Allocate full magnitude to meet minimum stake + magnitudes[0] = uint64(1 ether); OperatorSet memory operatorSet = OperatorSet({ avs: address(middlewareDeployment.serviceManager), - id: 2 // Second operator set + id: 2 }); IAllocationManagerTypes.AllocateParams[] memory allocParams = new IAllocationManagerTypes.AllocateParams[](1); @@ -275,7 +313,6 @@ contract End2EndTest is Test { newMagnitudes: magnitudes }); - // Allocate stake for each operator using helper function for (uint256 i = 0; i < 5; i++) { vm.startPrank(operators[i].key.addr); OperatorLib.modifyOperatorAllocations( @@ -287,199 +324,76 @@ contract End2EndTest is Test { } vm.roll(block.number + 100); - console.log("Current block number:", block.number); - console.log("======================"); - - - - // Register operators to second operator set - uint32[] memory operatorSetIds2 = new uint32[](1); - operatorSetIds2[0] = 2; // Second operator set - - // Register each operator to the second set - for (uint256 i = 0; i < 5; i++) { - vm.startPrank(operators[i].key.addr); - OperatorLib.registerOperatorFromAVS_OpSet( - operators[i], - coreDeployment.allocationManager, - middlewareDeployment.registryCoordinator, - middlewareDeployment.serviceManager, - operatorSetIds2 - ); - vm.stopPrank(); - } - - // Fast forward 10 blocks - vm.roll(block.number + 10); - - // Get all registered operators for second set and sort them - address[][] memory registeredOperators2 = new address[][](1); - registeredOperators2[0] = new address[](5); - for (uint256 i = 0; i < 5; i++) { - registeredOperators2[0][i] = operators[i].key.addr; - } - - // Sort operator addresses in ascending order - for (uint256 i = 0; i < registeredOperators2[0].length - 1; i++) { - for (uint256 j = 0; j < registeredOperators2[0].length - i - 1; j++) { - if (registeredOperators2[0][j] > registeredOperators2[0][j + 1]) { - address temp = registeredOperators2[0][j]; - registeredOperators2[0][j] = registeredOperators2[0][j + 1]; - registeredOperators2[0][j + 1] = temp; - } - } - } - - // Update operators for quorum 2 - bytes memory quorumNumbers2 = new bytes(1); - quorumNumbers2[0] = bytes1(uint8(2)); // Quorum 2 - - vm.prank(middlewareConfig.admin); - RegistryCoordinator(middlewareDeployment.registryCoordinator).updateOperatorsForQuorum( - registeredOperators2, - quorumNumbers2 - ); + } - // Propose a new slasher account + function _executeSlashing( + OperatorLib.Operator[] memory operators, + MiddlewareDeploymentLib.ConfigData memory middlewareConfig, + MiddlewareDeploymentLib.DeploymentData memory middlewareDeployment + ) internal { address newSlasher = makeAddr("newSlasher"); - vm.prank(middlewareConfig.admin); + vm.startPrank(middlewareConfig.admin); ServiceManagerMock(middlewareDeployment.serviceManager).proposeNewSlasher(newSlasher); - - // Fast forward 8 days to pass the SLASHER_PROPOSAL_DELAY (7 days) vm.warp(block.timestamp + 8 days); - - // Accept the proposed slasher - vm.prank(middlewareConfig.admin); ServiceManagerMock(middlewareDeployment.serviceManager).acceptProposedSlasher(); + vm.stopPrank(); - // Create slashing params IAllocationManagerTypes.SlashingParams memory slashingParams = IAllocationManagerTypes.SlashingParams({ - operator: operators[0].key.addr, // Slash first operator - operatorSetId: operatorSetIds2[0], // Using second operator set + operator: operators[0].key.addr, + operatorSetId: 2, strategies: new IStrategy[](1), wadsToSlash: new uint256[](1), description: "Test slashing" }); - // Add strategy to slash slashingParams.strategies[0] = IStrategy(middlewareDeployment.strategy); - - // Slash 50% of operator's stake (0.5e18) slashingParams.wadsToSlash[0] = 0.5e18; - // Call slash as the slasher vm.prank(newSlasher); ServiceManagerMock(middlewareDeployment.serviceManager).slashOperator(slashingParams); } - function testEndToEndSetup_M2Migration() public { - // Fork Holesky testnet - string memory rpcUrl = vm.envString("HOLESKY_RPC_URL"); - vm.createSelectFork(rpcUrl); - - // Create 5 operators - OperatorLib.Operator[] memory operators = new OperatorLib.Operator[](5); - for (uint256 i = 0; i < 5; i++) { - operators[i] = OperatorLib.createOperator(string(abi.encodePacked("operator-", i + 100))); - } - - // Read core deployment data from json - CoreDeploymentLib.DeploymentData memory coreDeployment = CoreDeploymentLib.readCoreDeploymentJson("./script/config", 17000, "preprod"); - - // Setup middleware deployment data - MiddlewareDeploymentLib.ConfigData memory middlewareConfig; - middlewareConfig.proxyAdmin = UpgradeableProxyLib.deployProxyAdmin(); - middlewareConfig.admin = address(this); - middlewareConfig.numQuorums = 1; - middlewareConfig.operatorParams = new uint256[](3); - middlewareConfig.operatorParams[0] = 10; - middlewareConfig.operatorParams[1] = 100; - middlewareConfig.operatorParams[2] = 100; - - MiddlewareDeploymentLib.DeploymentData memory middlewareDeployment = MiddlewareDeploymentLib.deployContracts(coreDeployment, middlewareConfig); - - // Upgrade contracts - MiddlewareDeploymentLib.upgradeContracts(middlewareDeployment, middlewareConfig, coreDeployment); - - // Verify operators are registered - for (uint256 i = 0; i < 5; i++) { - bool isRegistered = IDelegationManager(coreDeployment.delegationManager).isOperator(operators[i].key.addr); - assertFalse(isRegistered, "Operator should not be registered"); - } - // Register operators - for (uint256 i = 0; i < 5; i++) { - vm.startPrank(operators[i].key.addr); /// TODO: for script need to just vm.startBroadcast from operator - OperatorLib.registerAsOperator(operators[i], coreDeployment.delegationManager); - vm.stopPrank(); - } - - // Verify operators are registered - for (uint256 i = 0; i < 5; i++) { - bool isRegistered = IDelegationManager(coreDeployment.delegationManager).isOperator(operators[i].key.addr); - assertTrue(isRegistered, "Operator should be registered"); - } - - // Mint mock tokens to each operator - uint256 mintAmount = 1000 * 1e18; - for (uint256 i = 0; i < 5; i++) { - OperatorLib.mintMockTokens(operators[i], middlewareDeployment.token, mintAmount); - } - - // Verify token balances + function _getAndSortOperators(OperatorLib.Operator[] memory operators) internal pure returns (address[][] memory) { + address[][] memory registeredOperators = new address[][](1); + registeredOperators[0] = new address[](5); for (uint256 i = 0; i < 5; i++) { - uint256 balance = IERC20(middlewareDeployment.token).balanceOf(operators[i].key.addr); - assertEq(balance, mintAmount, "Operator should have correct token balance"); + registeredOperators[0][i] = operators[i].key.addr; } - // Deposit tokens into strategy for each operator - for (uint256 i = 0; i < 5; i++) { - vm.startPrank(operators[i].key.addr); - uint256 shares = OperatorLib.depositTokenIntoStrategy( - operators[i], - coreDeployment.strategyManager, - middlewareDeployment.strategy, - middlewareDeployment.token, - mintAmount - ); - assertTrue(shares > 0, "Should have received shares for deposit"); - vm.stopPrank(); + // Sort operator addresses + for (uint256 i = 0; i < registeredOperators[0].length - 1; i++) { + for (uint256 j = 0; j < registeredOperators[0].length - i - 1; j++) { + if (registeredOperators[0][j] > registeredOperators[0][j + 1]) { + address temp = registeredOperators[0][j]; + registeredOperators[0][j] = registeredOperators[0][j + 1]; + registeredOperators[0][j + 1] = temp; + } + } } - // Verify strategy shares for each operator - for (uint256 i = 0; i < 5; i++) { - uint256 shares = IStrategy(middlewareDeployment.strategy).shares(operators[i].key.addr); - assertEq(shares, mintAmount, "Operator shares should equal deposit amount"); - } + return registeredOperators; + } - IAllocationManagerTypes.CreateSetParams[] memory params = new IAllocationManagerTypes.CreateSetParams[](1); - IStrategy[] memory strategies = new IStrategy[](1); - strategies[0] = IStrategy(middlewareDeployment.strategy); - params[0] = IAllocationManagerTypes.CreateSetParams({ - operatorSetId: 0, - strategies: strategies - }); + function testEndToEndSetup_M2Migration() public { + (OperatorLib.Operator[] memory operators, CoreDeploymentLib.DeploymentData memory coreDeployment, + MiddlewareDeploymentLib.DeploymentData memory middlewareDeployment, + MiddlewareDeploymentLib.ConfigData memory middlewareConfig) = _setupInitialState(); - // Create quorum for non-slashable stake - IRegistryCoordinator.OperatorSetParam memory operatorSetParams = IRegistryCoordinator.OperatorSetParam({ - maxOperatorCount: 10, - kickBIPsOfOperatorStake: 100, // 1% - kickBIPsOfTotalStake: 100 // 1% - }); + _setupOperatorsAndTokens(operators, coreDeployment, middlewareDeployment); - IStakeRegistry.StrategyParams[] memory strategyParams = new IStakeRegistry.StrategyParams[](1); - strategyParams[0] = IStakeRegistry.StrategyParams({ - strategy: IStrategy(middlewareDeployment.strategy), - multiplier: 1 ether - }); + _setupFirstQuorumAndOperatorSet(operators, middlewareConfig, coreDeployment, middlewareDeployment); - RegistryCoordinator(middlewareDeployment.registryCoordinator).createTotalDelegatedStakeQuorum( - operatorSetParams, - 100, // Minimum stake of 100 tokens - strategyParams - ); + _setupSecondQuorumAndOperatorSet(operators, middlewareConfig, coreDeployment, middlewareDeployment); - vm.stopPrank(); + _executeSlashing(operators, middlewareConfig, middlewareDeployment); + } + function _setupFirstQuorumAndOperatorSet_M2( + OperatorLib.Operator[] memory operators, + MiddlewareDeploymentLib.ConfigData memory middlewareConfig, + CoreDeploymentLib.DeploymentData memory coreDeployment, + MiddlewareDeploymentLib.DeploymentData memory middlewareDeployment + ) internal { // Register operators to AVS through AllocationManager uint32[] memory operatorSetIds = new uint32[](1); operatorSetIds[0] = 1; // First operator set @@ -504,22 +418,7 @@ contract End2EndTest is Test { vm.roll(block.number + 10); // Get all registered operators and sort them - address[][] memory registeredOperators = new address[][](1); - registeredOperators[0] = new address[](5); - for (uint256 i = 0; i < 5; i++) { - registeredOperators[0][i] = operators[i].key.addr; - } - - // Sort operator addresses in ascending order - for (uint256 i = 0; i < registeredOperators[0].length - 1; i++) { - for (uint256 j = 0; j < registeredOperators[0].length - i - 1; j++) { - if (registeredOperators[0][j] > registeredOperators[0][j + 1]) { - address temp = registeredOperators[0][j]; - registeredOperators[0][j] = registeredOperators[0][j + 1]; - registeredOperators[0][j + 1] = temp; - } - } - } + address[][] memory registeredOperators = _getAndSortOperators(operators); // Update operators for quorum 1 bytes memory quorumNumbers = new bytes(1); @@ -535,7 +434,15 @@ contract End2EndTest is Test { // Migrate AVS to operator sets vm.startPrank(middlewareConfig.admin); RegistryCoordinator(middlewareDeployment.registryCoordinator).enableOperatorSets(); + vm.stopPrank(); + } + function _setupSecondQuorumAndOperatorSet_M2( + OperatorLib.Operator[] memory operators, + MiddlewareDeploymentLib.ConfigData memory middlewareConfig, + CoreDeploymentLib.DeploymentData memory coreDeployment, + MiddlewareDeploymentLib.DeploymentData memory middlewareDeployment + ) internal { // Create a second operator set for slashable stake IStakeRegistry.StrategyParams[] memory strategyParams2 = new IStakeRegistry.StrategyParams[](1); strategyParams2[0] = IStakeRegistry.StrategyParams({ @@ -572,10 +479,6 @@ contract End2EndTest is Test { vm.stopPrank(); } - // Roll forward 1 block and advance time by 1 second - console.log("Current block number:", block.number); - console.log("======================"); - vm.roll(block.number + 100); // Set up allocation parameters for each operator @@ -609,10 +512,6 @@ contract End2EndTest is Test { } vm.roll(block.number + 100); - console.log("Current block number:", block.number); - console.log("======================"); - - // Register operators to second operator set uint32[] memory operatorSetIds2 = new uint32[](1); @@ -631,26 +530,10 @@ contract End2EndTest is Test { vm.stopPrank(); } - // Fast forward 10 blocks vm.roll(block.number + 10); // Get all registered operators for second set and sort them - address[][] memory registeredOperators2 = new address[][](1); - registeredOperators2[0] = new address[](5); - for (uint256 i = 0; i < 5; i++) { - registeredOperators2[0][i] = operators[i].key.addr; - } - - // Sort operator addresses in ascending order - for (uint256 i = 0; i < registeredOperators2[0].length - 1; i++) { - for (uint256 j = 0; j < registeredOperators2[0].length - i - 1; j++) { - if (registeredOperators2[0][j] > registeredOperators2[0][j + 1]) { - address temp = registeredOperators2[0][j]; - registeredOperators2[0][j] = registeredOperators2[0][j + 1]; - registeredOperators2[0][j + 1] = temp; - } - } - } + address[][] memory registeredOperators2 = _getAndSortOperators(operators); // Update operators for quorum 2 bytes memory quorumNumbers2 = new bytes(1); @@ -661,37 +544,5 @@ contract End2EndTest is Test { registeredOperators2, quorumNumbers2 ); - - // Propose a new slasher account - address newSlasher = makeAddr("newSlasher"); - vm.prank(middlewareConfig.admin); - ServiceManagerMock(middlewareDeployment.serviceManager).proposeNewSlasher(newSlasher); - - // Fast forward 8 days to pass the SLASHER_PROPOSAL_DELAY (7 days) - vm.warp(block.timestamp + 8 days); - - // Accept the proposed slasher - vm.prank(middlewareConfig.admin); - ServiceManagerMock(middlewareDeployment.serviceManager).acceptProposedSlasher(); - - // Create slashing params - IAllocationManagerTypes.SlashingParams memory slashingParams = IAllocationManagerTypes.SlashingParams({ - operator: operators[0].key.addr, // Slash first operator - operatorSetId: operatorSetIds2[0], // Using second operator set - strategies: new IStrategy[](1), - wadsToSlash: new uint256[](1), - description: "Test slashing" - }); - - // Add strategy to slash - slashingParams.strategies[0] = IStrategy(middlewareDeployment.strategy); - - // Slash 50% of operator's stake (0.5e18) - slashingParams.wadsToSlash[0] = 0.5e18; - - // Call slash as the slasher - vm.prank(newSlasher); - ServiceManagerMock(middlewareDeployment.serviceManager).slashOperator(slashingParams); } - } From c3d1dac9d3eab86b0d5110d79f4b345cd14085a0 Mon Sep 17 00:00:00 2001 From: steven Date: Wed, 15 Jan 2025 14:42:16 -0500 Subject: [PATCH 73/80] chore: naming for CI pattern --- .github/workflows/tests.yml | 4 ++-- test/unit/End2End.t.sol | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 3788bf5c..d79b8d63 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -33,13 +33,13 @@ jobs: - name: Run Forge build run: | forge --version - forge build --sizes --skip script --skip test --via-ir + forge build --sizes id: build - name: Run tests shell: bash run: | - forge test --no-match-contract FFI + forge test --no-match-contract FFI,ForkTest env: RPC_MAINNET: ${{ secrets.RPC_MAINNET }} diff --git a/test/unit/End2End.t.sol b/test/unit/End2End.t.sol index 87e38220..e43cdd61 100644 --- a/test/unit/End2End.t.sol +++ b/test/unit/End2End.t.sol @@ -18,7 +18,7 @@ import {IRegistryCoordinator} from "../../src/interfaces/IRegistryCoordinator.so import {OperatorSet} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; import {ServiceManagerMock} from "../mocks/ServiceManagerMock.sol"; -contract End2EndTest is Test { +contract End2EndForkTest is Test { using OperatorLib for *; function _createOperators(uint256 numOperators, uint256 startIndex) internal returns (OperatorLib.Operator[] memory) { From 98f38d70fcafcaf71bb499a2098303f270a367c8 Mon Sep 17 00:00:00 2001 From: steven Date: Wed, 15 Jan 2025 14:43:27 -0500 Subject: [PATCH 74/80] chore: nitt --- test/unit/End2End.t.sol | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/unit/End2End.t.sol b/test/unit/End2End.t.sol index e43cdd61..18609186 100644 --- a/test/unit/End2End.t.sol +++ b/test/unit/End2End.t.sol @@ -70,8 +70,6 @@ contract End2EndForkTest is Test { assertTrue(isValid, "Signature should be valid"); } - // ... existing code ... - function testEndToEndSetup() public { (OperatorLib.Operator[] memory operators, CoreDeploymentLib.DeploymentData memory coreDeployment, MiddlewareDeploymentLib.DeploymentData memory middlewareDeployment, From f2b2a44a9445674e70fa8c44c972fa0ab777f76e Mon Sep 17 00:00:00 2001 From: steven Date: Wed, 15 Jan 2025 14:45:25 -0500 Subject: [PATCH 75/80] fix: param on script --- script/utils/MiddlewareDeploymentLib.sol | 1 - 1 file changed, 1 deletion(-) diff --git a/script/utils/MiddlewareDeploymentLib.sol b/script/utils/MiddlewareDeploymentLib.sol index c9350e43..81aefb36 100644 --- a/script/utils/MiddlewareDeploymentLib.sol +++ b/script/utils/MiddlewareDeploymentLib.sol @@ -170,7 +170,6 @@ library MiddlewareDeploymentLib { IStakeRegistry(deployment.stakeRegistry), IBLSApkRegistry(deployment.blsapkRegistry), IIndexRegistry(deployment.indexRegistry), - IAVSDirectory(core.avsDirectory), IPauserRegistry(deployment.pauserRegistry) ) ); From d990a9e48b820e81b7d3eba82e0c771711db762a Mon Sep 17 00:00:00 2001 From: steven Date: Thu, 16 Jan 2025 12:42:24 -0500 Subject: [PATCH 76/80] chore: refactor out the proxyAdmin from config --- script/DeployMiddleware.s.sol | 5 ++--- script/utils/MiddlewareDeploymentLib.sol | 12 ++++++------ test/unit/End2End.t.sol | 6 ++++-- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/script/DeployMiddleware.s.sol b/script/DeployMiddleware.s.sol index b13acd33..0ba1aec9 100644 --- a/script/DeployMiddleware.s.sol +++ b/script/DeployMiddleware.s.sol @@ -36,9 +36,8 @@ contract DeployMiddleware is Script { function run() external { vm.startBroadcast(deployer); - /// TODO: Pass proxy admin instead of config - config.proxyAdmin = UpgradeableProxyLib.deployProxyAdmin(); - middlewareDeployment = MiddlewareDeploymentLib.deployContracts(core, config); + address proxyAdmin = UpgradeableProxyLib.deployProxyAdmin(); + middlewareDeployment = MiddlewareDeploymentLib.deployContracts(proxyAdmin, core, config); labelContracts(core, middlewareDeployment); diff --git a/script/utils/MiddlewareDeploymentLib.sol b/script/utils/MiddlewareDeploymentLib.sol index 81aefb36..9fde5003 100644 --- a/script/utils/MiddlewareDeploymentLib.sol +++ b/script/utils/MiddlewareDeploymentLib.sol @@ -57,7 +57,6 @@ library MiddlewareDeploymentLib { } struct ConfigData { - address proxyAdmin; address admin; uint256 numQuorums; uint256[] operatorParams; @@ -78,6 +77,7 @@ library MiddlewareDeploymentLib { } function deployContracts( + address proxyAdmin, CoreDeploymentLib.DeploymentData memory core, ConfigData memory config ) internal returns (DeploymentData memory) { @@ -87,11 +87,11 @@ library MiddlewareDeploymentLib { result.pauserRegistry = _deployPauserRegistry(config.admin); // Deploy proxies - result.stakeRegistry = UpgradeableProxyLib.setUpEmptyProxy(config.proxyAdmin); - result.registryCoordinator = UpgradeableProxyLib.setUpEmptyProxy(config.proxyAdmin); - result.blsapkRegistry = UpgradeableProxyLib.setUpEmptyProxy(config.proxyAdmin); - result.indexRegistry = UpgradeableProxyLib.setUpEmptyProxy(config.proxyAdmin); - result.serviceManager = UpgradeableProxyLib.setUpEmptyProxy(config.proxyAdmin); + result.stakeRegistry = UpgradeableProxyLib.setUpEmptyProxy(proxyAdmin); + result.registryCoordinator = UpgradeableProxyLib.setUpEmptyProxy(proxyAdmin); + result.blsapkRegistry = UpgradeableProxyLib.setUpEmptyProxy(proxyAdmin); + result.indexRegistry = UpgradeableProxyLib.setUpEmptyProxy(proxyAdmin); + result.serviceManager = UpgradeableProxyLib.setUpEmptyProxy(proxyAdmin); // Deploy operator state retriever result.operatorStateRetriever = address(new OperatorStateRetriever()); diff --git a/test/unit/End2End.t.sol b/test/unit/End2End.t.sol index 18609186..9a39ada1 100644 --- a/test/unit/End2End.t.sol +++ b/test/unit/End2End.t.sol @@ -21,6 +21,8 @@ import {ServiceManagerMock} from "../mocks/ServiceManagerMock.sol"; contract End2EndForkTest is Test { using OperatorLib for *; + address internal proxyAdmin; + function _createOperators(uint256 numOperators, uint256 startIndex) internal returns (OperatorLib.Operator[] memory) { OperatorLib.Operator[] memory operators = new OperatorLib.Operator[](numOperators); for (uint256 i = 0; i < numOperators; i++) { @@ -101,7 +103,7 @@ contract End2EndForkTest is Test { coreDeployment = CoreDeploymentLib.readCoreDeploymentJson("./script/config", 17000, "preprod"); // Setup middleware deployment data - middlewareConfig.proxyAdmin = UpgradeableProxyLib.deployProxyAdmin(); + proxyAdmin = UpgradeableProxyLib.deployProxyAdmin(); middlewareConfig.admin = address(this); middlewareConfig.numQuorums = 1; middlewareConfig.operatorParams = new uint256[](3); @@ -109,7 +111,7 @@ contract End2EndForkTest is Test { middlewareConfig.operatorParams[1] = 100; middlewareConfig.operatorParams[2] = 100; - middlewareDeployment = MiddlewareDeploymentLib.deployContracts(coreDeployment, middlewareConfig); + middlewareDeployment = MiddlewareDeploymentLib.deployContracts(proxyAdmin, coreDeployment, middlewareConfig); MiddlewareDeploymentLib.upgradeContracts(middlewareDeployment, middlewareConfig, coreDeployment); } From 891b779e03436012118e5ed0ac7e2cbde79415cb Mon Sep 17 00:00:00 2001 From: steven Date: Thu, 16 Jan 2025 12:50:10 -0500 Subject: [PATCH 77/80] fix: ci with no match --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index d79b8d63..8d1b4115 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -39,7 +39,7 @@ jobs: - name: Run tests shell: bash run: | - forge test --no-match-contract FFI,ForkTest + forge test --no-match-contract FFI --no-match-contract ForkTest env: RPC_MAINNET: ${{ secrets.RPC_MAINNET }} From fe1313ebffa6d48c79d8c10f62408a18fd13cf1a Mon Sep 17 00:00:00 2001 From: steven Date: Thu, 16 Jan 2025 12:52:26 -0500 Subject: [PATCH 78/80] fix: ci ignore fork test and ffi --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 8d1b4115..102bf394 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -39,7 +39,7 @@ jobs: - name: Run tests shell: bash run: | - forge test --no-match-contract FFI --no-match-contract ForkTest + forge test --no-match-contract "FFI|ForkTest" env: RPC_MAINNET: ${{ secrets.RPC_MAINNET }} From 5261bfa28b2bedc7acab971426a4b2ade8c00ce8 Mon Sep 17 00:00:00 2001 From: steven Date: Thu, 16 Jan 2025 13:21:20 -0500 Subject: [PATCH 79/80] feat: more ergonomic upgrade script --- script/utils/MiddlewareDeploymentLib.sol | 47 +++++++++++++++ script/utils/MiddlewareUpgrade.s.sol | 73 ++++++++++++++++++++++++ 2 files changed, 120 insertions(+) create mode 100644 script/utils/MiddlewareUpgrade.s.sol diff --git a/script/utils/MiddlewareDeploymentLib.sol b/script/utils/MiddlewareDeploymentLib.sol index 9fde5003..d5ca7844 100644 --- a/script/utils/MiddlewareDeploymentLib.sol +++ b/script/utils/MiddlewareDeploymentLib.sol @@ -248,4 +248,51 @@ library MiddlewareDeploymentLib { UpgradeableProxyLib.upgrade(deployment.indexRegistry, impls.indexRegistryImpl); UpgradeableProxyLib.upgradeAndCall(deployment.registryCoordinator, impls.registryCoordinatorImpl, registryCoordinatorUpgradeCall); } + + function upgradeContracts( + DeploymentData memory deployment, + CoreDeploymentLib.DeploymentData memory core + ) internal { + ImplementationAddresses memory impls = _deployImplementations(deployment, core); + + _executeUpgrades(deployment, impls); + } + + function _executeUpgrades( + DeploymentData memory deployment, + ImplementationAddresses memory impls + ) private { + UpgradeableProxyLib.upgrade(deployment.serviceManager, impls.serviceManagerImpl); + UpgradeableProxyLib.upgrade(deployment.stakeRegistry, impls.stakeRegistryImpl); + UpgradeableProxyLib.upgrade(deployment.blsapkRegistry, impls.blsApkRegistryImpl); + UpgradeableProxyLib.upgrade(deployment.indexRegistry, impls.indexRegistryImpl); + UpgradeableProxyLib.upgrade(deployment.registryCoordinator, impls.registryCoordinatorImpl); + } + + function readDeploymentJson(string memory path, uint256 chainId) internal returns (DeploymentData memory) { + string memory filePath = string(abi.encodePacked(path, "/", vm.toString(chainId), ".json")); + return parseDeploymentJson(filePath); + } + + function readDeploymentJson(string memory path, uint256 chainId, string memory environment) internal returns (DeploymentData memory) { + string memory filePath = string(abi.encodePacked(path, "/", vm.toString(chainId), "-", environment, ".json")); + return parseDeploymentJson(filePath); + } + + function parseDeploymentJson(string memory filePath) internal returns (DeploymentData memory) { + string memory json = vm.readFile(filePath); + require(vm.exists(filePath), "Deployment file does not exist"); + DeploymentData memory deploymentData; + + deploymentData.serviceManager = json.readAddress(".serviceManager"); + deploymentData.registryCoordinator = json.readAddress(".registryCoordinator"); + deploymentData.blsapkRegistry = json.readAddress(".blsapkRegistry"); + deploymentData.indexRegistry = json.readAddress(".indexRegistry"); + deploymentData.stakeRegistry = json.readAddress(".stakeRegistry"); + deploymentData.operatorStateRetriever = json.readAddress(".operatorStateRetriever"); + deploymentData.token = json.readAddress(".token"); + deploymentData.strategy = json.readAddress(".strategy"); + + return deploymentData; + } } \ No newline at end of file diff --git a/script/utils/MiddlewareUpgrade.s.sol b/script/utils/MiddlewareUpgrade.s.sol new file mode 100644 index 00000000..1c2c3fe9 --- /dev/null +++ b/script/utils/MiddlewareUpgrade.s.sol @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.12; + +import {Script} from "forge-std/Script.sol"; +import {console2 as console} from "forge-std/Test.sol"; +import {UpgradeableProxyLib} from "./UpgradeableProxyLib.sol"; +import {CoreDeploymentLib} from "./CoreDeploymentLib.sol"; +import {MiddlewareDeploymentLib} from "./MiddlewareDeploymentLib.sol"; +import {stdJson} from "forge-std/StdJson.sol"; + +contract MiddlewareUpgrade is Script { + using stdJson for string; + + CoreDeploymentLib.DeploymentData internal core; + MiddlewareDeploymentLib.DeploymentData internal middlewareDeployment; + address internal deployer; + + function setUp() public { + deployer = vm.rememberKey(vm.envUint("HOLESKY_PRIVATE_KEY")); + vm.label(deployer, "Deployer"); + + // Read core deployment data from json + core = CoreDeploymentLib.readCoreDeploymentJson("./script/config", 17000, "preprod"); + + // Read existing middleware deployment from json + middlewareDeployment = MiddlewareDeploymentLib.readDeploymentJson("./script/deployments", 17000, "preprod"); + + } + + function run() external { + vm.startBroadcast(deployer); + + // Upgrade contracts + MiddlewareDeploymentLib.upgradeContracts(middlewareDeployment, core); + + // Write updated deployment info to json + string memory deploymentJson = generateDeploymentJson(); + vm.writeFile(string(abi.encodePacked("./script/deployments/", vm.toString(uint256(17000)), "-preprod.json")), deploymentJson); + + logDeploymentDetails(middlewareDeployment); + + vm.stopBroadcast(); + } + + function generateDeploymentJson() internal view returns (string memory) { + return string.concat( + "{", + string.concat( + '"serviceManager":"', vm.toString(middlewareDeployment.serviceManager), '",', + '"registryCoordinator":"', vm.toString(middlewareDeployment.registryCoordinator), '",', + '"blsapkRegistry":"', vm.toString(middlewareDeployment.blsapkRegistry), '",', + '"indexRegistry":"', vm.toString(middlewareDeployment.indexRegistry), '",', + '"stakeRegistry":"', vm.toString(middlewareDeployment.stakeRegistry), '",', + '"operatorStateRetriever":"', vm.toString(middlewareDeployment.operatorStateRetriever), '",', + '"token":"', vm.toString(middlewareDeployment.token), '",', + '"strategy":"', vm.toString(middlewareDeployment.strategy), '"' + ), + "}" + ); + } + + function logDeploymentDetails(MiddlewareDeploymentLib.DeploymentData memory result) internal pure { + console.log("Upgrade completed"); + console.log("ServiceManager:", result.serviceManager); + console.log("RegistryCoordinator:", result.registryCoordinator); + console.log("BLSApkRegistry:", result.blsapkRegistry); + console.log("IndexRegistry:", result.indexRegistry); + console.log("StakeRegistry:", result.stakeRegistry); + console.log("OperatorStateRetriever:", result.operatorStateRetriever); + console.log("Token:", result.token); + console.log("Strategy:", result.strategy); + } +} From b51ba83b3c3e4199da9b1be5bb66c07bb01a1df9 Mon Sep 17 00:00:00 2001 From: steven Date: Thu, 16 Jan 2025 15:29:11 -0500 Subject: [PATCH 80/80] chore: move file, add note, and parameterize chain --- script/DeployMiddleware.s.sol | 3 ++- script/{utils => }/MiddlewareUpgrade.s.sol | 13 +++++++------ 2 files changed, 9 insertions(+), 7 deletions(-) rename script/{utils => }/MiddlewareUpgrade.s.sol (86%) diff --git a/script/DeployMiddleware.s.sol b/script/DeployMiddleware.s.sol index 0ba1aec9..14d6a03e 100644 --- a/script/DeployMiddleware.s.sol +++ b/script/DeployMiddleware.s.sol @@ -14,11 +14,12 @@ contract DeployMiddleware is Script { address internal deployer; function setUp() public { + /// TODO: Right now we're only supporting pre-prod deployer = vm.rememberKey(vm.envUint("HOLESKY_PRIVATE_KEY")); vm.label(deployer, "Deployer"); // Read core deployment data from json - core = CoreDeploymentLib.readCoreDeploymentJson("./script/config", 17000, "preprod"); + core = CoreDeploymentLib.readCoreDeploymentJson("./script/config", block.chainid, "preprod"); config.admin = deployer; config.numQuorums = 1; diff --git a/script/utils/MiddlewareUpgrade.s.sol b/script/MiddlewareUpgrade.s.sol similarity index 86% rename from script/utils/MiddlewareUpgrade.s.sol rename to script/MiddlewareUpgrade.s.sol index 1c2c3fe9..276be73f 100644 --- a/script/utils/MiddlewareUpgrade.s.sol +++ b/script/MiddlewareUpgrade.s.sol @@ -3,9 +3,9 @@ pragma solidity ^0.8.12; import {Script} from "forge-std/Script.sol"; import {console2 as console} from "forge-std/Test.sol"; -import {UpgradeableProxyLib} from "./UpgradeableProxyLib.sol"; -import {CoreDeploymentLib} from "./CoreDeploymentLib.sol"; -import {MiddlewareDeploymentLib} from "./MiddlewareDeploymentLib.sol"; +import {UpgradeableProxyLib} from "./utils/UpgradeableProxyLib.sol"; +import {CoreDeploymentLib} from "./utils/CoreDeploymentLib.sol"; +import {MiddlewareDeploymentLib} from "./utils/MiddlewareDeploymentLib.sol"; import {stdJson} from "forge-std/StdJson.sol"; contract MiddlewareUpgrade is Script { @@ -16,14 +16,15 @@ contract MiddlewareUpgrade is Script { address internal deployer; function setUp() public { + /// TODO: Right now we're only supporting pre-prod deployer = vm.rememberKey(vm.envUint("HOLESKY_PRIVATE_KEY")); vm.label(deployer, "Deployer"); // Read core deployment data from json - core = CoreDeploymentLib.readCoreDeploymentJson("./script/config", 17000, "preprod"); + core = CoreDeploymentLib.readCoreDeploymentJson("./script/config", block.chainid, "preprod"); // Read existing middleware deployment from json - middlewareDeployment = MiddlewareDeploymentLib.readDeploymentJson("./script/deployments", 17000, "preprod"); + middlewareDeployment = MiddlewareDeploymentLib.readDeploymentJson("./script/deployments", block.chainid, "preprod"); } @@ -35,7 +36,7 @@ contract MiddlewareUpgrade is Script { // Write updated deployment info to json string memory deploymentJson = generateDeploymentJson(); - vm.writeFile(string(abi.encodePacked("./script/deployments/", vm.toString(uint256(17000)), "-preprod.json")), deploymentJson); + vm.writeFile(string(abi.encodePacked("./script/deployments/", vm.toString(uint256(block.chainid)), "-preprod.json")), deploymentJson); logDeploymentDetails(middlewareDeployment);