From 79baf27e7c393881693719faadb4680a724d546c Mon Sep 17 00:00:00 2001 From: steven Date: Tue, 21 Jan 2025 17:06:01 -0500 Subject: [PATCH] chore: merge dev and bump mainnet rewards v2 --- .gitmodules | 3 - ...pdf => M2 Mainnet - Dedaub - Feb 2024.pdf} | Bin audits/Rewards v2 - SigmaPrime - Dec 2024.pdf | Bin 0 -> 493116 bytes docs/README.md | 4 +- docs/ServiceManagerBase.md | 2 +- docs/experimental/AVS-Guide.md | 2 +- foundry.toml | 1 + lib/ds-test | 1 - lib/eigenlayer-contracts | 2 +- lib/forge-std | 2 +- src/BLSApkRegistry.sol | 12 +- src/BLSSignatureChecker.sol | 178 ++- src/EjectionManager.sol | 184 +++ src/IndexRegistry.sol | 6 +- src/OperatorStateRetriever.sol | 33 + src/RegistryCoordinator.sol | 126 +- src/RegistryCoordinatorStorage.sol | 14 +- src/ServiceManagerBase.sol | 228 ++- src/ServiceManagerBaseStorage.sol | 53 + src/ServiceManagerRouter.sol | 6 +- src/SocketRegistry.sol | 45 + src/StakeRegistry.sol | 32 +- .../IECDSAStakeRegistryEventsAndErrors.sol | 27 +- src/interfaces/IEjectionManager.sol | 55 + src/interfaces/IRegistryCoordinator.sol | 8 + src/interfaces/IServiceManager.sol | 71 +- src/interfaces/IServiceManagerUI.sol | 61 + src/interfaces/ISocketRegistry.sol | 10 + src/interfaces/ISocketUpdater.sol | 20 - src/unaudited/ECDSAServiceManagerBase.sol | 353 +++++ src/unaudited/ECDSAStakeRegistry.sol | 279 +++- src/unaudited/ECDSAStakeRegistryStorage.sol | 13 +- .../ECDSAStakeRegistryPermissioned.sol | 24 +- test/events/IServiceManagerBaseEvents.sol | 128 ++ test/ffi/BLSPubKeyCompendiumFFI.t.sol | 2 +- .../RegistryCoordinatorHarness.t.sol | 5 +- test/integration/CoreRegistration.t.sol | 8 +- test/integration/IntegrationDeployer.t.sol | 194 ++- test/integration/TimeMachine.t.sol | 2 +- test/integration/User.t.sol | 6 +- test/integration/utils/Sort.t.sol | 31 +- test/mocks/AVSDirectoryMock.sol | 36 +- test/mocks/DelegationMock.sol | 64 +- test/mocks/ECDSAServiceManagerMock.sol | 22 + test/mocks/ECDSAStakeRegistryMock.sol | 14 + test/mocks/RegistryCoordinatorMock.sol | 2 + test/mocks/RewardsCoordinatorMock.sol | 138 ++ test/mocks/ServiceManagerMock.sol | 12 +- test/unit/BLSApkRegistryUnit.t.sol | 516 +++++-- test/unit/BLSSignatureCheckerUnit.t.sol | 3 +- test/unit/BitmapUtils.t.sol | 2 +- test/unit/ECDSAServiceManager.t.sol | 186 +++ .../ECDSAStakeRegistryEqualWeightUnit.t.sol | 74 +- .../ECDSAStakeRegistryPermissionedUnit.t.sol | 64 +- test/unit/ECDSAStakeRegistryUnit.t.sol | 577 ++++++-- test/unit/EjectionManagerUnit.t.sol | 448 ++++++ test/unit/OperatorStateRetrieverUnit.t.sol | 376 +++-- test/unit/RegistryCoordinatorUnit.t.sol | 114 +- test/unit/ServiceManagerBase.t.sol | 1082 +++++++++++++++ test/unit/ServiceManagerRouter.t.sol | 1 + test/unit/SocketRegistryUnit.t.sol | 27 + test/unit/StakeRegistryUnit.t.sol | 1234 +++++++++++------ test/utils/MockAVSDeployer.sol | 227 +-- 63 files changed, 6057 insertions(+), 1393 deletions(-) rename audits/{Dedaub - Middleware Audit - Final - Feb'24.pdf => M2 Mainnet - Dedaub - Feb 2024.pdf} (100%) create mode 100644 audits/Rewards v2 - SigmaPrime - Dec 2024.pdf delete mode 160000 lib/ds-test create mode 100644 src/EjectionManager.sol create mode 100644 src/ServiceManagerBaseStorage.sol create mode 100644 src/SocketRegistry.sol create mode 100644 src/interfaces/IEjectionManager.sol create mode 100644 src/interfaces/IServiceManagerUI.sol create mode 100644 src/interfaces/ISocketRegistry.sol delete mode 100644 src/interfaces/ISocketUpdater.sol create mode 100644 src/unaudited/ECDSAServiceManagerBase.sol create mode 100644 test/events/IServiceManagerBaseEvents.sol create mode 100644 test/mocks/ECDSAServiceManagerMock.sol create mode 100644 test/mocks/ECDSAStakeRegistryMock.sol create mode 100644 test/mocks/RewardsCoordinatorMock.sol create mode 100644 test/unit/ECDSAServiceManager.t.sol create mode 100644 test/unit/EjectionManagerUnit.t.sol create mode 100644 test/unit/ServiceManagerBase.t.sol create mode 100644 test/unit/SocketRegistryUnit.t.sol diff --git a/.gitmodules b/.gitmodules index 961c5645..43ea590c 100644 --- a/.gitmodules +++ b/.gitmodules @@ -5,9 +5,6 @@ path = lib/eigenlayer-contracts url = https://github.com/Layr-labs/eigenlayer-contracts branch = dev -[submodule "lib/ds-test"] - path = lib/ds-test - url = https://github.com/dapphub/ds-test [submodule "lib/openzeppelin-contracts"] path = lib/openzeppelin-contracts url = https://github.com/Openzeppelin/openzeppelin-contracts diff --git a/audits/Dedaub - Middleware Audit - Final - Feb'24.pdf b/audits/M2 Mainnet - Dedaub - Feb 2024.pdf similarity index 100% rename from audits/Dedaub - Middleware Audit - Final - Feb'24.pdf rename to audits/M2 Mainnet - Dedaub - Feb 2024.pdf diff --git a/audits/Rewards v2 - SigmaPrime - Dec 2024.pdf b/audits/Rewards v2 - SigmaPrime - Dec 2024.pdf new file mode 100644 index 0000000000000000000000000000000000000000..65d4c5d5d69b72f82138904344bff295b9bbc922 GIT binary patch literal 493116 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
  • 3snE}TWpqpaSp`9!piYKdJc5_S=Kd83~gms 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
    fhXc57tVyhplJcM!$qz= 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Π8=LG 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

    3gWb6iQp_rSK}m5(*S;i~`}OlwP=)YTh` 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

  • 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 mL#>7O#%VeI-#>{r28PD? uint256(nonSigners.pubkeyHashes[j - 1]), + uint256(nonSigners.pubkeyHashes[j]) > + uint256(nonSigners.pubkeyHashes[j - 1]), "BLSSignatureChecker.checkSignatures: nonSignerPubkeys not sorted" ); } // Get the quorums the nonsigner was registered for at referenceBlockNumber - nonSigners.quorumBitmaps[j] = - registryCoordinator.getQuorumBitmapAtBlockNumberByIndex({ + nonSigners.quorumBitmaps[j] = registryCoordinator + .getQuorumBitmapAtBlockNumberByIndex({ operatorId: nonSigners.pubkeyHashes[j], blockNumber: referenceBlockNumber, index: params.nonSignerQuorumBitmapIndices[j] @@ -162,10 +171,11 @@ contract BLSSignatureChecker is IBLSSignatureChecker { // of quorums they have in common with the signing quorums, because their // public key will be a part of each signing quorum's aggregate pubkey apk = apk.plus( - params.nonSignerPubkeys[j] - .scalar_mul_tiny( - BitmapUtils.countNumOnes(nonSigners.quorumBitmaps[j] & signingQuorumBitmap) + params.nonSignerPubkeys[j].scalar_mul_tiny( + BitmapUtils.countNumOnes( + nonSigners.quorumBitmaps[j] & signingQuorumBitmap ) + ) ); } } @@ -183,14 +193,20 @@ contract BLSSignatureChecker is IBLSSignatureChecker { */ { bool _staleStakesForbidden = staleStakesForbidden; - 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 // is within withdrawalDelayBlocks if (_staleStakesForbidden) { require( - registryCoordinator.quorumUpdateBlockNumber(uint8(quorumNumbers[i])) + withdrawalDelayBlocks > referenceBlockNumber, + registryCoordinator.quorumUpdateBlockNumber( + uint8(quorumNumbers[i]) + ) + + withdrawalDelayBlocks > + referenceBlockNumber, "BLSSignatureChecker.checkSignatures: StakeRegistry updates must be within withdrawalDelayBlocks window" ); } @@ -198,7 +214,7 @@ contract BLSSignatureChecker is IBLSSignatureChecker { // Validate params.quorumApks is correct for this quorum at the referenceBlockNumber, // then add it to the total apk require( - bytes24(params.quorumApks[i].hashG1Point()) == + bytes24(params.quorumApks[i].hashG1Point()) == blsApkRegistry.getApkHashAtBlockNumberAndIndex({ quorumNumber: uint8(quorumNumbers[i]), blockNumber: referenceBlockNumber, @@ -209,28 +225,36 @@ contract BLSSignatureChecker is IBLSSignatureChecker { apk = apk.plus(params.quorumApks[i]); // Get the total and starting signed stake for the quorum at referenceBlockNumber - stakeTotals.totalStakeForQuorum[i] = - stakeRegistry.getTotalStakeAtBlockNumberFromIndex({ + stakeTotals.totalStakeForQuorum[i] = stakeRegistry + .getTotalStakeAtBlockNumberFromIndex({ quorumNumber: uint8(quorumNumbers[i]), blockNumber: referenceBlockNumber, index: params.totalStakeIndices[i] }); - stakeTotals.signedStakeForQuorum[i] = stakeTotals.totalStakeForQuorum[i]; + stakeTotals.signedStakeForQuorum[i] = stakeTotals + .totalStakeForQuorum[i]; // Keep track of the nonSigners index in the quorum uint256 nonSignerForQuorumIndex = 0; - + // loop through all nonSigners, checking that they are a part of the quorum via their quorumBitmap // if so, load their stake at referenceBlockNumber and subtract it from running stake signed for (uint256 j = 0; j < params.nonSignerPubkeys.length; j++) { // if the nonSigner is a part of the quorum, subtract their stake from the running total - if (BitmapUtils.isSet(nonSigners.quorumBitmaps[j], uint8(quorumNumbers[i]))) { - stakeTotals.signedStakeForQuorum[i] -= - stakeRegistry.getStakeAtBlockNumberAndIndex({ + if ( + BitmapUtils.isSet( + nonSigners.quorumBitmaps[j], + uint8(quorumNumbers[i]) + ) + ) { + stakeTotals.signedStakeForQuorum[i] -= stakeRegistry + .getStakeAtBlockNumberAndIndex({ quorumNumber: uint8(quorumNumbers[i]), blockNumber: referenceBlockNumber, operatorId: nonSigners.pubkeyHashes[j], - index: params.nonSignerStakeIndices[i][nonSignerForQuorumIndex] + index: params.nonSignerStakeIndices[i][ + nonSignerForQuorumIndex + ] }); unchecked { ++nonSignerForQuorumIndex; @@ -241,17 +265,28 @@ contract BLSSignatureChecker is IBLSSignatureChecker { } { // verify the signature - (bool pairingSuccessful, bool signatureIsValid) = trySignatureAndApkVerification( - msgHash, - apk, - params.apkG2, - params.sigma + ( + bool pairingSuccessful, + bool signatureIsValid + ) = trySignatureAndApkVerification( + msgHash, + apk, + params.apkG2, + params.sigma + ); + require( + pairingSuccessful, + "BLSSignatureChecker.checkSignatures: pairing precompile call failed" + ); + require( + signatureIsValid, + "BLSSignatureChecker.checkSignatures: signature is invalid" ); - require(pairingSuccessful, "BLSSignatureChecker.checkSignatures: pairing precompile call failed"); - require(signatureIsValid, "BLSSignatureChecker.checkSignatures: signature is invalid"); } // set signatoryRecordHash variable used for fraudproofs - bytes32 signatoryRecordHash = keccak256(abi.encodePacked(referenceBlockNumber, nonSigners.pubkeyHashes)); + bytes32 signatoryRecordHash = keccak256( + abi.encodePacked(referenceBlockNumber, nonSigners.pubkeyHashes) + ); // return the total stakes that signed for each quorum, and a hash of the information required to prove the exact signers and stake return (stakeTotals, signatoryRecordHash); @@ -271,17 +306,36 @@ contract BLSSignatureChecker is IBLSSignatureChecker { BN254.G1Point memory apk, BN254.G2Point memory apkG2, BN254.G1Point memory sigma - ) public view returns(bool pairingSuccessful, bool siganatureIsValid) { + ) public view returns (bool pairingSuccessful, bool siganatureIsValid) { // gamma = keccak256(abi.encodePacked(msgHash, apk, apkG2, sigma)) - uint256 gamma = uint256(keccak256(abi.encodePacked(msgHash, apk.X, apk.Y, apkG2.X[0], apkG2.X[1], apkG2.Y[0], apkG2.Y[1], sigma.X, sigma.Y))) % BN254.FR_MODULUS; + uint256 gamma = uint256( + keccak256( + abi.encodePacked( + msgHash, + apk.X, + apk.Y, + apkG2.X[0], + apkG2.X[1], + apkG2.Y[0], + apkG2.Y[1], + sigma.X, + sigma.Y + ) + ) + ) % BN254.FR_MODULUS; // verify the signature (pairingSuccessful, siganatureIsValid) = BN254.safePairing( - sigma.plus(apk.scalar_mul(gamma)), - BN254.negGeneratorG2(), - BN254.hashToG1(msgHash).plus(BN254.generatorG1().scalar_mul(gamma)), - apkG2, - PAIRING_EQUALITY_CHECK_GAS - ); + sigma.plus(apk.scalar_mul(gamma)), + BN254.negGeneratorG2(), + BN254.hashToG1(msgHash).plus(BN254.generatorG1().scalar_mul(gamma)), + apkG2, + PAIRING_EQUALITY_CHECK_GAS + ); + } + + function _setStaleStakesForbidden(bool value) internal { + staleStakesForbidden = value; + emit StaleStakesForbiddenUpdate(value); } // storage gap for upgradeability diff --git a/src/EjectionManager.sol b/src/EjectionManager.sol new file mode 100644 index 00000000..ca81676b --- /dev/null +++ b/src/EjectionManager.sol @@ -0,0 +1,184 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.12; + +import {OwnableUpgradeable} from "@openzeppelin-upgrades/contracts/access/OwnableUpgradeable.sol"; +import {IEjectionManager} from "./interfaces/IEjectionManager.sol"; +import {IRegistryCoordinator} from "./interfaces/IRegistryCoordinator.sol"; +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{ + + /// @notice The basis point denominator for the ejectable stake percent + uint16 internal constant BIPS_DENOMINATOR = 10000; + + /// @notice the RegistryCoordinator contract that is the entry point for ejection + IRegistryCoordinator public immutable registryCoordinator; + /// @notice the StakeRegistry contract that keeps track of quorum stake + IStakeRegistry public immutable stakeRegistry; + + /// @notice Addresses permissioned to eject operators under a ratelimit + mapping(address => bool) public isEjector; + + /// @notice Keeps track of the total stake ejected for a quorum + mapping(uint8 => StakeEjection[]) public stakeEjectedForQuorum; + /// @notice Ratelimit parameters for each quorum + mapping(uint8 => QuorumEjectionParams) public quorumEjectionParams; + + constructor( + IRegistryCoordinator _registryCoordinator, + IStakeRegistry _stakeRegistry + ) { + registryCoordinator = _registryCoordinator; + stakeRegistry = _stakeRegistry; + + _disableInitializers(); + } + + /** + * @param _owner will hold the owner role + * @param _ejectors will hold the ejector role + * @param _quorumEjectionParams are the ratelimit parameters for the quorum at each index + */ + function initialize( + address _owner, + address[] memory _ejectors, + QuorumEjectionParams[] memory _quorumEjectionParams + ) external initializer { + _transferOwnership(_owner); + for(uint8 i = 0; i < _ejectors.length; i++) { + _setEjector(_ejectors[i], true); + } + for(uint8 i = 0; i < _quorumEjectionParams.length; i++) { + _setQuorumEjectionParams(i, _quorumEjectionParams[i]); + } + } + + /** + * @notice Ejects operators from the AVSs RegistryCoordinator under a ratelimit + * @param _operatorIds The ids of the operators 'j' to eject for each quorum 'i' + * @dev This function will eject as many operators as possible prioritizing operators at the lower index + * @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"); + + for(uint i = 0; i < _operatorIds.length; ++i) { + uint8 quorumNumber = uint8(i); + + uint256 amountEjectable = amountEjectableForQuorum(quorumNumber); + uint256 stakeForEjection; + uint32 ejectedOperators; + + bool ratelimitHit; + if(amountEjectable > 0 || msg.sender == owner()){ + for(uint8 j = 0; j < _operatorIds[i].length; ++j) { + uint256 operatorStake = stakeRegistry.getCurrentStake(_operatorIds[i][j], quorumNumber); + + //if caller is ejector enforce ratelimit + if( + isEjector[msg.sender] && + quorumEjectionParams[quorumNumber].rateLimitWindow > 0 && + stakeForEjection + operatorStake > amountEjectable + ){ + ratelimitHit = true; + + stakeForEjection += operatorStake; + ++ejectedOperators; + + registryCoordinator.ejectOperator( + registryCoordinator.getOperatorFromId(_operatorIds[i][j]), + abi.encodePacked(quorumNumber) + ); + + emit OperatorEjected(_operatorIds[i][j], quorumNumber); + + break; + } + + stakeForEjection += operatorStake; + ++ejectedOperators; + + registryCoordinator.ejectOperator( + registryCoordinator.getOperatorFromId(_operatorIds[i][j]), + abi.encodePacked(quorumNumber) + ); + + emit OperatorEjected(_operatorIds[i][j], quorumNumber); + } + } + + //record the stake ejected if ejector and ratelimit enforced + if(isEjector[msg.sender] && stakeForEjection > 0){ + stakeEjectedForQuorum[quorumNumber].push(StakeEjection({ + timestamp: block.timestamp, + stakeEjected: stakeForEjection + })); + } + + emit QuorumEjection(ejectedOperators, ratelimitHit); + } + } + + /** + * @notice Sets the ratelimit parameters for a quorum + * @param _quorumNumber The quorum number to set the ratelimit parameters for + * @param _quorumEjectionParams The quorum ratelimit parameters to set for the given quorum + */ + function setQuorumEjectionParams(uint8 _quorumNumber, QuorumEjectionParams memory _quorumEjectionParams) external onlyOwner() { + _setQuorumEjectionParams(_quorumNumber, _quorumEjectionParams); + } + + /** + * @notice Sets the address permissioned to eject operators under a ratelimit + * @param _ejector The address to permission + * @param _status The status to set for the given address + */ + function setEjector(address _ejector, bool _status) external onlyOwner() { + _setEjector(_ejector, _status); + } + + ///@dev internal function to set the quorum ejection params + function _setQuorumEjectionParams(uint8 _quorumNumber, QuorumEjectionParams memory _quorumEjectionParams) internal { + quorumEjectionParams[_quorumNumber] = _quorumEjectionParams; + emit QuorumEjectionParamsSet(_quorumNumber, _quorumEjectionParams.rateLimitWindow, _quorumEjectionParams.ejectableStakePercent); + } + + ///@dev internal function to set the ejector + function _setEjector(address _ejector, bool _status) internal { + isEjector[_ejector] = _status; + emit EjectorUpdated(_ejector, _status); + } + + /** + * @notice Returns the amount of stake that can be ejected for a quorum at the current block.timestamp + * @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; + if (stakeEjectedForQuorum[_quorumNumber].length == 0) { + return totalEjectable; + } + i = stakeEjectedForQuorum[_quorumNumber].length - 1; + + while(stakeEjectedForQuorum[_quorumNumber][i].timestamp > cutoffTime) { + totalEjected += stakeEjectedForQuorum[_quorumNumber][i].stakeEjected; + if(i == 0){ + break; + } else { + --i; + } + } + + if(totalEjected >= totalEjectable){ + return 0; + } + return totalEjectable - totalEjected; + } +} \ No newline at end of file diff --git a/src/IndexRegistry.sol b/src/IndexRegistry.sol index 4e63e78a..8df2c0a1 100644 --- a/src/IndexRegistry.sol +++ b/src/IndexRegistry.sol @@ -12,7 +12,7 @@ contract IndexRegistry is IndexRegistryStorage { /// @notice when applied to a function, only allows the RegistryCoordinator to call it modifier onlyRegistryCoordinator() { - require(msg.sender == address(registryCoordinator), "IndexRegistry.onlyRegistryCoordinator: caller is not the registry coordinator"); + _checkRegistryCoordinator(); _; } @@ -340,4 +340,8 @@ contract IndexRegistry is IndexRegistryStorage { function totalOperatorsForQuorum(uint8 quorumNumber) external view returns (uint32){ return _latestQuorumUpdate(quorumNumber).numOperators; } + + function _checkRegistryCoordinator() internal view { + require(msg.sender == address(registryCoordinator), "IndexRegistry.onlyRegistryCoordinator: caller is not the registry coordinator"); + } } diff --git a/src/OperatorStateRetriever.sol b/src/OperatorStateRetriever.sol index ffdc65fe..f0547a2d 100644 --- a/src/OperatorStateRetriever.sol +++ b/src/OperatorStateRetriever.sol @@ -178,4 +178,37 @@ contract OperatorStateRetriever { } return quorumBitmaps; } + + /** + * @notice This function returns the operatorIds for each of the operators in the operators array + * @param registryCoordinator is the AVS registry coordinator to fetch the operator information from + * @param operators is the array of operator address to get corresponding operatorIds for + * @dev if an operator is not registered, the operatorId will be 0 + */ + function getBatchOperatorId( + IRegistryCoordinator registryCoordinator, + address[] memory operators + ) external view returns (bytes32[] memory operatorIds) { + operatorIds = new bytes32[](operators.length); + for (uint256 i = 0; i < operators.length; ++i) { + operatorIds[i] = registryCoordinator.getOperatorId(operators[i]); + } + } + + /** + * @notice This function returns the operator addresses for each of the operators in the operatorIds array + * @param registryCoordinator is the AVS registry coordinator to fetch the operator information from + * @param operators is the array of operatorIds to get corresponding operator addresses for + * @dev if an operator is not registered, the operator address will be 0 + */ + function getBatchOperatorFromId( + IRegistryCoordinator registryCoordinator, + bytes32[] memory operatorIds + ) external view returns (address[] memory operators) { + operators = new address[](operatorIds.length); + for (uint256 i = 0; i < operatorIds.length; ++i) { + operators[i] = registryCoordinator.getOperatorFromId(operatorIds[i]); + } + } + } diff --git a/src/RegistryCoordinator.sol b/src/RegistryCoordinator.sol index 19367892..a774fb19 100644 --- a/src/RegistryCoordinator.sol +++ b/src/RegistryCoordinator.sol @@ -3,12 +3,12 @@ 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 {ISocketUpdater} from "./interfaces/ISocketUpdater.sol"; 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 {IRegistryCoordinator} from "./interfaces/IRegistryCoordinator.sol"; +import {ISocketRegistry} from "./interfaces/ISocketRegistry.sol"; import {EIP1271SignatureUtils} from "eigenlayer-contracts/src/contracts/libraries/EIP1271SignatureUtils.sol"; import {BitmapUtils} from "./libraries/BitmapUtils.sol"; @@ -35,24 +35,20 @@ contract RegistryCoordinator is Pausable, OwnableUpgradeable, RegistryCoordinatorStorage, - ISocketUpdater, ISignatureUtils { using BitmapUtils for *; using BN254 for BN254.G1Point; modifier onlyEjector { - require(msg.sender == ejector, "RegistryCoordinator.onlyEjector: caller is not the ejector"); + _checkEjector(); _; } /// @dev Checks that `quorumNumber` corresponds to a quorum that has been created /// via `initialize` or `createQuorum` modifier quorumExists(uint8 quorumNumber) { - require( - quorumNumber < quorumCount, - "RegistryCoordinator.quorumExists: quorum does not exist" - ); + _checkQuorumExists(quorumNumber); _; } @@ -60,9 +56,10 @@ contract RegistryCoordinator is IServiceManager _serviceManager, IStakeRegistry _stakeRegistry, IBLSApkRegistry _blsApkRegistry, - IIndexRegistry _indexRegistry + IIndexRegistry _indexRegistry, + ISocketRegistry _socketRegistry ) - RegistryCoordinatorStorage(_serviceManager, _stakeRegistry, _blsApkRegistry, _indexRegistry) + RegistryCoordinatorStorage(_serviceManager, _stakeRegistry, _blsApkRegistry, _indexRegistry, _socketRegistry) EIP712("AVSRegistryCoordinator", "v0.0.1") { _disableInitializers(); @@ -91,7 +88,7 @@ contract RegistryCoordinator is ) external initializer { require( _operatorSetParams.length == _minimumStakes.length && _minimumStakes.length == _strategyParams.length, - "RegistryCoordinator.initialize: input length mismatch" + "RegCoord.initialize: input length mismatch" ); // Initialize roles @@ -157,7 +154,7 @@ contract RegistryCoordinator is require( numOperatorsPerQuorum[i] <= _quorumParams[quorumNumber].maxOperatorCount, - "RegistryCoordinator.registerOperator: operator count exceeds maximum" + "RegCoord.registerOperator: operator count exceeds maximum" ); } } @@ -182,7 +179,7 @@ contract RegistryCoordinator is 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, "RegCoord.registerOperatorWithChurn: input length mismatch"); /** * If the operator has NEVER registered a pubkey before, use `params` to register @@ -292,7 +289,7 @@ contract RegistryCoordinator is uint192 quorumBitmap = uint192(BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers, quorumCount)); require( operatorsPerQuorum.length == quorumNumbers.length, - "RegistryCoordinator.updateOperatorsForQuorum: input length mismatch" + "RegCoord.updateOperatorsForQuorum: input length mismatch" ); // For each quorum, update ALL registered operators @@ -303,7 +300,7 @@ contract RegistryCoordinator is address[] calldata currQuorumOperators = operatorsPerQuorum[i]; require( currQuorumOperators.length == indexRegistry.totalOperatorsForQuorum(quorumNumber), - "RegistryCoordinator.updateOperatorsForQuorum: number of updated operators does not match quorum total" + "RegCoord.updateOperatorsForQuorum: number of updated operators does not match quorum total" ); address prevOperatorAddress = address(0); @@ -322,12 +319,12 @@ contract RegistryCoordinator is // Check that the operator is registered require( BitmapUtils.isSet(currentBitmap, quorumNumber), - "RegistryCoordinator.updateOperatorsForQuorum: operator not in quorum" + "RegCoord.updateOperatorsForQuorum: operator not in quorum" ); // Prevent duplicate operators require( operator > prevOperatorAddress, - "RegistryCoordinator.updateOperatorsForQuorum: operators array must be sorted in ascending address order" + "RegCoord.updateOperatorsForQuorum: operators array must be sorted in ascending address order" ); } @@ -347,8 +344,8 @@ 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"); - emit OperatorSocketUpdate(_operatorInfo[msg.sender].operatorId, socket); + require(_operatorInfo[msg.sender].status == OperatorStatus.REGISTERED, "RegCoord.updateSocket: operator not registered"); + _setOperatorSocket(_operatorInfo[msg.sender].operatorId, socket); } /******************************************************************************* @@ -359,15 +356,28 @@ contract RegistryCoordinator is * @notice Forcibly deregisters an operator from one or more quorums * @param operator the operator to eject * @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 { - _deregisterOperator({ - operator: operator, - quorumNumbers: quorumNumbers - }); + lastEjectionTimestamp[operator] = block.timestamp; + + OperatorInfo storage operatorInfo = _operatorInfo[operator]; + bytes32 operatorId = operatorInfo.operatorId; + 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 + }); + } } /******************************************************************************* @@ -423,6 +433,16 @@ contract RegistryCoordinator is _setEjector(_ejector); } + /** + * @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 + */ + function setEjectionCooldown(uint256 _ejectionCooldown) external onlyOwner { + ejectionCooldown = _ejectionCooldown; + } + /******************************************************************************* INTERNAL FUNCTIONS *******************************************************************************/ @@ -453,10 +473,13 @@ contract RegistryCoordinator is */ 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(), "RegCoord._registerOperator: bitmap cannot be 0"); + require(quorumsToAdd.noBitsInCommon(currentBitmap), "RegCoord._registerOperator: operator already registered for some quorums"); uint192 newBitmap = uint192(currentBitmap.plus(quorumsToAdd)); + // Check that the operator can reregister if ejected + require(lastEjectionTimestamp[operator] + ejectionCooldown < block.timestamp, "RegCoord._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. @@ -466,8 +489,6 @@ contract RegistryCoordinator is 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) { @@ -479,6 +500,8 @@ contract RegistryCoordinator is // Register the operator with the EigenLayer core contracts via this AVS's ServiceManager serviceManager.registerOperatorToAVS(operator, operatorSignature); + _setOperatorSocket(operatorId, socket); + emit OperatorRegistered(operator, operatorId); } @@ -491,6 +514,26 @@ contract RegistryCoordinator is return results; } + /** + * @notice Checks if the caller is the ejector + * @dev Reverts if the caller is not the ejector + */ + function _checkEjector() internal view { + require(msg.sender == ejector, "RegCoord.onlyEjector: caller is not the ejector"); + } + + /** + * @notice Checks if a quorum exists + * @param quorumNumber The quorum number to check + * @dev Reverts if the quorum does not exist + */ + function _checkQuorumExists(uint8 quorumNumber) internal view { + require( + quorumNumber < quorumCount, + "RegCoord.quorumExists: quorum does not exist" + ); + } + /** * @notice Fetches an operator's pubkey hash from the BLSApkRegistry. If the * operator has not registered a pubkey, attempts to register a pubkey using @@ -538,18 +581,18 @@ contract RegistryCoordinator is ) 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, "RegCoord._validateChurn: cannot churn self"); + require(kickParams.quorumNumber == quorumNumber, "RegCoord._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); require( newOperatorStake > _individualKickThreshold(operatorToKickStake, setParams), - "RegistryCoordinator._validateChurn: incoming operator has insufficient stake for churn" + "RegCoord._validateChurn: incoming operator has insufficient stake for churn" ); require( operatorToKickStake < _totalKickThreshold(totalQuorumStake, setParams), - "RegistryCoordinator._validateChurn: cannot kick operator with more than kickBIPsOfTotalStake" + "RegCoord._validateChurn: cannot kick operator with more than kickBIPsOfTotalStake" ); } @@ -565,7 +608,7 @@ contract RegistryCoordinator is // 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, "RegCoord._deregisterOperator: operator is not registered"); /** * Get bitmap of quorums to deregister from and operator's current bitmap. Validate that: @@ -576,8 +619,8 @@ contract RegistryCoordinator is */ 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(), "RegCoord._deregisterOperator: bitmap cannot be 0"); + require(quorumsToRemove.isSubsetOf(currentBitmap), "RegCoord._deregisterOperator: operator is not registered for quorums"); uint192 newBitmap = uint192(currentBitmap.minus(quorumsToRemove)); // Update operator's bitmap and status @@ -649,8 +692,8 @@ contract RegistryCoordinator is 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], "RegCoord._verifyChurnApproverSignature: churnApprover salt already used"); + require(churnApproverSignature.expiry >= block.timestamp, "RegCoord._verifyChurnApproverSignature: churnApprover signature expired"); // set salt used to true isChurnApproverSaltUsed[churnApproverSignature.salt] = true; @@ -678,7 +721,7 @@ 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, "RegCoord.createQuorum: max quorums reached"); quorumCount = prevQuorumCount + 1; // The previous count is the new quorum's number @@ -760,7 +803,7 @@ contract RegistryCoordinator is } revert( - "RegistryCoordinator.getQuorumBitmapIndexAtBlockNumber: no bitmap update found for operatorId at block number" + "RegCoord.getQuorumBitmapIndexAtBlockNumber: no bitmap update found for operator at blockNumber" ); } @@ -779,6 +822,11 @@ contract RegistryCoordinator is ejector = newEjector; } + function _setOperatorSocket(bytes32 operatorId, string memory socket) internal { + socketRegistry.setOperatorSocket(operatorId, socket); + emit OperatorSocketUpdate(operatorId, socket); + } + /******************************************************************************* VIEW FUNCTIONS *******************************************************************************/ @@ -844,11 +892,11 @@ contract RegistryCoordinator is */ require( blockNumber >= quorumBitmapUpdate.updateBlockNumber, - "RegistryCoordinator.getQuorumBitmapAtBlockNumberByIndex: quorumBitmapUpdate is from after blockNumber" + "RegCoord.getQuorumBitmapAtBlockNumberByIndex: quorumBitmapUpdate is from after blockNumber" ); require( quorumBitmapUpdate.nextUpdateBlockNumber == 0 || blockNumber < quorumBitmapUpdate.nextUpdateBlockNumber, - "RegistryCoordinator.getQuorumBitmapAtBlockNumberByIndex: quorumBitmapUpdate is from before blockNumber" + "RegCoord.getQuorumBitmapAtBlockNumberByIndex: quorumBitmapUpdate is from before blockNumber" ); return quorumBitmapUpdate.quorumBitmap; diff --git a/src/RegistryCoordinatorStorage.sol b/src/RegistryCoordinatorStorage.sol index e8c42850..a8b4fced 100644 --- a/src/RegistryCoordinatorStorage.sol +++ b/src/RegistryCoordinatorStorage.sol @@ -6,6 +6,7 @@ import {IStakeRegistry} from "./interfaces/IStakeRegistry.sol"; import {IIndexRegistry} from "./interfaces/IIndexRegistry.sol"; import {IServiceManager} from "./interfaces/IServiceManager.sol"; import {IRegistryCoordinator} from "./interfaces/IRegistryCoordinator.sol"; +import {ISocketRegistry} from "./interfaces/ISocketRegistry.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 Socket Registry contract that will keep track of operators' sockets + ISocketRegistry public immutable socketRegistry; /******************************************************************************* STATE @@ -64,19 +67,26 @@ abstract contract RegistryCoordinatorStorage is IRegistryCoordinator { /// @notice the address of the entity allowed to eject operators from the AVS address public ejector; + /// @notice the last timestamp an operator was ejected + mapping(address => uint256) public lastEjectionTimestamp; + /// @notice the delay in seconds before an operator can reregister after being ejected + uint256 public ejectionCooldown; + constructor( IServiceManager _serviceManager, IStakeRegistry _stakeRegistry, IBLSApkRegistry _blsApkRegistry, - IIndexRegistry _indexRegistry + IIndexRegistry _indexRegistry, + ISocketRegistry _socketRegistry ) { serviceManager = _serviceManager; stakeRegistry = _stakeRegistry; blsApkRegistry = _blsApkRegistry; indexRegistry = _indexRegistry; + socketRegistry = _socketRegistry; } // storage gap for upgradeability // slither-disable-next-line shadowing-state - uint256[41] private __GAP; + uint256[39] private __GAP; } diff --git a/src/ServiceManagerBase.sol b/src/ServiceManagerBase.sol index 71d262d1..65a5a152 100644 --- a/src/ServiceManagerBase.sol +++ b/src/ServiceManagerBase.sol @@ -1,28 +1,28 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.12; -import {OwnableUpgradeable} from "@openzeppelin-upgrades/contracts/access/OwnableUpgradeable.sol"; - -import {BitmapUtils} from "./libraries/BitmapUtils.sol"; +import {Initializable} from "@openzeppelin-upgrades/contracts/proxy/utils/Initializable.sol"; +import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.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 {ServiceManagerBaseStorage} from "./ServiceManagerBaseStorage.sol"; import {IServiceManager} from "./interfaces/IServiceManager.sol"; import {IRegistryCoordinator} from "./interfaces/IRegistryCoordinator.sol"; import {IStakeRegistry} from "./interfaces/IStakeRegistry.sol"; +import {BitmapUtils} from "./libraries/BitmapUtils.sol"; /** * @title Minimal implementation of a ServiceManager-type contract. * This contract can be inherited from or simply used as a point-of-reference. * @author Layr Labs, Inc. */ -abstract contract ServiceManagerBase is IServiceManager, OwnableUpgradeable { +abstract contract ServiceManagerBase is ServiceManagerBaseStorage { + using SafeERC20 for IERC20; using BitmapUtils for *; - IRegistryCoordinator internal immutable _registryCoordinator; - IStakeRegistry internal immutable _stakeRegistry; - IAVSDirectory internal immutable _avsDirectory; - /// @notice when applied to a function, only allows the RegistryCoordinator to call it modifier onlyRegistryCoordinator() { require( @@ -32,20 +32,42 @@ abstract contract ServiceManagerBase is IServiceManager, OwnableUpgradeable { _; } + /// @notice only rewardsInitiator can call createAVSRewardsSubmission + modifier onlyRewardsInitiator() { + _checkRewardsInitiator(); + _; + } + + function _checkRewardsInitiator() internal view { + require( + msg.sender == rewardsInitiator, + "ServiceManagerBase.onlyRewardsInitiator: caller is not the rewards initiator" + ); + } + /// @notice Sets the (immutable) `_registryCoordinator` address constructor( IAVSDirectory __avsDirectory, + IRewardsCoordinator __rewardsCoordinator, IRegistryCoordinator __registryCoordinator, IStakeRegistry __stakeRegistry - ) { - _avsDirectory = __avsDirectory; - _registryCoordinator = __registryCoordinator; - _stakeRegistry = __stakeRegistry; + ) + ServiceManagerBaseStorage( + __avsDirectory, + __rewardsCoordinator, + __registryCoordinator, + __stakeRegistry + ) + { _disableInitializers(); } - function __ServiceManagerBase_init(address initialOwner) internal virtual onlyInitializing { + function __ServiceManagerBase_init( + address initialOwner, + address _rewardsInitiator + ) internal virtual onlyInitializing { _transferOwnership(initialOwner); + _setRewardsInitiator(_rewardsInitiator); } /** @@ -53,10 +75,109 @@ abstract contract ServiceManagerBase is IServiceManager, OwnableUpgradeable { * @param _metadataURI is the metadata URI for the AVS * @dev only callable by the owner */ - function updateAVSMetadataURI(string memory _metadataURI) public virtual onlyOwner { + function updateAVSMetadataURI( + string memory _metadataURI + ) public virtual onlyOwner { _avsDirectory.updateAVSMetadataURI(_metadataURI); } + /** + * @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 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 + * @dev This function will revert if the `rewardsSubmission` is malformed, + * e.g. if the `strategies` and `weights` arrays are of non-equal lengths + * @dev This function may fail to execute with a large number of submissions due to gas limits. Use a + * smaller array of submissions if necessary. + */ + 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.safeTransferFrom( + msg.sender, + address(this), + rewardsSubmissions[i].amount + ); + rewardsSubmissions[i].token.safeIncreaseAllowance( + address(_rewardsCoordinator), + rewardsSubmissions[i].amount + ); + } + + _rewardsCoordinator.createAVSRewardsSubmission(rewardsSubmissions); + } + + /** + * @notice Creates a new operator-directed rewards submission, to be split amongst the operators and + * set of stakers delegated to operators who are registered to this `avs`. + * @param operatorDirectedRewardsSubmissions The operator-directed rewards submissions being created. + * @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 This contract needs a token approval of sum of all `operatorRewards` in the `operatorDirectedRewardsSubmissions`, before calling this function. + * @dev Strategies must be in ascending order of addresses to check for duplicates + * @dev Operators must be in ascending order of addresses to check for duplicates. + * @dev This function will revert if the `operatorDirectedRewardsSubmissions` is malformed. + * @dev This function may fail to execute with a large number of submissions due to gas limits. Use a + * smaller array of submissions if necessary. + */ + function createOperatorDirectedAVSRewardsSubmission( + IRewardsCoordinator.OperatorDirectedRewardsSubmission[] + calldata operatorDirectedRewardsSubmissions + ) public virtual onlyRewardsInitiator { + for ( + uint256 i = 0; + i < operatorDirectedRewardsSubmissions.length; + ++i + ) { + // Calculate total amount of token to transfer + uint256 totalAmount = 0; + for ( + uint256 j = 0; + j < + operatorDirectedRewardsSubmissions[i].operatorRewards.length; + ++j + ) { + totalAmount += operatorDirectedRewardsSubmissions[i] + .operatorRewards[j] + .amount; + } + + // Transfer token to ServiceManager and approve RewardsCoordinator to transfer again + // in createOperatorDirectedAVSRewardsSubmission() call + operatorDirectedRewardsSubmissions[i].token.safeTransferFrom( + msg.sender, + address(this), + totalAmount + ); + operatorDirectedRewardsSubmissions[i].token.safeIncreaseAllowance( + address(_rewardsCoordinator), + totalAmount + ); + } + + _rewardsCoordinator.createOperatorDirectedAVSRewardsSubmission( + address(this), + operatorDirectedRewardsSubmissions + ); + } + + /** + * @notice Forwards a call to Eigenlayer's RewardsCoordinator contract to set the address of the entity that can call `processClaim` on behalf of this contract. + * @param claimer The address of the entity that can call `processClaim` on behalf of the earner + * @dev Only callable by the owner. + */ + function setClaimerFor(address claimer) public virtual onlyOwner { + _rewardsCoordinator.setClaimerFor(claimer); + } + /** * @notice Forwards a call to EigenLayer's AVSDirectory contract to confirm operator registration with the AVS * @param operator The address of the operator to register. @@ -73,34 +194,61 @@ abstract contract ServiceManagerBase is IServiceManager, OwnableUpgradeable { * @notice Forwards a call to EigenLayer's AVSDirectory contract to confirm operator deregistration from the AVS * @param operator The address of the operator to deregister. */ - function deregisterOperatorFromAVS(address operator) public virtual onlyRegistryCoordinator { + function deregisterOperatorFromAVS( + address operator + ) public virtual onlyRegistryCoordinator { _avsDirectory.deregisterOperatorFromAVS(operator); } + /** + * @notice Sets the rewards initiator address + * @param newRewardsInitiator The new rewards initiator address + * @dev only callable by the owner + */ + function setRewardsInitiator( + address newRewardsInitiator + ) external onlyOwner { + _setRewardsInitiator(newRewardsInitiator); + } + + function _setRewardsInitiator(address newRewardsInitiator) internal { + emit RewardsInitiatorUpdated(rewardsInitiator, newRewardsInitiator); + rewardsInitiator = newRewardsInitiator; + } + /** * @notice Returns the list of strategies that the AVS supports for restaking * @dev This function is intended to be called off-chain - * @dev No guarantee is made on uniqueness of each element in the returned array. + * @dev No guarantee is made on uniqueness of each element in the returned array. * The off-chain service should do that validation separately */ - function getRestakeableStrategies() external view returns (address[] memory) { + function getRestakeableStrategies() + external + view + virtual + returns (address[] memory) + { uint256 quorumCount = _registryCoordinator.quorumCount(); if (quorumCount == 0) { return new address[](0); } - + uint256 strategyCount; - for(uint256 i = 0; i < quorumCount; i++) { + for (uint256 i = 0; i < quorumCount; i++) { strategyCount += _stakeRegistry.strategyParamsLength(uint8(i)); } address[] memory restakedStrategies = new address[](strategyCount); uint256 index = 0; - for(uint256 i = 0; i < _registryCoordinator.quorumCount(); i++) { - uint256 strategyParamsLength = _stakeRegistry.strategyParamsLength(uint8(i)); + for (uint256 i = 0; i < _registryCoordinator.quorumCount(); i++) { + uint256 strategyParamsLength = _stakeRegistry.strategyParamsLength( + uint8(i) + ); for (uint256 j = 0; j < strategyParamsLength; j++) { - restakedStrategies[index] = address(_stakeRegistry.strategyParamsByIndex(uint8(i), j).strategy); + restakedStrategies[index] = address( + _stakeRegistry.strategyParamsByIndex(uint8(i), j).strategy + ); index++; } } @@ -111,44 +259,52 @@ abstract contract ServiceManagerBase is IServiceManager, OwnableUpgradeable { * @notice Returns the list of strategies that the operator has potentially restaked on the AVS * @param operator The address of the operator to get restaked strategies for * @dev This function is intended to be called off-chain - * @dev No guarantee is made on whether the operator has shares for a strategy in a quorum or uniqueness + * @dev No guarantee is made on whether the operator has shares for a strategy in a quorum or uniqueness * of each element in the returned array. The off-chain service should do that validation separately */ - function getOperatorRestakedStrategies(address operator) external view returns (address[] memory) { + function getOperatorRestakedStrategies( + address operator + ) external view virtual returns (address[] memory) { bytes32 operatorId = _registryCoordinator.getOperatorId(operator); - uint192 operatorBitmap = _registryCoordinator.getCurrentQuorumBitmap(operatorId); + uint192 operatorBitmap = _registryCoordinator.getCurrentQuorumBitmap( + operatorId + ); if (operatorBitmap == 0 || _registryCoordinator.quorumCount() == 0) { return new address[](0); } // Get number of strategies for each quorum in operator bitmap - bytes memory operatorRestakedQuorums = BitmapUtils.bitmapToBytesArray(operatorBitmap); + bytes memory operatorRestakedQuorums = BitmapUtils.bitmapToBytesArray( + operatorBitmap + ); uint256 strategyCount; - for(uint256 i = 0; i < operatorRestakedQuorums.length; i++) { - strategyCount += _stakeRegistry.strategyParamsLength(uint8(operatorRestakedQuorums[i])); + for (uint256 i = 0; i < operatorRestakedQuorums.length; i++) { + strategyCount += _stakeRegistry.strategyParamsLength( + uint8(operatorRestakedQuorums[i]) + ); } // Get strategies for each quorum in operator bitmap address[] memory restakedStrategies = new address[](strategyCount); uint256 index = 0; - for(uint256 i = 0; i < operatorRestakedQuorums.length; i++) { + for (uint256 i = 0; i < operatorRestakedQuorums.length; i++) { uint8 quorum = uint8(operatorRestakedQuorums[i]); - uint256 strategyParamsLength = _stakeRegistry.strategyParamsLength(quorum); + uint256 strategyParamsLength = _stakeRegistry.strategyParamsLength( + quorum + ); for (uint256 j = 0; j < strategyParamsLength; j++) { - restakedStrategies[index] = address(_stakeRegistry.strategyParamsByIndex(quorum, j).strategy); + restakedStrategies[index] = address( + _stakeRegistry.strategyParamsByIndex(quorum, j).strategy + ); index++; } } - return restakedStrategies; + return restakedStrategies; } /// @notice Returns the EigenLayer AVSDirectory contract. function avsDirectory() external view override returns (address) { return address(_avsDirectory); } - - // storage gap for upgradeability - // slither-disable-next-line shadowing-state - uint256[50] private __GAP; } diff --git a/src/ServiceManagerBaseStorage.sol b/src/ServiceManagerBaseStorage.sol new file mode 100644 index 00000000..e0c1d86a --- /dev/null +++ b/src/ServiceManagerBaseStorage.sol @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.12; + +import {OwnableUpgradeable} from "@openzeppelin-upgrades/contracts/access/OwnableUpgradeable.sol"; + +import {IServiceManager} from "./interfaces/IServiceManager.sol"; +import {IRegistryCoordinator} from "./interfaces/IRegistryCoordinator.sol"; +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"; + +/** + * @title Storage variables for the `ServiceManagerBase` contract. + * @author Layr Labs, Inc. + * @notice This storage contract is separate from the logic to simplify the upgrade process. + */ +abstract contract ServiceManagerBaseStorage is IServiceManager, OwnableUpgradeable { + /** + * + * CONSTANTS AND IMMUTABLES + * + */ + IAVSDirectory internal immutable _avsDirectory; + IRewardsCoordinator internal immutable _rewardsCoordinator; + IRegistryCoordinator internal immutable _registryCoordinator; + IStakeRegistry internal immutable _stakeRegistry; + + /** + * + * STATE VARIABLES + * + */ + + /// @notice The address of the entity that can initiate rewards + address public rewardsInitiator; + + /// @notice Sets the (immutable) `_avsDirectory`, `_rewardsCoordinator`, `_registryCoordinator`, and `_stakeRegistry` addresses + constructor( + IAVSDirectory __avsDirectory, + IRewardsCoordinator __rewardsCoordinator, + IRegistryCoordinator __registryCoordinator, + IStakeRegistry __stakeRegistry + ) { + _avsDirectory = __avsDirectory; + _rewardsCoordinator = __rewardsCoordinator; + _registryCoordinator = __registryCoordinator; + _stakeRegistry = __stakeRegistry; + } + + // storage gap for upgradeability + uint256[49] private __GAP; +} diff --git a/src/ServiceManagerRouter.sol b/src/ServiceManagerRouter.sol index 477ed2db..e2259cfb 100644 --- a/src/ServiceManagerRouter.sol +++ b/src/ServiceManagerRouter.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.12; -import {IServiceManager} from "./interfaces/IServiceManager.sol"; +import {IServiceManagerUI} from "./interfaces/IServiceManagerUI.sol"; /** * @title Contract that proxies calls to a ServiceManager contract. @@ -20,7 +20,7 @@ contract ServiceManagerRouter { */ function getRestakeableStrategies(address serviceManager) external view returns (address[] memory) { bytes memory data = abi.encodeWithSelector( - IServiceManager.getRestakeableStrategies.selector + IServiceManagerUI.getRestakeableStrategies.selector ); return _makeCall(serviceManager, data); } @@ -32,7 +32,7 @@ contract ServiceManagerRouter { */ function getOperatorRestakedStrategies(address serviceManager, address operator) external view returns (address[] memory) { bytes memory data = abi.encodeWithSelector( - IServiceManager.getOperatorRestakedStrategies.selector, + IServiceManagerUI.getOperatorRestakedStrategies.selector, operator ); return _makeCall(serviceManager, data); diff --git a/src/SocketRegistry.sol b/src/SocketRegistry.sol new file mode 100644 index 00000000..8ede8dda --- /dev/null +++ b/src/SocketRegistry.sol @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.12; + +import {IRegistryCoordinator} from "./interfaces/IRegistryCoordinator.sol"; +import {ISocketRegistry} from "./interfaces/ISocketRegistry.sol"; + +/** + * @title A `Registry` that keeps track of operator sockets. + * @author Layr Labs, Inc. + */ +contract SocketRegistry is ISocketRegistry { + + /// @notice The address of the RegistryCoordinator + address public immutable registryCoordinator; + + /// @notice A mapping from operator IDs to their sockets + mapping(bytes32 => string) public operatorIdToSocket; + + /// @notice A modifier that only allows the RegistryCoordinator to call a function + modifier onlyRegistryCoordinator() { + require(msg.sender == address(registryCoordinator), "SocketRegistry.onlyRegistryCoordinator: caller is not the RegistryCoordinator"); + _; + } + + /// @notice A modifier that only allows the owner of the RegistryCoordinator to call a function + modifier onlyCoordinatorOwner() { + require(msg.sender == IRegistryCoordinator(registryCoordinator).owner(), "SocketRegistry.onlyCoordinatorOwner: caller is not the owner of the registryCoordinator"); + _; + } + + constructor(IRegistryCoordinator _registryCoordinator) { + registryCoordinator = address(_registryCoordinator); + } + + /// @notice sets the socket for an operator only callable by the RegistryCoordinator + function setOperatorSocket(bytes32 _operatorId, string memory _socket) external onlyRegistryCoordinator { + operatorIdToSocket[_operatorId] = _socket; + } + + /// @notice gets the stored socket for an operator + function getOperatorSocket(bytes32 _operatorId) external view returns (string memory) { + return operatorIdToSocket[_operatorId]; + } + +} diff --git a/src/StakeRegistry.sol b/src/StakeRegistry.sol index 87e04fdf..929b67d9 100644 --- a/src/StakeRegistry.sol +++ b/src/StakeRegistry.sol @@ -24,20 +24,17 @@ contract StakeRegistry is StakeRegistryStorage { using BitmapUtils for *; modifier onlyRegistryCoordinator() { - require( - msg.sender == address(registryCoordinator), - "StakeRegistry.onlyRegistryCoordinator: caller is not the RegistryCoordinator" - ); + _checkRegistryCoordinator(); _; } modifier onlyCoordinatorOwner() { - require(msg.sender == IRegistryCoordinator(registryCoordinator).owner(), "StakeRegistry.onlyCoordinatorOwner: caller is not the owner of the registryCoordinator"); + _checkRegistryCoordinatorOwner(); _; } modifier quorumExists(uint8 quorumNumber) { - require(_quorumExists(quorumNumber), "StakeRegistry.quorumExists: quorum does not exist"); + _checkQuorumExists(quorumNumber); _; } @@ -74,7 +71,7 @@ contract StakeRegistry is StakeRegistryStorage { for (uint256 i = 0; i < quorumNumbers.length; i++) { uint8 quorumNumber = uint8(quorumNumbers[i]); - require(_quorumExists(quorumNumber), "StakeRegistry.registerOperator: quorum does not exist"); + _checkQuorumExists(quorumNumber); // Retrieve the operator's current weighted stake for the quorum, reverting if they have not met // the minimum. @@ -121,7 +118,7 @@ contract StakeRegistry is StakeRegistryStorage { */ for (uint256 i = 0; i < quorumNumbers.length; i++) { uint8 quorumNumber = uint8(quorumNumbers[i]); - require(_quorumExists(quorumNumber), "StakeRegistry.deregisterOperator: quorum does not exist"); + _checkQuorumExists(quorumNumber); // Update the operator's stake for the quorum and retrieve the shares removed int256 stakeDelta = _recordOperatorStakeUpdate({ @@ -161,7 +158,7 @@ contract StakeRegistry is StakeRegistryStorage { */ for (uint256 i = 0; i < quorumNumbers.length; i++) { uint8 quorumNumber = uint8(quorumNumbers[i]); - require(_quorumExists(quorumNumber), "StakeRegistry.updateOperatorStake: quorum does not exist"); + _checkQuorumExists(quorumNumber); // Fetch the operator's current stake, applying weighting parameters and checking // against the minimum stake requirements for the quorum. @@ -697,7 +694,7 @@ contract StakeRegistry is StakeRegistryStorage { uint32[] memory indices = new uint32[](quorumNumbers.length); for (uint256 i = 0; i < quorumNumbers.length; i++) { uint8 quorumNumber = uint8(quorumNumbers[i]); - require(_quorumExists(quorumNumber), "StakeRegistry.getTotalStakeIndicesAtBlockNumber: quorum does not exist"); + _checkQuorumExists(quorumNumber); require( _totalStakeHistory[quorumNumber][0].updateBlockNumber <= blockNumber, "StakeRegistry.getTotalStakeIndicesAtBlockNumber: quorum has no stake history at blockNumber" @@ -712,4 +709,19 @@ contract StakeRegistry is StakeRegistryStorage { } return indices; } + + function _checkRegistryCoordinator() internal view { + require( + msg.sender == address(registryCoordinator), + "StakeRegistry.onlyRegistryCoordinator: caller is not the RegistryCoordinator" + ); + } + + function _checkRegistryCoordinatorOwner() internal view { + require(msg.sender == IRegistryCoordinator(registryCoordinator).owner(), "StakeRegistry.onlyCoordinatorOwner: caller is not the owner of the registryCoordinator"); + } + + function _checkQuorumExists(uint8 quorumNumber) internal view { + require(_quorumExists(quorumNumber), "StakeRegistry.quorumExists: quorum does not exist"); + } } diff --git a/src/interfaces/IECDSAStakeRegistryEventsAndErrors.sol b/src/interfaces/IECDSAStakeRegistryEventsAndErrors.sol index 48072f54..445db814 100644 --- a/src/interfaces/IECDSAStakeRegistryEventsAndErrors.sol +++ b/src/interfaces/IECDSAStakeRegistryEventsAndErrors.sol @@ -36,13 +36,20 @@ interface ECDSAStakeRegistryEventsAndErrors { /// @notice Emitted when the weight required to be an operator changes /// @param oldMinimumWeight The previous weight /// @param newMinimumWeight The updated weight - event UpdateMinimumWeight(uint256 oldMinimumWeight, uint256 newMinimumWeight); + event UpdateMinimumWeight( + uint256 oldMinimumWeight, + uint256 newMinimumWeight + ); /// @notice Emitted when the system updates an operator's weight /// @param _operator The address of the operator updated /// @param oldWeight The operator's weight before the update /// @param newWeight The operator's weight after the update - event OperatorWeightUpdated(address indexed _operator, uint256 oldWeight, uint256 newWeight); + event OperatorWeightUpdated( + address indexed _operator, + uint256 oldWeight, + uint256 newWeight + ); /// @notice Emitted when the system updates the total weight /// @param oldTotalWeight The total weight before the update @@ -52,6 +59,17 @@ interface ECDSAStakeRegistryEventsAndErrors { /// @notice Emits when setting a new threshold weight. event ThresholdWeightUpdated(uint256 _thresholdWeight); + /// @notice Emitted when an operator's signing key is updated + /// @param operator The address of the operator whose signing key was updated + /// @param updateBlock The block number at which the signing key was updated + /// @param newSigningKey The operator's signing key after the update + /// @param oldSigningKey The operator's signing key before the update + event SigningKeyUpdate( + address indexed operator, + uint256 indexed updateBlock, + address indexed newSigningKey, + address oldSigningKey + ); /// @notice Indicates when the lengths of the signers array and signatures array do not match. error LengthMismatch(); @@ -64,9 +82,12 @@ interface ECDSAStakeRegistryEventsAndErrors { /// @notice Thrown when the threshold update is greater than BPS error InvalidThreshold(); - /// @notice Thrown when missing operators in an update + /// @notice Thrown when missing operators in an update error MustUpdateAllOperators(); + /// @notice Reference blocks must be for blocks that have already been confirmed + error InvalidReferenceBlock(); + /// @notice Indicates operator weights were out of sync and the signed weight exceed the total error InvalidSignedWeight(); diff --git a/src/interfaces/IEjectionManager.sol b/src/interfaces/IEjectionManager.sol new file mode 100644 index 00000000..6649e75b --- /dev/null +++ b/src/interfaces/IEjectionManager.sol @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.12; + +/** + * @title Interface for a contract that ejects operators from an AVSs RegistryCoordinator + * @author Layr Labs, Inc. + */ +interface IEjectionManager { + + /// @notice A quorum's ratelimit parameters + struct QuorumEjectionParams { + uint32 rateLimitWindow; // Time delta to track ejection over + uint16 ejectableStakePercent; // Max stake to be ejectable per time delta + } + + /// @notice A stake ejection event + struct StakeEjection { + uint256 timestamp; // Timestamp of the ejection + uint256 stakeEjected; // Amount of stake ejected at the timestamp + } + + ///@notice Emitted when the ejector address is set + event EjectorUpdated(address ejector, bool status); + ///@notice Emitted when the ratelimit parameters for a quorum are set + 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 + event QuorumEjection(uint32 ejectedOperators, bool ratelimitHit); + + /** + * @notice Ejects operators from the AVSs registryCoordinator under a ratelimit + * @param _operatorIds The ids of the operators to eject for each quorum + */ + function ejectOperators(bytes32[][] memory _operatorIds) external; + + /** + * @notice Sets the ratelimit parameters for a quorum + * @param _quorumNumber The quorum number to set the ratelimit parameters for + * @param _quorumEjectionParams The quorum ratelimit parameters to set for the given quorum + */ + function setQuorumEjectionParams(uint8 _quorumNumber, QuorumEjectionParams memory _quorumEjectionParams) external; + + /** + * @notice Sets the address permissioned to eject operators under a ratelimit + * @param _ejector The address to permission + */ + function setEjector(address _ejector, bool _status) external; + + /** + * @notice Returns the amount of stake that can be ejected for a quorum at the current block.timestamp + * @param _quorumNumber The quorum number to view ejectable stake for + */ + function amountEjectableForQuorum(uint8 _quorumNumber) external view returns (uint256); +} \ No newline at end of file diff --git a/src/interfaces/IRegistryCoordinator.sol b/src/interfaces/IRegistryCoordinator.sol index 43fa7e0a..631ca3a3 100644 --- a/src/interfaces/IRegistryCoordinator.sol +++ b/src/interfaces/IRegistryCoordinator.sol @@ -25,6 +25,8 @@ interface IRegistryCoordinator { event EjectorUpdated(address prevEjector, address newEjector); + event OperatorSocketUpdate(bytes32 indexed operatorId, string socket); + /// @notice emitted when all the operators for a quorum are updated at once event QuorumBlockNumberUpdated(uint8 indexed quorumNumber, uint256 blocknumber); @@ -150,4 +152,10 @@ interface IRegistryCoordinator { /// @notice The owner of the registry coordinator function owner() external view returns (address); + + /** + * @notice Updates the socket of the msg.sender given they are a registered operator + * @param socket is the new socket of the operator + */ + function updateSocket(string memory socket) external; } diff --git a/src/interfaces/IServiceManager.sol b/src/interfaces/IServiceManager.sol index bda03a88..c8f49166 100644 --- a/src/interfaces/IServiceManager.sol +++ b/src/interfaces/IServiceManager.sol @@ -1,53 +1,56 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity >=0.5.0; -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 {IServiceManagerUI} from "./IServiceManagerUI.sol"; /** * @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 { +interface IServiceManager is IServiceManagerUI { /** - * @notice Updates the metadata URI for the AVS - * @param _metadataURI is the metadata URI for the AVS + * @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 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 + * @dev This function will revert if the `rewardsSubmission` is malformed, + * e.g. if the `strategies` and `weights` arrays are of non-equal lengths */ - function updateAVSMetadataURI(string memory _metadataURI) external; - - /** - * @notice Forwards a call to EigenLayer's DelegationManager contract to confirm operator registration 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 + function createAVSRewardsSubmission( + IRewardsCoordinator.RewardsSubmission[] calldata rewardsSubmissions ) external; /** - * @notice Forwards a call to EigenLayer's DelegationManager contract to confirm operator deregistration from the AVS - * @param operator The address of the operator to deregister. + * @notice Creates a new operator-directed rewards submission on behalf of an AVS, to be split amongst the operators and + * set of stakers delegated to operators who are registered to the `avs`. + * @param operatorDirectedRewardsSubmissions The operator-directed rewards submissions being created + * @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 This contract needs a token approval of sum of all `operatorRewards` in the `operatorDirectedRewardsSubmissions`, before calling this function. + * @dev Strategies must be in ascending order of addresses to check for duplicates + * @dev Operators must be in ascending order of addresses to check for duplicates. + * @dev This function will revert if the `operatorDirectedRewardsSubmissions` is malformed. */ - function deregisterOperatorFromAVS(address operator) external; - - /** - * @notice Returns the list of strategies that the operator has potentially restaked on the AVS - * @param operator The address of the operator to get restaked strategies for - * @dev This function is intended to be called off-chain - * @dev No guarantee is made on whether the operator has shares for a strategy in a quorum or uniqueness - * of each element in the returned array. The off-chain service should do that validation separately - */ - function getOperatorRestakedStrategies(address operator) external view returns (address[] memory); + function createOperatorDirectedAVSRewardsSubmission( + IRewardsCoordinator.OperatorDirectedRewardsSubmission[] + calldata operatorDirectedRewardsSubmissions + ) external; /** - * @notice Returns the list of strategies that the AVS supports for restaking - * @dev This function is intended to be called off-chain - * @dev No guarantee is made on uniqueness of each element in the returned array. - * The off-chain service should do that validation separately + * @notice Forwards a call to Eigenlayer's RewardsCoordinator contract to set the address of the entity that can call `processClaim` on behalf of this contract. + * @param claimer The address of the entity that can call `processClaim` on behalf of the earner + * @dev Only callable by the owner. */ - function getRestakeableStrategies() external view returns (address[] memory); + function setClaimerFor(address claimer) external; - /// @notice Returns the EigenLayer AVSDirectory contract. - function avsDirectory() external view returns (address); + // EVENTS + event RewardsInitiatorUpdated( + address prevRewardsInitiator, + address newRewardsInitiator + ); } diff --git a/src/interfaces/IServiceManagerUI.sol b/src/interfaces/IServiceManagerUI.sol new file mode 100644 index 00000000..92cdce9c --- /dev/null +++ b/src/interfaces/IServiceManagerUI.sol @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity >=0.5.0; + +import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol"; + +/** + * @title Minimal interface for a ServiceManager-type contract that AVS ServiceManager contracts must implement + * for eigenlabs to be able to index their data on the AVS marketplace frontend. + * @author Layr Labs, Inc. + */ +interface IServiceManagerUI { + /** + * Metadata should follow the format outlined by this example. + { + "name": "EigenLabs AVS 1", + "website": "https://www.eigenlayer.xyz/", + "description": "This is my 1st AVS", + "logo": "https://holesky-operator-metadata.s3.amazonaws.com/eigenlayer.png", + "twitter": "https://twitter.com/eigenlayer" + } + * @notice Updates the metadata URI for the AVS + * @param _metadataURI is the metadata URI for the AVS + */ + function updateAVSMetadataURI(string memory _metadataURI) external; + + /** + * @notice Forwards a call to EigenLayer's AVSDirectory contract to confirm operator registration 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 Forwards a call to EigenLayer's AVSDirectory contract to confirm operator deregistration from the AVS + * @param operator The address of the operator to deregister. + */ + function deregisterOperatorFromAVS(address operator) external; + + /** + * @notice Returns the list of strategies that the operator has potentially restaked on the AVS + * @param operator The address of the operator to get restaked strategies for + * @dev This function is intended to be called off-chain + * @dev No guarantee is made on whether the operator has shares for a strategy in a quorum or uniqueness + * of each element in the returned array. The off-chain service should do that validation separately + */ + function getOperatorRestakedStrategies(address operator) external view returns (address[] memory); + + /** + * @notice Returns the list of strategies that the AVS supports for restaking + * @dev This function is intended to be called off-chain + * @dev No guarantee is made on uniqueness of each element in the returned array. + * The off-chain service should do that validation separately + */ + function getRestakeableStrategies() external view returns (address[] memory); + + /// @notice Returns the EigenLayer AVSDirectory contract. + function avsDirectory() external view returns (address); +} diff --git a/src/interfaces/ISocketRegistry.sol b/src/interfaces/ISocketRegistry.sol new file mode 100644 index 00000000..136a345b --- /dev/null +++ b/src/interfaces/ISocketRegistry.sol @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +interface ISocketRegistry { + /// @notice sets the socket for an operator only callable by the RegistryCoordinator + function setOperatorSocket(bytes32 _operatorId, string memory _socket) external; + + /// @notice gets the stored socket for an operator + function getOperatorSocket(bytes32 _operatorId) external view returns (string memory); +} diff --git a/src/interfaces/ISocketUpdater.sol b/src/interfaces/ISocketUpdater.sol deleted file mode 100644 index dcf5a865..00000000 --- a/src/interfaces/ISocketUpdater.sol +++ /dev/null @@ -1,20 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; - -/** - * @title Interface for an `ISocketUpdater` where operators can update their sockets. - * @author Layr Labs, Inc. - */ -interface ISocketUpdater { - // EVENTS - - 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 - */ - function updateSocket(string memory socket) external; -} diff --git a/src/unaudited/ECDSAServiceManagerBase.sol b/src/unaudited/ECDSAServiceManagerBase.sol new file mode 100644 index 00000000..91738da5 --- /dev/null +++ b/src/unaudited/ECDSAServiceManagerBase.sol @@ -0,0 +1,353 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.12; + +import {OwnableUpgradeable} from "@openzeppelin-upgrades/contracts/access/OwnableUpgradeable.sol"; +import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; +import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol"; +import {IAVSDirectory} from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol"; +import {IServiceManager} from "../interfaces/IServiceManager.sol"; +import {IServiceManagerUI} from "../interfaces/IServiceManagerUI.sol"; +import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; +import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; +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"; + +abstract contract ECDSAServiceManagerBase is + IServiceManager, + OwnableUpgradeable +{ + using SafeERC20 for IERC20; + + /// @notice Address of the stake registry contract, which manages registration and stake recording. + address public immutable stakeRegistry; + + /// @notice Address of the AVS directory contract, which manages AVS-related data for registered operators. + address public immutable avsDirectory; + + /// @notice Address of the rewards coordinator contract, which handles rewards distributions. + address internal immutable rewardsCoordinator; + + /// @notice Address of the delegation manager contract, which manages staker delegations to operators. + address internal immutable delegationManager; + + /// @notice Address of the rewards initiator, which is allowed to create AVS rewards submissions. + address public rewardsInitiator; + + /** + * @dev Ensures that the function is only callable by the `stakeRegistry` contract. + * 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" + ); + _; + } + + /** + * @dev Ensures that the function is only callable by the `rewardsInitiator`. + */ + modifier onlyRewardsInitiator() { + _checkRewardsInitiator(); + _; + } + + function _checkRewardsInitiator() internal view { + require( + msg.sender == rewardsInitiator, + "ECDSAServiceManagerBase.onlyRewardsInitiator: caller is not the rewards initiator" + ); + } + + /** + * @dev Constructor for ECDSAServiceManagerBase, initializing immutable contract addresses and disabling initializers. + * @param _avsDirectory The address of the AVS directory contract, managing AVS-related data for registered operators. + * @param _stakeRegistry The address of the stake registry contract, managing registration and stake recording. + * @param _rewardsCoordinator The address of the rewards coordinator contract, handling rewards distributions. + * @param _delegationManager The address of the delegation manager contract, managing staker delegations to operators. + */ + constructor( + address _avsDirectory, + address _stakeRegistry, + address _rewardsCoordinator, + address _delegationManager + ) { + avsDirectory = _avsDirectory; + stakeRegistry = _stakeRegistry; + rewardsCoordinator = _rewardsCoordinator; + delegationManager = _delegationManager; + _disableInitializers(); + } + + /** + * @dev Initializes the base service manager by transferring ownership to the initial owner. + * @param initialOwner The address to which the ownership of the contract will be transferred. + * @param _rewardsInitiator The address which is allowed to create AVS rewards submissions. + */ + function __ServiceManagerBase_init( + address initialOwner, + address _rewardsInitiator + ) internal virtual onlyInitializing { + _transferOwnership(initialOwner); + _setRewardsInitiator(_rewardsInitiator); + } + + /// @inheritdoc IServiceManagerUI + function updateAVSMetadataURI( + string memory _metadataURI + ) external virtual onlyOwner { + _updateAVSMetadataURI(_metadataURI); + } + + /// @inheritdoc IServiceManager + function createAVSRewardsSubmission( + IRewardsCoordinator.RewardsSubmission[] calldata rewardsSubmissions + ) external virtual onlyRewardsInitiator { + _createAVSRewardsSubmission(rewardsSubmissions); + } + + /// @inheritdoc IServiceManager + function createOperatorDirectedAVSRewardsSubmission( + IRewardsCoordinator.OperatorDirectedRewardsSubmission[] + calldata operatorDirectedRewardsSubmissions + ) external virtual onlyRewardsInitiator { + _createOperatorDirectedAVSRewardsSubmission( + operatorDirectedRewardsSubmissions + ); + } + + /// @inheritdoc IServiceManager + function setClaimerFor(address claimer) external virtual onlyOwner { + _setClaimerFor(claimer); + } + + /// @inheritdoc IServiceManagerUI + function registerOperatorToAVS( + address operator, + ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature + ) external virtual onlyStakeRegistry { + _registerOperatorToAVS(operator, operatorSignature); + } + + /// @inheritdoc IServiceManagerUI + function deregisterOperatorFromAVS( + address operator + ) external virtual onlyStakeRegistry { + _deregisterOperatorFromAVS(operator); + } + + /// @inheritdoc IServiceManagerUI + function getRestakeableStrategies() + external + view + virtual + returns (address[] memory) + { + return _getRestakeableStrategies(); + } + + /// @inheritdoc IServiceManagerUI + function getOperatorRestakedStrategies( + address _operator + ) external view virtual returns (address[] memory) { + return _getOperatorRestakedStrategies(_operator); + } + + /** + * @notice Forwards the call to update AVS metadata URI in the AVSDirectory contract. + * @dev This internal function is a proxy to the `updateAVSMetadataURI` function of the AVSDirectory contract. + * @param _metadataURI The new metadata URI to be set. + */ + function _updateAVSMetadataURI( + string memory _metadataURI + ) internal virtual { + IAVSDirectory(avsDirectory).updateAVSMetadataURI(_metadataURI); + } + + /** + * @notice Forwards the call to register an operator in the AVSDirectory contract. + * @dev This internal function is a proxy to the `registerOperatorToAVS` function of the AVSDirectory contract. + * @param operator The address of the operator to register. + * @param operatorSignature The signature, salt, and expiry details of the operator's registration. + */ + function _registerOperatorToAVS( + address operator, + ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature + ) internal virtual { + IAVSDirectory(avsDirectory).registerOperatorToAVS( + operator, + operatorSignature + ); + } + + /** + * @notice Forwards the call to deregister an operator from the AVSDirectory contract. + * @dev This internal function is a proxy to the `deregisterOperatorFromAVS` function of the AVSDirectory contract. + * @param operator The address of the operator to deregister. + */ + function _deregisterOperatorFromAVS(address operator) internal virtual { + IAVSDirectory(avsDirectory).deregisterOperatorFromAVS(operator); + } + + /** + * @notice Processes a batch of rewards submissions by transferring the specified amounts from the sender to this contract and then approving the RewardsCoordinator to use these amounts. + * @dev This function handles the transfer and approval of tokens necessary for rewards submissions. It then delegates the actual rewards logic to the RewardsCoordinator contract. + * @param rewardsSubmissions An array of `RewardsSubmission` structs, each representing rewards for a specific range. + */ + function _createAVSRewardsSubmission( + IRewardsCoordinator.RewardsSubmission[] calldata rewardsSubmissions + ) internal virtual { + for (uint256 i = 0; i < rewardsSubmissions.length; ++i) { + rewardsSubmissions[i].token.safeTransferFrom( + msg.sender, + address(this), + rewardsSubmissions[i].amount + ); + rewardsSubmissions[i].token.safeIncreaseAllowance( + rewardsCoordinator, + rewardsSubmissions[i].amount + ); + } + + IRewardsCoordinator(rewardsCoordinator).createAVSRewardsSubmission( + rewardsSubmissions + ); + } + + /** + * @notice Creates a new operator-directed rewards submission, to be split amongst the operators and + * set of stakers delegated to operators who are registered to this `avs`. + * @param operatorDirectedRewardsSubmissions The operator-directed rewards submissions being created. + */ + function _createOperatorDirectedAVSRewardsSubmission( + IRewardsCoordinator.OperatorDirectedRewardsSubmission[] + calldata operatorDirectedRewardsSubmissions + ) internal virtual { + for ( + uint256 i = 0; + i < operatorDirectedRewardsSubmissions.length; + ++i + ) { + // Calculate total amount of token to transfer + uint256 totalAmount = 0; + for ( + uint256 j = 0; + j < + operatorDirectedRewardsSubmissions[i].operatorRewards.length; + ++j + ) { + totalAmount += operatorDirectedRewardsSubmissions[i] + .operatorRewards[j] + .amount; + } + + // Transfer token to ServiceManager and approve RewardsCoordinator to transfer again + // in createOperatorDirectedAVSRewardsSubmission() call + operatorDirectedRewardsSubmissions[i].token.safeTransferFrom( + msg.sender, + address(this), + totalAmount + ); + operatorDirectedRewardsSubmissions[i].token.safeIncreaseAllowance( + rewardsCoordinator, + totalAmount + ); + } + + IRewardsCoordinator(rewardsCoordinator) + .createOperatorDirectedAVSRewardsSubmission( + address(this), + operatorDirectedRewardsSubmissions + ); + } + + /** + * @notice Forwards a call to Eigenlayer's RewardsCoordinator contract to set the address of the entity that can call `processClaim` on behalf of this contract. + * @param claimer The address of the entity that can call `processClaim` on behalf of the earner. + */ + function _setClaimerFor(address claimer) internal virtual { + IRewardsCoordinator(rewardsCoordinator).setClaimerFor(claimer); + } + + /** + * @notice Retrieves the addresses of all strategies that are part of the current quorum. + * @dev Fetches the quorum configuration from the ECDSAStakeRegistry and extracts the strategy addresses. + * @return strategies An array of addresses representing the strategies in the current quorum. + */ + function _getRestakeableStrategies() + internal + view + virtual + returns (address[] memory) + { + Quorum memory quorum = ECDSAStakeRegistry(stakeRegistry).quorum(); + address[] memory strategies = new address[](quorum.strategies.length); + for (uint256 i = 0; i < quorum.strategies.length; i++) { + strategies[i] = address(quorum.strategies[i].strategy); + } + return strategies; + } + + /** + * @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, + * and filters out strategies with non-zero shares indicating active restaking by the operator. + * @param _operator The address of the operator whose restaked strategies are to be retrieved. + * @return restakedStrategies An array of addresses of strategies where the operator has active restakes. + */ + function _getOperatorRestakedStrategies( + address _operator + ) internal view virtual returns (address[] memory) { + Quorum memory quorum = ECDSAStakeRegistry(stakeRegistry).quorum(); + uint256 count = quorum.strategies.length; + IStrategy[] memory strategies = new IStrategy[](count); + for (uint256 i; i < count; i++) { + strategies[i] = quorum.strategies[i].strategy; + } + uint256[] memory shares = IDelegationManager(delegationManager) + .getOperatorShares(_operator, strategies); + + uint256 activeCount; + for (uint256 i; i < count; i++) { + if (shares[i] > 0) { + activeCount++; + } + } + + // Resize the array to fit only the active strategies + address[] memory restakedStrategies = new address[](activeCount); + uint256 index; + for (uint256 j = 0; j < count; j++) { + if (shares[j] > 0) { + restakedStrategies[index] = address(strategies[j]); + index++; + } + } + + return restakedStrategies; + } + + /** + * @notice Sets the rewards initiator address. + * @param newRewardsInitiator The new rewards initiator address. + * @dev Only callable by the owner. + */ + function setRewardsInitiator( + address newRewardsInitiator + ) external onlyOwner { + _setRewardsInitiator(newRewardsInitiator); + } + + function _setRewardsInitiator(address newRewardsInitiator) internal { + emit RewardsInitiatorUpdated(rewardsInitiator, newRewardsInitiator); + rewardsInitiator = newRewardsInitiator; + } + + // storage gap for upgradeability + // slither-disable-next-line shadowing-state + uint256[49] private __GAP; +} diff --git a/src/unaudited/ECDSAStakeRegistry.sol b/src/unaudited/ECDSAStakeRegistry.sol index 94b6c638..ab4bdbeb 100644 --- a/src/unaudited/ECDSAStakeRegistry.sol +++ b/src/unaudited/ECDSAStakeRegistry.sol @@ -43,13 +43,14 @@ contract ECDSAStakeRegistry is __ECDSAStakeRegistry_init(_serviceManager, _thresholdWeight, _quorum); } - /// @notice Registers a new operator using a provided signature + /// @notice Registers a new operator using a provided signature and signing key /// @param _operatorSignature Contains the operator's signature, salt, and expiry + /// @param _signingKey The signing key to add to the operator's history function registerOperatorWithSignature( - address _operator, - ISignatureUtils.SignatureWithSaltAndExpiry memory _operatorSignature + ISignatureUtils.SignatureWithSaltAndExpiry memory _operatorSignature, + address _signingKey ) external { - _registerOperatorWithSig(_operator, _operatorSignature); + _registerOperatorWithSig(msg.sender, _operatorSignature, _signingKey); } /// @notice Deregisters an existing operator @@ -58,7 +59,19 @@ contract ECDSAStakeRegistry is } /** - * @notice Updates the StakeRegistry's view of one or more operators' stakes adding a new entry in their history of stake checkpoints, + * @notice Updates the signing key for an operator + * @dev Only callable by the operator themselves + * @param _newSigningKey The new signing key to set for the operator + */ + function updateOperatorSigningKey(address _newSigningKey) external { + if (!_operatorRegistered[msg.sender]) { + revert OperatorNotRegistered(); + } + _updateOperatorSigningKey(msg.sender, _newSigningKey); + } + + /** + * @notice Updates the StakeRegistry's view of one or more operators' stakes adding a new entry in their history of stake checkpoints, * @dev Queries stakes from the Eigenlayer core DelegationManager contract * @param _operators A list of operator addresses to update */ @@ -68,12 +81,15 @@ contract ECDSAStakeRegistry is /** * @notice Updates the quorum configuration and the set of operators - * @dev Only callable by the contract owner. + * @dev Only callable by the contract owner. * It first updates the quorum configuration and then updates the list of operators. * @param _quorum The new quorum configuration, including strategies and their new weights * @param _operators The list of operator addresses to update stakes for */ - function updateQuorumConfig(Quorum memory _quorum, address[] memory _operators) external onlyOwner { + function updateQuorumConfig( + Quorum memory _quorum, + address[] memory _operators + ) external onlyOwner { _updateQuorumConfig(_quorum); _updateOperators(_operators); } @@ -81,17 +97,20 @@ contract ECDSAStakeRegistry is /// @notice Updates the weight an operator must have to join the operator set /// @dev Access controlled to the contract owner /// @param _newMinimumWeight The new weight an operator must have to join the operator set - function updateMinimumWeight(uint256 _newMinimumWeight, address[] memory _operators) external onlyOwner { + function updateMinimumWeight( + uint256 _newMinimumWeight, + address[] memory _operators + ) external onlyOwner { _updateMinimumWeight(_newMinimumWeight); _updateOperators(_operators); } /** * @notice Sets a new cumulative threshold weight for message validation by operator set signatures. - * @dev This function can only be invoked by the owner of the contract. It delegates the update to - * an internal function `_updateStakeThreshold`. - * @param _thresholdWeight The updated threshold weight required to validate a message. This is the - * cumulative weight that must be met or exceeded by the sum of the stakes of the signatories for + * @dev This function can only be invoked by the owner of the contract. It delegates the update to + * an internal function `_updateStakeThreshold`. + * @param _thresholdWeight The updated threshold weight required to validate a message. This is the + * cumulative weight that must be met or exceeded by the sum of the stakes of the signatories for * a message to be deemed valid. */ function updateStakeThreshold(uint256 _thresholdWeight) external onlyOwner { @@ -100,17 +119,18 @@ contract ECDSAStakeRegistry is /// @notice Verifies if the provided signature data is valid for the given data hash. /// @param _dataHash The hash of the data that was signed. - /// @param _signatureData Encoded signature data consisting of an array of signers, an array of signatures, and a reference block number. + /// @param _signatureData Encoded signature data consisting of an array of operators, an array of signatures, and a reference block number. /// @return The function selector that indicates the signature is valid according to ERC1271 standard. function isValidSignature( bytes32 _dataHash, bytes memory _signatureData ) external view returns (bytes4) { - (address[] memory signers, bytes[] memory signatures, uint32 referenceBlock) = abi.decode( - _signatureData, - (address[], bytes[], uint32) - ); - _checkSignatures(_dataHash, signers, signatures, referenceBlock); + ( + address[] memory operators, + bytes[] memory signatures, + uint32 referenceBlock + ) = abi.decode(_signatureData, (address[], bytes[], uint32)); + _checkSignatures(_dataHash, operators, signatures, referenceBlock); return IERC1271Upgradeable.isValidSignature.selector; } @@ -120,10 +140,43 @@ contract ECDSAStakeRegistry is return _quorum; } + /** + * @notice Retrieves the latest signing key for a given operator. + * @param _operator The address of the operator. + * @return The latest signing key of the operator. + */ + function getLastestOperatorSigningKey( + address _operator + ) external view returns (address) { + return address(uint160(_operatorSigningKeyHistory[_operator].latest())); + } + + /** + * @notice Retrieves the latest signing key for a given operator at a specific block number. + * @param _operator The address of the operator. + * @param _blockNumber The block number to get the operator's signing key. + * @return The signing key of the operator at the given block. + */ + function getOperatorSigningKeyAtBlock( + address _operator, + uint256 _blockNumber + ) external view returns (address) { + return + address( + uint160( + _operatorSigningKeyHistory[_operator].getAtBlock( + _blockNumber + ) + ) + ); + } + /// @notice Retrieves the last recorded weight for a given operator. /// @param _operator The address of the operator. /// @return uint256 - The latest weight of the operator. - function getLastCheckpointOperatorWeight(address _operator) external view returns (uint256) { + function getLastCheckpointOperatorWeight( + address _operator + ) external view returns (uint256) { return _operatorWeightHistory[_operator].latest(); } @@ -133,9 +186,13 @@ contract ECDSAStakeRegistry is return _totalWeightHistory.latest(); } - /// @notice Retrieves the last recorded threshold weight + /// @notice Retrieves the last recorded threshold weight /// @return uint256 - The latest threshold weight. - function getLastCheckpointThresholdWeight() external view returns (uint256) { + function getLastCheckpointThresholdWeight() + external + view + returns (uint256) + { return _thresholdWeightHistory.latest(); } @@ -168,10 +225,12 @@ contract ECDSAStakeRegistry is return _thresholdWeightHistory.getAtBlock(_blockNumber); } - function operatorRegistered(address _operator) external view returns (bool) { + function operatorRegistered( + address _operator + ) external view returns (bool) { return _operatorRegistered[_operator]; } - + /// @notice Returns the weight an operator must have to contribute to validating an AVS function minimumWeight() external view returns (uint256) { return _minimumWeight; @@ -180,18 +239,21 @@ contract ECDSAStakeRegistry is /// @notice Calculates the current weight of an operator based on their delegated stake in the strategies considered in the quorum /// @param _operator The address of the operator. /// @return uint256 - The current weight of the operator; returns 0 if below the threshold. - function getOperatorWeight(address _operator) public view returns (uint256) { + function getOperatorWeight( + address _operator + ) public view returns (uint256) { StrategyParams[] memory strategyParams = _quorum.strategies; uint256 weight; IStrategy[] memory strategies = new IStrategy[](strategyParams.length); for (uint256 i; i < strategyParams.length; i++) { - strategies[i]=strategyParams[i].strategy; - + strategies[i] = strategyParams[i].strategy; } - uint256[] memory shares = DELEGATION_MANAGER.getOperatorShares(_operator, strategies); - for (uint256 i; i= _currentSigner){ + function _validateSortedSigners( + address _lastSigner, + address _currentSigner + ) internal pure { + if (_lastSigner >= _currentSigner) { revert NotSorted(); } } @@ -440,6 +545,26 @@ contract ECDSAStakeRegistry is } } + /// @notice Retrieves the operator weight for a signer, either at the last checkpoint or a specified block. + /// @param _operator The operator to query their signing key history for + /// @param _referenceBlock The block number to query the operator's weight at, or the maximum uint32 value for the last checkpoint. + /// @return The weight of the operator. + function _getOperatorSigningKey( + address _operator, + uint32 _referenceBlock + ) internal view returns (address) { + if (_referenceBlock >= block.number) { + revert InvalidReferenceBlock(); + } + return + address( + uint160( + _operatorSigningKeyHistory[_operator].getAtBlock( + _referenceBlock + ) + ) + ); + } /// @notice Retrieves the operator weight for a signer, either at the last checkpoint or a specified block. /// @param _signer The address of the signer whose weight is returned. @@ -449,47 +574,51 @@ contract ECDSAStakeRegistry is address _signer, uint32 _referenceBlock ) internal view returns (uint256) { - if (_referenceBlock == type(uint32).max) { - return _operatorWeightHistory[_signer].latest(); - } else { - return _operatorWeightHistory[_signer].getAtBlock(_referenceBlock); + if (_referenceBlock >= block.number) { + revert InvalidReferenceBlock(); } + return _operatorWeightHistory[_signer].getAtBlock(_referenceBlock); } /// @notice Retrieve the total stake weight at a specific block or the latest if not specified. /// @dev If the `_referenceBlock` is the maximum value for uint32, the latest total weight is returned. /// @param _referenceBlock The block number to retrieve the total stake weight from. /// @return The total stake weight at the given block or the latest if the given block is the max uint32 value. - function _getTotalWeight(uint32 _referenceBlock) internal view returns (uint256) { - if (_referenceBlock == type(uint32).max) { - return _totalWeightHistory.latest(); - } else { - return _totalWeightHistory.getAtBlock(_referenceBlock); + function _getTotalWeight( + uint32 _referenceBlock + ) internal view returns (uint256) { + if (_referenceBlock >= block.number) { + revert InvalidReferenceBlock(); } + return _totalWeightHistory.getAtBlock(_referenceBlock); } /// @notice Retrieves the threshold stake for a given reference block. /// @param _referenceBlock The block number to query the threshold stake for. /// If set to the maximum uint32 value, it retrieves the latest threshold stake. /// @return The threshold stake in basis points for the reference block. - function _getThresholdStake(uint32 _referenceBlock) internal view returns (uint256) { - if (_referenceBlock == type(uint32).max) { - return _thresholdWeightHistory.latest(); - } else { - return _thresholdWeightHistory.getAtBlock(_referenceBlock); + function _getThresholdStake( + uint32 _referenceBlock + ) internal view returns (uint256) { + if (_referenceBlock >= block.number) { + revert InvalidReferenceBlock(); } + return _thresholdWeightHistory.getAtBlock(_referenceBlock); } /// @notice Validates that the cumulative stake of signed messages meets or exceeds the required threshold. /// @param _signedWeight The cumulative weight of the signers that have signed the message. /// @param _referenceBlock The block number to verify the stake threshold for - function _validateThresholdStake(uint256 _signedWeight, uint32 _referenceBlock) internal view { + function _validateThresholdStake( + uint256 _signedWeight, + uint32 _referenceBlock + ) internal view { uint256 totalWeight = _getTotalWeight(_referenceBlock); - if (_signedWeight > totalWeight){ + if (_signedWeight > totalWeight) { revert InvalidSignedWeight(); } uint256 thresholdStake = _getThresholdStake(_referenceBlock); - if (thresholdStake > _signedWeight){ + if (thresholdStake > _signedWeight) { revert InsufficientSignedStake(); } } diff --git a/src/unaudited/ECDSAStakeRegistryStorage.sol b/src/unaudited/ECDSAStakeRegistryStorage.sol index fe09b9d8..0742157a 100644 --- a/src/unaudited/ECDSAStakeRegistryStorage.sol +++ b/src/unaudited/ECDSAStakeRegistryStorage.sol @@ -5,7 +5,9 @@ import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/ import {CheckpointsUpgradeable} from "@openzeppelin-upgrades/contracts/utils/CheckpointsUpgradeable.sol"; import {ECDSAStakeRegistryEventsAndErrors, Quorum, StrategyParams} from "../interfaces/IECDSAStakeRegistryEventsAndErrors.sol"; -abstract contract ECDSAStakeRegistryStorage is ECDSAStakeRegistryEventsAndErrors { +abstract contract ECDSAStakeRegistryStorage is + ECDSAStakeRegistryEventsAndErrors +{ /// @notice Manages staking delegations through the DelegationManager interface IDelegationManager internal immutable DELEGATION_MANAGER; @@ -27,6 +29,10 @@ abstract contract ECDSAStakeRegistryStorage is ECDSAStakeRegistryEventsAndErrors /// @notice Defines the duration after which the stake's weight expires. uint256 internal _stakeExpiry; + /// @notice Maps an operator to their signing key history using checkpoints + mapping(address => CheckpointsUpgradeable.History) + internal _operatorSigningKeyHistory; + /// @notice Tracks the total stake history over time using checkpoints CheckpointsUpgradeable.History internal _totalWeightHistory; @@ -34,7 +40,8 @@ abstract contract ECDSAStakeRegistryStorage is ECDSAStakeRegistryEventsAndErrors CheckpointsUpgradeable.History internal _thresholdWeightHistory; /// @notice Maps operator addresses to their respective stake histories using checkpoints - mapping(address => CheckpointsUpgradeable.History) internal _operatorWeightHistory; + mapping(address => CheckpointsUpgradeable.History) + internal _operatorWeightHistory; /// @notice Maps an operator to their registration status mapping(address => bool) internal _operatorRegistered; @@ -47,5 +54,5 @@ abstract contract ECDSAStakeRegistryStorage is ECDSAStakeRegistryEventsAndErrors // slither-disable-next-line shadowing-state /// @dev Reserves storage slots for future upgrades // solhint-disable-next-line - uint256[42] private __gap; + uint256[40] private __gap; } diff --git a/src/unaudited/examples/ECDSAStakeRegistryPermissioned.sol b/src/unaudited/examples/ECDSAStakeRegistryPermissioned.sol index c5329ce2..ef2e691c 100644 --- a/src/unaudited/examples/ECDSAStakeRegistryPermissioned.sol +++ b/src/unaudited/examples/ECDSAStakeRegistryPermissioned.sol @@ -27,7 +27,9 @@ contract ECDSAStakeRegistryPermissioned is ECDSAStakeRegistry { /// @dev Custom error to signal that an operator is already allowlisted. error OperatorAlreadyAllowlisted(); - constructor(IDelegationManager _delegationManager) ECDSAStakeRegistry(_delegationManager) { + constructor( + IDelegationManager _delegationManager + ) ECDSAStakeRegistry(_delegationManager) { // _disableInitializers(); } @@ -63,38 +65,40 @@ contract ECDSAStakeRegistryPermissioned is ECDSAStakeRegistry { /// Doesn't register the operator into the operator set /// @param _operator The address of the operator to allowlist. function _permitOperator(address _operator) internal { - if (allowlistedOperators[_operator]){ + if (allowlistedOperators[_operator]) { revert OperatorAlreadyAllowlisted(); } allowlistedOperators[_operator] = true; emit OperatorPermitted(_operator); - } /// @dev Removes an operator from the allowlist. /// If the operator is registered, also deregisters the operator. /// @param _operator The address of the operator to be revoked. function _revokeOperator(address _operator) internal { - if (!allowlistedOperators[_operator]){ + if (!allowlistedOperators[_operator]) { revert OperatorNotAllowlisted(); } delete allowlistedOperators[_operator]; emit OperatorRevoked(_operator); - if (_operatorRegistered[_operator]){ + if (_operatorRegistered[_operator]) { _ejectOperator(_operator); } - } /// @inheritdoc ECDSAStakeRegistry function _registerOperatorWithSig( address _operator, - ISignatureUtils.SignatureWithSaltAndExpiry memory _operatorSignature + ISignatureUtils.SignatureWithSaltAndExpiry memory _operatorSignature, + address _operatorSigningKey ) internal override { - if (allowlistedOperators[_operator] != true){ + if (allowlistedOperators[_operator] != true) { revert OperatorNotAllowlisted(); } - super._registerOperatorWithSig(_operator, _operatorSignature); + super._registerOperatorWithSig( + _operator, + _operatorSignature, + _operatorSigningKey + ); } } - diff --git a/test/events/IServiceManagerBaseEvents.sol b/test/events/IServiceManagerBaseEvents.sol new file mode 100644 index 00000000..4ae9b92e --- /dev/null +++ b/test/events/IServiceManagerBaseEvents.sol @@ -0,0 +1,128 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.12; + +import {IRewardsCoordinator, IERC20} from "eigenlayer-contracts/src/contracts/interfaces/IRewardsCoordinator.sol"; + +interface IServiceManagerBaseEvents { + /// RewardsCoordinator EVENTS /// + + /// @notice emitted when an AVS creates a valid RewardsSubmission + event AVSRewardsSubmissionCreated( + address indexed avs, + uint256 indexed submissionNonce, + bytes32 indexed rewardsSubmissionHash, + IRewardsCoordinator.RewardsSubmission rewardsSubmission + ); + /// @notice emitted when a valid RewardsSubmission is created for all stakers by a valid submitter + event RewardsSubmissionForAllCreated( + address indexed submitter, + uint256 indexed submissionNonce, + bytes32 indexed rewardsSubmissionHash, + IRewardsCoordinator.RewardsSubmission rewardsSubmission + ); + /// @notice rewardsUpdater is responsible for submiting DistributionRoots, only owner can set rewardsUpdater + event RewardsUpdaterSet( + address indexed oldRewardsUpdater, + address indexed newRewardsUpdater + ); + event RewardsForAllSubmitterSet( + address indexed rewardsForAllSubmitter, + bool indexed oldValue, + bool indexed newValue + ); + event ActivationDelaySet( + uint32 oldActivationDelay, + uint32 newActivationDelay + ); + event DefaultOperatorSplitBipsSet( + uint16 oldDefaultOperatorSplitBips, + uint16 newDefaultOperatorSplitBips + ); + event ClaimerForSet( + address indexed earner, + address indexed oldClaimer, + address indexed claimer + ); + /// @notice rootIndex is the specific array index of the newly created root in the storage array + event DistributionRootSubmitted( + uint32 indexed rootIndex, + bytes32 indexed root, + uint32 indexed rewardsCalculationEndTimestamp, + uint32 activatedAt + ); + /// @notice root is one of the submitted distribution roots that was claimed against + event RewardsClaimed( + bytes32 root, + address indexed earner, + address indexed claimer, + address indexed recipient, + IERC20 token, + uint256 claimedAmount + ); + /** + * @notice Emitted when an AVS creates a valid `OperatorDirectedRewardsSubmission` + * @param caller The address calling `createOperatorDirectedAVSRewardsSubmission`. + * @param avs The avs on behalf of which the operator-directed rewards are being submitted. + * @param operatorDirectedRewardsSubmissionHash Keccak256 hash of (`avs`, `submissionNonce` and `operatorDirectedRewardsSubmission`). + * @param submissionNonce Current nonce of the avs. Used to generate a unique submission hash. + * @param operatorDirectedRewardsSubmission The Operator-Directed Rewards Submission. Contains the token, start timestamp, duration, operator rewards, description and, strategy and multipliers. + */ + event OperatorDirectedAVSRewardsSubmissionCreated( + address indexed caller, + address indexed avs, + bytes32 indexed operatorDirectedRewardsSubmissionHash, + uint256 submissionNonce, + IRewardsCoordinator.OperatorDirectedRewardsSubmission operatorDirectedRewardsSubmission + ); + /** + * @notice Emitted when the operator split for an AVS is set. + * @param caller The address calling `setOperatorAVSSplit`. + * @param operator The operator on behalf of which the split is being set. + * @param avs The avs for which the split is being set by the operator. + * @param activatedAt The timestamp at which the split will be activated. + * @param oldOperatorAVSSplitBips The old split for the operator for the AVS. + * @param newOperatorAVSSplitBips The new split for the operator for the AVS. + */ + event OperatorAVSSplitBipsSet( + address indexed caller, + address indexed operator, + address indexed avs, + uint32 activatedAt, + uint16 oldOperatorAVSSplitBips, + uint16 newOperatorAVSSplitBips + ); + /** + * @notice Emitted when the operator split for Programmatic Incentives is set. + * @param caller The address calling `setOperatorPISplit`. + * @param operator The operator on behalf of which the split is being set. + * @param activatedAt The timestamp at which the split will be activated. + * @param oldOperatorPISplitBips The old split for the operator for Programmatic Incentives. + * @param newOperatorPISplitBips The new split for the operator for Programmatic Incentives. + */ + event OperatorPISplitBipsSet( + address indexed caller, + address indexed operator, + uint32 activatedAt, + uint16 oldOperatorPISplitBips, + uint16 newOperatorPISplitBips + ); + + /// TOKEN EVENTS FOR TESTING /// + /** + * @dev Emitted when `value` tokens are moved from one account (`from`) to + * another (`to`). + * + * Note that `value` may be zero. + */ + event Transfer(address indexed from, address indexed to, uint256 value); + + /** + * @dev Emitted when the allowance of a `spender` for an `owner` is set by + * a call to {approve}. `value` is the new allowance. + */ + event Approval( + address indexed owner, + address indexed spender, + uint256 value + ); +} diff --git a/test/ffi/BLSPubKeyCompendiumFFI.t.sol b/test/ffi/BLSPubKeyCompendiumFFI.t.sol index 4034749f..18d49a17 100644 --- a/test/ffi/BLSPubKeyCompendiumFFI.t.sol +++ b/test/ffi/BLSPubKeyCompendiumFFI.t.sol @@ -9,7 +9,7 @@ contract BLSApkRegistryFFITests is G2Operations { using BN254 for BN254.G1Point; using Strings for uint256; - Vm cheats = Vm(HEVM_ADDRESS); + Vm cheats = Vm(VM_ADDRESS); BLSApkRegistry blsApkRegistry; IRegistryCoordinator registryCoordinator; diff --git a/test/harnesses/RegistryCoordinatorHarness.t.sol b/test/harnesses/RegistryCoordinatorHarness.t.sol index d7ae81ae..80b38ca2 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, + ISocketRegistry _socketRegistry + ) RegistryCoordinator(_serviceManager, _stakeRegistry, _blsApkRegistry, _indexRegistry, _socketRegistry) { _transferOwnership(msg.sender); } diff --git a/test/integration/CoreRegistration.t.sol b/test/integration/CoreRegistration.t.sol index d1d1d161..64730b17 100644 --- a/test/integration/CoreRegistration.t.sol +++ b/test/integration/CoreRegistration.t.sol @@ -6,6 +6,8 @@ import { AVSDirectory } from "eigenlayer-contracts/src/contracts/core/AVSDirecto import { IAVSDirectory } from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol"; import { DelegationManager } from "eigenlayer-contracts/src/contracts/core/DelegationManager.sol"; import { IDelegationManager } 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"; contract Test_CoreRegistration is MockAVSDeployer { // Contracts @@ -62,10 +64,13 @@ contract Test_CoreRegistration is MockAVSDeployer { ) ); + // Deploy Mock RewardsCoordinator + rewardsCoordinatorMock = new RewardsCoordinatorMock(); // Deploy New ServiceManager & RegistryCoordinator implementations serviceManagerImplementation = new ServiceManagerMock( avsDirectory, + rewardsCoordinatorMock, registryCoordinator, stakeRegistry ); @@ -74,7 +79,8 @@ contract Test_CoreRegistration is MockAVSDeployer { serviceManager, stakeRegistry, blsApkRegistry, - indexRegistry + indexRegistry, + socketRegistry ); // Upgrade Registry Coordinator & ServiceManager diff --git a/test/integration/IntegrationDeployer.t.sol b/test/integration/IntegrationDeployer.t.sol index e8d74673..d69a908b 100644 --- a/test/integration/IntegrationDeployer.t.sol +++ b/test/integration/IntegrationDeployer.t.sol @@ -16,13 +16,12 @@ 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/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/test/mocks/ETHDepositMock.sol"; -// import "eigenlayer-contracts/src/test/integration/mocks/BeaconChainOracleMock.t.sol"; -import "test/integration/mocks/BeaconChainOracleMock.t.sol"; // Middleware contracts import "src/RegistryCoordinator.sol"; @@ -31,6 +30,7 @@ import "src/IndexRegistry.sol"; import "src/BLSApkRegistry.sol"; import "test/mocks/ServiceManagerMock.sol"; import "src/OperatorStateRetriever.sol"; +import "src/SocketRegistry.sol"; // Mocks and More import "src/libraries/BN254.sol"; @@ -41,22 +41,21 @@ import "eigenlayer-contracts/src/test/mocks/EmptyContract.sol"; import "test/integration/User.t.sol"; abstract contract IntegrationDeployer is Test, IUserDeployer { - using Strings for *; - Vm cheats = Vm(HEVM_ADDRESS); + Vm cheats = Vm(VM_ADDRESS); // Core contracts to deploy DelegationManager delegationManager; AVSDirectory public avsDirectory; StrategyManager strategyManager; EigenPodManager eigenPodManager; + RewardsCoordinator rewardsCoordinator; PauserRegistry pauserRegistry; Slasher slasher; IBeacon eigenPodBeacon; EigenPod pod; ETHPOSDepositMock ethPOSDeposit; - BeaconChainOracleMock beaconChainOracle; // Base strategy implementation in case we want to create more strategies later StrategyBase baseStrategyImplementation; @@ -67,6 +66,7 @@ abstract contract IntegrationDeployer is Test, IUserDeployer { BLSApkRegistry blsApkRegistry; StakeRegistry stakeRegistry; IndexRegistry indexRegistry; + SocketRegistry socketRegistry; OperatorStateRetriever operatorStateRetriever; TimeMachine public timeMachine; @@ -81,17 +81,30 @@ abstract contract IntegrationDeployer is Test, IUserDeployer { address eigenLayerReputedMultisig = address(this); // admin address address constant pauser = address(555); address constant unpauser = address(556); - address public registryCoordinatorOwner = address(uint160(uint256(keccak256("registryCoordinatorOwner")))); + address public registryCoordinatorOwner = + address(uint160(uint256(keccak256("registryCoordinatorOwner")))); uint256 public churnApproverPrivateKey = uint256(keccak256("churnApproverPrivateKey")); address public churnApprover = cheats.addr(churnApproverPrivateKey); address ejector = address(uint160(uint256(keccak256("ejector")))); + address rewardsUpdater = address(uint160(uint256(keccak256("rewardsUpdater")))); // Constants/Defaults - uint64 constant MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR = 32e9; - uint constant MIN_BALANCE = 1e6; - uint constant MAX_BALANCE = 5e6; - uint constant MAX_STRATEGY_COUNT = 32; // From StakeRegistry.MAX_WEIGHING_FUNCTION_LENGTH + uint64 constant GENESIS_TIME_LOCAL = 1 hours * 12; + uint256 constant MIN_BALANCE = 1e6; + uint256 constant MAX_BALANCE = 5e6; + uint256 constant MAX_STRATEGY_COUNT = 32; // From StakeRegistry.MAX_WEIGHING_FUNCTION_LENGTH uint96 constant DEFAULT_STRATEGY_MULTIPLIER = 1e18; + // RewardsCoordinator + uint32 MAX_REWARDS_DURATION = 70 days; + uint32 MAX_RETROACTIVE_LENGTH = 84 days; + uint32 MAX_FUTURE_LENGTH = 28 days; + uint32 GENESIS_REWARDS_TIMESTAMP = 1_712_092_632; + /// @notice Delay in timestamp before a posted root can be claimed against + uint32 activationDelay = 7 days; + /// @notice intervals(epochs) are 2 weeks + uint32 calculationIntervalSeconds = 14 days; + /// @notice the commission for all operators across all avss + uint16 globalCommissionBips = 1000; function setUp() public virtual { // Deploy ProxyAdmin @@ -105,49 +118,67 @@ abstract contract IntegrationDeployer is Test, IUserDeployer { // Deploy mocks EmptyContract emptyContract = new EmptyContract(); ethPOSDeposit = new ETHPOSDepositMock(); - beaconChainOracle = new BeaconChainOracleMock(); /** * First, deploy upgradeable proxy contracts that **will point** to the implementations. Since the implementation contracts are * not yet deployed, we give these proxies an empty contract as the initial implementation, to act as if they have no code. */ delegationManager = DelegationManager( - address(new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), "")) + address( + new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), "") + ) ); strategyManager = StrategyManager( - address(new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), "")) + address( + new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), "") + ) ); slasher = Slasher( - address(new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), "")) + address( + new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), "") + ) ); eigenPodManager = EigenPodManager( - address(new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), "")) + address( + new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), "") + ) ); avsDirectory = AVSDirectory( - address(new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), "")) + address( + new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), "") + ) ); + // RewardsCoordinator = RewardsCoordinator( + // address(new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), "")) + // ); // Deploy EigenPod Contracts pod = new EigenPod( ethPOSDeposit, eigenPodManager, - MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR + GENESIS_TIME_LOCAL ); eigenPodBeacon = new UpgradeableBeacon(address(pod)); // Second, deploy the *implementation* contracts, using the *proxy contracts* as inputs - DelegationManager delegationImplementation = new DelegationManager(strategyManager, slasher, eigenPodManager); - StrategyManager strategyManagerImplementation = new StrategyManager(delegationManager, eigenPodManager, slasher); + DelegationManager delegationImplementation = + new DelegationManager(strategyManager, slasher, eigenPodManager); + StrategyManager strategyManagerImplementation = + new StrategyManager(delegationManager, eigenPodManager, slasher); Slasher slasherImplementation = new Slasher(strategyManager, delegationManager); EigenPodManager eigenPodManagerImplementation = new EigenPodManager( - ethPOSDeposit, - eigenPodBeacon, - strategyManager, - slasher, - delegationManager + ethPOSDeposit, eigenPodBeacon, strategyManager, slasher, delegationManager ); AVSDirectory avsDirectoryImplemntation = new AVSDirectory(delegationManager); + // 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; @@ -161,7 +192,7 @@ abstract contract IntegrationDeployer is Test, IUserDeployer { DelegationManager.initialize.selector, eigenLayerReputedMultisig, // initialOwner pauserRegistry, - 0 /* initialPausedStatus */, + 0, /* initialPausedStatus */ minWithdrawalDelayBlocks, initializeStrategiesToSetDelayBlocks, initializeWithdrawalDelayBlocks @@ -196,7 +227,6 @@ abstract contract IntegrationDeployer is Test, IUserDeployer { address(eigenPodManagerImplementation), abi.encodeWithSelector( EigenPodManager.initialize.selector, - address(beaconChainOracle), eigenLayerReputedMultisig, // initialOwner pauserRegistry, 0 // initialPausedStatus @@ -213,11 +243,26 @@ abstract contract IntegrationDeployer is Test, IUserDeployer { 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 + // ) + // ); // Deploy and whitelist strategies baseStrategyImplementation = new StrategyBase(strategyManager); - for (uint i = 0; i < MAX_STRATEGY_COUNT; i++) { - string memory number = uint(i).toString(); + for (uint256 i = 0; i < MAX_STRATEGY_COUNT; i++) { + string memory number = uint256(i).toString(); string memory stratName = string.concat("StrategyToken", number); string memory stratSymbol = string.concat("STT", number); _newStrategyAndToken(stratName, stratSymbol, 10e50, address(this)); @@ -227,59 +272,59 @@ abstract contract IntegrationDeployer is Test, IUserDeployer { timeMachine = new TimeMachine(); cheats.startPrank(registryCoordinatorOwner); - registryCoordinator = RegistryCoordinator(address( - new TransparentUpgradeableProxy( - address(emptyContract), - address(proxyAdmin), - "" + registryCoordinator = RegistryCoordinator( + address( + new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), "") ) - )); + ); stakeRegistry = StakeRegistry( address( - new TransparentUpgradeableProxy( - address(emptyContract), - address(proxyAdmin), - "" - ) + new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), "") + ) + ); + + socketRegistry = SocketRegistry( + address( + new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), "") ) ); indexRegistry = IndexRegistry( address( - new TransparentUpgradeableProxy( - address(emptyContract), - address(proxyAdmin), - "" - ) + new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), "") ) ); blsApkRegistry = BLSApkRegistry( address( - new TransparentUpgradeableProxy( - address(emptyContract), - address(proxyAdmin), - "" - ) + new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), "") ) ); serviceManager = ServiceManagerMock( address( - new TransparentUpgradeableProxy( - address(emptyContract), - address(proxyAdmin), - "" - ) + new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), "") ) ); cheats.stopPrank(); - StakeRegistry stakeRegistryImplementation = new StakeRegistry(IRegistryCoordinator(registryCoordinator), IDelegationManager(delegationManager)); - BLSApkRegistry blsApkRegistryImplementation = new BLSApkRegistry(IRegistryCoordinator(registryCoordinator)); - IndexRegistry indexRegistryImplementation = new IndexRegistry(IRegistryCoordinator(registryCoordinator)); - ServiceManagerMock serviceManagerImplementation = new ServiceManagerMock(IAVSDirectory(avsDirectory), IRegistryCoordinator(registryCoordinator), stakeRegistry); + StakeRegistry stakeRegistryImplementation = new StakeRegistry( + IRegistryCoordinator(registryCoordinator), IDelegationManager(delegationManager) + ); + BLSApkRegistry blsApkRegistryImplementation = + new BLSApkRegistry(IRegistryCoordinator(registryCoordinator)); + IndexRegistry indexRegistryImplementation = + new IndexRegistry(IRegistryCoordinator(registryCoordinator)); + ServiceManagerMock serviceManagerImplementation = new ServiceManagerMock( + IAVSDirectory(avsDirectory), + rewardsCoordinator, + IRegistryCoordinator(registryCoordinator), + stakeRegistry + ); + SocketRegistry socketRegistryImplementation = new SocketRegistry( + IRegistryCoordinator(registryCoordinator) + ); proxyAdmin.upgrade( TransparentUpgradeableProxy(payable(address(stakeRegistry))), @@ -301,9 +346,18 @@ abstract contract IntegrationDeployer is Test, IUserDeployer { address(serviceManagerImplementation) ); - serviceManager.initialize({initialOwner: registryCoordinatorOwner}); + proxyAdmin.upgrade( + TransparentUpgradeableProxy(payable(address(socketRegistry))), + address(socketRegistryImplementation) + ); - RegistryCoordinator registryCoordinatorImplementation = new RegistryCoordinator(serviceManager, stakeRegistry, blsApkRegistry, indexRegistry); + serviceManager.initialize({ + initialOwner: registryCoordinatorOwner, + rewardsInitiator: address(msg.sender) + }); + + RegistryCoordinator registryCoordinatorImplementation = + new RegistryCoordinator(serviceManager, stakeRegistry, blsApkRegistry, indexRegistry, socketRegistry); proxyAdmin.upgradeAndCall( TransparentUpgradeableProxy(payable(address(registryCoordinator))), address(registryCoordinatorImplementation), @@ -313,7 +367,7 @@ abstract contract IntegrationDeployer is Test, IUserDeployer { churnApprover, ejector, pauserRegistry, - 0/*initialPausedStatus*/, + 0, /*initialPausedStatus*/ new IRegistryCoordinator.OperatorSetParam[](0), new uint96[](0), new IStakeRegistry.StrategyParams[][](0) @@ -325,14 +379,22 @@ abstract contract IntegrationDeployer is Test, IUserDeployer { /// @dev Deploy a strategy and its underlying token, push to global lists of tokens/strategies, and whitelist /// strategy in strategyManager - function _newStrategyAndToken(string memory tokenName, string memory tokenSymbol, uint initialSupply, address owner) internal { - IERC20 underlyingToken = new ERC20PresetFixedSupply(tokenName, tokenSymbol, initialSupply, owner); + function _newStrategyAndToken( + string memory tokenName, + string memory tokenSymbol, + uint256 initialSupply, + address owner + ) internal { + IERC20 underlyingToken = + new ERC20PresetFixedSupply(tokenName, tokenSymbol, initialSupply, owner); StrategyBase strategy = StrategyBase( address( new TransparentUpgradeableProxy( address(baseStrategyImplementation), address(proxyAdmin), - abi.encodeWithSelector(StrategyBase.initialize.selector, underlyingToken, pauserRegistry) + abi.encodeWithSelector( + StrategyBase.initialize.selector, underlyingToken, pauserRegistry + ) ) ) ); @@ -342,7 +404,9 @@ abstract contract IntegrationDeployer is Test, IUserDeployer { bool[] memory thirdPartyTransfersForbiddenValues = new bool[](1); strategies[0] = strategy; cheats.prank(strategyManager.strategyWhitelister()); - strategyManager.addStrategiesToDepositWhitelist(strategies, thirdPartyTransfersForbiddenValues); + strategyManager.addStrategiesToDepositWhitelist( + strategies, thirdPartyTransfersForbiddenValues + ); // Add to allStrats allStrats.push(strategy); diff --git a/test/integration/TimeMachine.t.sol b/test/integration/TimeMachine.t.sol index 129c9892..b1df82d5 100644 --- a/test/integration/TimeMachine.t.sol +++ b/test/integration/TimeMachine.t.sol @@ -5,7 +5,7 @@ import "forge-std/Test.sol"; contract TimeMachine is Test { - Vm cheats = Vm(HEVM_ADDRESS); + Vm cheats = Vm(VM_ADDRESS); bool pastExists = false; uint lastSnapshot; diff --git a/test/integration/User.t.sol b/test/integration/User.t.sol index f809e3b4..c0da643f 100644 --- a/test/integration/User.t.sol +++ b/test/integration/User.t.sol @@ -43,7 +43,7 @@ contract User is Test { using BitmapStrings for *; using BitmapUtils for *; - Vm cheats = Vm(HEVM_ADDRESS); + Vm cheats = Vm(VM_ADDRESS); // Core contracts DelegationManager delegationManager; @@ -117,6 +117,7 @@ contract User is Test { function registerOperator(bytes calldata quorums) public createSnapshot virtual returns (bytes32) { _log("registerOperator", quorums); + vm.warp(block.timestamp + 1); registryCoordinator.registerOperator({ quorumNumbers: quorums, socket: NAME, @@ -208,6 +209,7 @@ contract User is Test { expiry: expiry }); + vm.warp(block.timestamp + 1); registryCoordinator.registerOperatorWithChurn({ quorumNumbers: allQuorums, socket: NAME, @@ -397,7 +399,7 @@ contract User_AltMethods is User { operatorsPerQuorum[i][j] = blsApkRegistry.getOperatorFromPubkeyHash(operatorIds[j]); } - Sort.sort(operatorsPerQuorum[i]); + operatorsPerQuorum[i] = Sort.sortAddresses(operatorsPerQuorum[i]); } registryCoordinator.updateOperatorsForQuorum(operatorsPerQuorum, allQuorums); diff --git a/test/integration/utils/Sort.t.sol b/test/integration/utils/Sort.t.sol index 3853e544..46a2fc2b 100644 --- a/test/integration/utils/Sort.t.sol +++ b/test/integration/utils/Sort.t.sol @@ -2,23 +2,24 @@ pragma solidity ^0.8.12; library Sort { - - /// @dev In-place insertion sort of addrs, h/t ChatGPT - function sort(address[] memory addrs) internal pure { - for (uint i = 1; i < addrs.length; i++) { - address key = addrs[i]; - uint j = i - 1; - - // Move elements of addrs[0..i-1], that are greater than key, - // to one position ahead of their current position - while (j >= 0 && addrs[j] > key) { - addrs[j + 1] = addrs[j]; - if(j == 0) { - break; + /** + * @notice Sorts an array of addresses in ascending order. h/t ChatGPT take 2 + * @dev This function uses the Bubble Sort algorithm, which is simple but has O(n^2) complexity. + * @param addresses The array of addresses to be sorted. + * @return sortedAddresses The array of addresses sorted in ascending order. + */ + function sortAddresses(address[] memory addresses) internal pure returns (address[] memory) { + uint256 n = addresses.length; + for (uint256 i = 0; i < n; i++) { + for (uint256 j = 0; j < n - 1; j++) { + // Compare and swap if the current address is greater than the next one + if (addresses[j] > addresses[j + 1]) { + address temp = addresses[j]; + addresses[j] = addresses[j + 1]; + addresses[j + 1] = temp; } - j--; } - addrs[j + 1] = key; } + return addresses; } } diff --git a/test/mocks/AVSDirectoryMock.sol b/test/mocks/AVSDirectoryMock.sol index 9d9f1b79..deef83da 100644 --- a/test/mocks/AVSDirectoryMock.sol +++ b/test/mocks/AVSDirectoryMock.sol @@ -4,42 +4,21 @@ 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 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. - */ + mapping(address => mapping(bytes32 => bool)) public operatorSaltIsSpentMapping; + 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 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) {} - - /** - * @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 operatorSaltIsSpent(address operator, bytes32 salt) external view returns (bool) { + return operatorSaltIsSpentMapping[operator][salt]; + } + function calculateOperatorAVSRegistrationDigestHash( address operator, address avs, @@ -47,10 +26,9 @@ contract AVSDirectoryMock is IAVSDirectory { uint256 expiry ) 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) {} function cancelSalt(bytes32 salt) external {} function domainSeparator() external view returns (bytes32) {} -} +} \ No newline at end of file diff --git a/test/mocks/DelegationMock.sol b/test/mocks/DelegationMock.sol index 491f0f10..190e1aa5 100644 --- a/test/mocks/DelegationMock.sol +++ b/test/mocks/DelegationMock.sol @@ -1,17 +1,20 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.12; -import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; +import "eigenlayer-contracts/src/contracts/interfaces/IStrategyManager.sol"; -import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; -import {IStrategyManager} from "eigenlayer-contracts/src/contracts/interfaces/IStrategyManager.sol"; -import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; -import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol"; contract DelegationMock is IDelegationManager { mapping(address => bool) public isOperator; mapping(address => mapping(IStrategy => uint256)) public operatorShares; + function getDelegatableShares(address staker) external view returns (IStrategy[] memory, uint256[] memory) {} + + function setMinWithdrawalDelayBlocks(uint256 newMinWithdrawalDelayBlocks) external {} + + function setStrategyWithdrawalDelayBlocks(IStrategy[] calldata strategies, uint256[] calldata withdrawalDelayBlocks) external {} + function setIsOperator(address operator, bool _isOperatorReturnValue) external { isOperator[operator] = _isOperatorReturnValue; } @@ -24,7 +27,7 @@ contract DelegationMock is IDelegationManager { 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 {} @@ -65,10 +68,6 @@ contract DelegationMock is IDelegationManager { return returnValue; } - function earningsReceiver(address operator) external pure returns (address) { - return operator; - } - function delegationApprover(address operator) external pure returns (address) { return operator; } @@ -77,31 +76,34 @@ contract DelegationMock is IDelegationManager { return 0; } - function minWithdrawalDelayBlocks() external view returns (uint256) { - return 50400; + function minWithdrawalDelayBlocks() external pure returns (uint256) { + return 0; } + /// @notice return address of the beaconChainETHStrategy + function beaconChainETHStrategy() external view returns (IStrategy) {} + /** * @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) { + function strategyWithdrawalDelayBlocks(IStrategy /*strategy*/) external pure 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) { + 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 getWithdrawalDelay(IStrategy[] calldata /*strategies*/) public pure returns (uint256) { + return type(uint256).max; } function isDelegated(address staker) external view returns (bool) { @@ -143,14 +145,20 @@ contract DelegationMock is IDelegationManager { function DELEGATION_APPROVAL_TYPEHASH() external view returns (bytes32) {} - function domainSeparator() external view returns (bytes32) {} + function OPERATOR_AVS_REGISTRATION_TYPEHASH() external view returns (bytes32) {} function cumulativeWithdrawalsQueued(address staker) external view returns (uint256) {} function calculateWithdrawalRoot(Withdrawal memory withdrawal) external pure returns (bytes32) {} + function registerOperatorToAVS(address operator, ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature) external {} + + function deregisterOperatorFromAVS(address operator) external {} + function operatorSaltIsSpent(address avs, bytes32 salt) external view returns (bool) {} + function domainSeparator() external view returns (bytes32) {} + function queueWithdrawals( QueuedWithdrawalParams[] calldata queuedWithdrawalParams ) external returns (bytes32[] memory) {} @@ -168,9 +176,7 @@ contract DelegationMock is IDelegationManager { uint256[] calldata middlewareTimesIndexes, bool[] calldata receiveAsTokens ) external {} - - // function migrateQueuedWithdrawals(IStrategyManager.DeprecatedStruct_QueuedWithdrawal[] memory withdrawalsToQueue) external {} - + // onlyDelegationManager functions in StrategyManager function addShares( IStrategyManager strategyManager, @@ -200,20 +206,4 @@ contract DelegationMock is IDelegationManager { ) external { strategyManager.withdrawSharesAsTokens(recipient, strategy, shares, token); } - - function getDelegatableShares(address staker) external view returns (IStrategy[] memory, uint256[] memory) { - IStrategy[] memory strategies; - uint256[] memory shares; - return (strategies, shares); - } - - function beaconChainETHStrategy() external view returns (IStrategy) { - return IStrategy(address(0)); - } - - function setMinWithdrawalDelayBlocks(uint256 newMinWithdrawalDelayBlocks) external { - } - - function setStrategyWithdrawalDelayBlocks(IStrategy[] calldata strategies, uint256[] calldata withdrawalDelayBlocks) external { - } } diff --git a/test/mocks/ECDSAServiceManagerMock.sol b/test/mocks/ECDSAServiceManagerMock.sol new file mode 100644 index 00000000..528270ae --- /dev/null +++ b/test/mocks/ECDSAServiceManagerMock.sol @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.12; + +import "../../src/unaudited/ECDSAServiceManagerBase.sol"; + +contract ECDSAServiceManagerMock is ECDSAServiceManagerBase { + constructor( + address _avsDirectory, + address _stakeRegistry, + address _rewardsCoordinator, + address _delegationManager + ) + ECDSAServiceManagerBase(_avsDirectory, _stakeRegistry, _rewardsCoordinator, _delegationManager) + {} + + function initialize( + address initialOwner, + address rewardsInitiator + ) public virtual initializer { + __ServiceManagerBase_init(initialOwner, rewardsInitiator); + } +} diff --git a/test/mocks/ECDSAStakeRegistryMock.sol b/test/mocks/ECDSAStakeRegistryMock.sol new file mode 100644 index 00000000..7ad6043e --- /dev/null +++ b/test/mocks/ECDSAStakeRegistryMock.sol @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.12; + +import "../../src/unaudited/ECDSAStakeRegistry.sol"; + +/** + * @title Mock for ECDSAStakeRegistry + * @dev This contract is a mock implementation of the ECDSAStakeRegistry for testing purposes. + */ +contract ECDSAStakeRegistryMock is ECDSAStakeRegistry { + + constructor(IDelegationManager _delegationManager) ECDSAStakeRegistry(_delegationManager) { + } +} diff --git a/test/mocks/RegistryCoordinatorMock.sol b/test/mocks/RegistryCoordinatorMock.sol index abee1a6a..378d357e 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 updateSocket(string memory socket) external {} } diff --git a/test/mocks/RewardsCoordinatorMock.sol b/test/mocks/RewardsCoordinatorMock.sol new file mode 100644 index 00000000..c3e36490 --- /dev/null +++ b/test/mocks/RewardsCoordinatorMock.sol @@ -0,0 +1,138 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.12; + +import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; + +import {IRewardsCoordinator} from "eigenlayer-contracts/src/contracts/interfaces/IRewardsCoordinator.sol"; + +contract RewardsCoordinatorMock is IRewardsCoordinator { + function rewardsUpdater() external view returns (address) {} + + function CALCULATION_INTERVAL_SECONDS() external view returns (uint32) {} + + function MAX_REWARDS_DURATION() external view returns (uint32) {} + + function MAX_RETROACTIVE_LENGTH() external view returns (uint32) {} + + function MAX_FUTURE_LENGTH() external view returns (uint32) {} + + function GENESIS_REWARDS_TIMESTAMP() external view returns (uint32) {} + + function activationDelay() external view returns (uint32) {} + + function claimerFor(address earner) external view returns (address) {} + + function cumulativeClaimed( + address claimer, + IERC20 token + ) external view returns (uint256) {} + + function defaultOperatorSplitBips() external view returns (uint16) {} + + function calculateEarnerLeafHash( + EarnerTreeMerkleLeaf calldata leaf + ) external pure returns (bytes32) {} + + function calculateTokenLeafHash( + TokenTreeMerkleLeaf calldata leaf + ) external pure returns (bytes32) {} + + function checkClaim( + RewardsMerkleClaim calldata claim + ) external view returns (bool) {} + + function currRewardsCalculationEndTimestamp() + external + view + returns (uint32) + {} + + function getDistributionRootsLength() external view returns (uint256) {} + + function getDistributionRootAtIndex( + uint256 index + ) external view returns (DistributionRoot memory) {} + + function getCurrentDistributionRoot() + external + view + returns (DistributionRoot memory) + {} + + function getCurrentClaimableDistributionRoot() + external + view + returns (DistributionRoot memory) + {} + + function getRootIndexFromHash( + bytes32 rootHash + ) external view returns (uint32) {} + + function domainSeparator() external view returns (bytes32) {} + + function getOperatorAVSSplit( + address operator, + address avs + ) external view returns (uint16) {} + + function getOperatorPISplit( + address operator + ) external view returns (uint16) {} + + function createAVSRewardsSubmission( + RewardsSubmission[] calldata rewardsSubmissions + ) external {} + + function createRewardsForAllSubmission( + RewardsSubmission[] calldata rewardsSubmission + ) external {} + + function createRewardsForAllEarners( + RewardsSubmission[] calldata rewardsSubmissions + ) external {} + + function createOperatorDirectedAVSRewardsSubmission( + address avs, + OperatorDirectedRewardsSubmission[] + calldata operatorDirectedRewardsSubmissions + ) external {} + + function processClaim( + RewardsMerkleClaim calldata claim, + address recipient + ) external {} + + function processClaims( + RewardsMerkleClaim[] calldata claims, + address recipient + ) external {} + + function submitRoot( + bytes32 root, + uint32 rewardsCalculationEndTimestamp + ) external {} + + function disableRoot(uint32 rootIndex) external {} + + function setClaimerFor(address claimer) external {} + + function setActivationDelay(uint32 _activationDelay) external {} + + function setDefaultOperatorSplit(uint16 split) external {} + + function setRewardsUpdater(address _rewardsUpdater) external {} + + function setRewardsForAllSubmitter( + address _submitter, + bool _newValue + ) external {} + + function setOperatorAVSSplit( + address operator, + address avs, + uint16 split + ) external {} + + function setOperatorPISplit(address operator, uint16 split) external {} +} diff --git a/test/mocks/ServiceManagerMock.sol b/test/mocks/ServiceManagerMock.sol index 1a7f0089..8af99426 100644 --- a/test/mocks/ServiceManagerMock.sol +++ b/test/mocks/ServiceManagerMock.sol @@ -6,11 +6,17 @@ import "../../src/ServiceManagerBase.sol"; contract ServiceManagerMock is ServiceManagerBase { constructor( IAVSDirectory _avsDirectory, + IRewardsCoordinator _rewardsCoordinator, IRegistryCoordinator _registryCoordinator, IStakeRegistry _stakeRegistry - ) ServiceManagerBase(_avsDirectory, _registryCoordinator, _stakeRegistry) {} + ) + ServiceManagerBase(_avsDirectory, _rewardsCoordinator, _registryCoordinator, _stakeRegistry) + {} - function initialize(address initialOwner) public virtual initializer { - __ServiceManagerBase_init(initialOwner); + function initialize( + address initialOwner, + address rewardsInitiator + ) public virtual initializer { + __ServiceManagerBase_init(initialOwner, rewardsInitiator); } } diff --git a/test/unit/BLSApkRegistryUnit.t.sol b/test/unit/BLSApkRegistryUnit.t.sol index 4b3c1f1d..5f800444 100644 --- a/test/unit/BLSApkRegistryUnit.t.sol +++ b/test/unit/BLSApkRegistryUnit.t.sol @@ -57,20 +57,26 @@ contract BLSApkRegistryUnitTests is BLSMockAVSDeployer, IBLSApkRegistryEvents { addressIsExcludedFromFuzzedInputs[defaultOperator] = true; addressIsExcludedFromFuzzedInputs[address(proxyAdmin)] = true; - pubkeyRegistrationParams.pubkeyG1 = BN254.generatorG1().scalar_mul(privKey); + pubkeyRegistrationParams.pubkeyG1 = BN254.generatorG1().scalar_mul( + privKey + ); defaultPubkey = pubkeyRegistrationParams.pubkeyG1; defaultPubkeyHash = BN254.hashG1Point(defaultPubkey); //privKey*G2 - pubkeyRegistrationParams.pubkeyG2.X[1] = - 19_101_821_850_089_705_274_637_533_855_249_918_363_070_101_489_527_618_151_493_230_256_975_900_223_847; - pubkeyRegistrationParams.pubkeyG2.X[0] = - 5_334_410_886_741_819_556_325_359_147_377_682_006_012_228_123_419_628_681_352_847_439_302_316_235_957; - pubkeyRegistrationParams.pubkeyG2.Y[1] = - 354_176_189_041_917_478_648_604_979_334_478_067_325_821_134_838_555_150_300_539_079_146_482_658_331; - pubkeyRegistrationParams.pubkeyG2.Y[0] = - 4_185_483_097_059_047_421_902_184_823_581_361_466_320_657_066_600_218_863_748_375_739_772_335_928_910; + pubkeyRegistrationParams.pubkeyG2.X[ + 1 + ] = 19_101_821_850_089_705_274_637_533_855_249_918_363_070_101_489_527_618_151_493_230_256_975_900_223_847; + pubkeyRegistrationParams.pubkeyG2.X[ + 0 + ] = 5_334_410_886_741_819_556_325_359_147_377_682_006_012_228_123_419_628_681_352_847_439_302_316_235_957; + pubkeyRegistrationParams.pubkeyG2.Y[ + 1 + ] = 354_176_189_041_917_478_648_604_979_334_478_067_325_821_134_838_555_150_300_539_079_146_482_658_331; + pubkeyRegistrationParams.pubkeyG2.Y[ + 0 + ] = 4_185_483_097_059_047_421_902_184_823_581_361_466_320_657_066_600_218_863_748_375_739_772_335_928_910; // Initialize 3 quorums _initializeQuorum(); @@ -89,7 +95,9 @@ contract BLSApkRegistryUnitTests is BLSMockAVSDeployer, IBLSApkRegistryEvents { initializedQuorums[quorumNumber] = true; // Mark quorum initialized for other tests - initializedQuorumBitmap = uint192(initializedQuorumBitmap.setBit(quorumNumber)); + initializedQuorumBitmap = uint192( + initializedQuorumBitmap.setBit(quorumNumber) + ); initializedQuorumBytes = initializedQuorumBitmap.bitmapToBytesArray(); } @@ -105,7 +113,9 @@ contract BLSApkRegistryUnitTests is BLSMockAVSDeployer, IBLSApkRegistryEvents { /// @dev initializeQuorum based on passed in bitmap of quorum numbers /// assumes that bitmap does not contain already initailized quorums and doesn't increment nextQuorum function _initializeFuzzedQuorums(uint192 bitmap) internal { - bytes memory quorumNumbers = bitmapUtilsWrapper.bitmapToBytesArray(bitmap); + bytes memory quorumNumbers = bitmapUtilsWrapper.bitmapToBytesArray( + bitmap + ); for (uint256 i = 0; i < quorumNumbers.length; i++) { uint8 quorumNumber = uint8(quorumNumbers[i]); @@ -131,7 +141,9 @@ contract BLSApkRegistryUnitTests is BLSMockAVSDeployer, IBLSApkRegistryEvents { } function _getRandBool(uint256 seed) internal view returns (bool) { - uint256 randomNumber = uint256(keccak256(abi.encodePacked(block.timestamp, seed))); + uint256 randomNumber = uint256( + keccak256(abi.encodePacked(block.timestamp, seed)) + ); return randomNumber % 2 == 0; } @@ -141,8 +153,11 @@ contract BLSApkRegistryUnitTests is BLSMockAVSDeployer, IBLSApkRegistryEvents { * */ - function _signMessage(address signer) internal view returns (BN254.G1Point memory) { - BN254.G1Point memory messageHash = registryCoordinator.pubkeyRegistrationMessageHash(signer); + function _signMessage( + address signer + ) internal view returns (BN254.G1Point memory) { + BN254.G1Point memory messageHash = registryCoordinator + .pubkeyRegistrationMessageHash(signer); return BN254.scalar_mul(messageHash, privKey); } @@ -165,19 +180,31 @@ contract BLSApkRegistryUnitTests is BLSMockAVSDeployer, IBLSApkRegistryEvents { /** * @dev registering operator with the default preset BLS key */ - function _registerDefaultBLSPubkey(address operator) internal returns (bytes32) { - pubkeyRegistrationParams.pubkeyRegistrationSignature = _signMessage(operator); - BN254.G1Point memory messageHash = - registryCoordinator.pubkeyRegistrationMessageHash(operator); + function _registerDefaultBLSPubkey( + address operator + ) internal returns (bytes32) { + pubkeyRegistrationParams.pubkeyRegistrationSignature = _signMessage( + operator + ); + BN254.G1Point memory messageHash = registryCoordinator + .pubkeyRegistrationMessageHash(operator); cheats.prank(address(registryCoordinator)); - return blsApkRegistry.registerBLSPublicKey(operator, pubkeyRegistrationParams, messageHash); + return + blsApkRegistry.registerBLSPublicKey( + operator, + pubkeyRegistrationParams, + messageHash + ); } /** * @dev register operator, assumes operator has a registered BLS public key and that quorumNumbers are valid */ - function _registerOperator(address operator, bytes memory quorumNumbers) internal { + function _registerOperator( + address operator, + bytes memory quorumNumbers + ) internal { bytes32 operatorId = blsApkRegistry.getOperatorId(operator); cheats.prank(address(registryCoordinator)); cheats.expectEmit(true, true, true, true, address(blsApkRegistry)); @@ -188,7 +215,10 @@ contract BLSApkRegistryUnitTests is BLSMockAVSDeployer, IBLSApkRegistryEvents { /** * @dev deregister operator, assumes operator has a registered BLS public key and that quorumNumbers are valid */ - function _deregisterOperator(address operator, bytes memory quorumNumbers) internal { + function _deregisterOperator( + address operator, + bytes memory quorumNumbers + ) internal { bytes32 operatorId = blsApkRegistry.getOperatorId(operator); cheats.prank(address(registryCoordinator)); cheats.expectEmit(true, true, true, true, address(blsApkRegistry)); @@ -202,8 +232,12 @@ contract BLSApkRegistryUnitTests is BLSMockAVSDeployer, IBLSApkRegistryEvents { * */ - function _getApks(bytes memory quorumNumbers) internal view returns (BN254.G1Point[] memory) { - BN254.G1Point[] memory quorumApks = new BN254.G1Point[](quorumNumbers.length); + function _getApks( + bytes memory quorumNumbers + ) internal view returns (BN254.G1Point[] memory) { + BN254.G1Point[] memory quorumApks = new BN254.G1Point[]( + quorumNumbers.length + ); for (uint8 i = 0; i < quorumNumbers.length; i++) { quorumApks[i] = blsApkRegistry.getApk(uint8(quorumNumbers[i])); } @@ -222,7 +256,9 @@ contract BLSApkRegistryUnitTests is BLSMockAVSDeployer, IBLSApkRegistryEvents { "apksBefore and quorumNumbers must be the same length" ); assertEq( - apksBefore.length, apksAfter.length, "apksBefore and apksAfter must be the same length" + apksBefore.length, + apksAfter.length, + "apksBefore and apksAfter must be the same length" ); for (uint256 i = 0; i < apksBefore.length; i++) { uint8 quorumNumber = uint8(quorumNumbers[i]); @@ -234,9 +270,11 @@ contract BLSApkRegistryUnitTests is BLSMockAVSDeployer, IBLSApkRegistryEvents { "quorum apk not updated correctly adding the operator pubkey" ); - uint32 quorumHistoryLength = blsApkRegistry.getApkHistoryLength(quorumNumber); - IBLSApkRegistry.ApkUpdate memory latestApkUpdate = - blsApkRegistry.getApkUpdateAtIndex(quorumNumber, quorumHistoryLength - 1); + uint32 quorumHistoryLength = blsApkRegistry.getApkHistoryLength( + quorumNumber + ); + IBLSApkRegistry.ApkUpdate memory latestApkUpdate = blsApkRegistry + .getApkUpdateAtIndex(quorumNumber, quorumHistoryLength - 1); assertEq( latestApkUpdate.apkHash, bytes24(BN254.hashG1Point(apkAfter)), @@ -279,7 +317,9 @@ contract BLSApkRegistryUnitTests_configAndGetters is BLSApkRegistryUnitTests { } /// @notice test for BLSApkRegistry.registerBLSPublicKey() -contract BLSApkRegistryUnitTests_registerBLSPublicKey is BLSApkRegistryUnitTests { +contract BLSApkRegistryUnitTests_registerBLSPublicKey is + BLSApkRegistryUnitTests +{ using BN254 for BN254.G1Point; function testFuzz_registerOperator_Revert_WhenNotRegistryCoordinator( @@ -287,59 +327,99 @@ contract BLSApkRegistryUnitTests_registerBLSPublicKey is BLSApkRegistryUnitTests ) public filterFuzzedAddressInputs(nonCoordinatorAddress) { cheats.assume(nonCoordinatorAddress != address(registryCoordinator)); - pubkeyRegistrationParams.pubkeyRegistrationSignature = _signMessage(defaultOperator); - BN254.G1Point memory messageHash = - registryCoordinator.pubkeyRegistrationMessageHash(defaultOperator); + pubkeyRegistrationParams.pubkeyRegistrationSignature = _signMessage( + defaultOperator + ); + BN254.G1Point memory messageHash = registryCoordinator + .pubkeyRegistrationMessageHash(defaultOperator); cheats.prank(address(nonCoordinatorAddress)); cheats.expectRevert( "BLSApkRegistry.onlyRegistryCoordinator: caller is not the registry coordinator" ); - blsApkRegistry.registerBLSPublicKey(defaultOperator, pubkeyRegistrationParams, messageHash); + blsApkRegistry.registerBLSPublicKey( + defaultOperator, + pubkeyRegistrationParams, + messageHash + ); } - function testFuzz_registerOperator_Revert_WhenZeroPubkeyHash(address operator) public { + function testFuzz_registerOperator_Revert_WhenZeroPubkeyHash( + address operator + ) public filterFuzzedAddressInputs(operator) { pubkeyRegistrationParams.pubkeyG1.X = 0; pubkeyRegistrationParams.pubkeyG1.Y = 0; - BN254.G1Point memory messageHash = - registryCoordinator.pubkeyRegistrationMessageHash(operator); + BN254.G1Point memory messageHash = registryCoordinator + .pubkeyRegistrationMessageHash(operator); cheats.prank(address(registryCoordinator)); - cheats.expectRevert("BLSApkRegistry.registerBLSPublicKey: cannot register zero pubkey"); - blsApkRegistry.registerBLSPublicKey(operator, pubkeyRegistrationParams, messageHash); + cheats.expectRevert( + "BLSApkRegistry.registerBLSPublicKey: cannot register zero pubkey" + ); + blsApkRegistry.registerBLSPublicKey( + operator, + pubkeyRegistrationParams, + messageHash + ); } - function testFuzz_registerOperator_Revert_WhenOperatorAlreadyRegistered(address operator) - public - { - pubkeyRegistrationParams.pubkeyRegistrationSignature = _signMessage(operator); - BN254.G1Point memory messageHash = - registryCoordinator.pubkeyRegistrationMessageHash(operator); + function testFuzz_registerOperator_Revert_WhenOperatorAlreadyRegistered( + address operator + ) public filterFuzzedAddressInputs(operator) { + pubkeyRegistrationParams.pubkeyRegistrationSignature = _signMessage( + operator + ); + BN254.G1Point memory messageHash = registryCoordinator + .pubkeyRegistrationMessageHash(operator); cheats.startPrank(address(registryCoordinator)); - blsApkRegistry.registerBLSPublicKey(operator, pubkeyRegistrationParams, messageHash); + blsApkRegistry.registerBLSPublicKey( + operator, + pubkeyRegistrationParams, + messageHash + ); cheats.expectRevert( "BLSApkRegistry.registerBLSPublicKey: operator already registered pubkey" ); - blsApkRegistry.registerBLSPublicKey(operator, pubkeyRegistrationParams, messageHash); + blsApkRegistry.registerBLSPublicKey( + operator, + pubkeyRegistrationParams, + messageHash + ); } function testFuzz_registerOperator_Revert_WhenPubkeyAlreadyRegistered( address operator, address operator2 - ) public { + ) + public + filterFuzzedAddressInputs(operator) + filterFuzzedAddressInputs(operator2) + { cheats.assume(operator != address(0)); cheats.assume(operator != operator2); - BN254.G1Point memory messageHash = - registryCoordinator.pubkeyRegistrationMessageHash(operator); - pubkeyRegistrationParams.pubkeyRegistrationSignature = _signMessage(operator); + BN254.G1Point memory messageHash = registryCoordinator + .pubkeyRegistrationMessageHash(operator); + pubkeyRegistrationParams.pubkeyRegistrationSignature = _signMessage( + operator + ); cheats.startPrank(address(registryCoordinator)); - blsApkRegistry.registerBLSPublicKey(operator, pubkeyRegistrationParams, messageHash); + blsApkRegistry.registerBLSPublicKey( + operator, + pubkeyRegistrationParams, + messageHash + ); - cheats.expectRevert("BLSApkRegistry.registerBLSPublicKey: public key already registered"); - blsApkRegistry.registerBLSPublicKey(operator2, pubkeyRegistrationParams, messageHash); + cheats.expectRevert( + "BLSApkRegistry.registerBLSPublicKey: public key already registered" + ); + blsApkRegistry.registerBLSPublicKey( + operator2, + pubkeyRegistrationParams, + messageHash + ); } /** @@ -349,10 +429,14 @@ contract BLSApkRegistryUnitTests_registerBLSPublicKey is BLSApkRegistryUnitTests function testFuzz_registerOperator_Revert_WhenInvalidSignature( address operator, address invalidOperator - ) public { + ) + public + filterFuzzedAddressInputs(operator) + filterFuzzedAddressInputs(invalidOperator) + { cheats.assume(invalidOperator != operator); - BN254.G1Point memory messageHash = - registryCoordinator.pubkeyRegistrationMessageHash(operator); + BN254.G1Point memory messageHash = registryCoordinator + .pubkeyRegistrationMessageHash(operator); BN254.G1Point memory invalidSignature = _signMessage(invalidOperator); pubkeyRegistrationParams.pubkeyRegistrationSignature = invalidSignature; @@ -361,56 +445,88 @@ contract BLSApkRegistryUnitTests_registerBLSPublicKey is BLSApkRegistryUnitTests cheats.expectRevert( "BLSApkRegistry.registerBLSPublicKey: either the G1 signature is wrong, or G1 and G2 private key do not match" ); - blsApkRegistry.registerBLSPublicKey(operator, pubkeyRegistrationParams, messageHash); + blsApkRegistry.registerBLSPublicKey( + operator, + pubkeyRegistrationParams, + messageHash + ); } /** * @dev operator is registering their public key but G1 and G2 private keys do not match */ - function testFuzz_registerOperator_Revert_WhenInvalidSignatureMismatchKey(address operator) - public - filterFuzzedAddressInputs(operator) - { - pubkeyRegistrationParams.pubkeyRegistrationSignature = _signMessage(operator); + function testFuzz_registerOperator_Revert_WhenInvalidSignatureMismatchKey( + address operator + ) public filterFuzzedAddressInputs(operator) { + pubkeyRegistrationParams.pubkeyRegistrationSignature = _signMessage( + operator + ); BN254.G1Point memory badPubkeyG1 = BN254.generatorG1().scalar_mul(420); // mismatch public keys pubkeyRegistrationParams.pubkeyG1 = badPubkeyG1; - BN254.G1Point memory messageHash = - registryCoordinator.pubkeyRegistrationMessageHash(operator); + 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" ); - blsApkRegistry.registerBLSPublicKey(operator, pubkeyRegistrationParams, messageHash); + blsApkRegistry.registerBLSPublicKey( + operator, + pubkeyRegistrationParams, + messageHash + ); } /** * @dev fuzz tests for different operator addresses but uses the same BLS key for each. * Checks for storage mappings being set correctly. */ - function testFuzz_registerBLSPublicKey(address operator) - public - filterFuzzedAddressInputs(operator) - { + function testFuzz_registerBLSPublicKey( + address operator + ) public filterFuzzedAddressInputs(operator) { // sign messagehash for operator with private key - pubkeyRegistrationParams.pubkeyRegistrationSignature = _signMessage(operator); - BN254.G1Point memory messageHash = - registryCoordinator.pubkeyRegistrationMessageHash(operator); + pubkeyRegistrationParams.pubkeyRegistrationSignature = _signMessage( + operator + ); + BN254.G1Point memory messageHash = registryCoordinator + .pubkeyRegistrationMessageHash(operator); cheats.prank(address(registryCoordinator)); cheats.expectEmit(true, true, true, true, address(blsApkRegistry)); emit NewPubkeyRegistration( - operator, pubkeyRegistrationParams.pubkeyG1, pubkeyRegistrationParams.pubkeyG2 + operator, + pubkeyRegistrationParams.pubkeyG1, + pubkeyRegistrationParams.pubkeyG2 + ); + blsApkRegistry.registerBLSPublicKey( + operator, + pubkeyRegistrationParams, + messageHash ); - blsApkRegistry.registerBLSPublicKey(operator, pubkeyRegistrationParams, messageHash); - (BN254.G1Point memory registeredPubkey, bytes32 registeredpkHash) = - blsApkRegistry.getRegisteredPubkey(operator); - assertEq(registeredPubkey.X, defaultPubkey.X, "registeredPubkey not set correctly"); - assertEq(registeredPubkey.Y, defaultPubkey.Y, "registeredPubkey not set correctly"); - assertEq(registeredpkHash, defaultPubkeyHash, "registeredpkHash not set correctly"); + ( + BN254.G1Point memory registeredPubkey, + bytes32 registeredpkHash + ) = blsApkRegistry.getRegisteredPubkey(operator); + assertEq( + registeredPubkey.X, + defaultPubkey.X, + "registeredPubkey not set correctly" + ); + assertEq( + registeredPubkey.Y, + defaultPubkey.Y, + "registeredPubkey not set correctly" + ); + assertEq( + registeredpkHash, + defaultPubkeyHash, + "registeredpkHash not set correctly" + ); assertEq( - blsApkRegistry.pubkeyHashToOperator(BN254.hashG1Point(defaultPubkey)), + blsApkRegistry.pubkeyHashToOperator( + BN254.hashG1Point(defaultPubkey) + ), operator, "operator address not stored correctly" ); @@ -434,12 +550,13 @@ contract BLSApkRegistryUnitTests_registerOperator is BLSApkRegistryUnitTests { blsApkRegistry.registerOperator(nonCoordinatorAddress, new bytes(0)); } - function testFuzz_registerOperator_Revert_WhenOperatorDoesNotOwnPubkey(address operator) - public - filterFuzzedAddressInputs(operator) - { + function testFuzz_registerOperator_Revert_WhenOperatorDoesNotOwnPubkey( + address operator + ) public filterFuzzedAddressInputs(operator) { cheats.prank(address(registryCoordinator)); - cheats.expectRevert("BLSApkRegistry.getRegisteredPubkey: operator is not registered"); + cheats.expectRevert( + "BLSApkRegistry.getRegisteredPubkey: operator is not registered" + ); blsApkRegistry.registerOperator(operator, new bytes(1)); } @@ -450,13 +567,19 @@ contract BLSApkRegistryUnitTests_registerOperator is BLSApkRegistryUnitTests { cheats.prank(address(registryCoordinator)); cheats.assume(quorumBitmap > initializedQuorumBitmap); // mask out quorums that are already initialized - quorumBitmap = uint192(quorumBitmap.minus(uint256(initializedQuorumBitmap))); - bytes memory quorumNumbers = bitmapUtilsWrapper.bitmapToBytesArray(quorumBitmap); + quorumBitmap = uint192( + quorumBitmap.minus(uint256(initializedQuorumBitmap)) + ); + bytes memory quorumNumbers = bitmapUtilsWrapper.bitmapToBytesArray( + quorumBitmap + ); _registerDefaultBLSPubkey(operator); cheats.prank(address(registryCoordinator)); - cheats.expectRevert("BLSApkRegistry._processQuorumApkUpdate: quorum does not exist"); + cheats.expectRevert( + "BLSApkRegistry._processQuorumApkUpdate: quorum does not exist" + ); blsApkRegistry.registerOperator(operator, quorumNumbers); } @@ -472,15 +595,26 @@ contract BLSApkRegistryUnitTests_registerOperator is BLSApkRegistryUnitTests { ) public filterFuzzedAddressInputs(operator) { // Test setup, initialize fuzzed quorums and register operator BLS pubkey cheats.assume(quorumBitmap > initializedQuorumBitmap); - uint192 initializingBitmap = uint192(quorumBitmap.minus(uint256(initializedQuorumBitmap))); + uint192 initializingBitmap = uint192( + quorumBitmap.minus(uint256(initializedQuorumBitmap)) + ); _initializeFuzzedQuorums(initializingBitmap); - bytes memory quorumNumbers = bitmapUtilsWrapper.bitmapToBytesArray(quorumBitmap); - (BN254.G1Point memory pubkey,) = _registerRandomBLSPubkey(operator, randomSeed); + bytes memory quorumNumbers = bitmapUtilsWrapper.bitmapToBytesArray( + quorumBitmap + ); + (BN254.G1Point memory pubkey, ) = _registerRandomBLSPubkey( + operator, + randomSeed + ); // get before values - BN254.G1Point[] memory quorumApksBefore = new BN254.G1Point[](quorumNumbers.length); + BN254.G1Point[] memory quorumApksBefore = new BN254.G1Point[]( + quorumNumbers.length + ); for (uint8 i = 0; i < quorumNumbers.length; i++) { - quorumApksBefore[i] = blsApkRegistry.getApk(uint8(quorumNumbers[i])); + quorumApksBefore[i] = blsApkRegistry.getApk( + uint8(quorumNumbers[i]) + ); } // registerOperator with expected OperatorAddedToQuorums event @@ -494,16 +628,20 @@ contract BLSApkRegistryUnitTests_registerOperator is BLSApkRegistryUnitTests { for (uint8 i = 0; i < quorumNumbers.length; i++) { // Check currentApk[quorumNumber] values uint8 quorumNumber = uint8(quorumNumbers[i]); - BN254.G1Point memory quorumApkAfter = blsApkRegistry.getApk(uint8(quorumNumbers[i])); + BN254.G1Point memory quorumApkAfter = blsApkRegistry.getApk( + uint8(quorumNumbers[i]) + ); assertEq( BN254.hashG1Point(quorumApkAfter), BN254.hashG1Point(quorumApksBefore[i].plus(pubkey)), "quorum apk not updated correctly adding the operator pubkey" ); // Check the latest ApkUpdate values - uint32 quorumHistoryLength = blsApkRegistry.getApkHistoryLength(quorumNumber); - IBLSApkRegistry.ApkUpdate memory latestApkUpdate = - blsApkRegistry.getApkUpdateAtIndex(quorumNumber, quorumHistoryLength - 1); + uint32 quorumHistoryLength = blsApkRegistry.getApkHistoryLength( + quorumNumber + ); + IBLSApkRegistry.ApkUpdate memory latestApkUpdate = blsApkRegistry + .getApkUpdateAtIndex(quorumNumber, quorumHistoryLength - 1); assertEq( latestApkUpdate.apkHash, bytes24(BN254.hashG1Point(quorumApkAfter)), @@ -540,12 +678,13 @@ contract BLSApkRegistryUnitTests_deregisterOperator is BLSApkRegistryUnitTests { blsApkRegistry.deregisterOperator(nonCoordinatorAddress, new bytes(0)); } - function testFuzz_deregisterOperator_Revert_WhenOperatorDoesNotOwnPubkey(address operator) - public - filterFuzzedAddressInputs(operator) - { + function testFuzz_deregisterOperator_Revert_WhenOperatorDoesNotOwnPubkey( + address operator + ) public filterFuzzedAddressInputs(operator) { cheats.prank(address(registryCoordinator)); - cheats.expectRevert("BLSApkRegistry.getRegisteredPubkey: operator is not registered"); + cheats.expectRevert( + "BLSApkRegistry.getRegisteredPubkey: operator is not registered" + ); blsApkRegistry.registerOperator(operator, new bytes(1)); } @@ -556,16 +695,22 @@ contract BLSApkRegistryUnitTests_deregisterOperator is BLSApkRegistryUnitTests { cheats.prank(address(registryCoordinator)); cheats.assume(quorumBitmap > initializedQuorumBitmap); // mask out quorums that are already initialized - quorumBitmap = uint192(quorumBitmap.minus(uint256(initializedQuorumBitmap))); - bytes memory validQuorumNumbers = - bitmapUtilsWrapper.bitmapToBytesArray(initializedQuorumBitmap); - bytes memory invalidQuorumNumbers = bitmapUtilsWrapper.bitmapToBytesArray(quorumBitmap); + quorumBitmap = uint192( + quorumBitmap.minus(uint256(initializedQuorumBitmap)) + ); + bytes memory validQuorumNumbers = bitmapUtilsWrapper.bitmapToBytesArray( + initializedQuorumBitmap + ); + bytes memory invalidQuorumNumbers = bitmapUtilsWrapper + .bitmapToBytesArray(quorumBitmap); _registerDefaultBLSPubkey(operator); _registerOperator(operator, validQuorumNumbers); cheats.prank(address(registryCoordinator)); - cheats.expectRevert("BLSApkRegistry._processQuorumApkUpdate: quorum does not exist"); + cheats.expectRevert( + "BLSApkRegistry._processQuorumApkUpdate: quorum does not exist" + ); blsApkRegistry.deregisterOperator(operator, invalidQuorumNumbers); } @@ -581,16 +726,27 @@ contract BLSApkRegistryUnitTests_deregisterOperator is BLSApkRegistryUnitTests { ) public filterFuzzedAddressInputs(operator) { // Test setup, initialize fuzzed quorums and register operator BLS pubkey cheats.assume(quorumBitmap > initializedQuorumBitmap); - uint192 initializingBitmap = uint192(quorumBitmap.minus(uint256(initializedQuorumBitmap))); + uint192 initializingBitmap = uint192( + quorumBitmap.minus(uint256(initializedQuorumBitmap)) + ); _initializeFuzzedQuorums(initializingBitmap); - bytes memory quorumNumbers = bitmapUtilsWrapper.bitmapToBytesArray(quorumBitmap); - (BN254.G1Point memory pubkey,) = _registerRandomBLSPubkey(operator, randomSeed); + bytes memory quorumNumbers = bitmapUtilsWrapper.bitmapToBytesArray( + quorumBitmap + ); + (BN254.G1Point memory pubkey, ) = _registerRandomBLSPubkey( + operator, + randomSeed + ); _registerOperator(operator, quorumNumbers); // get before values - BN254.G1Point[] memory quorumApksBefore = new BN254.G1Point[](quorumNumbers.length); + BN254.G1Point[] memory quorumApksBefore = new BN254.G1Point[]( + quorumNumbers.length + ); for (uint8 i = 0; i < quorumNumbers.length; i++) { - quorumApksBefore[i] = blsApkRegistry.getApk(uint8(quorumNumbers[i])); + quorumApksBefore[i] = blsApkRegistry.getApk( + uint8(quorumNumbers[i]) + ); } // registerOperator with expected OperatorAddedToQuorums event @@ -604,16 +760,20 @@ contract BLSApkRegistryUnitTests_deregisterOperator is BLSApkRegistryUnitTests { for (uint8 i = 0; i < quorumNumbers.length; i++) { // Check currentApk[quorumNumber] values uint8 quorumNumber = uint8(quorumNumbers[i]); - BN254.G1Point memory quorumApkAfter = blsApkRegistry.getApk(uint8(quorumNumbers[i])); + BN254.G1Point memory quorumApkAfter = blsApkRegistry.getApk( + uint8(quorumNumbers[i]) + ); assertEq( BN254.hashG1Point(quorumApkAfter), BN254.hashG1Point(quorumApksBefore[i].plus(pubkey.negate())), "quorum apk not updated correctly removing the operator pubkey" ); // Check the latest ApkUpdate values - uint32 quorumHistoryLength = blsApkRegistry.getApkHistoryLength(quorumNumber); - IBLSApkRegistry.ApkUpdate memory latestApkUpdate = - blsApkRegistry.getApkUpdateAtIndex(quorumNumber, quorumHistoryLength - 1); + uint32 quorumHistoryLength = blsApkRegistry.getApkHistoryLength( + quorumNumber + ); + IBLSApkRegistry.ApkUpdate memory latestApkUpdate = blsApkRegistry + .getApkUpdateAtIndex(quorumNumber, quorumHistoryLength - 1); assertEq( latestApkUpdate.apkHash, bytes24(BN254.hashG1Point(quorumApkAfter)), @@ -645,7 +805,10 @@ contract BLSApkRegistryUnitTests_quorumApkUpdates is BLSApkRegistryUnitTests { * @dev register/deregister up to 200 operators and check quorum apk updates * Test uses only the defaultQuorumNumber */ - function testFuzz_quorumApkUpdates(uint256 numOperators, uint256[200] memory randSeed) public { + function testFuzz_quorumApkUpdates( + uint256 numOperators, + uint256[200] memory randSeed + ) public { cheats.assume(0 < numOperators && numOperators <= 200); bytes memory quorumNumbers = new bytes(1); quorumNumbers[0] = bytes1(defaultQuorumNumber); @@ -659,11 +822,17 @@ contract BLSApkRegistryUnitTests_quorumApkUpdates is BLSApkRegistryUnitTests { // register and check quorum apk updates BN254.G1Point[] memory quorumApksBefore = _getApks(quorumNumbers); address operator = _selectNewOperator(); - (BN254.G1Point memory operatorPubkey,) = _registerRandomBLSPubkey(operator, randSeed[i]); + (BN254.G1Point memory operatorPubkey, ) = _registerRandomBLSPubkey( + operator, + randSeed[i] + ); _registerOperator(operator, quorumNumbers); BN254.G1Point[] memory quorumApksAfter = _getApks(quorumNumbers); _assertQuorumApkUpdates( - quorumNumbers, quorumApksBefore, quorumApksAfter, operatorPubkey + quorumNumbers, + quorumApksBefore, + quorumApksAfter, + operatorPubkey ); // deregister and check quorum apk updates @@ -673,7 +842,10 @@ contract BLSApkRegistryUnitTests_quorumApkUpdates is BLSApkRegistryUnitTests { _deregisterOperator(operator, quorumNumbers); quorumApksAfter = _getApks(quorumNumbers); _assertQuorumApkUpdates( - quorumNumbers, quorumApksBefore, quorumApksAfter, operatorPubkey.negate() + quorumNumbers, + quorumApksBefore, + quorumApksAfter, + operatorPubkey.negate() ); } } @@ -691,9 +863,13 @@ contract BLSApkRegistryUnitTests_quorumApkUpdates is BLSApkRegistryUnitTests { cheats.assume(0 < numOperators && numOperators <= 50); cheats.assume(quorumBitmap > initializedQuorumBitmap); // mask out quorums that are already initialized - uint192 initializingBitmap = uint192(quorumBitmap.minus(uint256(initializedQuorumBitmap))); + uint192 initializingBitmap = uint192( + quorumBitmap.minus(uint256(initializedQuorumBitmap)) + ); _initializeFuzzedQuorums(initializingBitmap); - bytes memory quorumNumbers = bitmapUtilsWrapper.bitmapToBytesArray(quorumBitmap); + bytes memory quorumNumbers = bitmapUtilsWrapper.bitmapToBytesArray( + quorumBitmap + ); /** * For each operator, randomly proceed with either registering/deregistering an operator @@ -704,11 +880,17 @@ contract BLSApkRegistryUnitTests_quorumApkUpdates is BLSApkRegistryUnitTests { // register and check quorum apk updates BN254.G1Point[] memory quorumApksBefore = _getApks(quorumNumbers); address operator = _selectNewOperator(); - (BN254.G1Point memory operatorPubkey,) = _registerRandomBLSPubkey(operator, randSeed[i]); + (BN254.G1Point memory operatorPubkey, ) = _registerRandomBLSPubkey( + operator, + randSeed[i] + ); _registerOperator(operator, quorumNumbers); BN254.G1Point[] memory quorumApksAfter = _getApks(quorumNumbers); _assertQuorumApkUpdates( - quorumNumbers, quorumApksBefore, quorumApksAfter, operatorPubkey + quorumNumbers, + quorumApksBefore, + quorumApksAfter, + operatorPubkey ); // deregister and check quorum apk updates @@ -718,7 +900,10 @@ contract BLSApkRegistryUnitTests_quorumApkUpdates is BLSApkRegistryUnitTests { _deregisterOperator(operator, quorumNumbers); quorumApksAfter = _getApks(quorumNumbers); _assertQuorumApkUpdates( - quorumNumbers, quorumApksBefore, quorumApksAfter, operatorPubkey.negate() + quorumNumbers, + quorumApksBefore, + quorumApksAfter, + operatorPubkey.negate() ); } } @@ -740,9 +925,13 @@ contract BLSApkRegistryUnitTests_quorumApkUpdates is BLSApkRegistryUnitTests { _initializeFuzzedQuorum(quorumNumber2); } - BN254.G1Point[] memory quorumApksBefore = new BN254.G1Point[](quorumNumbers.length); + BN254.G1Point[] memory quorumApksBefore = new BN254.G1Point[]( + quorumNumbers.length + ); for (uint8 i = 0; i < quorumNumbers.length; i++) { - quorumApksBefore[i] = blsApkRegistry.getApk(uint8(quorumNumbers[i])); + quorumApksBefore[i] = blsApkRegistry.getApk( + uint8(quorumNumbers[i]) + ); } // use harnessed function to directly set the pubkey, bypassing the ordinary checks @@ -753,9 +942,16 @@ contract BLSApkRegistryUnitTests_quorumApkUpdates is BLSApkRegistryUnitTests { //check quorum apk updates for (uint8 i = 0; i < quorumNumbers.length; i++) { - BN254.G1Point memory quorumApkAfter = blsApkRegistry.getApk(uint8(quorumNumbers[i])); + BN254.G1Point memory quorumApkAfter = blsApkRegistry.getApk( + uint8(quorumNumbers[i]) + ); assertEq( - BN254.hashG1Point(BN254.plus(quorumApkAfter, BN254.negate(quorumApksBefore[i]))), + BN254.hashG1Point( + BN254.plus( + quorumApkAfter, + BN254.negate(quorumApksBefore[i]) + ) + ), BN254.hashG1Point(defaultPubKey), "quorum apk not updated correctly" ); @@ -775,7 +971,9 @@ contract BLSApkRegistryUnitTests_quorumApkUpdates is BLSApkRegistryUnitTests { _registerRandomBLSPubkey(defaultOperator, randSeed); _registerOperator(defaultOperator, quorumNumbers); - BN254.G1Point memory quorumApk = blsApkRegistry.getApk(defaultQuorumNumber); + BN254.G1Point memory quorumApk = blsApkRegistry.getApk( + defaultQuorumNumber + ); BN254.G1Point memory negatedQuorumApk = BN254.negate(quorumApk); //register for one quorum with negative quorum apk @@ -800,8 +998,8 @@ contract BLSApkRegistryUnitTests_quorumApkUpdates is BLSApkRegistryUnitTests { uint256 blockGap, uint256 randSeed ) external { - numRegistrants = bound(numRegistrants, 1, 99); - blockGap = bound(blockGap, 0, 99); + numRegistrants = bound(numRegistrants, 1, 100); + blockGap = bound(blockGap, 0, 100); bytes memory quorumNumbers = new bytes(1); quorumNumbers[0] = bytes1(defaultQuorumNumber); @@ -811,18 +1009,23 @@ contract BLSApkRegistryUnitTests_quorumApkUpdates is BLSApkRegistryUnitTests { for (uint256 i = 0; i < numRegistrants; i++) { // generate operator and register them with BLS pubkey address operator = _selectNewOperator(); - (BN254.G1Point memory operatorPubkey,) = _registerRandomBLSPubkey( - operator, uint256(keccak256(abi.encodePacked(operator, randSeed))) + (BN254.G1Point memory operatorPubkey, ) = _registerRandomBLSPubkey( + operator, + uint256(keccak256(abi.encodePacked(operator, randSeed))) ); _registerOperator(operator, quorumNumbers); quorumApk = quorumApk.plus(operatorPubkey); quorumApkHash = bytes24(BN254.hashG1Point(quorumApk)); - uint256 historyLength = blsApkRegistry.getApkHistoryLength(defaultQuorumNumber); + uint256 historyLength = blsApkRegistry.getApkHistoryLength( + defaultQuorumNumber + ); assertEq( quorumApkHash, blsApkRegistry.getApkHashAtBlockNumberAndIndex( - defaultQuorumNumber, uint32(block.number + blockGap), historyLength - 1 + defaultQuorumNumber, + uint32(block.number + blockGap), + historyLength - 1 ), "incorrect quorum apk update" ); @@ -831,11 +1034,15 @@ contract BLSApkRegistryUnitTests_quorumApkUpdates is BLSApkRegistryUnitTests { _deregisterOperator(operator, quorumNumbers); quorumApk = quorumApk.plus(operatorPubkey.negate()); quorumApkHash = bytes24(BN254.hashG1Point(quorumApk)); - historyLength = blsApkRegistry.getApkHistoryLength(defaultQuorumNumber); + historyLength = blsApkRegistry.getApkHistoryLength( + defaultQuorumNumber + ); assertEq( quorumApkHash, blsApkRegistry.getApkHashAtBlockNumberAndIndex( - defaultQuorumNumber, uint32(block.number + blockGap), historyLength - 1 + defaultQuorumNumber, + uint32(block.number + blockGap), + historyLength - 1 ), "incorrect quorum apk update" ); @@ -850,12 +1057,12 @@ contract BLSApkRegistryUnitTests_quorumApkUpdates is BLSApkRegistryUnitTests { * and checking the correct revert messages are emitted for wrong blocknumber inputs */ function testFuzz_quorumApkUpdates_IncorrectBlockNumber( - uint32 numRegistrants, + uint256 numRegistrants, uint32 indexToCheck, uint32 wrongBlockNumber, uint256 randSeed ) external { - numRegistrants = uint32(bound(numRegistrants, 1, 99)); + numRegistrants = bound(numRegistrants, 1, 100); cheats.assume(indexToCheck < numRegistrants - 1); bytes memory quorumNumbers = new bytes(1); quorumNumbers[0] = bytes1(defaultQuorumNumber); @@ -865,25 +1072,34 @@ contract BLSApkRegistryUnitTests_quorumApkUpdates is BLSApkRegistryUnitTests { for (uint256 i = 0; i < numRegistrants; i++) { address operator = _selectNewOperator(); _registerRandomBLSPubkey( - operator, uint256(keccak256(abi.encodePacked(operator, randSeed))) + operator, + uint256(keccak256(abi.encodePacked(operator, randSeed))) ); _registerOperator(operator, quorumNumbers); cheats.roll(block.number + 100); } if (wrongBlockNumber < startingBlockNumber + indexToCheck * 100) { emit log_named_uint("index too recent: ", indexToCheck); - cheats.expectRevert("BLSApkRegistry._validateApkHashAtBlockNumber: index too recent"); + cheats.expectRevert( + "BLSApkRegistry._validateApkHashAtBlockNumber: index too recent" + ); blsApkRegistry.getApkHashAtBlockNumberAndIndex( - defaultQuorumNumber, wrongBlockNumber, indexToCheck + defaultQuorumNumber, + wrongBlockNumber, + indexToCheck ); } - if (wrongBlockNumber >= startingBlockNumber + (indexToCheck + 1) * 100) { + if ( + wrongBlockNumber >= startingBlockNumber + (indexToCheck + 1) * 100 + ) { emit log_named_uint("index not latest: ", indexToCheck); cheats.expectRevert( "BLSApkRegistry._validateApkHashAtBlockNumber: not latest apk update" ); blsApkRegistry.getApkHashAtBlockNumberAndIndex( - defaultQuorumNumber, wrongBlockNumber, indexToCheck + defaultQuorumNumber, + wrongBlockNumber, + indexToCheck ); } } @@ -909,7 +1125,9 @@ contract BLSApkRegistryUnitTests_quorumApkUpdates is BLSApkRegistryUnitTests { BN254.G1Point[] memory quorumApksBefore = new BN254.G1Point[](2); for (uint8 i = 0; i < quorumNumbers.length; i++) { - quorumApksBefore[i] = blsApkRegistry.getApk(uint8(quorumNumbers[i])); + quorumApksBefore[i] = blsApkRegistry.getApk( + uint8(quorumNumbers[i]) + ); } cheats.startPrank(address(registryCoordinator)); @@ -919,9 +1137,13 @@ contract BLSApkRegistryUnitTests_quorumApkUpdates is BLSApkRegistryUnitTests { BN254.G1Point memory quorumApkAfter; for (uint8 i = 0; i < quorumNumbers.length; i++) { quorumApkAfter = blsApkRegistry.getApk(uint8(quorumNumbers[i])); - BN254.G1Point memory quorumApk = blsApkRegistry.getApk(defaultQuorumNumber); + BN254.G1Point memory quorumApk = blsApkRegistry.getApk( + defaultQuorumNumber + ); assertEq( - BN254.hashG1Point(quorumApksBefore[i].plus(defaultPubKey.negate())), + BN254.hashG1Point( + quorumApksBefore[i].plus(defaultPubKey.negate()) + ), BN254.hashG1Point(quorumApkAfter), "quorum apk not updated correctly" ); diff --git a/test/unit/BLSSignatureCheckerUnit.t.sol b/test/unit/BLSSignatureCheckerUnit.t.sol index 29369b3f..3b5cf6e1 100644 --- a/test/unit/BLSSignatureCheckerUnit.t.sol +++ b/test/unit/BLSSignatureCheckerUnit.t.sol @@ -294,6 +294,7 @@ contract BLSSignatureCheckerUnitTests is BLSMockAVSDeployer { } function test_checkSignatures_revert_staleStakes() public { + vm.skip(true); uint256 numNonSigners = 2; uint256 quorumBitmap = 1; uint256 nonRandomNumber = 777; @@ -353,7 +354,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("RegCoord.getQuorumBitmapAtBlockNumberByIndex: quorumBitmapUpdate is from after blockNumber"); blsSignatureChecker.checkSignatures( msgHash, quorumNumbers, diff --git a/test/unit/BitmapUtils.t.sol b/test/unit/BitmapUtils.t.sol index 9de0df75..fd51298d 100644 --- a/test/unit/BitmapUtils.t.sol +++ b/test/unit/BitmapUtils.t.sol @@ -7,7 +7,7 @@ import "../harnesses/BitmapUtilsWrapper.sol"; import "forge-std/Test.sol"; contract BitmapUtilsUnitTests is Test { - Vm cheats = Vm(HEVM_ADDRESS); + Vm cheats = Vm(VM_ADDRESS); BitmapUtilsWrapper public bitmapUtilsWrapper; diff --git a/test/unit/ECDSAServiceManager.t.sol b/test/unit/ECDSAServiceManager.t.sol new file mode 100644 index 00000000..3b533d47 --- /dev/null +++ b/test/unit/ECDSAServiceManager.t.sol @@ -0,0 +1,186 @@ +// 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 MockRewardsCoordinator { + function createAVSRewardsSubmission( + IRewardsCoordinator.RewardsSubmission[] calldata + ) external pure {} +} + +contract ECDSAServiceManagerSetup is Test { + MockDelegationManager public mockDelegationManager; + MockAVSDirectory public mockAVSDirectory; + 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(); + mockStakeRegistry = new ECDSAStakeRegistryMock( + IDelegationManager(address(mockDelegationManager)) + ); + mockRewardsCoordinator = new MockRewardsCoordinator(); + + serviceManager = new ECDSAServiceManagerMock( + address(mockAVSDirectory), + address(mockStakeRegistry), + address(mockRewardsCoordinator), + address(mockDelegationManager) + ); + + 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)); + + uint256[] memory shares = new uint256[](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/ECDSAStakeRegistryEqualWeightUnit.t.sol b/test/unit/ECDSAStakeRegistryEqualWeightUnit.t.sol index 8775cd55..bc6337c5 100644 --- a/test/unit/ECDSAStakeRegistryEqualWeightUnit.t.sol +++ b/test/unit/ECDSAStakeRegistryEqualWeightUnit.t.sol @@ -11,40 +11,80 @@ import {ECDSAStakeRegistryEqualWeight} from "../../src/unaudited/examples/ECDSAS contract EqualWeightECDSARegistry is ECDSAStakeRegistrySetup { ECDSAStakeRegistryEqualWeight internal fixedWeightRegistry; + function setUp() public virtual override { super.setUp(); - fixedWeightRegistry = new ECDSAStakeRegistryEqualWeight(IDelegationManager(address(mockDelegationManager))); + fixedWeightRegistry = new ECDSAStakeRegistryEqualWeight( + IDelegationManager(address(mockDelegationManager)) + ); IStrategy mockStrategy = IStrategy(address(0x1234)); Quorum memory quorum = Quorum({strategies: new StrategyParams[](1)}); - quorum.strategies[0] = StrategyParams({strategy: mockStrategy, multiplier: 10000}); - fixedWeightRegistry.initialize(address(mockServiceManager), 100, quorum); + quorum.strategies[0] = StrategyParams({ + strategy: mockStrategy, + multiplier: 10000 + }); + fixedWeightRegistry.initialize( + address(mockServiceManager), + 100, + quorum + ); fixedWeightRegistry.permitOperator(operator1); fixedWeightRegistry.permitOperator(operator2); ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature; - fixedWeightRegistry.registerOperatorWithSignature(operator1, operatorSignature); - fixedWeightRegistry.registerOperatorWithSignature(operator2, operatorSignature); + vm.prank(operator1); + fixedWeightRegistry.registerOperatorWithSignature( + operatorSignature, + operator1 + ); + vm.prank(operator2); + fixedWeightRegistry.registerOperatorWithSignature( + operatorSignature, + operator2 + ); } function test_FixedStakeUpdates() public { - assertEq(fixedWeightRegistry.getLastCheckpointOperatorWeight(operator1), 1); - assertEq(fixedWeightRegistry.getLastCheckpointOperatorWeight(operator2), 1); + assertEq( + fixedWeightRegistry.getLastCheckpointOperatorWeight(operator1), + 1 + ); + assertEq( + fixedWeightRegistry.getLastCheckpointOperatorWeight(operator2), + 1 + ); assertEq(fixedWeightRegistry.getLastCheckpointTotalWeight(), 2); vm.roll(block.number + 1); vm.prank(operator1); fixedWeightRegistry.deregisterOperator(); - assertEq(fixedWeightRegistry.getLastCheckpointOperatorWeight(operator1), 0); - assertEq(fixedWeightRegistry.getLastCheckpointOperatorWeight(operator2), 1); + assertEq( + fixedWeightRegistry.getLastCheckpointOperatorWeight(operator1), + 0 + ); + assertEq( + fixedWeightRegistry.getLastCheckpointOperatorWeight(operator2), + 1 + ); assertEq(fixedWeightRegistry.getLastCheckpointTotalWeight(), 1); vm.roll(block.number + 1); ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature; - fixedWeightRegistry.registerOperatorWithSignature(operator1, operatorSignature); + vm.prank(operator1); + fixedWeightRegistry.registerOperatorWithSignature( + operatorSignature, + operator1 + ); - assertEq(fixedWeightRegistry.getLastCheckpointOperatorWeight(operator1), 1); - assertEq(fixedWeightRegistry.getLastCheckpointOperatorWeight(operator2), 1); + assertEq( + fixedWeightRegistry.getLastCheckpointOperatorWeight(operator1), + 1 + ); + assertEq( + fixedWeightRegistry.getLastCheckpointOperatorWeight(operator2), + 1 + ); assertEq(fixedWeightRegistry.getLastCheckpointTotalWeight(), 2); vm.roll(block.number + 1); @@ -53,8 +93,14 @@ contract EqualWeightECDSARegistry is ECDSAStakeRegistrySetup { operators[1] = operator2; fixedWeightRegistry.updateOperators(operators); - assertEq(fixedWeightRegistry.getLastCheckpointOperatorWeight(operator1), 1); - assertEq(fixedWeightRegistry.getLastCheckpointOperatorWeight(operator2), 1); + assertEq( + fixedWeightRegistry.getLastCheckpointOperatorWeight(operator1), + 1 + ); + assertEq( + fixedWeightRegistry.getLastCheckpointOperatorWeight(operator2), + 1 + ); assertEq(fixedWeightRegistry.getLastCheckpointTotalWeight(), 2); } } diff --git a/test/unit/ECDSAStakeRegistryPermissionedUnit.t.sol b/test/unit/ECDSAStakeRegistryPermissionedUnit.t.sol index 57031b29..dffb9174 100644 --- a/test/unit/ECDSAStakeRegistryPermissionedUnit.t.sol +++ b/test/unit/ECDSAStakeRegistryPermissionedUnit.t.sol @@ -11,23 +11,41 @@ import {ECDSAStakeRegistryPermissioned} from "../../src/unaudited/examples/ECDSA contract PermissionedECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { ECDSAStakeRegistryPermissioned internal permissionedRegistry; + function setUp() public virtual override { super.setUp(); - permissionedRegistry = new ECDSAStakeRegistryPermissioned(IDelegationManager(address(mockDelegationManager))); + permissionedRegistry = new ECDSAStakeRegistryPermissioned( + IDelegationManager(address(mockDelegationManager)) + ); IStrategy mockStrategy = IStrategy(address(0x1234)); Quorum memory quorum = Quorum({strategies: new StrategyParams[](1)}); - quorum.strategies[0] = StrategyParams({strategy: mockStrategy, multiplier: 10000}); - permissionedRegistry.initialize(address(mockServiceManager), 100, quorum); + quorum.strategies[0] = StrategyParams({ + strategy: mockStrategy, + multiplier: 10000 + }); + permissionedRegistry.initialize( + address(mockServiceManager), + 100, + quorum + ); permissionedRegistry.permitOperator(operator1); permissionedRegistry.permitOperator(operator2); ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature; - permissionedRegistry.registerOperatorWithSignature(operator1, operatorSignature); - permissionedRegistry.registerOperatorWithSignature(operator2, operatorSignature); + vm.prank(operator1); + permissionedRegistry.registerOperatorWithSignature( + operatorSignature, + operator1 + ); + vm.prank(operator2); + permissionedRegistry.registerOperatorWithSignature( + operatorSignature, + operator1 + ); } function test_RevertsWhen_NotOwner_PermitOperator() public { - address notOwner=address(0xBEEF); + address notOwner = address(0xBEEF); vm.prank(notOwner); vm.expectRevert("Ownable: caller is not the owner"); permissionedRegistry.permitOperator(operator1); @@ -39,14 +57,14 @@ contract PermissionedECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { } function test_RevertsWhen_NotOwner_RevokeOperator() public { - address notOwner=address(0xBEEF); + address notOwner = address(0xBEEF); vm.prank(notOwner); vm.expectRevert("Ownable: caller is not the owner"); permissionedRegistry.revokeOperator(operator1); } function test_When_NotOperator_RevokeOperator() public { - address notOperator=address(0xBEEF); + address notOperator = address(0xBEEF); permissionedRegistry.permitOperator(notOperator); permissionedRegistry.revokeOperator(notOperator); @@ -57,14 +75,14 @@ contract PermissionedECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { } function test_RevertsWhen_NotOwner_EjectOperator() public { - address notOwner=address(0xBEEF); + address notOwner = address(0xBEEF); vm.prank(notOwner); vm.expectRevert("Ownable: caller is not the owner"); permissionedRegistry.ejectOperator(operator1); } function test_RevertsWhen_NotOperator_EjectOperator() public { - address notOperator=address(0xBEEF); + address notOperator = address(0xBEEF); vm.expectRevert(abi.encodeWithSelector(OperatorNotRegistered.selector)); permissionedRegistry.ejectOperator(notOperator); } @@ -77,26 +95,40 @@ contract PermissionedECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { address operator3 = address(0xBEEF); ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature; - vm.expectRevert(abi.encodeWithSelector(ECDSAStakeRegistryPermissioned.OperatorNotAllowlisted.selector)); - permissionedRegistry.registerOperatorWithSignature(operator3, operatorSignature); - + vm.expectRevert( + abi.encodeWithSelector( + ECDSAStakeRegistryPermissioned.OperatorNotAllowlisted.selector + ) + ); + vm.prank(operator3); + permissionedRegistry.registerOperatorWithSignature( + operatorSignature, + operator3 + ); } function test_WhenAllowlisted_RegisterOperatorWithSig() public { address operator3 = address(0xBEEF); permissionedRegistry.permitOperator(operator3); ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature; - permissionedRegistry.registerOperatorWithSignature(operator3, operatorSignature); + vm.prank(operator3); + permissionedRegistry.registerOperatorWithSignature( + operatorSignature, + operator3 + ); } function test_DeregisterOperator() public { address operator3 = address(0xBEEF); permissionedRegistry.permitOperator(operator3); ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature; - permissionedRegistry.registerOperatorWithSignature(operator3, operatorSignature); + vm.prank(operator3); + permissionedRegistry.registerOperatorWithSignature( + operatorSignature, + operator3 + ); vm.prank(operator3); permissionedRegistry.deregisterOperator(); } - } diff --git a/test/unit/ECDSAStakeRegistryUnit.t.sol b/test/unit/ECDSAStakeRegistryUnit.t.sol index 7ffbb7d8..d374144d 100644 --- a/test/unit/ECDSAStakeRegistryUnit.t.sol +++ b/test/unit/ECDSAStakeRegistryUnit.t.sol @@ -10,7 +10,6 @@ import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy import {ECDSAStakeRegistry} from "../../src/unaudited/ECDSAStakeRegistry.sol"; import {ECDSAStakeRegistryEventsAndErrors, Quorum, StrategyParams} from "../../src/interfaces/IECDSAStakeRegistryEventsAndErrors.sol"; - contract MockServiceManager { // solhint-disable-next-line function deregisterOperatorFromAVS(address) external {} @@ -26,9 +25,12 @@ contract MockDelegationManager { return 1000; // Return a dummy value for simplicity } - function getOperatorShares(address, address[] memory strategies) external pure returns (uint256[] memory) { - uint256[] memory response = new uint256[](strategies.length); - for (uint256 i; i < strategies.length; i++){ + function getOperatorShares( + address, + address[] 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 @@ -38,6 +40,7 @@ contract MockDelegationManager { contract ECDSAStakeRegistrySetup is Test, ECDSAStakeRegistryEventsAndErrors { MockDelegationManager public mockDelegationManager; MockServiceManager public mockServiceManager; + ECDSAStakeRegistry public registry; address internal operator1; address internal operator2; uint256 internal operator1Pk; @@ -53,33 +56,36 @@ contract ECDSAStakeRegistrySetup is Test, ECDSAStakeRegistryEventsAndErrors { (operator2, operator2Pk) = makeAddrAndKey("Signer 2"); mockDelegationManager = new MockDelegationManager(); mockServiceManager = new MockServiceManager(); - - } -} - -contract ECDSAStakeRegistryTest is ECDSAStakeRegistrySetup{ - ECDSAStakeRegistry public registry; - - function setUp() public virtual override { - super.setUp(); IStrategy mockStrategy = IStrategy(address(0x1234)); Quorum memory quorum = Quorum({strategies: new StrategyParams[](1)}); - quorum.strategies[0] = StrategyParams({strategy: mockStrategy, multiplier: 10000}); - registry = new ECDSAStakeRegistry(IDelegationManager(address(mockDelegationManager))); + quorum.strategies[0] = StrategyParams({ + strategy: mockStrategy, + multiplier: 10_000 + }); + registry = new ECDSAStakeRegistry( + IDelegationManager(address(mockDelegationManager)) + ); registry.initialize(address(mockServiceManager), 100, quorum); ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature; - registry.registerOperatorWithSignature(operator1, operatorSignature); - registry.registerOperatorWithSignature(operator2, operatorSignature); - + vm.prank(operator1); + registry.registerOperatorWithSignature(operatorSignature, operator1); + vm.prank(operator2); + registry.registerOperatorWithSignature(operatorSignature, operator2); + vm.roll(block.number + 1); } +} -function test_UpdateQuorumConfig() public { +contract ECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { + function test_UpdateQuorumConfig() public { IStrategy mockStrategy = IStrategy(address(420)); Quorum memory oldQuorum = registry.quorum(); Quorum memory newQuorum = Quorum({strategies: new StrategyParams[](1)}); - newQuorum.strategies[0] = StrategyParams({strategy: mockStrategy, multiplier: 10000}); - address[] memory operators = new address[](2); + newQuorum.strategies[0] = StrategyParams({ + strategy: mockStrategy, + multiplier: 10_000 + }); + address[] memory operators = new address[](2); operators[0] = operator1; operators[1] = operator2; @@ -90,25 +96,34 @@ function test_UpdateQuorumConfig() public { } function test_RevertsWhen_InvalidQuorum_UpdateQuourmConfig() public { - Quorum memory invalidQuorum = Quorum({strategies: new StrategyParams[](1)}); + Quorum memory invalidQuorum = Quorum({ + strategies: new StrategyParams[](1) + }); invalidQuorum.strategies[0] = StrategyParams({ /// TODO: Make mock strategy strategy: IStrategy(address(420)), multiplier: 5000 // This should cause the update to revert as it's not the total required }); - address[] memory operators = new address[](2); + address[] memory operators = new address[](2); operators[0] = operator1; operators[1] = operator2; - vm.expectRevert(ECDSAStakeRegistryEventsAndErrors.InvalidQuorum.selector); + vm.expectRevert( + ECDSAStakeRegistryEventsAndErrors.InvalidQuorum.selector + ); registry.updateQuorumConfig(invalidQuorum, operators); } function test_RevertsWhen_NotOwner_UpdateQuorumConfig() public { - Quorum memory validQuorum = Quorum({strategies: new StrategyParams[](1)}); - validQuorum.strategies[0] = StrategyParams({strategy: IStrategy(address(420)), multiplier: 10000}); + Quorum memory validQuorum = Quorum({ + strategies: new StrategyParams[](1) + }); + validQuorum.strategies[0] = StrategyParams({ + strategy: IStrategy(address(420)), + multiplier: 10_000 + }); - address[] memory operators = new address[](2); + address[] memory operators = new address[](2); operators[0] = operator1; operators[1] = operator2; @@ -121,7 +136,7 @@ function test_UpdateQuorumConfig() public { function test_RevertsWhen_SameQuorum_UpdateQuorumConfig() public { Quorum memory quorum = registry.quorum(); - address[] memory operators = new address[](2); + address[] memory operators = new address[](2); operators[0] = operator1; operators[1] = operator2; @@ -130,58 +145,89 @@ function test_UpdateQuorumConfig() public { } function test_RevertSWhen_Duplicate_UpdateQuorumConfig() public { - Quorum memory validQuorum = Quorum({strategies: new StrategyParams[](2)}); - validQuorum.strategies[0] = StrategyParams({strategy: IStrategy(address(420)), multiplier: 5_000}); - address[] memory operators = new address[](2); + Quorum memory invalidQuorum = Quorum({ + strategies: new StrategyParams[](2) + }); + invalidQuorum.strategies[0] = StrategyParams({ + strategy: IStrategy(address(420)), + multiplier: 5000 + }); + address[] memory operators = new address[](2); operators[0] = operator1; operators[1] = operator2; - validQuorum.strategies[1] = StrategyParams({strategy: IStrategy(address(420)), multiplier: 5_000}); + invalidQuorum.strategies[1] = StrategyParams({ + strategy: IStrategy(address(420)), + multiplier: 5000 + }); vm.expectRevert(ECDSAStakeRegistryEventsAndErrors.NotSorted.selector); - registry.updateQuorumConfig(validQuorum, operators); + registry.updateQuorumConfig(invalidQuorum, operators); } function test_RevertSWhen_NotSorted_UpdateQuorumConfig() public { - Quorum memory validQuorum = Quorum({strategies: new StrategyParams[](2)}); - validQuorum.strategies[0] = StrategyParams({strategy: IStrategy(address(420)), multiplier: 5_000}); - address[] memory operators = new address[](2); + Quorum memory invalidQuorum = Quorum({ + strategies: new StrategyParams[](2) + }); + invalidQuorum.strategies[0] = StrategyParams({ + strategy: IStrategy(address(420)), + multiplier: 5000 + }); + address[] memory operators = new address[](2); operators[0] = operator1; operators[1] = operator2; - validQuorum.strategies[1] = StrategyParams({strategy: IStrategy(address(419)), multiplier: 5_000}); + invalidQuorum.strategies[1] = StrategyParams({ + strategy: IStrategy(address(419)), + multiplier: 5000 + }); vm.expectRevert(ECDSAStakeRegistryEventsAndErrors.NotSorted.selector); - registry.updateQuorumConfig(validQuorum, operators); + registry.updateQuorumConfig(invalidQuorum, operators); } function test_RevertSWhen_OverMultiplierTotal_UpdateQuorumConfig() public { - Quorum memory validQuorum = Quorum({strategies: new StrategyParams[](1)}); - validQuorum.strategies[0] = StrategyParams({strategy: IStrategy(address(420)), multiplier: 10001}); - address[] memory operators = new address[](2); + Quorum memory invalidQuorum = Quorum({ + strategies: new StrategyParams[](1) + }); + invalidQuorum.strategies[0] = StrategyParams({ + strategy: IStrategy(address(420)), + multiplier: 10_001 + }); + address[] memory operators = new address[](2); operators[0] = operator1; operators[1] = operator2; - vm.expectRevert(ECDSAStakeRegistryEventsAndErrors.InvalidQuorum.selector); - registry.updateQuorumConfig(validQuorum,operators); + vm.expectRevert( + ECDSAStakeRegistryEventsAndErrors.InvalidQuorum.selector + ); + registry.updateQuorumConfig(invalidQuorum, operators); } function test_RegisterOperatorWithSignature() public { address operator3 = address(0x125); ISignatureUtils.SignatureWithSaltAndExpiry memory signature; - registry.registerOperatorWithSignature(operator3, signature); + vm.prank(operator3); + registry.registerOperatorWithSignature(signature, operator3); assertTrue(registry.operatorRegistered(operator3)); assertEq(registry.getLastCheckpointOperatorWeight(operator3), 1000); } - function test_RevertsWhen_AlreadyRegistered_RegisterOperatorWithSignature() public { + function test_RevertsWhen_AlreadyRegistered_RegisterOperatorWithSignature() + public + { assertEq(registry.getLastCheckpointOperatorWeight(operator1), 1000); assertEq(registry.getLastCheckpointTotalWeight(), 2000); ISignatureUtils.SignatureWithSaltAndExpiry memory signature; - vm.expectRevert(ECDSAStakeRegistryEventsAndErrors.OperatorAlreadyRegistered.selector); - registry.registerOperatorWithSignature(operator1, signature); + vm.expectRevert( + ECDSAStakeRegistryEventsAndErrors.OperatorAlreadyRegistered.selector + ); + vm.prank(operator1); + registry.registerOperatorWithSignature(signature, operator1); } - function test_RevertsWhen_SignatureIsInvalid_RegisterOperatorWithSignature() public { + function test_RevertsWhen_SignatureIsInvalid_RegisterOperatorWithSignature() + public + { bytes memory signatureData; vm.mockCall( address(mockServiceManager), @@ -212,7 +258,9 @@ function test_UpdateQuorumConfig() public { function test_RevertsWhen_NotOperator_DeregisterOperator() public { address notOperator = address(0x2); vm.prank(notOperator); - vm.expectRevert(ECDSAStakeRegistryEventsAndErrors.OperatorNotRegistered.selector); + vm.expectRevert( + ECDSAStakeRegistryEventsAndErrors.OperatorNotRegistered.selector + ); registry.deregisterOperator(); } @@ -229,7 +277,6 @@ function test_UpdateQuorumConfig() public { operators[2] = operator3; registry.updateOperators(operators); assertEq(registry.getLastCheckpointOperatorWeight(operator3), 0); - } function test_When_SingleOperator_UpdateOperators() public { @@ -237,7 +284,9 @@ function test_UpdateQuorumConfig() public { operators[0] = operator1; registry.updateOperators(operators); - uint256 updatedWeight = registry.getLastCheckpointOperatorWeight(operator1); + uint256 updatedWeight = registry.getLastCheckpointOperatorWeight( + operator1 + ); assertEq(updatedWeight, 1000); } @@ -269,8 +318,12 @@ function test_UpdateQuorumConfig() public { registry.updateOperators(operators); - uint256 updatedWeight1 = registry.getLastCheckpointOperatorWeight(operator1); - uint256 updatedWeight2 = registry.getLastCheckpointOperatorWeight(operator2); + uint256 updatedWeight1 = registry.getLastCheckpointOperatorWeight( + operator1 + ); + uint256 updatedWeight2 = registry.getLastCheckpointOperatorWeight( + operator2 + ); assertEq(updatedWeight1, 1000); assertEq(updatedWeight2, 1000); } @@ -282,7 +335,9 @@ function test_UpdateQuorumConfig() public { registry.updateOperators(operators); - uint256 updatedWeight = registry.getLastCheckpointOperatorWeight(operator1); + uint256 updatedWeight = registry.getLastCheckpointOperatorWeight( + operator1 + ); assertEq(updatedWeight, 1000); } @@ -291,8 +346,14 @@ function test_UpdateQuorumConfig() public { IStrategy mockStrategy2 = IStrategy(address(421)); Quorum memory quorum = Quorum({strategies: new StrategyParams[](2)}); - quorum.strategies[0] = StrategyParams({strategy: mockStrategy, multiplier: 5_000}); - quorum.strategies[1] = StrategyParams({strategy: mockStrategy2, multiplier: 5_000}); + quorum.strategies[0] = StrategyParams({ + strategy: mockStrategy, + multiplier: 5000 + }); + quorum.strategies[1] = StrategyParams({ + strategy: mockStrategy2, + multiplier: 5000 + }); address[] memory operators = new address[](2); operators[0] = operator1; @@ -302,20 +363,28 @@ function test_UpdateQuorumConfig() public { address[] memory strategies = new address[](2); uint256[] memory shares = new uint256[](2); - strategies[0]=address(mockStrategy); - strategies[1]=address(mockStrategy2); + strategies[0] = address(mockStrategy); + strategies[1] = address(mockStrategy2); shares[0] = 50; shares[1] = 1000; vm.mockCall( address(mockDelegationManager), - abi.encodeWithSelector(MockDelegationManager.getOperatorShares.selector, operator1, strategies), + abi.encodeWithSelector( + MockDelegationManager.getOperatorShares.selector, + operator1, + strategies + ), abi.encode(shares) ); registry.updateOperators(operators); - uint256 updatedWeight1 = registry.getLastCheckpointOperatorWeight(operator1); - uint256 updatedWeight2 = registry.getLastCheckpointOperatorWeight(operator2); + uint256 updatedWeight1 = registry.getLastCheckpointOperatorWeight( + operator1 + ); + uint256 updatedWeight2 = registry.getLastCheckpointOperatorWeight( + operator2 + ); assertEq(updatedWeight1, 525); assertEq(updatedWeight2, 1000); vm.roll(block.number + 1); @@ -327,7 +396,7 @@ function test_UpdateQuorumConfig() public { assertEq(initialMinimumWeight, 0); // Assuming initial state is 0 - address[] memory operators = new address[](2); + address[] memory operators = new address[](2); operators[0] = operator1; operators[1] = operator2; registry.updateMinimumWeight(newMinimumWeight, operators); @@ -338,7 +407,7 @@ function test_UpdateQuorumConfig() public { function test_RevertsWhen_NotOwner_UpdateMinimumWeight() public { uint256 newMinimumWeight = 5000; - address[] memory operators = new address[](2); + address[] memory operators = new address[](2); operators[0] = operator1; operators[1] = operator2; vm.prank(address(0xBEEF)); // An arbitrary non-owner address @@ -348,7 +417,7 @@ function test_UpdateQuorumConfig() public { function test_When_SameWeight_UpdateMinimumWeight() public { uint256 initialMinimumWeight = 5000; - address[] memory operators = new address[](2); + address[] memory operators = new address[](2); operators[0] = operator1; operators[1] = operator2; registry.updateMinimumWeight(initialMinimumWeight, operators); @@ -359,7 +428,7 @@ function test_UpdateQuorumConfig() public { function test_When_Weight0_UpdateMinimumWeight() public { uint256 initialMinimumWeight = 5000; - address[] memory operators = new address[](2); + address[] memory operators = new address[](2); operators[0] = operator1; operators[1] = operator2; registry.updateMinimumWeight(initialMinimumWeight, operators); @@ -371,19 +440,21 @@ function test_UpdateQuorumConfig() public { uint256 updatedMinimumWeight = registry.minimumWeight(); assertEq(updatedMinimumWeight, newMinimumWeight); } + function testUpdateThresholdStake_UpdateThresholdStake() public { - uint256 thresholdWeight = 10000000000; + uint256 thresholdWeight = 10_000_000_000; vm.prank(registry.owner()); registry.updateStakeThreshold(thresholdWeight); } function test_RevertsWhen_NotOwner_UpdateThresholdStake() public { - uint256 thresholdWeight = 10000000000; + uint256 thresholdWeight = 10_000_000_000; address notOwner = address(0x123); vm.prank(notOwner); vm.expectRevert("Ownable: caller is not the owner"); registry.updateStakeThreshold(thresholdWeight); } + function test_CheckSignatures() public { msgHash = keccak256("data"); signers = new address[](2); @@ -394,7 +465,10 @@ function test_UpdateQuorumConfig() public { (v, r, s) = vm.sign(operator2Pk, msgHash); signatures[1] = abi.encodePacked(r, s, v); - registry.isValidSignature(msgHash, abi.encode(signers, signatures, type(uint32).max)); + registry.isValidSignature( + msgHash, + abi.encode(signers, signatures, block.number - 1) + ); } function test_RevertsWhen_LengthMismatch_CheckSignatures() public { @@ -405,8 +479,13 @@ function test_UpdateQuorumConfig() public { (uint8 v, bytes32 r, bytes32 s) = vm.sign(operator1Pk, msgHash); signatures[0] = abi.encode(v, r, s); - vm.expectRevert(ECDSAStakeRegistryEventsAndErrors.LengthMismatch.selector); - registry.isValidSignature(msgHash, abi.encode(signers, signatures, type(uint32).max)); + vm.expectRevert( + ECDSAStakeRegistryEventsAndErrors.LengthMismatch.selector + ); + registry.isValidSignature( + msgHash, + abi.encode(signers, signatures, block.number - 1) + ); } function test_RevertsWhen_InvalidLength_CheckSignatures() public { @@ -414,8 +493,13 @@ function test_UpdateQuorumConfig() public { address[] memory signers = new address[](0); bytes[] memory signatures = new bytes[](0); - vm.expectRevert(ECDSAStakeRegistryEventsAndErrors.InvalidLength.selector); - registry.isValidSignature(dataHash, abi.encode(signers, signatures, type(uint32).max)); + vm.expectRevert( + ECDSAStakeRegistryEventsAndErrors.InvalidLength.selector + ); + registry.isValidSignature( + dataHash, + abi.encode(signers, signatures, block.number - 1) + ); } function test_RevertsWhen_NotSorted_CheckSignatures() public { @@ -430,14 +514,17 @@ function test_UpdateQuorumConfig() public { signatures[0] = abi.encodePacked(r, s, v); vm.expectRevert(ECDSAStakeRegistryEventsAndErrors.NotSorted.selector); - registry.isValidSignature(msgHash, abi.encode(signers, signatures, type(uint32).max)); + registry.isValidSignature( + msgHash, + abi.encode(signers, signatures, block.number - 1) + ); } function test_RevertsWhen_Duplicates_CheckSignatures() public { msgHash = keccak256("data"); signers = new address[](2); - signers[1]=operator1; - signers[0]=operator1; + signers[1] = operator1; + signers[0] = operator1; /// Duplicate assertEq(signers[0], signers[1]); @@ -448,7 +535,10 @@ function test_UpdateQuorumConfig() public { signatures[1] = abi.encodePacked(r, s, v); vm.expectRevert(ECDSAStakeRegistryEventsAndErrors.NotSorted.selector); - registry.isValidSignature(msgHash, abi.encode(signers, signatures, type(uint32).max)); + registry.isValidSignature( + msgHash, + abi.encode(signers, signatures, block.number - 1) + ); } function test_RevetsWhen_InvalidSignature_CheckSignatures() public { @@ -458,8 +548,13 @@ function test_UpdateQuorumConfig() public { bytes[] memory signatures = new bytes[](1); signatures[0] = "invalid-signature"; - vm.expectRevert(ECDSAStakeRegistryEventsAndErrors.InvalidSignature.selector); - registry.isValidSignature(dataHash, abi.encode(signers, signatures, type(uint32).max)); + vm.expectRevert( + ECDSAStakeRegistryEventsAndErrors.InvalidSignature.selector + ); + registry.isValidSignature( + dataHash, + abi.encode(signers, signatures, block.number - 1) + ); } function test_RevertsWhen_InsufficientSignedStake_CheckSignatures() public { @@ -473,19 +568,27 @@ function test_UpdateQuorumConfig() public { (v, r, s) = vm.sign(operator2Pk, msgHash); signatures[1] = abi.encodePacked(r, s, v); - uint256 thresholdWeight = 10000000000; + uint256 thresholdWeight = 10_000_000_000; vm.prank(registry.owner()); registry.updateStakeThreshold(thresholdWeight); vm.roll(block.number + 1); vm.mockCall( address(registry), - abi.encodeWithSelector(ECDSAStakeRegistry.getLastCheckpointOperatorWeight.selector, operator1), + abi.encodeWithSelector( + ECDSAStakeRegistry.getLastCheckpointOperatorWeight.selector, + operator1 + ), abi.encode(50) ); - vm.expectRevert(ECDSAStakeRegistryEventsAndErrors.InsufficientSignedStake.selector); - registry.isValidSignature(msgHash, abi.encode(signers, signatures, type(uint32).max)); + vm.expectRevert( + ECDSAStakeRegistryEventsAndErrors.InsufficientSignedStake.selector + ); + registry.isValidSignature( + msgHash, + abi.encode(signers, signatures, block.number - 1) + ); } function test_RevertsWhen_LengthMismatch_CheckSignaturesAtBlock() public { @@ -496,8 +599,13 @@ function test_UpdateQuorumConfig() public { signers[1] = operator2; bytes[] memory signatures = new bytes[](1); - vm.expectRevert(ECDSAStakeRegistryEventsAndErrors.LengthMismatch.selector); - registry.isValidSignature(dataHash, abi.encode(signers, signatures, referenceBlock)); + vm.expectRevert( + ECDSAStakeRegistryEventsAndErrors.LengthMismatch.selector + ); + registry.isValidSignature( + dataHash, + abi.encode(signers, signatures, referenceBlock) + ); } function test_RevertsWhen_InvalidLength_CheckSignaturesAtBlock() public { @@ -506,8 +614,13 @@ function test_UpdateQuorumConfig() public { address[] memory signers = new address[](0); bytes[] memory signatures = new bytes[](0); - vm.expectRevert(ECDSAStakeRegistryEventsAndErrors.InvalidLength.selector); - registry.isValidSignature(dataHash, abi.encode(signers, signatures, referenceBlock)); + vm.expectRevert( + ECDSAStakeRegistryEventsAndErrors.InvalidLength.selector + ); + registry.isValidSignature( + dataHash, + abi.encode(signers, signatures, referenceBlock) + ); } function test_RevertsWhen_NotSorted_CheckSignaturesAtBlock() public { @@ -524,10 +637,15 @@ function test_UpdateQuorumConfig() public { signatures[0] = abi.encodePacked(r, s, v); vm.expectRevert(ECDSAStakeRegistryEventsAndErrors.NotSorted.selector); - registry.isValidSignature(msgHash, abi.encode(signers, signatures, referenceBlock)); + registry.isValidSignature( + msgHash, + abi.encode(signers, signatures, referenceBlock) + ); } - function test_RevetsWhen_InsufficientSignedStake_CheckSignaturesAtBlock() public { + function test_RevetsWhen_InsufficientSignedStake_CheckSignaturesAtBlock() + public + { uint32 referenceBlock = 123; msgHash = keccak256("data"); signers = new address[](2); @@ -539,19 +657,28 @@ function test_UpdateQuorumConfig() public { (v, r, s) = vm.sign(operator2Pk, msgHash); signatures[1] = abi.encodePacked(r, s, v); - uint256 thresholdWeight = 10000000000; + uint256 thresholdWeight = 10_000_000_000; vm.prank(registry.owner()); registry.updateStakeThreshold(thresholdWeight); vm.roll(referenceBlock + 1); vm.mockCall( address(registry), - abi.encodeWithSelector(ECDSAStakeRegistry.getOperatorWeightAtBlock.selector, operator1, referenceBlock), + abi.encodeWithSelector( + ECDSAStakeRegistry.getOperatorWeightAtBlock.selector, + operator1, + referenceBlock + ), abi.encode(50) ); - vm.expectRevert(ECDSAStakeRegistryEventsAndErrors.InsufficientSignedStake.selector); - registry.isValidSignature(msgHash, abi.encode(signers, signatures, referenceBlock)); + vm.expectRevert( + ECDSAStakeRegistryEventsAndErrors.InsufficientSignedStake.selector + ); + registry.isValidSignature( + msgHash, + abi.encode(signers, signatures, referenceBlock) + ); } function test_Gas_UpdateOperators() public { @@ -564,14 +691,18 @@ function test_UpdateQuorumConfig() public { ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature; address[] memory operators = new address[](30); - for (uint256 i; i< operators.length;i++) { - operators[i]=address(uint160(i)); - registry.registerOperatorWithSignature(operators[i], operatorSignature); + for (uint256 i; i < operators.length; i++) { + operators[i] = address(uint160(i)); + vm.prank(operators[i]); + registry.registerOperatorWithSignature( + operatorSignature, + operators[i] + ); } vm.resumeGasMetering(); registry.updateOperators(operators); - emit log_named_uint("Gas consumed",before - gasleft()); + emit log_named_uint("Gas consumed", before - gasleft()); } function test_Gas_CheckSignatures() public { @@ -589,23 +720,259 @@ function test_UpdateQuorumConfig() public { uint8 v; bytes32 r; bytes32 s; - for (uint256 i=1; i< operators.length+1;i++) { - operators[i-1]=address(vm.addr(i)); - registry.registerOperatorWithSignature(operators[i-1], operatorSignature); + for (uint256 i = 1; i < operators.length + 1; i++) { + operators[i - 1] = address(vm.addr(i)); + vm.prank(operators[i - 1]); + registry.registerOperatorWithSignature( + operatorSignature, + operators[i - 1] + ); (v, r, s) = vm.sign(i, msgHash); - signatures[i-1] = abi.encodePacked(r, s, v); + signatures[i - 1] = abi.encodePacked(r, s, v); } (operators, signatures) = _sort(operators, signatures); registry.updateOperators(operators); + vm.roll(block.number + 1); vm.resumeGasMetering(); - registry.isValidSignature(msgHash, abi.encode(operators, signatures, type(uint32).max)); - emit log_named_uint("Gas consumed",before - gasleft()); + registry.isValidSignature( + msgHash, + abi.encode(operators, signatures, block.number - 1) + ); + + emit log_named_uint("Gas consumed", before - gasleft()); + } + + // Define private and public keys for operator3 and signer + uint256 private operator3Pk = 3; + address private operator3 = address(vm.addr(operator3Pk)); + uint256 private signerPk = 4; + address private signer = address(vm.addr(signerPk)); + + function test_WhenUsingSigningKey_RegierOperatorWithSignature() public { + address operator = operator3; + + ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature; + + // Register operator with a different signing key + vm.prank(operator); + registry.registerOperatorWithSignature(operatorSignature, signer); + + // Verify that the signing key has been successfully registered for the operator + address registeredSigningKey = registry.getLastestOperatorSigningKey( + operator + ); + assertEq( + registeredSigningKey, + signer, + "The registered signing key does not match the provided signing key" + ); + } + + function test_Twice_RegierOperatorWithSignature() public { + address operator = operator3; + + ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature; + + // Register operator with a different signing key + vm.prank(operator); + registry.registerOperatorWithSignature(operatorSignature, signer); + + /// Register a second time + vm.prank(operator); + registry.updateOperatorSigningKey(address(420)); + + // Verify that the signing key has been successfully registered for the operator + address registeredSigningKey = registry.getLastestOperatorSigningKey( + operator + ); + + vm.roll(block.number + 1); + registeredSigningKey = registry.getOperatorSigningKeyAtBlock( + operator, + uint32(block.number - 1) + ); + assertEq( + registeredSigningKey, + address(420), + "The registered signing key does not match the provided signing key" + ); + } + + function test_WhenUsingSigningKey_CheckSignatures() public { + address operator = operator3; + + ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature; + + // Register operator with a different signing key + vm.prank(operator); + registry.registerOperatorWithSignature(operatorSignature, signer); + vm.roll(block.number + 1); + + // Prepare data for signature + bytes32 dataHash = keccak256("data"); + address[] memory operators = new address[](1); + operators[0] = operator; + bytes[] memory signatures = new bytes[](1); + + // Generate signature using the signing key + (uint8 v, bytes32 r, bytes32 s) = vm.sign(signerPk, dataHash); + signatures[0] = abi.encodePacked(r, s, v); + + // Check signatures using the registered signing key + registry.isValidSignature( + dataHash, + abi.encode(operators, signatures, block.number - 1) + ); } - function _sort(address[] memory operators, bytes[] memory signatures) internal pure returns (address[] memory, bytes[] memory) { - require(operators.length == signatures.length, "Operators and signatures length mismatch"); - + function test_WhenUsingSigningKey_CheckSignaturesAtBlock() public { + address operator = operator3; + address initialSigningKey = address(vm.addr(signerPk)); + address updatedSigningKey = address(vm.addr(signerPk + 1)); + + ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature; + + // Register operator with the initial signing key + vm.prank(operator); + registry.registerOperatorWithSignature( + operatorSignature, + initialSigningKey + ); + vm.roll(block.number + 1); + + // Prepare data for signature with initial signing key + bytes32 dataHash = keccak256("data"); + address[] memory operators = new address[](1); + operators[0] = operator; + bytes[] memory signatures = new bytes[](1); + + // Generate signature using the initial signing key + (uint8 v, bytes32 r, bytes32 s) = vm.sign(signerPk, dataHash); + signatures[0] = abi.encodePacked(r, s, v); + + // Check signatures using the initial registered signing key + registry.isValidSignature( + dataHash, + abi.encode(operators, signatures, block.number - 1) + ); + + // Increase block number + vm.roll(block.number + 10); + + // Update operator's signing key + vm.prank(operator); + registry.updateOperatorSigningKey(updatedSigningKey); + vm.roll(block.number + 1); + + // Generate signature using the updated signing key + (v, r, s) = vm.sign(signerPk + 1, dataHash); + signatures[0] = abi.encodePacked(r, s, v); + + // Check signatures using the updated registered signing key + registry.isValidSignature( + dataHash, + abi.encode(operators, signatures, block.number - 1) + ); + } + + function test_WhenUsingPriorSigningKey_CheckSignaturesAtBlock() public { + address operator = operator3; + address initialSigningKey = address(vm.addr(signerPk)); + address updatedSigningKey = address(vm.addr(signerPk + 1)); + + ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature; + + // Register operator with the initial signing key + vm.prank(operator); + registry.registerOperatorWithSignature( + operatorSignature, + initialSigningKey + ); + vm.roll(block.number + 1); + + // Prepare data for signature with initial signing key + bytes32 dataHash = keccak256("data"); + address[] memory operators = new address[](1); + operators[0] = operator; + bytes[] memory signatures = new bytes[](1); + + // Generate signature using the initial signing key + (uint8 v, bytes32 r, bytes32 s) = vm.sign(signerPk, dataHash); + signatures[0] = abi.encodePacked(r, s, v); + + // Increase block number + vm.roll(block.number + 10); + + // Update operator's signing key + vm.prank(operator); + registry.updateOperatorSigningKey(updatedSigningKey); + + // Check signatures using the initial registered signing key at the previous block + registry.isValidSignature( + dataHash, + abi.encode(operators, signatures, block.number - 10) + ); + } + + function test_RevertsWhen_SigningCurrentBlock_IsValidSignature() public { + address operator = operator1; + address signingKey = address(vm.addr(signerPk)); + bytes32 dataHash = keccak256(abi.encodePacked("test data")); + uint256 currentBlock = block.number; + + (uint8 v, bytes32 r, bytes32 s) = vm.sign(signerPk, dataHash); + bytes memory signature = abi.encodePacked(r, s, v); + address[] memory operators = new address[](1); + operators[0] = operator; + bytes[] memory signatures = new bytes[](1); + signatures[0] = signature; + + vm.expectRevert(abi.encodeWithSignature("InvalidReferenceBlock()")); + registry.isValidSignature( + dataHash, + abi.encode(operators, signatures, currentBlock) + ); + } + + function test_RevertsWhen_SigningKeyNotValidAtBlock_IsValidSignature() + public + { + address operator = operator1; + uint256 invalidSignerPk = signerPk + 1; + address updatedSigningKey = address(vm.addr(invalidSignerPk)); + /// Different key to simulate invalid signing key + bytes32 dataHash = keccak256(abi.encodePacked("test data")); + uint256 referenceBlock = block.number; + /// Past reference block where the signer update won't be valid + vm.roll(block.number + 1); + + vm.prank(operator); + registry.updateOperatorSigningKey(address(updatedSigningKey)); + + (uint8 v, bytes32 r, bytes32 s) = vm.sign(invalidSignerPk, dataHash); + bytes memory signature = abi.encodePacked(r, s, v); + address[] memory operators = new address[](1); + operators[0] = operator; + bytes[] memory signatures = new bytes[](1); + signatures[0] = signature; + + vm.expectRevert(abi.encodeWithSignature("InvalidSignature()")); + registry.isValidSignature( + dataHash, + abi.encode(operators, signatures, referenceBlock) + ); + } + + function _sort( + address[] memory operators, + bytes[] memory signatures + ) internal pure returns (address[] memory, bytes[] memory) { + require( + operators.length == signatures.length, + "Operators and signatures length mismatch" + ); + uint256 length = operators.length; for (uint256 i = 0; i < length - 1; i++) { uint256 minIndex = i; diff --git a/test/unit/EjectionManagerUnit.t.sol b/test/unit/EjectionManagerUnit.t.sol new file mode 100644 index 00000000..6ec539fc --- /dev/null +++ b/test/unit/EjectionManagerUnit.t.sol @@ -0,0 +1,448 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity ^0.8.12; + +import {EjectionManager} from "../../src/EjectionManager.sol"; +import {IEjectionManager} from "../../src/interfaces/IEjectionManager.sol"; + +import "../utils/MockAVSDeployer.sol"; + +contract EjectionManagerUnitTests is MockAVSDeployer { + + event EjectorUpdated(address ejector, bool status); + event QuorumEjectionParamsSet(uint8 quorumNumber, uint32 rateLimitWindow, uint16 ejectableStakePercent); + event OperatorEjected(bytes32 operatorId, uint8 quorumNumber); + event FailedOperatorEjection(bytes32 operatorId, uint8 quorumNumber, bytes err); + + EjectionManager public ejectionManager; + IEjectionManager public ejectionManagerImplementation; + + IEjectionManager.QuorumEjectionParams[] public quorumEjectionParams; + + uint32 public ratelimitWindow = 1 days; + uint16 public ejectableStakePercent = 1000; + + function setUp() virtual public { + for(uint8 i = 0; i < numQuorums; i++) { + quorumEjectionParams.push(IEjectionManager.QuorumEjectionParams({ + rateLimitWindow: ratelimitWindow, + ejectableStakePercent: ejectableStakePercent + })); + } + + defaultMaxOperatorCount = 200; + _deployMockEigenLayerAndAVS(); + + ejectionManager = EjectionManager(address( + new TransparentUpgradeableProxy( + address(emptyContract), + address(proxyAdmin), + "" + ) + )); + + ejectionManagerImplementation = new EjectionManager(registryCoordinator, stakeRegistry); + + address[] memory ejectors = new address[](1); + ejectors[0] = ejector; + + cheats.prank(proxyAdminOwner); + proxyAdmin.upgradeAndCall( + TransparentUpgradeableProxy(payable(address(ejectionManager))), + address(ejectionManagerImplementation), + abi.encodeWithSelector( + EjectionManager.initialize.selector, + registryCoordinatorOwner, + ejectors, + quorumEjectionParams + ) + ); + + cheats.prank(registryCoordinatorOwner); + registryCoordinator.setEjector(address(ejectionManager)); + + cheats.warp(block.timestamp + ratelimitWindow); + } + + function testEjectOperators_OneOperatorInsideRatelimit() public { + uint8 operatorsToEject = 1; + uint8 numOperators = 10; + uint96 stake = 1 ether; + _registerOperaters(numOperators, stake); + + bytes32[][] memory operatorIds = new bytes32[][](numQuorums); + for (uint8 i = 0; i < numQuorums; i++) { + operatorIds[i] = new bytes32[](operatorsToEject); + for (uint j = 0; j < operatorsToEject; j++) { + operatorIds[i][j] = registryCoordinator.getOperatorId(_incrementAddress(defaultOperator, j)); + } + } + + assertEq(uint8(registryCoordinator.getOperatorStatus(defaultOperator)), uint8(IRegistryCoordinator.OperatorStatus.REGISTERED)); + + for(uint8 i = 0; i < numQuorums; i++) { + for(uint8 j = 0; j < operatorsToEject; j++) { + cheats.expectEmit(true, true, true, true, address(ejectionManager)); + emit OperatorEjected(operatorIds[i][j], i); + } + } + + cheats.prank(ejector); + ejectionManager.ejectOperators(operatorIds); + + assertEq(uint8(registryCoordinator.getOperatorStatus(defaultOperator)), uint8(IRegistryCoordinator.OperatorStatus.DEREGISTERED)); + } + + function testEjectOperators_MultipleOperatorInsideRatelimit() public { + uint8 operatorsToEject = 10; + uint8 numOperators = 100; + uint96 stake = 1 ether; + _registerOperaters(numOperators, stake); + + bytes32[][] memory operatorIds = new bytes32[][](numQuorums); + for (uint8 i = 0; i < numQuorums; i++) { + operatorIds[i] = new bytes32[](operatorsToEject); + for (uint j = 0; j < operatorsToEject; j++) { + operatorIds[i][j] = registryCoordinator.getOperatorId(_incrementAddress(defaultOperator, j)); + } + } + + for(uint8 i = 0; i < operatorsToEject; i++) { + assertEq(uint8(registryCoordinator.getOperatorStatus(_incrementAddress(defaultOperator, i))), uint8(IRegistryCoordinator.OperatorStatus.REGISTERED)); + } + + for(uint8 i = 0; i < numQuorums; i++) { + for(uint8 j = 0; j < operatorsToEject; j++) { + cheats.expectEmit(true, true, true, true, address(ejectionManager)); + emit OperatorEjected(operatorIds[i][j], i); + } + } + + cheats.prank(ejector); + ejectionManager.ejectOperators(operatorIds); + + for(uint8 i = 0; i < operatorsToEject; i++) { + assertEq(uint8(registryCoordinator.getOperatorStatus(_incrementAddress(defaultOperator, i))), uint8(IRegistryCoordinator.OperatorStatus.DEREGISTERED)); + } + } + + function testEjectOperators_MultipleOperatorOutsideRatelimit() public { + uint8 operatorsCanEject = 2; + uint8 operatorsToEject = 10; + uint8 numOperators = 10; + uint96 stake = 1 ether; + _registerOperaters(numOperators, stake); + + bytes32[][] memory operatorIds = new bytes32[][](numQuorums); + for (uint8 i = 0; i < numQuorums; i++) { + operatorIds[i] = new bytes32[](operatorsToEject); + for (uint j = 0; j < operatorsToEject; j++) { + operatorIds[i][j] = registryCoordinator.getOperatorId(_incrementAddress(defaultOperator, j)); + } + } + + for(uint8 i = 0; i < operatorsToEject; i++) { + assertEq(uint8(registryCoordinator.getOperatorStatus(_incrementAddress(defaultOperator, i))), uint8(IRegistryCoordinator.OperatorStatus.REGISTERED)); + } + + for(uint8 i = 0; i < numQuorums; i++) { + for(uint8 j = 0; j < operatorsCanEject; j++) { + cheats.expectEmit(true, true, true, true, address(ejectionManager)); + emit OperatorEjected(operatorIds[i][j], i); + } + } + + cheats.prank(ejector); + ejectionManager.ejectOperators(operatorIds); + + for(uint8 i = 0; i < operatorsCanEject; i++) { + assertEq(uint8(registryCoordinator.getOperatorStatus(_incrementAddress(defaultOperator, i))), uint8(IRegistryCoordinator.OperatorStatus.DEREGISTERED)); + } + + for(uint8 i = operatorsCanEject; i < operatorsToEject; i++) { + assertEq(uint8(registryCoordinator.getOperatorStatus(_incrementAddress(defaultOperator, i))), uint8(IRegistryCoordinator.OperatorStatus.REGISTERED)); + } + } + + function testEjectOperators_NoEjectionForNoEjectableStake() public { + uint8 operatorsCanEject = 2; + uint8 operatorsToEject = 10; + uint8 numOperators = 10; + uint96 stake = 1 ether; + _registerOperaters(numOperators, stake); + + bytes32[][] memory operatorIds = new bytes32[][](numQuorums); + for (uint8 i = 0; i < numQuorums; i++) { + operatorIds[i] = new bytes32[](operatorsToEject); + for (uint j = 0; j < operatorsToEject; j++) { + operatorIds[i][j] = registryCoordinator.getOperatorId(_incrementAddress(defaultOperator, j)); + } + } + + for(uint8 i = 0; i < operatorsToEject; i++) { + assertEq(uint8(registryCoordinator.getOperatorStatus(_incrementAddress(defaultOperator, i))), uint8(IRegistryCoordinator.OperatorStatus.REGISTERED)); + } + + for(uint8 i = 0; i < numQuorums; i++) { + for(uint8 j = 0; j < operatorsCanEject; j++) { + cheats.expectEmit(true, true, true, true, address(ejectionManager)); + emit OperatorEjected(operatorIds[i][j], i); + } + } + + cheats.prank(ejector); + ejectionManager.ejectOperators(operatorIds); + + for(uint8 i = 0; i < operatorsCanEject; i++) { + assertEq(uint8(registryCoordinator.getOperatorStatus(_incrementAddress(defaultOperator, i))), uint8(IRegistryCoordinator.OperatorStatus.DEREGISTERED)); + } + + for(uint8 i = operatorsCanEject; i < operatorsToEject; i++) { + assertEq(uint8(registryCoordinator.getOperatorStatus(_incrementAddress(defaultOperator, i))), uint8(IRegistryCoordinator.OperatorStatus.REGISTERED)); + } + + cheats.prank(ejector); + ejectionManager.ejectOperators(operatorIds); + + for(uint8 i = 0; i < operatorsCanEject; i++) { + assertEq(uint8(registryCoordinator.getOperatorStatus(_incrementAddress(defaultOperator, i))), uint8(IRegistryCoordinator.OperatorStatus.DEREGISTERED)); + } + + for(uint8 i = operatorsCanEject; i < operatorsToEject; i++) { + assertEq(uint8(registryCoordinator.getOperatorStatus(_incrementAddress(defaultOperator, i))), uint8(IRegistryCoordinator.OperatorStatus.REGISTERED)); + } + } + + function testEjectOperators_MultipleOperatorMultipleTimesInsideRatelimit() public { + uint8 operatorsToEject = 4; + uint8 numOperators = 100; + uint96 stake = 1 ether; + _registerOperaters(numOperators, stake); + + bytes32[][] memory operatorIds = new bytes32[][](numQuorums); + for (uint8 i = 0; i < numQuorums; i++) { + operatorIds[i] = new bytes32[](operatorsToEject); + for (uint j = 0; j < operatorsToEject; j++) { + operatorIds[i][j] = registryCoordinator.getOperatorId(_incrementAddress(defaultOperator, j)); + } + } + + for(uint8 i = 0; i < operatorsToEject; i++) { + assertEq(uint8(registryCoordinator.getOperatorStatus(_incrementAddress(defaultOperator, i))), uint8(IRegistryCoordinator.OperatorStatus.REGISTERED)); + } + + for(uint8 i = 0; i < numQuorums; i++) { + for(uint8 j = 0; j < operatorsToEject; j++) { + cheats.expectEmit(true, true, true, true, address(ejectionManager)); + emit OperatorEjected(operatorIds[i][j], i); + } + } + + cheats.prank(ejector); + ejectionManager.ejectOperators(operatorIds); + + for(uint8 i = 0; i < operatorsToEject; i++) { + assertEq(uint8(registryCoordinator.getOperatorStatus(_incrementAddress(defaultOperator, i))), uint8(IRegistryCoordinator.OperatorStatus.DEREGISTERED)); + } + + cheats.warp(block.timestamp + (ratelimitWindow / 2)); + + operatorIds = new bytes32[][](numQuorums); + for (uint8 i = 0; i < numQuorums; i++) { + operatorIds[i] = new bytes32[](operatorsToEject); + for (uint j = 0; j < operatorsToEject; j++) { + operatorIds[i][j] = registryCoordinator.getOperatorId(_incrementAddress(defaultOperator, operatorsToEject + j)); + } + } + + for(uint8 i = 0; i < operatorsToEject; i++) { + assertEq(uint8(registryCoordinator.getOperatorStatus(_incrementAddress(defaultOperator, operatorsToEject + i))), uint8(IRegistryCoordinator.OperatorStatus.REGISTERED)); + } + + for(uint8 i = 0; i < numQuorums; i++) { + for(uint8 j = 0; j < operatorsToEject; j++) { + cheats.expectEmit(true, true, true, true, address(ejectionManager)); + emit OperatorEjected(operatorIds[i][j], i); + } + } + + cheats.prank(ejector); + ejectionManager.ejectOperators(operatorIds); + + for(uint8 i = 0; i < operatorsToEject; i++) { + assertEq(uint8(registryCoordinator.getOperatorStatus(_incrementAddress(defaultOperator, operatorsToEject + i))), uint8(IRegistryCoordinator.OperatorStatus.DEREGISTERED)); + } + } + + function testEjectOperators_MultipleOperatorAfterRatelimitReset() public { + uint8 operatorsToEject = 10; + uint8 numOperators = 100; + uint96 stake = 1 ether; + + testEjectOperators_MultipleOperatorInsideRatelimit(); + + vm.warp(block.timestamp + 1); + + _registerOperaters(operatorsToEject, stake); + + vm.warp(block.timestamp + ratelimitWindow); + + bytes32[][] memory operatorIds = new bytes32[][](numQuorums); + for (uint8 i = 0; i < numQuorums; i++) { + operatorIds[i] = new bytes32[](operatorsToEject); + for (uint j = 0; j < operatorsToEject; j++) { + operatorIds[i][j] = registryCoordinator.getOperatorId(_incrementAddress(defaultOperator, j)); + } + } + + for(uint8 i = 0; i < operatorsToEject; i++) { + assertEq(uint8(registryCoordinator.getOperatorStatus(_incrementAddress(defaultOperator, i))), uint8(IRegistryCoordinator.OperatorStatus.REGISTERED)); + } + + for(uint8 i = 0; i < numQuorums; i++) { + for(uint8 j = 0; j < operatorsToEject; j++) { + cheats.expectEmit(true, true, true, true, address(ejectionManager)); + emit OperatorEjected(operatorIds[i][j], i); + } + } + + cheats.prank(ejector); + ejectionManager.ejectOperators(operatorIds); + + for(uint8 i = 0; i < operatorsToEject; i++) { + assertEq(uint8(registryCoordinator.getOperatorStatus(_incrementAddress(defaultOperator, i))), uint8(IRegistryCoordinator.OperatorStatus.DEREGISTERED)); + } + } + + function testEjectOperators_NoRatelimitForOwner() public { + uint8 operatorsToEject = 100; + uint8 numOperators = 100; + uint96 stake = 1 ether; + _registerOperaters(numOperators, stake); + + bytes32[][] memory operatorIds = new bytes32[][](numQuorums); + for (uint8 i = 0; i < numQuorums; i++) { + operatorIds[i] = new bytes32[](operatorsToEject); + for (uint j = 0; j < operatorsToEject; j++) { + operatorIds[i][j] = registryCoordinator.getOperatorId(_incrementAddress(defaultOperator, j)); + } + } + + for(uint8 i = 0; i < operatorsToEject; i++) { + assertEq(uint8(registryCoordinator.getOperatorStatus(_incrementAddress(defaultOperator, i))), uint8(IRegistryCoordinator.OperatorStatus.REGISTERED)); + } + + for(uint8 i = 0; i < numQuorums; i++) { + for(uint8 j = 0; j < operatorsToEject; j++) { + cheats.expectEmit(true, true, true, true, address(ejectionManager)); + emit OperatorEjected(operatorIds[i][j], i); + } + } + + cheats.prank(registryCoordinatorOwner); + ejectionManager.ejectOperators(operatorIds); + + for(uint8 i = 0; i < operatorsToEject; i++) { + assertEq(uint8(registryCoordinator.getOperatorStatus(_incrementAddress(defaultOperator, i))), uint8(IRegistryCoordinator.OperatorStatus.DEREGISTERED)); + } + } + + function testEjectOperators_NoRevertOnMissedEjection() public { + uint8 operatorsToEject = 10; + uint8 numOperators = 100; + uint96 stake = 1 ether; + _registerOperaters(numOperators, stake); + + bytes32[][] memory operatorIds = new bytes32[][](numQuorums); + for (uint8 i = 0; i < numQuorums; i++) { + operatorIds[i] = new bytes32[](operatorsToEject); + for (uint j = 0; j < operatorsToEject; j++) { + operatorIds[i][j] = registryCoordinator.getOperatorId(_incrementAddress(defaultOperator, j)); + } + } + + cheats.prank(defaultOperator); + registryCoordinator.deregisterOperator(BitmapUtils.bitmapToBytesArray(MAX_QUORUM_BITMAP)); + + for(uint8 i = 1; i < operatorsToEject; i++) { + assertEq(uint8(registryCoordinator.getOperatorStatus(_incrementAddress(defaultOperator, i))), uint8(IRegistryCoordinator.OperatorStatus.REGISTERED)); + } + + for(uint8 i = 0; i < numQuorums; i++) { + for(uint8 j = 1; j < operatorsToEject; j++) { + cheats.expectEmit(true, true, true, true, address(ejectionManager)); + emit OperatorEjected(operatorIds[i][j], i); + } + } + + cheats.prank(ejector); + ejectionManager.ejectOperators(operatorIds); + + for(uint8 i = 0; i < operatorsToEject; i++) { + assertEq(uint8(registryCoordinator.getOperatorStatus(_incrementAddress(defaultOperator, i))), uint8(IRegistryCoordinator.OperatorStatus.DEREGISTERED)); + } + } + + function testSetQuorumEjectionParams() public { + uint8 quorumNumber = 0; + ratelimitWindow = 2 days; + ejectableStakePercent = 2000; + IEjectionManager.QuorumEjectionParams memory _quorumEjectionParams = IEjectionManager.QuorumEjectionParams({ + rateLimitWindow: ratelimitWindow, + ejectableStakePercent: ejectableStakePercent + }); + + cheats.expectEmit(true, true, true, true, address(ejectionManager)); + emit QuorumEjectionParamsSet(quorumNumber, ratelimitWindow, ejectableStakePercent); + + cheats.prank(registryCoordinatorOwner); + ejectionManager.setQuorumEjectionParams(quorumNumber, _quorumEjectionParams); + + (uint32 setRatelimitWindow, uint16 setEjectableStakePercent) = ejectionManager.quorumEjectionParams(quorumNumber); + assertEq(setRatelimitWindow, _quorumEjectionParams.rateLimitWindow); + assertEq(setEjectableStakePercent, _quorumEjectionParams.ejectableStakePercent); + } + + function testSetEjector() public { + cheats.expectEmit(true, true, true, true, address(ejectionManager)); + emit EjectorUpdated(address(0), true); + + cheats.prank(registryCoordinatorOwner); + ejectionManager.setEjector(address(0), true); + + assertEq(ejectionManager.isEjector(address(0)), true); + } + + function test_Revert_NotPermissioned() public { + bytes32[][] memory operatorIds; + cheats.expectRevert("Ejector: Only owner or ejector can eject"); + ejectionManager.ejectOperators(operatorIds); + + EjectionManager.QuorumEjectionParams memory _quorumEjectionParams; + cheats.expectRevert("Ownable: caller is not the owner"); + ejectionManager.setQuorumEjectionParams(0, _quorumEjectionParams); + + cheats.expectRevert("Ownable: caller is not the owner"); + ejectionManager.setEjector(address(0), true); + } + + function test_Overflow_Regression() public { + cheats.prank(registryCoordinatorOwner); + ejectionManager.setQuorumEjectionParams(0, IEjectionManager.QuorumEjectionParams({ + rateLimitWindow: 7 days, + ejectableStakePercent: 9999 + })); + + stakeRegistry.recordTotalStakeUpdate(1, 2_000_000_000 * 1 ether); + + ejectionManager.amountEjectableForQuorum(1); + } + + function _registerOperaters(uint8 numOperators, uint96 stake) internal { + for (uint i = 0; i < numOperators; i++) { + BN254.G1Point memory pubKey = BN254.hashToG1(keccak256(abi.encodePacked(i))); + address operator = _incrementAddress(defaultOperator, i); + _registerOperatorWithCoordinator(operator, MAX_QUORUM_BITMAP, pubKey, stake); + } + } +} \ No newline at end of file diff --git a/test/unit/OperatorStateRetrieverUnit.t.sol b/test/unit/OperatorStateRetrieverUnit.t.sol index 89a9d94a..f79c345c 100644 --- a/test/unit/OperatorStateRetrieverUnit.t.sol +++ b/test/unit/OperatorStateRetrieverUnit.t.sol @@ -6,15 +6,18 @@ import "../utils/MockAVSDeployer.sol"; contract OperatorStateRetrieverUnitTests is MockAVSDeployer { using BN254 for BN254.G1Point; - - function setUp() virtual public { + function setUp() public virtual { numQuorums = 8; _deployMockEigenLayerAndAVS(numQuorums); } function test_getOperatorState_revert_neverRegistered() public { - cheats.expectRevert("RegistryCoordinator.getQuorumBitmapIndexAtBlockNumber: no bitmap update found for operatorId at block number"); - operatorStateRetriever.getOperatorState(registryCoordinator, defaultOperatorId, uint32(block.number)); + cheats.expectRevert( + "RegCoord.getQuorumBitmapIndexAtBlockNumber: no bitmap update found for operator at blockNumber" + ); + operatorStateRetriever.getOperatorState( + registryCoordinator, defaultOperatorId, uint32(block.number) + ); } function test_getOperatorState_revert_registeredFirstAfterReferenceBlockNumber() public { @@ -22,8 +25,12 @@ contract OperatorStateRetrieverUnitTests is MockAVSDeployer { _registerOperatorWithCoordinator(defaultOperator, 1, defaultPubKey); // 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"); - operatorStateRetriever.getOperatorState(registryCoordinator, defaultOperatorId, registrationBlockNumber - 1); + cheats.expectRevert( + "RegCoord.getQuorumBitmapIndexAtBlockNumber: no bitmap update found for operator at blockNumber" + ); + operatorStateRetriever.getOperatorState( + registryCoordinator, defaultOperatorId, registrationBlockNumber - 1 + ); } function test_getOperatorState_deregisteredBeforeReferenceBlockNumber() public { @@ -35,7 +42,10 @@ contract OperatorStateRetrieverUnitTests is MockAVSDeployer { cheats.prank(defaultOperator); registryCoordinator.deregisterOperator(BitmapUtils.bitmapToBytesArray(quorumBitmap)); - (uint256 fetchedQuorumBitmap, OperatorStateRetriever.Operator[][] memory operators) = operatorStateRetriever.getOperatorState(registryCoordinator, defaultOperatorId, uint32(block.number)); + (uint256 fetchedQuorumBitmap, OperatorStateRetriever.Operator[][] memory operators) = + operatorStateRetriever.getOperatorState( + registryCoordinator, defaultOperatorId, uint32(block.number) + ); assertEq(fetchedQuorumBitmap, 0); assertEq(operators.length, 0); } @@ -45,7 +55,10 @@ contract OperatorStateRetrieverUnitTests is MockAVSDeployer { cheats.roll(registrationBlockNumber); _registerOperatorWithCoordinator(defaultOperator, quorumBitmap, defaultPubKey); - (uint256 fetchedQuorumBitmap, OperatorStateRetriever.Operator[][] memory operators) = operatorStateRetriever.getOperatorState(registryCoordinator, defaultOperatorId, uint32(block.number)); + (uint256 fetchedQuorumBitmap, OperatorStateRetriever.Operator[][] memory operators) = + operatorStateRetriever.getOperatorState( + registryCoordinator, defaultOperatorId, uint32(block.number) + ); assertEq(fetchedQuorumBitmap, 1); assertEq(operators.length, 1); assertEq(operators[0].length, 1); @@ -55,31 +68,41 @@ contract OperatorStateRetrieverUnitTests is MockAVSDeployer { } function test_getOperatorState_revert_quorumNotCreatedAtCallTime() public { - cheats.expectRevert("IndexRegistry._operatorCountAtBlockNumber: quorum did not exist at given block number"); - operatorStateRetriever.getOperatorState(registryCoordinator, BitmapUtils.bitmapToBytesArray(1 << numQuorums), uint32(block.number)); + cheats.expectRevert( + "IndexRegistry._operatorCountAtBlockNumber: quorum did not exist at given block number" + ); + operatorStateRetriever.getOperatorState( + registryCoordinator, + BitmapUtils.bitmapToBytesArray(1 << numQuorums), + uint32(block.number) + ); } function test_getOperatorState_revert_quorumNotCreatedAtReferenceBlockNumber() public { cheats.roll(registrationBlockNumber); - IRegistryCoordinator.OperatorSetParam memory operatorSetParams = - IRegistryCoordinator.OperatorSetParam({ - maxOperatorCount: defaultMaxOperatorCount, - kickBIPsOfOperatorStake: defaultKickBIPsOfOperatorStake, - kickBIPsOfTotalStake: defaultKickBIPsOfTotalStake - }); + IRegistryCoordinator.OperatorSetParam memory operatorSetParams = IRegistryCoordinator + .OperatorSetParam({ + maxOperatorCount: defaultMaxOperatorCount, + kickBIPsOfOperatorStake: defaultKickBIPsOfOperatorStake, + kickBIPsOfTotalStake: defaultKickBIPsOfTotalStake + }); uint96 minimumStake = 1; - IStakeRegistry.StrategyParams[] memory strategyParams = new IStakeRegistry.StrategyParams[](1); + IStakeRegistry.StrategyParams[] memory strategyParams = + new IStakeRegistry.StrategyParams[](1); strategyParams[0] = - IStakeRegistry.StrategyParams({ - strategy: IStrategy(address(1000)), - multiplier: 1e16 - }); + IStakeRegistry.StrategyParams({strategy: IStrategy(address(1000)), multiplier: 1e16}); cheats.prank(registryCoordinator.owner()); registryCoordinator.createQuorum(operatorSetParams, minimumStake, strategyParams); - cheats.expectRevert("IndexRegistry._operatorCountAtBlockNumber: quorum did not exist at given block number"); - operatorStateRetriever.getOperatorState(registryCoordinator, BitmapUtils.bitmapToBytesArray(1 << numQuorums), uint32(registrationBlockNumber - 1)); + cheats.expectRevert( + "IndexRegistry._operatorCountAtBlockNumber: quorum did not exist at given block number" + ); + operatorStateRetriever.getOperatorState( + registryCoordinator, + BitmapUtils.bitmapToBytesArray(1 << numQuorums), + uint32(registrationBlockNumber - 1) + ); } function test_getOperatorState_returnsCorrect() public { @@ -91,9 +114,16 @@ contract OperatorStateRetrieverUnitTests is MockAVSDeployer { address otherOperator = _incrementAddress(defaultOperator, 1); BN254.G1Point memory otherPubKey = BN254.G1Point(1, 2); bytes32 otherOperatorId = BN254.hashG1Point(otherPubKey); - _registerOperatorWithCoordinator(otherOperator, quorumBitmapThree, otherPubKey, defaultStake -1); + _registerOperatorWithCoordinator( + otherOperator, quorumBitmapThree, otherPubKey, defaultStake - 1 + ); - OperatorStateRetriever.Operator[][] memory operators = operatorStateRetriever.getOperatorState(registryCoordinator, BitmapUtils.bitmapToBytesArray(quorumBitmapThree), uint32(block.number)); + OperatorStateRetriever.Operator[][] memory operators = operatorStateRetriever + .getOperatorState( + registryCoordinator, + BitmapUtils.bitmapToBytesArray(quorumBitmapThree), + uint32(block.number) + ); assertEq(operators.length, 2); assertEq(operators[0].length, 2); assertEq(operators[1].length, 1); @@ -112,11 +142,20 @@ contract OperatorStateRetrieverUnitTests is MockAVSDeployer { bytes32[] memory nonSignerOperatorIds = new bytes32[](1); nonSignerOperatorIds[0] = defaultOperatorId; - cheats.expectRevert("RegistryCoordinator.getQuorumBitmapIndexAtBlockNumber: no bitmap update found for operatorId at block number"); - operatorStateRetriever.getCheckSignaturesIndices(registryCoordinator, uint32(block.number), BitmapUtils.bitmapToBytesArray(1), nonSignerOperatorIds); + cheats.expectRevert( + "RegCoord.getQuorumBitmapIndexAtBlockNumber: no bitmap update found for operator at blockNumber" + ); + operatorStateRetriever.getCheckSignaturesIndices( + registryCoordinator, + uint32(block.number), + BitmapUtils.bitmapToBytesArray(1), + nonSignerOperatorIds + ); } - function test_getCheckSignaturesIndices_revert_registeredFirstAfterReferenceBlockNumber() public { + function test_getCheckSignaturesIndices_revert_registeredFirstAfterReferenceBlockNumber() + public + { bytes32[] memory nonSignerOperatorIds = new bytes32[](1); nonSignerOperatorIds[0] = defaultOperatorId; @@ -124,8 +163,15 @@ contract OperatorStateRetrieverUnitTests is MockAVSDeployer { _registerOperatorWithCoordinator(defaultOperator, 1, defaultPubKey); // 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"); - operatorStateRetriever.getCheckSignaturesIndices(registryCoordinator, registrationBlockNumber - 1, BitmapUtils.bitmapToBytesArray(1), nonSignerOperatorIds); + cheats.expectRevert( + "RegCoord.getQuorumBitmapIndexAtBlockNumber: no bitmap update found for operator at blockNumber" + ); + operatorStateRetriever.getCheckSignaturesIndices( + registryCoordinator, + registrationBlockNumber - 1, + BitmapUtils.bitmapToBytesArray(1), + nonSignerOperatorIds + ); } function test_getCheckSignaturesIndices_revert_deregisteredAtReferenceBlockNumber() public { @@ -140,8 +186,15 @@ 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"); - operatorStateRetriever.getCheckSignaturesIndices(registryCoordinator, uint32(block.number), BitmapUtils.bitmapToBytesArray(1), nonSignerOperatorIds); + cheats.expectRevert( + "OperatorStateRetriever.getCheckSignaturesIndices: operator must be registered at blocknumber" + ); + operatorStateRetriever.getCheckSignaturesIndices( + registryCoordinator, + uint32(block.number), + BitmapUtils.bitmapToBytesArray(1), + nonSignerOperatorIds + ); } function test_getCheckSignaturesIndices_revert_quorumNotCreatedAtCallTime() public { @@ -150,11 +203,18 @@ contract OperatorStateRetrieverUnitTests is MockAVSDeployer { _registerOperatorWithCoordinator(defaultOperator, 1, defaultPubKey); - cheats.expectRevert("StakeRegistry.getTotalStakeIndicesAtBlockNumber: quorum does not exist"); - operatorStateRetriever.getCheckSignaturesIndices(registryCoordinator, uint32(block.number), BitmapUtils.bitmapToBytesArray(1 << numQuorums), nonSignerOperatorIds); + cheats.expectRevert("StakeRegistry.quorumExists: quorum does not exist"); + operatorStateRetriever.getCheckSignaturesIndices( + registryCoordinator, + uint32(block.number), + BitmapUtils.bitmapToBytesArray(1 << numQuorums), + nonSignerOperatorIds + ); } - function test_getCheckSignaturesIndices_revert_quorumNotCreatedAtReferenceBlockNumber() public { + function test_getCheckSignaturesIndices_revert_quorumNotCreatedAtReferenceBlockNumber() + public + { cheats.roll(registrationBlockNumber); _registerOperatorWithCoordinator(defaultOperator, 1, defaultPubKey); @@ -162,25 +222,30 @@ contract OperatorStateRetrieverUnitTests is MockAVSDeployer { bytes32[] memory nonSignerOperatorIds = new bytes32[](1); nonSignerOperatorIds[0] = defaultOperatorId; - IRegistryCoordinator.OperatorSetParam memory operatorSetParams = - IRegistryCoordinator.OperatorSetParam({ - maxOperatorCount: defaultMaxOperatorCount, - kickBIPsOfOperatorStake: defaultKickBIPsOfOperatorStake, - kickBIPsOfTotalStake: defaultKickBIPsOfTotalStake - }); + IRegistryCoordinator.OperatorSetParam memory operatorSetParams = IRegistryCoordinator + .OperatorSetParam({ + maxOperatorCount: defaultMaxOperatorCount, + kickBIPsOfOperatorStake: defaultKickBIPsOfOperatorStake, + kickBIPsOfTotalStake: defaultKickBIPsOfTotalStake + }); uint96 minimumStake = 1; - IStakeRegistry.StrategyParams[] memory strategyParams = new IStakeRegistry.StrategyParams[](1); + IStakeRegistry.StrategyParams[] memory strategyParams = + new IStakeRegistry.StrategyParams[](1); strategyParams[0] = - IStakeRegistry.StrategyParams({ - strategy: IStrategy(address(1000)), - multiplier: 1e16 - }); + IStakeRegistry.StrategyParams({strategy: IStrategy(address(1000)), multiplier: 1e16}); cheats.prank(registryCoordinator.owner()); registryCoordinator.createQuorum(operatorSetParams, minimumStake, strategyParams); - cheats.expectRevert("StakeRegistry.getTotalStakeIndicesAtBlockNumber: quorum has no stake history at blockNumber"); - operatorStateRetriever.getCheckSignaturesIndices(registryCoordinator, registrationBlockNumber + 5, BitmapUtils.bitmapToBytesArray(1 << numQuorums), nonSignerOperatorIds); + cheats.expectRevert( + "StakeRegistry.getTotalStakeIndicesAtBlockNumber: quorum has no stake history at blockNumber" + ); + operatorStateRetriever.getCheckSignaturesIndices( + registryCoordinator, + registrationBlockNumber + 5, + BitmapUtils.bitmapToBytesArray(1 << numQuorums), + nonSignerOperatorIds + ); } function test_getCheckSignaturesIndices_returnsCorrect() public { @@ -195,7 +260,9 @@ contract OperatorStateRetrieverUnitTests is MockAVSDeployer { address otherOperator = _incrementAddress(defaultOperator, 1); BN254.G1Point memory otherPubKey = BN254.G1Point(1, 2); bytes32 otherOperatorId = BN254.hashG1Point(otherPubKey); - _registerOperatorWithCoordinator(otherOperator, quorumBitmapThree, otherPubKey, defaultStake -1); + _registerOperatorWithCoordinator( + otherOperator, quorumBitmapThree, otherPubKey, defaultStake - 1 + ); cheats.roll(registrationBlockNumber + 15); cheats.prank(defaultOperator); @@ -209,13 +276,21 @@ contract OperatorStateRetrieverUnitTests is MockAVSDeployer { registryCoordinator.deregisterOperator(BitmapUtils.bitmapToBytesArray(quorumBitmapTwo)); cheats.roll(registrationBlockNumber + 30); - _registerOperatorWithCoordinator(otherOperator, quorumBitmapTwo, otherPubKey, defaultStake - 2); + _registerOperatorWithCoordinator( + otherOperator, quorumBitmapTwo, otherPubKey, defaultStake - 2 + ); bytes32[] memory nonSignerOperatorIds = new bytes32[](2); nonSignerOperatorIds[0] = defaultOperatorId; nonSignerOperatorIds[1] = otherOperatorId; - OperatorStateRetriever.CheckSignaturesIndices memory checkSignaturesIndices = operatorStateRetriever.getCheckSignaturesIndices(registryCoordinator, uint32(block.number), BitmapUtils.bitmapToBytesArray(quorumBitmapThree), nonSignerOperatorIds); + OperatorStateRetriever.CheckSignaturesIndices memory checkSignaturesIndices = + operatorStateRetriever.getCheckSignaturesIndices( + registryCoordinator, + uint32(block.number), + BitmapUtils.bitmapToBytesArray(quorumBitmapThree), + nonSignerOperatorIds + ); // we're querying for 2 operators, so there should be 2 nonSignerQuorumBitmapIndices assertEq(checkSignaturesIndices.nonSignerQuorumBitmapIndices.length, 2); // the first operator (0) registered for quorum 1, (1) deregistered from quorum 1, and (2) registered for quorum 2 @@ -250,7 +325,12 @@ contract OperatorStateRetrieverUnitTests is MockAVSDeployer { nonSignerOperatorIds = new bytes32[](1); nonSignerOperatorIds[0] = otherOperatorId; // taking only the deregistration into account - checkSignaturesIndices = operatorStateRetriever.getCheckSignaturesIndices(registryCoordinator, registrationBlockNumber + 15, BitmapUtils.bitmapToBytesArray(quorumBitmapThree), nonSignerOperatorIds); + checkSignaturesIndices = operatorStateRetriever.getCheckSignaturesIndices( + registryCoordinator, + registrationBlockNumber + 15, + BitmapUtils.bitmapToBytesArray(quorumBitmapThree), + nonSignerOperatorIds + ); // we're querying for 1 operator, so there should be 1 nonSignerQuorumBitmapIndices assertEq(checkSignaturesIndices.nonSignerQuorumBitmapIndices.length, 1); // the second operator (0) registered for quorum 1 and 2 @@ -286,44 +366,47 @@ contract OperatorStateRetrieverUnitTests is MockAVSDeployer { uint256[][] memory expectedOperatorOverallIndices ) = _registerRandomOperators(pseudoRandomNumber); - for (uint i = 0; i < operatorMetadatas.length; i++) { + for (uint256 i = 0; i < operatorMetadatas.length; i++) { uint32 blockNumber = uint32(registrationBlockNumber + blocksBetweenRegistrations * i); uint256 gasBefore = gasleft(); // retrieve the ordered list of operators for each quorum along with their id and stake - (uint256 quorumBitmap, OperatorStateRetriever.Operator[][] memory operators) = - operatorStateRetriever.getOperatorState(registryCoordinator, operatorMetadatas[i].operatorId, blockNumber); + (uint256 quorumBitmap, OperatorStateRetriever.Operator[][] memory operators) = + operatorStateRetriever.getOperatorState( + registryCoordinator, operatorMetadatas[i].operatorId, blockNumber + ); uint256 gasAfter = gasleft(); emit log_named_uint("gasUsed", gasBefore - gasAfter); assertEq(operatorMetadatas[i].quorumBitmap, quorumBitmap); bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); - + // assert that the operators returned are the expected ones _assertExpectedOperators( - quorumNumbers, - operators, - expectedOperatorOverallIndices, - operatorMetadatas + quorumNumbers, operators, expectedOperatorOverallIndices, operatorMetadatas ); } // choose a random operator to deregister uint256 operatorIndexToDeregister = pseudoRandomNumber % maxOperatorsToRegister; - bytes memory quorumNumbersToDeregister = BitmapUtils.bitmapToBytesArray(operatorMetadatas[operatorIndexToDeregister].quorumBitmap); + bytes memory quorumNumbersToDeregister = BitmapUtils.bitmapToBytesArray( + operatorMetadatas[operatorIndexToDeregister].quorumBitmap + ); - uint32 deregistrationBlockNumber = registrationBlockNumber + blocksBetweenRegistrations * (uint32(operatorMetadatas.length) + 1); + uint32 deregistrationBlockNumber = registrationBlockNumber + + blocksBetweenRegistrations * (uint32(operatorMetadatas.length) + 1); cheats.roll(deregistrationBlockNumber); cheats.prank(_incrementAddress(defaultOperator, operatorIndexToDeregister)); registryCoordinator.deregisterOperator(quorumNumbersToDeregister); // modify expectedOperatorOverallIndices by moving th operatorIdsToSwap to the index where the operatorIndexToDeregister was - for (uint i = 0; i < quorumNumbersToDeregister.length; i++) { + for (uint256 i = 0; i < quorumNumbersToDeregister.length; i++) { uint8 quorumNumber = uint8(quorumNumbersToDeregister[i]); // loop through indices till we find operatorIndexToDeregister, then move that last operator into that index - for (uint j = 0; j < expectedOperatorOverallIndices[quorumNumber].length; j++) { + for (uint256 j = 0; j < expectedOperatorOverallIndices[quorumNumber].length; j++) { if (expectedOperatorOverallIndices[quorumNumber][j] == operatorIndexToDeregister) { - expectedOperatorOverallIndices[quorumNumber][j] = expectedOperatorOverallIndices[quorumNumber][expectedOperatorOverallIndices[quorumNumber].length - 1]; + expectedOperatorOverallIndices[quorumNumber][j] = expectedOperatorOverallIndices[quorumNumber][expectedOperatorOverallIndices[quorumNumber] + .length - 1]; break; } } @@ -334,10 +417,12 @@ contract OperatorStateRetrieverUnitTests is MockAVSDeployer { for (uint8 i = 0; i < allQuorumNumbers.length; i++) { allQuorumNumbers[i] = bytes1(i); } - + _assertExpectedOperators( allQuorumNumbers, - operatorStateRetriever.getOperatorState(registryCoordinator, allQuorumNumbers, deregistrationBlockNumber), + operatorStateRetriever.getOperatorState( + registryCoordinator, allQuorumNumbers, deregistrationBlockNumber + ), expectedOperatorOverallIndices, operatorMetadatas ); @@ -349,7 +434,8 @@ contract OperatorStateRetrieverUnitTests is MockAVSDeployer { uint256[][] memory expectedOperatorOverallIndices ) = _registerRandomOperators(pseudoRandomNumber); - uint32 cumulativeBlockNumber = registrationBlockNumber + blocksBetweenRegistrations * uint32(operatorMetadatas.length); + uint32 cumulativeBlockNumber = + registrationBlockNumber + blocksBetweenRegistrations * uint32(operatorMetadatas.length); // get the quorum bitmap for which there is at least 1 operator uint256 allInclusiveQuorumBitmap = 0; @@ -357,28 +443,53 @@ contract OperatorStateRetrieverUnitTests is MockAVSDeployer { allInclusiveQuorumBitmap |= operatorMetadatas[i].quorumBitmap; } - bytes memory allInclusiveQuorumNumbers = BitmapUtils.bitmapToBytesArray(allInclusiveQuorumBitmap); + bytes memory allInclusiveQuorumNumbers = + BitmapUtils.bitmapToBytesArray(allInclusiveQuorumBitmap); bytes32[] memory nonSignerOperatorIds = new bytes32[](0); OperatorStateRetriever.CheckSignaturesIndices memory checkSignaturesIndices = - operatorStateRetriever.getCheckSignaturesIndices( - registryCoordinator, - cumulativeBlockNumber, - allInclusiveQuorumNumbers, - nonSignerOperatorIds - ); + operatorStateRetriever.getCheckSignaturesIndices( + registryCoordinator, + cumulativeBlockNumber, + allInclusiveQuorumNumbers, + nonSignerOperatorIds + ); - assertEq(checkSignaturesIndices.nonSignerQuorumBitmapIndices.length, 0, "nonSignerQuorumBitmapIndices should be empty if no nonsigners"); - assertEq(checkSignaturesIndices.quorumApkIndices.length, allInclusiveQuorumNumbers.length, "quorumApkIndices should be the number of quorums queried for"); - assertEq(checkSignaturesIndices.totalStakeIndices.length, allInclusiveQuorumNumbers.length, "totalStakeIndices should be the number of quorums queried for"); - assertEq(checkSignaturesIndices.nonSignerStakeIndices.length, allInclusiveQuorumNumbers.length, "nonSignerStakeIndices should be the number of quorums queried for"); + assertEq( + checkSignaturesIndices.nonSignerQuorumBitmapIndices.length, + 0, + "nonSignerQuorumBitmapIndices should be empty if no nonsigners" + ); + assertEq( + checkSignaturesIndices.quorumApkIndices.length, + allInclusiveQuorumNumbers.length, + "quorumApkIndices should be the number of quorums queried for" + ); + assertEq( + checkSignaturesIndices.totalStakeIndices.length, + allInclusiveQuorumNumbers.length, + "totalStakeIndices should be the number of quorums queried for" + ); + assertEq( + checkSignaturesIndices.nonSignerStakeIndices.length, + allInclusiveQuorumNumbers.length, + "nonSignerStakeIndices should be the number of quorums queried for" + ); // assert the indices are the number of registered operators for the quorum minus 1 for (uint8 i = 0; i < allInclusiveQuorumNumbers.length; i++) { uint8 quorumNumber = uint8(allInclusiveQuorumNumbers[i]); - assertEq(checkSignaturesIndices.quorumApkIndices[i], expectedOperatorOverallIndices[quorumNumber].length, "quorumApkIndex should be the number of registered operators for the quorum"); - assertEq(checkSignaturesIndices.totalStakeIndices[i], expectedOperatorOverallIndices[quorumNumber].length, "totalStakeIndex should be the number of registered operators for the quorum"); + assertEq( + checkSignaturesIndices.quorumApkIndices[i], + expectedOperatorOverallIndices[quorumNumber].length, + "quorumApkIndex should be the number of registered operators for the quorum" + ); + assertEq( + checkSignaturesIndices.totalStakeIndices[i], + expectedOperatorOverallIndices[quorumNumber].length, + "totalStakeIndex should be the number of registered operators for the quorum" + ); } } @@ -388,7 +499,8 @@ contract OperatorStateRetrieverUnitTests is MockAVSDeployer { uint256[][] memory expectedOperatorOverallIndices ) = _registerRandomOperators(pseudoRandomNumber); - uint32 cumulativeBlockNumber = registrationBlockNumber + blocksBetweenRegistrations * uint32(operatorMetadatas.length); + uint32 cumulativeBlockNumber = + registrationBlockNumber + blocksBetweenRegistrations * uint32(operatorMetadatas.length); // get the quorum bitmap for which there is at least 1 operator uint256 allInclusiveQuorumBitmap = 0; @@ -396,41 +508,78 @@ contract OperatorStateRetrieverUnitTests is MockAVSDeployer { allInclusiveQuorumBitmap |= operatorMetadatas[i].quorumBitmap; } - bytes memory allInclusiveQuorumNumbers = BitmapUtils.bitmapToBytesArray(allInclusiveQuorumBitmap); - - bytes32[] memory nonSignerOperatorIds = new bytes32[](pseudoRandomNumber % (operatorMetadatas.length - 1) + 1); - uint256 randomIndex = uint256(keccak256(abi.encodePacked("nonSignerOperatorIds", pseudoRandomNumber))) % operatorMetadatas.length; - for (uint i = 0; i < nonSignerOperatorIds.length; i++) { - nonSignerOperatorIds[i] = operatorMetadatas[(randomIndex + i) % operatorMetadatas.length].operatorId; + bytes memory allInclusiveQuorumNumbers = + BitmapUtils.bitmapToBytesArray(allInclusiveQuorumBitmap); + + bytes32[] memory nonSignerOperatorIds = + new bytes32[](pseudoRandomNumber % (operatorMetadatas.length - 1) + 1); + uint256 randomIndex = uint256( + keccak256(abi.encodePacked("nonSignerOperatorIds", pseudoRandomNumber)) + ) % operatorMetadatas.length; + for (uint256 i = 0; i < nonSignerOperatorIds.length; i++) { + nonSignerOperatorIds[i] = + operatorMetadatas[(randomIndex + i) % operatorMetadatas.length].operatorId; } OperatorStateRetriever.CheckSignaturesIndices memory checkSignaturesIndices = - operatorStateRetriever.getCheckSignaturesIndices( - registryCoordinator, - cumulativeBlockNumber, - allInclusiveQuorumNumbers, - nonSignerOperatorIds - ); + operatorStateRetriever.getCheckSignaturesIndices( + registryCoordinator, + cumulativeBlockNumber, + allInclusiveQuorumNumbers, + nonSignerOperatorIds + ); - assertEq(checkSignaturesIndices.nonSignerQuorumBitmapIndices.length, nonSignerOperatorIds.length, "nonSignerQuorumBitmapIndices should be the number of nonsigners"); - assertEq(checkSignaturesIndices.quorumApkIndices.length, allInclusiveQuorumNumbers.length, "quorumApkIndices should be the number of quorums queried for"); - assertEq(checkSignaturesIndices.totalStakeIndices.length, allInclusiveQuorumNumbers.length, "totalStakeIndices should be the number of quorums queried for"); - assertEq(checkSignaturesIndices.nonSignerStakeIndices.length, allInclusiveQuorumNumbers.length, "nonSignerStakeIndices should be the number of quorums queried for"); + assertEq( + checkSignaturesIndices.nonSignerQuorumBitmapIndices.length, + nonSignerOperatorIds.length, + "nonSignerQuorumBitmapIndices should be the number of nonsigners" + ); + assertEq( + checkSignaturesIndices.quorumApkIndices.length, + allInclusiveQuorumNumbers.length, + "quorumApkIndices should be the number of quorums queried for" + ); + assertEq( + checkSignaturesIndices.totalStakeIndices.length, + allInclusiveQuorumNumbers.length, + "totalStakeIndices should be the number of quorums queried for" + ); + assertEq( + checkSignaturesIndices.nonSignerStakeIndices.length, + allInclusiveQuorumNumbers.length, + "nonSignerStakeIndices should be the number of quorums queried for" + ); // assert the indices are the number of registered operators for the quorum minus 1 for (uint8 i = 0; i < allInclusiveQuorumNumbers.length; i++) { uint8 quorumNumber = uint8(allInclusiveQuorumNumbers[i]); - assertEq(checkSignaturesIndices.quorumApkIndices[i], expectedOperatorOverallIndices[quorumNumber].length, "quorumApkIndex should be the number of registered operators for the quorum"); - assertEq(checkSignaturesIndices.totalStakeIndices[i], expectedOperatorOverallIndices[quorumNumber].length, "totalStakeIndex should be the number of registered operators for the quorum"); + assertEq( + checkSignaturesIndices.quorumApkIndices[i], + expectedOperatorOverallIndices[quorumNumber].length, + "quorumApkIndex should be the number of registered operators for the quorum" + ); + assertEq( + checkSignaturesIndices.totalStakeIndices[i], + expectedOperatorOverallIndices[quorumNumber].length, + "totalStakeIndex should be the number of registered operators for the quorum" + ); } // assert the quorum bitmap and stake indices are zero because there have been no kicks or stake updates - for (uint i = 0; i < nonSignerOperatorIds.length; i++) { - assertEq(checkSignaturesIndices.nonSignerQuorumBitmapIndices[i], 0, "nonSignerQuorumBitmapIndices should be zero because there have been no kicks"); + for (uint256 i = 0; i < nonSignerOperatorIds.length; i++) { + assertEq( + checkSignaturesIndices.nonSignerQuorumBitmapIndices[i], + 0, + "nonSignerQuorumBitmapIndices should be zero because there have been no kicks" + ); } - for (uint i = 0; i < checkSignaturesIndices.nonSignerStakeIndices.length; i++) { - for (uint j = 0; j < checkSignaturesIndices.nonSignerStakeIndices[i].length; j++) { - assertEq(checkSignaturesIndices.nonSignerStakeIndices[i][j], 0, "nonSignerStakeIndices should be zero because there have been no stake updates past the first one"); + for (uint256 i = 0; i < checkSignaturesIndices.nonSignerStakeIndices.length; i++) { + for (uint256 j = 0; j < checkSignaturesIndices.nonSignerStakeIndices[i].length; j++) { + assertEq( + checkSignaturesIndices.nonSignerStakeIndices[i][j], + 0, + "nonSignerStakeIndices should be zero because there have been no stake updates past the first one" + ); } } } @@ -444,12 +593,16 @@ contract OperatorStateRetrieverUnitTests is MockAVSDeployer { address otherOperator = _incrementAddress(defaultOperator, 1); BN254.G1Point memory otherPubKey = BN254.G1Point(1, 2); bytes32 otherOperatorId = BN254.hashG1Point(otherPubKey); - _registerOperatorWithCoordinator(otherOperator, quorumBitmapThree, otherPubKey, defaultStake -1); + _registerOperatorWithCoordinator( + otherOperator, quorumBitmapThree, otherPubKey, defaultStake - 1 + ); bytes32[] memory operatorIds = new bytes32[](2); operatorIds[0] = defaultOperatorId; operatorIds[1] = otherOperatorId; - uint256[] memory quorumBitmaps = operatorStateRetriever.getQuorumBitmapsAtBlockNumber(registryCoordinator, operatorIds, uint32(block.number)); + uint256[] memory quorumBitmaps = operatorStateRetriever.getQuorumBitmapsAtBlockNumber( + registryCoordinator, operatorIds, uint32(block.number) + ); assertEq(quorumBitmaps.length, 2); assertEq(quorumBitmaps[0], quorumBitmapOne); @@ -463,11 +616,14 @@ contract OperatorStateRetrieverUnitTests is MockAVSDeployer { OperatorMetadata[] memory operatorMetadatas ) internal { // for each quorum - for (uint j = 0; j < quorumNumbers.length; j++) { + for (uint256 j = 0; j < quorumNumbers.length; j++) { // make sure the each operator id and stake is correct - for (uint k = 0; k < operators[j].length; k++) { + for (uint256 k = 0; k < operators[j].length; k++) { uint8 quorumNumber = uint8(quorumNumbers[j]); - assertEq(operators[j][k].operatorId, operatorMetadatas[expectedOperatorOverallIndices[quorumNumber][k]].operatorId); + assertEq( + operators[j][k].operatorId, + operatorMetadatas[expectedOperatorOverallIndices[quorumNumber][k]].operatorId + ); // using assertApprox to account for rounding errors assertApproxEqAbs( operators[j][k].stake, diff --git a/test/unit/RegistryCoordinatorUnit.t.sol b/test/unit/RegistryCoordinatorUnit.t.sol index b906b8be..cd67e7a2 100644 --- a/test/unit/RegistryCoordinatorUnit.t.sol +++ b/test/unit/RegistryCoordinatorUnit.t.sol @@ -192,12 +192,12 @@ contract RegistryCoordinatorUnitTests_Initialization_Setters is RegistryCoordina cheats.expectEmit(true, true, true, true, address(registryCoordinator)); emit OperatorSocketUpdate(defaultOperatorId, "localhost:32004"); registryCoordinator.updateSocket("localhost:32004"); - + assertEq(socketRegistry.getOperatorSocket(defaultOperatorId), "localhost:32004"); } function test_updateSocket_revert_notRegistered() public { cheats.prank(defaultOperator); - cheats.expectRevert("RegistryCoordinator.updateSocket: operator is not registered"); + cheats.expectRevert("RegCoord.updateSocket: operator 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("RegCoord._registerOperator: bitmap cannot be 0"); cheats.prank(defaultOperator); registryCoordinator.registerOperator(emptyQuorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); } @@ -414,8 +414,8 @@ contract RegistryCoordinatorUnitTests_RegisterOperator is RegistryCoordinatorUni newQuorumNumbers[0] = bytes1(defaultQuorumNumber+1); uint96 actualStake = _setOperatorWeight(defaultOperator, uint8(newQuorumNumbers[0]), defaultStake); - cheats.expectEmit(true, true, true, true, address(registryCoordinator)); - emit OperatorSocketUpdate(defaultOperatorId, defaultSocket); + //cheats.expectEmit(true, true, true, true, address(registryCoordinator)); + //emit OperatorSocketUpdate(defaultOperatorId, defaultSocket); cheats.expectEmit(true, true, true, true, address(blsApkRegistry)); emit OperatorAddedToQuorums(defaultOperator, defaultOperatorId, newQuorumNumbers); cheats.expectEmit(true, true, true, true, address(stakeRegistry)); @@ -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("RegCoord.registerOperator: operator count exceeds maximum"); registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); } @@ -502,7 +502,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("RegCoord._registerOperator: operator already registered for some quorums"); 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("RegCoord._registerOperator: bitmap cannot be 0"); registryCoordinator._registerOperatorExternal(defaultOperator, defaultOperatorId, emptyQuorumNumbers, defaultSocket, emptySig); } @@ -534,7 +534,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("RegCoord._registerOperator: operator already registered for some quorums"); registryCoordinator._registerOperatorExternal(defaultOperator, defaultOperatorId, quorumNumbers, 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("RegCoord._deregisterOperator: operator is 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("RegCoord._deregisterOperator: operator is not registered for quorums"); cheats.prank(defaultOperator); registryCoordinator.deregisterOperator(quorumNumbers); } @@ -956,13 +956,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("RegCoord._deregisterOperator: bitmap cannot be 0"); registryCoordinator._deregisterOperatorExternal(defaultOperator, emptyQuorumNumbers); } function test_deregisterOperatorExternal_revert_notRegistered() public { bytes memory emptyQuorumNumbers = new bytes(0); - cheats.expectRevert("RegistryCoordinator._deregisterOperator: operator is not registered"); + cheats.expectRevert("RegCoord._deregisterOperator: operator is not registered"); registryCoordinator._deregisterOperatorExternal(defaultOperator, emptyQuorumNumbers); } @@ -984,10 +984,66 @@ 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("RegCoord._deregisterOperator: operator is not registered for quorums"); registryCoordinator._deregisterOperatorExternal(defaultOperator, incorrectQuorum); } + function test_reregisterOperator_revert_reregistrationDelay() public { + uint256 reregistrationDelay = 1 days; + cheats.warp(block.timestamp + reregistrationDelay); + cheats.prank(registryCoordinatorOwner); + registryCoordinator.setEjectionCooldown(reregistrationDelay); + + ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; + uint32 registrationBlockNumber = 100; + uint32 reregistrationBlockNumber = 200; + + bytes memory quorumNumbers = new bytes(1); + quorumNumbers[0] = bytes1(defaultQuorumNumber); + + _setOperatorWeight(defaultOperator, uint8(quorumNumbers[0]), defaultStake); + + cheats.prank(defaultOperator); + cheats.roll(registrationBlockNumber); + registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); + + cheats.prank(ejector); + registryCoordinator.ejectOperator(defaultOperator, quorumNumbers); + + cheats.prank(defaultOperator); + cheats.roll(reregistrationBlockNumber); + cheats.expectRevert("RegCoord._registerOperator: operator cannot reregister yet"); + registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); + } + + function test_reregisterOperator_reregistrationDelay() public { + uint256 reregistrationDelay = 1 days; + cheats.warp(block.timestamp + reregistrationDelay); + cheats.prank(registryCoordinatorOwner); + registryCoordinator.setEjectionCooldown(reregistrationDelay); + + ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; + uint32 registrationBlockNumber = 100; + uint32 reregistrationBlockNumber = 200; + + bytes memory quorumNumbers = new bytes(1); + quorumNumbers[0] = bytes1(defaultQuorumNumber); + + _setOperatorWeight(defaultOperator, uint8(quorumNumbers[0]), defaultStake); + + cheats.prank(defaultOperator); + cheats.roll(registrationBlockNumber); + registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); + + cheats.prank(ejector); + registryCoordinator.ejectOperator(defaultOperator, quorumNumbers); + + cheats.prank(defaultOperator); + cheats.roll(reregistrationBlockNumber); + cheats.warp(block.timestamp + reregistrationDelay + 1); + registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); + } + // note: this is not possible to test, because there is no route to getting the operator registered for nonexistent quorums // function test_deregisterOperatorExternal_revert_nonexistentQuorums() public { @@ -1157,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("RegCoord.onlyEjector: caller is not the ejector"); cheats.prank(defaultOperator); registryCoordinator.ejectOperator(defaultOperator, quorumNumbers); } @@ -1165,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("RegCoord.getQuorumBitmapIndexAtBlockNumber: no bitmap update found for operator at blockNumber"); registryCoordinator.getQuorumBitmapIndicesAtBlockNumber(blockNumber, operatorIds); } @@ -1186,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("RegCoord.getQuorumBitmapIndexAtBlockNumber: no bitmap update found for operator at blockNumber"); registryCoordinator.getQuorumBitmapIndicesAtBlockNumber(blockNumber, operatorIds); blockNumber = registrationBlockNumber; @@ -1208,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("RegCoord.getQuorumBitmapIndexAtBlockNumber: no bitmap update found for operator at blockNumber"); registryCoordinator.getQuorumBitmapIndicesAtBlockNumber(blockNumber, operatorIds); blockNumber = registrationBlockNumber; @@ -1241,7 +1297,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("RegCoord.getQuorumBitmapAtBlockNumberByIndex: quorumBitmapUpdate is from after blockNumber"); uint192 returnVal = registryCoordinator.getQuorumBitmapAtBlockNumberByIndex(operatorId, blockNumber, index); blockNumber = registrationBlockNumber; @@ -1254,7 +1310,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("RegCoord.getQuorumBitmapAtBlockNumberByIndex: quorumBitmapUpdate is from after blockNumber"); returnVal = registryCoordinator.getQuorumBitmapAtBlockNumberByIndex(operatorId, blockNumber, index); blockNumber = deregistrationBlockNumber; @@ -1267,7 +1323,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("RegCoord.getQuorumBitmapAtBlockNumberByIndex: quorumBitmapUpdate is from before blockNumber"); returnVal = registryCoordinator.getQuorumBitmapAtBlockNumberByIndex(operatorId, blockNumber, index); } } @@ -1406,7 +1462,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("RegCoord._validateChurn: incoming operator has insufficient stake for churn"); registryCoordinator.registerOperatorWithChurn( quorumNumbers, defaultSocket, @@ -1438,7 +1494,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("RegCoord._validateChurn: cannot kick operator with more than kickBIPsOfTotalStake"); registryCoordinator.registerOperatorWithChurn( quorumNumbers, defaultSocket, @@ -1500,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("RegCoord._verifyChurnApproverSignature: churnApprover signature expired"); registryCoordinator.registerOperatorWithChurn( quorumNumbers, defaultSocket, @@ -1621,7 +1677,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(bytes("RegCoord.updateOperatorsForQuorum: input length mismatch")); registryCoordinator.updateOperatorsForQuorum(operatorsToUpdate, quorumNumbers); } @@ -1633,7 +1689,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(bytes("RegCoord.updateOperatorsForQuorum: number of updated operators does not match quorum total")); registryCoordinator.updateOperatorsForQuorum(operatorsToUpdate, quorumNumbers); } @@ -1654,7 +1710,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(bytes("RegCoord.updateOperatorsForQuorum: operator not in quorum")); registryCoordinator.updateOperatorsForQuorum(operatorsToUpdate, quorumNumbers); } @@ -1682,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("RegCoord.updateOperatorsForQuorum: operators array must be sorted in ascending address order")); registryCoordinator.updateOperatorsForQuorum(operatorsToUpdate, quorumNumbers); } @@ -1708,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("RegCoord.updateOperatorsForQuorum: operators array must be sorted in ascending address order")); registryCoordinator.updateOperatorsForQuorum(operatorsToUpdate, quorumNumbers); } diff --git a/test/unit/ServiceManagerBase.t.sol b/test/unit/ServiceManagerBase.t.sol new file mode 100644 index 00000000..a7f6464a --- /dev/null +++ b/test/unit/ServiceManagerBase.t.sol @@ -0,0 +1,1082 @@ +// 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 "../utils/MockAVSDeployer.sol"; + +contract ServiceManagerBase_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; + /// @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; + + // mapping to setting fuzzed inputs + mapping(address => bool) public addressIsExcludedFromFuzzedInputs; + + modifier filterFuzzedAddressInputs(address fuzzedAddress) { + cheats.assume(!addressIsExcludedFromFuzzedInputs[fuzzedAddress]); + _; + } + + function setUp() public virtual { + _deployMockEigenLayerAndAVS(); + // Deploy rewards coordinator + rewardsCoordinatorImplementation = new RewardsCoordinator( + delegationMock, + 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, + registryCoordinatorImplementation, + stakeRegistryImplementation + ); + + 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; + } + + /// @notice deploy token to owner and approve ServiceManager. Used for deploying reward tokens + function _deployMockRewardTokens( + address owner, + uint256 numTokens + ) internal virtual { + cheats.startPrank(owner); + for (uint256 i = 0; i < numTokens; ++i) { + IERC20 token = new ERC20PresetFixedSupply( + "dog wif hat", + "MOCK1", + mockTokenInitialSupply, + owner + ); + rewardTokens.push(token); + token.approve(address(serviceManager), mockTokenInitialSupply); + } + cheats.stopPrank(); + } + + function _getBalanceForTokens( + IERC20[] memory tokens, + address holder + ) internal view returns (uint256[] memory) { + uint256[] memory balances = new uint256[](tokens.length); + for (uint256 i = 0; i < tokens.length; ++i) { + balances[i] = tokens[i].balanceOf(holder); + } + return balances; + } + + 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 _maxTimestamp( + uint32 timestamp1, + uint32 timestamp2 + ) internal pure returns (uint32) { + return timestamp1 > timestamp2 ? timestamp1 : timestamp2; + } + + function testFuzz_createAVSRewardsSubmission_Revert_WhenNotOwner( + address caller + ) public filterFuzzedAddressInputs(caller) { + cheats.assume(caller != rewardsInitiator); + IRewardsCoordinator.RewardsSubmission[] memory rewardsSubmissions; + + cheats.prank(caller); + cheats.expectRevert( + "ServiceManagerBase.onlyRewardsInitiator: caller is not the rewards initiator" + ); + serviceManager.createAVSRewardsSubmission(rewardsSubmissions); + } + + function test_createAVSRewardsSubmission_Revert_WhenERC20NotApproved() + public + { + IERC20 token = new ERC20PresetFixedSupply( + "dog wif hat", + "MOCK1", + mockTokenInitialSupply, + rewardsInitiator + ); + + IRewardsCoordinator.RewardsSubmission[] + memory rewardsSubmissions = new IRewardsCoordinator.RewardsSubmission[]( + 1 + ); + rewardsSubmissions[0] = IRewardsCoordinator.RewardsSubmission({ + strategiesAndMultipliers: defaultStrategyAndMultipliers, + token: token, + amount: 100, + startTimestamp: uint32(block.timestamp), + duration: uint32(1 weeks) + }); + + cheats.prank(rewardsInitiator); + cheats.expectRevert("ERC20: insufficient allowance"); + serviceManager.createAVSRewardsSubmission(rewardsSubmissions); + } + + function test_createAVSRewardsSubmission_SingleSubmission( + uint256 startTimestamp, + uint256 duration, + uint256 amount + ) public { + // 1. Bound fuzz inputs to valid ranges and amounts + IERC20 rewardToken = new ERC20PresetFixedSupply( + "dog wif hat", + "MOCK1", + mockTokenInitialSupply, + rewardsInitiator + ); + amount = bound(amount, 1, MAX_REWARDS_AMOUNT); + duration = bound(duration, 0, MAX_REWARDS_DURATION); + duration = duration - (duration % CALCULATION_INTERVAL_SECONDS); + startTimestamp = bound( + startTimestamp, + uint256( + _maxTimestamp( + GENESIS_REWARDS_TIMESTAMP, + uint32(block.timestamp) - MAX_RETROACTIVE_LENGTH + ) + ) + + CALCULATION_INTERVAL_SECONDS - + 1, + block.timestamp + uint256(MAX_FUTURE_LENGTH) + ); + startTimestamp = + startTimestamp - + (startTimestamp % CALCULATION_INTERVAL_SECONDS); + + // 2. Create reward submission input param + IRewardsCoordinator.RewardsSubmission[] + memory rewardsSubmissions = new IRewardsCoordinator.RewardsSubmission[]( + 1 + ); + rewardsSubmissions[0] = IRewardsCoordinator.RewardsSubmission({ + strategiesAndMultipliers: defaultStrategyAndMultipliers, + token: rewardToken, + amount: amount, + startTimestamp: uint32(startTimestamp), + duration: uint32(duration) + }); + + // 3. Approve serviceManager for ERC20 + cheats.startPrank(rewardsInitiator); + rewardToken.approve(address(serviceManager), amount); + + // 4. call createAVSRewardsSubmission() with expected event emitted + uint256 rewardsInitiatorBalanceBefore = rewardToken.balanceOf( + address(rewardsInitiator) + ); + uint256 rewardsCoordinatorBalanceBefore = rewardToken.balanceOf( + address(rewardsCoordinator) + ); + + rewardToken.approve(address(rewardsCoordinator), amount); + uint256 currSubmissionNonce = rewardsCoordinator.submissionNonce( + address(serviceManager) + ); + bytes32 avsSubmissionHash = keccak256( + abi.encode( + address(serviceManager), + currSubmissionNonce, + rewardsSubmissions[0] + ) + ); + + cheats.expectEmit(true, true, true, true, address(rewardsCoordinator)); + emit AVSRewardsSubmissionCreated( + address(serviceManager), + currSubmissionNonce, + avsSubmissionHash, + rewardsSubmissions[0] + ); + serviceManager.createAVSRewardsSubmission(rewardsSubmissions); + cheats.stopPrank(); + + assertTrue( + rewardsCoordinator.isAVSRewardsSubmissionHash( + address(serviceManager), + avsSubmissionHash + ), + "reward submission hash not submitted" + ); + assertEq( + currSubmissionNonce + 1, + rewardsCoordinator.submissionNonce(address(serviceManager)), + "submission nonce not incremented" + ); + assertEq( + rewardsInitiatorBalanceBefore - amount, + rewardToken.balanceOf(rewardsInitiator), + "rewardsInitiator balance not decremented by amount of reward submission" + ); + assertEq( + rewardsCoordinatorBalanceBefore + amount, + rewardToken.balanceOf(address(rewardsCoordinator)), + "RewardsCoordinator balance not incremented by amount of reward submission" + ); + } + + function test_createAVSRewardsSubmission_MultipleSubmissions( + uint256 startTimestamp, + uint256 duration, + uint256 amount, + uint256 numSubmissions + ) public { + cheats.assume(2 <= numSubmissions && numSubmissions <= 10); + cheats.prank(rewardsCoordinator.owner()); + + IRewardsCoordinator.RewardsSubmission[] + memory rewardsSubmissions = new IRewardsCoordinator.RewardsSubmission[]( + numSubmissions + ); + bytes32[] memory avsSubmissionHashes = new bytes32[](numSubmissions); + uint256 startSubmissionNonce = rewardsCoordinator.submissionNonce( + address(serviceManager) + ); + _deployMockRewardTokens(rewardsInitiator, numSubmissions); + + uint256[] memory avsBalancesBefore = _getBalanceForTokens( + rewardTokens, + rewardsInitiator + ); + uint256[] + memory rewardsCoordinatorBalancesBefore = _getBalanceForTokens( + rewardTokens, + address(rewardsCoordinator) + ); + uint256[] memory amounts = new uint256[](numSubmissions); + + // Create multiple rewards submissions and their expected event + for (uint256 i = 0; i < numSubmissions; ++i) { + // 1. Bound fuzz inputs to valid ranges and amounts using randSeed for each + amount = bound(amount + i, 1, MAX_REWARDS_AMOUNT); + amounts[i] = amount; + duration = bound(duration + i, 0, MAX_REWARDS_DURATION); + duration = duration - (duration % CALCULATION_INTERVAL_SECONDS); + startTimestamp = bound( + startTimestamp + i, + uint256( + _maxTimestamp( + GENESIS_REWARDS_TIMESTAMP, + uint32(block.timestamp) - MAX_RETROACTIVE_LENGTH + ) + ) + + CALCULATION_INTERVAL_SECONDS - + 1, + block.timestamp + uint256(MAX_FUTURE_LENGTH) + ); + startTimestamp = + startTimestamp - + (startTimestamp % CALCULATION_INTERVAL_SECONDS); + + // 2. Create reward submission input param + IRewardsCoordinator.RewardsSubmission + memory rewardsSubmission = IRewardsCoordinator + .RewardsSubmission({ + strategiesAndMultipliers: defaultStrategyAndMultipliers, + token: rewardTokens[i], + amount: amounts[i], + startTimestamp: uint32(startTimestamp), + duration: uint32(duration) + }); + rewardsSubmissions[i] = rewardsSubmission; + + // 3. expected event emitted for this rewardsSubmission + avsSubmissionHashes[i] = keccak256( + abi.encode( + address(serviceManager), + startSubmissionNonce + i, + rewardsSubmissions[i] + ) + ); + cheats.expectEmit( + true, + true, + true, + true, + address(rewardsCoordinator) + ); + emit AVSRewardsSubmissionCreated( + address(serviceManager), + startSubmissionNonce + i, + avsSubmissionHashes[i], + rewardsSubmissions[i] + ); + } + + // 4. call createAVSRewardsSubmission() + cheats.prank(rewardsInitiator); + serviceManager.createAVSRewardsSubmission(rewardsSubmissions); + + // 5. Check for submissionNonce() and avsSubmissionHashes being set + assertEq( + startSubmissionNonce + numSubmissions, + rewardsCoordinator.submissionNonce(address(serviceManager)), + "avs submission nonce not incremented properly" + ); + + for (uint256 i = 0; i < numSubmissions; ++i) { + assertTrue( + rewardsCoordinator.isAVSRewardsSubmissionHash( + address(serviceManager), + avsSubmissionHashes[i] + ), + "rewards submission hash not submitted" + ); + assertEq( + avsBalancesBefore[i] - amounts[i], + rewardTokens[i].balanceOf(rewardsInitiator), + "AVS balance not decremented by amount of rewards submission" + ); + assertEq( + rewardsCoordinatorBalancesBefore[i] + amounts[i], + rewardTokens[i].balanceOf(address(rewardsCoordinator)), + "RewardsCoordinator balance not incremented by amount of rewards submission" + ); + } + } + + function test_createAVSRewardsSubmission_MultipleSubmissionsSingleToken( + uint256 startTimestamp, + uint256 duration, + uint256 amount, + uint256 numSubmissions + ) public { + cheats.assume(2 <= numSubmissions && numSubmissions <= 10); + cheats.prank(rewardsCoordinator.owner()); + + IRewardsCoordinator.RewardsSubmission[] + memory rewardsSubmissions = new IRewardsCoordinator.RewardsSubmission[]( + numSubmissions + ); + bytes32[] memory avsSubmissionHashes = new bytes32[](numSubmissions); + uint256 startSubmissionNonce = rewardsCoordinator.submissionNonce( + address(serviceManager) + ); + IERC20 rewardToken = new ERC20PresetFixedSupply( + "dog wif hat", + "MOCK1", + mockTokenInitialSupply, + rewardsInitiator + ); + cheats.prank(rewardsInitiator); + rewardToken.approve(address(serviceManager), mockTokenInitialSupply); + uint256 avsBalanceBefore = rewardToken.balanceOf(rewardsInitiator); + uint256 rewardsCoordinatorBalanceBefore = rewardToken.balanceOf( + address(rewardsCoordinator) + ); + uint256 totalAmount = 0; + + uint256[] memory amounts = new uint256[](numSubmissions); + + // Create multiple rewards submissions and their expected event + for (uint256 i = 0; i < numSubmissions; ++i) { + // 1. Bound fuzz inputs to valid ranges and amounts using randSeed for each + amount = bound(amount + i, 1, MAX_REWARDS_AMOUNT); + amounts[i] = amount; + totalAmount += amount; + duration = bound(duration + i, 0, MAX_REWARDS_DURATION); + duration = duration - (duration % CALCULATION_INTERVAL_SECONDS); + startTimestamp = bound( + startTimestamp + i, + uint256( + _maxTimestamp( + GENESIS_REWARDS_TIMESTAMP, + uint32(block.timestamp) - MAX_RETROACTIVE_LENGTH + ) + ) + + CALCULATION_INTERVAL_SECONDS - + 1, + block.timestamp + uint256(MAX_FUTURE_LENGTH) + ); + startTimestamp = + startTimestamp - + (startTimestamp % CALCULATION_INTERVAL_SECONDS); + + // 2. Create reward submission input param + IRewardsCoordinator.RewardsSubmission + memory rewardsSubmission = IRewardsCoordinator + .RewardsSubmission({ + strategiesAndMultipliers: defaultStrategyAndMultipliers, + token: rewardToken, + amount: amounts[i], + startTimestamp: uint32(startTimestamp), + duration: uint32(duration) + }); + rewardsSubmissions[i] = rewardsSubmission; + + // 3. expected event emitted for this avs rewards submission + avsSubmissionHashes[i] = keccak256( + abi.encode( + address(serviceManager), + startSubmissionNonce + i, + rewardsSubmissions[i] + ) + ); + cheats.expectEmit( + true, + true, + true, + true, + address(rewardsCoordinator) + ); + emit AVSRewardsSubmissionCreated( + address(serviceManager), + startSubmissionNonce + i, + avsSubmissionHashes[i], + rewardsSubmissions[i] + ); + } + + // 4. call createAVSRewardsSubmission() + cheats.prank(rewardsInitiator); + serviceManager.createAVSRewardsSubmission(rewardsSubmissions); + + // 5. Check for submissionNonce() and avsSubmissionHashes being set + assertEq( + startSubmissionNonce + numSubmissions, + rewardsCoordinator.submissionNonce(address(serviceManager)), + "avs submission nonce not incremented properly" + ); + assertEq( + avsBalanceBefore - totalAmount, + rewardToken.balanceOf(rewardsInitiator), + "AVS balance not decremented by amount of rewards submissions" + ); + assertEq( + rewardsCoordinatorBalanceBefore + totalAmount, + rewardToken.balanceOf(address(rewardsCoordinator)), + "RewardsCoordinator balance not incremented by amount of rewards submissions" + ); + + for (uint256 i = 0; i < numSubmissions; ++i) { + assertTrue( + rewardsCoordinator.isAVSRewardsSubmissionHash( + address(serviceManager), + avsSubmissionHashes[i] + ), + "rewards submission hash not submitted" + ); + } + } + + function test_setRewardsInitiator() public { + address newRewardsInitiator = address( + uint160(uint256(keccak256("newRewardsInitiator"))) + ); + cheats.prank(serviceManagerOwner); + serviceManager.setRewardsInitiator(newRewardsInitiator); + assertEq(newRewardsInitiator, serviceManager.rewardsInitiator()); + } + + function test_setRewardsInitiator_revert_notOwner() public { + address caller = address(uint160(uint256(keccak256("caller")))); + address newRewardsInitiator = address( + uint160(uint256(keccak256("newRewardsInitiator"))) + ); + cheats.expectRevert("Ownable: caller is not the owner"); + cheats.prank(caller); + serviceManager.setRewardsInitiator(newRewardsInitiator); + } + + function testFuzz_setClaimerFor(address claimer) public { + cheats.startPrank(serviceManagerOwner); + cheats.expectEmit(true, true, true, true, address(rewardsCoordinator)); + emit ClaimerForSet( + address(serviceManager), + rewardsCoordinator.claimerFor(address(serviceManager)), + claimer + ); + serviceManager.setClaimerFor(claimer); + assertEq( + claimer, + rewardsCoordinator.claimerFor(address(serviceManager)), + "claimerFor not set" + ); + cheats.stopPrank(); + } + + function testFuzz_setClaimerFor_revert_notOwner( + address caller, + address claimer + ) public filterFuzzedAddressInputs(caller) { + cheats.assume(caller != serviceManagerOwner); + cheats.prank(caller); + cheats.expectRevert("Ownable: caller is not the owner"); + serviceManager.setClaimerFor(claimer); + } +} + +contract ServiceManagerBase_createOperatorDirectedAVSRewardsSubmission is + ServiceManagerBase_UnitTests +{ + // used for stack too deep + struct FuzzOperatorDirectedAVSRewardsSubmission { + uint256 startTimestamp; + uint256 duration; + } + + IRewardsCoordinator.OperatorReward[] defaultOperatorRewards; + + function setUp() public virtual override { + ServiceManagerBase_UnitTests.setUp(); + + address[] memory operators = new address[](3); + operators[0] = makeAddr("operator1"); + operators[1] = makeAddr("operator2"); + operators[2] = makeAddr("operator3"); + operators = _sortAddressArrayAsc(operators); + + defaultOperatorRewards.push( + IRewardsCoordinator.OperatorReward(operators[0], 1e18) + ); + defaultOperatorRewards.push( + IRewardsCoordinator.OperatorReward(operators[1], 2e18) + ); + defaultOperatorRewards.push( + IRewardsCoordinator.OperatorReward(operators[2], 3e18) + ); + + // Set the timestamp to when Rewards v2 will realisticly go out (i.e 6 months) + cheats.warp(GENESIS_REWARDS_TIMESTAMP + 168 days); + } + + /// @dev Sort to ensure that the array is in ascending order for addresses + function _sortAddressArrayAsc( + address[] memory arr + ) internal pure returns (address[] memory) { + uint256 l = arr.length; + for (uint256 i = 0; i < l; i++) { + for (uint256 j = i + 1; j < l; j++) { + if (arr[i] > arr[j]) { + address temp = arr[i]; + arr[i] = arr[j]; + arr[j] = temp; + } + } + } + return arr; + } + + function _getTotalRewardsAmount( + IRewardsCoordinator.OperatorReward[] memory operatorRewards + ) internal pure returns (uint256) { + uint256 totalAmount = 0; + for (uint256 i = 0; i < operatorRewards.length; ++i) { + totalAmount += operatorRewards[i].amount; + } + return totalAmount; + } + + function testFuzz_createOperatorDirectedAVSRewardsSubmission_Revert_WhenNotOwner( + address caller + ) public filterFuzzedAddressInputs(caller) { + cheats.assume(caller != rewardsInitiator); + IRewardsCoordinator.OperatorDirectedRewardsSubmission[] + memory operatorDirectedRewardsSubmissions; + + cheats.prank(caller); + cheats.expectRevert( + "ServiceManagerBase.onlyRewardsInitiator: caller is not the rewards initiator" + ); + serviceManager.createOperatorDirectedAVSRewardsSubmission( + operatorDirectedRewardsSubmissions + ); + } + + function testFuzz_createOperatorDirectedAVSRewardsSubmission_Revert_WhenERC20NotApproved( + uint256 startTimestamp, + uint256 duration + ) public { + // 1. Bound fuzz inputs to valid ranges and amounts + IERC20 rewardToken = new ERC20PresetFixedSupply( + "dog wif hat", + "MOCK1", + mockTokenInitialSupply, + rewardsInitiator + ); + duration = bound(duration, 0, MAX_REWARDS_DURATION); + duration = duration - (duration % CALCULATION_INTERVAL_SECONDS); + startTimestamp = bound( + startTimestamp, + uint256( + _maxTimestamp( + GENESIS_REWARDS_TIMESTAMP, + uint32(block.timestamp) - MAX_RETROACTIVE_LENGTH + ) + ) + + CALCULATION_INTERVAL_SECONDS - + 1, + block.timestamp - duration - 1 + ); + startTimestamp = + startTimestamp - + (startTimestamp % CALCULATION_INTERVAL_SECONDS); + + // 2. Create operator directed rewards submission input param + IRewardsCoordinator.OperatorDirectedRewardsSubmission[] + memory operatorDirectedRewardsSubmissions = new IRewardsCoordinator.OperatorDirectedRewardsSubmission[]( + 1 + ); + operatorDirectedRewardsSubmissions[0] = IRewardsCoordinator + .OperatorDirectedRewardsSubmission({ + strategiesAndMultipliers: defaultStrategyAndMultipliers, + token: rewardToken, + operatorRewards: defaultOperatorRewards, + startTimestamp: uint32(startTimestamp), + duration: uint32(duration), + description: "" + }); + + // 3. Call createOperatorDirectedAVSRewardsSubmission() + cheats.prank(rewardsInitiator); + cheats.expectRevert("ERC20: insufficient allowance"); + serviceManager.createOperatorDirectedAVSRewardsSubmission( + operatorDirectedRewardsSubmissions + ); + } + + /** + * @notice test a single rewards submission asserting for the following + * - correct event emitted + * - submission nonce incrementation by 1, and rewards submission hash being set in storage. + * - rewards submission hash being set in storage + * - token balance before and after of rewards initiator and rewardsCoordinator + */ + function testFuzz_createOperatorDirectedAVSRewardsSubmission_SingleSubmission( + uint256 startTimestamp, + uint256 duration + ) public { + // 1. Bound fuzz inputs to valid ranges and amounts + IERC20 rewardToken = new ERC20PresetFixedSupply( + "dog wif hat", + "MOCK1", + mockTokenInitialSupply, + rewardsInitiator + ); + duration = bound(duration, 0, MAX_REWARDS_DURATION); + duration = duration - (duration % CALCULATION_INTERVAL_SECONDS); + startTimestamp = bound( + startTimestamp, + uint256( + _maxTimestamp( + GENESIS_REWARDS_TIMESTAMP, + uint32(block.timestamp) - MAX_RETROACTIVE_LENGTH + ) + ) + + CALCULATION_INTERVAL_SECONDS - + 1, + block.timestamp - duration - 1 + ); + startTimestamp = + startTimestamp - + (startTimestamp % CALCULATION_INTERVAL_SECONDS); + + // 2. Create operator directed rewards submission input param + IRewardsCoordinator.OperatorDirectedRewardsSubmission[] + memory operatorDirectedRewardsSubmissions = new IRewardsCoordinator.OperatorDirectedRewardsSubmission[]( + 1 + ); + operatorDirectedRewardsSubmissions[0] = IRewardsCoordinator + .OperatorDirectedRewardsSubmission({ + strategiesAndMultipliers: defaultStrategyAndMultipliers, + token: rewardToken, + operatorRewards: defaultOperatorRewards, + startTimestamp: uint32(startTimestamp), + duration: uint32(duration), + description: "" + }); + + // 3. Get total amount + uint256 amount = _getTotalRewardsAmount(defaultOperatorRewards); + + // 4. Approve serviceManager for ERC20 + cheats.startPrank(rewardsInitiator); + rewardToken.approve(address(serviceManager), amount); + + // 3. call createOperatorDirectedAVSRewardsSubmission() with expected event emitted + uint256 rewardsInitiatorBalanceBefore = rewardToken.balanceOf( + rewardsInitiator + ); + uint256 rewardsCoordinatorBalanceBefore = rewardToken.balanceOf( + address(rewardsCoordinator) + ); + uint256 currSubmissionNonce = rewardsCoordinator.submissionNonce( + address(serviceManager) + ); + bytes32 rewardsSubmissionHash = keccak256( + abi.encode( + address(serviceManager), + currSubmissionNonce, + operatorDirectedRewardsSubmissions[0] + ) + ); + cheats.expectEmit(true, true, true, true, address(rewardsCoordinator)); + emit OperatorDirectedAVSRewardsSubmissionCreated( + address(serviceManager), + address(serviceManager), + rewardsSubmissionHash, + currSubmissionNonce, + operatorDirectedRewardsSubmissions[0] + ); + serviceManager.createOperatorDirectedAVSRewardsSubmission( + operatorDirectedRewardsSubmissions + ); + cheats.stopPrank(); + + assertTrue( + rewardsCoordinator.isOperatorDirectedAVSRewardsSubmissionHash( + address(serviceManager), + rewardsSubmissionHash + ), + "rewards submission hash not submitted" + ); + assertEq( + currSubmissionNonce + 1, + rewardsCoordinator.submissionNonce(address(serviceManager)), + "submission nonce not incremented" + ); + assertEq( + rewardsInitiatorBalanceBefore - amount, + rewardToken.balanceOf(rewardsInitiator), + "rewardsInitiator balance not decremented by amount of rewards submission" + ); + assertEq( + rewardsCoordinatorBalanceBefore + amount, + rewardToken.balanceOf(address(rewardsCoordinator)), + "RewardsCoordinator balance not incremented by amount of rewards submission" + ); + } + + /** + * @notice test a multiple rewards submission asserting for the following + * - correct event emitted + * - submission nonce incrementation by 1, and rewards submission hash being set in storage. + * - rewards submission hash being set in storage + * - token balance before and after of rewards initiator and rewardsCoordinator + */ + function testFuzz_createOperatorDirectedAVSRewardsSubmission_MultipleSubmissions( + FuzzOperatorDirectedAVSRewardsSubmission memory param, + uint256 numSubmissions + ) public { + cheats.assume(2 <= numSubmissions && numSubmissions <= 10); + cheats.prank(rewardsCoordinator.owner()); + + IRewardsCoordinator.OperatorDirectedRewardsSubmission[] + memory rewardsSubmissions = new IRewardsCoordinator.OperatorDirectedRewardsSubmission[]( + numSubmissions + ); + bytes32[] memory rewardsSubmissionHashes = new bytes32[]( + numSubmissions + ); + uint256 startSubmissionNonce = rewardsCoordinator.submissionNonce( + address(serviceManager) + ); + _deployMockRewardTokens(rewardsInitiator, numSubmissions); + + uint256[] memory rewardsInitiatorBalancesBefore = _getBalanceForTokens( + rewardTokens, + rewardsInitiator + ); + uint256[] + memory rewardsCoordinatorBalancesBefore = _getBalanceForTokens( + rewardTokens, + address(rewardsCoordinator) + ); + uint256[] memory amounts = new uint256[](numSubmissions); + + // Create multiple rewards submissions and their expected event + for (uint256 i = 0; i < numSubmissions; ++i) { + // 1. Bound fuzz inputs to valid ranges and amounts using randSeed for each + amounts[i] = _getTotalRewardsAmount(defaultOperatorRewards); + param.duration = bound(param.duration, 0, MAX_REWARDS_DURATION); + param.duration = + param.duration - + (param.duration % CALCULATION_INTERVAL_SECONDS); + param.startTimestamp = bound( + param.startTimestamp + i, + uint256( + _maxTimestamp( + GENESIS_REWARDS_TIMESTAMP, + uint32(block.timestamp) - MAX_RETROACTIVE_LENGTH + ) + ) + + CALCULATION_INTERVAL_SECONDS - + 1, + block.timestamp + uint256(MAX_FUTURE_LENGTH) + ); + param.startTimestamp = + param.startTimestamp - + (param.startTimestamp % CALCULATION_INTERVAL_SECONDS); + + param.duration = bound(param.duration, 0, MAX_REWARDS_DURATION); + param.duration = + param.duration - + (param.duration % CALCULATION_INTERVAL_SECONDS); + param.startTimestamp = bound( + param.startTimestamp, + uint256( + _maxTimestamp( + GENESIS_REWARDS_TIMESTAMP, + uint32(block.timestamp) - MAX_RETROACTIVE_LENGTH + ) + ) + + CALCULATION_INTERVAL_SECONDS - + 1, + block.timestamp - param.duration - 1 + ); + param.startTimestamp = + param.startTimestamp - + (param.startTimestamp % CALCULATION_INTERVAL_SECONDS); + + // 2. Create rewards submission input param + IRewardsCoordinator.OperatorDirectedRewardsSubmission + memory rewardsSubmission = IRewardsCoordinator + .OperatorDirectedRewardsSubmission({ + strategiesAndMultipliers: defaultStrategyAndMultipliers, + token: rewardTokens[i], + operatorRewards: defaultOperatorRewards, + startTimestamp: uint32(param.startTimestamp), + duration: uint32(param.duration), + description: "" + }); + rewardsSubmissions[i] = rewardsSubmission; + + // 3. expected event emitted for this rewardsSubmission + rewardsSubmissionHashes[i] = keccak256( + abi.encode( + address(serviceManager), + startSubmissionNonce + i, + rewardsSubmissions[i] + ) + ); + cheats.expectEmit( + true, + true, + true, + true, + address(rewardsCoordinator) + ); + emit OperatorDirectedAVSRewardsSubmissionCreated( + address(serviceManager), + address(serviceManager), + rewardsSubmissionHashes[i], + startSubmissionNonce + i, + rewardsSubmissions[i] + ); + } + + // 4. call createAVSRewardsSubmission() + cheats.prank(rewardsInitiator); + serviceManager.createOperatorDirectedAVSRewardsSubmission( + rewardsSubmissions + ); + + // 5. Check for submissionNonce() and rewardsSubmissionHashes being set + assertEq( + startSubmissionNonce + numSubmissions, + rewardsCoordinator.submissionNonce(address(serviceManager)), + "submission nonce not incremented properly" + ); + + for (uint256 i = 0; i < numSubmissions; ++i) { + assertTrue( + rewardsCoordinator.isOperatorDirectedAVSRewardsSubmissionHash( + address(serviceManager), + rewardsSubmissionHashes[i] + ), + "rewards submission hash not submitted" + ); + assertEq( + rewardsInitiatorBalancesBefore[i] - amounts[i], + rewardTokens[i].balanceOf(rewardsInitiator), + "rewardsInitiator balance not decremented by amount of rewards submission" + ); + assertEq( + rewardsCoordinatorBalancesBefore[i] + amounts[i], + rewardTokens[i].balanceOf(address(rewardsCoordinator)), + "RewardsCoordinator balance not incremented by amount of rewards submission" + ); + } + } +} diff --git a/test/unit/ServiceManagerRouter.t.sol b/test/unit/ServiceManagerRouter.t.sol index 0064fcb1..9fc2c0f7 100644 --- a/test/unit/ServiceManagerRouter.t.sol +++ b/test/unit/ServiceManagerRouter.t.sol @@ -17,6 +17,7 @@ contract ServiceManagerRouter_UnitTests is MockAVSDeployer { // Deploy dummy serviceManager dummyServiceManager = new ServiceManagerMock( avsDirectory, + rewardsCoordinatorImplementation, registryCoordinatorImplementation, stakeRegistryImplementation ); diff --git a/test/unit/SocketRegistryUnit.t.sol b/test/unit/SocketRegistryUnit.t.sol new file mode 100644 index 00000000..af5be131 --- /dev/null +++ b/test/unit/SocketRegistryUnit.t.sol @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.12; + +import {SocketRegistry} from "../../src/SocketRegistry.sol"; +import {IRegistryCoordinator} from "../../src/interfaces/IRegistryCoordinator.sol"; +import "../utils/MockAVSDeployer.sol"; + +contract SocketRegistryUnitTests is MockAVSDeployer { + + function setUp() virtual public { + _deployMockEigenLayerAndAVS(); + } + + function test_setOperatorSocket() public { + vm.startPrank(address(registryCoordinator)); + socketRegistry.setOperatorSocket(defaultOperatorId, "testSocket"); + assertEq(socketRegistry.getOperatorSocket(defaultOperatorId), "testSocket"); + } + + function test_setOperatorSocket_revert_notRegistryCoordinator() public { + vm.startPrank(address(0)); + vm.expectRevert("SocketRegistry.onlyRegistryCoordinator: caller is not the RegistryCoordinator"); + socketRegistry.setOperatorSocket(defaultOperatorId, "testSocket"); + } + +} \ No newline at end of file diff --git a/test/unit/StakeRegistryUnit.t.sol b/test/unit/StakeRegistryUnit.t.sol index bc9d46f6..52769f78 100644 --- a/test/unit/StakeRegistryUnit.t.sol +++ b/test/unit/StakeRegistryUnit.t.sol @@ -38,7 +38,7 @@ contract StakeRegistryUnitTests is MockAVSDeployer, IStakeRegistryEvents { _; } - function setUp() virtual public { + function setUp() public virtual { // Deploy contracts but with 0 quorums initialized, will initializeQuorums afterwards _deployMockEigenLayerAndAVS(0); @@ -48,40 +48,40 @@ contract StakeRegistryUnitTests is MockAVSDeployer, IStakeRegistryEvents { serviceManager, stakeRegistry, IBLSApkRegistry(blsApkRegistry), - IIndexRegistry(indexRegistry) + IIndexRegistry(indexRegistry), + socketRegistry ); stakeRegistryImplementation = new StakeRegistryHarness( - IRegistryCoordinator(address(registryCoordinator)), - delegationMock + IRegistryCoordinator(address(registryCoordinator)), delegationMock ); stakeRegistry = StakeRegistryHarness( address( new TransparentUpgradeableProxy( - address(stakeRegistryImplementation), - address(proxyAdmin), - "" + address(stakeRegistryImplementation), address(proxyAdmin), "" ) ) ); cheats.stopPrank(); // Initialize several quorums with varying minimum stakes - _initializeQuorum({ minimumStake: uint96(type(uint16).max) }); - _initializeQuorum({ minimumStake: uint96(type(uint24).max) }); - _initializeQuorum({ minimumStake: uint96(type(uint32).max) }); - _initializeQuorum({ minimumStake: uint96(type(uint64).max) }); + _initializeQuorum({minimumStake: uint96(type(uint16).max)}); + _initializeQuorum({minimumStake: uint96(type(uint24).max)}); + _initializeQuorum({minimumStake: uint96(type(uint32).max)}); + _initializeQuorum({minimumStake: uint96(type(uint64).max)}); - _initializeQuorum({ minimumStake: uint96(type(uint16).max) + 1 }); - _initializeQuorum({ minimumStake: uint96(type(uint24).max) + 1 }); - _initializeQuorum({ minimumStake: uint96(type(uint32).max) + 1 }); - _initializeQuorum({ minimumStake: uint96(type(uint64).max) + 1 }); + _initializeQuorum({minimumStake: uint96(type(uint16).max) + 1}); + _initializeQuorum({minimumStake: uint96(type(uint24).max) + 1}); + _initializeQuorum({minimumStake: uint96(type(uint32).max) + 1}); + _initializeQuorum({minimumStake: uint96(type(uint64).max) + 1}); } - /******************************************************************************* - initializers - *******************************************************************************/ + /** + * + * initializers + * + */ /** * @dev Initialize a new quorum with `minimumStake` @@ -91,7 +91,7 @@ contract StakeRegistryUnitTests is MockAVSDeployer, IStakeRegistryEvents { uint8 quorumNumber = nextQuorum; IStakeRegistry.StrategyParams[] memory strategyParams = - new IStakeRegistry.StrategyParams[](1); + new IStakeRegistry.StrategyParams[](1); strategyParams[0] = IStakeRegistry.StrategyParams( IStrategy(address(uint160(uint256(keccak256(abi.encodePacked(quorumNumber)))))), uint96(WEIGHTING_DIVISOR) @@ -107,15 +107,16 @@ contract StakeRegistryUnitTests is MockAVSDeployer, IStakeRegistryEvents { initializedQuorumBytes = initializedQuorumBitmap.bitmapToBytesArray(); } - /** + /** * @dev Initialize a new quorum with `minimumStake` and `numStrats` - * Create `numStrats` dummy strategies with multiplier of 1 for each. + * Create `numStrats` dummy strategies with multiplier of 1 for each. * Returns quorumNumber that was just initialized */ function _initializeQuorum(uint96 minimumStake, uint256 numStrats) internal returns (uint8) { uint8 quorumNumber = nextQuorum; - IStakeRegistry.StrategyParams[] memory strategyParams = new IStakeRegistry.StrategyParams[](numStrats); + IStakeRegistry.StrategyParams[] memory strategyParams = + new IStakeRegistry.StrategyParams[](numStrats); for (uint256 i = 0; i < strategyParams.length; i++) { strategyParams[i] = IStakeRegistry.StrategyParams( IStrategy(address(uint160(uint256(keccak256(abi.encodePacked(quorumNumber, i)))))), @@ -145,10 +146,11 @@ contract StakeRegistryUnitTests is MockAVSDeployer, IStakeRegistryEvents { return (operator, operatorId); } - /******************************************************************************* - test setup methods - *******************************************************************************/ - + /** + * + * test setup methods + * + */ struct RegisterSetup { address operator; bytes32 operatorId; @@ -162,20 +164,25 @@ contract StakeRegistryUnitTests is MockAVSDeployer, IStakeRegistryEvents { /// @dev Utility function set up a new operator to be registered for some quorums /// The operator's weight is set to the quorum's minimum, plus fuzzy_addtlStake (overflows are skipped) /// This function guarantees at least one quorum, and any quorums returned are initialized - function _fuzz_setupRegisterOperator(uint192 fuzzy_Bitmap, uint16 fuzzy_addtlStake) internal returns (RegisterSetup memory) { + function _fuzz_setupRegisterOperator( + uint192 fuzzy_Bitmap, + uint16 fuzzy_addtlStake + ) internal returns (RegisterSetup memory) { // Select an unused operator to register (address operator, bytes32 operatorId) = _selectNewOperator(); - + // Pick quorums to register for and get each quorum's minimum stake - ( , bytes memory quorumNumbers) = _fuzz_getQuorums(fuzzy_Bitmap); + (, bytes memory quorumNumbers) = _fuzz_getQuorums(fuzzy_Bitmap); uint96[] memory minimumStakes = _getMinimumStakes(quorumNumbers); // For each quorum, set the operator's weight as the minimum + addtlStake uint96[] memory operatorWeights = new uint96[](quorumNumbers.length); - for (uint i = 0; i < quorumNumbers.length; i++) { + for (uint256 i = 0; i < quorumNumbers.length; i++) { uint8 quorumNumber = uint8(quorumNumbers[i]); - unchecked { operatorWeights[i] = minimumStakes[i] + fuzzy_addtlStake; } + unchecked { + operatorWeights[i] = minimumStakes[i] + fuzzy_addtlStake; + } cheats.assume(operatorWeights[i] >= minimumStakes[i]); cheats.assume(operatorWeights[i] >= fuzzy_addtlStake); @@ -183,11 +190,13 @@ contract StakeRegistryUnitTests is MockAVSDeployer, IStakeRegistryEvents { } /// Get starting state - IStakeRegistry.StakeUpdate[] memory prevOperatorStakes = _getLatestStakeUpdates(operatorId, quorumNumbers); - IStakeRegistry.StakeUpdate[] memory prevTotalStakes = _getLatestTotalStakeUpdates(quorumNumbers); + IStakeRegistry.StakeUpdate[] memory prevOperatorStakes = + _getLatestStakeUpdates(operatorId, quorumNumbers); + IStakeRegistry.StakeUpdate[] memory prevTotalStakes = + _getLatestTotalStakeUpdates(quorumNumbers); // Ensure that the operator has not previously registered - for (uint i = 0; i < quorumNumbers.length; i++) { + for (uint256 i = 0; i < quorumNumbers.length; i++) { assertTrue(prevOperatorStakes[i].updateBlockNumber == 0, "operator already registered"); assertTrue(prevOperatorStakes[i].stake == 0, "operator already has stake"); } @@ -203,10 +212,14 @@ contract StakeRegistryUnitTests is MockAVSDeployer, IStakeRegistryEvents { }); } - function _fuzz_setupRegisterOperators(uint192 fuzzy_Bitmap, uint16 fuzzy_addtlStake, uint numOperators) internal returns (RegisterSetup[] memory) { + function _fuzz_setupRegisterOperators( + uint192 fuzzy_Bitmap, + uint16 fuzzy_addtlStake, + uint256 numOperators + ) internal returns (RegisterSetup[] memory) { RegisterSetup[] memory setups = new RegisterSetup[](numOperators); - for (uint i = 0; i < numOperators; i++) { + for (uint256 i = 0; i < numOperators; i++) { setups[i] = _fuzz_setupRegisterOperator(fuzzy_Bitmap, fuzzy_addtlStake); } @@ -229,21 +242,27 @@ contract StakeRegistryUnitTests is MockAVSDeployer, IStakeRegistryEvents { /// The operator's weight is set to the quorum's minimum, plus fuzzy_addtlStake (overflows are skipped) /// This function guarantees at least one quorum, and any quorums returned are initialized function _fuzz_setupDeregisterOperator( - uint192 registeredFor, - uint192 fuzzy_toRemove, + uint192 registeredFor, + uint192 fuzzy_toRemove, uint16 fuzzy_addtlStake ) internal returns (DeregisterSetup memory) { - RegisterSetup memory registerSetup = _fuzz_setupRegisterOperator(registeredFor, fuzzy_addtlStake); + RegisterSetup memory registerSetup = + _fuzz_setupRegisterOperator(registeredFor, fuzzy_addtlStake); // registerOperator cheats.prank(address(registryCoordinator)); - stakeRegistry.registerOperator(registerSetup.operator, registerSetup.operatorId, registerSetup.quorumNumbers); + stakeRegistry.registerOperator( + registerSetup.operator, registerSetup.operatorId, registerSetup.quorumNumbers + ); // Get state after registering: - IStakeRegistry.StakeUpdate[] memory operatorStakes = _getLatestStakeUpdates(registerSetup.operatorId, registerSetup.quorumNumbers); - IStakeRegistry.StakeUpdate[] memory totalStakes = _getLatestTotalStakeUpdates(registerSetup.quorumNumbers); - - (uint192 quorumsToRemoveBitmap, bytes memory quorumsToRemove) = _fuzz_getQuorums(fuzzy_toRemove); + IStakeRegistry.StakeUpdate[] memory operatorStakes = + _getLatestStakeUpdates(registerSetup.operatorId, registerSetup.quorumNumbers); + IStakeRegistry.StakeUpdate[] memory totalStakes = + _getLatestTotalStakeUpdates(registerSetup.quorumNumbers); + + (uint192 quorumsToRemoveBitmap, bytes memory quorumsToRemove) = + _fuzz_getQuorums(fuzzy_toRemove); return DeregisterSetup({ operator: registerSetup.operator, @@ -257,15 +276,16 @@ contract StakeRegistryUnitTests is MockAVSDeployer, IStakeRegistryEvents { } function _fuzz_setupDeregisterOperators( - uint192 registeredFor, - uint192 fuzzy_toRemove, - uint16 fuzzy_addtlStake, - uint numOperators + uint192 registeredFor, + uint192 fuzzy_toRemove, + uint16 fuzzy_addtlStake, + uint256 numOperators ) internal returns (DeregisterSetup[] memory) { DeregisterSetup[] memory setups = new DeregisterSetup[](numOperators); - for (uint i = 0; i < numOperators; i++) { - setups[i] = _fuzz_setupDeregisterOperator(registeredFor, fuzzy_toRemove, fuzzy_addtlStake); + for (uint256 i = 0; i < numOperators; i++) { + setups[i] = + _fuzz_setupDeregisterOperator(registeredFor, fuzzy_toRemove, fuzzy_addtlStake); } return setups; @@ -286,37 +306,52 @@ contract StakeRegistryUnitTests is MockAVSDeployer, IStakeRegistryEvents { /// After registering, and before returning, `fuzzy_Delta` is applied to the operator's weight /// to place the operator's weight above or below the minimum stake. (or unchanged!) /// The next time `updateOperatorStake` is called, this new weight will be used. - function _fuzz_setupUpdateOperatorStake(uint192 registeredFor, int8 fuzzy_Delta) internal returns (UpdateSetup memory) { + function _fuzz_setupUpdateOperatorStake( + uint192 registeredFor, + int8 fuzzy_Delta + ) internal returns (UpdateSetup memory) { RegisterSetup memory registerSetup = _fuzz_setupRegisterOperator(registeredFor, 0); // registerOperator cheats.prank(address(registryCoordinator)); - stakeRegistry.registerOperator(registerSetup.operator, registerSetup.operatorId, registerSetup.quorumNumbers); + stakeRegistry.registerOperator( + registerSetup.operator, registerSetup.operatorId, registerSetup.quorumNumbers + ); uint96[] memory minimumStakes = _getMinimumStakes(registerSetup.quorumNumbers); uint96[] memory endingWeights = new uint96[](minimumStakes.length); - for (uint i = 0; i < minimumStakes.length; i++) { + for (uint256 i = 0; i < minimumStakes.length; i++) { uint8 quorumNumber = uint8(registerSetup.quorumNumbers[i]); endingWeights[i] = _applyDelta(minimumStakes[i], int256(fuzzy_Delta)); // Sanity-check setup: if (fuzzy_Delta > 0) { - assertGt(endingWeights[i], minimumStakes[i], "_fuzz_setupUpdateOperatorStake: overflow during setup"); + assertGt( + endingWeights[i], + minimumStakes[i], + "_fuzz_setupUpdateOperatorStake: overflow during setup" + ); } else if (fuzzy_Delta < 0) { - assertLt(endingWeights[i], minimumStakes[i], "_fuzz_setupUpdateOperatorStake: underflow during setup"); + assertLt( + endingWeights[i], + minimumStakes[i], + "_fuzz_setupUpdateOperatorStake: underflow during setup" + ); } else { - assertEq(endingWeights[i], minimumStakes[i], "_fuzz_setupUpdateOperatorStake: invalid delta during setup"); + assertEq( + endingWeights[i], + minimumStakes[i], + "_fuzz_setupUpdateOperatorStake: invalid delta during setup" + ); } // Set operator weights. The next time we call `updateOperatorStake`, these new weights will be used _setOperatorWeight(registerSetup.operator, quorumNumber, endingWeights[i]); } - uint96 stakeDeltaAbs = - fuzzy_Delta < 0 ? - uint96(-int96(fuzzy_Delta)) : - uint96(int96(fuzzy_Delta)); + uint96 stakeDeltaAbs = + fuzzy_Delta < 0 ? uint96(-int96(fuzzy_Delta)) : uint96(int96(fuzzy_Delta)); return UpdateSetup({ operator: registerSetup.operator, @@ -328,19 +363,25 @@ contract StakeRegistryUnitTests is MockAVSDeployer, IStakeRegistryEvents { }); } - function _fuzz_setupUpdateOperatorStakes(uint8 numOperators, uint192 registeredFor, int8 fuzzy_Delta) internal returns (UpdateSetup[] memory) { + function _fuzz_setupUpdateOperatorStakes( + uint8 numOperators, + uint192 registeredFor, + int8 fuzzy_Delta + ) internal returns (UpdateSetup[] memory) { UpdateSetup[] memory setups = new UpdateSetup[](numOperators); - for (uint i = 0; i < numOperators; i++) { + for (uint256 i = 0; i < numOperators; i++) { setups[i] = _fuzz_setupUpdateOperatorStake(registeredFor, fuzzy_Delta); } return setups; } - /******************************************************************************* - helpful getters - *******************************************************************************/ + /** + * + * helpful getters + * + */ /// @notice Given a fuzzed bitmap input, returns a bitmap and array of quorum numbers /// that are guaranteed to be initialized. @@ -355,7 +396,7 @@ contract StakeRegistryUnitTests is MockAVSDeployer, IStakeRegistryEvents { /// @param rand is used to determine how many legitimate quorums to insert, so we can /// check this works for lists of varying lengths function _fuzz_getInvalidQuorums(bytes32 rand) internal returns (bytes memory) { - uint length = _randUint({ rand: rand, min: 1, max: initializedQuorumBytes.length + 1 }); + uint256 length = _randUint({rand: rand, min: 1, max: initializedQuorumBytes.length + 1}); bytes memory invalidQuorums = new bytes(length); // Create an invalid quorum number by incrementing the last initialized quorum @@ -364,7 +405,9 @@ contract StakeRegistryUnitTests is MockAVSDeployer, IStakeRegistryEvents { // Select real quorums up to the length, then insert an invalid quorum for (uint8 quorum = 0; quorum < length - 1; quorum++) { // sanity check test setup - assertTrue(initializedQuorumBitmap.isSet(quorum), "_fuzz_getInvalidQuorums: invalid quorum"); + assertTrue( + initializedQuorumBitmap.isSet(quorum), "_fuzz_getInvalidQuorums: invalid quorum" + ); invalidQuorums[quorum] = bytes1(quorum); } @@ -374,21 +417,24 @@ contract StakeRegistryUnitTests is MockAVSDeployer, IStakeRegistryEvents { /// @notice Returns true iff two StakeUpdates are identical function _isUnchanged( - IStakeRegistry.StakeUpdate memory prev, + IStakeRegistry.StakeUpdate memory prev, IStakeRegistry.StakeUpdate memory cur ) internal pure returns (bool) { return ( - prev.stake == cur.stake && - prev.updateBlockNumber == cur.updateBlockNumber && - prev.nextUpdateBlockNumber == cur.nextUpdateBlockNumber + prev.stake == cur.stake && prev.updateBlockNumber == cur.updateBlockNumber + && prev.nextUpdateBlockNumber == cur.nextUpdateBlockNumber ); } /// @dev Return the minimum stakes required for a list of quorums - function _getMinimumStakes(bytes memory quorumNumbers) internal view returns (uint96[] memory) { + function _getMinimumStakes(bytes memory quorumNumbers) + internal + view + returns (uint96[] memory) + { uint96[] memory minimumStakes = new uint96[](quorumNumbers.length); - - for (uint i = 0; i < quorumNumbers.length; i++) { + + for (uint256 i = 0; i < quorumNumbers.length; i++) { uint8 quorumNumber = uint8(quorumNumbers[i]); minimumStakes[i] = stakeRegistry.minimumStakeForQuorum(quorumNumber); } @@ -398,13 +444,13 @@ contract StakeRegistryUnitTests is MockAVSDeployer, IStakeRegistryEvents { /// @dev Return the most recent stake update history entries for an operator function _getLatestStakeUpdates( - bytes32 operatorId, + bytes32 operatorId, bytes memory quorumNumbers ) internal view returns (IStakeRegistry.StakeUpdate[] memory) { - IStakeRegistry.StakeUpdate[] memory stakeUpdates = + IStakeRegistry.StakeUpdate[] memory stakeUpdates = new IStakeRegistry.StakeUpdate[](quorumNumbers.length); - - for (uint i = 0; i < quorumNumbers.length; i++) { + + for (uint256 i = 0; i < quorumNumbers.length; i++) { uint8 quorumNumber = uint8(quorumNumbers[i]); stakeUpdates[i] = stakeRegistry.getLatestStakeUpdate(operatorId, quorumNumber); } @@ -413,17 +459,20 @@ contract StakeRegistryUnitTests is MockAVSDeployer, IStakeRegistryEvents { } /// @dev Return the most recent total stake update history entries - function _getLatestTotalStakeUpdates( - bytes memory quorumNumbers - ) internal view returns (IStakeRegistry.StakeUpdate[] memory) { - IStakeRegistry.StakeUpdate[] memory stakeUpdates = + function _getLatestTotalStakeUpdates(bytes memory quorumNumbers) + internal + view + returns (IStakeRegistry.StakeUpdate[] memory) + { + IStakeRegistry.StakeUpdate[] memory stakeUpdates = new IStakeRegistry.StakeUpdate[](quorumNumbers.length); - - for (uint i = 0; i < quorumNumbers.length; i++) { + + for (uint256 i = 0; i < quorumNumbers.length; i++) { uint8 quorumNumber = uint8(quorumNumbers[i]); - uint historyLength = stakeRegistry.getTotalStakeHistoryLength(quorumNumber); - stakeUpdates[i] = stakeRegistry.getTotalStakeUpdateAtIndex(quorumNumber, historyLength - 1); + uint256 historyLength = stakeRegistry.getTotalStakeHistoryLength(quorumNumber); + stakeUpdates[i] = + stakeRegistry.getTotalStakeUpdateAtIndex(quorumNumber, historyLength - 1); } return stakeUpdates; @@ -439,16 +488,19 @@ contract StakeRegistryUnitTests is MockAVSDeployer, IStakeRegistryEvents { for (uint256 i = 0; i < quorumNumbers.length; i++) { uint8 quorumNumber = uint8(quorumNumbers[i]); - operatorStakeHistoryLengths[i] = stakeRegistry.getStakeHistoryLength(operatorId, quorumNumber); + operatorStakeHistoryLengths[i] = + stakeRegistry.getStakeHistoryLength(operatorId, quorumNumber); } return operatorStakeHistoryLengths; } /// @dev Return the lengths of the total stake update history - function _getTotalStakeHistoryLengths( - bytes memory quorumNumbers - ) internal view returns (uint256[] memory) { + function _getTotalStakeHistoryLengths(bytes memory quorumNumbers) + internal + view + returns (uint256[] memory) + { uint256[] memory historyLengths = new uint256[](quorumNumbers.length); for (uint256 i = 0; i < quorumNumbers.length; i++) { @@ -461,30 +513,24 @@ contract StakeRegistryUnitTests is MockAVSDeployer, IStakeRegistryEvents { } function _calculateDelta(uint96 prev, uint96 cur) internal view returns (int256) { - return stakeRegistry.calculateDelta({ - prev: prev, - cur: cur - }); + return stakeRegistry.calculateDelta({prev: prev, cur: cur}); } function _applyDelta(uint96 value, int256 delta) internal view returns (uint96) { - return stakeRegistry.applyDelta({ - value: value, - delta: delta - }); + return stakeRegistry.applyDelta({value: value, delta: delta}); } /// @dev Uses `rand` to return a random uint, with a range given by `min` and `max` (inclusive) /// @return `min` <= result <= `max` - function _randUint(bytes32 rand, uint min, uint max) internal pure returns (uint) { + function _randUint(bytes32 rand, uint256 min, uint256 max) internal pure returns (uint256) { // hashing makes for more uniform randomness rand = keccak256(abi.encodePacked(rand)); - - uint range = max - min + 1; + + uint256 range = max - min + 1; // calculate the number of bits needed for the range - uint bitsNeeded = 0; - uint tempRange = range; + uint256 bitsNeeded = 0; + uint256 tempRange = range; while (tempRange > 0) { bitsNeeded++; tempRange >>= 1; @@ -492,8 +538,8 @@ contract StakeRegistryUnitTests is MockAVSDeployer, IStakeRegistryEvents { // create a mask for the required number of bits // and extract the value from the hash - uint mask = (1 << bitsNeeded) - 1; - uint value = uint(rand) & mask; + uint256 mask = (1 << bitsNeeded) - 1; + uint256 value = uint256(rand) & mask; // in case value is out of range, wrap around or retry while (value >= range) { @@ -506,9 +552,9 @@ contract StakeRegistryUnitTests is MockAVSDeployer, IStakeRegistryEvents { /// @dev Sort to ensure that the array is in desscending order for removeStrategies function _sortArrayDesc(uint256[] memory arr) internal pure returns (uint256[] memory) { uint256 l = arr.length; - for(uint256 i = 0; i < l; i++) { - for(uint256 j = i+1; j < l ;j++) { - if(arr[i] < arr[j]) { + for (uint256 i = 0; i < l; i++) { + for (uint256 j = i + 1; j < l; j++) { + if (arr[i] < arr[j]) { uint256 temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; @@ -521,17 +567,19 @@ contract StakeRegistryUnitTests is MockAVSDeployer, IStakeRegistryEvents { /// @notice Tests for any nonstandard/permissioned methods contract StakeRegistryUnitTests_Config is StakeRegistryUnitTests { - - /******************************************************************************* - initializeQuorum - *******************************************************************************/ - + /** + * + * initializeQuorum + * + */ function testFuzz_initializeQuorum_Revert_WhenNotRegistryCoordinator( uint8 quorumNumber, uint96 minimumStake, IStakeRegistry.StrategyParams[] memory strategyParams ) public { - cheats.expectRevert("StakeRegistry.onlyRegistryCoordinator: caller is not the RegistryCoordinator"); + cheats.expectRevert( + "StakeRegistry.onlyRegistryCoordinator: caller is not the RegistryCoordinator" + ); stakeRegistry.initializeQuorum(quorumNumber, minimumStake, strategyParams); } @@ -550,7 +598,8 @@ contract StakeRegistryUnitTests_Config is StakeRegistryUnitTests { uint96 minimumStake ) public { cheats.assume(quorumNumber >= nextQuorum); - IStakeRegistry.StrategyParams[] memory strategyParams = new IStakeRegistry.StrategyParams[](0); + IStakeRegistry.StrategyParams[] memory strategyParams = + new IStakeRegistry.StrategyParams[](0); cheats.expectRevert("StakeRegistry._addStrategyParams: no strategies provided"); cheats.prank(address(registryCoordinator)); stakeRegistry.initializeQuorum(quorumNumber, minimumStake, strategyParams); @@ -558,8 +607,7 @@ contract StakeRegistryUnitTests_Config is StakeRegistryUnitTests { strategyParams = new IStakeRegistry.StrategyParams[](MAX_WEIGHING_FUNCTION_LENGTH + 1); for (uint256 i = 0; i < strategyParams.length; i++) { strategyParams[i] = IStakeRegistry.StrategyParams( - IStrategy(address(uint160(uint256(keccak256(abi.encodePacked(i)))))), - uint96(1) + IStrategy(address(uint160(uint256(keccak256(abi.encodePacked(i)))))), uint96(1) ); } cheats.expectRevert("StakeRegistry._addStrategyParams: exceed MAX_WEIGHING_FUNCTION_LENGTH"); @@ -569,7 +617,7 @@ contract StakeRegistryUnitTests_Config is StakeRegistryUnitTests { /** * @dev Initializes a quorum with StrategyParams with fuzzed multipliers inputs and corresponding - * strategy addresses. + * strategy addresses. */ function testFuzz_initializeQuorum( uint8 quorumNumber, @@ -578,41 +626,63 @@ contract StakeRegistryUnitTests_Config is StakeRegistryUnitTests { ) public { cheats.assume(quorumNumber >= nextQuorum); cheats.assume(0 < multipliers.length && multipliers.length <= MAX_WEIGHING_FUNCTION_LENGTH); - IStakeRegistry.StrategyParams[] memory strategyParams = new IStakeRegistry.StrategyParams[](multipliers.length); + IStakeRegistry.StrategyParams[] memory strategyParams = + new IStakeRegistry.StrategyParams[](multipliers.length); for (uint256 i = 0; i < strategyParams.length; i++) { cheats.assume(multipliers[i] > 0); strategyParams[i] = IStakeRegistry.StrategyParams( - IStrategy(address(uint160(uint256(keccak256(abi.encodePacked(i)))))), - multipliers[i] + IStrategy(address(uint160(uint256(keccak256(abi.encodePacked(i)))))), multipliers[i] ); } quorumNumber = nextQuorum; cheats.prank(address(registryCoordinator)); stakeRegistry.initializeQuorum(quorumNumber, minimumStake, strategyParams); - IStakeRegistry.StakeUpdate memory initialStakeUpdate = stakeRegistry.getTotalStakeUpdateAtIndex(quorumNumber, 0); - assertEq(stakeRegistry.minimumStakeForQuorum(quorumNumber), minimumStake, "invalid minimum stake"); - assertEq(stakeRegistry.getTotalStakeHistoryLength(quorumNumber), 1, "invalid total stake history length"); + IStakeRegistry.StakeUpdate memory initialStakeUpdate = + stakeRegistry.getTotalStakeUpdateAtIndex(quorumNumber, 0); + assertEq( + stakeRegistry.minimumStakeForQuorum(quorumNumber), minimumStake, "invalid minimum stake" + ); + assertEq( + stakeRegistry.getTotalStakeHistoryLength(quorumNumber), + 1, + "invalid total stake history length" + ); assertEq(initialStakeUpdate.stake, 0, "invalid stake update"); - assertEq(initialStakeUpdate.updateBlockNumber, uint32(block.number), "invalid updateBlockNumber stake update"); - assertEq(initialStakeUpdate.nextUpdateBlockNumber, 0, "invalid nextUpdateBlockNumber stake update"); - assertEq(stakeRegistry.strategyParamsLength(quorumNumber), strategyParams.length, "invalid strategy params length"); + assertEq( + initialStakeUpdate.updateBlockNumber, + uint32(block.number), + "invalid updateBlockNumber stake update" + ); + assertEq( + initialStakeUpdate.nextUpdateBlockNumber, + 0, + "invalid nextUpdateBlockNumber stake update" + ); + assertEq( + stakeRegistry.strategyParamsLength(quorumNumber), + strategyParams.length, + "invalid strategy params length" + ); for (uint256 i = 0; i < strategyParams.length; i++) { - (IStrategy strategy , uint96 multiplier) = stakeRegistry.strategyParams(quorumNumber, i); + (IStrategy strategy, uint96 multiplier) = stakeRegistry.strategyParams(quorumNumber, i); assertEq(address(strategy), address(strategyParams[i].strategy), "invalid strategy"); assertEq(multiplier, strategyParams[i].multiplier, "invalid multiplier"); } } - /******************************************************************************* - setMinimumStakeForQuorum - *******************************************************************************/ - + /** + * + * setMinimumStakeForQuorum + * + */ function testFuzz_setMinimumStakeForQuorum_Revert_WhenNotRegistryCoordinatorOwner( uint8 quorumNumber, uint96 minimumStakeForQuorum ) public fuzzOnlyInitializedQuorums(quorumNumber) { - cheats.expectRevert("StakeRegistry.onlyCoordinatorOwner: caller is not the owner of the registryCoordinator"); + cheats.expectRevert( + "StakeRegistry.onlyCoordinatorOwner: caller is not the owner of the registryCoordinator" + ); stakeRegistry.setMinimumStakeForQuorum(quorumNumber, minimumStakeForQuorum); } @@ -626,7 +696,7 @@ contract StakeRegistryUnitTests_Config is StakeRegistryUnitTests { cheats.prank(registryCoordinatorOwner); stakeRegistry.setMinimumStakeForQuorum(quorumNumber, minimumStakeForQuorum); } - + /// @dev Fuzzes initialized quorum numbers and minimum stakes to set to function testFuzz_setMinimumStakeForQuorum( uint8 quorumNumber, @@ -634,18 +704,25 @@ contract StakeRegistryUnitTests_Config is StakeRegistryUnitTests { ) public fuzzOnlyInitializedQuorums(quorumNumber) { cheats.prank(registryCoordinatorOwner); stakeRegistry.setMinimumStakeForQuorum(quorumNumber, minimumStakeForQuorum); - assertEq(stakeRegistry.minimumStakeForQuorum(quorumNumber), minimumStakeForQuorum, "invalid minimum stake"); + assertEq( + stakeRegistry.minimumStakeForQuorum(quorumNumber), + minimumStakeForQuorum, + "invalid minimum stake" + ); } - /******************************************************************************* - addStrategies - *******************************************************************************/ - + /** + * + * addStrategies + * + */ function testFuzz_addStrategies_Revert_WhenNotRegistryCoordinatorOwner( uint8 quorumNumber, IStakeRegistry.StrategyParams[] memory strategyParams ) public fuzzOnlyInitializedQuorums(quorumNumber) { - cheats.expectRevert("StakeRegistry.onlyCoordinatorOwner: caller is not the owner of the registryCoordinator"); + cheats.expectRevert( + "StakeRegistry.onlyCoordinatorOwner: caller is not the owner of the registryCoordinator" + ); stakeRegistry.addStrategies(quorumNumber, strategyParams); } @@ -663,16 +740,12 @@ contract StakeRegistryUnitTests_Config is StakeRegistryUnitTests { function test_addStrategies_Revert_WhenDuplicateStrategies() public { uint8 quorumNumber = _initializeQuorum(uint96(type(uint16).max), 1); - IStrategy strat = IStrategy(address(uint160(uint256(keccak256(abi.encodePacked("duplicate strat")))))); - IStakeRegistry.StrategyParams[] memory strategyParams = new IStakeRegistry.StrategyParams[](2); - strategyParams[0] = IStakeRegistry.StrategyParams( - strat, - uint96(WEIGHTING_DIVISOR) - ); - strategyParams[1] = IStakeRegistry.StrategyParams( - strat, - uint96(WEIGHTING_DIVISOR) - ); + IStrategy strat = + IStrategy(address(uint160(uint256(keccak256(abi.encodePacked("duplicate strat")))))); + IStakeRegistry.StrategyParams[] memory strategyParams = + new IStakeRegistry.StrategyParams[](2); + 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.prank(registryCoordinatorOwner); @@ -682,14 +755,15 @@ contract StakeRegistryUnitTests_Config is StakeRegistryUnitTests { function test_addStrategies_Revert_WhenZeroWeight() public { uint8 quorumNumber = _initializeQuorum(uint96(type(uint16).max), 1); - IStrategy strat = IStrategy(address(uint160(uint256(keccak256(abi.encodePacked("duplicate strat")))))); - IStakeRegistry.StrategyParams[] memory strategyParams = new IStakeRegistry.StrategyParams[](2); - strategyParams[0] = IStakeRegistry.StrategyParams( - strat, - 0 - ); + IStrategy strat = + IStrategy(address(uint160(uint256(keccak256(abi.encodePacked("duplicate strat")))))); + IStakeRegistry.StrategyParams[] memory strategyParams = + new IStakeRegistry.StrategyParams[](2); + strategyParams[0] = IStakeRegistry.StrategyParams(strat, 0); - cheats.expectRevert("StakeRegistry._addStrategyParams: cannot add strategy with zero weight"); + cheats.expectRevert( + "StakeRegistry._addStrategyParams: cannot add strategy with zero weight" + ); cheats.prank(registryCoordinatorOwner); stakeRegistry.addStrategies(quorumNumber, strategyParams); } @@ -704,18 +778,19 @@ contract StakeRegistryUnitTests_Config is StakeRegistryUnitTests { ) public fuzzOnlyInitializedQuorums(quorumNumber) { uint256 currNumStrategies = stakeRegistry.strategyParamsLength(quorumNumber); // Assume nonzero multipliers, and total added strategies length is less than MAX_WEIGHING_FUNCTION_LENGTH - cheats.assume(0 < multipliers.length && multipliers.length <= MAX_WEIGHING_FUNCTION_LENGTH - currNumStrategies); + cheats.assume( + 0 < multipliers.length + && multipliers.length <= MAX_WEIGHING_FUNCTION_LENGTH - currNumStrategies + ); for (uint256 i = 0; i < multipliers.length; i++) { cheats.assume(multipliers[i] > 0); } // Expected events emitted - IStakeRegistry.StrategyParams[] memory strategyParams = new IStakeRegistry.StrategyParams[](multipliers.length); + IStakeRegistry.StrategyParams[] memory strategyParams = + new IStakeRegistry.StrategyParams[](multipliers.length); for (uint256 i = 0; i < strategyParams.length; i++) { IStrategy strat = IStrategy(address(uint160(uint256(keccak256(abi.encodePacked(i)))))); - strategyParams[i] = IStakeRegistry.StrategyParams( - strat, - multipliers[i] - ); + strategyParams[i] = IStakeRegistry.StrategyParams(strat, multipliers[i]); cheats.expectEmit(true, true, true, true, address(stakeRegistry)); emit StrategyAddedToQuorum(quorumNumber, strat); @@ -726,22 +801,31 @@ contract StakeRegistryUnitTests_Config is StakeRegistryUnitTests { // addStrategies() call and expected assertions cheats.prank(registryCoordinatorOwner); stakeRegistry.addStrategies(quorumNumber, strategyParams); - assertEq(stakeRegistry.strategyParamsLength(quorumNumber), strategyParams.length + 1, "invalid strategy params length"); + assertEq( + stakeRegistry.strategyParamsLength(quorumNumber), + strategyParams.length + 1, + "invalid strategy params length" + ); for (uint256 i = 0; i < strategyParams.length; i++) { - (IStrategy strategy , uint96 multiplier) = stakeRegistry.strategyParams(quorumNumber, i + 1); + (IStrategy strategy, uint96 multiplier) = + stakeRegistry.strategyParams(quorumNumber, i + 1); assertEq(address(strategy), address(strategyParams[i].strategy), "invalid strategy"); assertEq(multiplier, strategyParams[i].multiplier, "invalid multiplier"); } } - /******************************************************************************* - removeStrategies - *******************************************************************************/ + /** + * + * removeStrategies + * + */ function testFuzz_removeStrategies_Revert_WhenNotRegistryCoordinatorOwner( uint8 quorumNumber, uint256[] memory indicesToRemove ) public fuzzOnlyInitializedQuorums(quorumNumber) { - cheats.expectRevert("StakeRegistry.onlyCoordinatorOwner: caller is not the owner of the registryCoordinator"); + cheats.expectRevert( + "StakeRegistry.onlyCoordinatorOwner: caller is not the owner of the registryCoordinator" + ); stakeRegistry.removeStrategies(quorumNumber, indicesToRemove); } @@ -802,7 +886,7 @@ contract StakeRegistryUnitTests_Config is StakeRegistryUnitTests { // Create array of indicesToRemove, sort desc, and assume no duplicates uint256[] memory indicesToRemove = new uint256[](numStrategiesToRemove); for (uint256 i = 0; i < numStrategiesToRemove; i++) { - indicesToRemove[i] = _randUint({ rand: bytes32(i), min: 0, max: numStrategiesToAdd - 1 }); + indicesToRemove[i] = _randUint({rand: bytes32(i), min: 0, max: numStrategiesToAdd - 1}); } indicesToRemove = _sortArrayDesc(indicesToRemove); uint256 prevIndex = indicesToRemove[0]; @@ -815,7 +899,7 @@ contract StakeRegistryUnitTests_Config is StakeRegistryUnitTests { // Expected events emitted for (uint256 i = 0; i < indicesToRemove.length; i++) { - (IStrategy strategy, ) = stakeRegistry.strategyParams(quorumNumber, indicesToRemove[i]); + (IStrategy strategy,) = stakeRegistry.strategyParams(quorumNumber, indicesToRemove[i]); cheats.expectEmit(true, true, true, true, address(stakeRegistry)); emit StrategyRemovedFromQuorum(quorumNumber, strategy); cheats.expectEmit(true, true, true, true, address(stakeRegistry)); @@ -832,16 +916,20 @@ contract StakeRegistryUnitTests_Config is StakeRegistryUnitTests { ); } - /******************************************************************************* - modifyStrategyParams - *******************************************************************************/ + /** + * + * modifyStrategyParams + * + */ function testFuzz_modifyStrategyParams_Revert_WhenNotRegistryCoordinatorOwner( uint8 quorumNumber, uint256[] calldata strategyIndices, uint96[] calldata newMultipliers ) public fuzzOnlyInitializedQuorums(quorumNumber) { - cheats.expectRevert("StakeRegistry.onlyCoordinatorOwner: caller is not the owner of the registryCoordinator"); - stakeRegistry.modifyStrategyParams(quorumNumber, strategyIndices, newMultipliers); + cheats.expectRevert( + "StakeRegistry.onlyCoordinatorOwner: caller is not the owner of the registryCoordinator" + ); + stakeRegistry.modifyStrategyParams(quorumNumber, strategyIndices, newMultipliers); } function testFuzz_modifyStrategyParams_Revert_WhenInvalidQuorum( @@ -855,10 +943,11 @@ contract StakeRegistryUnitTests_Config is StakeRegistryUnitTests { cheats.prank(registryCoordinatorOwner); stakeRegistry.modifyStrategyParams(quorumNumber, strategyIndices, newMultipliers); } - - function testFuzz_modifyStrategyParams_Revert_WhenEmptyArray( - uint8 quorumNumber - ) public fuzzOnlyInitializedQuorums(quorumNumber) { + + function testFuzz_modifyStrategyParams_Revert_WhenEmptyArray(uint8 quorumNumber) + public + fuzzOnlyInitializedQuorums(quorumNumber) + { uint256[] memory strategyIndices = new uint256[](0); uint96[] memory newMultipliers = new uint96[](0); cheats.expectRevert("StakeRegistry.modifyStrategyParams: no strategy indices provided"); @@ -875,7 +964,7 @@ contract StakeRegistryUnitTests_Config is StakeRegistryUnitTests { cheats.assume(strategyIndices.length > 0); cheats.expectRevert("StakeRegistry.modifyStrategyParams: input length mismatch"); cheats.prank(registryCoordinatorOwner); - stakeRegistry.modifyStrategyParams(quorumNumber, strategyIndices, newMultipliers); + stakeRegistry.modifyStrategyParams(quorumNumber, strategyIndices, newMultipliers); } /** @@ -893,8 +982,8 @@ contract StakeRegistryUnitTests_Config is StakeRegistryUnitTests { uint96[] memory newMultipliers = new uint96[](numStrategiesToModify); // create array of indices to modify, assume no duplicates, and create array of multipliers for each index for (uint256 i = 0; i < numStrategiesToModify; i++) { - strategyIndices[i] = _randUint({ rand: bytes32(i), min: 0, max: numStrategiesToAdd - 1 }); - newMultipliers[i] = uint96(_randUint({ rand: bytes32(i), min: 1, max: type(uint96).max })); + strategyIndices[i] = _randUint({rand: bytes32(i), min: 0, max: numStrategiesToAdd - 1}); + newMultipliers[i] = uint96(_randUint({rand: bytes32(i), min: 1, max: type(uint96).max})); // ensure no duplicate indices if (i == 0) { prevIndex = strategyIndices[0]; @@ -905,9 +994,9 @@ contract StakeRegistryUnitTests_Config is StakeRegistryUnitTests { } // Expected events emitted - uint8 quorumNumber = _initializeQuorum(0 /* minimumStake */, numStrategiesToAdd); + uint8 quorumNumber = _initializeQuorum(0, /* minimumStake */ numStrategiesToAdd); for (uint256 i = 0; i < strategyIndices.length; i++) { - (IStrategy strategy, ) = stakeRegistry.strategyParams(quorumNumber, strategyIndices[i]); + (IStrategy strategy,) = stakeRegistry.strategyParams(quorumNumber, strategyIndices[i]); cheats.expectEmit(true, true, true, true, address(stakeRegistry)); emit StrategyMultiplierUpdated(quorumNumber, strategy, newMultipliers[i]); } @@ -924,15 +1013,17 @@ contract StakeRegistryUnitTests_Config is StakeRegistryUnitTests { /// @notice Tests for StakeRegistry.registerOperator contract StakeRegistryUnitTests_Register is StakeRegistryUnitTests { - - /******************************************************************************* - registerOperator - *******************************************************************************/ - + /** + * + * registerOperator + * + */ function test_registerOperator_Revert_WhenNotRegistryCoordinator() public { (address operator, bytes32 operatorId) = _selectNewOperator(); - cheats.expectRevert("StakeRegistry.onlyRegistryCoordinator: caller is not the RegistryCoordinator"); + cheats.expectRevert( + "StakeRegistry.onlyRegistryCoordinator: caller is not the RegistryCoordinator" + ); stakeRegistry.registerOperator(operator, operatorId, initializedQuorumBytes); } @@ -942,28 +1033,31 @@ 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.registerOperator: quorum does not exist"); + cheats.expectRevert("StakeRegistry.quorumExists: quorum does not exist"); cheats.prank(address(registryCoordinator)); stakeRegistry.registerOperator(setup.operator, setup.operatorId, invalidQuorums); } /// @dev Attempt to register for all quorums, selecting one quorum to attempt with /// insufficient stake - function testFuzz_registerOperator_Revert_WhenInsufficientStake( - uint8 failingQuorum - ) public fuzzOnlyInitializedQuorums(failingQuorum) { + function testFuzz_registerOperator_Revert_WhenInsufficientStake(uint8 failingQuorum) + public + fuzzOnlyInitializedQuorums(failingQuorum) + { (address operator, bytes32 operatorId) = _selectNewOperator(); bytes memory quorumNumbers = initializedQuorumBytes; uint96[] memory minimumStakes = _getMinimumStakes(quorumNumbers); // Set the operator's weight to the minimum stake for each quorum // ... except the failing quorum, which gets minimum stake - 1 - for (uint i = 0; i < quorumNumbers.length; i++) { + for (uint256 i = 0; i < quorumNumbers.length; i++) { uint8 quorumNumber = uint8(quorumNumbers[i]); uint96 operatorWeight; if (quorumNumber == failingQuorum) { - unchecked { operatorWeight = minimumStakes[i] - 1; } + unchecked { + operatorWeight = minimumStakes[i] - 1; + } assertTrue(operatorWeight < minimumStakes[i], "minimum stake underflow"); } else { operatorWeight = minimumStakes[i]; @@ -973,7 +1067,9 @@ contract StakeRegistryUnitTests_Register is StakeRegistryUnitTests { } // Attempt to register - cheats.expectRevert("StakeRegistry.registerOperator: Operator does not meet minimum stake requirement for quorum"); + cheats.expectRevert( + "StakeRegistry.registerOperator: Operator does not meet minimum stake requirement for quorum" + ); cheats.prank(address(registryCoordinator)); stakeRegistry.registerOperator(operator, operatorId, quorumNumbers); } @@ -993,31 +1089,57 @@ contract StakeRegistryUnitTests_Register is StakeRegistryUnitTests { /// registerOperator cheats.prank(address(registryCoordinator)); - (uint96[] memory resultingStakes, uint96[] memory totalStakes) = + (uint96[] memory resultingStakes, uint96[] memory totalStakes) = stakeRegistry.registerOperator(setup.operator, setup.operatorId, setup.quorumNumbers); /// Read ending state - IStakeRegistry.StakeUpdate[] memory newOperatorStakes = _getLatestStakeUpdates(setup.operatorId, setup.quorumNumbers); - IStakeRegistry.StakeUpdate[] memory newTotalStakes = _getLatestTotalStakeUpdates(setup.quorumNumbers); - uint256[] memory operatorStakeHistoryLengths = _getStakeHistoryLengths(setup.operatorId, setup.quorumNumbers); + IStakeRegistry.StakeUpdate[] memory newOperatorStakes = + _getLatestStakeUpdates(setup.operatorId, setup.quorumNumbers); + IStakeRegistry.StakeUpdate[] memory newTotalStakes = + _getLatestTotalStakeUpdates(setup.quorumNumbers); + uint256[] memory operatorStakeHistoryLengths = + _getStakeHistoryLengths(setup.operatorId, setup.quorumNumbers); /// Check results - assertTrue(resultingStakes.length == setup.quorumNumbers.length, "invalid return length for operator stakes"); - assertTrue(totalStakes.length == setup.quorumNumbers.length, "invalid return length for total stakes"); + assertTrue( + resultingStakes.length == setup.quorumNumbers.length, + "invalid return length for operator stakes" + ); + assertTrue( + totalStakes.length == setup.quorumNumbers.length, + "invalid return length for total stakes" + ); - for (uint i = 0; i < setup.quorumNumbers.length; i++) { + for (uint256 i = 0; i < setup.quorumNumbers.length; i++) { IStakeRegistry.StakeUpdate memory newOperatorStake = newOperatorStakes[i]; IStakeRegistry.StakeUpdate memory newTotalStake = newTotalStakes[i]; // Check return value against weights, latest state read, and minimum stake - assertEq(resultingStakes[i], setup.operatorWeights[i], "stake registry did not return correct stake"); - assertEq(resultingStakes[i], newOperatorStake.stake, "invalid latest operator stake update"); + assertEq( + resultingStakes[i], + setup.operatorWeights[i], + "stake registry did not return correct stake" + ); + assertEq( + resultingStakes[i], newOperatorStake.stake, "invalid latest operator stake update" + ); assertTrue(resultingStakes[i] != 0, "registered operator with zero stake"); - assertTrue(resultingStakes[i] >= setup.minimumStakes[i], "stake registry did not return correct stake"); - + assertTrue( + resultingStakes[i] >= setup.minimumStakes[i], + "stake registry did not return correct stake" + ); + // Check stake increase from fuzzed input - assertEq(resultingStakes[i], newOperatorStake.stake, "did not add additional stake to operator correctly"); - assertEq(resultingStakes[i], newTotalStake.stake, "did not add additional stake to total correctly"); + assertEq( + resultingStakes[i], + newOperatorStake.stake, + "did not add additional stake to operator correctly" + ); + assertEq( + resultingStakes[i], + newTotalStake.stake, + "did not add additional stake to total correctly" + ); // Check that we had an update this block assertEq(newOperatorStake.updateBlockNumber, uint32(block.number), ""); @@ -1046,36 +1168,60 @@ contract StakeRegistryUnitTests_Register is StakeRegistryUnitTests { ) public { cheats.assume(numOperators > 1 && numOperators < 20); - RegisterSetup[] memory setups = _fuzz_setupRegisterOperators(quorumBitmap, additionalStake, numOperators); + RegisterSetup[] memory setups = + _fuzz_setupRegisterOperators(quorumBitmap, additionalStake, numOperators); // Register each operator one at a time, and check results: - for (uint i = 0; i < numOperators; i++) { + for (uint256 i = 0; i < numOperators; i++) { RegisterSetup memory setup = setups[i]; cheats.prank(address(registryCoordinator)); - (uint96[] memory resultingStakes, uint96[] memory totalStakes) = - stakeRegistry.registerOperator(setup.operator, setup.operatorId, setup.quorumNumbers); - + (uint96[] memory resultingStakes, uint96[] memory totalStakes) = stakeRegistry + .registerOperator(setup.operator, setup.operatorId, setup.quorumNumbers); + /// Read ending state - IStakeRegistry.StakeUpdate[] memory newOperatorStakes = _getLatestStakeUpdates(setup.operatorId, setup.quorumNumbers); - uint256[] memory operatorStakeHistoryLengths = _getStakeHistoryLengths(setup.operatorId, setup.quorumNumbers); + IStakeRegistry.StakeUpdate[] memory newOperatorStakes = + _getLatestStakeUpdates(setup.operatorId, setup.quorumNumbers); + uint256[] memory operatorStakeHistoryLengths = + _getStakeHistoryLengths(setup.operatorId, setup.quorumNumbers); // Sum stakes in `_totalStakeAdded` to be checked later _tallyTotalStakeAdded(setup.quorumNumbers, resultingStakes); /// Check results - assertTrue(resultingStakes.length == setup.quorumNumbers.length, "invalid return length for operator stakes"); - assertTrue(totalStakes.length == setup.quorumNumbers.length, "invalid return length for total stakes"); - for (uint j = 0; j < setup.quorumNumbers.length; j++) { + assertTrue( + resultingStakes.length == setup.quorumNumbers.length, + "invalid return length for operator stakes" + ); + assertTrue( + totalStakes.length == setup.quorumNumbers.length, + "invalid return length for total stakes" + ); + for (uint256 j = 0; j < setup.quorumNumbers.length; j++) { // Check result against weights and latest state read - assertEq(resultingStakes[j], setup.operatorWeights[j], "stake registry did not return correct stake"); - assertEq(resultingStakes[j], newOperatorStakes[j].stake, "invalid latest operator stake update"); + assertEq( + resultingStakes[j], + setup.operatorWeights[j], + "stake registry did not return correct stake" + ); + assertEq( + resultingStakes[j], + newOperatorStakes[j].stake, + "invalid latest operator stake update" + ); assertTrue(resultingStakes[j] != 0, "registered operator with zero stake"); // Check result against minimum stake - assertTrue(resultingStakes[j] >= setup.minimumStakes[j], "stake registry did not return correct stake"); - + assertTrue( + resultingStakes[j] >= setup.minimumStakes[j], + "stake registry did not return correct stake" + ); + // Check stake increase from fuzzed input - assertEq(resultingStakes[j], newOperatorStakes[j].stake, "did not add additional stake to operator correctly"); + assertEq( + resultingStakes[j], + newOperatorStakes[j].stake, + "did not add additional stake to operator correctly" + ); // Check this is the first entry in the operator stake history assertEq(operatorStakeHistoryLengths[j], 1, "invalid total stake history length"); } @@ -1083,12 +1229,25 @@ contract StakeRegistryUnitTests_Register is StakeRegistryUnitTests { // Check total stake results bytes memory quorumNumbers = initializedQuorumBytes; - IStakeRegistry.StakeUpdate[] memory newTotalStakes = _getLatestTotalStakeUpdates(quorumNumbers); - for (uint i = 0; i < quorumNumbers.length; i++) { + IStakeRegistry.StakeUpdate[] memory newTotalStakes = + _getLatestTotalStakeUpdates(quorumNumbers); + for (uint256 i = 0; i < quorumNumbers.length; i++) { uint8 quorumNumber = uint8(quorumNumbers[i]); - assertEq(newTotalStakes[i].stake, _totalStakeAdded[quorumNumber], "incorrect latest total stake"); - assertEq(newTotalStakes[i].nextUpdateBlockNumber, 0, "incorrect total stake next update block"); - assertEq(newTotalStakes[i].updateBlockNumber, uint32(block.number), "incorrect total stake next update block"); + assertEq( + newTotalStakes[i].stake, + _totalStakeAdded[quorumNumber], + "incorrect latest total stake" + ); + assertEq( + newTotalStakes[i].nextUpdateBlockNumber, + 0, + "incorrect total stake next update block" + ); + assertEq( + newTotalStakes[i].updateBlockNumber, + uint32(block.number), + "incorrect total stake next update block" + ); } } @@ -1111,56 +1270,89 @@ contract StakeRegistryUnitTests_Register is StakeRegistryUnitTests { cheats.assume(operatorsPerBlock >= 1 && operatorsPerBlock <= 4); cheats.assume(totalBlocks >= 2 && totalBlocks <= 5); - uint startBlock = block.number; - for (uint i = 1; i <= totalBlocks; i++) { + uint256 startBlock = block.number; + for (uint256 i = 1; i <= totalBlocks; i++) { // Move to current block number - uint currBlock = startBlock + i; + uint256 currBlock = startBlock + i; cheats.roll(currBlock); - RegisterSetup[] memory setups = - _fuzz_setupRegisterOperators(initializedQuorumBitmap, additionalStake, operatorsPerBlock); + RegisterSetup[] memory setups = _fuzz_setupRegisterOperators( + initializedQuorumBitmap, additionalStake, operatorsPerBlock + ); // Get prior total stake updates bytes memory quorumNumbers = setups[0].quorumNumbers; - uint[] memory prevHistoryLengths = _getTotalStakeHistoryLengths(quorumNumbers); - - for (uint j = 0; j < operatorsPerBlock; j++) { + uint256[] memory prevHistoryLengths = _getTotalStakeHistoryLengths(quorumNumbers); + + for (uint256 j = 0; j < operatorsPerBlock; j++) { RegisterSetup memory setup = setups[j]; cheats.prank(address(registryCoordinator)); - (uint96[] memory resultingStakes, ) = - stakeRegistry.registerOperator(setup.operator, setup.operatorId, setup.quorumNumbers); - + (uint96[] memory resultingStakes,) = stakeRegistry.registerOperator( + setup.operator, setup.operatorId, setup.quorumNumbers + ); + // Sum stakes in `_totalStakeAdded` to be checked later _tallyTotalStakeAdded(setup.quorumNumbers, resultingStakes); } // Get new total stake updates - uint[] memory newHistoryLengths = _getTotalStakeHistoryLengths(quorumNumbers); - IStakeRegistry.StakeUpdate[] memory newTotalStakes = _getLatestTotalStakeUpdates(quorumNumbers); + uint256[] memory newHistoryLengths = _getTotalStakeHistoryLengths(quorumNumbers); + IStakeRegistry.StakeUpdate[] memory newTotalStakes = + _getLatestTotalStakeUpdates(quorumNumbers); - for (uint j = 0; j < quorumNumbers.length; j++) { + for (uint256 j = 0; j < quorumNumbers.length; j++) { uint8 quorumNumber = uint8(quorumNumbers[j]); // Check that we've added 1 to total stake history length - assertEq(prevHistoryLengths[j] + 1, newHistoryLengths[j], "total history should have a new entry"); + assertEq( + prevHistoryLengths[j] + 1, + newHistoryLengths[j], + "total history should have a new entry" + ); // Validate latest entry correctness - assertEq(newTotalStakes[j].stake, _totalStakeAdded[quorumNumber], "latest update should match total stake added"); - assertEq(newTotalStakes[j].updateBlockNumber, currBlock, "latest update should be from current block"); - assertEq(newTotalStakes[j].nextUpdateBlockNumber, 0, "latest update should not have next update block"); + assertEq( + newTotalStakes[j].stake, + _totalStakeAdded[quorumNumber], + "latest update should match total stake added" + ); + assertEq( + newTotalStakes[j].updateBlockNumber, + currBlock, + "latest update should be from current block" + ); + assertEq( + newTotalStakes[j].nextUpdateBlockNumber, + 0, + "latest update should not have next update block" + ); // Validate previous entry was updated correctly - IStakeRegistry.StakeUpdate memory prevUpdate = - stakeRegistry.getTotalStakeUpdateAtIndex(quorumNumber, prevHistoryLengths[j]-1); - assertTrue(prevUpdate.stake < newTotalStakes[j].stake, "previous update should have lower stake than latest"); - assertEq(prevUpdate.updateBlockNumber + 1, newTotalStakes[j].updateBlockNumber, "prev entry should be from last block"); - assertEq(prevUpdate.nextUpdateBlockNumber, newTotalStakes[j].updateBlockNumber, "prev entry.next should be latest.cur"); + IStakeRegistry.StakeUpdate memory prevUpdate = stakeRegistry + .getTotalStakeUpdateAtIndex(quorumNumber, prevHistoryLengths[j] - 1); + assertTrue( + prevUpdate.stake < newTotalStakes[j].stake, + "previous update should have lower stake than latest" + ); + assertEq( + prevUpdate.updateBlockNumber + 1, + newTotalStakes[j].updateBlockNumber, + "prev entry should be from last block" + ); + assertEq( + prevUpdate.nextUpdateBlockNumber, + newTotalStakes[j].updateBlockNumber, + "prev entry.next should be latest.cur" + ); } } } - function _tallyTotalStakeAdded(bytes memory quorumNumbers, uint96[] memory stakeAdded) internal { - for (uint i = 0; i < quorumNumbers.length; i++) { + function _tallyTotalStakeAdded( + bytes memory quorumNumbers, + uint96[] memory stakeAdded + ) internal { + for (uint256 i = 0; i < quorumNumbers.length; i++) { uint8 quorumNumber = uint8(quorumNumbers[i]); _totalStakeAdded[quorumNumber] += stakeAdded[i]; } @@ -1169,13 +1361,13 @@ contract StakeRegistryUnitTests_Register is StakeRegistryUnitTests { /// @notice Tests for StakeRegistry.deregisterOperator contract StakeRegistryUnitTests_Deregister is StakeRegistryUnitTests { - using BitmapUtils for *; - /******************************************************************************* - deregisterOperator - *******************************************************************************/ - + /** + * + * deregisterOperator + * + */ function test_deregisterOperator_Revert_WhenNotRegistryCoordinator() public { DeregisterSetup memory setup = _fuzz_setupDeregisterOperator({ registeredFor: initializedQuorumBitmap, @@ -1183,7 +1375,9 @@ contract StakeRegistryUnitTests_Deregister is StakeRegistryUnitTests { fuzzy_addtlStake: 0 }); - cheats.expectRevert("StakeRegistry.onlyRegistryCoordinator: caller is not the RegistryCoordinator"); + cheats.expectRevert( + "StakeRegistry.onlyRegistryCoordinator: caller is not the RegistryCoordinator" + ); stakeRegistry.deregisterOperator(setup.operatorId, setup.quorumsToRemove); } @@ -1194,11 +1388,11 @@ contract StakeRegistryUnitTests_Deregister is StakeRegistryUnitTests { fuzzy_toRemove: initializedQuorumBitmap, fuzzy_addtlStake: 0 }); - + // Get a list of valid quorums ending in an invalid quorum number bytes memory invalidQuorums = _fuzz_getInvalidQuorums(rand); - cheats.expectRevert("StakeRegistry.registerOperator: quorum does not exist"); + cheats.expectRevert("StakeRegistry.quorumExists: quorum does not exist"); cheats.prank(address(registryCoordinator)); stakeRegistry.registerOperator(setup.operator, setup.operatorId, invalidQuorums); } @@ -1224,10 +1418,12 @@ contract StakeRegistryUnitTests_Deregister is StakeRegistryUnitTests { cheats.prank(address(registryCoordinator)); stakeRegistry.deregisterOperator(setup.operatorId, setup.quorumsToRemove); - IStakeRegistry.StakeUpdate[] memory newOperatorStakes = _getLatestStakeUpdates(setup.operatorId, setup.registeredQuorumNumbers); - IStakeRegistry.StakeUpdate[] memory newTotalStakes = _getLatestTotalStakeUpdates(setup.registeredQuorumNumbers); + IStakeRegistry.StakeUpdate[] memory newOperatorStakes = + _getLatestStakeUpdates(setup.operatorId, setup.registeredQuorumNumbers); + IStakeRegistry.StakeUpdate[] memory newTotalStakes = + _getLatestTotalStakeUpdates(setup.registeredQuorumNumbers); - for (uint i = 0; i < setup.registeredQuorumNumbers.length; i++) { + for (uint256 i = 0; i < setup.registeredQuorumNumbers.length; i++) { uint8 registeredQuorum = uint8(setup.registeredQuorumNumbers[i]); IStakeRegistry.StakeUpdate memory prevOperatorStake = setup.prevOperatorStakes[i]; @@ -1242,24 +1438,49 @@ contract StakeRegistryUnitTests_Deregister is StakeRegistryUnitTests { if (deregistered) { // Check that operator's stake was removed from both operator and total assertEq(newOperatorStake.stake, 0, "failed to remove stake"); - assertEq(newTotalStake.stake + prevOperatorStake.stake, prevTotalStake.stake, "failed to remove stake from total"); - + assertEq( + newTotalStake.stake + prevOperatorStake.stake, + prevTotalStake.stake, + "failed to remove stake from total" + ); + // Check that we had an update this block - assertEq(newOperatorStake.updateBlockNumber, uint32(block.number), "operator stake has incorrect update block"); - assertEq(newOperatorStake.nextUpdateBlockNumber, 0, "operator stake has incorrect next update block"); - assertEq(newTotalStake.updateBlockNumber, uint32(block.number), "total stake has incorrect update block"); - assertEq(newTotalStake.nextUpdateBlockNumber, 0, "total stake has incorrect next update block"); + assertEq( + newOperatorStake.updateBlockNumber, + uint32(block.number), + "operator stake has incorrect update block" + ); + assertEq( + newOperatorStake.nextUpdateBlockNumber, + 0, + "operator stake has incorrect next update block" + ); + assertEq( + newTotalStake.updateBlockNumber, + uint32(block.number), + "total stake has incorrect update block" + ); + assertEq( + newTotalStake.nextUpdateBlockNumber, + 0, + "total stake has incorrect next update block" + ); } else { // Ensure no change to operator or total stakes - assertTrue(_isUnchanged(prevOperatorStake, newOperatorStake), "operator stake incorrectly updated"); - assertTrue(_isUnchanged(prevTotalStake, newTotalStake), "total stake incorrectly updated"); + assertTrue( + _isUnchanged(prevOperatorStake, newOperatorStake), + "operator stake incorrectly updated" + ); + assertTrue( + _isUnchanged(prevTotalStake, newTotalStake), "total stake incorrectly updated" + ); } } } // Track total stake removed from each quorum as we deregister operators mapping(uint8 => uint96) _totalStakeRemoved; - + /** * @dev Registers multiple operators for each initialized quorum, adding `additionalStake` * to the minimum stake for each quorum. Tests deregistering the operators for @@ -1284,21 +1505,24 @@ contract StakeRegistryUnitTests_Deregister is StakeRegistryUnitTests { bytes memory registeredQuorums = initializedQuorumBytes; uint192 quorumsToRemoveBitmap = setups[0].quorumsToRemoveBitmap; - IStakeRegistry.StakeUpdate[] memory prevTotalStakes = _getLatestTotalStakeUpdates(registeredQuorums); + IStakeRegistry.StakeUpdate[] memory prevTotalStakes = + _getLatestTotalStakeUpdates(registeredQuorums); // Deregister operators one at a time and check results - for (uint i = 0; i < numOperators; i++) { + for (uint256 i = 0; i < numOperators; i++) { DeregisterSetup memory setup = setups[i]; bytes32 operatorId = setup.operatorId; cheats.prank(address(registryCoordinator)); stakeRegistry.deregisterOperator(setup.operatorId, setup.quorumsToRemove); - IStakeRegistry.StakeUpdate[] memory newOperatorStakes = _getLatestStakeUpdates(operatorId, registeredQuorums); - IStakeRegistry.StakeUpdate[] memory newTotalStakes = _getLatestTotalStakeUpdates(registeredQuorums); + IStakeRegistry.StakeUpdate[] memory newOperatorStakes = + _getLatestStakeUpdates(operatorId, registeredQuorums); + IStakeRegistry.StakeUpdate[] memory newTotalStakes = + _getLatestTotalStakeUpdates(registeredQuorums); // Check results for each quorum - for (uint j = 0; j < registeredQuorums.length; j++) { + for (uint256 j = 0; j < registeredQuorums.length; j++) { uint8 registeredQuorum = uint8(registeredQuorums[j]); IStakeRegistry.StakeUpdate memory prevOperatorStake = setup.prevOperatorStakes[j]; @@ -1315,24 +1539,48 @@ contract StakeRegistryUnitTests_Deregister is StakeRegistryUnitTests { // Check that operator's stake was removed from both operator and total assertEq(newOperatorStake.stake, 0, "failed to remove stake"); - assertEq(newTotalStake.stake + _totalStakeRemoved[registeredQuorum], prevTotalStake.stake, "failed to remove stake from total"); - + assertEq( + newTotalStake.stake + _totalStakeRemoved[registeredQuorum], + prevTotalStake.stake, + "failed to remove stake from total" + ); + // Check that we had an update this block - assertEq(newOperatorStake.updateBlockNumber, uint32(block.number), "operator stake has incorrect update block"); - assertEq(newOperatorStake.nextUpdateBlockNumber, 0, "operator stake has incorrect next update block"); - assertEq(newTotalStake.updateBlockNumber, uint32(block.number), "total stake has incorrect update block"); - assertEq(newTotalStake.nextUpdateBlockNumber, 0, "total stake has incorrect next update block"); + assertEq( + newOperatorStake.updateBlockNumber, + uint32(block.number), + "operator stake has incorrect update block" + ); + assertEq( + newOperatorStake.nextUpdateBlockNumber, + 0, + "operator stake has incorrect next update block" + ); + assertEq( + newTotalStake.updateBlockNumber, + uint32(block.number), + "total stake has incorrect update block" + ); + assertEq( + newTotalStake.nextUpdateBlockNumber, + 0, + "total stake has incorrect next update block" + ); } else { // Ensure no change to operator stake - assertTrue(_isUnchanged(prevOperatorStake, newOperatorStake), "operator stake incorrectly updated"); + assertTrue( + _isUnchanged(prevOperatorStake, newOperatorStake), + "operator stake incorrectly updated" + ); } } } // Now that we've deregistered all the operators, check the final results // For the quorums we chose to deregister from, the total stake should be zero - IStakeRegistry.StakeUpdate[] memory finalTotalStakes = _getLatestTotalStakeUpdates(registeredQuorums); - for (uint i = 0; i < registeredQuorums.length; i++) { + IStakeRegistry.StakeUpdate[] memory finalTotalStakes = + _getLatestTotalStakeUpdates(registeredQuorums); + for (uint256 i = 0; i < registeredQuorums.length; i++) { uint8 registeredQuorum = uint8(registeredQuorums[i]); // Whether or not we deregistered operators from this quorum @@ -1340,10 +1588,21 @@ contract StakeRegistryUnitTests_Deregister is StakeRegistryUnitTests { if (deregistered) { assertEq(finalTotalStakes[i].stake, 0, "failed to remove all stake from quorum"); - assertEq(finalTotalStakes[i].updateBlockNumber, uint32(block.number), "failed to remove all stake from quorum"); - assertEq(finalTotalStakes[i].nextUpdateBlockNumber, 0, "failed to remove all stake from quorum"); + assertEq( + finalTotalStakes[i].updateBlockNumber, + uint32(block.number), + "failed to remove all stake from quorum" + ); + assertEq( + finalTotalStakes[i].nextUpdateBlockNumber, + 0, + "failed to remove all stake from quorum" + ); } else { - assertTrue(_isUnchanged(finalTotalStakes[i], prevTotalStakes[i]), "incorrectly updated total stake history for unmodified quorum"); + assertTrue( + _isUnchanged(finalTotalStakes[i], prevTotalStakes[i]), + "incorrectly updated total stake history for unmodified quorum" + ); } } } @@ -1367,8 +1626,8 @@ contract StakeRegistryUnitTests_Deregister is StakeRegistryUnitTests { cheats.assume(operatorsPerBlock >= 1 && operatorsPerBlock <= 4); cheats.assume(totalBlocks >= 2 && totalBlocks <= 5); - uint numOperators = operatorsPerBlock * totalBlocks; - uint operatorIdx; // track index in setups over test + uint256 numOperators = operatorsPerBlock * totalBlocks; + uint256 operatorIdx; // track index in setups over test // Select multiple new operators, set their weight equal to the minimum plus some additional, // then register them for all initialized quorums @@ -1382,61 +1641,91 @@ contract StakeRegistryUnitTests_Deregister is StakeRegistryUnitTests { // For all operators, we're going to register for and then deregister from all initialized quorums bytes memory registeredQuorums = initializedQuorumBytes; - IStakeRegistry.StakeUpdate[] memory prevTotalStakes = _getLatestTotalStakeUpdates(registeredQuorums); - uint startBlock = block.number; + IStakeRegistry.StakeUpdate[] memory prevTotalStakes = + _getLatestTotalStakeUpdates(registeredQuorums); + uint256 startBlock = block.number; - for (uint i = 1; i <= totalBlocks; i++) { + for (uint256 i = 1; i <= totalBlocks; i++) { // Move to current block number - uint currBlock = startBlock + i; + uint256 currBlock = startBlock + i; cheats.roll(currBlock); - uint[] memory prevHistoryLengths = _getTotalStakeHistoryLengths(registeredQuorums); + uint256[] memory prevHistoryLengths = _getTotalStakeHistoryLengths(registeredQuorums); // Within this block: deregister some operators for all quorums and add the stake removed // to `_totalStakeRemoved` for later checks - for (uint j = 0; j < operatorsPerBlock; j++) { + for (uint256 j = 0; j < operatorsPerBlock; j++) { DeregisterSetup memory setup = setups[operatorIdx]; operatorIdx++; cheats.prank(address(registryCoordinator)); stakeRegistry.deregisterOperator(setup.operatorId, setup.quorumsToRemove); - for (uint k = 0; k < registeredQuorums.length; k++) { + for (uint256 k = 0; k < registeredQuorums.length; k++) { uint8 quorumNumber = uint8(registeredQuorums[k]); _totalStakeRemoved[quorumNumber] += setup.prevOperatorStakes[k].stake; } } - uint[] memory newHistoryLengths = _getTotalStakeHistoryLengths(registeredQuorums); - IStakeRegistry.StakeUpdate[] memory newTotalStakes = _getLatestTotalStakeUpdates(registeredQuorums); + uint256[] memory newHistoryLengths = _getTotalStakeHistoryLengths(registeredQuorums); + IStakeRegistry.StakeUpdate[] memory newTotalStakes = + _getLatestTotalStakeUpdates(registeredQuorums); // Validate the sum of all updates this block: // Each quorum should have a new historical entry with the correct update block pointers // ... and each quorum's stake should have decreased by `_totalStakeRemoved[quorum]` - for (uint j = 0; j < registeredQuorums.length; j++) { + for (uint256 j = 0; j < registeredQuorums.length; j++) { uint8 quorumNumber = uint8(registeredQuorums[j]); // Check that we've added 1 to total stake history length - assertEq(prevHistoryLengths[j] + 1, newHistoryLengths[j], "total history should have a new entry"); + assertEq( + prevHistoryLengths[j] + 1, + newHistoryLengths[j], + "total history should have a new entry" + ); // Validate latest entry correctness - assertEq(newTotalStakes[j].stake + _totalStakeRemoved[quorumNumber], prevTotalStakes[j].stake, "stake not removed correctly from total stake"); - assertEq(newTotalStakes[j].updateBlockNumber, currBlock, "latest update should be from current block"); - assertEq(newTotalStakes[j].nextUpdateBlockNumber, 0, "latest update should not have next update block"); - - IStakeRegistry.StakeUpdate memory prevUpdate = - stakeRegistry.getTotalStakeUpdateAtIndex(quorumNumber, prevHistoryLengths[j]-1); + assertEq( + newTotalStakes[j].stake + _totalStakeRemoved[quorumNumber], + prevTotalStakes[j].stake, + "stake not removed correctly from total stake" + ); + assertEq( + newTotalStakes[j].updateBlockNumber, + currBlock, + "latest update should be from current block" + ); + assertEq( + newTotalStakes[j].nextUpdateBlockNumber, + 0, + "latest update should not have next update block" + ); + + IStakeRegistry.StakeUpdate memory prevUpdate = stakeRegistry + .getTotalStakeUpdateAtIndex(quorumNumber, prevHistoryLengths[j] - 1); // Validate previous entry was updated correctly - assertTrue(prevUpdate.stake > newTotalStakes[j].stake, "previous update should have higher stake than latest"); - assertEq(prevUpdate.updateBlockNumber + 1, newTotalStakes[j].updateBlockNumber, "prev entry should be from last block"); - assertEq(prevUpdate.nextUpdateBlockNumber, newTotalStakes[j].updateBlockNumber, "prev entry.next should be latest.cur"); + assertTrue( + prevUpdate.stake > newTotalStakes[j].stake, + "previous update should have higher stake than latest" + ); + assertEq( + prevUpdate.updateBlockNumber + 1, + newTotalStakes[j].updateBlockNumber, + "prev entry should be from last block" + ); + assertEq( + prevUpdate.nextUpdateBlockNumber, + newTotalStakes[j].updateBlockNumber, + "prev entry.next should be latest.cur" + ); } } // Now that we've deregistered all the operators, check the final results // Each quorum's stake should be zero - IStakeRegistry.StakeUpdate[] memory finalTotalStakes = _getLatestTotalStakeUpdates(registeredQuorums); - for (uint i = 0; i < registeredQuorums.length; i++) { + IStakeRegistry.StakeUpdate[] memory finalTotalStakes = + _getLatestTotalStakeUpdates(registeredQuorums); + for (uint256 i = 0; i < registeredQuorums.length; i++) { assertEq(finalTotalStakes[i].stake, 0, "failed to remove all stake from quorum"); } } @@ -1444,30 +1733,27 @@ contract StakeRegistryUnitTests_Deregister is StakeRegistryUnitTests { /// @notice Tests for StakeRegistry.updateOperatorStake contract StakeRegistryUnitTests_StakeUpdates is StakeRegistryUnitTests { - using BitmapUtils for *; function test_updateOperatorStake_Revert_WhenNotRegistryCoordinator() public { - UpdateSetup memory setup = _fuzz_setupUpdateOperatorStake({ - registeredFor: initializedQuorumBitmap, - fuzzy_Delta: 0 - }); + UpdateSetup memory setup = + _fuzz_setupUpdateOperatorStake({registeredFor: initializedQuorumBitmap, fuzzy_Delta: 0}); - cheats.expectRevert("StakeRegistry.onlyRegistryCoordinator: caller is not the RegistryCoordinator"); + cheats.expectRevert( + "StakeRegistry.onlyRegistryCoordinator: caller is not the RegistryCoordinator" + ); stakeRegistry.updateOperatorStake(setup.operator, setup.operatorId, setup.quorumNumbers); } function testFuzz_updateOperatorStake_Revert_WhenQuorumDoesNotExist(bytes32 rand) public { // Create a new operator registered for all quorums - UpdateSetup memory setup = _fuzz_setupUpdateOperatorStake({ - registeredFor: initializedQuorumBitmap, - fuzzy_Delta: 0 - }); - + UpdateSetup memory setup = + _fuzz_setupUpdateOperatorStake({registeredFor: initializedQuorumBitmap, fuzzy_Delta: 0}); + // Get a list of valid quorums ending in an invalid quorum number bytes memory invalidQuorums = _fuzz_getInvalidQuorums(rand); - cheats.expectRevert("StakeRegistry.updateOperatorStake: quorum does not exist"); + cheats.expectRevert("StakeRegistry.quorumExists: quorum does not exist"); cheats.prank(address(registryCoordinator)); stakeRegistry.updateOperatorStake(setup.operator, setup.operatorId, invalidQuorums); } @@ -1487,20 +1773,24 @@ contract StakeRegistryUnitTests_StakeUpdates is StakeRegistryUnitTests { }); // Get starting state - IStakeRegistry.StakeUpdate[] memory prevOperatorStakes = _getLatestStakeUpdates(setup.operatorId, setup.quorumNumbers); - IStakeRegistry.StakeUpdate[] memory prevTotalStakes = _getLatestTotalStakeUpdates(setup.quorumNumbers); + IStakeRegistry.StakeUpdate[] memory prevOperatorStakes = + _getLatestStakeUpdates(setup.operatorId, setup.quorumNumbers); + IStakeRegistry.StakeUpdate[] memory prevTotalStakes = + _getLatestTotalStakeUpdates(setup.quorumNumbers); // updateOperatorStake cheats.prank(address(registryCoordinator)); - uint192 quorumsToRemove = + uint192 quorumsToRemove = stakeRegistry.updateOperatorStake(setup.operator, setup.operatorId, setup.quorumNumbers); // Get ending state - IStakeRegistry.StakeUpdate[] memory newOperatorStakes = _getLatestStakeUpdates(setup.operatorId, setup.quorumNumbers); - IStakeRegistry.StakeUpdate[] memory newTotalStakes = _getLatestTotalStakeUpdates(setup.quorumNumbers); + IStakeRegistry.StakeUpdate[] memory newOperatorStakes = + _getLatestStakeUpdates(setup.operatorId, setup.quorumNumbers); + IStakeRegistry.StakeUpdate[] memory newTotalStakes = + _getLatestTotalStakeUpdates(setup.quorumNumbers); // Check results for each quorum - for (uint i = 0; i < setup.quorumNumbers.length; i++) { + for (uint256 i = 0; i < setup.quorumNumbers.length; i++) { uint8 quorumNumber = uint8(setup.quorumNumbers[i]); uint96 minimumStake = setup.minimumStakes[i]; @@ -1513,33 +1803,63 @@ contract StakeRegistryUnitTests_StakeUpdates is StakeRegistryUnitTests { IStakeRegistry.StakeUpdate memory newTotalStake = newTotalStakes[i]; // Sanity-check setup - operator should start with minimumStake - assertTrue(prevOperatorStake.stake == minimumStake, "operator should start with nonzero stake"); + assertTrue( + prevOperatorStake.stake == minimumStake, "operator should start with nonzero stake" + ); if (endingWeight > minimumStake) { // Check updating an operator who has added stake above the minimum: // Only updates should be stake added to operator/total stakes uint96 stakeAdded = setup.stakeDeltaAbs; - assertEq(prevOperatorStake.stake + stakeAdded, newOperatorStake.stake, "failed to add delta to operator stake"); - assertEq(prevTotalStake.stake + stakeAdded, newTotalStake.stake, "failed to add delta to total stake"); + assertEq( + prevOperatorStake.stake + stakeAdded, + newOperatorStake.stake, + "failed to add delta to operator stake" + ); + assertEq( + prevTotalStake.stake + stakeAdded, + newTotalStake.stake, + "failed to add delta to total stake" + ); // Return value should be empty since we're still above the minimum - assertTrue(quorumsToRemove.isEmpty(), "positive stake delta should not remove any quorums"); + assertTrue( + quorumsToRemove.isEmpty(), "positive stake delta should not remove any quorums" + ); } else if (endingWeight < minimumStake) { // Check updating an operator who is now below the minimum: // Stake should now be zero, regardless of stake delta uint96 stakeRemoved = minimumStake; - assertEq(prevOperatorStake.stake - stakeRemoved, newOperatorStake.stake, "failed to remove delta from operator stake"); - assertEq(prevTotalStake.stake - stakeRemoved, newTotalStake.stake, "failed to remove delta from total stake"); + assertEq( + prevOperatorStake.stake - stakeRemoved, + newOperatorStake.stake, + "failed to remove delta from operator stake" + ); + assertEq( + prevTotalStake.stake - stakeRemoved, + newTotalStake.stake, + "failed to remove delta from total stake" + ); assertEq(newOperatorStake.stake, 0, "operator stake should now be zero"); // Quorum should be added to return bitmap - assertTrue(quorumsToRemove.isSet(quorumNumber), "quorum should be in removal bitmap"); + assertTrue( + quorumsToRemove.isSet(quorumNumber), "quorum should be in removal bitmap" + ); } else { // Check that no update occurs if weight remains the same - assertTrue(_isUnchanged(prevOperatorStake, newOperatorStake), "neutral stake delta should not have changed operator stake history"); - assertTrue(_isUnchanged(prevTotalStake, newTotalStake), "neutral stake delta should not have changed total stake history"); + assertTrue( + _isUnchanged(prevOperatorStake, newOperatorStake), + "neutral stake delta should not have changed operator stake history" + ); + assertTrue( + _isUnchanged(prevTotalStake, newTotalStake), + "neutral stake delta should not have changed total stake history" + ); // Check that return value is empty - we're still at the minimum, so no quorums should be removed - assertTrue(quorumsToRemove.isEmpty(), "neutral stake delta should not remove any quorums"); + assertTrue( + quorumsToRemove.isEmpty(), "neutral stake delta should not remove any quorums" + ); } } } @@ -1568,11 +1888,12 @@ contract StakeRegistryUnitTests_StakeUpdates is StakeRegistryUnitTests { bytes memory quorumNumbers = initializedQuorumBytes; // Get initial total history state - uint[] memory initialHistoryLengths = _getTotalStakeHistoryLengths(quorumNumbers); - IStakeRegistry.StakeUpdate[] memory initialTotalStakes = _getLatestTotalStakeUpdates(quorumNumbers); + uint256[] memory initialHistoryLengths = _getTotalStakeHistoryLengths(quorumNumbers); + IStakeRegistry.StakeUpdate[] memory initialTotalStakes = + _getLatestTotalStakeUpdates(quorumNumbers); // Call `updateOperatorStake` one by one - for (uint i = 0; i < numOperators; i++) { + for (uint256 i = 0; i < numOperators; i++) { UpdateSetup memory setup = setups[i]; // updateOperatorStake @@ -1581,10 +1902,11 @@ contract StakeRegistryUnitTests_StakeUpdates is StakeRegistryUnitTests { } // Check final results for each quorum - uint[] memory finalHistoryLengths = _getTotalStakeHistoryLengths(quorumNumbers); - IStakeRegistry.StakeUpdate[] memory finalTotalStakes = _getLatestTotalStakeUpdates(quorumNumbers); + uint256[] memory finalHistoryLengths = _getTotalStakeHistoryLengths(quorumNumbers); + IStakeRegistry.StakeUpdate[] memory finalTotalStakes = + _getLatestTotalStakeUpdates(quorumNumbers); - for (uint i = 0; i < quorumNumbers.length; i++) { + for (uint256 i = 0; i < quorumNumbers.length; i++) { IStakeRegistry.StakeUpdate memory initialTotalStake = initialTotalStakes[i]; IStakeRegistry.StakeUpdate memory finalTotalStake = finalTotalStakes[i]; @@ -1593,23 +1915,42 @@ contract StakeRegistryUnitTests_StakeUpdates is StakeRegistryUnitTests { uint96 stakeDeltaAbs = setups[0].stakeDeltaAbs; // Sanity-check setup: previous total stake should be minimumStake * numOperators - assertEq(initialTotalStake.stake, minimumStake * numOperators, "quorum should start with minimum stake from all operators"); + assertEq( + initialTotalStake.stake, + minimumStake * numOperators, + "quorum should start with minimum stake from all operators" + ); // history lengths should be unchanged - assertEq(initialHistoryLengths[i], finalHistoryLengths[i], "history lengths should remain unchanged"); - + assertEq( + initialHistoryLengths[i], + finalHistoryLengths[i], + "history lengths should remain unchanged" + ); + if (endingWeight > minimumStake) { // All operators had their stake increased by stakeDelta uint96 stakeAdded = numOperators * stakeDeltaAbs; - assertEq(initialTotalStake.stake + stakeAdded, finalTotalStake.stake, "failed to add delta for all operators"); + assertEq( + initialTotalStake.stake + stakeAdded, + finalTotalStake.stake, + "failed to add delta for all operators" + ); } else if (endingWeight < minimumStake) { // All operators had their entire stake removed uint96 stakeRemoved = numOperators * minimumStake; - assertEq(initialTotalStake.stake - stakeRemoved, finalTotalStake.stake, "failed to remove delta from total stake"); + assertEq( + initialTotalStake.stake - stakeRemoved, + finalTotalStake.stake, + "failed to remove delta from total stake" + ); assertEq(finalTotalStake.stake, 0, "final total stake should be zero"); } else { // No change in stake for any operator - assertTrue(_isUnchanged(initialTotalStake, finalTotalStake), "neutral stake delta should result in no change"); + assertTrue( + _isUnchanged(initialTotalStake, finalTotalStake), + "neutral stake delta should result in no change" + ); } } } @@ -1626,7 +1967,7 @@ contract StakeRegistryUnitTests_StakeUpdates is StakeRegistryUnitTests { int8 stakeDelta ) public { cheats.assume(totalBlocks >= 2 && totalBlocks <= 8); - + uint256 startBlock = block.number; for (uint256 j = 1; j <= totalBlocks; j++) { UpdateSetup memory setup = _fuzz_setupUpdateOperatorStake({ @@ -1635,9 +1976,12 @@ contract StakeRegistryUnitTests_StakeUpdates is StakeRegistryUnitTests { }); // Get starting state - IStakeRegistry.StakeUpdate[] memory prevOperatorStakes = _getLatestStakeUpdates(setup.operatorId, setup.quorumNumbers); - IStakeRegistry.StakeUpdate[] memory prevTotalStakes = _getLatestTotalStakeUpdates(setup.quorumNumbers); - uint256[] memory prevOperatorHistoryLengths = _getStakeHistoryLengths(setup.operatorId, setup.quorumNumbers); + IStakeRegistry.StakeUpdate[] memory prevOperatorStakes = + _getLatestStakeUpdates(setup.operatorId, setup.quorumNumbers); + IStakeRegistry.StakeUpdate[] memory prevTotalStakes = + _getLatestTotalStakeUpdates(setup.quorumNumbers); + uint256[] memory prevOperatorHistoryLengths = + _getStakeHistoryLengths(setup.operatorId, setup.quorumNumbers); // Move to current block number uint256 currBlock = startBlock + j; @@ -1645,16 +1989,20 @@ contract StakeRegistryUnitTests_StakeUpdates is StakeRegistryUnitTests { // updateOperatorStake cheats.prank(address(registryCoordinator)); - uint192 quorumsToRemove = - stakeRegistry.updateOperatorStake(setup.operator, setup.operatorId, setup.quorumNumbers); + uint192 quorumsToRemove = stakeRegistry.updateOperatorStake( + setup.operator, setup.operatorId, setup.quorumNumbers + ); // Get ending state - IStakeRegistry.StakeUpdate[] memory newOperatorStakes = _getLatestStakeUpdates(setup.operatorId, setup.quorumNumbers); - IStakeRegistry.StakeUpdate[] memory newTotalStakes = _getLatestTotalStakeUpdates(setup.quorumNumbers); - uint256[] memory newOperatorHistoryLengths = _getStakeHistoryLengths(setup.operatorId, setup.quorumNumbers); + IStakeRegistry.StakeUpdate[] memory newOperatorStakes = + _getLatestStakeUpdates(setup.operatorId, setup.quorumNumbers); + IStakeRegistry.StakeUpdate[] memory newTotalStakes = + _getLatestTotalStakeUpdates(setup.quorumNumbers); + uint256[] memory newOperatorHistoryLengths = + _getStakeHistoryLengths(setup.operatorId, setup.quorumNumbers); // Check results for each quorum - for (uint i = 0; i < setup.quorumNumbers.length; i++) { + for (uint256 i = 0; i < setup.quorumNumbers.length; i++) { uint8 quorumNumber = uint8(setup.quorumNumbers[i]); uint96 minimumStake = setup.minimumStakes[i]; @@ -1667,42 +2015,95 @@ contract StakeRegistryUnitTests_StakeUpdates is StakeRegistryUnitTests { // IStakeRegistry.StakeUpdate memory newTotalStake = newTotalStakes[i]; // Sanity-check setup - operator should start with minimumStake - assertTrue(prevOperatorStake.stake == minimumStake, "operator should start with nonzero stake"); + assertTrue( + prevOperatorStake.stake == minimumStake, + "operator should start with nonzero stake" + ); if (endingWeight > minimumStake) { // Check updating an operator who has added stake above the minimum: uint96 stakeAdded = setup.stakeDeltaAbs; - assertEq(prevOperatorStake.stake + stakeAdded, newOperatorStake.stake, "failed to add delta to operator stake"); - assertEq(prevTotalStakes[i].stake + stakeAdded, newTotalStakes[i].stake, "failed to add delta to total stake"); + assertEq( + prevOperatorStake.stake + stakeAdded, + newOperatorStake.stake, + "failed to add delta to operator stake" + ); + assertEq( + prevTotalStakes[i].stake + stakeAdded, + newTotalStakes[i].stake, + "failed to add delta to total stake" + ); // Return value should be empty since we're still above the minimum - assertTrue(quorumsToRemove.isEmpty(), "positive stake delta should not remove any quorums"); - assertEq(prevOperatorHistoryLengths[i] + 1, newOperatorHistoryLengths[i], "operator should have a new pushed update"); + assertTrue( + quorumsToRemove.isEmpty(), + "positive stake delta should not remove any quorums" + ); + assertEq( + prevOperatorHistoryLengths[i] + 1, + newOperatorHistoryLengths[i], + "operator should have a new pushed update" + ); } else if (endingWeight < minimumStake) { // Check updating an operator who is now below the minimum: // Stake should now be zero, regardless of stake delta uint96 stakeRemoved = minimumStake; - assertEq(prevOperatorStake.stake - stakeRemoved, newOperatorStake.stake, "failed to remove delta from operator stake"); + assertEq( + prevOperatorStake.stake - stakeRemoved, + newOperatorStake.stake, + "failed to remove delta from operator stake" + ); // assertEq(prevTotalStake.stake - stakeRemoved, newTotalStake.stake, "failed to remove delta from total stake"); assertEq(newOperatorStake.stake, 0, "operator stake should now be zero"); // Quorum should be added to return bitmap - assertTrue(quorumsToRemove.isSet(quorumNumber), "quorum should be in removal bitmap"); + assertTrue( + quorumsToRemove.isSet(quorumNumber), "quorum should be in removal bitmap" + ); if (prevOperatorStake.stake >= minimumStake) { // Total stakes and operator history should be updated - assertEq(prevOperatorHistoryLengths[i] + 1, newOperatorHistoryLengths[i], "operator should have a new pushed update"); - assertEq(prevTotalStakes[i].stake, newTotalStakes[i].stake + prevOperatorStake.stake, "failed to remove from total stake"); + assertEq( + prevOperatorHistoryLengths[i] + 1, + newOperatorHistoryLengths[i], + "operator should have a new pushed update" + ); + assertEq( + prevTotalStakes[i].stake, + newTotalStakes[i].stake + prevOperatorStake.stake, + "failed to remove from total stake" + ); } else { // Total stakes and history should remain unchanged - assertEq(prevOperatorHistoryLengths[i], newOperatorHistoryLengths[i], "history lengths should remain unchanged"); - assertEq(prevTotalStakes[i].stake, newTotalStakes[i].stake, "total stake should remain unchanged"); + assertEq( + prevOperatorHistoryLengths[i], + newOperatorHistoryLengths[i], + "history lengths should remain unchanged" + ); + assertEq( + prevTotalStakes[i].stake, + newTotalStakes[i].stake, + "total stake should remain unchanged" + ); } } else { // Check that no update occurs if weight remains the same - assertTrue(_isUnchanged(prevOperatorStake, newOperatorStake), "neutral stake delta should not have changed operator stake history"); - assertTrue(_isUnchanged(prevTotalStakes[i], newTotalStakes[i]), "neutral stake delta should not have changed total stake history"); + assertTrue( + _isUnchanged(prevOperatorStake, newOperatorStake), + "neutral stake delta should not have changed operator stake history" + ); + assertTrue( + _isUnchanged(prevTotalStakes[i], newTotalStakes[i]), + "neutral stake delta should not have changed total stake history" + ); // Check that return value is empty - we're still at the minimum, so no quorums should be removed - assertTrue(quorumsToRemove.isEmpty(), "neutral stake delta should not remove any quorums"); - assertEq(prevOperatorHistoryLengths[i], newOperatorHistoryLengths[i], "history lengths should remain unchanged"); + assertTrue( + quorumsToRemove.isEmpty(), + "neutral stake delta should not remove any quorums" + ); + assertEq( + prevOperatorHistoryLengths[i], + newOperatorHistoryLengths[i], + "history lengths should remain unchanged" + ); } } } @@ -1715,7 +2116,7 @@ contract StakeRegistryUnitTests_weightOfOperatorForQuorum is StakeRegistryUnitTe /** * @dev Initialize a new quorum with fuzzed multipliers and corresponding shares for an operator. - * The minimum stake for the quorum is 0 so that any fuzzed input shares will register the operator + * The minimum stake for the quorum is 0 so that any fuzzed input shares will register the operator * successfully and return a value for weightOfOperatorForQuorum. Fuzz test sets the operator shares * and asserts that the summed weight of the operator is correct. */ @@ -1730,23 +2131,30 @@ contract StakeRegistryUnitTests_weightOfOperatorForQuorum is StakeRegistryUnitTe // Initialize quorum with strategies of fuzzed multipliers. // Bound multipliers and shares max values to prevent overflows - IStakeRegistry.StrategyParams[] memory strategyParams = new IStakeRegistry.StrategyParams[](3); - for (uint i = 0; i < strategyParams.length; i++) { - multipliers[i] = uint96(_randUint({rand: bytes32(uint256(multipliers[i])), min: 0, max: 1000*WEIGHTING_DIVISOR })); - shares[i] = uint96(_randUint({rand: bytes32(uint256(shares[i])), min: 0, max: 10e20 })); + IStakeRegistry.StrategyParams[] memory strategyParams = + new IStakeRegistry.StrategyParams[](3); + for (uint256 i = 0; i < strategyParams.length; i++) { + multipliers[i] = uint96( + _randUint({ + rand: bytes32(uint256(multipliers[i])), + min: 0, + max: 1000 * WEIGHTING_DIVISOR + }) + ); + shares[i] = uint96(_randUint({rand: bytes32(uint256(shares[i])), min: 0, max: 10e20})); - IStrategy strat = IStrategy(address(uint160(uint256(keccak256(abi.encodePacked("Voteweighing test", i)))))); - strategyParams[i] = IStakeRegistry.StrategyParams( - strat, - uint96(WEIGHTING_DIVISOR) + multipliers[i] + IStrategy strat = IStrategy( + address(uint160(uint256(keccak256(abi.encodePacked("Voteweighing test", i))))) ); + strategyParams[i] = + IStakeRegistry.StrategyParams(strat, uint96(WEIGHTING_DIVISOR) + multipliers[i]); } cheats.prank(address(registryCoordinator)); uint8 quorumNumber = nextQuorum; - stakeRegistry.initializeQuorum(quorumNumber, 0 /* minimumStake */, strategyParams); + stakeRegistry.initializeQuorum(quorumNumber, 0, /* minimumStake */ strategyParams); // set the operator shares - for (uint i = 0; i < strategyParams.length; i++) { + for (uint256 i = 0; i < strategyParams.length; i++) { delegationMock.setOperatorShares(operator, strategyParams[i].strategy, shares[i]); } @@ -1756,11 +2164,12 @@ contract StakeRegistryUnitTests_weightOfOperatorForQuorum is StakeRegistryUnitTe cheats.prank(address(registryCoordinator)); stakeRegistry.registerOperator(operator, defaultOperatorId, quorumNumbers); - // assert weight of the operator uint96 expectedWeight = 0; - for (uint i = 0; i < strategyParams.length; i++) { - expectedWeight += uint96(uint256(shares[i]) * uint256(strategyParams[i].multiplier) / WEIGHTING_DIVISOR); + for (uint256 i = 0; i < strategyParams.length; i++) { + expectedWeight += uint96( + uint256(shares[i]) * uint256(strategyParams[i].multiplier) / WEIGHTING_DIVISOR + ); } assertEq(stakeRegistry.weightOfOperatorForQuorum(quorumNumber, operator), expectedWeight); } @@ -1772,27 +2181,27 @@ contract StakeRegistryUnitTests_weightOfOperatorForQuorum is StakeRegistryUnitTe ) public { // 3 LST Strat multipliers, rETH, stETH, ETH uint96[] memory multipliers = new uint96[](3); - multipliers[0] = uint96(1070136092289993178); - multipliers[1] = uint96(1071364636818145808); - multipliers[2] = uint96(1000000000000000000); - - IStakeRegistry.StrategyParams[] memory strategyParams = new IStakeRegistry.StrategyParams[](3); - for (uint i = 0; i < strategyParams.length; i++) { - shares[i] = uint96(_randUint({rand: bytes32(uint256(shares[i])), min: 0, max: 1e24 })); - IStrategy strat = IStrategy(address(uint160(uint256(keccak256(abi.encodePacked("Voteweighing test", i)))))); - strategyParams[i] = IStakeRegistry.StrategyParams( - strat, - multipliers[i] + multipliers[0] = uint96(1_070_136_092_289_993_178); + multipliers[1] = uint96(1_071_364_636_818_145_808); + multipliers[2] = uint96(1_000_000_000_000_000_000); + + IStakeRegistry.StrategyParams[] memory strategyParams = + new IStakeRegistry.StrategyParams[](3); + for (uint256 i = 0; i < strategyParams.length; i++) { + shares[i] = uint96(_randUint({rand: bytes32(uint256(shares[i])), min: 0, max: 1e24})); + IStrategy strat = IStrategy( + address(uint160(uint256(keccak256(abi.encodePacked("Voteweighing test", i))))) ); + strategyParams[i] = IStakeRegistry.StrategyParams(strat, multipliers[i]); } // create a valid quorum cheats.prank(address(registryCoordinator)); uint8 quorumNumber = nextQuorum; - stakeRegistry.initializeQuorum(quorumNumber, 0 /* minimumStake */, strategyParams); + stakeRegistry.initializeQuorum(quorumNumber, 0, /* minimumStake */ strategyParams); // set the operator shares - for (uint i = 0; i < strategyParams.length; i++) { + for (uint256 i = 0; i < strategyParams.length; i++) { delegationMock.setOperatorShares(operator, strategyParams[i].strategy, shares[i]); } @@ -1802,11 +2211,12 @@ contract StakeRegistryUnitTests_weightOfOperatorForQuorum is StakeRegistryUnitTe cheats.prank(address(registryCoordinator)); stakeRegistry.registerOperator(operator, defaultOperatorId, quorumNumbers); - // assert weight of the operator uint96 expectedWeight = 0; - for (uint i = 0; i < strategyParams.length; i++) { - expectedWeight += uint96(uint256(shares[i]) * uint256(strategyParams[i].multiplier) / WEIGHTING_DIVISOR); + for (uint256 i = 0; i < strategyParams.length; i++) { + expectedWeight += uint96( + uint256(shares[i]) * uint256(strategyParams[i].multiplier) / WEIGHTING_DIVISOR + ); } assertEq(stakeRegistry.weightOfOperatorForQuorum(quorumNumber, operator), expectedWeight); } diff --git a/test/utils/MockAVSDeployer.sol b/test/utils/MockAVSDeployer.sol index 4f78f3dc..6db2781f 100644 --- a/test/utils/MockAVSDeployer.sol +++ b/test/utils/MockAVSDeployer.sol @@ -24,7 +24,7 @@ import {IStakeRegistry} from "../../src/interfaces/IStakeRegistry.sol"; import {IIndexRegistry} from "../../src/interfaces/IIndexRegistry.sol"; import {IRegistryCoordinator} from "../../src/interfaces/IRegistryCoordinator.sol"; import {IServiceManager} from "../../src/interfaces/IServiceManager.sol"; - +import {SocketRegistry} from "../../src/SocketRegistry.sol"; import {StrategyManagerMock} from "eigenlayer-contracts/src/test/mocks/StrategyManagerMock.sol"; import {EigenPodManagerMock} from "eigenlayer-contracts/src/test/mocks/EigenPodManagerMock.sol"; @@ -33,6 +33,10 @@ 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"; +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 {BLSApkRegistryHarness} from "../harnesses/BLSApkRegistryHarness.sol"; import {EmptyContract} from "eigenlayer-contracts/src/test/mocks/EmptyContract.sol"; @@ -44,7 +48,7 @@ import "forge-std/Test.sol"; contract MockAVSDeployer is Test { using BN254 for BN254.G1Point; - Vm cheats = Vm(HEVM_ADDRESS); + Vm cheats = Vm(VM_ADDRESS); ProxyAdmin public proxyAdmin; PauserRegistry public pauserRegistry; @@ -58,6 +62,7 @@ contract MockAVSDeployer is Test { StakeRegistryHarness public stakeRegistryImplementation; IBLSApkRegistry public blsApkRegistryImplementation; IIndexRegistry public indexRegistryImplementation; + SocketRegistry public socketRegistryImplementation; ServiceManagerMock public serviceManagerImplementation; OperatorStateRetriever public operatorStateRetriever; @@ -66,6 +71,7 @@ contract MockAVSDeployer is Test { BLSApkRegistryHarness public blsApkRegistry; IIndexRegistry public indexRegistry; ServiceManagerMock public serviceManager; + SocketRegistry public socketRegistry; StrategyManagerMock public strategyManagerMock; DelegationMock public delegationMock; @@ -73,12 +79,16 @@ contract MockAVSDeployer is Test { AVSDirectory public avsDirectory; AVSDirectory public avsDirectoryImplementation; AVSDirectoryMock public avsDirectoryMock; + RewardsCoordinator public rewardsCoordinator; + RewardsCoordinator public rewardsCoordinatorImplementation; + RewardsCoordinatorMock public rewardsCoordinatorMock; /// @notice StakeRegistry, Constant used as a divisor in calculating weights. uint256 public constant WEIGHTING_DIVISOR = 1e18; address public proxyAdminOwner = address(uint160(uint256(keccak256("proxyAdminOwner")))); - address public registryCoordinatorOwner = address(uint160(uint256(keccak256("registryCoordinatorOwner")))); + address public registryCoordinatorOwner = + address(uint160(uint256(keccak256("registryCoordinatorOwner")))); address public pauser = address(uint160(uint256(keccak256("pauser")))); address public unpauser = address(uint160(uint256(keccak256("unpauser")))); @@ -90,13 +100,16 @@ contract MockAVSDeployer is Test { address defaultOperator = address(uint160(uint256(keccak256("defaultOperator")))); bytes32 defaultOperatorId; - BN254.G1Point internal defaultPubKey = BN254.G1Point(18260007818883133054078754218619977578772505796600400998181738095793040006897,3432351341799135763167709827653955074218841517684851694584291831827675065899); + BN254.G1Point internal defaultPubKey = BN254.G1Point( + 18_260_007_818_883_133_054_078_754_218_619_977_578_772_505_796_600_400_998_181_738_095_793_040_006_897, + 3_432_351_341_799_135_763_167_709_827_653_955_074_218_841_517_684_851_694_584_291_831_827_675_065_899 + ); string defaultSocket = "69.69.69.69:420"; uint96 defaultStake = 1 ether; uint8 defaultQuorumNumber = 0; uint32 defaultMaxOperatorCount = 10; - uint16 defaultKickBIPsOfOperatorStake = 15000; + uint16 defaultKickBIPsOfOperatorStake = 15_000; uint16 defaultKickBIPsOfTotalStake = 150; uint8 numQuorums = 192; @@ -135,7 +148,6 @@ contract MockAVSDeployer is Test { pausers[0] = pauser; pauserRegistry = new PauserRegistry(pausers, unpauser); - delegationMock = new DelegationMock(); avsDirectoryMock = new AVSDirectoryMock(); eigenPodManagerMock = new EigenPodManagerMock(pauserRegistry); @@ -146,7 +158,12 @@ contract MockAVSDeployer is Test { new TransparentUpgradeableProxy( address(slasherImplementation), address(proxyAdmin), - abi.encodeWithSelector(Slasher.initialize.selector, msg.sender, pauserRegistry, 0/*initialPausedStatus*/) + abi.encodeWithSelector( + Slasher.initialize.selector, + msg.sender, + pauserRegistry, + 0 /*initialPausedStatus*/ + ) ) ) ); @@ -157,64 +174,54 @@ contract MockAVSDeployer is Test { new TransparentUpgradeableProxy( address(avsDirectoryImplementation), address(proxyAdmin), - abi.encodeWithSelector(AVSDirectory.initialize.selector, msg.sender, pauserRegistry, 0/*initialPausedStatus*/) + abi.encodeWithSelector( + AVSDirectory.initialize.selector, + msg.sender, + pauserRegistry, + 0 /*initialPausedStatus*/ + ) ) ) ); + rewardsCoordinatorMock = new RewardsCoordinatorMock(); - strategyManagerMock.setAddresses( - delegationMock, - eigenPodManagerMock, - slasher - ); + strategyManagerMock.setAddresses(delegationMock, eigenPodManagerMock, slasher); cheats.stopPrank(); cheats.startPrank(registryCoordinatorOwner); - registryCoordinator = RegistryCoordinatorHarness(address( - new TransparentUpgradeableProxy( - address(emptyContract), - address(proxyAdmin), - "" + registryCoordinator = RegistryCoordinatorHarness( + address( + new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), "") ) - )); + ); stakeRegistry = StakeRegistryHarness( address( - new TransparentUpgradeableProxy( - address(emptyContract), - address(proxyAdmin), - "" - ) + new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), "") + ) + ); + + socketRegistry = SocketRegistry( + address( + new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), "") ) ); indexRegistry = IndexRegistry( address( - new TransparentUpgradeableProxy( - address(emptyContract), - address(proxyAdmin), - "" - ) + new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), "") ) ); blsApkRegistry = BLSApkRegistryHarness( address( - new TransparentUpgradeableProxy( - address(emptyContract), - address(proxyAdmin), - "" - ) + new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), "") ) ); serviceManager = ServiceManagerMock( address( - new TransparentUpgradeableProxy( - address(emptyContract), - address(proxyAdmin), - "" - ) + new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), "") ) ); @@ -222,28 +229,29 @@ contract MockAVSDeployer is Test { cheats.startPrank(proxyAdminOwner); - stakeRegistryImplementation = new StakeRegistryHarness( - IRegistryCoordinator(registryCoordinator), - delegationMock - ); + stakeRegistryImplementation = + new StakeRegistryHarness(IRegistryCoordinator(registryCoordinator), delegationMock); proxyAdmin.upgrade( TransparentUpgradeableProxy(payable(address(stakeRegistry))), address(stakeRegistryImplementation) ); - blsApkRegistryImplementation = new BLSApkRegistryHarness( - registryCoordinator + socketRegistryImplementation = new SocketRegistry(registryCoordinator); + + proxyAdmin.upgrade( + TransparentUpgradeableProxy(payable(address(socketRegistry))), + address(socketRegistryImplementation) ); + blsApkRegistryImplementation = new BLSApkRegistryHarness(registryCoordinator); + proxyAdmin.upgrade( TransparentUpgradeableProxy(payable(address(blsApkRegistry))), address(blsApkRegistryImplementation) ); - indexRegistryImplementation = new IndexRegistry( - registryCoordinator - ); + indexRegistryImplementation = new IndexRegistry(registryCoordinator); proxyAdmin.upgrade( TransparentUpgradeableProxy(payable(address(indexRegistry))), @@ -252,6 +260,7 @@ contract MockAVSDeployer is Test { serviceManagerImplementation = new ServiceManagerMock( avsDirectoryMock, + IRewardsCoordinator(address(rewardsCoordinatorMock)), registryCoordinator, stakeRegistry ); @@ -261,7 +270,10 @@ contract MockAVSDeployer is Test { address(serviceManagerImplementation) ); - serviceManager.initialize({initialOwner: registryCoordinatorOwner}); + serviceManager.initialize({ + initialOwner: registryCoordinatorOwner, + rewardsInitiator: address(proxyAdminOwner) + }); // set the public key for an operator, using harnessed function to bypass checks blsApkRegistry.setBLSPublicKey(defaultOperator, defaultPubKey); @@ -269,7 +281,7 @@ contract MockAVSDeployer is Test { // setup the dummy minimum stake for quorum uint96[] memory minimumStakeForQuorum = new uint96[](numQuorumsToAdd); for (uint256 i = 0; i < minimumStakeForQuorum.length; i++) { - minimumStakeForQuorum[i] = uint96(i+1); + minimumStakeForQuorum[i] = uint96(i + 1); } // setup the dummy quorum strategies @@ -278,26 +290,24 @@ contract MockAVSDeployer is Test { for (uint256 i = 0; i < quorumStrategiesConsideredAndMultipliers.length; i++) { quorumStrategiesConsideredAndMultipliers[i] = new IStakeRegistry.StrategyParams[](1); quorumStrategiesConsideredAndMultipliers[i][0] = IStakeRegistry.StrategyParams( - IStrategy(address(uint160(i))), - uint96(WEIGHTING_DIVISOR) + IStrategy(address(uint160(i))), uint96(WEIGHTING_DIVISOR) ); } registryCoordinatorImplementation = new RegistryCoordinatorHarness( - serviceManager, - stakeRegistry, - blsApkRegistry, - indexRegistry + serviceManager, stakeRegistry, blsApkRegistry, indexRegistry, socketRegistry ); { delete operatorSetParams; - for (uint i = 0; i < numQuorumsToAdd; i++) { + for (uint256 i = 0; i < numQuorumsToAdd; i++) { // hard code these for now - operatorSetParams.push(IRegistryCoordinator.OperatorSetParam({ - maxOperatorCount: defaultMaxOperatorCount, - kickBIPsOfOperatorStake: defaultKickBIPsOfOperatorStake, - kickBIPsOfTotalStake: defaultKickBIPsOfTotalStake - })); + operatorSetParams.push( + IRegistryCoordinator.OperatorSetParam({ + maxOperatorCount: defaultMaxOperatorCount, + kickBIPsOfOperatorStake: defaultKickBIPsOfOperatorStake, + kickBIPsOfTotalStake: defaultKickBIPsOfTotalStake + }) + ); } proxyAdmin.upgradeAndCall( @@ -309,7 +319,7 @@ contract MockAVSDeployer is Test { churnApprover, ejector, pauserRegistry, - 0/*initialPausedStatus*/, + 0, /*initialPausedStatus*/ operatorSetParams, minimumStakeForQuorum, quorumStrategiesConsideredAndMultipliers @@ -325,75 +335,101 @@ contract MockAVSDeployer is Test { /** * @notice registers operator with coordinator */ - function _registerOperatorWithCoordinator(address operator, uint256 quorumBitmap, BN254.G1Point memory pubKey) internal { + function _registerOperatorWithCoordinator( + address operator, + uint256 quorumBitmap, + BN254.G1Point memory pubKey + ) internal { _registerOperatorWithCoordinator(operator, quorumBitmap, pubKey, defaultStake); } /** * @notice registers operator with coordinator */ - function _registerOperatorWithCoordinator(address operator, uint256 quorumBitmap, BN254.G1Point memory pubKey, uint96 stake) internal { + function _registerOperatorWithCoordinator( + address operator, + uint256 quorumBitmap, + BN254.G1Point memory pubKey, + uint96 stake + ) internal { // quorumBitmap can only have 192 least significant bits quorumBitmap &= MAX_QUORUM_BITMAP; blsApkRegistry.setBLSPublicKey(operator, pubKey); bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); - for (uint i = 0; i < quorumNumbers.length; i++) { + for (uint256 i = 0; i < quorumNumbers.length; i++) { _setOperatorWeight(operator, uint8(quorumNumbers[i]), stake); } ISignatureUtils.SignatureWithSaltAndExpiry memory emptySignatureAndExpiry; cheats.prank(operator); - registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySignatureAndExpiry); + registryCoordinator.registerOperator( + quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySignatureAndExpiry + ); } /** * @notice registers operator with coordinator */ - function _registerOperatorWithCoordinator(address operator, uint256 quorumBitmap, BN254.G1Point memory pubKey, uint96[] memory stakes) internal { + function _registerOperatorWithCoordinator( + address operator, + uint256 quorumBitmap, + BN254.G1Point memory pubKey, + uint96[] memory stakes + ) internal { // quorumBitmap can only have 192 least significant bits quorumBitmap &= MAX_QUORUM_BITMAP; blsApkRegistry.setBLSPublicKey(operator, pubKey); bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); - for (uint i = 0; i < quorumNumbers.length; i++) { + for (uint256 i = 0; i < quorumNumbers.length; i++) { _setOperatorWeight(operator, uint8(quorumNumbers[i]), stakes[uint8(quorumNumbers[i])]); } ISignatureUtils.SignatureWithSaltAndExpiry memory emptySignatureAndExpiry; cheats.prank(operator); - registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySignatureAndExpiry); + registryCoordinator.registerOperator( + quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySignatureAndExpiry + ); } - function _registerRandomOperators(uint256 pseudoRandomNumber) internal returns(OperatorMetadata[] memory, uint256[][] memory) { + function _registerRandomOperators(uint256 pseudoRandomNumber) + internal + returns (OperatorMetadata[] memory, uint256[][] memory) + { OperatorMetadata[] memory operatorMetadatas = new OperatorMetadata[](maxOperatorsToRegister); - for (uint i = 0; i < operatorMetadatas.length; i++) { + for (uint256 i = 0; i < operatorMetadatas.length; i++) { // limit to 16 quorums so we don't run out of gas, make them all register for quorum 0 as well - operatorMetadatas[i].quorumBitmap = uint256(keccak256(abi.encodePacked("quorumBitmap", pseudoRandomNumber, i))) & (1 << maxQuorumsToRegisterFor - 1) | 1; + operatorMetadatas[i].quorumBitmap = uint256( + keccak256(abi.encodePacked("quorumBitmap", pseudoRandomNumber, i)) + ) & (1 << maxQuorumsToRegisterFor - 1) | 1; operatorMetadatas[i].operator = _incrementAddress(defaultOperator, i); - operatorMetadatas[i].pubkey = BN254.hashToG1(keccak256(abi.encodePacked("pubkey", pseudoRandomNumber, i))); + operatorMetadatas[i].pubkey = + BN254.hashToG1(keccak256(abi.encodePacked("pubkey", pseudoRandomNumber, i))); operatorMetadatas[i].operatorId = operatorMetadatas[i].pubkey.hashG1Point(); operatorMetadatas[i].stakes = new uint96[](maxQuorumsToRegisterFor); - for (uint j = 0; j < maxQuorumsToRegisterFor; j++) { - operatorMetadatas[i].stakes[j] = uint96(uint64(uint256(keccak256(abi.encodePacked("stakes", pseudoRandomNumber, i, j))))); + for (uint256 j = 0; j < maxQuorumsToRegisterFor; j++) { + operatorMetadatas[i].stakes[j] = uint96( + uint64(uint256(keccak256(abi.encodePacked("stakes", pseudoRandomNumber, i, j)))) + ); } } // get the index in quorumBitmaps of each operator in each quorum in the order they will register uint256[][] memory expectedOperatorOverallIndices = new uint256[][](numQuorums); - for (uint i = 0; i < numQuorums; i++) { + for (uint256 i = 0; i < numQuorums; i++) { uint32 numOperatorsInQuorum; // for each quorumBitmap, check if the i'th bit is set - for (uint j = 0; j < operatorMetadatas.length; j++) { + for (uint256 j = 0; j < operatorMetadatas.length; j++) { if (operatorMetadatas[j].quorumBitmap >> i & 1 == 1) { numOperatorsInQuorum++; } } expectedOperatorOverallIndices[i] = new uint256[](numOperatorsInQuorum); uint256 numOperatorCounter; - for (uint j = 0; j < operatorMetadatas.length; j++) { + for (uint256 j = 0; j < operatorMetadatas.length; j++) { if (operatorMetadatas[j].quorumBitmap >> i & 1 == 1) { expectedOperatorOverallIndices[i][numOperatorCounter] = j; numOperatorCounter++; @@ -402,10 +438,15 @@ contract MockAVSDeployer is Test { } // register operators - for (uint i = 0; i < operatorMetadatas.length; i++) { + for (uint256 i = 0; i < operatorMetadatas.length; i++) { cheats.roll(registrationBlockNumber + blocksBetweenRegistrations * i); - _registerOperatorWithCoordinator(operatorMetadatas[i].operator, operatorMetadatas[i].quorumBitmap, operatorMetadatas[i].pubkey, operatorMetadatas[i].stakes); + _registerOperatorWithCoordinator( + operatorMetadatas[i].operator, + operatorMetadatas[i].quorumBitmap, + operatorMetadatas[i].pubkey, + operatorMetadatas[i].stakes + ); } return (operatorMetadatas, expectedOperatorOverallIndices); @@ -417,7 +458,11 @@ contract MockAVSDeployer is Test { * Returns actual weight calculated set for operator shares in DelegationMock since multiplier and WEIGHTING_DIVISOR calculations * can give small rounding errors. */ - function _setOperatorWeight(address operator, uint8 quorumNumber, uint96 weight) internal returns (uint96) { + function _setOperatorWeight( + address operator, + uint8 quorumNumber, + uint96 weight + ) internal returns (uint96) { // Set StakeRegistry operator weight by setting DelegationManager operator shares (IStrategy strategy, uint96 multiplier) = stakeRegistry.strategyParams(quorumNumber, 0); uint256 actualWeight = ((uint256(weight) * WEIGHTING_DIVISOR) / uint256(multiplier)); @@ -425,21 +470,23 @@ contract MockAVSDeployer is Test { return uint96(actualWeight); } - function _incrementAddress(address start, uint256 inc) internal pure returns(address) { + function _incrementAddress(address start, uint256 inc) internal pure returns (address) { return address(uint160(uint256(uint160(start) + inc))); } - function _incrementBytes32(bytes32 start, uint256 inc) internal pure returns(bytes32) { + function _incrementBytes32(bytes32 start, uint256 inc) internal pure returns (bytes32) { return bytes32(uint256(start) + inc); } - function _signOperatorChurnApproval(address registeringOperator, bytes32 registeringOperatorId, IRegistryCoordinator.OperatorKickParam[] memory operatorKickParams, bytes32 salt, uint256 expiry) internal view returns(ISignatureUtils.SignatureWithSaltAndExpiry memory) { + function _signOperatorChurnApproval( + address registeringOperator, + bytes32 registeringOperatorId, + IRegistryCoordinator.OperatorKickParam[] memory operatorKickParams, + bytes32 salt, + uint256 expiry + ) internal view returns (ISignatureUtils.SignatureWithSaltAndExpiry memory) { bytes32 digestHash = registryCoordinator.calculateOperatorChurnApprovalDigestHash( - registeringOperator, - registeringOperatorId, - operatorKickParams, - salt, - expiry + registeringOperator, registeringOperatorId, operatorKickParams, salt, expiry ); (uint8 v, bytes32 r, bytes32 s) = vm.sign(churnApproverPrivateKey, digestHash); return ISignatureUtils.SignatureWithSaltAndExpiry({
  • n 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