From 47b560dbfc279d478e560d2d2145a3a363ef447e Mon Sep 17 00:00:00 2001 From: Michael Sun Date: Tue, 21 Jan 2025 16:17:06 -0500 Subject: [PATCH] refactor: slashing registry coordinator refactor: registry coordinator refactor: remove sm calls fix: tests wip --- src/BLSApkRegistry.sol | 6 +- src/BLSApkRegistryStorage.sol | 8 +- src/BLSSignatureChecker.sol | 8 +- src/EjectionManager.sol | 9 +- src/IndexRegistry.sol | 6 +- src/IndexRegistryStorage.sol | 8 +- src/OperatorStateRetriever.sol | 14 +- src/RegistryCoordinator.sol | 1061 +------- src/ServiceManagerBase.sol | 4 +- src/ServiceManagerBaseStorage.sol | 6 +- src/SlashingRegistryCoordinator.sol | 1055 +++++++ ...=> SlashingRegistryCoordinatorStorage.sol} | 28 +- src/StakeRegistry.sol | 69 +- src/StakeRegistryStorage.sol | 24 +- src/interfaces/IBLSSignatureChecker.sol | 4 +- src/interfaces/IRegistryCoordinator.sol | 240 +- .../ISlashingRegistryCoordinator.sol | 205 ++ src/interfaces/IStakeRegistry.sol | 4 +- src/libraries/QuorumBitmapHistoryLib.sol | 20 +- src/slashers/InstantSlasher.sol | 9 +- src/slashers/VetoableSlasher.sol | 6 +- src/slashers/base/SlasherBase.sol | 25 +- src/slashers/base/SlasherStorage.sol | 19 +- test/ffi/BLSPubKeyCompendiumFFI.t.sol | 3 +- test/harnesses/BLSApkRegistryHarness.sol | 4 +- test/harnesses/StakeRegistryHarness.sol | 16 +- test/integration/IntegrationBase.t.sol | 26 +- test/integration/IntegrationConfig.t.sol | 17 +- test/integration/IntegrationDeployer.t.sol | 20 +- test/integration/User.t.sol | 22 +- test/mocks/RegistryCoordinatorMock.sol | 30 +- test/mocks/ServiceManagerMock.sol | 4 +- test/unit/EjectionManagerUnit.t.sol | 123 +- test/unit/OperatorStateRetrieverUnit.t.sol | 9 +- test/unit/RegistryCoordinatorUnit.t.sol | 683 ++--- .../SlashingRegistryCoordinatorUnit.t.sol | 2421 +++++++++++++++++ test/unit/StakeRegistryUnit.t.sol | 22 +- test/utils/MockAVSDeployer.sol | 85 +- 38 files changed, 4290 insertions(+), 2033 deletions(-) create mode 100644 src/SlashingRegistryCoordinator.sol rename src/{RegistryCoordinatorStorage.sol => SlashingRegistryCoordinatorStorage.sol} (86%) create mode 100644 src/interfaces/ISlashingRegistryCoordinator.sol create mode 100644 test/unit/SlashingRegistryCoordinatorUnit.t.sol diff --git a/src/BLSApkRegistry.sol b/src/BLSApkRegistry.sol index 93972f5c..65d125fb 100644 --- a/src/BLSApkRegistry.sol +++ b/src/BLSApkRegistry.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.27; import {BLSApkRegistryStorage} from "./BLSApkRegistryStorage.sol"; -import {IRegistryCoordinator} from "./interfaces/IRegistryCoordinator.sol"; +import {ISlashingRegistryCoordinator} from "./interfaces/ISlashingRegistryCoordinator.sol"; import {BN254} from "./libraries/BN254.sol"; @@ -18,8 +18,8 @@ contract BLSApkRegistry is BLSApkRegistryStorage { /// @notice Sets the (immutable) `registryCoordinator` address constructor( - IRegistryCoordinator _registryCoordinator - ) BLSApkRegistryStorage(_registryCoordinator) {} + ISlashingRegistryCoordinator _slashingRegistryCoordinator + ) BLSApkRegistryStorage(_slashingRegistryCoordinator) {} /** * diff --git a/src/BLSApkRegistryStorage.sol b/src/BLSApkRegistryStorage.sol index 5007367a..7fab9d84 100644 --- a/src/BLSApkRegistryStorage.sol +++ b/src/BLSApkRegistryStorage.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.27; import {IBLSApkRegistry} from "./interfaces/IBLSApkRegistry.sol"; -import {IRegistryCoordinator} from "./interfaces/IRegistryCoordinator.sol"; +import {ISlashingRegistryCoordinator} from "./interfaces/ISlashingRegistryCoordinator.sol"; import {Initializable} from "@openzeppelin-upgrades/contracts/proxy/utils/Initializable.sol"; @@ -30,10 +30,8 @@ abstract contract BLSApkRegistryStorage is Initializable, IBLSApkRegistry { /// @notice maps quorumNumber => current aggregate pubkey of quorum mapping(uint8 => BN254.G1Point) public currentApk; - constructor( - IRegistryCoordinator _registryCoordinator - ) { - registryCoordinator = address(_registryCoordinator); + constructor(ISlashingRegistryCoordinator _slashingRegistryCoordinator) { + registryCoordinator = address(_slashingRegistryCoordinator); // disable initializers so that the implementation contract cannot be initialized _disableInitializers(); } diff --git a/src/BLSSignatureChecker.sol b/src/BLSSignatureChecker.sol index 2ff55e37..ffa46872 100644 --- a/src/BLSSignatureChecker.sol +++ b/src/BLSSignatureChecker.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.27; import {IBLSSignatureChecker} from "./interfaces/IBLSSignatureChecker.sol"; -import {IRegistryCoordinator} from "./interfaces/IRegistryCoordinator.sol"; +import {ISlashingRegistryCoordinator} from "./interfaces/ISlashingRegistryCoordinator.sol"; import {IBLSApkRegistry} from "./interfaces/IBLSApkRegistry.sol"; import {IStakeRegistry, IDelegationManager} from "./interfaces/IStakeRegistry.sol"; @@ -23,7 +23,7 @@ contract BLSSignatureChecker is IBLSSignatureChecker { // gas cost of multiplying 2 pairings uint256 internal constant PAIRING_EQUALITY_CHECK_GAS = 120_000; - IRegistryCoordinator public immutable registryCoordinator; + ISlashingRegistryCoordinator public immutable registryCoordinator; IStakeRegistry public immutable stakeRegistry; IBLSApkRegistry public immutable blsApkRegistry; IDelegationManager public immutable delegation; @@ -35,9 +35,7 @@ contract BLSSignatureChecker is IBLSSignatureChecker { _; } - constructor( - IRegistryCoordinator _registryCoordinator - ) { + constructor(ISlashingRegistryCoordinator _registryCoordinator) { registryCoordinator = _registryCoordinator; stakeRegistry = _registryCoordinator.stakeRegistry(); blsApkRegistry = _registryCoordinator.blsApkRegistry(); diff --git a/src/EjectionManager.sol b/src/EjectionManager.sol index f7d3d55d..f40e754e 100644 --- a/src/EjectionManager.sol +++ b/src/EjectionManager.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.27; import {OwnableUpgradeable} from "@openzeppelin-upgrades/contracts/access/OwnableUpgradeable.sol"; import {IEjectionManager} from "./interfaces/IEjectionManager.sol"; -import {IRegistryCoordinator} from "./interfaces/IRegistryCoordinator.sol"; +import {ISlashingRegistryCoordinator} from "./interfaces/ISlashingRegistryCoordinator.sol"; import {IStakeRegistry} from "./interfaces/IStakeRegistry.sol"; /** @@ -18,7 +18,7 @@ contract EjectionManager is IEjectionManager, OwnableUpgradeable { uint8 internal constant MAX_QUORUM_COUNT = 192; /// @notice the RegistryCoordinator contract that is the entry point for ejection - IRegistryCoordinator public immutable registryCoordinator; + ISlashingRegistryCoordinator public immutable registryCoordinator; /// @notice the StakeRegistry contract that keeps track of quorum stake IStakeRegistry public immutable stakeRegistry; @@ -30,7 +30,10 @@ contract EjectionManager is IEjectionManager, OwnableUpgradeable { /// @notice Ratelimit parameters for each quorum mapping(uint8 => QuorumEjectionParams) public quorumEjectionParams; - constructor(IRegistryCoordinator _registryCoordinator, IStakeRegistry _stakeRegistry) { + constructor( + ISlashingRegistryCoordinator _registryCoordinator, + IStakeRegistry _stakeRegistry + ) { registryCoordinator = _registryCoordinator; stakeRegistry = _stakeRegistry; diff --git a/src/IndexRegistry.sol b/src/IndexRegistry.sol index c8b46061..ef9b0c49 100644 --- a/src/IndexRegistry.sol +++ b/src/IndexRegistry.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.27; import {IndexRegistryStorage} from "./IndexRegistryStorage.sol"; -import {IRegistryCoordinator} from "./interfaces/IRegistryCoordinator.sol"; +import {ISlashingRegistryCoordinator} from "./interfaces/ISlashingRegistryCoordinator.sol"; /** * @title A `Registry` that keeps track of an ordered list of operators for each quorum @@ -17,8 +17,8 @@ contract IndexRegistry is IndexRegistryStorage { /// @notice sets the (immutable) `registryCoordinator` address constructor( - IRegistryCoordinator _registryCoordinator - ) IndexRegistryStorage(_registryCoordinator) {} + ISlashingRegistryCoordinator _slashingRegistryCoordinator + ) IndexRegistryStorage(_slashingRegistryCoordinator) {} /** * diff --git a/src/IndexRegistryStorage.sol b/src/IndexRegistryStorage.sol index 95f39e34..271aafe9 100644 --- a/src/IndexRegistryStorage.sol +++ b/src/IndexRegistryStorage.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.27; import {Initializable} from "@openzeppelin-upgrades/contracts/proxy/utils/Initializable.sol"; -import {IRegistryCoordinator} from "./interfaces/IRegistryCoordinator.sol"; +import {ISlashingRegistryCoordinator} from "./interfaces/ISlashingRegistryCoordinator.sol"; import {IIndexRegistry} from "./interfaces/IIndexRegistry.sol"; /** @@ -30,9 +30,9 @@ abstract contract IndexRegistryStorage is Initializable, IIndexRegistry { mapping(uint8 => QuorumUpdate[]) internal _operatorCountHistory; constructor( - IRegistryCoordinator _registryCoordinator - ) { - registryCoordinator = address(_registryCoordinator); + ISlashingRegistryCoordinator _slashingRegistryCoordinator + ){ + registryCoordinator = address(_slashingRegistryCoordinator); // disable initializers so that the implementation contract cannot be initialized _disableInitializers(); } diff --git a/src/OperatorStateRetriever.sol b/src/OperatorStateRetriever.sol index 82dc8d6e..d3e06490 100644 --- a/src/OperatorStateRetriever.sol +++ b/src/OperatorStateRetriever.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.27; -import {IRegistryCoordinator} from "./interfaces/IRegistryCoordinator.sol"; +import {ISlashingRegistryCoordinator} from "./interfaces/ISlashingRegistryCoordinator.sol"; import {IBLSApkRegistry} from "./interfaces/IBLSApkRegistry.sol"; import {IStakeRegistry} from "./interfaces/IStakeRegistry.sol"; import {IIndexRegistry} from "./interfaces/IIndexRegistry.sol"; @@ -40,7 +40,7 @@ contract OperatorStateRetriever { * was a part of at `blockNumber`, an ordered list of operators. */ function getOperatorState( - IRegistryCoordinator registryCoordinator, + ISlashingRegistryCoordinator registryCoordinator, bytes32 operatorId, uint32 blockNumber ) external view returns (uint256, Operator[][] memory) { @@ -66,7 +66,7 @@ contract OperatorStateRetriever { * @return 2d array of Operators. For each quorum, an ordered list of Operators */ function getOperatorState( - IRegistryCoordinator registryCoordinator, + ISlashingRegistryCoordinator registryCoordinator, bytes memory quorumNumbers, uint32 blockNumber ) public view returns (Operator[][] memory) { @@ -109,7 +109,7 @@ contract OperatorStateRetriever { * 4) the indices of the quorum apks for each of the provided quorums at the given blocknumber */ function getCheckSignaturesIndices( - IRegistryCoordinator registryCoordinator, + ISlashingRegistryCoordinator registryCoordinator, uint32 referenceBlockNumber, bytes calldata quorumNumbers, bytes32[] calldata nonSignerOperatorIds @@ -185,7 +185,7 @@ contract OperatorStateRetriever { * @param blockNumber is the block number to get the quorumBitmaps for */ function getQuorumBitmapsAtBlockNumber( - IRegistryCoordinator registryCoordinator, + ISlashingRegistryCoordinator registryCoordinator, bytes32[] memory operatorIds, uint32 blockNumber ) external view returns (uint256[] memory) { @@ -207,7 +207,7 @@ contract OperatorStateRetriever { * @dev if an operator is not registered, the operatorId will be 0 */ function getBatchOperatorId( - IRegistryCoordinator registryCoordinator, + ISlashingRegistryCoordinator registryCoordinator, address[] memory operators ) external view returns (bytes32[] memory operatorIds) { operatorIds = new bytes32[](operators.length); @@ -223,7 +223,7 @@ contract OperatorStateRetriever { * @dev if an operator is not registered, the operator address will be 0 */ function getBatchOperatorFromId( - IRegistryCoordinator registryCoordinator, + ISlashingRegistryCoordinator registryCoordinator, bytes32[] memory operatorIds ) external view returns (address[] memory operators) { operators = new address[](operatorIds.length); diff --git a/src/RegistryCoordinator.sol b/src/RegistryCoordinator.sol index fbcb154a..62b56105 100644 --- a/src/RegistryCoordinator.sol +++ b/src/RegistryCoordinator.sol @@ -2,33 +2,15 @@ pragma solidity ^0.8.27; import {IPauserRegistry} from "eigenlayer-contracts/src/contracts/interfaces/IPauserRegistry.sol"; -import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol"; -import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; -import { - IAllocationManager, - OperatorSet, - IAllocationManagerTypes -} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; -import {ISocketUpdater} from "./interfaces/ISocketUpdater.sol"; +import {IAllocationManager} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; import {IBLSApkRegistry} from "./interfaces/IBLSApkRegistry.sol"; -import {IStakeRegistry, StakeType} from "./interfaces/IStakeRegistry.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 {BitmapUtils} from "./libraries/BitmapUtils.sol"; -import {BN254} from "./libraries/BN254.sol"; -import {SignatureCheckerLib} from "./libraries/SignatureCheckerLib.sol"; -import {QuorumBitmapHistoryLib} from "./libraries/QuorumBitmapHistoryLib.sol"; -import {AVSRegistrar} from "./AVSRegistrar.sol"; - -import {OwnableUpgradeable} from "@openzeppelin-upgrades/contracts/access/OwnableUpgradeable.sol"; -import {Initializable} from "@openzeppelin-upgrades/contracts/proxy/utils/Initializable.sol"; -import {EIP712} from "@openzeppelin/contracts/utils/cryptography/draft-EIP712.sol"; - -import {Pausable} from "eigenlayer-contracts/src/contracts/permissions/Pausable.sol"; -import {RegistryCoordinatorStorage} from "./RegistryCoordinatorStorage.sol"; -import {IAVSRegistrar} from "eigenlayer-contracts/src/contracts/interfaces/IAVSRegistrar.sol"; +import {SlashingRegistryCoordinator} from "./SlashingRegistryCoordinator.sol"; /** * @title A `RegistryCoordinator` that has three registries: @@ -38,32 +20,11 @@ import {IAVSRegistrar} from "eigenlayer-contracts/src/contracts/interfaces/IAVSR * * @author Layr Labs, Inc. */ -contract RegistryCoordinator is - EIP712, - Initializable, - Pausable, - OwnableUpgradeable, - RegistryCoordinatorStorage, - AVSRegistrar, - ISocketUpdater, - ISignatureUtils -{ +contract RegistryCoordinator is SlashingRegistryCoordinator, IRegistryCoordinator { using BitmapUtils for *; - using BN254 for BN254.G1Point; - - modifier onlyEjector() { - _checkEjector(); - _; - } - /// @dev Checks that `quorumNumber` corresponds to a quorum that has been created - /// via `initialize` or `createQuorum` - modifier quorumExists( - uint8 quorumNumber - ) { - _checkQuorumExists(quorumNumber); - _; - } + /// @notice the ServiceManager for this AVS, which forwards calls onto EigenLayer's core contracts + IServiceManager public immutable serviceManager; constructor( IServiceManager _serviceManager, @@ -73,69 +34,15 @@ contract RegistryCoordinator is IAllocationManager _allocationManager, IPauserRegistry _pauserRegistry ) - RegistryCoordinatorStorage( - _serviceManager, + SlashingRegistryCoordinator( _stakeRegistry, _blsApkRegistry, _indexRegistry, - _allocationManager + _allocationManager, + _pauserRegistry ) - EIP712("AVSRegistryCoordinator", "v0.0.1") - Pausable(_pauserRegistry) { - _disableInitializers(); - } - - /** - * @param _initialOwner will hold the owner role - * @param _churnApprover will hold the churnApprover role, which authorizes registering with churn - * @param _ejector will hold the ejector role, which can force-eject operators from quorums - * @param _initialPausedStatus pause status after calling initialize - * Config for initial quorums (see `createQuorum`): - * @param _operatorSetParams max operator count and operator churn parameters - * @param _minimumStakes minimum stake weight to allow an operator to register - * @param _strategyParams which Strategies/multipliers a quorum considers when calculating stake weight - */ - function initialize( - address _initialOwner, - address _churnApprover, - address _ejector, - uint256 _initialPausedStatus, - OperatorSetParam[] memory _operatorSetParams, - uint96[] memory _minimumStakes, - IStakeRegistry.StrategyParams[][] memory _strategyParams, - StakeType[] memory _stakeTypes, - uint32[] memory _lookAheadPeriods - ) external initializer { - require( - _operatorSetParams.length == _minimumStakes.length - && _minimumStakes.length == _strategyParams.length - && _strategyParams.length == _stakeTypes.length - && _stakeTypes.length == _lookAheadPeriods.length, - InputLengthMismatch() - ); - - // Initialize roles - _transferOwnership(_initialOwner); - _setChurnApprover(_churnApprover); - _setPausedStatus(_initialPausedStatus); - _setEjector(_ejector); - - // Add registry contracts to the registries array - registries.push(address(stakeRegistry)); - registries.push(address(blsApkRegistry)); - registries.push(address(indexRegistry)); - - // Create quorums - for (uint256 i = 0; i < _operatorSetParams.length; i++) { - _createQuorum( - _operatorSetParams[i], - _minimumStakes[i], - _strategyParams[i], - _stakeTypes[i], - _lookAheadPeriods[i] - ); - } + serviceManager = _serviceManager; } /** @@ -144,23 +51,14 @@ contract RegistryCoordinator is * */ - /** - * @notice Registers msg.sender as an operator for one or more quorums. If any quorum exceeds its maximum - * operator capacity after the operator is registered, this method will fail. - * @param quorumNumbers is an ordered byte array containing the quorum numbers being registered for - * @param socket is the socket of the operator (typically an IP address) - * @param params contains the G1 & G2 public keys of the operator, and a signature proving their ownership - * @param operatorSignature is the signature of the operator used by the AVS to register the operator in the delegation manager - * @dev `params` is ignored if the caller has previously registered a public key - * @dev `operatorSignature` is ignored if the operator's status is already REGISTERED - */ + /// @inheritdoc IRegistryCoordinator function registerOperator( bytes memory quorumNumbers, string memory socket, IBLSApkRegistry.PubkeyRegistrationParams memory params, SignatureWithSaltAndExpiry memory operatorSignature ) external onlyWhenNotPaused(PAUSED_REGISTER_OPERATOR) { - require(!isUsingOperatorSets(), OperatorSetsEnabled()); + require(!isOperatorSetAVS, OperatorSetsEnabled()); /** * If the operator has NEVER registered a pubkey before, use `params` to register * their pubkey in blsApkRegistry @@ -192,18 +90,7 @@ contract RegistryCoordinator is } } - /** - * @notice Registers msg.sender as an operator for one or more quorums. If any quorum reaches its maximum operator - * capacity, `operatorKickParams` is used to replace an old operator with the new one. - * @param quorumNumbers is an ordered byte array containing the quorum numbers being registered for - * @param params contains the G1 & G2 public keys of the operator, and a signature proving their ownership - * @param operatorKickParams used to determine which operator is removed to maintain quorum capacity as the - * operator registers for quorums - * @param churnApproverSignature is the signature of the churnApprover over the `operatorKickParams` - * @param operatorSignature is the signature of the operator used by the AVS to register the operator in the delegation manager - * @dev `params` is ignored if the caller has previously registered a public key - * @dev `operatorSignature` is ignored if the operator's status is already REGISTERED - */ + /// @inheritdoc IRegistryCoordinator function registerOperatorWithChurn( bytes calldata quorumNumbers, string memory socket, @@ -212,8 +99,11 @@ contract RegistryCoordinator is SignatureWithSaltAndExpiry memory churnApproverSignature, SignatureWithSaltAndExpiry memory operatorSignature ) external onlyWhenNotPaused(PAUSED_REGISTER_OPERATOR) { - require(!isUsingOperatorSets(), OperatorSetsEnabled()); - require(operatorKickParams.length == quorumNumbers.length, InputLengthMismatch()); + require(!isOperatorSetAVS, OperatorSetsEnabled()); + require( + operatorKickParams.length == quorumNumbers.length, + InputLengthMismatch() + ); /** * If the operator has NEVER registered a pubkey before, use `params` to register @@ -266,27 +156,19 @@ contract RegistryCoordinator is } } - /** - * @notice Deregisters the caller from one or more quorums - * @param quorumNumbers is an ordered byte array containing the quorum numbers being deregistered from - */ + /// @inheritdoc IRegistryCoordinator function deregisterOperator( bytes memory quorumNumbers ) external onlyWhenNotPaused(PAUSED_DEREGISTER_OPERATOR) { - // Check that either: - // 1. The AVS hasn't migrated to operator sets yet (!isOperatorSetAVS), or - // 2. The AVS has migrated but this is an M2 quorum + // Check that the quorum numbers are M2 quorums and not operator sets + // if operator sets are enabled for (uint256 i = 0; i < quorumNumbers.length; i++) { - uint8 quorumNumber = uint8(quorumNumbers[i]); - require(!isOperatorSetAVS || isM2Quorum[quorumNumber], OperatorSetsEnabled()); + require(!isOperatorSetAVS || isM2Quorum[uint8(quorumNumbers[i])], OperatorSetsEnabled()); } _deregisterOperator({operator: msg.sender, quorumNumbers: quorumNumbers}); } - function isUsingOperatorSets() public view returns (bool) { - return isOperatorSetAVS; - } - + /// @inheritdoc IRegistryCoordinator function enableOperatorSets() external onlyOwner { require(!isOperatorSetAVS, OperatorSetsEnabled()); @@ -298,394 +180,12 @@ contract RegistryCoordinator is // Enable operator sets mode isOperatorSetAVS = true; } - enum RegistrationType { - NORMAL, - CHURN - } - - error InvalidRegistrationType(); - - function registerOperator( - address operator, - uint32[] memory operatorSetIds, - bytes calldata data - ) external override onlyWhenNotPaused(PAUSED_REGISTER_OPERATOR) { - require(isUsingOperatorSets(), OperatorSetsNotEnabled()); - for (uint256 i = 0; i < operatorSetIds.length; i++) { - require(!isM2Quorum[uint8(operatorSetIds[i])], OperatorSetsNotSupported()); - } - _checkAllocationManager(); - bytes memory quorumNumbers = new bytes(operatorSetIds.length); - for (uint256 i = 0; i < operatorSetIds.length; i++) { - quorumNumbers[i] = bytes1(uint8(operatorSetIds[i])); - } - - // Handle churn or normal registration based on first byte in `data` - RegistrationType registrationType = RegistrationType(uint8(bytes1(data[0:1]))); - if (registrationType == RegistrationType.NORMAL) { - (, string memory socket, IBLSApkRegistry.PubkeyRegistrationParams memory params) = abi.decode(data, (RegistrationType, string, IBLSApkRegistry.PubkeyRegistrationParams)); - bytes32 operatorId = _getOrCreateOperatorId(operator, params); - _registerOperatorToOperatorSet(operator, operatorId, quorumNumbers, socket); - } else if (registrationType == RegistrationType.CHURN) { - // Decode registration data from bytes - ( - , - string memory socket, - IBLSApkRegistry.PubkeyRegistrationParams memory params, - OperatorKickParam[] memory operatorKickParams, - SignatureWithSaltAndExpiry memory churnApproverSignature - ) = abi.decode( - data, - ( - RegistrationType, - string, - IBLSApkRegistry.PubkeyRegistrationParams, - OperatorKickParam[], - SignatureWithSaltAndExpiry - ) - ); - - _registerOperatorWithChurn(operator, quorumNumbers, socket, params, operatorKickParams, churnApproverSignature); - } else { - revert InvalidRegistrationType(); - } - } - - function _registerOperatorWithChurn( - address operator, - bytes memory quorumNumbers, - string memory socket, - IBLSApkRegistry.PubkeyRegistrationParams memory params, - OperatorKickParam[] memory operatorKickParams, - SignatureWithSaltAndExpiry memory churnApproverSignature - ) internal virtual { - require(operatorKickParams.length == quorumNumbers.length, InputLengthMismatch()); - - /** - * If the operator has NEVER registered a pubkey before, use `params` to register - * their pubkey in blsApkRegistry - * - * If the operator HAS registered a pubkey, `params` is ignored and the pubkey hash - * (operatorId) is fetched instead - */ - bytes32 operatorId = _getOrCreateOperatorId(operator, params); - - // Verify the churn approver's signature for the registering operator and kick params - _verifyChurnApproverSignature({ - registeringOperator: operator, - registeringOperatorId: operatorId, - operatorKickParams: operatorKickParams, - churnApproverSignature: churnApproverSignature - }); - - // Register the operator in each of the registry contracts and update the operator's - // quorum bitmap and registration status - RegisterResults memory results = _registerOperatorToOperatorSet(operator, operatorId, quorumNumbers, socket); - - // Check that each quorum's operator count is below the configured maximum. If the max - // is exceeded, use `operatorKickParams` to deregister an existing operator to make space - for (uint256 i = 0; i < quorumNumbers.length; i++) { - OperatorSetParam memory operatorSetParams = _quorumParams[uint8(quorumNumbers[i])]; - - /** - * If the new operator count for any quorum exceeds the maximum, validate - * that churn can be performed, then deregister the specified operator - */ - if (results.numOperatorsPerQuorum[i] > operatorSetParams.maxOperatorCount) { - _validateChurn({ - quorumNumber: uint8(quorumNumbers[i]), - totalQuorumStake: results.totalStakes[i], - newOperator: operator, - newOperatorStake: results.operatorStakes[i], - kickParams: operatorKickParams[i], - setParams: operatorSetParams - }); - - bytes memory singleQuorumNumber = new bytes(1); - singleQuorumNumber[0] = quorumNumbers[i]; - _deregisterOperator(operatorKickParams[i].operator, singleQuorumNumber); - if (isUsingOperatorSets()) { - _handleOperatorSetDeregistration(operatorKickParams[i].operator, singleQuorumNumber); - } - } - } - } - - function deregisterOperator( - address operator, - uint32[] memory operatorSetIds - ) external override onlyWhenNotPaused(PAUSED_REGISTER_OPERATOR) { - require(isUsingOperatorSets(), OperatorSetsNotEnabled()); - for (uint256 i = 0; i < operatorSetIds.length; i++) { - require(!isM2Quorum[uint8(operatorSetIds[i])], OperatorSetsNotSupported()); - } - _checkAllocationManager(); - bytes memory quorumNumbers = new bytes(operatorSetIds.length); - for (uint256 i = 0; i < operatorSetIds.length; i++) { - quorumNumbers[i] = bytes1(uint8(operatorSetIds[i])); - } - - _deregisterOperator(operator, quorumNumbers); - } - - /** - * @notice Updates the StakeRegistry's view of one or more operators' stakes. If any operator - * is found to be below the minimum stake for the quorum, they are deregistered. - * @dev stakes are queried from the Eigenlayer core DelegationManager contract - * @param operators a list of operator addresses to update - */ - function updateOperators( - address[] memory operators - ) external onlyWhenNotPaused(PAUSED_UPDATE_OPERATOR) { - for (uint256 i = 0; i < operators.length; i++) { - address operator = operators[i]; - OperatorInfo memory operatorInfo = _operatorInfo[operator]; - bytes32 operatorId = operatorInfo.operatorId; - - // Update the operator's stake for their active quorums - uint192 currentBitmap = _currentOperatorBitmap(operatorId); - bytes memory quorumsToUpdate = BitmapUtils.bitmapToBytesArray(currentBitmap); - _updateOperator(operator, operatorInfo, quorumsToUpdate); - } - } - - /** - * @notice For each quorum in `quorumNumbers`, updates the StakeRegistry's view of ALL its registered operators' stakes. - * Each quorum's `quorumUpdateBlockNumber` is also updated, which tracks the most recent block number when ALL registered - * operators were updated. - * @dev stakes are queried from the Eigenlayer core DelegationManager contract - * @param operatorsPerQuorum for each quorum in `quorumNumbers`, this has a corresponding list of operators to update. - * @dev Each list of operator addresses MUST be sorted in ascending order - * @dev Each list of operator addresses MUST represent the entire list of registered operators for the corresponding quorum - * @param quorumNumbers is an ordered byte array containing the quorum numbers being updated - * @dev invariant: Each list of `operatorsPerQuorum` MUST be a sorted version of `IndexRegistry.getOperatorListAtBlockNumber` - * for the corresponding quorum. - * @dev note on race condition: if an operator registers/deregisters for any quorum in `quorumNumbers` after a txn to - * this method is broadcast (but before it is executed), the method will fail - */ - function updateOperatorsForQuorum( - address[][] memory operatorsPerQuorum, - bytes calldata quorumNumbers - ) external onlyWhenNotPaused(PAUSED_UPDATE_OPERATOR) { - // Input validation - // - all quorums should exist (checked against `quorumCount` in orderedBytesArrayToBitmap) - // - there should be no duplicates in `quorumNumbers` - // - there should be one list of operators per quorum - BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers, quorumCount); - require(operatorsPerQuorum.length == quorumNumbers.length, InputLengthMismatch()); - - // For each quorum, update ALL registered operators - for (uint256 i = 0; i < quorumNumbers.length; ++i) { - uint8 quorumNumber = uint8(quorumNumbers[i]); - - // Ensure we've passed in the correct number of operators for this quorum - address[] memory currQuorumOperators = operatorsPerQuorum[i]; - require( - currQuorumOperators.length == indexRegistry.totalOperatorsForQuorum(quorumNumber), - QuorumOperatorCountMismatch() - ); - - address prevOperatorAddress = address(0); - // For each operator: - // - check that they are registered for this quorum - // - check that their address is strictly greater than the last operator - // ... then, update their stakes - for (uint256 j = 0; j < currQuorumOperators.length; ++j) { - address operator = currQuorumOperators[j]; - - OperatorInfo memory operatorInfo = _operatorInfo[operator]; - bytes32 operatorId = operatorInfo.operatorId; - - { - uint192 currentBitmap = _currentOperatorBitmap(operatorId); - // Check that the operator is registered - require( - BitmapUtils.isSet(currentBitmap, quorumNumber), NotRegisteredForQuorum() - ); - // Prevent duplicate operators - require(operator > prevOperatorAddress, NotSorted()); - } - - // Update the operator - _updateOperator(operator, operatorInfo, quorumNumbers[i:i + 1]); - prevOperatorAddress = operator; - } - - // Update timestamp that all operators in quorum have been updated all at once - quorumUpdateBlockNumber[quorumNumber] = block.number; - emit QuorumBlockNumberUpdated(quorumNumber, block.number); - } - } - - /** - * @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 { - require(_operatorInfo[msg.sender].status == OperatorStatus.REGISTERED, NotRegistered()); - emit OperatorSocketUpdate(_operatorInfo[msg.sender].operatorId, socket); - } - - /** - * - * EXTERNAL FUNCTIONS - EJECTOR - * - */ - - /** - * @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 memory quorumNumbers) external onlyEjector { - 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) - ) { - // If using operator sets, check for non-M2 quorums - if (isUsingOperatorSets()) { - _handleOperatorSetDeregistration(operator, quorumNumbers); - } - - _deregisterOperator({operator: operator, quorumNumbers: quorumNumbers}); - } - } - - /** - * @notice Helper function to handle operator set deregistration for non-M2 quorums - * @param operator The operator to deregister - * @param quorumNumbers The quorum numbers to check - */ - function _handleOperatorSetDeregistration(address operator, bytes memory quorumNumbers) internal { - uint32[] memory nonM2OperatorSetIds = new uint32[](quorumNumbers.length); - uint256 numNonM2Quorums; - - // Check each quorum's stake type - for (uint256 i = 0; i < quorumNumbers.length; i++) { - uint8 quorumNumber = uint8(quorumNumbers[i]); - if (isM2Quorum[quorumNumber]) { - nonM2OperatorSetIds[numNonM2Quorums++] = quorumNumber; - } - } - - // If any non-M2 quorums found, deregister from AVS - if (numNonM2Quorums > 0) { - // Resize array to exact size needed - assembly { - mstore(nonM2OperatorSetIds, numNonM2Quorums) - } - serviceManager.deregisterOperatorFromOperatorSets(operator, nonM2OperatorSetIds); - } - } - - /** - * - * EXTERNAL FUNCTIONS - OWNER - * - */ - - /** - * @notice Creates a quorum and initializes it in each registry contract - * @param operatorSetParams configures the quorum's max operator count and churn parameters - * @param minimumStake sets the minimum stake required for an operator to register or remain - * registered - * @param strategyParams a list of strategies and multipliers used by the StakeRegistry to - * calculate an operator's stake weight for the quorum - * @dev For m2 AVS this function has the same behavior as createQuorum before - * For migrated AVS that enable operator sets this will create a quorum that measures total delegated stake for operator set - * - */ - function createTotalDelegatedStakeQuorum( - OperatorSetParam memory operatorSetParams, - uint96 minimumStake, - IStakeRegistry.StrategyParams[] memory strategyParams - ) external virtual onlyOwner { - _createQuorum(operatorSetParams, minimumStake, strategyParams, StakeType.TOTAL_DELEGATED, 0); - } - - function createSlashableStakeQuorum( - OperatorSetParam memory operatorSetParams, - uint96 minimumStake, - IStakeRegistry.StrategyParams[] memory strategyParams, - uint32 lookAheadPeriod - ) external virtual onlyOwner { - require(isUsingOperatorSets(), OperatorSetsNotEnabled()); - _createQuorum( - operatorSetParams, - minimumStake, - strategyParams, - StakeType.TOTAL_SLASHABLE, - lookAheadPeriod - ); - } - - /** - * @notice Updates an existing quorum's configuration with a new max operator count - * and operator churn parameters - * @param quorumNumber the quorum number to update - * @param operatorSetParams the new config - * @dev only callable by the owner - */ - function setOperatorSetParams( - uint8 quorumNumber, - OperatorSetParam memory operatorSetParams - ) external onlyOwner quorumExists(quorumNumber) { - _setOperatorSetParams(quorumNumber, operatorSetParams); - } - - /** - * @notice Sets the churnApprover, which approves operator registration with churn - * (see `registerOperatorWithChurn`) - * @param _churnApprover the new churn approver - * @dev only callable by the owner - */ - function setChurnApprover( - address _churnApprover - ) external onlyOwner { - _setChurnApprover(_churnApprover); - } - - /** - * @notice Sets the ejector, which can force-deregister operators from quorums - * @param _ejector the new ejector - * @dev only callable by the owner - */ - function setEjector( - address _ejector - ) external onlyOwner { - _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 * */ - struct RegisterResults { - uint32[] numOperatorsPerQuorum; - uint96[] operatorStakes; - uint96[] totalStakes; - } /** * @notice Register the operator for one or more quorums. This method updates the @@ -745,523 +245,14 @@ contract RegistryCoordinator is return results; } - /** - * @notice Register the operator for one or more quorums. This method updates the - * operator's quorum bitmap, socket, and status, then registers them with each registry. - */ - function _registerOperatorToOperatorSet( - address operator, - bytes32 operatorId, - bytes memory quorumNumbers, - string memory socket - ) internal virtual returns (RegisterResults memory results) { - /** - * Get bitmap of quorums to register for and operator's current bitmap. Validate that: - * - we're trying to register for at least 1 quorum - * - the quorums we're registering for exist (checked against `quorumCount` in orderedBytesArrayToBitmap) - * - the operator is not currently registered for any quorums we're registering for - * Then, calculate the operator's new bitmap after registration - */ - uint192 quorumsToAdd = - uint192(BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers, quorumCount)); - uint192 currentBitmap = _currentOperatorBitmap(operatorId); - require(!quorumsToAdd.isEmpty(), BitmapEmpty()); - require(quorumsToAdd.noBitsInCommon(currentBitmap), AlreadyRegisteredForQuorums()); - uint192 newBitmap = uint192(currentBitmap.plus(quorumsToAdd)); - - // Check that the operator can reregister if ejected - require( - lastEjectionTimestamp[operator] + ejectionCooldown < block.timestamp, - CannotReregisterYet() - ); - - /** - * Update operator's bitmap, socket, and status. Only update operatorInfo if needed: - * if we're `REGISTERED`, the operatorId and status are already correct. - */ - _updateOperatorBitmap({operatorId: operatorId, newBitmap: newBitmap}); - - emit OperatorSocketUpdate(operatorId, socket); - - // If the operator wasn't registered for any quorums, update their status - // and register them with this AVS in EigenLayer core (DelegationManager) - if (_operatorInfo[operator].status != OperatorStatus.REGISTERED) { - _operatorInfo[operator] = OperatorInfo(operatorId, OperatorStatus.REGISTERED); - } - - // Register the operator with the BLSApkRegistry, StakeRegistry, and IndexRegistry - blsApkRegistry.registerOperator(operator, quorumNumbers); - (results.operatorStakes, results.totalStakes) = - stakeRegistry.registerOperator(operator, operatorId, quorumNumbers); - results.numOperatorsPerQuorum = indexRegistry.registerOperator(operatorId, quorumNumbers); - - 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, OnlyEjector()); - } - - function _checkAllocationManager() internal view { - require(msg.sender == address(allocationManager), OnlyAllocationManager()); - } - - /** - * @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, QuorumDoesNotExist()); - } - - /** - * @notice Fetches an operator's pubkey hash from the BLSApkRegistry. If the - * operator has not registered a pubkey, attempts to register a pubkey using - * `params` - * @param operator the operator whose pubkey to query from the BLSApkRegistry - * @param params contains the G1 & G2 public keys of the operator, and a signature proving their ownership - * @dev `params` can be empty if the operator has already registered a pubkey in the BLSApkRegistry - */ - function _getOrCreateOperatorId( - address operator, - IBLSApkRegistry.PubkeyRegistrationParams memory params - ) internal returns (bytes32 operatorId) { - operatorId = blsApkRegistry.getOperatorId(operator); - if (operatorId == 0) { - operatorId = blsApkRegistry.registerBLSPublicKey( - operator, params, pubkeyRegistrationMessageHash(operator) - ); - } - return operatorId; - } - - /** - * @notice Validates that an incoming operator is eligible to replace an existing - * operator based on the stake of both - * @dev In order to churn, the incoming operator needs to have more stake than the - * existing operator by a proportion given by `kickBIPsOfOperatorStake` - * @dev In order to be churned out, the existing operator needs to have a proportion - * of the total quorum stake less than `kickBIPsOfTotalStake` - * @param quorumNumber `newOperator` is trying to replace an operator in this quorum - * @param totalQuorumStake the total stake of all operators in the quorum, after the - * `newOperator` registers - * @param newOperator the incoming operator - * @param newOperatorStake the incoming operator's stake - * @param kickParams the quorum number and existing operator to replace - * @dev the existing operator's registration to this quorum isn't checked here, but - * if we attempt to deregister them, this will be checked in `_deregisterOperator` - * @param setParams config for this quorum containing `kickBIPsX` stake proportions - * mentioned above - */ - function _validateChurn( - uint8 quorumNumber, - uint96 totalQuorumStake, - address newOperator, - uint96 newOperatorStake, - OperatorKickParam memory kickParams, - OperatorSetParam memory setParams - ) internal view { - address operatorToKick = kickParams.operator; - bytes32 idToKick = _operatorInfo[operatorToKick].operatorId; - require(newOperator != operatorToKick, CannotChurnSelf()); - require(kickParams.quorumNumber == quorumNumber, QuorumOperatorCountMismatch()); - - // Get the target operator's stake and check that it is below the kick thresholds - uint96 operatorToKickStake = stakeRegistry.getCurrentStake(idToKick, quorumNumber); - require( - newOperatorStake > _individualKickThreshold(operatorToKickStake, setParams), - InsufficientStakeForChurn() - ); - require( - operatorToKickStake < _totalKickThreshold(totalQuorumStake, setParams), - CannotKickOperatorAboveThreshold() - ); - } - - /** - * @dev Deregister the operator from one or more quorums - * This method updates the operator's quorum bitmap and status, then deregisters - * the operator with the BLSApkRegistry, IndexRegistry, and StakeRegistry - */ - function _deregisterOperator(address operator, bytes memory quorumNumbers) internal virtual { - // Fetch the operator's info and ensure they are registered - OperatorInfo storage operatorInfo = _operatorInfo[operator]; - bytes32 operatorId = operatorInfo.operatorId; - require(operatorInfo.status == OperatorStatus.REGISTERED, NotRegistered()); - - /** - * Get bitmap of quorums to deregister from and operator's current bitmap. Validate that: - * - we're trying to deregister from at least 1 quorum - * - the quorums we're deregistering from exist (checked against `quorumCount` in orderedBytesArrayToBitmap) - * - the operator is currently registered for any quorums we're trying to deregister from - * Then, calculate the operator's new bitmap after deregistration - */ - uint192 quorumsToRemove = - uint192(BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers, quorumCount)); - uint192 currentBitmap = _currentOperatorBitmap(operatorId); - require(!quorumsToRemove.isEmpty(), BitmapCannotBeZero()); - require(quorumsToRemove.isSubsetOf(currentBitmap), NotRegisteredForQuorum()); - uint192 newBitmap = uint192(currentBitmap.minus(quorumsToRemove)); - - // Update operator's bitmap and status - _updateOperatorBitmap({operatorId: operatorId, newBitmap: newBitmap}); - + /// @dev Hook to allow for any post-deregister logic + function _afterDeregisterOperator(address operator, bytes32 operatorId, bytes memory quorumNumbers, uint192 newBitmap) internal virtual override { // If the operator is no longer registered for any quorums, update their status and deregister // them from the AVS via the EigenLayer core contracts if (newBitmap.isEmpty()) { - operatorInfo.status = OperatorStatus.DEREGISTERED; + _operatorInfo[operator].status = OperatorStatus.DEREGISTERED; serviceManager.deregisterOperatorFromAVS(operator); emit OperatorDeregistered(operator, operatorId); } - - // Deregister operator with each of the registry contracts - blsApkRegistry.deregisterOperator(operator, quorumNumbers); - stakeRegistry.deregisterOperator(operatorId, quorumNumbers); - indexRegistry.deregisterOperator(operatorId, quorumNumbers); - } - - /** - * @notice Updates the StakeRegistry's view of the operator's stake in one or more quorums. - * For any quorums where the StakeRegistry finds the operator is under the configured minimum - * stake, `quorumsToRemove` is returned and used to deregister the operator from those quorums - * @dev does nothing if operator is not registered for any quorums. - */ - function _updateOperator( - address operator, - OperatorInfo memory operatorInfo, - bytes memory quorumsToUpdate - ) internal { - if (operatorInfo.status != OperatorStatus.REGISTERED) { - return; - } - bytes32 operatorId = operatorInfo.operatorId; - uint192 quorumsToRemove = - stakeRegistry.updateOperatorStake(operator, operatorId, quorumsToUpdate); - - if (!quorumsToRemove.isEmpty()) { - _deregisterOperator({ - operator: operator, - quorumNumbers: BitmapUtils.bitmapToBytesArray(quorumsToRemove) - }); - } - } - - /** - * @notice Returns the stake threshold required for an incoming operator to replace an existing operator - * The incoming operator must have more stake than the return value. - */ - function _individualKickThreshold( - uint96 operatorStake, - OperatorSetParam memory setParams - ) internal pure returns (uint96) { - return operatorStake * setParams.kickBIPsOfOperatorStake / BIPS_DENOMINATOR; - } - - /** - * @notice Returns the total stake threshold required for an operator to remain in a quorum. - * The operator must have at least the returned stake amount to keep their position. - */ - function _totalKickThreshold( - uint96 totalStake, - OperatorSetParam memory setParams - ) internal pure returns (uint96) { - return totalStake * setParams.kickBIPsOfTotalStake / BIPS_DENOMINATOR; - } - - /// @notice verifies churnApprover's signature on operator churn approval and increments the churnApprover nonce - function _verifyChurnApproverSignature( - address registeringOperator, - bytes32 registeringOperatorId, - OperatorKickParam[] memory operatorKickParams, - SignatureWithSaltAndExpiry memory churnApproverSignature - ) internal { - // make sure the salt hasn't been used already - require(!isChurnApproverSaltUsed[churnApproverSignature.salt], ChurnApproverSaltUsed()); - require(churnApproverSignature.expiry >= block.timestamp, SignatureExpired()); - - // set salt used to true - isChurnApproverSaltUsed[churnApproverSignature.salt] = true; - - // check the churnApprover's signature - SignatureCheckerLib.isValidSignature( - churnApprover, - calculateOperatorChurnApprovalDigestHash( - registeringOperator, - registeringOperatorId, - operatorKickParams, - churnApproverSignature.salt, - churnApproverSignature.expiry - ), - churnApproverSignature.signature - ); - } - - /** - * @notice Creates a quorum and initializes it in each registry contract - * @param operatorSetParams configures the quorum's max operator count and churn parameters - * @param minimumStake sets the minimum stake required for an operator to register or remain - * registered - * @param strategyParams a list of strategies and multipliers used by the StakeRegistry to - * calculate an operator's stake weight for the quorum - */ - function _createQuorum( - OperatorSetParam memory operatorSetParams, - uint96 minimumStake, - IStakeRegistry.StrategyParams[] memory strategyParams, - StakeType stakeType, - uint32 lookAheadPeriod - ) internal { - // Increment the total quorum count. Fails if we're already at the max - uint8 prevQuorumCount = quorumCount; - require(prevQuorumCount < MAX_QUORUM_COUNT, MaxQuorumsReached()); - quorumCount = prevQuorumCount + 1; - - // The previous count is the new quorum's number - uint8 quorumNumber = prevQuorumCount; - - // Initialize the quorum here and in each registry - _setOperatorSetParams(quorumNumber, operatorSetParams); - - /// Update the AllocationManager if operatorSetQuorum - if (isOperatorSetAVS && !isM2Quorum[quorumNumber]) { - // Create array of CreateSetParams for the new quorum - IAllocationManagerTypes.CreateSetParams[] memory createSetParams = - new IAllocationManagerTypes.CreateSetParams[](1); - - // Extract strategies from strategyParams - IStrategy[] memory strategies = new IStrategy[](strategyParams.length); - for (uint256 i = 0; i < strategyParams.length; i++) { - strategies[i] = strategyParams[i].strategy; - } - - // Initialize CreateSetParams with quorumNumber as operatorSetId - createSetParams[0] = IAllocationManagerTypes.CreateSetParams({ - operatorSetId: quorumNumber, - strategies: strategies - }); - allocationManager.createOperatorSets({ - avs: address(serviceManager), - params: createSetParams - }); - } - // Initialize stake registry based on stake type - if (stakeType == StakeType.TOTAL_DELEGATED) { - stakeRegistry.initializeDelegatedStakeQuorum(quorumNumber, minimumStake, strategyParams); - } else if (stakeType == StakeType.TOTAL_SLASHABLE) { - stakeRegistry.initializeSlashableStakeQuorum( - quorumNumber, minimumStake, lookAheadPeriod, strategyParams - ); - } - - indexRegistry.initializeQuorum(quorumNumber); - blsApkRegistry.initializeQuorum(quorumNumber); - } - - /** - * @notice Record an update to an operator's quorum bitmap. - * @param newBitmap is the most up-to-date set of bitmaps the operator is registered for - */ - function _updateOperatorBitmap(bytes32 operatorId, uint192 newBitmap) internal { - QuorumBitmapHistoryLib.updateOperatorBitmap(_operatorBitmapHistory, operatorId, newBitmap); - } - - /// @notice Get the most recent bitmap for the operator, returning an empty bitmap if - /// the operator is not registered. - function _currentOperatorBitmap( - bytes32 operatorId - ) internal view returns (uint192) { - return QuorumBitmapHistoryLib.currentOperatorBitmap(_operatorBitmapHistory, operatorId); - } - - /** - * @notice Returns the index of the quorumBitmap for the provided `operatorId` at the given `blockNumber` - * @dev Reverts if the operator had not yet (ever) registered at `blockNumber` - * @dev This function is designed to find proper inputs to the `getQuorumBitmapAtBlockNumberByIndex` function - */ - function _getQuorumBitmapIndexAtBlockNumber( - uint32 blockNumber, - bytes32 operatorId - ) internal view returns (uint32 index) { - return QuorumBitmapHistoryLib.getQuorumBitmapIndexAtBlockNumber( - _operatorBitmapHistory, blockNumber, operatorId - ); - } - - function _setOperatorSetParams( - uint8 quorumNumber, - OperatorSetParam memory operatorSetParams - ) internal { - _quorumParams[quorumNumber] = operatorSetParams; - emit OperatorSetParamsUpdated(quorumNumber, operatorSetParams); - } - - function _setChurnApprover( - address newChurnApprover - ) internal { - emit ChurnApproverUpdated(churnApprover, newChurnApprover); - churnApprover = newChurnApprover; - } - - function _setEjector( - address newEjector - ) internal { - emit EjectorUpdated(ejector, newEjector); - ejector = newEjector; - } - - /** - * - * VIEW FUNCTIONS - * - */ - - /// @notice Returns the operator set params for the given `quorumNumber` - function getOperatorSetParams( - uint8 quorumNumber - ) external view returns (OperatorSetParam memory) { - return _quorumParams[quorumNumber]; - } - - /// @notice Returns the operator struct for the given `operator` - function getOperator( - address operator - ) external view returns (OperatorInfo memory) { - return _operatorInfo[operator]; - } - - /// @notice Returns the operatorId for the given `operator` - function getOperatorId( - address operator - ) external view returns (bytes32) { - return _operatorInfo[operator].operatorId; - } - - /// @notice Returns the operator address for the given `operatorId` - function getOperatorFromId( - bytes32 operatorId - ) external view returns (address) { - return blsApkRegistry.getOperatorFromPubkeyHash(operatorId); - } - - /// @notice Returns the status for the given `operator` - function getOperatorStatus( - address operator - ) external view returns (IRegistryCoordinator.OperatorStatus) { - return _operatorInfo[operator].status; - } - - /** - * @notice Returns the indices of the quorumBitmaps for the provided `operatorIds` at the given `blockNumber` - * @dev Reverts if any of the `operatorIds` was not (yet) registered at `blockNumber` - * @dev This function is designed to find proper inputs to the `getQuorumBitmapAtBlockNumberByIndex` function - */ - function getQuorumBitmapIndicesAtBlockNumber( - uint32 blockNumber, - bytes32[] memory operatorIds - ) external view returns (uint32[] memory) { - return QuorumBitmapHistoryLib.getQuorumBitmapIndicesAtBlockNumber( - _operatorBitmapHistory, blockNumber, operatorIds - ); - } - - /** - * @notice Returns the quorum bitmap for the given `operatorId` at the given `blockNumber` via the `index`, - * reverting if `index` is incorrect - * @dev This function is meant to be used in concert with `getQuorumBitmapIndicesAtBlockNumber`, which - * helps off-chain processes to fetch the correct `index` input - */ - function getQuorumBitmapAtBlockNumberByIndex( - bytes32 operatorId, - uint32 blockNumber, - uint256 index - ) external view returns (uint192) { - return QuorumBitmapHistoryLib.getQuorumBitmapAtBlockNumberByIndex( - _operatorBitmapHistory, operatorId, blockNumber, index - ); - } - - /// @notice Returns the `index`th entry in the operator with `operatorId`'s bitmap history - function getQuorumBitmapUpdateByIndex( - bytes32 operatorId, - uint256 index - ) external view returns (QuorumBitmapUpdate memory) { - return _operatorBitmapHistory[operatorId][index]; - } - - /// @notice Returns the current quorum bitmap for the given `operatorId` or 0 if the operator is not registered for any quorum - function getCurrentQuorumBitmap( - bytes32 operatorId - ) external view returns (uint192) { - return _currentOperatorBitmap(operatorId); - } - - /// @notice Returns the length of the quorum bitmap history for the given `operatorId` - function getQuorumBitmapHistoryLength( - bytes32 operatorId - ) external view returns (uint256) { - return _operatorBitmapHistory[operatorId].length; - } - - /// @notice Returns the number of registries - function numRegistries() external view returns (uint256) { - return registries.length; - } - - /** - * @notice Public function for the the churnApprover signature hash calculation when operators are being kicked from quorums - * @param registeringOperatorId The id of the registering operator - * @param operatorKickParams The parameters needed to kick the operator from the quorums that have reached their caps - * @param salt The salt to use for the churnApprover's signature - * @param expiry The desired expiry time of the churnApprover's signature - */ - function calculateOperatorChurnApprovalDigestHash( - address registeringOperator, - bytes32 registeringOperatorId, - OperatorKickParam[] memory operatorKickParams, - bytes32 salt, - uint256 expiry - ) public view returns (bytes32) { - // calculate the digest hash - return _hashTypedDataV4( - keccak256( - abi.encode( - OPERATOR_CHURN_APPROVAL_TYPEHASH, - registeringOperator, - registeringOperatorId, - operatorKickParams, - salt, - expiry - ) - ) - ); - } - - /** - * @notice Returns the message hash that an operator must sign to register their BLS public key. - * @param operator is the address of the operator registering their BLS public key - */ - function pubkeyRegistrationMessageHash( - address operator - ) public view returns (BN254.G1Point memory) { - return BN254.hashToG1( - _hashTypedDataV4(keccak256(abi.encode(PUBKEY_REGISTRATION_TYPEHASH, operator))) - ); - } - - /// @dev need to override function here since its defined in both these contracts - function owner() - public - view - override(OwnableUpgradeable, IRegistryCoordinator) - returns (address) - { - return OwnableUpgradeable.owner(); } } diff --git a/src/ServiceManagerBase.sol b/src/ServiceManagerBase.sol index 4c5866c4..0daf9e7d 100644 --- a/src/ServiceManagerBase.sol +++ b/src/ServiceManagerBase.sol @@ -15,7 +15,7 @@ import {IPermissionController} from import {ServiceManagerBaseStorage} from "./ServiceManagerBaseStorage.sol"; import {IServiceManager} from "./interfaces/IServiceManager.sol"; -import {IRegistryCoordinator} from "./interfaces/IRegistryCoordinator.sol"; +import {ISlashingRegistryCoordinator} from "./interfaces/ISlashingRegistryCoordinator.sol"; import {IStakeRegistry} from "./interfaces/IStakeRegistry.sol"; import {BitmapUtils} from "./libraries/BitmapUtils.sol"; @@ -47,7 +47,7 @@ abstract contract ServiceManagerBase is ServiceManagerBaseStorage { constructor( IAVSDirectory __avsDirectory, IRewardsCoordinator __rewardsCoordinator, - IRegistryCoordinator __registryCoordinator, + ISlashingRegistryCoordinator __registryCoordinator, IStakeRegistry __stakeRegistry, IPermissionController __permissionController, IAllocationManager __allocationManager diff --git a/src/ServiceManagerBaseStorage.sol b/src/ServiceManagerBaseStorage.sol index ca548ea2..b9273ccd 100644 --- a/src/ServiceManagerBaseStorage.sol +++ b/src/ServiceManagerBaseStorage.sol @@ -4,7 +4,7 @@ pragma solidity ^0.8.27; import {OwnableUpgradeable} from "@openzeppelin-upgrades/contracts/access/OwnableUpgradeable.sol"; import {IServiceManager} from "./interfaces/IServiceManager.sol"; -import {IRegistryCoordinator} from "./interfaces/IRegistryCoordinator.sol"; +import {ISlashingRegistryCoordinator} from "./interfaces/ISlashingRegistryCoordinator.sol"; import {IStakeRegistry} from "./interfaces/IStakeRegistry.sol"; import {IAVSDirectory} from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol"; @@ -27,7 +27,7 @@ abstract contract ServiceManagerBaseStorage is IServiceManager, OwnableUpgradeab IAVSDirectory internal immutable _avsDirectory; IAllocationManager internal immutable _allocationManager; IRewardsCoordinator internal immutable _rewardsCoordinator; - IRegistryCoordinator internal immutable _registryCoordinator; + ISlashingRegistryCoordinator internal immutable _registryCoordinator; IStakeRegistry internal immutable _stakeRegistry; IPermissionController internal immutable _permissionController; @@ -56,7 +56,7 @@ abstract contract ServiceManagerBaseStorage is IServiceManager, OwnableUpgradeab constructor( IAVSDirectory __avsDirectory, IRewardsCoordinator __rewardsCoordinator, - IRegistryCoordinator __registryCoordinator, + ISlashingRegistryCoordinator __registryCoordinator, IStakeRegistry __stakeRegistry, IPermissionController __permissionController, IAllocationManager __allocationManager diff --git a/src/SlashingRegistryCoordinator.sol b/src/SlashingRegistryCoordinator.sol new file mode 100644 index 00000000..823766fa --- /dev/null +++ b/src/SlashingRegistryCoordinator.sol @@ -0,0 +1,1055 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.27; + +import {IPauserRegistry} from "eigenlayer-contracts/src/contracts/interfaces/IPauserRegistry.sol"; +import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol"; +import {IStrategy } from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; +import {IAllocationManager, OperatorSet, IAllocationManagerTypes} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; +import {ISocketUpdater} from "./interfaces/ISocketUpdater.sol"; +import {IBLSApkRegistry} from "./interfaces/IBLSApkRegistry.sol"; +import {IStakeRegistry, StakeType} from "./interfaces/IStakeRegistry.sol"; +import {IIndexRegistry} from "./interfaces/IIndexRegistry.sol"; +import {ISlashingRegistryCoordinator} from "./interfaces/ISlashingRegistryCoordinator.sol"; + +import {BitmapUtils} from "./libraries/BitmapUtils.sol"; +import {BN254} from "./libraries/BN254.sol"; +import {SignatureCheckerLib} from "./libraries/SignatureCheckerLib.sol"; +import {QuorumBitmapHistoryLib} from "./libraries/QuorumBitmapHistoryLib.sol"; +import {AVSRegistrar} from "./AVSRegistrar.sol"; + +import {OwnableUpgradeable} from "@openzeppelin-upgrades/contracts/access/OwnableUpgradeable.sol"; +import {Initializable} from "@openzeppelin-upgrades/contracts/proxy/utils/Initializable.sol"; +import {EIP712} from "@openzeppelin/contracts/utils/cryptography/draft-EIP712.sol"; + +import {Pausable} from "eigenlayer-contracts/src/contracts/permissions/Pausable.sol"; +import {SlashingRegistryCoordinatorStorage} from "./SlashingRegistryCoordinatorStorage.sol"; +import {IAVSRegistrar} from "eigenlayer-contracts/src/contracts/interfaces/IAVSRegistrar.sol"; + +/** + * @title A `RegistryCoordinator` that has three registries: + * 1) a `StakeRegistry` that keeps track of operators' stakes + * 2) a `BLSApkRegistry` that keeps track of operators' BLS public keys and aggregate BLS public keys for each quorum + * 3) an `IndexRegistry` that keeps track of an ordered list of operators for each quorum + * + * @author Layr Labs, Inc. + */ +contract SlashingRegistryCoordinator is + EIP712, + Initializable, + Pausable, + OwnableUpgradeable, + SlashingRegistryCoordinatorStorage, + AVSRegistrar, + ISocketUpdater, + ISignatureUtils +{ + using BitmapUtils for *; + using BN254 for BN254.G1Point; + + modifier onlyAllocationManager() { + _checkAllocationManager(); + _; + } + + modifier onlyEjector() { + _checkEjector(); + _; + } + + /// @dev Checks that `quorumNumber` corresponds to a quorum that has been created + /// via `initialize` or `createQuorum` + modifier quorumExists(uint8 quorumNumber) { + _checkQuorumExists(quorumNumber); + _; + } + + constructor( + IStakeRegistry _stakeRegistry, + IBLSApkRegistry _blsApkRegistry, + IIndexRegistry _indexRegistry, + IAllocationManager _allocationManager, + IPauserRegistry _pauserRegistry + ) + SlashingRegistryCoordinatorStorage( + _stakeRegistry, + _blsApkRegistry, + _indexRegistry, + _allocationManager + ) + EIP712("AVSRegistryCoordinator", "v0.0.1") + Pausable(_pauserRegistry) + { + _disableInitializers(); + } + + /** + * + * EXTERNAL FUNCTIONS + * + */ + + function initialize( + address _initialOwner, + address _churnApprover, + address _ejector, + uint256 _initialPausedStatus, + address _accountIdentifier + ) external initializer { + _transferOwnership(_initialOwner); + _setChurnApprover(_churnApprover); + _setPausedStatus(_initialPausedStatus); + _setEjector(_ejector); + _setAccountIdentifier(_accountIdentifier); + // Add registry contracts to the registries array + registries.push(address(stakeRegistry)); + registries.push(address(blsApkRegistry)); + registries.push(address(indexRegistry)); + + // Set the AVS to be OperatorSets compatible + isOperatorSetAVS = true; + } + + /** + * @notice Creates a quorum and initializes it in each registry contract + * @param operatorSetParams configures the quorum's max operator count and churn parameters + * @param minimumStake sets the minimum stake required for an operator to register or remain + * registered + * @param strategyParams a list of strategies and multipliers used by the StakeRegistry to + * calculate an operator's stake weight for the quorum + * @dev For m2 AVS this function has the same behavior as createQuorum before + * For migrated AVS that enable operator sets this will create a quorum that measures total delegated stake for operator set + * + */ + function createTotalDelegatedStakeQuorum( + OperatorSetParam memory operatorSetParams, + uint96 minimumStake, + IStakeRegistry.StrategyParams[] memory strategyParams + ) external virtual onlyOwner { + _createQuorum(operatorSetParams, minimumStake, strategyParams, StakeType.TOTAL_DELEGATED, 0); + } + + function createSlashableStakeQuorum( + OperatorSetParam memory operatorSetParams, + uint96 minimumStake, + IStakeRegistry.StrategyParams[] memory strategyParams, + uint32 lookAheadPeriod + ) external virtual onlyOwner { + require(isOperatorSetAVS, OperatorSetsNotEnabled()); + _createQuorum(operatorSetParams, minimumStake, strategyParams, StakeType.TOTAL_SLASHABLE, lookAheadPeriod); + } + + function registerOperator( + address operator, + uint32[] memory operatorSetIds, + bytes calldata data + ) external override onlyAllocationManager onlyWhenNotPaused(PAUSED_REGISTER_OPERATOR) { + require(isOperatorSetAVS, OperatorSetsNotEnabled()); + for (uint256 i = 0; i < operatorSetIds.length; i++) { + require(!isM2Quorum[uint8(operatorSetIds[i])], OperatorSetsNotSupported()); + } + bytes memory quorumNumbers = new bytes(operatorSetIds.length); + for (uint256 i = 0; i < operatorSetIds.length; i++) { + quorumNumbers[i] = bytes1(uint8(operatorSetIds[i])); + } + + // Handle churn or normal registration based on first byte in `data` + RegistrationType registrationType = RegistrationType(uint8(bytes1(data[0:1]))); + if (registrationType == RegistrationType.NORMAL) { + (, string memory socket, IBLSApkRegistry.PubkeyRegistrationParams memory params) = abi.decode(data, (RegistrationType, string, IBLSApkRegistry.PubkeyRegistrationParams)); + bytes32 operatorId = _getOrCreateOperatorId(operator, params); + _registerOperatorToOperatorSet(operator, operatorId, quorumNumbers, socket); + } else if (registrationType == RegistrationType.CHURN) { + // Decode registration data from bytes + ( + , + string memory socket, + IBLSApkRegistry.PubkeyRegistrationParams memory params, + OperatorKickParam[] memory operatorKickParams, + SignatureWithSaltAndExpiry memory churnApproverSignature + ) = abi.decode( + data, + ( + RegistrationType, + string, + IBLSApkRegistry.PubkeyRegistrationParams, + OperatorKickParam[], + SignatureWithSaltAndExpiry + ) + ); + + _registerOperatorWithChurn(operator, quorumNumbers, socket, params, operatorKickParams, churnApproverSignature); + } else { + revert InvalidRegistrationType(); + } + } + + function deregisterOperator( + address operator, + uint32[] memory operatorSetIds + ) external override onlyAllocationManager onlyWhenNotPaused(PAUSED_REGISTER_OPERATOR) { + require(isOperatorSetAVS, OperatorSetsNotEnabled()); + for (uint256 i = 0; i < operatorSetIds.length; i++) { + require(!isM2Quorum[uint8(operatorSetIds[i])], OperatorSetsNotSupported()); + } + bytes memory quorumNumbers = new bytes(operatorSetIds.length); + for (uint256 i = 0; i < operatorSetIds.length; i++) { + quorumNumbers[i] = bytes1(uint8(operatorSetIds[i])); + } + + _deregisterOperator(operator, quorumNumbers); + } + + /** + * @notice Updates the StakeRegistry's view of one or more operators' stakes. If any operator + * is found to be below the minimum stake for the quorum, they are deregistered. + * @dev stakes are queried from the Eigenlayer core DelegationManager contract + * @param operators a list of operator addresses to update + */ + function updateOperators(address[] memory operators) + external + onlyWhenNotPaused(PAUSED_UPDATE_OPERATOR) + { + for (uint256 i = 0; i < operators.length; i++) { + address operator = operators[i]; + OperatorInfo memory operatorInfo = _operatorInfo[operator]; + bytes32 operatorId = operatorInfo.operatorId; + + // Update the operator's stake for their active quorums + uint192 currentBitmap = _currentOperatorBitmap(operatorId); + bytes memory quorumsToUpdate = BitmapUtils.bitmapToBytesArray(currentBitmap); + _updateOperator(operator, operatorInfo, quorumsToUpdate); + } + } + + /** + * @notice For each quorum in `quorumNumbers`, updates the StakeRegistry's view of ALL its registered operators' stakes. + * Each quorum's `quorumUpdateBlockNumber` is also updated, which tracks the most recent block number when ALL registered + * operators were updated. + * @dev stakes are queried from the Eigenlayer core DelegationManager contract + * @param operatorsPerQuorum for each quorum in `quorumNumbers`, this has a corresponding list of operators to update. + * @dev Each list of operator addresses MUST be sorted in ascending order + * @dev Each list of operator addresses MUST represent the entire list of registered operators for the corresponding quorum + * @param quorumNumbers is an ordered byte array containing the quorum numbers being updated + * @dev invariant: Each list of `operatorsPerQuorum` MUST be a sorted version of `IndexRegistry.getOperatorListAtBlockNumber` + * for the corresponding quorum. + * @dev note on race condition: if an operator registers/deregisters for any quorum in `quorumNumbers` after a txn to + * this method is broadcast (but before it is executed), the method will fail + */ + function updateOperatorsForQuorum( + address[][] memory operatorsPerQuorum, + bytes calldata quorumNumbers + ) external onlyWhenNotPaused(PAUSED_UPDATE_OPERATOR) { + // Input validation + // - all quorums should exist (checked against `quorumCount` in orderedBytesArrayToBitmap) + // - there should be no duplicates in `quorumNumbers` + // - there should be one list of operators per quorum + BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers, quorumCount); + require( + operatorsPerQuorum.length == quorumNumbers.length, + InputLengthMismatch() + ); + + // For each quorum, update ALL registered operators + for (uint256 i = 0; i < quorumNumbers.length; ++i) { + uint8 quorumNumber = uint8(quorumNumbers[i]); + + // Ensure we've passed in the correct number of operators for this quorum + address[] memory currQuorumOperators = operatorsPerQuorum[i]; + require( + currQuorumOperators.length == indexRegistry.totalOperatorsForQuorum(quorumNumber), + QuorumOperatorCountMismatch() + ); + + address prevOperatorAddress = address(0); + // For each operator: + // - check that they are registered for this quorum + // - check that their address is strictly greater than the last operator + // ... then, update their stakes + for (uint256 j = 0; j < currQuorumOperators.length; ++j) { + address operator = currQuorumOperators[j]; + + OperatorInfo memory operatorInfo = _operatorInfo[operator]; + bytes32 operatorId = operatorInfo.operatorId; + + { + uint192 currentBitmap = _currentOperatorBitmap(operatorId); + // Check that the operator is registered + require( + BitmapUtils.isSet(currentBitmap, quorumNumber), + NotRegisteredForQuorum() + ); + // Prevent duplicate operators + require( + operator > prevOperatorAddress, + NotSorted() + ); + } + + // Update the operator + _updateOperator(operator, operatorInfo, quorumNumbers[i:i + 1]); + prevOperatorAddress = operator; + } + + // Update timestamp that all operators in quorum have been updated all at once + quorumUpdateBlockNumber[quorumNumber] = block.number; + emit QuorumBlockNumberUpdated(quorumNumber, block.number); + } + } + + /** + * @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 { + require( + _operatorInfo[msg.sender].status == OperatorStatus.REGISTERED, + NotRegistered() + ); + emit OperatorSocketUpdate(_operatorInfo[msg.sender].operatorId, socket); + } + + /** + * + * EXTERNAL FUNCTIONS - EJECTOR + * + */ + + /** + * @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 memory quorumNumbers) external onlyEjector { + 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}); + + if (isOperatorSetAVS) { + _forceDeregisterOperator(operator, quorumNumbers); + } + } + } + + /** + * + * EXTERNAL FUNCTIONS - OWNER + * + */ + + /** + * @notice Updates an existing quorum's configuration with a new max operator count + * and operator churn parameters + * @param quorumNumber the quorum number to update + * @param operatorSetParams the new config + * @dev only callable by the owner + */ + function setOperatorSetParams( + uint8 quorumNumber, + OperatorSetParam memory operatorSetParams + ) external onlyOwner quorumExists(quorumNumber) { + _setOperatorSetParams(quorumNumber, operatorSetParams); + } + + /** + * @notice Sets the churnApprover, which approves operator registration with churn + * (see `registerOperatorWithChurn`) + * @param _churnApprover the new churn approver + * @dev only callable by the owner + */ + function setChurnApprover(address _churnApprover) external onlyOwner { + _setChurnApprover(_churnApprover); + } + + /** + * @notice Sets the ejector, which can force-deregister operators from quorums + * @param _ejector the new ejector + * @dev only callable by the owner + */ + function setEjector(address _ejector) external onlyOwner { + _setEjector(_ejector); + } + + /** + * @notice Sets the account identifier for this AVS (used for UAM integration in EigenLayer) + * @param _accountIdentifier the new account identifier + * @dev only callable by the owner + */ + function setAccountIdentifier(address _accountIdentifier) external onlyOwner { + _setAccountIdentifier(_accountIdentifier); + } + + /** + * @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 + * + */ + + /** + * @notice Register the operator for one or more quorums. This method updates the + * operator's quorum bitmap, socket, and status, then registers them with each registry. + */ + function _registerOperatorToOperatorSet( + address operator, + bytes32 operatorId, + bytes memory quorumNumbers, + string memory socket + ) internal virtual returns (RegisterResults memory results) { + /** + * Get bitmap of quorums to register for and operator's current bitmap. Validate that: + * - we're trying to register for at least 1 quorum + * - the quorums we're registering for exist (checked against `quorumCount` in orderedBytesArrayToBitmap) + * - the operator is not currently registered for any quorums we're registering for + * Then, calculate the operator's new bitmap after registration + */ + uint192 quorumsToAdd = + uint192(BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers, quorumCount)); + uint192 currentBitmap = _currentOperatorBitmap(operatorId); + + // call hook to allow for any pre-register logic + _beforeRegisterOperator(operator, operatorId, quorumNumbers, currentBitmap); + + require( + !quorumsToAdd.isEmpty(), BitmapEmpty() + ); + require( + quorumsToAdd.noBitsInCommon(currentBitmap), + AlreadyRegisteredForQuorums() + ); + uint192 newBitmap = uint192(currentBitmap.plus(quorumsToAdd)); + + // Check that the operator can reregister if ejected + require( + lastEjectionTimestamp[operator] + ejectionCooldown < block.timestamp, + CannotReregisterYet() + ); + + /** + * Update operator's bitmap, socket, and status. Only update operatorInfo if needed: + * if we're `REGISTERED`, the operatorId and status are already correct. + */ + _updateOperatorBitmap({operatorId: operatorId, newBitmap: newBitmap}); + + emit OperatorSocketUpdate(operatorId, socket); + + // If the operator wasn't registered for any quorums, update their status + // and register them with this AVS in EigenLayer core (DelegationManager) + if (_operatorInfo[operator].status != OperatorStatus.REGISTERED) { + _operatorInfo[operator] = OperatorInfo(operatorId, OperatorStatus.REGISTERED); + } + + // Register the operator with the BLSApkRegistry, StakeRegistry, and IndexRegistry + blsApkRegistry.registerOperator(operator, quorumNumbers); + (results.operatorStakes, results.totalStakes) = + stakeRegistry.registerOperator(operator, operatorId, quorumNumbers); + results.numOperatorsPerQuorum = indexRegistry.registerOperator(operatorId, quorumNumbers); + + // call hook to allow for any post-register logic + _afterRegisterOperator(operator, operatorId, quorumNumbers, newBitmap); + + return results; + } + + function _registerOperatorWithChurn( + address operator, + bytes memory quorumNumbers, + string memory socket, + IBLSApkRegistry.PubkeyRegistrationParams memory params, + OperatorKickParam[] memory operatorKickParams, + SignatureWithSaltAndExpiry memory churnApproverSignature + ) internal virtual { + require(operatorKickParams.length == quorumNumbers.length, InputLengthMismatch()); + + /** + * If the operator has NEVER registered a pubkey before, use `params` to register + * their pubkey in blsApkRegistry + * + * If the operator HAS registered a pubkey, `params` is ignored and the pubkey hash + * (operatorId) is fetched instead + */ + bytes32 operatorId = _getOrCreateOperatorId(operator, params); + + // Verify the churn approver's signature for the registering operator and kick params + _verifyChurnApproverSignature({ + registeringOperator: operator, + registeringOperatorId: operatorId, + operatorKickParams: operatorKickParams, + churnApproverSignature: churnApproverSignature + }); + + // Register the operator in each of the registry contracts and update the operator's + // quorum bitmap and registration status + RegisterResults memory results = _registerOperatorToOperatorSet(operator, operatorId, quorumNumbers, socket); + + // Check that each quorum's operator count is below the configured maximum. If the max + // is exceeded, use `operatorKickParams` to deregister an existing operator to make space + for (uint256 i = 0; i < quorumNumbers.length; i++) { + OperatorSetParam memory operatorSetParams = _quorumParams[uint8(quorumNumbers[i])]; + + /** + * If the new operator count for any quorum exceeds the maximum, validate + * that churn can be performed, then deregister the specified operator + */ + if (results.numOperatorsPerQuorum[i] > operatorSetParams.maxOperatorCount) { + _validateChurn({ + quorumNumber: uint8(quorumNumbers[i]), + totalQuorumStake: results.totalStakes[i], + newOperator: operator, + newOperatorStake: results.operatorStakes[i], + kickParams: operatorKickParams[i], + setParams: operatorSetParams + }); + + bytes memory singleQuorumNumber = new bytes(1); + singleQuorumNumber[0] = quorumNumbers[i]; + _deregisterOperator(operatorKickParams[i].operator, singleQuorumNumber); + + if (isOperatorSetAVS) { + _forceDeregisterOperator(operatorKickParams[i].operator, singleQuorumNumber); + } + } + } + } + + /** + * @dev Deregister the operator from one or more quorums + * This method updates the operator's quorum bitmap and status, then deregisters + * the operator with the BLSApkRegistry, IndexRegistry, and StakeRegistry + */ + function _deregisterOperator(address operator, bytes memory quorumNumbers) internal virtual { + // Fetch the operator's info and ensure they are registered + OperatorInfo storage operatorInfo = _operatorInfo[operator]; + bytes32 operatorId = operatorInfo.operatorId; + uint192 currentBitmap = _currentOperatorBitmap(operatorId); + + // call hook to allow for any pre-deregister logic + _beforeDeregisterOperator(operator, operatorId, quorumNumbers, currentBitmap); + + require( + operatorInfo.status == OperatorStatus.REGISTERED, + NotRegistered() + ); + + /** + * Get bitmap of quorums to deregister from and operator's current bitmap. Validate that: + * - we're trying to deregister from at least 1 quorum + * - the quorums we're deregistering from exist (checked against `quorumCount` in orderedBytesArrayToBitmap) + * - the operator is currently registered for any quorums we're trying to deregister from + * Then, calculate the operator's new bitmap after deregistration + */ + uint192 quorumsToRemove = + uint192(BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers, quorumCount)); + require( + !quorumsToRemove.isEmpty(), + BitmapCannotBeZero() + ); + require( + quorumsToRemove.isSubsetOf(currentBitmap), + NotRegisteredForQuorum() + ); + uint192 newBitmap = uint192(currentBitmap.minus(quorumsToRemove)); + + // Update operator's bitmap and status + _updateOperatorBitmap({operatorId: operatorId, newBitmap: newBitmap}); + + // Deregister operator with each of the registry contracts + blsApkRegistry.deregisterOperator(operator, quorumNumbers); + stakeRegistry.deregisterOperator(operatorId, quorumNumbers); + indexRegistry.deregisterOperator(operatorId, quorumNumbers); + + // call hook to allow for any post-deregister logic + _afterDeregisterOperator(operator, operatorId, quorumNumbers, newBitmap); + } + + /** + * @notice Helper function to handle operator set deregistration for OperatorSets quorums. This is used + * when an operator is force-deregistered from a set of quorums. For any of the quorums that are + * OperatorSets quorums, we need to deregister the operator in the AllocationManager. + * @param operator The operator to deregister + * @param quorumNumbers The quorum numbers the operator is force-deregistered from + */ + function _forceDeregisterOperator(address operator, bytes memory quorumNumbers) internal { + uint32[] memory operatorSetIds = new uint32[](quorumNumbers.length); + uint256 numOperatorSetQuorums; + + // Check each quorum's stake type + for (uint256 i = 0; i < quorumNumbers.length; i++) { + uint8 quorumNumber = uint8(quorumNumbers[i]); + if (isM2Quorum[quorumNumber]) { + operatorSetIds[numOperatorSetQuorums++] = quorumNumber; + } + } + + // If any OperatorSet quorums found, deregister from AVS in the AllocationManager + if (numOperatorSetQuorums > 0) { + // Resize array to exact size needed + assembly { + mstore(operatorSetIds, numOperatorSetQuorums) + } + allocationManager.deregisterFromOperatorSets( + IAllocationManagerTypes.DeregisterParams({ + operator: operator, + avs: accountIdentifier, + operatorSetIds: operatorSetIds + }) + ); + } + } + + /** + * @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, OnlyEjector()); + } + + function _checkAllocationManager() internal view { + require(msg.sender == address(allocationManager), OnlyAllocationManager()); + } + + /** + * @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, + QuorumDoesNotExist() + ); + } + + /** + * @notice Fetches an operator's pubkey hash from the BLSApkRegistry. If the + * operator has not registered a pubkey, attempts to register a pubkey using + * `params` + * @param operator the operator whose pubkey to query from the BLSApkRegistry + * @param params contains the G1 & G2 public keys of the operator, and a signature proving their ownership + * @dev `params` can be empty if the operator has already registered a pubkey in the BLSApkRegistry + */ + function _getOrCreateOperatorId( + address operator, + IBLSApkRegistry.PubkeyRegistrationParams memory params + ) internal returns (bytes32 operatorId) { + operatorId = blsApkRegistry.getOperatorId(operator); + if (operatorId == 0) { + operatorId = blsApkRegistry.registerBLSPublicKey( + operator, params, pubkeyRegistrationMessageHash(operator) + ); + } + return operatorId; + } + + /** + * @notice Validates that an incoming operator is eligible to replace an existing + * operator based on the stake of both + * @dev In order to churn, the incoming operator needs to have more stake than the + * existing operator by a proportion given by `kickBIPsOfOperatorStake` + * @dev In order to be churned out, the existing operator needs to have a proportion + * of the total quorum stake less than `kickBIPsOfTotalStake` + * @param quorumNumber `newOperator` is trying to replace an operator in this quorum + * @param totalQuorumStake the total stake of all operators in the quorum, after the + * `newOperator` registers + * @param newOperator the incoming operator + * @param newOperatorStake the incoming operator's stake + * @param kickParams the quorum number and existing operator to replace + * @dev the existing operator's registration to this quorum isn't checked here, but + * if we attempt to deregister them, this will be checked in `_deregisterOperator` + * @param setParams config for this quorum containing `kickBIPsX` stake proportions + * mentioned above + */ + function _validateChurn( + uint8 quorumNumber, + uint96 totalQuorumStake, + address newOperator, + uint96 newOperatorStake, + OperatorKickParam memory kickParams, + OperatorSetParam memory setParams + ) internal view { + address operatorToKick = kickParams.operator; + bytes32 idToKick = _operatorInfo[operatorToKick].operatorId; + require( + newOperator != operatorToKick, + CannotChurnSelf() + ); + require( + kickParams.quorumNumber == quorumNumber, + QuorumOperatorCountMismatch() + ); + + // Get the target operator's stake and check that it is below the kick thresholds + uint96 operatorToKickStake = stakeRegistry.getCurrentStake(idToKick, quorumNumber); + require( + newOperatorStake > _individualKickThreshold(operatorToKickStake, setParams), + InsufficientStakeForChurn() + ); + require( + operatorToKickStake < _totalKickThreshold(totalQuorumStake, setParams), + CannotKickOperatorAboveThreshold() + ); + } + + /** + * @notice Updates the StakeRegistry's view of the operator's stake in one or more quorums. + * For any quorums where the StakeRegistry finds the operator is under the configured minimum + * stake, `quorumsToRemove` is returned and used to deregister the operator from those quorums + * @dev does nothing if operator is not registered for any quorums. + */ + function _updateOperator( + address operator, + OperatorInfo memory operatorInfo, + bytes memory quorumsToUpdate + ) internal { + if (operatorInfo.status != OperatorStatus.REGISTERED) { + return; + } + bytes32 operatorId = operatorInfo.operatorId; + uint192 quorumsToRemove = + stakeRegistry.updateOperatorStake(operator, operatorId, quorumsToUpdate); + + if (!quorumsToRemove.isEmpty()) { + _deregisterOperator({ + operator: operator, + quorumNumbers: BitmapUtils.bitmapToBytesArray(quorumsToRemove) + }); + } + } + + /** + * @notice Returns the stake threshold required for an incoming operator to replace an existing operator + * The incoming operator must have more stake than the return value. + */ + function _individualKickThreshold( + uint96 operatorStake, + OperatorSetParam memory setParams + ) internal pure returns (uint96) { + return operatorStake * setParams.kickBIPsOfOperatorStake / BIPS_DENOMINATOR; + } + + /** + * @notice Returns the total stake threshold required for an operator to remain in a quorum. + * The operator must have at least the returned stake amount to keep their position. + */ + function _totalKickThreshold( + uint96 totalStake, + OperatorSetParam memory setParams + ) internal pure returns (uint96) { + return totalStake * setParams.kickBIPsOfTotalStake / BIPS_DENOMINATOR; + } + + /// @notice verifies churnApprover's signature on operator churn approval and increments the churnApprover nonce + function _verifyChurnApproverSignature( + address registeringOperator, + bytes32 registeringOperatorId, + OperatorKickParam[] memory operatorKickParams, + SignatureWithSaltAndExpiry memory churnApproverSignature + ) internal { + // make sure the salt hasn't been used already + require( + !isChurnApproverSaltUsed[churnApproverSignature.salt], + ChurnApproverSaltUsed() + ); + require( + churnApproverSignature.expiry >= block.timestamp, + SignatureExpired() + ); + + // set salt used to true + isChurnApproverSaltUsed[churnApproverSignature.salt] = true; + + // check the churnApprover's signature + SignatureCheckerLib.isValidSignature( + churnApprover, + calculateOperatorChurnApprovalDigestHash( + registeringOperator, + registeringOperatorId, + operatorKickParams, + churnApproverSignature.salt, + churnApproverSignature.expiry + ), + churnApproverSignature.signature + ); + } + + /** + * @notice Creates a quorum and initializes it in each registry contract + * @param operatorSetParams configures the quorum's max operator count and churn parameters + * @param minimumStake sets the minimum stake required for an operator to register or remain + * registered + * @param strategyParams a list of strategies and multipliers used by the StakeRegistry to + * calculate an operator's stake weight for the quorum + */ + function _createQuorum( + OperatorSetParam memory operatorSetParams, + uint96 minimumStake, + IStakeRegistry.StrategyParams[] memory strategyParams, + StakeType stakeType, + uint32 lookAheadPeriod + ) internal { + // Increment the total quorum count. Fails if we're already at the max + uint8 prevQuorumCount = quorumCount; + require( + prevQuorumCount < MAX_QUORUM_COUNT, + MaxQuorumsReached() + ); + quorumCount = prevQuorumCount + 1; + + // The previous count is the new quorum's number + uint8 quorumNumber = prevQuorumCount; + + // Initialize the quorum here and in each registry + _setOperatorSetParams(quorumNumber, operatorSetParams); + + /// Update the AllocationManager if operatorSetQuorum + if (isOperatorSetAVS && !isM2Quorum[quorumNumber]) { + // Create array of CreateSetParams for the new quorum + IAllocationManagerTypes.CreateSetParams[] memory createSetParams = new IAllocationManagerTypes.CreateSetParams[](1); + + // Extract strategies from strategyParams + IStrategy[] memory strategies = new IStrategy[](strategyParams.length); + for (uint256 i = 0; i < strategyParams.length; i++) { + strategies[i] = strategyParams[i].strategy; + } + + // Initialize CreateSetParams with quorumNumber as operatorSetId + createSetParams[0] = IAllocationManagerTypes.CreateSetParams({ + operatorSetId: quorumNumber, + strategies: strategies + }); + allocationManager.createOperatorSets({ + avs: accountIdentifier, + params: createSetParams + }); + } + // Initialize stake registry based on stake type + if (stakeType == StakeType.TOTAL_DELEGATED) { + stakeRegistry.initializeDelegatedStakeQuorum(quorumNumber, minimumStake, strategyParams); + } else if (stakeType == StakeType.TOTAL_SLASHABLE) { + stakeRegistry.initializeSlashableStakeQuorum(quorumNumber, minimumStake, lookAheadPeriod, strategyParams); + } + + indexRegistry.initializeQuorum(quorumNumber); + blsApkRegistry.initializeQuorum(quorumNumber); + } + + /** + * @notice Record an update to an operator's quorum bitmap. + * @param newBitmap is the most up-to-date set of bitmaps the operator is registered for + */ + function _updateOperatorBitmap(bytes32 operatorId, uint192 newBitmap) internal { + QuorumBitmapHistoryLib.updateOperatorBitmap(_operatorBitmapHistory, operatorId, newBitmap); + } + + /// @notice Get the most recent bitmap for the operator, returning an empty bitmap if + /// the operator is not registered. + function _currentOperatorBitmap(bytes32 operatorId) internal view returns (uint192) { + return QuorumBitmapHistoryLib.currentOperatorBitmap(_operatorBitmapHistory, operatorId); + } + + /** + * @notice Returns the index of the quorumBitmap for the provided `operatorId` at the given `blockNumber` + * @dev Reverts if the operator had not yet (ever) registered at `blockNumber` + * @dev This function is designed to find proper inputs to the `getQuorumBitmapAtBlockNumberByIndex` function + */ + function _getQuorumBitmapIndexAtBlockNumber( + uint32 blockNumber, + bytes32 operatorId + ) internal view returns (uint32 index) { + return QuorumBitmapHistoryLib.getQuorumBitmapIndexAtBlockNumber(_operatorBitmapHistory,blockNumber, operatorId); + } + + function _setOperatorSetParams( + uint8 quorumNumber, + OperatorSetParam memory operatorSetParams + ) internal { + _quorumParams[quorumNumber] = operatorSetParams; + emit OperatorSetParamsUpdated(quorumNumber, operatorSetParams); + } + + function _setChurnApprover(address newChurnApprover) internal { + emit ChurnApproverUpdated(churnApprover, newChurnApprover); + churnApprover = newChurnApprover; + } + + function _setEjector(address newEjector) internal { + emit EjectorUpdated(ejector, newEjector); + ejector = newEjector; + } + + function _setAccountIdentifier(address _accountIdentifier) internal { + accountIdentifier = _accountIdentifier; + } + + /// @dev Hook to allow for any pre-register logic in `_registerOperatorToOperatorSet` + function _beforeRegisterOperator(address operator, bytes32 operatorId, bytes memory quorumNumbers, uint192 currentBitmap) internal virtual {} + + /// @dev Hook to allow for any post-register logic in `_registerOperatorToOperatorSet` + function _afterRegisterOperator(address operator, bytes32 operatorId, bytes memory quorumNumbers, uint192 newBitmap) internal virtual {} + + /// @dev Hook to allow for any pre-deregister logic in `_deregisterOperator` + function _beforeDeregisterOperator(address operator, bytes32 operatorId, bytes memory quorumNumbers, uint192 currentBitmap) internal virtual {} + + /// @dev Hook to allow for any post-deregister logic in `_deregisterOperator` + function _afterDeregisterOperator(address operator, bytes32 operatorId, bytes memory quorumNumbers, uint192 newBitmap) internal virtual {} + + /** + * + * VIEW FUNCTIONS + * + */ + + /// @notice Returns the operator set params for the given `quorumNumber` + function getOperatorSetParams(uint8 quorumNumber) + external + view + returns (OperatorSetParam memory) + { + return _quorumParams[quorumNumber]; + } + + /// @notice Returns the operator struct for the given `operator` + function getOperator(address operator) external view returns (OperatorInfo memory) { + return _operatorInfo[operator]; + } + + /// @notice Returns the operatorId for the given `operator` + function getOperatorId(address operator) external view returns (bytes32) { + return _operatorInfo[operator].operatorId; + } + + /// @notice Returns the operator address for the given `operatorId` + function getOperatorFromId(bytes32 operatorId) external view returns (address) { + return blsApkRegistry.getOperatorFromPubkeyHash(operatorId); + } + + /// @notice Returns the status for the given `operator` + function getOperatorStatus(address operator) + external + view + returns (ISlashingRegistryCoordinator.OperatorStatus) + { + return _operatorInfo[operator].status; + } + + /** + * @notice Returns the indices of the quorumBitmaps for the provided `operatorIds` at the given `blockNumber` + * @dev Reverts if any of the `operatorIds` was not (yet) registered at `blockNumber` + * @dev This function is designed to find proper inputs to the `getQuorumBitmapAtBlockNumberByIndex` function + */ + function getQuorumBitmapIndicesAtBlockNumber( + uint32 blockNumber, + bytes32[] memory operatorIds + ) external view returns (uint32[] memory) { + return QuorumBitmapHistoryLib.getQuorumBitmapIndicesAtBlockNumber(_operatorBitmapHistory, blockNumber, operatorIds); + } + + /** + * @notice Returns the quorum bitmap for the given `operatorId` at the given `blockNumber` via the `index`, + * reverting if `index` is incorrect + * @dev This function is meant to be used in concert with `getQuorumBitmapIndicesAtBlockNumber`, which + * helps off-chain processes to fetch the correct `index` input + */ + function getQuorumBitmapAtBlockNumberByIndex( + bytes32 operatorId, + uint32 blockNumber, + uint256 index + ) external view returns (uint192) { + return QuorumBitmapHistoryLib.getQuorumBitmapAtBlockNumberByIndex(_operatorBitmapHistory, operatorId, blockNumber, index); + } + + /// @notice Returns the `index`th entry in the operator with `operatorId`'s bitmap history + function getQuorumBitmapUpdateByIndex( + bytes32 operatorId, + uint256 index + ) external view returns (QuorumBitmapUpdate memory) { + return _operatorBitmapHistory[operatorId][index]; + } + + /// @notice Returns the current quorum bitmap for the given `operatorId` or 0 if the operator is not registered for any quorum + function getCurrentQuorumBitmap(bytes32 operatorId) external view returns (uint192) { + return _currentOperatorBitmap(operatorId); + } + + /// @notice Returns the length of the quorum bitmap history for the given `operatorId` + function getQuorumBitmapHistoryLength(bytes32 operatorId) external view returns (uint256) { + return _operatorBitmapHistory[operatorId].length; + } + + /// @notice Returns the number of registries + function numRegistries() external view returns (uint256) { + return registries.length; + } + + /** + * @notice Public function for the the churnApprover signature hash calculation when operators are being kicked from quorums + * @param registeringOperatorId The id of the registering operator + * @param operatorKickParams The parameters needed to kick the operator from the quorums that have reached their caps + * @param salt The salt to use for the churnApprover's signature + * @param expiry The desired expiry time of the churnApprover's signature + */ + function calculateOperatorChurnApprovalDigestHash( + address registeringOperator, + bytes32 registeringOperatorId, + OperatorKickParam[] memory operatorKickParams, + bytes32 salt, + uint256 expiry + ) public view returns (bytes32) { + // calculate the digest hash + return _hashTypedDataV4( + keccak256( + abi.encode( + OPERATOR_CHURN_APPROVAL_TYPEHASH, + registeringOperator, + registeringOperatorId, + operatorKickParams, + salt, + expiry + ) + ) + ); + } + + /** + * @notice Returns the message hash that an operator must sign to register their BLS public key. + * @param operator is the address of the operator registering their BLS public key + */ + function pubkeyRegistrationMessageHash(address operator) + public + view + returns (BN254.G1Point memory) + { + return BN254.hashToG1( + _hashTypedDataV4(keccak256(abi.encode(PUBKEY_REGISTRATION_TYPEHASH, operator))) + ); + } + + /// @dev need to override function here since its defined in both these contracts + function owner() + public + view + override(OwnableUpgradeable, ISlashingRegistryCoordinator) + returns (address) + { + return OwnableUpgradeable.owner(); + } +} \ No newline at end of file diff --git a/src/RegistryCoordinatorStorage.sol b/src/SlashingRegistryCoordinatorStorage.sol similarity index 86% rename from src/RegistryCoordinatorStorage.sol rename to src/SlashingRegistryCoordinatorStorage.sol index 00a422ba..7e53c3a4 100644 --- a/src/RegistryCoordinatorStorage.sol +++ b/src/SlashingRegistryCoordinatorStorage.sol @@ -6,19 +6,14 @@ import {IStakeRegistry} from "./interfaces/IStakeRegistry.sol"; import {IIndexRegistry} from "./interfaces/IIndexRegistry.sol"; import {IServiceManager} from "./interfaces/IServiceManager.sol"; import {IAVSDirectory} from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol"; -import { - IAllocationManager, - OperatorSet, - IAllocationManagerTypes -} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; -import {IRegistryCoordinator} from "./interfaces/IRegistryCoordinator.sol"; +import {IAllocationManager, OperatorSet, IAllocationManagerTypes} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; +import {ISlashingRegistryCoordinator} from "./interfaces/ISlashingRegistryCoordinator.sol"; -abstract contract RegistryCoordinatorStorage is IRegistryCoordinator { - /** - * - * CONSTANTS AND IMMUTABLES - * - */ +abstract contract SlashingRegistryCoordinatorStorage is ISlashingRegistryCoordinator { + + /******************************************************************************* + CONSTANTS AND IMMUTABLES + *******************************************************************************/ /// @notice The EIP-712 typehash for the `DelegationApproval` struct used by the contract bytes32 public constant OPERATOR_CHURN_APPROVAL_TYPEHASH = keccak256( @@ -40,8 +35,6 @@ abstract contract RegistryCoordinatorStorage is IRegistryCoordinator { /// @notice The maximum number of quorums this contract supports uint8 internal constant MAX_QUORUM_COUNT = 192; - /// @notice the ServiceManager for this AVS, which forwards calls onto EigenLayer's core contracts - IServiceManager public immutable serviceManager; /// @notice the BLS Aggregate Pubkey Registry contract that will keep track of operators' aggregate BLS public keys per quorum IBLSApkRegistry public immutable blsApkRegistry; /// @notice the Stake Registry contract that will keep track of operators' stakes @@ -88,18 +81,21 @@ abstract contract RegistryCoordinatorStorage is IRegistryCoordinator { /// @dev If true, operators must register to operator sets via the AllocationManager bool public isOperatorSetAVS; + /// @notice The account identifier for this AVS (used for UAM integration in EigenLayer) + /// @dev NOTE: Updating this value will break existing OperatorSets and UAM integration. + /// This value should only be set once. + address public accountIdentifier; + /// @notice Mapping from quorum number to whether the quorum is an M2 quorum /// @dev M2 quorums are pre-operator sets and track total delegated stake only mapping(uint8 => bool) public isM2Quorum; constructor( - IServiceManager _serviceManager, IStakeRegistry _stakeRegistry, IBLSApkRegistry _blsApkRegistry, IIndexRegistry _indexRegistry, IAllocationManager _allocationManager ) { - serviceManager = _serviceManager; stakeRegistry = _stakeRegistry; blsApkRegistry = _blsApkRegistry; indexRegistry = _indexRegistry; diff --git a/src/StakeRegistry.sol b/src/StakeRegistry.sol index d93763c3..c5e2bfcc 100644 --- a/src/StakeRegistry.sol +++ b/src/StakeRegistry.sol @@ -5,13 +5,11 @@ import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; import {IAVSDirectory} from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol"; import {OperatorSet} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; -import {IAllocationManager} from - "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; -import {IServiceManager} from "./interfaces/IServiceManager.sol"; +import {IAllocationManager} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; import {StakeRegistryStorage, IStrategy} from "./StakeRegistryStorage.sol"; -import {IRegistryCoordinator} from "./interfaces/IRegistryCoordinator.sol"; +import {ISlashingRegistryCoordinator} from "./interfaces/ISlashingRegistryCoordinator.sol"; import {IStakeRegistry, StakeType} from "./interfaces/IStakeRegistry.sol"; import {BitmapUtils} from "./libraries/BitmapUtils.sol"; @@ -28,13 +26,13 @@ import {BitmapUtils} from "./libraries/BitmapUtils.sol"; contract StakeRegistry is StakeRegistryStorage { using BitmapUtils for *; - modifier onlyRegistryCoordinator() { - _checkRegistryCoordinator(); + modifier onlySlashingRegistryCoordinator() { + _checkSlashingRegistryCoordinator(); _; } modifier onlyCoordinatorOwner() { - _checkRegistryCoordinatorOwner(); + _checkSlashingRegistryCoordinatorOwner(); _; } @@ -46,20 +44,16 @@ contract StakeRegistry is StakeRegistryStorage { } constructor( - IRegistryCoordinator _registryCoordinator, + ISlashingRegistryCoordinator _slashingRegistryCoordinator, IDelegationManager _delegationManager, IAVSDirectory _avsDirectory, - IAllocationManager _allocationManager, - IServiceManager _serviceManager - ) - StakeRegistryStorage( - _registryCoordinator, - _delegationManager, - _avsDirectory, - _allocationManager, - _serviceManager - ) - {} + IAllocationManager _allocationManager + ) StakeRegistryStorage( + _slashingRegistryCoordinator, + _delegationManager, + _avsDirectory, + _allocationManager + ) {} /** * @@ -84,7 +78,8 @@ contract StakeRegistry is StakeRegistryStorage { address operator, bytes32 operatorId, bytes calldata quorumNumbers - ) public virtual onlyRegistryCoordinator returns (uint96[] memory, uint96[] memory) { + ) public virtual onlySlashingRegistryCoordinator returns (uint96[] memory, uint96[] memory) { + uint96[] memory currentStakes = new uint96[](quorumNumbers.length); uint96[] memory totalStakes = new uint96[](quorumNumbers.length); for (uint256 i = 0; i < quorumNumbers.length; i++) { @@ -127,7 +122,7 @@ contract StakeRegistry is StakeRegistryStorage { function deregisterOperator( bytes32 operatorId, bytes calldata quorumNumbers - ) public virtual onlyRegistryCoordinator { + ) public virtual onlySlashingRegistryCoordinator { /** * For each quorum, remove the operator's stake for the quorum and update * the quorum's total stake to account for the removal @@ -161,7 +156,7 @@ contract StakeRegistry is StakeRegistryStorage { address operator, bytes32 operatorId, bytes calldata quorumNumbers - ) external onlyRegistryCoordinator returns (uint192) { + ) external onlySlashingRegistryCoordinator returns (uint192) { uint192 quorumsToRemove; /** @@ -207,7 +202,7 @@ contract StakeRegistry is StakeRegistryStorage { uint8 quorumNumber, uint96 minimumStake, StrategyParams[] memory _strategyParams - ) public virtual onlyRegistryCoordinator { + ) public virtual onlySlashingRegistryCoordinator { require(!_quorumExists(quorumNumber), QuorumAlreadyExists()); _addStrategyParams(quorumNumber, _strategyParams); _setMinimumStakeForQuorum(quorumNumber, minimumStake); @@ -228,7 +223,7 @@ contract StakeRegistry is StakeRegistryStorage { uint96 minimumStake, uint32 lookAheadPeriod, StrategyParams[] memory _strategyParams - ) public virtual onlyRegistryCoordinator { + ) public virtual onlySlashingRegistryCoordinator { require(!_quorumExists(quorumNumber), QuorumAlreadyExists()); _addStrategyParams(quorumNumber, _strategyParams); _setMinimumStakeForQuorum(quorumNumber, minimumStake); @@ -283,7 +278,7 @@ contract StakeRegistry is StakeRegistryStorage { strategiesToAdd[i] = _strategyParams[i].strategy; } allocationManager.addStrategiesToOperatorSet({ - avs: address(serviceManager), + avs: ISlashingRegistryCoordinator(registryCoordinator).accountIdentifier(), operatorSetId: quorumNumber, strategies: strategiesToAdd }); @@ -325,7 +320,7 @@ contract StakeRegistry is StakeRegistryStorage { if (isOperatorSetQuorum(quorumNumber)) { allocationManager.removeStrategiesFromOperatorSet({ - avs: address(serviceManager), + avs: ISlashingRegistryCoordinator(registryCoordinator).accountIdentifier(), operatorSetId: quorumNumber, strategies: _strategiesToRemove }); @@ -562,7 +557,7 @@ contract StakeRegistry is StakeRegistryStorage { uint32(block.number + slashableStakeLookAheadPerQuorum[quorumNumber]); uint256[][] memory slashableShares = allocationManager.getMinimumSlashableStake( - OperatorSet(address(serviceManager), quorumNumber), + OperatorSet(ISlashingRegistryCoordinator(registryCoordinator).accountIdentifier(), quorumNumber), operators, strategiesPerQuorum[quorumNumber], beforeTimestamp @@ -638,11 +633,9 @@ contract StakeRegistry is StakeRegistryStorage { * @param quorumNumber The quorum number to check * @return True if the quorum is an operator set quorum */ - function isOperatorSetQuorum( - uint8 quorumNumber - ) public view returns (bool) { - bool isM2 = IRegistryCoordinator(registryCoordinator).isM2Quorum(quorumNumber); - bool isOperatorSet = IRegistryCoordinator(registryCoordinator).isOperatorSetAVS(); + function isOperatorSetQuorum(uint8 quorumNumber) public view returns (bool) { + bool isM2 = ISlashingRegistryCoordinator(registryCoordinator).isM2Quorum(quorumNumber); + bool isOperatorSet = ISlashingRegistryCoordinator(registryCoordinator).isOperatorSetAVS(); return isOperatorSet && !isM2; } @@ -896,15 +889,13 @@ contract StakeRegistry is StakeRegistryStorage { emit LookAheadPeriodChanged(oldLookAheadDays, _lookAheadBlocks); } - function _checkRegistryCoordinator() internal view { - require(msg.sender == address(registryCoordinator), OnlyRegistryCoordinator()); + + function _checkSlashingRegistryCoordinator() internal view { + require(msg.sender == registryCoordinator, OnlySlashingRegistryCoordinator()); } - function _checkRegistryCoordinatorOwner() internal view { - require( - msg.sender == IRegistryCoordinator(registryCoordinator).owner(), - OnlyRegistryCoordinatorOwner() - ); + function _checkSlashingRegistryCoordinatorOwner() internal view { + require(msg.sender == ISlashingRegistryCoordinator(registryCoordinator).owner(), OnlySlashingRegistryCoordinatorOwner()); } function _checkQuorumExists( diff --git a/src/StakeRegistryStorage.sol b/src/StakeRegistryStorage.sol index c0d7b933..2e2277a9 100644 --- a/src/StakeRegistryStorage.sol +++ b/src/StakeRegistryStorage.sol @@ -4,16 +4,11 @@ pragma solidity ^0.8.27; import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; import {IAVSDirectory} from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol"; -import {IAllocationManager} from - "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; -import {IServiceManager} from "./interfaces/IServiceManager.sol"; -import { - IStrategyManager, - IStrategy -} from "eigenlayer-contracts/src/contracts/interfaces/IStrategyManager.sol"; +import {IAllocationManager} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; +import {IStrategyManager, IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategyManager.sol"; -import {IRegistryCoordinator} from "./interfaces/IRegistryCoordinator.sol"; -import {IStakeRegistry, StakeType} from "./interfaces/IStakeRegistry.sol"; +import {ISlashingRegistryCoordinator} from "./interfaces/ISlashingRegistryCoordinator.sol"; +import {IStakeRegistry, StakeType} from "./interfaces/IStakeRegistry.sol"; /** * @title Storage variables for the `StakeRegistry` contract. @@ -37,9 +32,6 @@ abstract contract StakeRegistryStorage is IStakeRegistry { /// @notice the address of the AllocationManager for EigenLayer. IAllocationManager public immutable allocationManager; - /// @notice the address of the ServiceManager associtated with the stake registries - IServiceManager public immutable serviceManager; - /// @notice the coordinator contract that this registry is associated with address public immutable registryCoordinator; @@ -69,16 +61,14 @@ abstract contract StakeRegistryStorage is IStakeRegistry { mapping(uint8 quorumNumber => uint32) public slashableStakeLookAheadPerQuorum; constructor( - IRegistryCoordinator _registryCoordinator, + ISlashingRegistryCoordinator _slashingRegistryCoordinator, IDelegationManager _delegationManager, IAVSDirectory _avsDirectory, - IAllocationManager _allocationManager, - IServiceManager _serviceManager + IAllocationManager _allocationManager ) { - registryCoordinator = address(_registryCoordinator); + registryCoordinator = address(_slashingRegistryCoordinator); delegation = _delegationManager; avsDirectory = _avsDirectory; - serviceManager = _serviceManager; allocationManager = _allocationManager; } diff --git a/src/interfaces/IBLSSignatureChecker.sol b/src/interfaces/IBLSSignatureChecker.sol index 6a7df2c5..87feab40 100644 --- a/src/interfaces/IBLSSignatureChecker.sol +++ b/src/interfaces/IBLSSignatureChecker.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.27; -import {IRegistryCoordinator} from "./IRegistryCoordinator.sol"; +import {ISlashingRegistryCoordinator} from "./ISlashingRegistryCoordinator.sol"; import {IBLSApkRegistry} from "./IBLSApkRegistry.sol"; import {IStakeRegistry, IDelegationManager} from "./IStakeRegistry.sol"; @@ -68,7 +68,7 @@ interface IBLSSignatureChecker is IBLSSignatureCheckerErrors { // CONSTANTS & IMMUTABLES - function registryCoordinator() external view returns (IRegistryCoordinator); + function registryCoordinator() external view returns (ISlashingRegistryCoordinator); function stakeRegistry() external view returns (IStakeRegistry); function blsApkRegistry() external view returns (IBLSApkRegistry); function delegation() external view returns (IDelegationManager); diff --git a/src/interfaces/IRegistryCoordinator.sol b/src/interfaces/IRegistryCoordinator.sol index 3a7fcfd7..de48c810 100644 --- a/src/interfaces/IRegistryCoordinator.sol +++ b/src/interfaces/IRegistryCoordinator.sol @@ -1,218 +1,66 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.27; -import {IServiceManager} from "./IServiceManager.sol"; +import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol"; import {IBLSApkRegistry} from "./IBLSApkRegistry.sol"; -import {IStakeRegistry} from "./IStakeRegistry.sol"; -import {IIndexRegistry} from "./IIndexRegistry.sol"; -import {BN254} from "../libraries/BN254.sol"; +import {ISlashingRegistryCoordinator} from "./ISlashingRegistryCoordinator.sol"; -interface IRegistryCoordinatorErrors { - error InputLengthMismatch(); - error OperatorSetsEnabled(); - error OperatorSetsNotEnabled(); - error OperatorSetsNotSupported(); - error OnlyAllocationManager(); - error OnlyEjector(); - error QuorumDoesNotExist(); - error BitmapEmpty(); - error AlreadyRegisteredForQuorums(); - error CannotReregisterYet(); - error NotRegistered(); - error CannotChurnSelf(); - error QuorumOperatorCountMismatch(); - error InsufficientStakeForChurn(); - error CannotKickOperatorAboveThreshold(); - error BitmapCannotBeZero(); - error NotRegisteredForQuorum(); - error MaxQuorumsReached(); - error SaltAlreadyUsed(); - error RegistryCoordinatorSignatureExpired(); - error ChurnApproverSaltUsed(); - error NotSorted(); -} -/** - * @title Interface for a contract that coordinates between various registries for an AVS. - * @author Layr Labs, Inc. - */ - -interface IRegistryCoordinator is IRegistryCoordinatorErrors { - // EVENTS +interface IRegistryCoordinator { /// Emits when an operator is registered event OperatorRegistered(address indexed operator, bytes32 indexed operatorId); - /// Emits when an operator is deregistered event OperatorDeregistered(address indexed operator, bytes32 indexed operatorId); - - event OperatorSetParamsUpdated(uint8 indexed quorumNumber, OperatorSetParam operatorSetParams); - - event ChurnApproverUpdated(address prevChurnApprover, address newChurnApprover); - - event EjectorUpdated(address prevEjector, address newEjector); - - /// @notice emitted when all the operators for a quorum are updated at once - event QuorumBlockNumberUpdated(uint8 indexed quorumNumber, uint256 blocknumber); - - // DATA STRUCTURES - enum OperatorStatus { - // default is NEVER_REGISTERED - NEVER_REGISTERED, - REGISTERED, - DEREGISTERED - } - - // STRUCTS - - /** - * @notice Data structure for storing info on operators - */ - struct OperatorInfo { - // the id of the operator, which is likely the keccak256 hash of the operator's public key if using BLSRegistry - bytes32 operatorId; - // indicates whether the operator is actively registered for serving the middleware or not - OperatorStatus status; - } - - /** - * @notice Data structure for storing info on quorum bitmap updates where the `quorumBitmap` is the bitmap of the - * quorums the operator is registered for starting at (inclusive)`updateBlockNumber` and ending at (exclusive) `nextUpdateBlockNumber` - * @dev nextUpdateBlockNumber is initialized to 0 for the latest update - */ - struct QuorumBitmapUpdate { - uint32 updateBlockNumber; - uint32 nextUpdateBlockNumber; - uint192 quorumBitmap; - } - - /** - * @notice Data structure for storing operator set params for a given quorum. Specifically the - * `maxOperatorCount` is the maximum number of operators that can be registered for the quorum, - * `kickBIPsOfOperatorStake` is the basis points of a new operator needs to have of an operator they are trying to kick from the quorum, - * and `kickBIPsOfTotalStake` is the basis points of the total stake of the quorum that an operator needs to be below to be kicked. - */ - struct OperatorSetParam { - uint32 maxOperatorCount; - uint16 kickBIPsOfOperatorStake; - uint16 kickBIPsOfTotalStake; - } - + /** - * @notice Data structure for the parameters needed to kick an operator from a quorum with number `quorumNumber`, used during registration churn. - * `operator` is the address of the operator to kick + * @notice Registers msg.sender as an operator for one or more quorums. If any quorum exceeds its maximum + * operator capacity after the operator is registered, this method will fail. + * @param quorumNumbers is an ordered byte array containing the quorum numbers being registered for + * @param socket is the socket of the operator (typically an IP address) + * @param params contains the G1 & G2 public keys of the operator, and a signature proving their ownership + * @param operatorSignature is the signature of the operator used by the AVS to register the operator in the delegation manager + * @dev `params` is ignored if the caller has previously registered a public key + * @dev `operatorSignature` is ignored if the operator's status is already REGISTERED */ - struct OperatorKickParam { - uint8 quorumNumber; - address operator; - } - - /// @notice Returns the operator set params for the given `quorumNumber` - function getOperatorSetParams( - uint8 quorumNumber - ) external view returns (OperatorSetParam memory); - /// @notice the Stake registry contract that will keep track of operators' stakes - function stakeRegistry() external view returns (IStakeRegistry); - /// @notice the BLS Aggregate Pubkey Registry contract that will keep track of operators' BLS aggregate pubkeys per quorum - function blsApkRegistry() external view returns (IBLSApkRegistry); - /// @notice the index Registry contract that will keep track of operators' indexes - function indexRegistry() external view returns (IIndexRegistry); + function registerOperator( + bytes memory quorumNumbers, + string memory socket, + IBLSApkRegistry.PubkeyRegistrationParams memory params, + ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature + ) external; /** - * @notice Ejects the provided operator from the provided quorums from the AVS - * @param operator is the operator to eject - * @param quorumNumbers are the quorum numbers to eject the operator from + * @notice Registers msg.sender as an operator for one or more quorums. If any quorum reaches its maximum operator + * capacity, `operatorKickParams` is used to replace an old operator with the new one. + * @param quorumNumbers is an ordered byte array containing the quorum numbers being registered for + * @param params contains the G1 & G2 public keys of the operator, and a signature proving their ownership + * @param operatorKickParams used to determine which operator is removed to maintain quorum capacity as the + * operator registers for quorums + * @param churnApproverSignature is the signature of the churnApprover over the `operatorKickParams` + * @param operatorSignature is the signature of the operator used by the AVS to register the operator in the delegation manager + * @dev `params` is ignored if the caller has previously registered a public key + * @dev `operatorSignature` is ignored if the operator's status is already REGISTERED */ - function ejectOperator(address operator, bytes calldata quorumNumbers) external; - - /// @notice Returns the number of quorums the registry coordinator has created - function quorumCount() external view returns (uint8); - - /// @notice Returns the operator struct for the given `operator` - function getOperator( - address operator - ) external view returns (OperatorInfo memory); - - /// @notice Returns the operatorId for the given `operator` - function getOperatorId( - address operator - ) external view returns (bytes32); - - /// @notice Returns the operator address for the given `operatorId` - function getOperatorFromId( - bytes32 operatorId - ) external view returns (address operator); - - /// @notice Returns the status for the given `operator` - function getOperatorStatus( - address operator - ) external view returns (IRegistryCoordinator.OperatorStatus); - - /// @notice Returns the indices of the quorumBitmaps for the provided `operatorIds` at the given `blockNumber` - function getQuorumBitmapIndicesAtBlockNumber( - uint32 blockNumber, - bytes32[] memory operatorIds - ) external view returns (uint32[] memory); + function registerOperatorWithChurn( + bytes calldata quorumNumbers, + string memory socket, + IBLSApkRegistry.PubkeyRegistrationParams memory params, + ISlashingRegistryCoordinator.OperatorKickParam[] memory operatorKickParams, + ISignatureUtils.SignatureWithSaltAndExpiry memory churnApproverSignature, + ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature + ) external; /** - * @notice Returns the quorum bitmap for the given `operatorId` at the given `blockNumber` via the `index` - * @dev reverts if `index` is incorrect + * @notice Deregisters the caller from one or more quorums + * @param quorumNumbers is an ordered byte array containing the quorum numbers being deregistered from */ - function getQuorumBitmapAtBlockNumberByIndex( - bytes32 operatorId, - uint32 blockNumber, - uint256 index - ) external view returns (uint192); - - /// @notice Returns the `index`th entry in the operator with `operatorId`'s bitmap history - function getQuorumBitmapUpdateByIndex( - bytes32 operatorId, - uint256 index - ) external view returns (QuorumBitmapUpdate memory); - - /// @notice Returns the current quorum bitmap for the given `operatorId` - function getCurrentQuorumBitmap( - bytes32 operatorId - ) external view returns (uint192); - - /// @notice Returns the length of the quorum bitmap history for the given `operatorId` - function getQuorumBitmapHistoryLength( - bytes32 operatorId - ) external view returns (uint256); - - /// @notice Returns the registry at the desired index - function registries( - uint256 - ) external view returns (address); - - /// @notice Returns the number of registries - function numRegistries() external view returns (uint256); - - /// @notice Returns whether a quorum is an M2 quorum - /// @param quorumNumber The quorum number to check - /// @return True if the quorum is an M2 quorum - function isM2Quorum( - uint8 quorumNumber - ) external view returns (bool); - - /// @notice Returns whether the AVS is an operator set AVS - /// @return True if the AVS is an operator set AVS - function isOperatorSetAVS() external view returns (bool); + function deregisterOperator(bytes memory quorumNumbers) external; /** - * @notice Returns the message hash that an operator must sign to register their BLS public key. - * @param operator is the address of the operator registering their BLS public key + * @notice Enables operator sets mode. This is by default initialized to set `isOperatorSetAVS` to True. + * So this is only meant to be called for existing AVSs that have a existing quorums and a previously deployed + * version of middleware contracts. + * @dev This is only callable by the owner of the RegistryCoordinator */ - function pubkeyRegistrationMessageHash( - address operator - ) external view returns (BN254.G1Point memory); - - /// @notice returns the blocknumber the quorum was last updated all at once for all operators - function quorumUpdateBlockNumber( - uint8 quorumNumber - ) external view returns (uint256); - - /// @notice The owner of the registry coordinator - function owner() external view returns (address); - - function serviceManager() external view returns (IServiceManager); + function enableOperatorSets() external; } diff --git a/src/interfaces/ISlashingRegistryCoordinator.sol b/src/interfaces/ISlashingRegistryCoordinator.sol new file mode 100644 index 00000000..1d98bc72 --- /dev/null +++ b/src/interfaces/ISlashingRegistryCoordinator.sol @@ -0,0 +1,205 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.27; + +import {IServiceManager} from "./IServiceManager.sol"; +import {IBLSApkRegistry} from "./IBLSApkRegistry.sol"; +import {IStakeRegistry} from "./IStakeRegistry.sol"; +import {IIndexRegistry} from "./IIndexRegistry.sol"; +import {BN254} from "../libraries/BN254.sol"; + +interface IRegistryCoordinatorErrors { + error InputLengthMismatch(); + error OperatorSetsEnabled(); + error OperatorSetsNotEnabled(); + error OperatorSetsNotSupported(); + error OnlyAllocationManager(); + error OnlyEjector(); + error QuorumDoesNotExist(); + error BitmapEmpty(); + error AlreadyRegisteredForQuorums(); + error CannotReregisterYet(); + error NotRegistered(); + error CannotChurnSelf(); + error QuorumOperatorCountMismatch(); + error InsufficientStakeForChurn(); + error CannotKickOperatorAboveThreshold(); + error BitmapCannotBeZero(); + error NotRegisteredForQuorum(); + error MaxQuorumsReached(); + error SaltAlreadyUsed(); + error RegistryCoordinatorSignatureExpired(); + error ChurnApproverSaltUsed(); + error NotSorted(); + error InvalidRegistrationType(); +} +/** + * @title Interface for a contract that coordinates between various registries for an AVS. + * @author Layr Labs, Inc. + */ +interface ISlashingRegistryCoordinator is IRegistryCoordinatorErrors { + // EVENTS + + event OperatorSetParamsUpdated(uint8 indexed quorumNumber, OperatorSetParam operatorSetParams); + + event ChurnApproverUpdated(address prevChurnApprover, address newChurnApprover); + + event EjectorUpdated(address prevEjector, address newEjector); + + /// @notice emitted when all the operators for a quorum are updated at once + event QuorumBlockNumberUpdated(uint8 indexed quorumNumber, uint256 blocknumber); + + // DATA STRUCTURES + enum OperatorStatus { + // default is NEVER_REGISTERED + NEVER_REGISTERED, + REGISTERED, + DEREGISTERED + } + + enum RegistrationType { + NORMAL, + CHURN + } + + // STRUCTS + + /** + * @notice Data structure for storing info on operators + */ + struct OperatorInfo { + // the id of the operator, which is likely the keccak256 hash of the operator's public key if using BLSRegistry + bytes32 operatorId; + // indicates whether the operator is actively registered for serving the middleware or not + OperatorStatus status; + } + + /** + * @notice Data structure for storing info on quorum bitmap updates where the `quorumBitmap` is the bitmap of the + * quorums the operator is registered for starting at (inclusive)`updateBlockNumber` and ending at (exclusive) `nextUpdateBlockNumber` + * @dev nextUpdateBlockNumber is initialized to 0 for the latest update + */ + struct QuorumBitmapUpdate { + uint32 updateBlockNumber; + uint32 nextUpdateBlockNumber; + uint192 quorumBitmap; + } + + /** + * @notice Data structure for storing the results of a registerOperator call + * @dev For each quorum the operator registered for, numOperatorsPerQuorum is the number of operators registered + * @dev For each quorum the operator registered for, operatorStakes is the stake of the operator in the quorum + * @dev For each quorum the operator registered for, totalStakes is the total stake of the quorum + */ + struct RegisterResults { + uint32[] numOperatorsPerQuorum; + uint96[] operatorStakes; + uint96[] totalStakes; + } + + /** + * @notice Data structure for storing operator set params for a given quorum. Specifically the + * `maxOperatorCount` is the maximum number of operators that can be registered for the quorum, + * `kickBIPsOfOperatorStake` is the basis points of a new operator needs to have of an operator they are trying to kick from the quorum, + * and `kickBIPsOfTotalStake` is the basis points of the total stake of the quorum that an operator needs to be below to be kicked. + */ + struct OperatorSetParam { + uint32 maxOperatorCount; + uint16 kickBIPsOfOperatorStake; + uint16 kickBIPsOfTotalStake; + } + + /** + * @notice Data structure for the parameters needed to kick an operator from a quorum with number `quorumNumber`, used during registration churn. + * `operator` is the address of the operator to kick + */ + struct OperatorKickParam { + uint8 quorumNumber; + address operator; + } + + /// @notice Returns the operator set params for the given `quorumNumber` + function getOperatorSetParams(uint8 quorumNumber) external view returns (OperatorSetParam memory); + /// @notice the Stake registry contract that will keep track of operators' stakes + function stakeRegistry() external view returns (IStakeRegistry); + /// @notice the BLS Aggregate Pubkey Registry contract that will keep track of operators' BLS aggregate pubkeys per quorum + function blsApkRegistry() external view returns (IBLSApkRegistry); + /// @notice the index Registry contract that will keep track of operators' indexes + function indexRegistry() external view returns (IIndexRegistry); + + /** + * @notice Ejects the provided operator from the provided quorums from the AVS + * @param operator is the operator to eject + * @param quorumNumbers are the quorum numbers to eject the operator from + */ + function ejectOperator( + address operator, + bytes calldata quorumNumbers + ) external; + + /// @notice Returns the number of quorums the registry coordinator has created + function quorumCount() external view returns (uint8); + + /// @notice Returns the operator struct for the given `operator` + function getOperator(address operator) external view returns (OperatorInfo memory); + + /// @notice Returns the operatorId for the given `operator` + function getOperatorId(address operator) external view returns (bytes32); + + /// @notice Returns the operator address for the given `operatorId` + function getOperatorFromId(bytes32 operatorId) external view returns (address operator); + + /// @notice Returns the status for the given `operator` + function getOperatorStatus(address operator) external view returns (OperatorStatus); + + /// @notice Returns the indices of the quorumBitmaps for the provided `operatorIds` at the given `blockNumber` + function getQuorumBitmapIndicesAtBlockNumber(uint32 blockNumber, bytes32[] memory operatorIds) external view returns (uint32[] memory); + + /** + * @notice Returns the quorum bitmap for the given `operatorId` at the given `blockNumber` via the `index` + * @dev reverts if `index` is incorrect + */ + function getQuorumBitmapAtBlockNumberByIndex(bytes32 operatorId, uint32 blockNumber, uint256 index) external view returns (uint192); + + /// @notice Returns the `index`th entry in the operator with `operatorId`'s bitmap history + function getQuorumBitmapUpdateByIndex(bytes32 operatorId, uint256 index) external view returns (QuorumBitmapUpdate memory); + + /// @notice Returns the current quorum bitmap for the given `operatorId` + function getCurrentQuorumBitmap(bytes32 operatorId) external view returns (uint192); + + /// @notice Returns the length of the quorum bitmap history for the given `operatorId` + function getQuorumBitmapHistoryLength(bytes32 operatorId) external view returns (uint256); + + /// @notice Returns the registry at the desired index + function registries(uint256) external view returns (address); + + /// @notice Returns the number of registries + function numRegistries() external view returns (uint256); + + /// @notice Returns whether a quorum is an M2 quorum + /// @param quorumNumber The quorum number to check + /// @return True if the quorum is an M2 quorum + function isM2Quorum(uint8 quorumNumber) external view returns (bool); + + /// @notice Returns whether the AVS is an operator set AVS + /// @return True if the AVS is an operator set AVS + function isOperatorSetAVS() external view returns (bool); + + /** + * @notice Returns the message hash that an operator must sign to register their BLS public key. + * @param operator is the address of the operator registering their BLS public key + */ + function pubkeyRegistrationMessageHash(address operator) external view returns (BN254.G1Point memory); + + /// @notice returns the blocknumber the quorum was last updated all at once for all operators + function quorumUpdateBlockNumber(uint8 quorumNumber) external view returns (uint256); + + /// @notice The owner of the registry coordinator + function owner() external view returns (address); + + /** + * @notice The account identifier for this AVS (used for UAM integration in EigenLayer) + * @dev NOTE: Updating this value will break existing OperatorSets and UAM integration. + * This value should only be set once. + */ + function accountIdentifier() external view returns (address); +} diff --git a/src/interfaces/IStakeRegistry.sol b/src/interfaces/IStakeRegistry.sol index a9d3d5a3..66108a75 100644 --- a/src/interfaces/IStakeRegistry.sol +++ b/src/interfaces/IStakeRegistry.sol @@ -14,9 +14,9 @@ enum StakeType { interface IStakeRegistryErrors { /// @dev Thrown when the caller is not the registry coordinator - error OnlyRegistryCoordinator(); + error OnlySlashingRegistryCoordinator(); /// @dev Thrown when the caller is not the owner of the registry coordinator - error OnlyRegistryCoordinatorOwner(); + error OnlySlashingRegistryCoordinatorOwner(); /// @dev Thrown when the stake is below the minimum required for a quorum error BelowMinimumStakeRequirement(); /// @dev Thrown when a quorum being created already exists. diff --git a/src/libraries/QuorumBitmapHistoryLib.sol b/src/libraries/QuorumBitmapHistoryLib.sol index 92848aba..c881316d 100644 --- a/src/libraries/QuorumBitmapHistoryLib.sol +++ b/src/libraries/QuorumBitmapHistoryLib.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.27; -import {IRegistryCoordinator} from "../interfaces/IRegistryCoordinator.sol"; +import {ISlashingRegistryCoordinator} from "../interfaces/ISlashingRegistryCoordinator.sol"; /// @title QuorumBitmapHistoryLib /// @notice This library operates on the _operatorBitmapHistory in the RegistryCoordinator @@ -19,7 +19,7 @@ library QuorumBitmapHistoryLib { /// @param operatorId The ID of the operator /// @return index The index of the quorum bitmap update function getQuorumBitmapIndexAtBlockNumber( - mapping(bytes32 => IRegistryCoordinator.QuorumBitmapUpdate[]) storage self, + mapping(bytes32 => ISlashingRegistryCoordinator.QuorumBitmapUpdate[]) storage self, uint32 blockNumber, bytes32 operatorId ) internal view returns (uint32 index) { @@ -45,7 +45,7 @@ library QuorumBitmapHistoryLib { /// @param operatorId The ID of the operator /// @return The current quorum bitmap function currentOperatorBitmap( - mapping(bytes32 => IRegistryCoordinator.QuorumBitmapUpdate[]) storage self, + mapping(bytes32 => ISlashingRegistryCoordinator.QuorumBitmapUpdate[]) storage self, bytes32 operatorId ) internal view returns (uint192) { uint256 historyLength = self[operatorId].length; @@ -62,7 +62,7 @@ library QuorumBitmapHistoryLib { /// @param operatorIds The array of operator IDs /// @return An array of indices corresponding to the quorum bitmap updates function getQuorumBitmapIndicesAtBlockNumber( - mapping(bytes32 => IRegistryCoordinator.QuorumBitmapUpdate[]) storage self, + mapping(bytes32 => ISlashingRegistryCoordinator.QuorumBitmapUpdate[]) storage self, uint32 blockNumber, bytes32[] memory operatorIds ) internal view returns (uint32[] memory) { @@ -80,12 +80,12 @@ library QuorumBitmapHistoryLib { /// @param index The index of the quorum bitmap update /// @return The quorum bitmap at the specified index function getQuorumBitmapAtBlockNumberByIndex( - mapping(bytes32 => IRegistryCoordinator.QuorumBitmapUpdate[]) storage self, + mapping(bytes32 => ISlashingRegistryCoordinator.QuorumBitmapUpdate[]) storage self, bytes32 operatorId, uint32 blockNumber, uint256 index ) internal view returns (uint192) { - IRegistryCoordinator.QuorumBitmapUpdate memory quorumBitmapUpdate = self[operatorId][index]; + ISlashingRegistryCoordinator.QuorumBitmapUpdate memory quorumBitmapUpdate = self[operatorId][index]; /** * Validate that the update is valid for the given blockNumber: @@ -109,7 +109,7 @@ library QuorumBitmapHistoryLib { /// @param operatorId The ID of the operator /// @param newBitmap The new quorum bitmap to set function updateOperatorBitmap( - mapping(bytes32 => IRegistryCoordinator.QuorumBitmapUpdate[]) storage self, + mapping(bytes32 => ISlashingRegistryCoordinator.QuorumBitmapUpdate[]) storage self, bytes32 operatorId, uint192 newBitmap ) internal { @@ -118,7 +118,7 @@ library QuorumBitmapHistoryLib { if (historyLength == 0) { // No prior bitmap history - push our first entry self[operatorId].push( - IRegistryCoordinator.QuorumBitmapUpdate({ + ISlashingRegistryCoordinator.QuorumBitmapUpdate({ updateBlockNumber: uint32(block.number), nextUpdateBlockNumber: 0, quorumBitmap: newBitmap @@ -126,7 +126,7 @@ library QuorumBitmapHistoryLib { ); } else { // We have prior history - fetch our last-recorded update - IRegistryCoordinator.QuorumBitmapUpdate storage lastUpdate = + ISlashingRegistryCoordinator.QuorumBitmapUpdate storage lastUpdate = self[operatorId][historyLength - 1]; /** @@ -138,7 +138,7 @@ library QuorumBitmapHistoryLib { } else { lastUpdate.nextUpdateBlockNumber = uint32(block.number); self[operatorId].push( - IRegistryCoordinator.QuorumBitmapUpdate({ + ISlashingRegistryCoordinator.QuorumBitmapUpdate({ updateBlockNumber: uint32(block.number), nextUpdateBlockNumber: 0, quorumBitmap: newBitmap diff --git a/src/slashers/InstantSlasher.sol b/src/slashers/InstantSlasher.sol index 5575525a..bb987189 100644 --- a/src/slashers/InstantSlasher.sol +++ b/src/slashers/InstantSlasher.sol @@ -2,17 +2,16 @@ pragma solidity ^0.8.27; import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; -import {IAllocationManager} from - "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; -import {IServiceManager} from "../interfaces/IServiceManager.sol"; +import {IAllocationManager} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; +import {ISlashingRegistryCoordinator} from "../interfaces/ISlashingRegistryCoordinator.sol"; import {SlasherBase} from "./base/SlasherBase.sol"; contract InstantSlasher is SlasherBase { constructor( IAllocationManager _allocationManager, - IServiceManager _serviceManager, + ISlashingRegistryCoordinator _slashingRegistryCoordinator, address _slasher - ) SlasherBase(_allocationManager, _serviceManager) {} + ) SlasherBase(_allocationManager, _slashingRegistryCoordinator) {} function initialize( address _slasher diff --git a/src/slashers/VetoableSlasher.sol b/src/slashers/VetoableSlasher.sol index 87112d21..949c46c2 100644 --- a/src/slashers/VetoableSlasher.sol +++ b/src/slashers/VetoableSlasher.sol @@ -5,7 +5,7 @@ import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy import {IAllocationManager} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; import {SlasherBase} from "./base/SlasherBase.sol"; -import {IServiceManager} from "../interfaces/IServiceManager.sol"; +import {ISlashingRegistryCoordinator} from "../interfaces/ISlashingRegistryCoordinator.sol"; contract VetoableSlasher is SlasherBase { uint256 public constant VETO_PERIOD = 3 days; @@ -20,9 +20,9 @@ contract VetoableSlasher is SlasherBase { constructor( IAllocationManager _allocationManager, - IServiceManager _serviceManager, + ISlashingRegistryCoordinator _slashingRegistryCoordinator, address _slasher - ) SlasherBase(_allocationManager, _serviceManager) {} + ) SlasherBase(_allocationManager, _slashingRegistryCoordinator) {} function initialize(address _vetoCommittee, address _slasher) external virtual initializer { __SlasherBase_init(_slasher); diff --git a/src/slashers/base/SlasherBase.sol b/src/slashers/base/SlasherBase.sol index 370e60b1..a802dd97 100644 --- a/src/slashers/base/SlasherBase.sol +++ b/src/slashers/base/SlasherBase.sol @@ -2,12 +2,8 @@ pragma solidity ^0.8.27; import {Initializable} from "@openzeppelin-upgrades/contracts/proxy/utils/Initializable.sol"; -import {IServiceManager} from "../../interfaces/IServiceManager.sol"; -import {SlasherStorage} from "./SlasherStorage.sol"; -import { - IAllocationManagerTypes, - IAllocationManager -} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; +import {SlasherStorage, ISlashingRegistryCoordinator} from "./SlasherStorage.sol"; +import {IAllocationManagerTypes, IAllocationManager} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; abstract contract SlasherBase is Initializable, SlasherStorage { @@ -18,8 +14,8 @@ abstract contract SlasherBase is Initializable, SlasherStorage { constructor( IAllocationManager _allocationManager, - IServiceManager _serviceManager - ) SlasherStorage(_allocationManager, _serviceManager) { + ISlashingRegistryCoordinator _registryCoordinator + ) SlasherStorage(_allocationManager, _registryCoordinator) { _disableInitializers(); } @@ -33,14 +29,11 @@ abstract contract SlasherBase is Initializable, SlasherStorage { uint256 _requestId, IAllocationManager.SlashingParams memory _params ) internal virtual { - allocationManager.slashOperator({avs: address(serviceManager), params: _params}); - emit OperatorSlashed( - _requestId, - _params.operator, - _params.operatorSetId, - _params.wadsToSlash, - _params.description - ); + allocationManager.slashOperator({ + avs: slashingRegistryCoordinator.accountIdentifier(), + params: _params + }); + emit OperatorSlashed(_requestId, _params.operator, _params.operatorSetId, _params.wadsToSlash, _params.description); } function _checkSlasher( diff --git a/src/slashers/base/SlasherStorage.sol b/src/slashers/base/SlasherStorage.sol index 1b8d0b1b..0121554e 100644 --- a/src/slashers/base/SlasherStorage.sol +++ b/src/slashers/base/SlasherStorage.sol @@ -4,8 +4,7 @@ pragma solidity ^0.8.27; import {IAllocationManager} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; import {ISlasher} from "../../interfaces/ISlasher.sol"; -import {IServiceManager} from "../../interfaces/IServiceManager.sol"; - +import {ISlashingRegistryCoordinator} from "../../interfaces/ISlashingRegistryCoordinator.sol"; contract SlasherStorage is ISlasher { /** * @@ -15,21 +14,19 @@ contract SlasherStorage is ISlasher { /// @notice the AllocationManager that tracks OperatorSets and Slashing in EigenLayer IAllocationManager public immutable allocationManager; - /// @notice the ServiceManager for this AVS, which forwards calls onto EigenLayer's core contracts - IServiceManager public immutable serviceManager; + /// @notice the SlashingRegistryCoordinator for this AVS + ISlashingRegistryCoordinator public immutable slashingRegistryCoordinator; + /******************************************************************************* + STATE + *******************************************************************************/ - /** - * - * STATE - * - */ address public slasher; uint256 public nextRequestId; - constructor(IAllocationManager _allocationManager, IServiceManager _serviceManager) { + constructor(IAllocationManager _allocationManager, ISlashingRegistryCoordinator _slashingRegistryCoordinator) { allocationManager = _allocationManager; - serviceManager = _serviceManager; + slashingRegistryCoordinator = _slashingRegistryCoordinator; } uint256[48] private __gap; diff --git a/test/ffi/BLSPubKeyCompendiumFFI.t.sol b/test/ffi/BLSPubKeyCompendiumFFI.t.sol index b2c078b2..78d3ca72 100644 --- a/test/ffi/BLSPubKeyCompendiumFFI.t.sol +++ b/test/ffi/BLSPubKeyCompendiumFFI.t.sol @@ -4,6 +4,7 @@ pragma solidity ^0.8.27; import "../../src/BLSApkRegistry.sol"; import "../ffi/util/G2Operations.sol"; import {IBLSApkRegistry} from "../../src/interfaces/IBLSApkRegistry.sol"; +import {ISlashingRegistryCoordinator} from "../../src/interfaces/ISlashingRegistryCoordinator.sol"; contract BLSApkRegistryFFITests is G2Operations { using BN254 for BN254.G1Point; @@ -12,7 +13,7 @@ contract BLSApkRegistryFFITests is G2Operations { Vm cheats = Vm(VM_ADDRESS); BLSApkRegistry blsApkRegistry; - IRegistryCoordinator registryCoordinator; + ISlashingRegistryCoordinator registryCoordinator; uint256 privKey; IBLSApkRegistry.PubkeyRegistrationParams pubkeyRegistrationParams; diff --git a/test/harnesses/BLSApkRegistryHarness.sol b/test/harnesses/BLSApkRegistryHarness.sol index 1ee7df30..b97ba955 100644 --- a/test/harnesses/BLSApkRegistryHarness.sol +++ b/test/harnesses/BLSApkRegistryHarness.sol @@ -6,8 +6,8 @@ import "../../src/BLSApkRegistry.sol"; // wrapper around the BLSApkRegistry contract that exposes internal functionality, for unit testing _other functionality_. contract BLSApkRegistryHarness is BLSApkRegistry { constructor( - IRegistryCoordinator _registryCoordinator - ) BLSApkRegistry(_registryCoordinator) {} + ISlashingRegistryCoordinator _slashingRegistryCoordinator + ) BLSApkRegistry(_slashingRegistryCoordinator) {} function setBLSPublicKey(address account, BN254.G1Point memory pk) external { bytes32 pubkeyHash = BN254.hashG1Point(pk); diff --git a/test/harnesses/StakeRegistryHarness.sol b/test/harnesses/StakeRegistryHarness.sol index 4e156aac..3fe3a187 100644 --- a/test/harnesses/StakeRegistryHarness.sol +++ b/test/harnesses/StakeRegistryHarness.sol @@ -6,20 +6,12 @@ import "../../src/StakeRegistry.sol"; // wrapper around the StakeRegistry contract that exposes the internal functions for unit testing. contract StakeRegistryHarness is StakeRegistry { constructor( - IRegistryCoordinator _registryCoordinator, + ISlashingRegistryCoordinator _registryCoordinator, IDelegationManager _delegationManager, IAVSDirectory _avsDirectory, - IAllocationManager _allocationManager, - IServiceManager _serviceManager - ) - StakeRegistry( - _registryCoordinator, - _delegationManager, - _avsDirectory, - _allocationManager, - _serviceManager - ) - {} + IAllocationManager _allocationManager + ) StakeRegistry(_registryCoordinator, _delegationManager, _avsDirectory, _allocationManager) { + } function recordOperatorStakeUpdate( bytes32 operatorId, diff --git a/test/integration/IntegrationBase.t.sol b/test/integration/IntegrationBase.t.sol index 15371667..388535c6 100644 --- a/test/integration/IntegrationBase.t.sol +++ b/test/integration/IntegrationBase.t.sol @@ -29,24 +29,22 @@ abstract contract IntegrationBase is IntegrationConfig { /// @dev Also checks that the user has NEVER_REGISTERED status function assert_HasNoOperatorInfo(User user, string memory err) internal { - IRegistryCoordinator.OperatorInfo memory info = _getOperatorInfo(user); + ISlashingRegistryCoordinator.OperatorInfo memory info = _getOperatorInfo(user); assertEq(info.operatorId, bytes32(0), err); - assertTrue(info.status == IRegistryCoordinator.OperatorStatus.NEVER_REGISTERED, err); + assertTrue(info.status == ISlashingRegistryCoordinator.OperatorStatus.NEVER_REGISTERED, err); } function assert_HasRegisteredStatus(User user, string memory err) internal { - IRegistryCoordinator.OperatorStatus status = - registryCoordinator.getOperatorStatus(address(user)); + ISlashingRegistryCoordinator.OperatorStatus status = registryCoordinator.getOperatorStatus(address(user)); - assertTrue(status == IRegistryCoordinator.OperatorStatus.REGISTERED, err); + assertTrue(status == ISlashingRegistryCoordinator.OperatorStatus.REGISTERED, err); } function assert_HasDeregisteredStatus(User user, string memory err) internal { - IRegistryCoordinator.OperatorStatus status = - registryCoordinator.getOperatorStatus(address(user)); + ISlashingRegistryCoordinator.OperatorStatus status = registryCoordinator.getOperatorStatus(address(user)); - assertTrue(status == IRegistryCoordinator.OperatorStatus.DEREGISTERED, err); + assertTrue(status == ISlashingRegistryCoordinator.OperatorStatus.DEREGISTERED, err); } function assert_EmptyQuorumBitmap(User user, string memory err) internal { @@ -243,8 +241,8 @@ abstract contract IntegrationBase is IntegrationConfig { } function assert_Snap_Unchanged_OperatorInfo(User user, string memory err) internal { - IRegistryCoordinator.OperatorInfo memory curInfo = _getOperatorInfo(user); - IRegistryCoordinator.OperatorInfo memory prevInfo = _getPrevOperatorInfo(user); + ISlashingRegistryCoordinator.OperatorInfo memory curInfo = _getOperatorInfo(user); + ISlashingRegistryCoordinator.OperatorInfo memory prevInfo = _getPrevOperatorInfo(user); assertEq(prevInfo.operatorId, curInfo.operatorId, err); assertTrue(prevInfo.status == curInfo.status, err); @@ -864,15 +862,11 @@ abstract contract IntegrationBase is IntegrationConfig { /// RegistryCoordinator: - function _getOperatorInfo( - User user - ) internal view returns (IRegistryCoordinator.OperatorInfo memory) { + function _getOperatorInfo(User user) internal view returns (ISlashingRegistryCoordinator.OperatorInfo memory) { return registryCoordinator.getOperator(address(user)); } - function _getPrevOperatorInfo( - User user - ) internal timewarp returns (IRegistryCoordinator.OperatorInfo memory) { + function _getPrevOperatorInfo(User user) internal timewarp() returns (ISlashingRegistryCoordinator.OperatorInfo memory) { return _getOperatorInfo(user); } diff --git a/test/integration/IntegrationConfig.t.sol b/test/integration/IntegrationConfig.t.sol index e04a7204..4581372f 100644 --- a/test/integration/IntegrationConfig.t.sol +++ b/test/integration/IntegrationConfig.t.sol @@ -158,8 +158,7 @@ contract IntegrationConfig is IntegrationDeployer, G2Operations, Constants { emit log_named_uint("_configRand: number of quorums being initialized", quorumCount); // Default OperatorSetParams for all quorums - IRegistryCoordinator.OperatorSetParam memory operatorSet = IRegistryCoordinator - .OperatorSetParam({ + ISlashingRegistryCoordinator.OperatorSetParam memory operatorSet = ISlashingRegistryCoordinator.OperatorSetParam({ maxOperatorCount: MAX_OPERATOR_COUNT, kickBIPsOfOperatorStake: KICK_BIPS_OPERATOR_STAKE, kickBIPsOfTotalStake: KICK_BIPS_TOTAL_STAKE @@ -318,8 +317,8 @@ contract IntegrationConfig is IntegrationDeployer, G2Operations, Constants { for (uint256 i = 0; i < churnQuorums.length; i++) { uint8 quorum = uint8(churnQuorums[i]); - IRegistryCoordinator.OperatorSetParam memory params = - registryCoordinator.getOperatorSetParams(quorum); + ISlashingRegistryCoordinator.OperatorSetParam memory params + = registryCoordinator.getOperatorSetParams(quorum); // Sanity check - make sure we're at the operator cap uint32 curNumOperators = indexRegistry.totalOperatorsForQuorum(quorum); @@ -365,7 +364,7 @@ contract IntegrationConfig is IntegrationDeployer, G2Operations, Constants { /// From RegistryCoordinator._individualKickThreshold function _individualKickThreshold( uint96 operatorStake, - IRegistryCoordinator.OperatorSetParam memory setParams + ISlashingRegistryCoordinator.OperatorSetParam memory setParams ) internal pure returns (uint96) { return operatorStake * setParams.kickBIPsOfOperatorStake / BIPS_DENOMINATOR; } @@ -373,7 +372,7 @@ contract IntegrationConfig is IntegrationDeployer, G2Operations, Constants { /// From RegistryCoordinator._totalKickThreshold function _totalKickThreshold( uint96 totalStake, - IRegistryCoordinator.OperatorSetParam memory setParams + ISlashingRegistryCoordinator.OperatorSetParam memory setParams ) internal pure returns (uint96) { return totalStake * setParams.kickBIPsOfTotalStake / BIPS_DENOMINATOR; } @@ -565,10 +564,8 @@ contract IntegrationConfig is IntegrationDeployer, G2Operations, Constants { * @dev Uses _randFillType to determine how many operators to register for a quorum initially * @return The number of operators to register */ - function _randInitialOperators( - IRegistryCoordinator.OperatorSetParam memory operatorSet - ) private returns (uint256) { - uint256 fillTypeFlag = _randValue(fillTypeFlags); + function _randInitialOperators(ISlashingRegistryCoordinator.OperatorSetParam memory operatorSet) private returns (uint) { + uint fillTypeFlag = _randValue(fillTypeFlags); if (fillTypeFlag == EMPTY) { return 0; diff --git a/test/integration/IntegrationDeployer.t.sol b/test/integration/IntegrationDeployer.t.sol index f1899fe7..58b4091e 100644 --- a/test/integration/IntegrationDeployer.t.sol +++ b/test/integration/IntegrationDeployer.t.sol @@ -340,20 +340,16 @@ abstract contract IntegrationDeployer is Test, IUserDeployer { cheats.stopPrank(); StakeRegistry stakeRegistryImplementation = new StakeRegistry( - IRegistryCoordinator(registryCoordinator), - IDelegationManager(delegationManager), - IAVSDirectory(avsDirectory), - allocationManager, - IServiceManager(serviceManager) + ISlashingRegistryCoordinator(registryCoordinator), IDelegationManager(delegationManager), IAVSDirectory(avsDirectory), allocationManager ); BLSApkRegistry blsApkRegistryImplementation = - new BLSApkRegistry(IRegistryCoordinator(registryCoordinator)); + new BLSApkRegistry(ISlashingRegistryCoordinator(registryCoordinator)); IndexRegistry indexRegistryImplementation = - new IndexRegistry(IRegistryCoordinator(registryCoordinator)); + new IndexRegistry(ISlashingRegistryCoordinator(registryCoordinator)); ServiceManagerMock serviceManagerImplementation = new ServiceManagerMock( IAVSDirectory(avsDirectory), rewardsCoordinator, - IRegistryCoordinator(registryCoordinator), + ISlashingRegistryCoordinator(registryCoordinator), stakeRegistry, permissionController, allocationManager @@ -399,16 +395,12 @@ abstract contract IntegrationDeployer is Test, IUserDeployer { TransparentUpgradeableProxy(payable(address(registryCoordinator))), address(registryCoordinatorImplementation), abi.encodeWithSelector( - RegistryCoordinator.initialize.selector, + SlashingRegistryCoordinator.initialize.selector, registryCoordinatorOwner, churnApprover, ejector, 0, /*initialPausedStatus*/ - new IRegistryCoordinator.OperatorSetParam[](0), - new uint96[](0), - new IStakeRegistry.StrategyParams[][](0), - quorumStakeTypes, - slashableStakeQuorumLookAheadPeriods + address(serviceManager) // _accountIdentifier ) ); diff --git a/test/integration/User.t.sol b/test/integration/User.t.sol index fbc491f5..42e8740e 100644 --- a/test/integration/User.t.sol +++ b/test/integration/User.t.sol @@ -160,8 +160,8 @@ contract User is Test { bytes memory allQuorums = churnBitmap.plus(standardBitmap).bitmapToBytesArray(); - IRegistryCoordinator.OperatorKickParam[] memory kickParams = - new IRegistryCoordinator.OperatorKickParam[](allQuorums.length); + ISlashingRegistryCoordinator.OperatorKickParam[] memory kickParams + = new ISlashingRegistryCoordinator.OperatorKickParam[](allQuorums.length); // this constructs OperatorKickParam[] in ascending quorum order // (yikes) @@ -169,20 +169,22 @@ contract User is Test { uint256 stdIdx; while (churnIdx + stdIdx < allQuorums.length) { if (churnIdx == churnQuorums.length) { - kickParams[churnIdx + stdIdx] = - IRegistryCoordinator.OperatorKickParam({quorumNumber: 0, operator: address(0)}); + kickParams[churnIdx + stdIdx] = ISlashingRegistryCoordinator.OperatorKickParam({ + quorumNumber: 0, + operator: address(0) + }); stdIdx++; - } else if ( - stdIdx == standardQuorums.length || churnQuorums[churnIdx] < standardQuorums[stdIdx] - ) { - kickParams[churnIdx + stdIdx] = IRegistryCoordinator.OperatorKickParam({ + } else if (stdIdx == standardQuorums.length || churnQuorums[churnIdx] < standardQuorums[stdIdx]) { + kickParams[churnIdx + stdIdx] = ISlashingRegistryCoordinator.OperatorKickParam({ quorumNumber: uint8(churnQuorums[churnIdx]), operator: address(churnTargets[churnIdx]) }); churnIdx++; } else if (standardQuorums[stdIdx] < churnQuorums[churnIdx]) { - kickParams[churnIdx + stdIdx] = - IRegistryCoordinator.OperatorKickParam({quorumNumber: 0, operator: address(0)}); + kickParams[churnIdx + stdIdx] = ISlashingRegistryCoordinator.OperatorKickParam({ + quorumNumber: 0, + operator: address(0) + }); stdIdx++; } else { revert("User.registerOperatorWithChurn: malformed input"); diff --git a/test/mocks/RegistryCoordinatorMock.sol b/test/mocks/RegistryCoordinatorMock.sol index d0464db2..22574cda 100644 --- a/test/mocks/RegistryCoordinatorMock.sol +++ b/test/mocks/RegistryCoordinatorMock.sol @@ -2,8 +2,10 @@ pragma solidity ^0.8.27; import "../../src/interfaces/IRegistryCoordinator.sol"; +import "../../src/interfaces/ISlashingRegistryCoordinator.sol"; -contract RegistryCoordinatorMock is IRegistryCoordinator { + +contract RegistryCoordinatorMock is ISlashingRegistryCoordinator, IRegistryCoordinator { function blsApkRegistry() external view returns (IBLSApkRegistry) {} function ejectOperator(address operator, bytes calldata quorumNumbers) external {} @@ -37,9 +39,7 @@ contract RegistryCoordinatorMock is IRegistryCoordinator { ) external view returns (address) {} /// @notice Returns the status for the given `operator` - function getOperatorStatus( - address operator - ) external view returns (IRegistryCoordinator.OperatorStatus) {} + function getOperatorStatus(address operator) external view returns (OperatorStatus){} /// @notice Returns task number from when `operator` has been registered. function getFromTaskNumberForOperator( @@ -107,4 +107,26 @@ contract RegistryCoordinatorMock is IRegistryCoordinator { function isOperatorSetAVS() external view returns (bool) { return false; } + + function accountIdentifier() external view returns (address) {} + + function deregisterOperator(bytes memory quorumNumbers) external {} + + function enableOperatorSets() external {} + + function registerOperator( + bytes memory quorumNumbers, + string memory socket, + IBLSApkRegistry.PubkeyRegistrationParams memory params, + ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature + ) external {} + + function registerOperatorWithChurn( + bytes calldata quorumNumbers, + string memory socket, + IBLSApkRegistry.PubkeyRegistrationParams memory params, + ISlashingRegistryCoordinator.OperatorKickParam[] memory operatorKickParams, + ISignatureUtils.SignatureWithSaltAndExpiry memory churnApproverSignature, + ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature + ) external {} } diff --git a/test/mocks/ServiceManagerMock.sol b/test/mocks/ServiceManagerMock.sol index 68fbdf4c..b33df503 100644 --- a/test/mocks/ServiceManagerMock.sol +++ b/test/mocks/ServiceManagerMock.sol @@ -7,7 +7,7 @@ contract ServiceManagerMock is ServiceManagerBase { constructor( IAVSDirectory _avsDirectory, IRewardsCoordinator _rewardsCoordinator, - IRegistryCoordinator _registryCoordinator, + ISlashingRegistryCoordinator _slashingRegistryCoordinator, IStakeRegistry _stakeRegistry, IPermissionController _permissionController, IAllocationManager _allocationManager @@ -15,7 +15,7 @@ contract ServiceManagerMock is ServiceManagerBase { ServiceManagerBase( _avsDirectory, _rewardsCoordinator, - _registryCoordinator, + _slashingRegistryCoordinator, _stakeRegistry, _permissionController, _allocationManager diff --git a/test/unit/EjectionManagerUnit.t.sol b/test/unit/EjectionManagerUnit.t.sol index 2cfa859e..886a94f5 100644 --- a/test/unit/EjectionManagerUnit.t.sol +++ b/test/unit/EjectionManagerUnit.t.sol @@ -80,10 +80,7 @@ contract EjectionManagerUnitTests is MockAVSDeployer { } } - assertEq( - uint8(registryCoordinator.getOperatorStatus(defaultOperator)), - uint8(IRegistryCoordinator.OperatorStatus.REGISTERED) - ); + assertEq(uint8(registryCoordinator.getOperatorStatus(defaultOperator)), uint8(ISlashingRegistryCoordinator.OperatorStatus.REGISTERED)); for (uint8 i = 0; i < numQuorums; i++) { for (uint8 j = 0; j < operatorsToEject; j++) { @@ -95,10 +92,7 @@ contract EjectionManagerUnitTests is MockAVSDeployer { cheats.prank(ejector); ejectionManager.ejectOperators(operatorIds); - assertEq( - uint8(registryCoordinator.getOperatorStatus(defaultOperator)), - uint8(IRegistryCoordinator.OperatorStatus.DEREGISTERED) - ); + assertEq(uint8(registryCoordinator.getOperatorStatus(defaultOperator)), uint8(ISlashingRegistryCoordinator.OperatorStatus.DEREGISTERED)); } function testEjectOperators_MultipleOperatorInsideRatelimit() public { @@ -116,11 +110,8 @@ contract EjectionManagerUnitTests is MockAVSDeployer { } } - for (uint8 i = 0; i < operatorsToEject; i++) { - assertEq( - uint8(registryCoordinator.getOperatorStatus(_incrementAddress(defaultOperator, i))), - uint8(IRegistryCoordinator.OperatorStatus.REGISTERED) - ); + for(uint8 i = 0; i < operatorsToEject; i++) { + assertEq(uint8(registryCoordinator.getOperatorStatus(_incrementAddress(defaultOperator, i))), uint8(ISlashingRegistryCoordinator.OperatorStatus.REGISTERED)); } for (uint8 i = 0; i < numQuorums; i++) { @@ -133,11 +124,8 @@ contract EjectionManagerUnitTests is MockAVSDeployer { cheats.prank(ejector); ejectionManager.ejectOperators(operatorIds); - for (uint8 i = 0; i < operatorsToEject; i++) { - assertEq( - uint8(registryCoordinator.getOperatorStatus(_incrementAddress(defaultOperator, i))), - uint8(IRegistryCoordinator.OperatorStatus.DEREGISTERED) - ); + for(uint8 i = 0; i < operatorsToEject; i++) { + assertEq(uint8(registryCoordinator.getOperatorStatus(_incrementAddress(defaultOperator, i))), uint8(ISlashingRegistryCoordinator.OperatorStatus.DEREGISTERED)); } } @@ -157,11 +145,8 @@ contract EjectionManagerUnitTests is MockAVSDeployer { } } - for (uint8 i = 0; i < operatorsToEject; i++) { - assertEq( - uint8(registryCoordinator.getOperatorStatus(_incrementAddress(defaultOperator, i))), - uint8(IRegistryCoordinator.OperatorStatus.REGISTERED) - ); + for(uint8 i = 0; i < operatorsToEject; i++) { + assertEq(uint8(registryCoordinator.getOperatorStatus(_incrementAddress(defaultOperator, i))), uint8(ISlashingRegistryCoordinator.OperatorStatus.REGISTERED)); } for (uint8 i = 0; i < numQuorums; i++) { @@ -174,18 +159,12 @@ contract EjectionManagerUnitTests is MockAVSDeployer { 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 = 0; i < operatorsCanEject; i++) { + assertEq(uint8(registryCoordinator.getOperatorStatus(_incrementAddress(defaultOperator, i))), uint8(ISlashingRegistryCoordinator.OperatorStatus.DEREGISTERED)); } - for (uint8 i = operatorsCanEject; i < operatorsToEject; i++) { - assertEq( - uint8(registryCoordinator.getOperatorStatus(_incrementAddress(defaultOperator, i))), - uint8(IRegistryCoordinator.OperatorStatus.REGISTERED) - ); + for(uint8 i = operatorsCanEject; i < operatorsToEject; i++) { + assertEq(uint8(registryCoordinator.getOperatorStatus(_incrementAddress(defaultOperator, i))), uint8(ISlashingRegistryCoordinator.OperatorStatus.REGISTERED)); } } @@ -204,11 +183,8 @@ contract EjectionManagerUnitTests is MockAVSDeployer { } } - for (uint8 i = 0; i < operatorsToEject; i++) { - assertEq( - uint8(registryCoordinator.getOperatorStatus(_incrementAddress(defaultOperator, i))), - uint8(IRegistryCoordinator.OperatorStatus.REGISTERED) - ); + for(uint8 i = 0; i < operatorsToEject; i++) { + assertEq(uint8(registryCoordinator.getOperatorStatus(_incrementAddress(defaultOperator, i))), uint8(ISlashingRegistryCoordinator.OperatorStatus.REGISTERED)); } for (uint8 i = 0; i < numQuorums; i++) { @@ -221,11 +197,8 @@ contract EjectionManagerUnitTests is MockAVSDeployer { cheats.prank(ejector); ejectionManager.ejectOperators(operatorIds); - for (uint8 i = 0; i < operatorsToEject; i++) { - assertEq( - uint8(registryCoordinator.getOperatorStatus(_incrementAddress(defaultOperator, i))), - uint8(IRegistryCoordinator.OperatorStatus.DEREGISTERED) - ); + for(uint8 i = 0; i < operatorsToEject; i++) { + assertEq(uint8(registryCoordinator.getOperatorStatus(_incrementAddress(defaultOperator, i))), uint8(ISlashingRegistryCoordinator.OperatorStatus.DEREGISTERED)); } cheats.warp(block.timestamp + (ratelimitWindow / 2)); @@ -240,15 +213,8 @@ contract EjectionManagerUnitTests is MockAVSDeployer { } } - for (uint8 i = 0; i < operatorsToEject; i++) { - assertEq( - uint8( - registryCoordinator.getOperatorStatus( - _incrementAddress(defaultOperator, operatorsToEject + i) - ) - ), - uint8(IRegistryCoordinator.OperatorStatus.REGISTERED) - ); + for(uint8 i = 0; i < operatorsToEject; i++) { + assertEq(uint8(registryCoordinator.getOperatorStatus(_incrementAddress(defaultOperator, operatorsToEject + i))), uint8(ISlashingRegistryCoordinator.OperatorStatus.REGISTERED)); } for (uint8 i = 0; i < numQuorums; i++) { @@ -261,15 +227,8 @@ contract EjectionManagerUnitTests is MockAVSDeployer { 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) - ); + for(uint8 i = 0; i < operatorsToEject; i++) { + assertEq(uint8(registryCoordinator.getOperatorStatus(_incrementAddress(defaultOperator, operatorsToEject + i))), uint8(ISlashingRegistryCoordinator.OperatorStatus.DEREGISTERED)); } } @@ -295,11 +254,8 @@ contract EjectionManagerUnitTests is MockAVSDeployer { } } - for (uint8 i = 0; i < operatorsToEject; i++) { - assertEq( - uint8(registryCoordinator.getOperatorStatus(_incrementAddress(defaultOperator, i))), - uint8(IRegistryCoordinator.OperatorStatus.REGISTERED) - ); + for(uint8 i = 0; i < operatorsToEject; i++) { + assertEq(uint8(registryCoordinator.getOperatorStatus(_incrementAddress(defaultOperator, i))), uint8(ISlashingRegistryCoordinator.OperatorStatus.REGISTERED)); } for (uint8 i = 0; i < numQuorums; i++) { @@ -312,11 +268,8 @@ contract EjectionManagerUnitTests is MockAVSDeployer { cheats.prank(ejector); ejectionManager.ejectOperators(operatorIds); - for (uint8 i = 0; i < operatorsToEject; i++) { - assertEq( - uint8(registryCoordinator.getOperatorStatus(_incrementAddress(defaultOperator, i))), - uint8(IRegistryCoordinator.OperatorStatus.DEREGISTERED) - ); + for(uint8 i = 0; i < operatorsToEject; i++) { + assertEq(uint8(registryCoordinator.getOperatorStatus(_incrementAddress(defaultOperator, i))), uint8(ISlashingRegistryCoordinator.OperatorStatus.DEREGISTERED)); } } @@ -335,11 +288,8 @@ contract EjectionManagerUnitTests is MockAVSDeployer { } } - for (uint8 i = 0; i < operatorsToEject; i++) { - assertEq( - uint8(registryCoordinator.getOperatorStatus(_incrementAddress(defaultOperator, i))), - uint8(IRegistryCoordinator.OperatorStatus.REGISTERED) - ); + for(uint8 i = 0; i < operatorsToEject; i++) { + assertEq(uint8(registryCoordinator.getOperatorStatus(_incrementAddress(defaultOperator, i))), uint8(ISlashingRegistryCoordinator.OperatorStatus.REGISTERED)); } for (uint8 i = 0; i < numQuorums; i++) { @@ -352,11 +302,8 @@ contract EjectionManagerUnitTests is MockAVSDeployer { cheats.prank(registryCoordinatorOwner); ejectionManager.ejectOperators(operatorIds); - for (uint8 i = 0; i < operatorsToEject; i++) { - assertEq( - uint8(registryCoordinator.getOperatorStatus(_incrementAddress(defaultOperator, i))), - uint8(IRegistryCoordinator.OperatorStatus.DEREGISTERED) - ); + for(uint8 i = 0; i < operatorsToEject; i++) { + assertEq(uint8(registryCoordinator.getOperatorStatus(_incrementAddress(defaultOperator, i))), uint8(ISlashingRegistryCoordinator.OperatorStatus.DEREGISTERED)); } } @@ -378,11 +325,8 @@ contract EjectionManagerUnitTests is MockAVSDeployer { 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 = 1; i < operatorsToEject; i++) { + assertEq(uint8(registryCoordinator.getOperatorStatus(_incrementAddress(defaultOperator, i))), uint8(ISlashingRegistryCoordinator.OperatorStatus.REGISTERED)); } for (uint8 i = 0; i < numQuorums; i++) { @@ -395,11 +339,8 @@ contract EjectionManagerUnitTests is MockAVSDeployer { cheats.prank(ejector); ejectionManager.ejectOperators(operatorIds); - for (uint8 i = 0; i < operatorsToEject; i++) { - assertEq( - uint8(registryCoordinator.getOperatorStatus(_incrementAddress(defaultOperator, i))), - uint8(IRegistryCoordinator.OperatorStatus.DEREGISTERED) - ); + for(uint8 i = 0; i < operatorsToEject; i++) { + assertEq(uint8(registryCoordinator.getOperatorStatus(_incrementAddress(defaultOperator, i))), uint8(ISlashingRegistryCoordinator.OperatorStatus.DEREGISTERED)); } } diff --git a/test/unit/OperatorStateRetrieverUnit.t.sol b/test/unit/OperatorStateRetrieverUnit.t.sol index e16621fc..902ddc5c 100644 --- a/test/unit/OperatorStateRetrieverUnit.t.sol +++ b/test/unit/OperatorStateRetrieverUnit.t.sol @@ -81,7 +81,7 @@ contract OperatorStateRetrieverUnitTests is MockAVSDeployer { function test_getOperatorState_revert_quorumNotCreatedAtReferenceBlockNumber() public { cheats.roll(registrationBlockNumber); - IRegistryCoordinator.OperatorSetParam memory operatorSetParams = IRegistryCoordinator + ISlashingRegistryCoordinator.OperatorSetParam memory operatorSetParams = ISlashingRegistryCoordinator .OperatorSetParam({ maxOperatorCount: defaultMaxOperatorCount, kickBIPsOfOperatorStake: defaultKickBIPsOfOperatorStake, @@ -223,7 +223,7 @@ contract OperatorStateRetrieverUnitTests is MockAVSDeployer { bytes32[] memory nonSignerOperatorIds = new bytes32[](1); nonSignerOperatorIds[0] = defaultOperatorId; - IRegistryCoordinator.OperatorSetParam memory operatorSetParams = IRegistryCoordinator + ISlashingRegistryCoordinator.OperatorSetParam memory operatorSetParams = ISlashingRegistryCoordinator .OperatorSetParam({ maxOperatorCount: defaultMaxOperatorCount, kickBIPsOfOperatorStake: defaultKickBIPsOfOperatorStake, @@ -254,6 +254,11 @@ contract OperatorStateRetrieverUnitTests is MockAVSDeployer { uint256 quorumBitmapTwo = 2; uint256 quorumBitmapThree = 3; + assertFalse( + registryCoordinator.isOperatorSetAVS(), + "isOperatorSetAVS should be false" + ); + cheats.roll(registrationBlockNumber); _registerOperatorWithCoordinator(defaultOperator, quorumBitmapOne, defaultPubKey); diff --git a/test/unit/RegistryCoordinatorUnit.t.sol b/test/unit/RegistryCoordinatorUnit.t.sol index 4afc62bf..e29e210b 100644 --- a/test/unit/RegistryCoordinatorUnit.t.sol +++ b/test/unit/RegistryCoordinatorUnit.t.sol @@ -2,13 +2,14 @@ pragma solidity ^0.8.27; import "../utils/MockAVSDeployer.sol"; -import {IRegistryCoordinatorErrors} from "../../src/interfaces/IRegistryCoordinator.sol"; +import {ISlashingRegistryCoordinator, IRegistryCoordinatorErrors} from "../../src/interfaces/ISlashingRegistryCoordinator.sol"; import {QuorumBitmapHistoryLib} from "../../src/libraries/QuorumBitmapHistoryLib.sol"; import {BitmapUtils} from "../../src/libraries/BitmapUtils.sol"; import {console} from "forge-std/console.sol"; contract RegistryCoordinatorUnitTests is MockAVSDeployer { using BN254 for BN254.G1Point; + using stdStorage for StdStorage; uint8 internal constant PAUSED_REGISTER_OPERATOR = 0; uint8 internal constant PAUSED_DEREGISTER_OPERATOR = 1; @@ -34,9 +35,7 @@ contract RegistryCoordinatorUnitTests is MockAVSDeployer { // emitted when an operator's index in the orderd operator list for the quorum with number `quorumNumber` is updated event QuorumIndexUpdate(bytes32 indexed operatorId, uint8 quorumNumber, uint32 newIndex); - event OperatorSetParamsUpdated( - uint8 indexed quorumNumber, IRegistryCoordinator.OperatorSetParam operatorSetParams - ); + event OperatorSetParamsUpdated(uint8 indexed quorumNumber, ISlashingRegistryCoordinator.OperatorSetParam operatorSetParams); event ChurnApproverUpdated(address prevChurnApprover, address newChurnApprover); @@ -52,14 +51,11 @@ contract RegistryCoordinatorUnitTests is MockAVSDeployer { uint256 pseudoRandomNumber, bytes memory quorumNumbers, uint96 operatorToKickStake - ) - internal - returns ( - address operatorToRegister, - BN254.G1Point memory operatorToRegisterPubKey, - IRegistryCoordinator.OperatorKickParam[] memory operatorKickParams - ) - { + ) internal returns( + address operatorToRegister, + BN254.G1Point memory operatorToRegisterPubKey, + ISlashingRegistryCoordinator.OperatorKickParam[] memory operatorKickParams + ) { uint32 kickRegistrationBlockNumber = 100; uint256 quorumBitmap = BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers); @@ -82,7 +78,7 @@ contract RegistryCoordinatorUnitTests is MockAVSDeployer { address operatorToKick; // register last operator before kick - operatorKickParams = new IRegistryCoordinator.OperatorKickParam[](1); + operatorKickParams = new ISlashingRegistryCoordinator.OperatorKickParam[](1); { BN254.G1Point memory pubKey = BN254.hashToG1( keccak256(abi.encodePacked(pseudoRandomNumber, defaultMaxOperatorCount - 1)) @@ -99,7 +95,7 @@ contract RegistryCoordinatorUnitTests is MockAVSDeployer { // operatorIdsToSwap[0] = operatorToRegisterId operatorIdsToSwap[0] = operatorToRegisterId; - operatorKickParams[0] = IRegistryCoordinator.OperatorKickParam({ + operatorKickParams[0] = ISlashingRegistryCoordinator.OperatorKickParam({ quorumNumber: uint8(quorumNumbers[0]), operator: operatorToKick }); @@ -129,12 +125,8 @@ contract RegistryCoordinatorUnitTests_Initialization_Setters is RegistryCoordina registryCoordinatorOwner, churnApprover, ejector, - 0, /*initialPausedStatus*/ - operatorSetParams, - new uint96[](0), - new IStakeRegistry.StrategyParams[][](0), - new StakeType[](0), - new uint32[](0) + 0/*initialPausedStatus*/, + address(serviceManager) // _accountIdentifier ); } @@ -208,7 +200,7 @@ contract RegistryCoordinatorUnitTests_Initialization_Setters is RegistryCoordina } function test_createQuorum_revert_notOwner() public { - IRegistryCoordinator.OperatorSetParam memory operatorSetParams; + ISlashingRegistryCoordinator.OperatorSetParam memory operatorSetParams; uint96 minimumStake; IStakeRegistry.StrategyParams[] memory strategyParams; @@ -224,12 +216,12 @@ contract RegistryCoordinatorUnitTests_Initialization_Setters is RegistryCoordina // this is necessary since the default setup already configures the max number of quorums, preventing adding more _deployMockEigenLayerAndAVS(0); - IRegistryCoordinator.OperatorSetParam memory operatorSetParams = IRegistryCoordinator - .OperatorSetParam({ - maxOperatorCount: defaultMaxOperatorCount, - kickBIPsOfOperatorStake: defaultKickBIPsOfOperatorStake, - kickBIPsOfTotalStake: defaultKickBIPsOfTotalStake - }); + ISlashingRegistryCoordinator.OperatorSetParam memory operatorSetParams = + ISlashingRegistryCoordinator.OperatorSetParam({ + maxOperatorCount: defaultMaxOperatorCount, + kickBIPsOfOperatorStake: defaultKickBIPsOfOperatorStake, + kickBIPsOfTotalStake: defaultKickBIPsOfTotalStake + }); uint96 minimumStake = 1; IStakeRegistry.StrategyParams[] memory strategyParams = new IStakeRegistry.StrategyParams[](1); @@ -340,29 +332,19 @@ contract RegistryCoordinatorUnitTests_RegisterOperator is RegistryCoordinatorUni assertEq(registryCoordinator.getOperatorId(defaultOperator), defaultOperatorId); assertEq( keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), - keccak256( - abi.encode( - IRegistryCoordinator.OperatorInfo({ - operatorId: defaultOperatorId, - status: IRegistryCoordinator.OperatorStatus.REGISTERED - }) - ) - ) + keccak256(abi.encode(ISlashingRegistryCoordinator.OperatorInfo({ + operatorId: defaultOperatorId, + status: ISlashingRegistryCoordinator.OperatorStatus.REGISTERED + }))) ); assertEq(registryCoordinator.getCurrentQuorumBitmap(defaultOperatorId), quorumBitmap); assertEq( - keccak256( - abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0)) - ), - keccak256( - abi.encode( - IRegistryCoordinator.QuorumBitmapUpdate({ - quorumBitmap: uint192(quorumBitmap), - updateBlockNumber: uint32(block.number), - nextUpdateBlockNumber: 0 - }) - ) - ) + keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0))), + keccak256(abi.encode(ISlashingRegistryCoordinator.QuorumBitmapUpdate({ + quorumBitmap: uint192(quorumBitmap), + updateBlockNumber: uint32(block.number), + nextUpdateBlockNumber: 0 + }))) ); } @@ -411,29 +393,19 @@ contract RegistryCoordinatorUnitTests_RegisterOperator is RegistryCoordinatorUni assertEq(registryCoordinator.getOperatorId(defaultOperator), defaultOperatorId); assertEq( keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), - keccak256( - abi.encode( - IRegistryCoordinator.OperatorInfo({ - operatorId: defaultOperatorId, - status: IRegistryCoordinator.OperatorStatus.REGISTERED - }) - ) - ) + keccak256(abi.encode(ISlashingRegistryCoordinator.OperatorInfo({ + operatorId: defaultOperatorId, + status: ISlashingRegistryCoordinator.OperatorStatus.REGISTERED + }))) ); assertEq(registryCoordinator.getCurrentQuorumBitmap(defaultOperatorId), quorumBitmap); assertEq( - keccak256( - abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0)) - ), - keccak256( - abi.encode( - IRegistryCoordinator.QuorumBitmapUpdate({ - quorumBitmap: uint192(quorumBitmap), - updateBlockNumber: uint32(block.number), - nextUpdateBlockNumber: 0 - }) - ) - ) + keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0))), + keccak256(abi.encode(ISlashingRegistryCoordinator.QuorumBitmapUpdate({ + quorumBitmap: uint192(quorumBitmap), + updateBlockNumber: uint32(block.number), + nextUpdateBlockNumber: 0 + }))) ); } @@ -478,43 +450,27 @@ contract RegistryCoordinatorUnitTests_RegisterOperator is RegistryCoordinatorUni assertEq(registryCoordinator.getOperatorId(defaultOperator), defaultOperatorId); assertEq( keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), - keccak256( - abi.encode( - IRegistryCoordinator.OperatorInfo({ - operatorId: defaultOperatorId, - status: IRegistryCoordinator.OperatorStatus.REGISTERED - }) - ) - ) + keccak256(abi.encode(ISlashingRegistryCoordinator.OperatorInfo({ + operatorId: defaultOperatorId, + status: ISlashingRegistryCoordinator.OperatorStatus.REGISTERED + }))) ); assertEq(registryCoordinator.getCurrentQuorumBitmap(defaultOperatorId), quorumBitmap); assertEq( - keccak256( - abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0)) - ), - keccak256( - abi.encode( - IRegistryCoordinator.QuorumBitmapUpdate({ - quorumBitmap: uint192(BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers)), - updateBlockNumber: uint32(registrationBlockNumber), - nextUpdateBlockNumber: uint32(nextRegistrationBlockNumber) - }) - ) - ) + keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0))), + keccak256(abi.encode(ISlashingRegistryCoordinator.QuorumBitmapUpdate({ + quorumBitmap: uint192(BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers)), + updateBlockNumber: uint32(registrationBlockNumber), + nextUpdateBlockNumber: uint32(nextRegistrationBlockNumber) + }))) ); assertEq( - keccak256( - abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 1)) - ), - keccak256( - abi.encode( - IRegistryCoordinator.QuorumBitmapUpdate({ - quorumBitmap: uint192(quorumBitmap), - updateBlockNumber: uint32(nextRegistrationBlockNumber), - nextUpdateBlockNumber: 0 - }) - ) - ) + keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 1))), + keccak256(abi.encode(ISlashingRegistryCoordinator.QuorumBitmapUpdate({ + quorumBitmap: uint192(quorumBitmap), + updateBlockNumber: uint32(nextRegistrationBlockNumber), + nextUpdateBlockNumber: 0 + }))) ); } @@ -643,29 +599,19 @@ contract RegistryCoordinatorUnitTests_RegisterOperator is RegistryCoordinatorUni assertEq(registryCoordinator.getOperatorId(defaultOperator), defaultOperatorId); assertEq( keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), - keccak256( - abi.encode( - IRegistryCoordinator.OperatorInfo({ - operatorId: defaultOperatorId, - status: IRegistryCoordinator.OperatorStatus.REGISTERED - }) - ) - ) + keccak256(abi.encode(ISlashingRegistryCoordinator.OperatorInfo({ + operatorId: defaultOperatorId, + status: ISlashingRegistryCoordinator.OperatorStatus.REGISTERED + }))) ); assertEq(registryCoordinator.getCurrentQuorumBitmap(defaultOperatorId), quorumBitmap); assertEq( - keccak256( - abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0)) - ), - keccak256( - abi.encode( - IRegistryCoordinator.QuorumBitmapUpdate({ - quorumBitmap: uint192(quorumBitmap), - updateBlockNumber: uint32(block.number), - nextUpdateBlockNumber: 0 - }) - ) - ) + keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0))), + keccak256(abi.encode(ISlashingRegistryCoordinator.QuorumBitmapUpdate({ + quorumBitmap: uint192(quorumBitmap), + updateBlockNumber: uint32(block.number), + nextUpdateBlockNumber: 0 + }))) ); } } @@ -691,6 +637,10 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is } function test_deregisterOperator_revert_notRegistered() public { + // enable operatorSets + cheats.prank(registryCoordinator.owner()); + registryCoordinator.enableOperatorSets(); + bytes memory quorumNumbers = new bytes(1); quorumNumbers[0] = bytes1(defaultQuorumNumber); @@ -700,17 +650,24 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is } function test_deregisterOperator_revert_incorrectQuorums() public { + // enable operatorSets + cheats.prank(registryCoordinator.owner()); + registryCoordinator.enableOperatorSets(); + + assertTrue( + registryCoordinator.isM2Quorum(uint8(defaultQuorumNumber)), + "defaultQuorumNumber should be a M2 quorum" + ); + bytes memory quorumNumbers = new bytes(1); quorumNumbers[0] = bytes1(defaultQuorumNumber); uint256 quorumBitmap = BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers); - _registerOperatorWithCoordinator(defaultOperator, quorumBitmap, defaultPubKey); - quorumNumbers = new bytes(2); - quorumNumbers[0] = bytes1(defaultQuorumNumber + 1); - quorumNumbers[1] = bytes1(defaultQuorumNumber + 2); + quorumNumbers = new bytes(1); + quorumNumbers[0] = bytes1(defaultQuorumNumber); - cheats.expectRevert(bytes4(keccak256("NotRegisteredForQuorum()"))); + cheats.expectRevert(bytes4(keccak256("NotRegistered()"))); cheats.prank(defaultOperator); registryCoordinator.deregisterOperator(quorumNumbers); } @@ -750,29 +707,19 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is assertEq( keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), - keccak256( - abi.encode( - IRegistryCoordinator.OperatorInfo({ - operatorId: defaultOperatorId, - status: IRegistryCoordinator.OperatorStatus.DEREGISTERED - }) - ) - ) + keccak256(abi.encode(ISlashingRegistryCoordinator.OperatorInfo({ + operatorId: defaultOperatorId, + status: ISlashingRegistryCoordinator.OperatorStatus.DEREGISTERED + }))) ); assertEq(registryCoordinator.getCurrentQuorumBitmap(defaultOperatorId), 0); assertEq( - keccak256( - abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0)) - ), - keccak256( - abi.encode( - IRegistryCoordinator.QuorumBitmapUpdate({ - quorumBitmap: uint192(quorumBitmap), - updateBlockNumber: registrationBlockNumber, - nextUpdateBlockNumber: deregistrationBlockNumber - }) - ) - ) + keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0))), + keccak256(abi.encode(ISlashingRegistryCoordinator.QuorumBitmapUpdate({ + quorumBitmap: uint192(quorumBitmap), + updateBlockNumber: registrationBlockNumber, + nextUpdateBlockNumber: deregistrationBlockNumber + }))) ); } @@ -819,29 +766,19 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is assertEq( keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), - keccak256( - abi.encode( - IRegistryCoordinator.OperatorInfo({ - operatorId: defaultOperatorId, - status: IRegistryCoordinator.OperatorStatus.DEREGISTERED - }) - ) - ) + keccak256(abi.encode(ISlashingRegistryCoordinator.OperatorInfo({ + operatorId: defaultOperatorId, + status: ISlashingRegistryCoordinator.OperatorStatus.DEREGISTERED + }))) ); assertEq(registryCoordinator.getCurrentQuorumBitmap(defaultOperatorId), 0); assertEq( - keccak256( - abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0)) - ), - keccak256( - abi.encode( - IRegistryCoordinator.QuorumBitmapUpdate({ - quorumBitmap: uint192(quorumBitmap), - updateBlockNumber: registrationBlockNumber, - nextUpdateBlockNumber: deregistrationBlockNumber - }) - ) - ) + keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0))), + keccak256(abi.encode(ISlashingRegistryCoordinator.QuorumBitmapUpdate({ + quorumBitmap: uint192(quorumBitmap), + updateBlockNumber: registrationBlockNumber, + nextUpdateBlockNumber: deregistrationBlockNumber + }))) ); } // @notice verifies that an operator who was registered for a fuzzed set of quorums can be deregistered from a subset of those quorums @@ -900,26 +837,18 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is if (deregistrationQuorumBitmap == registrationQuorumBitmap) { assertEq( keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), - keccak256( - abi.encode( - IRegistryCoordinator.OperatorInfo({ - operatorId: defaultOperatorId, - status: IRegistryCoordinator.OperatorStatus.DEREGISTERED - }) - ) - ) + keccak256(abi.encode(ISlashingRegistryCoordinator.OperatorInfo({ + operatorId: defaultOperatorId, + status: ISlashingRegistryCoordinator.OperatorStatus.DEREGISTERED + }))) ); } else { assertEq( keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), - keccak256( - abi.encode( - IRegistryCoordinator.OperatorInfo({ - operatorId: defaultOperatorId, - status: IRegistryCoordinator.OperatorStatus.REGISTERED - }) - ) - ) + keccak256(abi.encode(ISlashingRegistryCoordinator.OperatorInfo({ + operatorId: defaultOperatorId, + status: ISlashingRegistryCoordinator.OperatorStatus.REGISTERED + }))) ); } // ensure that the operator's current quorum bitmap matches the expectation @@ -930,36 +859,22 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is ); // check that the quorum bitmap history is as expected assertEq( - keccak256( - abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0)) - ), - keccak256( - abi.encode( - IRegistryCoordinator.QuorumBitmapUpdate({ - quorumBitmap: uint192(registrationQuorumBitmap), - updateBlockNumber: registrationBlockNumber, - nextUpdateBlockNumber: deregistrationBlockNumber - }) - ) - ) + keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0))), + keccak256(abi.encode(ISlashingRegistryCoordinator.QuorumBitmapUpdate({ + quorumBitmap: uint192(registrationQuorumBitmap), + updateBlockNumber: registrationBlockNumber, + nextUpdateBlockNumber: deregistrationBlockNumber + }))) ); // note: there will be no second entry in the operator's bitmap history in the event that the operator has totally deregistered if (deregistrationQuorumBitmap != registrationQuorumBitmap) { assertEq( - keccak256( - abi.encode( - registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 1) - ) - ), - keccak256( - abi.encode( - IRegistryCoordinator.QuorumBitmapUpdate({ - quorumBitmap: uint192(expectedQuorumBitmap), - updateBlockNumber: deregistrationBlockNumber, - nextUpdateBlockNumber: 0 - }) - ) - ) + keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 1))), + keccak256(abi.encode(ISlashingRegistryCoordinator.QuorumBitmapUpdate({ + quorumBitmap: uint192(expectedQuorumBitmap), + updateBlockNumber: deregistrationBlockNumber, + nextUpdateBlockNumber: 0 + }))) ); } } @@ -1036,31 +951,19 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is assertEq( keccak256(abi.encode(registryCoordinator.getOperator(operatorToDeregister))), - keccak256( - abi.encode( - IRegistryCoordinator.OperatorInfo({ - operatorId: operatorToDeregisterId, - status: IRegistryCoordinator.OperatorStatus.DEREGISTERED - }) - ) - ) + keccak256(abi.encode(ISlashingRegistryCoordinator.OperatorInfo({ + operatorId: operatorToDeregisterId, + status: ISlashingRegistryCoordinator.OperatorStatus.DEREGISTERED + }))) ); assertEq(registryCoordinator.getCurrentQuorumBitmap(defaultOperatorId), 0); assertEq( - keccak256( - abi.encode( - registryCoordinator.getQuorumBitmapUpdateByIndex(operatorToDeregisterId, 0) - ) - ), - keccak256( - abi.encode( - IRegistryCoordinator.QuorumBitmapUpdate({ - quorumBitmap: uint192(operatorToDeregisterQuorumBitmap), - updateBlockNumber: registrationBlockNumber, - nextUpdateBlockNumber: deregistrationBlockNumber - }) - ) - ) + keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(operatorToDeregisterId, 0))), + keccak256(abi.encode(ISlashingRegistryCoordinator.QuorumBitmapUpdate({ + quorumBitmap: uint192(operatorToDeregisterQuorumBitmap), + updateBlockNumber: registrationBlockNumber, + nextUpdateBlockNumber: deregistrationBlockNumber + }))) ); } @@ -1079,7 +982,7 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is cheats.roll(reregistrationBlockNumber); // store data before registering, to check against later - IRegistryCoordinator.QuorumBitmapUpdate memory previousQuorumBitmapUpdate = + ISlashingRegistryCoordinator.QuorumBitmapUpdate memory previousQuorumBitmapUpdate = registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0); // re-register the operator @@ -1091,14 +994,10 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is assertEq(registryCoordinator.getOperatorId(defaultOperator), defaultOperatorId, "1"); assertEq( keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), - keccak256( - abi.encode( - IRegistryCoordinator.OperatorInfo({ - operatorId: defaultOperatorId, - status: IRegistryCoordinator.OperatorStatus.REGISTERED - }) - ) - ), + keccak256(abi.encode(ISlashingRegistryCoordinator.OperatorInfo({ + operatorId: defaultOperatorId, + status: ISlashingRegistryCoordinator.OperatorStatus.REGISTERED + }))), "2" ); assertEq(registryCoordinator.getCurrentQuorumBitmap(defaultOperatorId), quorumBitmap, "3"); @@ -1113,22 +1012,12 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is // check that new entry in bitmap history is as expected uint256 historyLength = registryCoordinator.getQuorumBitmapHistoryLength(defaultOperatorId); assertEq( - keccak256( - abi.encode( - registryCoordinator.getQuorumBitmapUpdateByIndex( - defaultOperatorId, historyLength - 1 - ) - ) - ), - keccak256( - abi.encode( - IRegistryCoordinator.QuorumBitmapUpdate({ - quorumBitmap: uint192(quorumBitmap), - updateBlockNumber: uint32(reregistrationBlockNumber), - nextUpdateBlockNumber: 0 - }) - ) - ), + keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, historyLength - 1))), + keccak256(abi.encode(ISlashingRegistryCoordinator.QuorumBitmapUpdate({ + quorumBitmap: uint192(quorumBitmap), + updateBlockNumber: uint32(reregistrationBlockNumber), + nextUpdateBlockNumber: 0 + }))), "5" ); } @@ -1303,26 +1192,18 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is if (deregistrationQuorumBitmap == registrationQuorumBitmap) { assertEq( keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), - keccak256( - abi.encode( - IRegistryCoordinator.OperatorInfo({ - operatorId: defaultOperatorId, - status: IRegistryCoordinator.OperatorStatus.DEREGISTERED - }) - ) - ) + keccak256(abi.encode(ISlashingRegistryCoordinator.OperatorInfo({ + operatorId: defaultOperatorId, + status: ISlashingRegistryCoordinator.OperatorStatus.DEREGISTERED + }))) ); } else { assertEq( keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), - keccak256( - abi.encode( - IRegistryCoordinator.OperatorInfo({ - operatorId: defaultOperatorId, - status: IRegistryCoordinator.OperatorStatus.REGISTERED - }) - ) - ) + keccak256(abi.encode(ISlashingRegistryCoordinator.OperatorInfo({ + operatorId: defaultOperatorId, + status: ISlashingRegistryCoordinator.OperatorStatus.REGISTERED + }))) ); } // ensure that the operator's current quorum bitmap matches the expectation @@ -1333,36 +1214,22 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is ); // check that the quorum bitmap history is as expected assertEq( - keccak256( - abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0)) - ), - keccak256( - abi.encode( - IRegistryCoordinator.QuorumBitmapUpdate({ - quorumBitmap: uint192(registrationQuorumBitmap), - updateBlockNumber: registrationBlockNumber, - nextUpdateBlockNumber: deregistrationBlockNumber - }) - ) - ) + keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0))), + keccak256(abi.encode(ISlashingRegistryCoordinator.QuorumBitmapUpdate({ + quorumBitmap: uint192(registrationQuorumBitmap), + updateBlockNumber: registrationBlockNumber, + nextUpdateBlockNumber: deregistrationBlockNumber + }))) ); // note: there will be no second entry in the operator's bitmap history in the event that the operator has totally deregistered if (deregistrationQuorumBitmap != registrationQuorumBitmap) { assertEq( - keccak256( - abi.encode( - registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 1) - ) - ), - keccak256( - abi.encode( - IRegistryCoordinator.QuorumBitmapUpdate({ - quorumBitmap: uint192(expectedQuorumBitmap), - updateBlockNumber: deregistrationBlockNumber, - nextUpdateBlockNumber: 0 - }) - ) - ) + keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 1))), + keccak256(abi.encode(ISlashingRegistryCoordinator.QuorumBitmapUpdate({ + quorumBitmap: uint192(expectedQuorumBitmap), + updateBlockNumber: deregistrationBlockNumber, + nextUpdateBlockNumber: 0 + }))) ); } } @@ -1393,14 +1260,10 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is // make sure the operator is deregistered assertEq( keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), - keccak256( - abi.encode( - IRegistryCoordinator.OperatorInfo({ - operatorId: defaultOperatorId, - status: IRegistryCoordinator.OperatorStatus.DEREGISTERED - }) - ) - ) + keccak256(abi.encode(ISlashingRegistryCoordinator.OperatorInfo({ + operatorId: defaultOperatorId, + status: ISlashingRegistryCoordinator.OperatorStatus.DEREGISTERED + }))) ); // make sure the operator is not in any quorums assertEq(registryCoordinator.getCurrentQuorumBitmap(defaultOperatorId), 0); @@ -1438,14 +1301,10 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is // make sure the operator is registered assertEq( keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), - keccak256( - abi.encode( - IRegistryCoordinator.OperatorInfo({ - operatorId: defaultOperatorId, - status: IRegistryCoordinator.OperatorStatus.REGISTERED - }) - ) - ) + keccak256(abi.encode(ISlashingRegistryCoordinator.OperatorInfo({ + operatorId: defaultOperatorId, + status: ISlashingRegistryCoordinator.OperatorStatus.REGISTERED + }))) ); // make sure the operator is properly removed from the quorums assertEq( @@ -1676,8 +1535,7 @@ contract RegistryCoordinatorUnitTests_RegisterOperatorWithChurn is RegistryCoord address operatorToKick; // register last operator before kick - IRegistryCoordinator.OperatorKickParam[] memory operatorKickParams = - new IRegistryCoordinator.OperatorKickParam[](1); + ISlashingRegistryCoordinator.OperatorKickParam[] memory operatorKickParams = new ISlashingRegistryCoordinator.OperatorKickParam[](1); { BN254.G1Point memory pubKey = BN254.hashToG1(keccak256(abi.encodePacked(pseudoRandomNumber, numOperators - 1))); @@ -1690,7 +1548,7 @@ contract RegistryCoordinatorUnitTests_RegisterOperatorWithChurn is RegistryCoord // operatorIdsToSwap[0] = operatorToRegisterId operatorIdsToSwap[0] = operatorToRegisterId; - operatorKickParams[0] = IRegistryCoordinator.OperatorKickParam({ + operatorKickParams[0] = ISlashingRegistryCoordinator.OperatorKickParam({ quorumNumber: defaultQuorumNumber, operator: operatorToKick }); @@ -1710,20 +1568,19 @@ contract RegistryCoordinatorUnitTests_RegisterOperatorWithChurn is RegistryCoord cheats.expectEmit(true, true, true, true, address(blsApkRegistry)); emit OperatorAddedToQuorums(operatorToRegister, operatorToRegisterId, quorumNumbers); cheats.expectEmit(true, true, true, false, address(stakeRegistry)); - emit OperatorStakeUpdate(operatorToRegisterId, defaultQuorumNumber, registeringStake - 1); + emit OperatorStakeUpdate(operatorToRegisterId, defaultQuorumNumber, registeringStake); cheats.expectEmit(true, true, true, true, address(indexRegistry)); emit QuorumIndexUpdate(operatorToRegisterId, defaultQuorumNumber, numOperators); - cheats.expectEmit(true, true, true, true, address(registryCoordinator)); - emit OperatorDeregistered(operatorKickParams[0].operator, operatorToKickId); + // Then expect the deregistration events cheats.expectEmit(true, true, true, true, address(blsApkRegistry)); - emit OperatorRemovedFromQuorums( - operatorKickParams[0].operator, operatorToKickId, quorumNumbers - ); + emit OperatorRemovedFromQuorums(operatorKickParams[0].operator, operatorToKickId, quorumNumbers); cheats.expectEmit(true, true, true, true, address(stakeRegistry)); emit OperatorStakeUpdate(operatorToKickId, defaultQuorumNumber, 0); cheats.expectEmit(true, true, true, true, address(indexRegistry)); emit QuorumIndexUpdate(operatorToRegisterId, defaultQuorumNumber, numOperators - 1); + cheats.expectEmit(true, true, true, true, address(registryCoordinator)); + emit OperatorDeregistered(operatorKickParams[0].operator, operatorToKickId); { ISignatureUtils.SignatureWithSaltAndExpiry memory emptyAVSRegSig; @@ -1751,39 +1608,25 @@ contract RegistryCoordinatorUnitTests_RegisterOperatorWithChurn is RegistryCoord assertEq( keccak256(abi.encode(registryCoordinator.getOperator(operatorToRegister))), - keccak256( - abi.encode( - IRegistryCoordinator.OperatorInfo({ - operatorId: operatorToRegisterId, - status: IRegistryCoordinator.OperatorStatus.REGISTERED - }) - ) - ) + keccak256(abi.encode(ISlashingRegistryCoordinator.OperatorInfo({ + operatorId: operatorToRegisterId, + status: ISlashingRegistryCoordinator.OperatorStatus.REGISTERED + }))) ); assertEq( keccak256(abi.encode(registryCoordinator.getOperator(operatorToKick))), - keccak256( - abi.encode( - IRegistryCoordinator.OperatorInfo({ - operatorId: operatorToKickId, - status: IRegistryCoordinator.OperatorStatus.DEREGISTERED - }) - ) - ) + keccak256(abi.encode(ISlashingRegistryCoordinator.OperatorInfo({ + operatorId: operatorToKickId, + status: ISlashingRegistryCoordinator.OperatorStatus.DEREGISTERED + }))) ); assertEq( - keccak256( - abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(operatorToKickId, 0)) - ), - keccak256( - abi.encode( - IRegistryCoordinator.QuorumBitmapUpdate({ - quorumBitmap: uint192(quorumBitmap), - updateBlockNumber: kickRegistrationBlockNumber, - nextUpdateBlockNumber: registrationBlockNumber - }) - ) - ) + keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(operatorToKickId, 0))), + keccak256(abi.encode(ISlashingRegistryCoordinator.QuorumBitmapUpdate({ + quorumBitmap: uint192(quorumBitmap), + updateBlockNumber: kickRegistrationBlockNumber, + nextUpdateBlockNumber: registrationBlockNumber + }))) ); } @@ -1797,7 +1640,7 @@ contract RegistryCoordinatorUnitTests_RegisterOperatorWithChurn is RegistryCoord ( address operatorToRegister, BN254.G1Point memory operatorToRegisterPubKey, - IRegistryCoordinator.OperatorKickParam[] memory operatorKickParams + ISlashingRegistryCoordinator.OperatorKickParam[] memory operatorKickParams ) = _test_registerOperatorWithChurn_SetUp(pseudoRandomNumber, quorumNumbers, defaultStake); bytes32 operatorToRegisterId = BN254.hashG1Point(operatorToRegisterPubKey); @@ -1835,10 +1678,8 @@ contract RegistryCoordinatorUnitTests_RegisterOperatorWithChurn is RegistryCoord ( address operatorToRegister, BN254.G1Point memory operatorToRegisterPubKey, - IRegistryCoordinator.OperatorKickParam[] memory operatorKickParams - ) = _test_registerOperatorWithChurn_SetUp( - pseudoRandomNumber, quorumNumbers, operatorToKickStake - ); + ISlashingRegistryCoordinator.OperatorKickParam[] memory operatorKickParams + ) = _test_registerOperatorWithChurn_SetUp(pseudoRandomNumber, quorumNumbers, operatorToKickStake); bytes32 operatorToRegisterId = BN254.hashG1Point(operatorToRegisterPubKey); // set the stake of the operator to register to the defaultKickBIPsOfOperatorStake multiple of the operatorToKickStake @@ -1879,7 +1720,7 @@ contract RegistryCoordinatorUnitTests_RegisterOperatorWithChurn is RegistryCoord ( address operatorToRegister, , - IRegistryCoordinator.OperatorKickParam[] memory operatorKickParams + ISlashingRegistryCoordinator.OperatorKickParam[] memory operatorKickParams ) = _test_registerOperatorWithChurn_SetUp(pseudoRandomNumber, quorumNumbers, defaultStake); uint96 registeringStake = defaultKickBIPsOfOperatorStake * defaultStake; @@ -1913,7 +1754,7 @@ contract RegistryCoordinatorUnitTests_RegisterOperatorWithChurn is RegistryCoord ( address operatorToRegister, BN254.G1Point memory operatorToRegisterPubKey, - IRegistryCoordinator.OperatorKickParam[] memory operatorKickParams + ISlashingRegistryCoordinator.OperatorKickParam[] memory operatorKickParams ) = _test_registerOperatorWithChurn_SetUp(pseudoRandomNumber, quorumNumbers, defaultStake); bytes32 operatorToRegisterId = BN254.hashG1Point(operatorToRegisterPubKey); @@ -2243,7 +2084,7 @@ contract RegistryCoordinatorUnitTests_UpdateOperators is RegistryCoordinatorUnit ), keccak256( abi.encode( - IRegistryCoordinator.QuorumBitmapUpdate({ + ISlashingRegistryCoordinator.QuorumBitmapUpdate({ quorumBitmap: uint192(newBitmap), updateBlockNumber: uint32(block.number), nextUpdateBlockNumber: 0 @@ -2262,18 +2103,12 @@ contract RegistryCoordinatorUnitTests_UpdateOperators is RegistryCoordinatorUnit registryCoordinator._updateOperatorBitmapExternal(defaultOperatorId, newBitmap); assertEq( - keccak256( - abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0)) - ), - keccak256( - abi.encode( - IRegistryCoordinator.QuorumBitmapUpdate({ - quorumBitmap: uint192(newBitmap), - updateBlockNumber: uint32(block.number), - nextUpdateBlockNumber: 0 - }) - ) - ) + keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0))), + keccak256(abi.encode(ISlashingRegistryCoordinator.QuorumBitmapUpdate({ + quorumBitmap: uint192(newBitmap), + updateBlockNumber: uint32(block.number), + nextUpdateBlockNumber: 0 + }))) ); } @@ -2290,32 +2125,20 @@ contract RegistryCoordinatorUnitTests_UpdateOperators is RegistryCoordinatorUnit registryCoordinator._updateOperatorBitmapExternal(defaultOperatorId, newBitmap); assertEq( - keccak256( - abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0)) - ), - keccak256( - abi.encode( - IRegistryCoordinator.QuorumBitmapUpdate({ - quorumBitmap: uint192(pastBitmap), - updateBlockNumber: uint32(previousBlockNumber), - nextUpdateBlockNumber: uint32(block.number) - }) - ) - ) + keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0))), + keccak256(abi.encode(ISlashingRegistryCoordinator.QuorumBitmapUpdate({ + quorumBitmap: uint192(pastBitmap), + updateBlockNumber: uint32(previousBlockNumber), + nextUpdateBlockNumber: uint32(block.number) + }))) ); assertEq( - keccak256( - abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 1)) - ), - keccak256( - abi.encode( - IRegistryCoordinator.QuorumBitmapUpdate({ - quorumBitmap: uint192(newBitmap), - updateBlockNumber: uint32(block.number), - nextUpdateBlockNumber: 0 - }) - ) - ) + keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 1))), + keccak256(abi.encode(ISlashingRegistryCoordinator.QuorumBitmapUpdate({ + quorumBitmap: uint192(newBitmap), + updateBlockNumber: uint32(block.number), + nextUpdateBlockNumber: 0 + }))) ); } } @@ -2340,8 +2163,7 @@ contract RegistryCoordinatorUnitTests_BeforeMigration is RegistryCoordinatorUnit function test_CreateTotalDelegatedStakeQuorum() public { _deployMockEigenLayerAndAVS(0); // Set up test params - IRegistryCoordinator.OperatorSetParam memory operatorSetParams = IRegistryCoordinator - .OperatorSetParam({ + ISlashingRegistryCoordinator.OperatorSetParam memory operatorSetParams = ISlashingRegistryCoordinator.OperatorSetParam({ maxOperatorCount: 10, kickBIPsOfOperatorStake: 0, kickBIPsOfTotalStake: 0 @@ -2365,8 +2187,7 @@ contract RegistryCoordinatorUnitTests_BeforeMigration is RegistryCoordinatorUnit assertEq(registryCoordinator.quorumCount(), initialQuorumCount + 1); // Verify quorum params were set correctly - IRegistryCoordinator.OperatorSetParam memory storedParams = - registryCoordinator.getOperatorSetParams(initialQuorumCount); + ISlashingRegistryCoordinator.OperatorSetParam memory storedParams = registryCoordinator.getOperatorSetParams(initialQuorumCount); assertEq(storedParams.maxOperatorCount, operatorSetParams.maxOperatorCount); assertEq(storedParams.kickBIPsOfOperatorStake, operatorSetParams.kickBIPsOfOperatorStake); assertEq(storedParams.kickBIPsOfTotalStake, operatorSetParams.kickBIPsOfTotalStake); @@ -2374,8 +2195,7 @@ contract RegistryCoordinatorUnitTests_BeforeMigration is RegistryCoordinatorUnit function test_CreateSlashableStakeQuorum_Reverts() public { _deployMockEigenLayerAndAVS(0); - IRegistryCoordinator.OperatorSetParam memory operatorSetParams = IRegistryCoordinator - .OperatorSetParam({ + ISlashingRegistryCoordinator.OperatorSetParam memory operatorSetParams = ISlashingRegistryCoordinator.OperatorSetParam({ maxOperatorCount: 10, kickBIPsOfOperatorStake: 0, kickBIPsOfTotalStake: 0 @@ -2399,15 +2219,16 @@ contract RegistryCoordinatorUnitTests_BeforeMigration is RegistryCoordinatorUnit _deployMockEigenLayerAndAVS(0); cheats.prank(registryCoordinatorOwner); registryCoordinator.enableOperatorSets(); - assertTrue(registryCoordinator.isUsingOperatorSets()); + assertTrue(registryCoordinator.isOperatorSetAVS()); } } contract RegistryCoordinatorUnitTests_AfterMigration is RegistryCoordinatorUnitTests { + function test_MigrateToOperatorSets() public { cheats.prank(registryCoordinatorOwner); registryCoordinator.enableOperatorSets(); - assertTrue(registryCoordinator.isUsingOperatorSets()); + assertTrue(registryCoordinator.isOperatorSetAVS()); } function test_M2_Deregister() public { @@ -2451,13 +2272,8 @@ contract RegistryCoordinatorUnitTests_AfterMigration is RegistryCoordinatorUnitT assertEq(bitmap, 0, "Operator bitmap should be empty after deregistration"); // Verify operator status is NEVER_REGISTERED - IRegistryCoordinator.OperatorStatus status = - registryCoordinator.getOperatorStatus(operatorToRegister); - assertEq( - uint8(status), - uint8(IRegistryCoordinator.OperatorStatus.NEVER_REGISTERED), - "Operator status should be NEVER_REGISTERED" - ); + ISlashingRegistryCoordinator.OperatorStatus status = registryCoordinator.getOperatorStatus(operatorToRegister); + assertEq(uint8(status), uint8(ISlashingRegistryCoordinator.OperatorStatus.NEVER_REGISTERED), "Operator status should be NEVER_REGISTERED"); } function test_M2_Register_Reverts() public { @@ -2484,8 +2300,7 @@ contract RegistryCoordinatorUnitTests_AfterMigration is RegistryCoordinatorUnitT registryCoordinator.enableOperatorSets(); // Create quorum params - IRegistryCoordinator.OperatorSetParam memory operatorSetParams = IRegistryCoordinator - .OperatorSetParam({ + ISlashingRegistryCoordinator.OperatorSetParam memory operatorSetParams = ISlashingRegistryCoordinator.OperatorSetParam({ maxOperatorCount: 10, kickBIPsOfOperatorStake: 1000, kickBIPsOfTotalStake: 100 @@ -2513,8 +2328,7 @@ contract RegistryCoordinatorUnitTests_AfterMigration is RegistryCoordinatorUnitT registryCoordinator.enableOperatorSets(); // Create quorum params - IRegistryCoordinator.OperatorSetParam memory operatorSetParams = IRegistryCoordinator - .OperatorSetParam({ + ISlashingRegistryCoordinator.OperatorSetParam memory operatorSetParams = ISlashingRegistryCoordinator.OperatorSetParam({ maxOperatorCount: 10, kickBIPsOfOperatorStake: 1000, kickBIPsOfTotalStake: 100 @@ -2536,13 +2350,13 @@ contract RegistryCoordinatorUnitTests_AfterMigration is RegistryCoordinatorUnitT vm.skip(true); _deployMockEigenLayerAndAVS(0); + // Enable operator sets first cheats.prank(registryCoordinatorOwner); registryCoordinator.enableOperatorSets(); // Create quorum params - IRegistryCoordinator.OperatorSetParam memory operatorSetParams = IRegistryCoordinator - .OperatorSetParam({ + ISlashingRegistryCoordinator.OperatorSetParam memory operatorSetParams = ISlashingRegistryCoordinator.OperatorSetParam({ maxOperatorCount: 10, kickBIPsOfOperatorStake: 1000, kickBIPsOfTotalStake: 100 @@ -2576,7 +2390,7 @@ contract RegistryCoordinatorUnitTests_AfterMigration is RegistryCoordinatorUnitT // Encode with RegistrationType.NORMAL bytes memory data = abi.encode( - RegistryCoordinator.RegistrationType.NORMAL, + ISlashingRegistryCoordinator.RegistrationType.NORMAL, socket, params ); @@ -2592,8 +2406,7 @@ contract RegistryCoordinatorUnitTests_AfterMigration is RegistryCoordinatorUnitT registryCoordinator.enableOperatorSets(); // Create quorum params - IRegistryCoordinator.OperatorSetParam memory operatorSetParams = IRegistryCoordinator - .OperatorSetParam({ + ISlashingRegistryCoordinator.OperatorSetParam memory operatorSetParams = ISlashingRegistryCoordinator.OperatorSetParam({ maxOperatorCount: 10, kickBIPsOfOperatorStake: 1000, kickBIPsOfTotalStake: 100 @@ -2621,16 +2434,17 @@ contract RegistryCoordinatorUnitTests_AfterMigration is RegistryCoordinatorUnitT // pubkeySignature: defaultPubKeySignature // }); - IRegistryCoordinator.OperatorKickParam[] memory operatorKickParams = - new IRegistryCoordinator.OperatorKickParam[](1); - operatorKickParams[0] = - IRegistryCoordinator.OperatorKickParam({operator: address(0x1), quorumNumber: 0}); + ISlashingRegistryCoordinator.OperatorKickParam[] memory operatorKickParams = new ISlashingRegistryCoordinator.OperatorKickParam[](1); + operatorKickParams[0] = ISlashingRegistryCoordinator.OperatorKickParam({ + operator: address(0x1), + quorumNumber: 0 + }); ISignatureUtils.SignatureWithSaltAndExpiry memory churnApproverSignature; // Encode with RegistrationType.CHURN bytes memory data = abi.encode( - RegistryCoordinator.RegistrationType.CHURN, + ISlashingRegistryCoordinator.RegistrationType.CHURN, socket, params, operatorKickParams, @@ -2646,8 +2460,7 @@ contract RegistryCoordinatorUnitTests_AfterMigration is RegistryCoordinatorUnitT vm.skip(true); _deployMockEigenLayerAndAVS(0); - IRegistryCoordinator.OperatorSetParam memory operatorSetParams = IRegistryCoordinator - .OperatorSetParam({ + ISlashingRegistryCoordinator.OperatorSetParam memory operatorSetParams = ISlashingRegistryCoordinator.OperatorSetParam({ maxOperatorCount: defaultMaxOperatorCount, kickBIPsOfOperatorStake: defaultKickBIPsOfOperatorStake, kickBIPsOfTotalStake: defaultKickBIPsOfTotalStake @@ -2676,8 +2489,7 @@ contract RegistryCoordinatorUnitTests_AfterMigration is RegistryCoordinatorUnitT registryCoordinator.enableOperatorSets(); // Create quorum params - IRegistryCoordinator.OperatorSetParam memory operatorSetParams = IRegistryCoordinator - .OperatorSetParam({ + ISlashingRegistryCoordinator.OperatorSetParam memory operatorSetParams = ISlashingRegistryCoordinator.OperatorSetParam({ maxOperatorCount: 10, kickBIPsOfOperatorStake: 1000, kickBIPsOfTotalStake: 100 @@ -2708,7 +2520,7 @@ contract RegistryCoordinatorUnitTests_AfterMigration is RegistryCoordinatorUnitT // Encode with RegistrationType.NORMAL bytes memory data = abi.encode( - RegistryCoordinator.RegistrationType.NORMAL, + ISlashingRegistryCoordinator.RegistrationType.NORMAL, socket, params ); @@ -2723,13 +2535,13 @@ contract RegistryCoordinatorUnitTests_AfterMigration is RegistryCoordinatorUnitT function test_registerHook_Reverts_WhenNotALM() public { _deployMockEigenLayerAndAVS(0); + // Enable operator sets first cheats.prank(registryCoordinatorOwner); registryCoordinator.enableOperatorSets(); // Create quorum params - IRegistryCoordinator.OperatorSetParam memory operatorSetParams = IRegistryCoordinator - .OperatorSetParam({ + ISlashingRegistryCoordinator.OperatorSetParam memory operatorSetParams = ISlashingRegistryCoordinator.OperatorSetParam({ maxOperatorCount: 10, kickBIPsOfOperatorStake: 1000, kickBIPsOfTotalStake: 100 @@ -2763,7 +2575,7 @@ contract RegistryCoordinatorUnitTests_AfterMigration is RegistryCoordinatorUnitT // Encode with RegistrationType.NORMAL bytes memory data = abi.encode( - RegistryCoordinator.RegistrationType.NORMAL, + ISlashingRegistryCoordinator.RegistrationType.NORMAL, socket, params ); @@ -2774,13 +2586,18 @@ contract RegistryCoordinatorUnitTests_AfterMigration is RegistryCoordinatorUnitT function test_deregisterHook_Reverts_WhenNotALM() public { _deployMockEigenLayerAndAVS(0); + // Enable operator sets first cheats.prank(registryCoordinatorOwner); registryCoordinator.enableOperatorSets(); + assertTrue( + registryCoordinator.isOperatorSetAVS(), + "isOperatorSetAVS should be true" + ); + // Create quorum params - IRegistryCoordinator.OperatorSetParam memory operatorSetParams = IRegistryCoordinator - .OperatorSetParam({ + ISlashingRegistryCoordinator.OperatorSetParam memory operatorSetParams = ISlashingRegistryCoordinator.OperatorSetParam({ maxOperatorCount: 10, kickBIPsOfOperatorStake: 1000, kickBIPsOfTotalStake: 100 @@ -2809,16 +2626,14 @@ contract RegistryCoordinatorUnitTests_AfterMigration is RegistryCoordinatorUnitT // pubkeySignature: defaultPubKeySignature // }); - // Encode with RegistrationType.NORMAL bytes memory data = abi.encode( - RegistryCoordinator.RegistrationType.NORMAL, + ISlashingRegistryCoordinator.RegistrationType.NORMAL, socket, params ); cheats.prank(address(registryCoordinator.allocationManager())); registryCoordinator.registerOperator(defaultOperator, operatorSetIds, data); - cheats.stopPrank(); cheats.expectRevert(); registryCoordinator.deregisterOperator(defaultOperator, operatorSetIds); diff --git a/test/unit/SlashingRegistryCoordinatorUnit.t.sol b/test/unit/SlashingRegistryCoordinatorUnit.t.sol new file mode 100644 index 00000000..ef3c4d8a --- /dev/null +++ b/test/unit/SlashingRegistryCoordinatorUnit.t.sol @@ -0,0 +1,2421 @@ +// // SPDX-License-Identifier: BUSL-1.1 +// pragma solidity ^0.8.27; + +// import "../utils/MockAVSDeployer.sol"; +// import {ISlashingRegistryCoordinator, IRegistryCoordinatorErrors} from "../../src/interfaces/ISlashingRegistryCoordinator.sol"; +// import {QuorumBitmapHistoryLib} from "../../src/libraries/QuorumBitmapHistoryLib.sol"; +// import {BitmapUtils} from "../../src/libraries/BitmapUtils.sol"; +// import {console} from "forge-std/console.sol"; + + +// contract RegistryCoordinatorUnitTests is MockAVSDeployer { +// using BN254 for BN254.G1Point; + +// uint8 internal constant PAUSED_REGISTER_OPERATOR = 0; +// uint8 internal constant PAUSED_DEREGISTER_OPERATOR = 1; +// uint8 internal constant PAUSED_UPDATE_OPERATOR = 2; +// uint8 internal constant MAX_QUORUM_COUNT = 192; + +// /// Emits when an operator is registered +// event OperatorRegistered(address indexed operator, bytes32 indexed operatorId); +// /// Emits when an operator is deregistered +// event OperatorDeregistered(address indexed operator, bytes32 indexed operatorId); + +// event OperatorSocketUpdate(bytes32 indexed operatorId, string socket); + +// /// @notice emitted whenever the stake of `operator` is updated +// event OperatorStakeUpdate( +// bytes32 indexed operatorId, +// uint8 quorumNumber, +// uint96 stake +// ); + +// // Emitted when a new operator pubkey is registered for a set of quorums +// event OperatorAddedToQuorums( +// address operator, +// bytes32 operatorId, +// bytes quorumNumbers +// ); + +// // Emitted when an operator pubkey is removed from a set of quorums +// event OperatorRemovedFromQuorums( +// address operator, +// bytes32 operatorId, +// bytes quorumNumbers +// ); + +// // emitted when an operator's index in the orderd operator list for the quorum with number `quorumNumber` is updated +// event QuorumIndexUpdate(bytes32 indexed operatorId, uint8 quorumNumber, uint32 newIndex); + +// event OperatorSetParamsUpdated(uint8 indexed quorumNumber, ISlashingRegistryCoordinator.OperatorSetParam operatorSetParams); + +// event ChurnApproverUpdated(address prevChurnApprover, address newChurnApprover); + +// event EjectorUpdated(address prevEjector, address newEjector); + +// event QuorumBlockNumberUpdated(uint8 indexed quorumNumber, uint256 blocknumber); + +// function setUp() virtual public { +// _deployMockEigenLayerAndAVS(numQuorums); +// } + +// function _test_registerOperatorWithChurn_SetUp( +// uint256 pseudoRandomNumber, +// bytes memory quorumNumbers, +// uint96 operatorToKickStake +// ) internal returns( +// address operatorToRegister, +// BN254.G1Point memory operatorToRegisterPubKey, +// ISlashingRegistryCoordinator.OperatorKickParam[] memory operatorKickParams +// ) { +// uint32 kickRegistrationBlockNumber = 100; + +// uint256 quorumBitmap = BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers); + +// cheats.roll(kickRegistrationBlockNumber); + +// for (uint i = 0; i < defaultMaxOperatorCount - 1; i++) { +// BN254.G1Point memory pubKey = BN254.hashToG1(keccak256(abi.encodePacked(pseudoRandomNumber, i))); +// address operator = _incrementAddress(defaultOperator, i); + +// _registerOperatorWithCoordinator(operator, quorumBitmap, pubKey); +// } + +// operatorToRegister = _incrementAddress(defaultOperator, defaultMaxOperatorCount); +// operatorToRegisterPubKey = BN254.hashToG1(keccak256(abi.encodePacked(pseudoRandomNumber, defaultMaxOperatorCount))); +// bytes32 operatorToRegisterId = BN254.hashG1Point(operatorToRegisterPubKey); +// bytes32 operatorToKickId; +// address operatorToKick; + +// // register last operator before kick +// operatorKickParams = new ISlashingRegistryCoordinator.OperatorKickParam[](1); +// { +// BN254.G1Point memory pubKey = BN254.hashToG1(keccak256(abi.encodePacked(pseudoRandomNumber, defaultMaxOperatorCount - 1))); +// operatorToKickId = BN254.hashG1Point(pubKey); +// operatorToKick = _incrementAddress(defaultOperator, defaultMaxOperatorCount - 1); + +// // register last operator with much more than the kickBIPsOfTotalStake stake +// _registerOperatorWithCoordinator(operatorToKick, quorumBitmap, pubKey, operatorToKickStake); + +// bytes32[] memory operatorIdsToSwap = new bytes32[](1); +// // operatorIdsToSwap[0] = operatorToRegisterId +// operatorIdsToSwap[0] = operatorToRegisterId; + +// operatorKickParams[0] = ISlashingRegistryCoordinator.OperatorKickParam({ +// quorumNumber: uint8(quorumNumbers[0]), +// operator: operatorToKick +// }); +// } + +// blsApkRegistry.setBLSPublicKey(operatorToRegister, operatorToRegisterPubKey); +// } +// } + +// contract RegistryCoordinatorUnitTests_Initialization_Setters is RegistryCoordinatorUnitTests { +// function test_initialization() public { +// assertEq(address(registryCoordinator.stakeRegistry()), address(stakeRegistry)); +// assertEq(address(registryCoordinator.blsApkRegistry()), address(blsApkRegistry)); +// assertEq(address(registryCoordinator.indexRegistry()), address(indexRegistry)); +// assertEq(address(registryCoordinator.serviceManager()), address(serviceManager)); + +// for (uint i = 0; i < numQuorums; i++) { +// assertEq( +// keccak256(abi.encode(registryCoordinator.getOperatorSetParams(uint8(i)))), +// keccak256(abi.encode(operatorSetParams[i])) +// ); +// } + +// // make sure the contract initializers are disabled +// cheats.expectRevert(bytes("Initializable: contract is already initialized")); +// registryCoordinator.initialize( +// registryCoordinatorOwner, +// churnApprover, +// ejector, +// 0/*initialPausedStatus*/, +// address(serviceManager) // _accountIdentifier +// ); +// } + +// function test_setOperatorSetParams() public { +// cheats.prank(registryCoordinatorOwner); +// cheats.expectEmit(true, true, true, true, address(registryCoordinator)); +// emit OperatorSetParamsUpdated(0, operatorSetParams[1]); +// registryCoordinator.setOperatorSetParams(0, operatorSetParams[1]); +// assertEq(keccak256(abi.encode(registryCoordinator.getOperatorSetParams(0))),keccak256(abi.encode(operatorSetParams[1])), +// "operator set params not updated correctly"); +// } + +// function test_setOperatorSetParams_revert_notOwner() public { +// cheats.expectRevert("Ownable: caller is not the owner"); +// cheats.prank(defaultOperator); +// registryCoordinator.setOperatorSetParams(0, operatorSetParams[0]); +// } + +// function test_setChurnApprover() public { +// address newChurnApprover = address(uint160(uint256(keccak256("newChurnApprover")))); +// cheats.prank(registryCoordinatorOwner); +// cheats.expectEmit(true, true, true, true, address(registryCoordinator)); +// emit ChurnApproverUpdated(churnApprover, newChurnApprover); +// registryCoordinator.setChurnApprover(newChurnApprover); +// assertEq(registryCoordinator.churnApprover(), newChurnApprover); +// } + +// function test_setChurnApprover_revert_notOwner() public { +// address newChurnApprover = address(uint160(uint256(keccak256("newChurnApprover")))); +// cheats.expectRevert("Ownable: caller is not the owner"); +// cheats.prank(defaultOperator); +// registryCoordinator.setChurnApprover(newChurnApprover); +// } + +// function test_setEjector() public { +// address newEjector = address(uint160(uint256(keccak256("newEjector")))); +// cheats.prank(registryCoordinatorOwner); +// cheats.expectEmit(true, true, true, true, address(registryCoordinator)); +// emit EjectorUpdated(ejector, newEjector); +// registryCoordinator.setEjector(newEjector); +// assertEq(registryCoordinator.ejector(), newEjector); +// } + +// function test_setEjector_revert_notOwner() public { +// address newEjector = address(uint160(uint256(keccak256("newEjector")))); +// cheats.expectRevert("Ownable: caller is not the owner"); +// cheats.prank(defaultOperator); +// registryCoordinator.setEjector(newEjector); +// } + +// function test_updateSocket() public { +// bytes memory quorumNumbers = new bytes(1); +// quorumNumbers[0] = bytes1(defaultQuorumNumber); + +// uint256 quorumBitmap = BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers); +// _registerOperatorWithCoordinator(defaultOperator, quorumBitmap, defaultPubKey); + +// cheats.prank(defaultOperator); +// cheats.expectEmit(true, true, true, true, address(registryCoordinator)); +// emit OperatorSocketUpdate(defaultOperatorId, "localhost:32004"); +// registryCoordinator.updateSocket("localhost:32004"); + +// } + +// function test_updateSocket_revert_notRegistered() public { +// cheats.prank(defaultOperator); +// cheats.expectRevert(bytes4(keccak256("NotRegistered()"))); +// registryCoordinator.updateSocket("localhost:32004"); +// } + +// function test_createQuorum_revert_notOwner() public { +// ISlashingRegistryCoordinator.OperatorSetParam memory operatorSetParams; +// uint96 minimumStake; +// IStakeRegistry.StrategyParams[] memory strategyParams; + +// cheats.expectRevert("Ownable: caller is not the owner"); +// cheats.prank(defaultOperator); +// registryCoordinator.createTotalDelegatedStakeQuorum(operatorSetParams, minimumStake, strategyParams); +// } + +// function test_createQuorum() public { +// // re-run setup, but setting up zero quorums +// // this is necessary since the default setup already configures the max number of quorums, preventing adding more +// _deployMockEigenLayerAndAVS(0); + +// ISlashingRegistryCoordinator.OperatorSetParam memory operatorSetParams = +// ISlashingRegistryCoordinator.OperatorSetParam({ +// maxOperatorCount: defaultMaxOperatorCount, +// kickBIPsOfOperatorStake: defaultKickBIPsOfOperatorStake, +// kickBIPsOfTotalStake: defaultKickBIPsOfTotalStake +// }); +// uint96 minimumStake = 1; +// IStakeRegistry.StrategyParams[] memory strategyParams = new IStakeRegistry.StrategyParams[](1); +// strategyParams[0] = +// IStakeRegistry.StrategyParams({ +// strategy: IStrategy(address(1000)), +// multiplier: 1e16 +// }); + +// uint8 quorumCountBefore = registryCoordinator.quorumCount(); + +// cheats.expectEmit(true, true, true, true, address(registryCoordinator)); +// emit OperatorSetParamsUpdated(quorumCountBefore, operatorSetParams); +// cheats.prank(registryCoordinatorOwner); +// registryCoordinator.createTotalDelegatedStakeQuorum(operatorSetParams, minimumStake, strategyParams); + +// uint8 quorumCountAfter = registryCoordinator.quorumCount(); +// assertEq(quorumCountAfter, quorumCountBefore + 1, "quorum count did not increase properly"); +// assertLe(quorumCountAfter, MAX_QUORUM_COUNT, "quorum count exceeded max"); + +// assertEq( +// keccak256(abi.encode(operatorSetParams)), +// keccak256(abi.encode(registryCoordinator.getOperatorSetParams(quorumCountBefore))), +// "OperatorSetParams not stored properly" +// ); +// } +// } + +// contract RegistryCoordinatorUnitTests_RegisterOperator is RegistryCoordinatorUnitTests { + +// function test_registerOperator_revert_paused() public { +// bytes memory emptyQuorumNumbers = new bytes(0); +// ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; + +// // pause registerOperator +// cheats.prank(pauser); +// registryCoordinator.pause(2 ** PAUSED_REGISTER_OPERATOR); + +// cheats.startPrank(defaultOperator); +// cheats.expectRevert(bytes4(keccak256("CurrentlyPaused()"))); +// registryCoordinator.registerOperator(emptyQuorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); +// } + +// function test_registerOperator_revert_emptyQuorumNumbers() public { +// bytes memory emptyQuorumNumbers = new bytes(0); +// ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; + +// cheats.expectRevert(bytes4(keccak256("BitmapEmpty()"))); +// cheats.prank(defaultOperator); +// registryCoordinator.registerOperator(emptyQuorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); +// } + +// function test_registerOperator_revert_invalidQuorum() public { +// bytes memory quorumNumbersTooLarge = new bytes(1); +// ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; + +// quorumNumbersTooLarge[0] = 0xC0; + +// cheats.expectRevert(BitmapUtils.BitmapValueTooLarge.selector); +// cheats.prank(defaultOperator); +// registryCoordinator.registerOperator(quorumNumbersTooLarge, defaultSocket, pubkeyRegistrationParams, emptySig); +// } + +// function test_registerOperator_revert_nonexistentQuorum() public { +// _deployMockEigenLayerAndAVS(10); +// bytes memory quorumNumbersNotCreated = new bytes(1); +// ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; + +// quorumNumbersNotCreated[0] = 0x0B; + +// cheats.prank(defaultOperator); +// cheats.expectRevert(BitmapUtils.BitmapValueTooLarge.selector); +// registryCoordinator.registerOperator(quorumNumbersNotCreated, defaultSocket, pubkeyRegistrationParams, emptySig); +// } + +// function test_registerOperator_singleQuorum() public { +// bytes memory quorumNumbers = new bytes(1); +// ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; +// quorumNumbers[0] = bytes1(defaultQuorumNumber); + +// uint96 actualStake = _setOperatorWeight(defaultOperator, defaultQuorumNumber, defaultStake); + +// cheats.expectEmit(true, true, true, true, address(registryCoordinator)); +// emit OperatorSocketUpdate(defaultOperatorId, defaultSocket); +// cheats.expectEmit(true, true, true, true, address(blsApkRegistry)); +// emit OperatorAddedToQuorums(defaultOperator, defaultOperatorId, quorumNumbers); +// cheats.expectEmit(true, true, true, true, address(stakeRegistry)); +// emit OperatorStakeUpdate(defaultOperatorId, defaultQuorumNumber, actualStake); +// cheats.expectEmit(true, true, true, true, address(indexRegistry)); +// emit QuorumIndexUpdate(defaultOperatorId, defaultQuorumNumber, 0); + +// uint256 gasBefore = gasleft(); +// cheats.prank(defaultOperator); +// registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); +// uint256 gasAfter = gasleft(); +// emit log_named_uint("gasUsed, register for single quorum", gasBefore - gasAfter); + +// uint256 quorumBitmap = BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers); + +// assertEq(registryCoordinator.getOperatorId(defaultOperator), defaultOperatorId); +// assertEq( +// keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), +// keccak256(abi.encode(ISlashingRegistryCoordinator.OperatorInfo({ +// operatorId: defaultOperatorId, +// status: ISlashingRegistryCoordinator.OperatorStatus.REGISTERED +// }))) +// ); +// assertEq(registryCoordinator.getCurrentQuorumBitmap(defaultOperatorId), quorumBitmap); +// assertEq( +// keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0))), +// keccak256(abi.encode(ISlashingRegistryCoordinator.QuorumBitmapUpdate({ +// quorumBitmap: uint192(quorumBitmap), +// updateBlockNumber: uint32(block.number), +// nextUpdateBlockNumber: 0 +// }))) +// ); +// } + +// // @notice tests registering an operator for a fuzzed assortment of quorums +// function testFuzz_registerOperator(uint256 quorumBitmap) public { +// // filter the fuzzed input down to only valid quorums +// quorumBitmap = quorumBitmap & MAX_QUORUM_BITMAP; +// ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; +// cheats.assume(quorumBitmap != 0); +// bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); + +// uint96 actualStake; +// for (uint i = 0; i < quorumNumbers.length; i++) { +// actualStake = _setOperatorWeight(defaultOperator, uint8(quorumNumbers[i]), defaultStake); +// } + +// cheats.expectEmit(true, true, true, true, address(registryCoordinator)); +// emit OperatorSocketUpdate(defaultOperatorId, defaultSocket); +// cheats.expectEmit(true, true, true, true, address(registryCoordinator)); +// emit OperatorRegistered(defaultOperator, defaultOperatorId); + +// cheats.expectEmit(true, true, true, true, address(blsApkRegistry)); +// emit OperatorAddedToQuorums(defaultOperator, defaultOperatorId, quorumNumbers); + +// for (uint i = 0; i < quorumNumbers.length; i++) { +// cheats.expectEmit(true, true, true, true, address(stakeRegistry)); +// emit OperatorStakeUpdate(defaultOperatorId, uint8(quorumNumbers[i]), actualStake); +// } + +// for (uint i = 0; i < quorumNumbers.length; i++) { +// cheats.expectEmit(true, true, true, true, address(indexRegistry)); +// emit QuorumIndexUpdate(defaultOperatorId, uint8(quorumNumbers[i]), 0); +// } + +// uint256 gasBefore = gasleft(); +// cheats.prank(defaultOperator); +// registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); +// uint256 gasAfter = gasleft(); +// emit log_named_uint("gasUsed", gasBefore - gasAfter); +// emit log_named_uint("numQuorums", quorumNumbers.length); + +// assertEq(registryCoordinator.getOperatorId(defaultOperator), defaultOperatorId); +// assertEq( +// keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), +// keccak256(abi.encode(ISlashingRegistryCoordinator.OperatorInfo({ +// operatorId: defaultOperatorId, +// status: ISlashingRegistryCoordinator.OperatorStatus.REGISTERED +// }))) +// ); +// assertEq(registryCoordinator.getCurrentQuorumBitmap(defaultOperatorId), quorumBitmap); +// assertEq( +// keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0))), +// keccak256(abi.encode(ISlashingRegistryCoordinator.QuorumBitmapUpdate({ +// quorumBitmap: uint192(quorumBitmap), +// updateBlockNumber: uint32(block.number), +// nextUpdateBlockNumber: 0 +// }))) +// ); +// } + +// // @notice tests registering an operator for a single quorum and later registering them for an additional quorum +// function test_registerOperator_addingQuorumsAfterInitialRegistration() public { +// uint256 registrationBlockNumber = block.number + 100; +// uint256 nextRegistrationBlockNumber = registrationBlockNumber + 100; +// ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; + +// 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); + +// bytes memory newQuorumNumbers = new bytes(1); +// 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(blsApkRegistry)); +// emit OperatorAddedToQuorums(defaultOperator, defaultOperatorId, newQuorumNumbers); +// cheats.expectEmit(true, true, true, true, address(stakeRegistry)); +// emit OperatorStakeUpdate(defaultOperatorId, uint8(newQuorumNumbers[0]), actualStake); +// cheats.expectEmit(true, true, true, true, address(indexRegistry)); +// emit QuorumIndexUpdate(defaultOperatorId, uint8(newQuorumNumbers[0]), 0); +// cheats.roll(nextRegistrationBlockNumber); +// cheats.prank(defaultOperator); +// registryCoordinator.registerOperator(newQuorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); + +// uint256 quorumBitmap = BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers) | BitmapUtils.orderedBytesArrayToBitmap(newQuorumNumbers); + +// assertEq(registryCoordinator.getOperatorId(defaultOperator), defaultOperatorId); +// assertEq( +// keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), +// keccak256(abi.encode(ISlashingRegistryCoordinator.OperatorInfo({ +// operatorId: defaultOperatorId, +// status: ISlashingRegistryCoordinator.OperatorStatus.REGISTERED +// }))) +// ); +// assertEq(registryCoordinator.getCurrentQuorumBitmap(defaultOperatorId), quorumBitmap); +// assertEq( +// keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0))), +// keccak256(abi.encode(ISlashingRegistryCoordinator.QuorumBitmapUpdate({ +// quorumBitmap: uint192(BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers)), +// updateBlockNumber: uint32(registrationBlockNumber), +// nextUpdateBlockNumber: uint32(nextRegistrationBlockNumber) +// }))) +// ); +// assertEq( +// keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 1))), +// keccak256(abi.encode(ISlashingRegistryCoordinator.QuorumBitmapUpdate({ +// quorumBitmap: uint192(quorumBitmap), +// updateBlockNumber: uint32(nextRegistrationBlockNumber), +// nextUpdateBlockNumber: 0 +// }))) +// ); +// } + +// function test_registerOperator_revert_overFilledQuorum(uint256 pseudoRandomNumber) public { +// uint32 numOperators = defaultMaxOperatorCount; +// uint32 registrationBlockNumber = 200; +// ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; + +// bytes memory quorumNumbers = new bytes(1); +// quorumNumbers[0] = bytes1(defaultQuorumNumber); + +// uint256 quorumBitmap = BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers); + +// cheats.roll(registrationBlockNumber); + +// for (uint i = 0; i < numOperators; i++) { +// BN254.G1Point memory pubKey = BN254.hashToG1(keccak256(abi.encodePacked(pseudoRandomNumber, i))); +// address operator = _incrementAddress(defaultOperator, i); + +// _registerOperatorWithCoordinator(operator, quorumBitmap, pubKey); +// } + +// address operatorToRegister = _incrementAddress(defaultOperator, numOperators); +// BN254.G1Point memory operatorToRegisterPubKey = BN254.hashToG1(keccak256(abi.encodePacked(pseudoRandomNumber, numOperators))); + +// blsApkRegistry.setBLSPublicKey(operatorToRegister, operatorToRegisterPubKey); + +// _setOperatorWeight(operatorToRegister, defaultQuorumNumber, defaultStake); + +// cheats.prank(operatorToRegister); +// cheats.expectRevert(bytes4(keccak256("MaxQuorumsReached()"))); +// registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); +// } + +// function test_registerOperator_revert_operatorAlreadyRegisteredForQuorum() public { +// uint256 registrationBlockNumber = block.number + 100; +// uint256 nextRegistrationBlockNumber = registrationBlockNumber + 100; +// ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; + +// 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(defaultOperator); +// cheats.roll(nextRegistrationBlockNumber); +// cheats.expectRevert(bytes4(keccak256("AlreadyRegisteredForQuorums()"))); +// registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); +// } + +// // tests for the internal `_registerOperator` function: +// function test_registerOperatorInternal_revert_noQuorums() public { +// bytes memory emptyQuorumNumbers = new bytes(0); +// ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; + +// cheats.expectRevert(bytes4(keccak256("BitmapEmpty()"))); +// registryCoordinator._registerOperatorExternal(defaultOperator, defaultOperatorId, emptyQuorumNumbers, defaultSocket, emptySig); +// } + +// function test_registerOperatorInternal_revert_nonexistentQuorum() public { +// bytes memory quorumNumbersTooLarge = new bytes(1); +// ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; + +// quorumNumbersTooLarge[0] = 0xC0; + +// cheats.expectRevert(BitmapUtils.BitmapValueTooLarge.selector); +// registryCoordinator._registerOperatorExternal(defaultOperator, defaultOperatorId, quorumNumbersTooLarge, defaultSocket, emptySig); +// } + +// function test_registerOperatorInternal_revert_operatorAlreadyRegisteredForQuorum() public { +// ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; +// bytes memory quorumNumbers = new bytes(1); +// quorumNumbers[0] = bytes1(defaultQuorumNumber); + +// _setOperatorWeight(defaultOperator, uint8(quorumNumbers[0]), defaultStake); +// registryCoordinator._registerOperatorExternal(defaultOperator, defaultOperatorId, quorumNumbers, defaultSocket, emptySig); + +// cheats.expectRevert(bytes4(keccak256("AlreadyRegisteredForQuorums()"))); +// registryCoordinator._registerOperatorExternal(defaultOperator, defaultOperatorId, quorumNumbers, defaultSocket, emptySig); +// } + +// function test_registerOperatorInternal() public { +// bytes memory quorumNumbers = new bytes(1); +// ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; +// quorumNumbers[0] = bytes1(defaultQuorumNumber); + +// defaultStake = _setOperatorWeight(defaultOperator, uint8(quorumNumbers[0]), defaultStake); + +// cheats.expectEmit(true, true, true, true, address(registryCoordinator)); +// emit OperatorSocketUpdate(defaultOperatorId, defaultSocket); +// cheats.expectEmit(true, true, true, true, address(blsApkRegistry)); +// emit OperatorAddedToQuorums(defaultOperator, defaultOperatorId, quorumNumbers); +// cheats.expectEmit(true, true, true, true, address(stakeRegistry)); +// emit OperatorStakeUpdate(defaultOperatorId, defaultQuorumNumber, defaultStake); +// cheats.expectEmit(true, true, true, true, address(indexRegistry)); +// emit QuorumIndexUpdate(defaultOperatorId, defaultQuorumNumber, 0); + +// registryCoordinator._registerOperatorExternal(defaultOperator, defaultOperatorId, quorumNumbers, defaultSocket, emptySig); + +// uint256 quorumBitmap = BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers); + +// assertEq(registryCoordinator.getOperatorId(defaultOperator), defaultOperatorId); +// assertEq( +// keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), +// keccak256(abi.encode(ISlashingRegistryCoordinator.OperatorInfo({ +// operatorId: defaultOperatorId, +// status: ISlashingRegistryCoordinator.OperatorStatus.REGISTERED +// }))) +// ); +// assertEq(registryCoordinator.getCurrentQuorumBitmap(defaultOperatorId), quorumBitmap); +// assertEq( +// keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0))), +// keccak256(abi.encode(ISlashingRegistryCoordinator.QuorumBitmapUpdate({ +// quorumBitmap: uint192(quorumBitmap), +// updateBlockNumber: uint32(block.number), +// nextUpdateBlockNumber: 0 +// }))) +// ); +// } +// } + +// // @dev note that this contract also contains tests for the `getQuorumBitmapIndicesAtBlockNumber` and `getQuorumBitmapAtBlockNumberByIndex` view fncs +// contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is RegistryCoordinatorUnitTests { +// function test_deregisterOperator_revert_paused() public { +// bytes memory quorumNumbers = new bytes(1); +// quorumNumbers[0] = bytes1(defaultQuorumNumber); +// uint256 quorumBitmap = BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers); + +// _registerOperatorWithCoordinator(defaultOperator, quorumBitmap, defaultPubKey); + +// // pause deregisterOperator +// cheats.prank(pauser); +// registryCoordinator.pause(2 ** PAUSED_DEREGISTER_OPERATOR); + +// cheats.expectRevert(bytes4(keccak256("CurrentlyPaused()"))); +// cheats.prank(defaultOperator); +// registryCoordinator.deregisterOperator(quorumNumbers); +// } + +// function test_deregisterOperator_revert_notRegistered() public { +// bytes memory quorumNumbers = new bytes(1); +// quorumNumbers[0] = bytes1(defaultQuorumNumber); + +// cheats.expectRevert(bytes4(keccak256("NotRegistered()"))); +// cheats.prank(defaultOperator); +// registryCoordinator.deregisterOperator(quorumNumbers); +// } + +// function test_deregisterOperator_revert_incorrectQuorums() public { +// bytes memory quorumNumbers = new bytes(1); +// quorumNumbers[0] = bytes1(defaultQuorumNumber); +// uint256 quorumBitmap = BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers); + +// _registerOperatorWithCoordinator(defaultOperator, quorumBitmap, defaultPubKey); + +// quorumNumbers = new bytes(2); +// quorumNumbers[0] = bytes1(defaultQuorumNumber + 1); +// quorumNumbers[1] = bytes1(defaultQuorumNumber + 2); + +// cheats.expectRevert(bytes4(keccak256("NotRegisteredForQuorum()"))); +// cheats.prank(defaultOperator); +// registryCoordinator.deregisterOperator(quorumNumbers); +// } + +// // @notice verifies that an operator who was registered for a single quorum can be deregistered +// function test_deregisterOperator_singleQuorumAndSingleOperator() public { +// ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; +// uint32 registrationBlockNumber = 100; +// uint32 deregistrationBlockNumber = 200; + +// bytes memory quorumNumbers = new bytes(1); +// quorumNumbers[0] = bytes1(defaultQuorumNumber); + +// _setOperatorWeight(defaultOperator, uint8(quorumNumbers[0]), defaultStake); + +// cheats.startPrank(defaultOperator); + +// cheats.roll(registrationBlockNumber); + +// registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); + +// uint256 quorumBitmap = BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers); + +// cheats.expectEmit(true, true, true, true, address(blsApkRegistry)); +// emit OperatorRemovedFromQuorums(defaultOperator, defaultOperatorId, quorumNumbers); +// cheats.expectEmit(true, true, true, true, address(stakeRegistry)); +// emit OperatorStakeUpdate(defaultOperatorId, defaultQuorumNumber, 0); + +// cheats.roll(deregistrationBlockNumber); + +// uint256 gasBefore = gasleft(); +// registryCoordinator.deregisterOperator(quorumNumbers); +// uint256 gasAfter = gasleft(); +// emit log_named_uint("gasUsed", gasBefore - gasAfter); + +// assertEq( +// keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), +// keccak256(abi.encode(ISlashingRegistryCoordinator.OperatorInfo({ +// operatorId: defaultOperatorId, +// status: ISlashingRegistryCoordinator.OperatorStatus.DEREGISTERED +// }))) +// ); +// assertEq(registryCoordinator.getCurrentQuorumBitmap(defaultOperatorId), 0); +// assertEq( +// keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0))), +// keccak256(abi.encode(ISlashingRegistryCoordinator.QuorumBitmapUpdate({ +// quorumBitmap: uint192(quorumBitmap), +// updateBlockNumber: registrationBlockNumber, +// nextUpdateBlockNumber: deregistrationBlockNumber +// }))) +// ); +// } + +// // @notice verifies that an operator who was registered for a fuzzed set of quorums can be deregistered +// // @dev deregisters the operator from *all* quorums for which they we registered. +// function testFuzz_deregisterOperator_fuzzedQuorumAndSingleOperator(uint256 quorumBitmap) public { +// ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; +// uint32 registrationBlockNumber = 100; +// uint32 deregistrationBlockNumber = 200; + +// // filter down fuzzed input to only valid quorums +// quorumBitmap = quorumBitmap & MAX_QUORUM_BITMAP; +// cheats.assume(quorumBitmap != 0); +// bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); + +// for (uint i = 0; i < quorumNumbers.length; i++) { +// _setOperatorWeight(defaultOperator, uint8(quorumNumbers[i]), defaultStake); +// } + +// cheats.startPrank(defaultOperator); + +// cheats.roll(registrationBlockNumber); + +// registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); + +// cheats.expectEmit(true, true, true, true, address(blsApkRegistry)); +// emit OperatorRemovedFromQuorums(defaultOperator, defaultOperatorId, quorumNumbers); +// for (uint i = 0; i < quorumNumbers.length; i++) { +// cheats.expectEmit(true, true, true, true, address(stakeRegistry)); +// emit OperatorStakeUpdate(defaultOperatorId, uint8(quorumNumbers[i]), 0); +// } + +// cheats.roll(deregistrationBlockNumber); + +// uint256 gasBefore = gasleft(); +// registryCoordinator.deregisterOperator(quorumNumbers); +// uint256 gasAfter = gasleft(); +// emit log_named_uint("gasUsed", gasBefore - gasAfter); +// emit log_named_uint("numQuorums", quorumNumbers.length); + +// assertEq( +// keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), +// keccak256(abi.encode(ISlashingRegistryCoordinator.OperatorInfo({ +// operatorId: defaultOperatorId, +// status: ISlashingRegistryCoordinator.OperatorStatus.DEREGISTERED +// }))) +// ); +// assertEq(registryCoordinator.getCurrentQuorumBitmap(defaultOperatorId), 0); +// assertEq( +// keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0))), +// keccak256(abi.encode(ISlashingRegistryCoordinator.QuorumBitmapUpdate({ +// quorumBitmap: uint192(quorumBitmap), +// updateBlockNumber: registrationBlockNumber, +// nextUpdateBlockNumber: deregistrationBlockNumber +// }))) +// ); +// } +// // @notice verifies that an operator who was registered for a fuzzed set of quorums can be deregistered from a subset of those quorums +// // @dev deregisters the operator from a fuzzed subset of the quorums for which they we registered. +// function testFuzz_deregisterOperator_singleOperator_partialDeregistration( +// uint256 registrationQuorumBitmap, +// uint256 deregistrationQuorumBitmap +// ) public { +// ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; +// uint32 registrationBlockNumber = 100; +// uint32 deregistrationBlockNumber = 200; + +// // filter down fuzzed input to only valid quorums +// registrationQuorumBitmap = registrationQuorumBitmap & MAX_QUORUM_BITMAP; +// cheats.assume(registrationQuorumBitmap != 0); +// // filter the other fuzzed input to a subset of the first fuzzed input +// deregistrationQuorumBitmap = deregistrationQuorumBitmap & registrationQuorumBitmap; +// cheats.assume(deregistrationQuorumBitmap != 0); +// bytes memory registrationquorumNumbers = BitmapUtils.bitmapToBytesArray(registrationQuorumBitmap); + +// for (uint i = 0; i < registrationquorumNumbers.length; i++) { +// _setOperatorWeight(defaultOperator, uint8(registrationquorumNumbers[i]), defaultStake); +// } + +// cheats.startPrank(defaultOperator); + +// cheats.roll(registrationBlockNumber); + +// registryCoordinator.registerOperator(registrationquorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); + +// bytes memory deregistrationquorumNumbers = BitmapUtils.bitmapToBytesArray(deregistrationQuorumBitmap); + +// cheats.expectEmit(true, true, true, true, address(blsApkRegistry)); +// emit OperatorRemovedFromQuorums(defaultOperator, defaultOperatorId, deregistrationquorumNumbers); +// for (uint i = 0; i < deregistrationquorumNumbers.length; i++) { +// cheats.expectEmit(true, true, true, true, address(stakeRegistry)); +// emit OperatorStakeUpdate(defaultOperatorId, uint8(deregistrationquorumNumbers[i]), 0); +// } + +// cheats.roll(deregistrationBlockNumber); + +// uint256 gasBefore = gasleft(); +// registryCoordinator.deregisterOperator(deregistrationquorumNumbers); +// uint256 gasAfter = gasleft(); +// emit log_named_uint("gasUsed", gasBefore - gasAfter); +// emit log_named_uint("numQuorums", deregistrationquorumNumbers.length); + +// // check that the operator is marked as 'degregistered' only if deregistered from *all* quorums +// if (deregistrationQuorumBitmap == registrationQuorumBitmap) { +// assertEq( +// keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), +// keccak256(abi.encode(ISlashingRegistryCoordinator.OperatorInfo({ +// operatorId: defaultOperatorId, +// status: ISlashingRegistryCoordinator.OperatorStatus.DEREGISTERED +// }))) +// ); +// } else { +// assertEq( +// keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), +// keccak256(abi.encode(ISlashingRegistryCoordinator.OperatorInfo({ +// operatorId: defaultOperatorId, +// status: ISlashingRegistryCoordinator.OperatorStatus.REGISTERED +// }))) +// ); +// } +// // ensure that the operator's current quorum bitmap matches the expectation +// uint256 expectedQuorumBitmap = BitmapUtils.minus(registrationQuorumBitmap, deregistrationQuorumBitmap); +// assertEq(registryCoordinator.getCurrentQuorumBitmap(defaultOperatorId), expectedQuorumBitmap); +// // check that the quorum bitmap history is as expected +// assertEq( +// keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0))), +// keccak256(abi.encode(ISlashingRegistryCoordinator.QuorumBitmapUpdate({ +// quorumBitmap: uint192(registrationQuorumBitmap), +// updateBlockNumber: registrationBlockNumber, +// nextUpdateBlockNumber: deregistrationBlockNumber +// }))) +// ); +// // note: there will be no second entry in the operator's bitmap history in the event that the operator has totally deregistered +// if (deregistrationQuorumBitmap != registrationQuorumBitmap) { +// assertEq( +// keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 1))), +// keccak256(abi.encode(ISlashingRegistryCoordinator.QuorumBitmapUpdate({ +// quorumBitmap: uint192(expectedQuorumBitmap), +// updateBlockNumber: deregistrationBlockNumber, +// nextUpdateBlockNumber: 0 +// }))) +// ); +// } +// } + +// // @notice registers the max number of operators with fuzzed bitmaps and then deregisters a pseudorandom operator (from all of their quorums) +// function testFuzz_deregisterOperator_manyOperators(uint256 pseudoRandomNumber) public { +// uint32 numOperators = defaultMaxOperatorCount; + +// uint32 registrationBlockNumber = 100; +// uint32 deregistrationBlockNumber = 200; + +// // pad quorumBitmap with 1 until it has numOperators elements +// uint256[] memory quorumBitmaps = new uint256[](numOperators); +// for (uint i = 0; i < numOperators; i++) { +// // limit to maxQuorumsToRegisterFor quorums via mask so we don't run out of gas, make them all register for quorum 0 as well +// quorumBitmaps[i] = uint256(keccak256(abi.encodePacked("quorumBitmap", pseudoRandomNumber, i))) & (1 << maxQuorumsToRegisterFor - 1) | 1; +// } + +// cheats.roll(registrationBlockNumber); + +// bytes32[] memory lastOperatorInQuorum = new bytes32[](numQuorums); +// for (uint i = 0; i < numOperators; i++) { +// emit log_named_uint("i", i); +// BN254.G1Point memory pubKey = BN254.hashToG1(keccak256(abi.encodePacked(pseudoRandomNumber, i))); +// bytes32 operatorId = BN254.hashG1Point(pubKey); +// address operator = _incrementAddress(defaultOperator, i); + +// _registerOperatorWithCoordinator(operator, quorumBitmaps[i], pubKey); + +// // for each quorum the operator is in, save the operatorId +// bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmaps[i]); +// for (uint j = 0; j < quorumNumbers.length; j++) { +// lastOperatorInQuorum[uint8(quorumNumbers[j])] = operatorId; +// } +// } + +// uint256 indexOfOperatorToDeregister = pseudoRandomNumber % numOperators; +// address operatorToDeregister = _incrementAddress(defaultOperator, indexOfOperatorToDeregister); +// BN254.G1Point memory operatorToDeregisterPubKey = BN254.hashToG1(keccak256(abi.encodePacked(pseudoRandomNumber, indexOfOperatorToDeregister))); +// bytes32 operatorToDeregisterId = BN254.hashG1Point(operatorToDeregisterPubKey); +// uint256 operatorToDeregisterQuorumBitmap = quorumBitmaps[indexOfOperatorToDeregister]; +// bytes memory operatorToDeregisterQuorumNumbers = BitmapUtils.bitmapToBytesArray(operatorToDeregisterQuorumBitmap); + +// bytes32[] memory operatorIdsToSwap = new bytes32[](operatorToDeregisterQuorumNumbers.length); +// for (uint i = 0; i < operatorToDeregisterQuorumNumbers.length; i++) { +// operatorIdsToSwap[i] = lastOperatorInQuorum[uint8(operatorToDeregisterQuorumNumbers[i])]; +// } + +// cheats.expectEmit(true, true, true, true, address(blsApkRegistry)); +// emit OperatorRemovedFromQuorums(operatorToDeregister, operatorToDeregisterId, operatorToDeregisterQuorumNumbers); + +// for (uint i = 0; i < operatorToDeregisterQuorumNumbers.length; i++) { +// cheats.expectEmit(true, true, true, true, address(stakeRegistry)); +// emit OperatorStakeUpdate(operatorToDeregisterId, uint8(operatorToDeregisterQuorumNumbers[i]), 0); +// } + +// cheats.roll(deregistrationBlockNumber); + +// cheats.prank(operatorToDeregister); +// registryCoordinator.deregisterOperator(operatorToDeregisterQuorumNumbers); + +// assertEq( +// keccak256(abi.encode(registryCoordinator.getOperator(operatorToDeregister))), +// keccak256(abi.encode(ISlashingRegistryCoordinator.OperatorInfo({ +// operatorId: operatorToDeregisterId, +// status: ISlashingRegistryCoordinator.OperatorStatus.DEREGISTERED +// }))) +// ); +// assertEq(registryCoordinator.getCurrentQuorumBitmap(defaultOperatorId), 0); +// assertEq( +// keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(operatorToDeregisterId, 0))), +// keccak256(abi.encode(ISlashingRegistryCoordinator.QuorumBitmapUpdate({ +// quorumBitmap: uint192(operatorToDeregisterQuorumBitmap), +// updateBlockNumber: registrationBlockNumber, +// nextUpdateBlockNumber: deregistrationBlockNumber +// }))) +// ); +// } + +// // @notice verify that it is possible for an operator to register, deregister, and then register again! +// function test_reregisterOperator() public { +// test_deregisterOperator_singleQuorumAndSingleOperator(); + +// ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; +// uint32 reregistrationBlockNumber = 201; + +// bytes memory quorumNumbers = new bytes(1); +// quorumNumbers[0] = bytes1(defaultQuorumNumber); + +// cheats.startPrank(defaultOperator); + +// cheats.roll(reregistrationBlockNumber); + +// // store data before registering, to check against later +// ISlashingRegistryCoordinator.QuorumBitmapUpdate memory previousQuorumBitmapUpdate = +// registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0); + +// // re-register the operator +// registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); +// // check success of registration +// uint256 quorumBitmap = BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers); +// assertEq(registryCoordinator.getOperatorId(defaultOperator), defaultOperatorId, "1"); +// assertEq( +// keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), +// keccak256(abi.encode(ISlashingRegistryCoordinator.OperatorInfo({ +// operatorId: defaultOperatorId, +// status: ISlashingRegistryCoordinator.OperatorStatus.REGISTERED +// }))), +// "2" +// ); +// assertEq(registryCoordinator.getCurrentQuorumBitmap(defaultOperatorId), quorumBitmap, "3"); +// // check that previous entry in bitmap history was not changed +// assertEq( +// keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0))), +// keccak256(abi.encode(previousQuorumBitmapUpdate)), +// "4" +// ); +// // check that new entry in bitmap history is as expected +// uint historyLength = registryCoordinator.getQuorumBitmapHistoryLength(defaultOperatorId); +// assertEq( +// keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, historyLength - 1))), +// keccak256(abi.encode(ISlashingRegistryCoordinator.QuorumBitmapUpdate({ +// quorumBitmap: uint192(quorumBitmap), +// updateBlockNumber: uint32(reregistrationBlockNumber), +// nextUpdateBlockNumber: 0 +// }))), +// "5" +// ); +// } + +// // tests for the internal `_deregisterOperator` function: +// function test_deregisterOperatorExternal_revert_noQuorums() public { +// ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; +// uint32 registrationBlockNumber = 100; +// uint32 deregistrationBlockNumber = 200; + +// bytes memory quorumNumbers = new bytes(1); +// quorumNumbers[0] = bytes1(defaultQuorumNumber); + +// _setOperatorWeight(defaultOperator, uint8(quorumNumbers[0]), defaultStake); + +// cheats.roll(registrationBlockNumber); +// cheats.startPrank(defaultOperator); +// registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); + +// bytes memory emptyQuorumNumbers = new bytes(0); + +// cheats.roll(deregistrationBlockNumber); +// cheats.expectRevert(bytes4(keccak256("BitmapCannotBeZero()"))); +// registryCoordinator._deregisterOperatorExternal(defaultOperator, emptyQuorumNumbers); +// } + +// function test_deregisterOperatorExternal_revert_notRegistered() public { +// bytes memory emptyQuorumNumbers = new bytes(0); +// cheats.expectRevert(bytes4(keccak256("NotRegistered()"))); +// registryCoordinator._deregisterOperatorExternal(defaultOperator, emptyQuorumNumbers); +// } + +// function test_deregisterOperatorExternal_revert_incorrectQuorums() public { +// ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; +// uint32 registrationBlockNumber = 100; +// uint32 deregistrationBlockNumber = 200; + +// bytes memory quorumNumbers = new bytes(1); +// quorumNumbers[0] = bytes1(defaultQuorumNumber); + +// _setOperatorWeight(defaultOperator, uint8(quorumNumbers[0]), defaultStake); + +// cheats.roll(registrationBlockNumber); +// cheats.startPrank(defaultOperator); +// registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); + +// bytes memory incorrectQuorum = new bytes(1); +// incorrectQuorum[0] = bytes1(defaultQuorumNumber + 1); + +// cheats.roll(deregistrationBlockNumber); +// cheats.expectRevert(bytes4(keccak256("NotRegisteredForQuorum()"))); +// 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(bytes4(keccak256("CannotReregisterYet()"))); +// 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 { + +// function testFuzz_deregisterOperatorInternal_partialDeregistration( +// uint256 registrationQuorumBitmap, +// uint256 deregistrationQuorumBitmap +// ) public { +// ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; +// uint32 registrationBlockNumber = 100; +// uint32 deregistrationBlockNumber = 200; + +// // filter down fuzzed input to only valid quorums +// registrationQuorumBitmap = registrationQuorumBitmap & MAX_QUORUM_BITMAP; +// cheats.assume(registrationQuorumBitmap != 0); +// // filter the other fuzzed input to a subset of the first fuzzed input +// deregistrationQuorumBitmap = deregistrationQuorumBitmap & registrationQuorumBitmap; +// cheats.assume(deregistrationQuorumBitmap != 0); +// bytes memory registrationquorumNumbers = BitmapUtils.bitmapToBytesArray(registrationQuorumBitmap); + +// for (uint i = 0; i < registrationquorumNumbers.length; i++) { +// _setOperatorWeight(defaultOperator, uint8(registrationquorumNumbers[i]), defaultStake); +// } + +// cheats.roll(registrationBlockNumber); +// cheats.startPrank(defaultOperator); +// registryCoordinator.registerOperator(registrationquorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); + +// bytes memory deregistrationquorumNumbers = BitmapUtils.bitmapToBytesArray(deregistrationQuorumBitmap); + +// cheats.expectEmit(true, true, true, true, address(blsApkRegistry)); +// emit OperatorRemovedFromQuorums(defaultOperator, defaultOperatorId, deregistrationquorumNumbers); +// for (uint i = 0; i < deregistrationquorumNumbers.length; i++) { +// cheats.expectEmit(true, true, true, true, address(stakeRegistry)); +// emit OperatorStakeUpdate(defaultOperatorId, uint8(deregistrationquorumNumbers[i]), 0); +// } + +// cheats.roll(deregistrationBlockNumber); + +// registryCoordinator._deregisterOperatorExternal(defaultOperator, deregistrationquorumNumbers); + +// // check that the operator is marked as 'degregistered' only if deregistered from *all* quorums +// if (deregistrationQuorumBitmap == registrationQuorumBitmap) { +// assertEq( +// keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), +// keccak256(abi.encode(ISlashingRegistryCoordinator.OperatorInfo({ +// operatorId: defaultOperatorId, +// status: ISlashingRegistryCoordinator.OperatorStatus.DEREGISTERED +// }))) +// ); +// } else { +// assertEq( +// keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), +// keccak256(abi.encode(ISlashingRegistryCoordinator.OperatorInfo({ +// operatorId: defaultOperatorId, +// status: ISlashingRegistryCoordinator.OperatorStatus.REGISTERED +// }))) +// ); +// } +// // ensure that the operator's current quorum bitmap matches the expectation +// uint256 expectedQuorumBitmap = BitmapUtils.minus(registrationQuorumBitmap, deregistrationQuorumBitmap); +// assertEq(registryCoordinator.getCurrentQuorumBitmap(defaultOperatorId), expectedQuorumBitmap); +// // check that the quorum bitmap history is as expected +// assertEq( +// keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0))), +// keccak256(abi.encode(ISlashingRegistryCoordinator.QuorumBitmapUpdate({ +// quorumBitmap: uint192(registrationQuorumBitmap), +// updateBlockNumber: registrationBlockNumber, +// nextUpdateBlockNumber: deregistrationBlockNumber +// }))) +// ); +// // note: there will be no second entry in the operator's bitmap history in the event that the operator has totally deregistered +// if (deregistrationQuorumBitmap != registrationQuorumBitmap) { +// assertEq( +// keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 1))), +// keccak256(abi.encode(ISlashingRegistryCoordinator.QuorumBitmapUpdate({ +// quorumBitmap: uint192(expectedQuorumBitmap), +// updateBlockNumber: deregistrationBlockNumber, +// nextUpdateBlockNumber: 0 +// }))) +// ); +// } +// } + +// function test_ejectOperator_allQuorums() public { +// // register operator with default stake with default quorum number +// bytes memory quorumNumbers = new bytes(1); +// quorumNumbers[0] = bytes1(defaultQuorumNumber); +// ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; + +// _setOperatorWeight(defaultOperator, uint8(quorumNumbers[0]), defaultStake); + +// cheats.prank(defaultOperator); +// registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); + +// cheats.expectEmit(true, true, true, true, address(blsApkRegistry)); +// emit OperatorRemovedFromQuorums(defaultOperator, defaultOperatorId, quorumNumbers); + +// cheats.expectEmit(true, true, true, true, address(stakeRegistry)); +// emit OperatorStakeUpdate(defaultOperatorId, uint8(quorumNumbers[0]), 0); + +// // eject +// cheats.prank(ejector); +// registryCoordinator.ejectOperator(defaultOperator, quorumNumbers); + +// // make sure the operator is deregistered +// assertEq( +// keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), +// keccak256(abi.encode(ISlashingRegistryCoordinator.OperatorInfo({ +// operatorId: defaultOperatorId, +// status: ISlashingRegistryCoordinator.OperatorStatus.DEREGISTERED +// }))) +// ); +// // make sure the operator is not in any quorums +// assertEq(registryCoordinator.getCurrentQuorumBitmap(defaultOperatorId), 0); +// } + +// function test_ejectOperator_subsetOfQuorums() public { +// // register operator with default stake with 2 quorums +// bytes memory quorumNumbers = new bytes(2); +// quorumNumbers[0] = bytes1(defaultQuorumNumber); +// quorumNumbers[1] = bytes1(defaultQuorumNumber + 1); +// ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; + +// for (uint i = 0; i < quorumNumbers.length; i++) { +// _setOperatorWeight(defaultOperator, uint8(quorumNumbers[i]), defaultStake); +// } + +// cheats.prank(defaultOperator); +// registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); + +// // eject from only first quorum +// bytes memory quorumNumbersToEject = new bytes(1); +// quorumNumbersToEject[0] = quorumNumbers[0]; + +// cheats.expectEmit(true, true, true, true, address(blsApkRegistry)); +// emit OperatorRemovedFromQuorums(defaultOperator, defaultOperatorId, quorumNumbersToEject); + +// cheats.expectEmit(true, true, true, true, address(stakeRegistry)); +// emit OperatorStakeUpdate(defaultOperatorId, uint8(quorumNumbersToEject[0]), 0); + +// cheats.prank(ejector); +// registryCoordinator.ejectOperator(defaultOperator, quorumNumbersToEject); + +// // make sure the operator is registered +// assertEq( +// keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), +// keccak256(abi.encode(ISlashingRegistryCoordinator.OperatorInfo({ +// operatorId: defaultOperatorId, +// status: ISlashingRegistryCoordinator.OperatorStatus.REGISTERED +// }))) +// ); +// // make sure the operator is properly removed from the quorums +// assertEq( +// registryCoordinator.getCurrentQuorumBitmap(defaultOperatorId), +// // quorumsRegisteredFor & ~quorumsEjectedFrom +// BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers) & ~BitmapUtils.orderedBytesArrayToBitmap(quorumNumbersToEject) +// ); +// } + +// function test_ejectOperator_revert_notEjector() public { +// bytes memory quorumNumbers = new bytes(1); +// quorumNumbers[0] = bytes1(defaultQuorumNumber); +// ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; + +// _setOperatorWeight(defaultOperator, uint8(quorumNumbers[0]), defaultStake); + +// cheats.prank(defaultOperator); +// registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); + +// cheats.expectRevert(bytes4(keccak256("OnlyEjector()"))); +// cheats.prank(defaultOperator); +// registryCoordinator.ejectOperator(defaultOperator, quorumNumbers); +// } + +// function test_getQuorumBitmapIndicesAtBlockNumber_revert_notRegistered() public { +// uint32 blockNumber; +// bytes32[] memory operatorIds = new bytes32[](1); +// cheats.expectRevert("RegistryCoordinator.getQuorumBitmapIndexAtBlockNumber: no bitmap update found for operatorId"); +// registryCoordinator.getQuorumBitmapIndicesAtBlockNumber(blockNumber, operatorIds); +// } + +// // @notice tests for correct reversion and return values in the event that an operator registers +// function test_getQuorumBitmapIndicesAtBlockNumber_operatorRegistered() public { +// // register the operator +// ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; +// uint32 registrationBlockNumber = 100; +// bytes memory quorumNumbers = new bytes(1); +// quorumNumbers[0] = bytes1(defaultQuorumNumber); +// _setOperatorWeight(defaultOperator, uint8(quorumNumbers[0]), defaultStake); +// cheats.roll(registrationBlockNumber); +// cheats.startPrank(defaultOperator); +// registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); + +// uint32 blockNumber = 0; +// bytes32[] memory operatorIds = new bytes32[](1); +// operatorIds[0] = defaultOperatorId; + +// uint32[] memory returnArray; +// cheats.expectRevert("RegistryCoordinator.getQuorumBitmapIndexAtBlockNumber: no bitmap update found for operatorId"); +// registryCoordinator.getQuorumBitmapIndicesAtBlockNumber(blockNumber, operatorIds); + +// blockNumber = registrationBlockNumber; +// returnArray = registryCoordinator.getQuorumBitmapIndicesAtBlockNumber(blockNumber, operatorIds); +// assertEq(returnArray[0], 0, "defaultOperator bitmap index at blockNumber registrationBlockNumber was not 0"); + +// blockNumber = registrationBlockNumber + 1; +// returnArray = registryCoordinator.getQuorumBitmapIndicesAtBlockNumber(blockNumber, operatorIds); +// assertEq(returnArray[0], 0, "defaultOperator bitmap index at blockNumber registrationBlockNumber + 1 was not 0"); +// } + +// // @notice tests for correct reversion and return values in the event that an operator registers and later deregisters +// function test_getQuorumBitmapIndicesAtBlockNumber_operatorDeregistered() public { +// test_deregisterOperator_singleQuorumAndSingleOperator(); +// uint32 registrationBlockNumber = 100; +// uint32 deregistrationBlockNumber = 200; +// uint32 blockNumber = 0; +// bytes32[] memory operatorIds = new bytes32[](1); +// operatorIds[0] = defaultOperatorId; + +// uint32[] memory returnArray; +// cheats.expectRevert("RegistryCoordinator.getQuorumBitmapIndexAtBlockNumber: no bitmap update found for operatorId"); +// registryCoordinator.getQuorumBitmapIndicesAtBlockNumber(blockNumber, operatorIds); + +// blockNumber = registrationBlockNumber; +// returnArray = registryCoordinator.getQuorumBitmapIndicesAtBlockNumber(blockNumber, operatorIds); +// assertEq(returnArray[0], 0, "defaultOperator bitmap index at blockNumber registrationBlockNumber was not 0"); + +// blockNumber = registrationBlockNumber + 1; +// returnArray = registryCoordinator.getQuorumBitmapIndicesAtBlockNumber(blockNumber, operatorIds); +// assertEq(returnArray[0], 0, "defaultOperator bitmap index at blockNumber registrationBlockNumber + 1 was not 0"); + +// blockNumber = deregistrationBlockNumber; +// returnArray = registryCoordinator.getQuorumBitmapIndicesAtBlockNumber(blockNumber, operatorIds); +// assertEq(returnArray[0], 1, "defaultOperator bitmap index at blockNumber deregistrationBlockNumber was not 1"); + +// blockNumber = deregistrationBlockNumber + 1; +// returnArray = registryCoordinator.getQuorumBitmapIndicesAtBlockNumber(blockNumber, operatorIds); +// assertEq(returnArray[0], 1, "defaultOperator bitmap index at blockNumber deregistrationBlockNumber + 1 was not 1"); +// } + +// // @notice tests for correct reversion and return values in the event that an operator registers and later deregisters +// function test_getQuorumBitmapAtBlockNumberByIndex_operatorDeregistered() public { +// test_deregisterOperator_singleQuorumAndSingleOperator(); +// uint32 registrationBlockNumber = 100; +// uint32 deregistrationBlockNumber = 200; +// uint32 blockNumber = 0; +// bytes32 operatorId = defaultOperatorId; +// uint256 index = 0; + +// uint192 defaultQuorumBitmap = 1; +// uint192 emptyBitmap = 0; + +// // try an incorrect blockNumber input and confirm reversion +// cheats.expectRevert(QuorumBitmapHistoryLib.BitmapUpdateIsAfterBlockNumber.selector); +// uint192 returnVal = registryCoordinator.getQuorumBitmapAtBlockNumberByIndex(operatorId, blockNumber, index); + +// blockNumber = registrationBlockNumber; +// returnVal = registryCoordinator.getQuorumBitmapAtBlockNumberByIndex(operatorId, blockNumber, index); +// assertEq(returnVal, defaultQuorumBitmap, "defaultOperator bitmap index at blockNumber registrationBlockNumber was not defaultQuorumBitmap"); + +// blockNumber = registrationBlockNumber + 1; +// returnVal = registryCoordinator.getQuorumBitmapAtBlockNumberByIndex(operatorId, blockNumber, index); +// assertEq(returnVal, defaultQuorumBitmap, "defaultOperator bitmap index at blockNumber registrationBlockNumber + 1 was not defaultQuorumBitmap"); + +// // try an incorrect index input and confirm reversion +// index = 1; +// cheats.expectRevert(QuorumBitmapHistoryLib.BitmapUpdateIsAfterBlockNumber.selector); +// returnVal = registryCoordinator.getQuorumBitmapAtBlockNumberByIndex(operatorId, blockNumber, index); + +// blockNumber = deregistrationBlockNumber; +// returnVal = registryCoordinator.getQuorumBitmapAtBlockNumberByIndex(operatorId, blockNumber, index); +// assertEq(returnVal, emptyBitmap, "defaultOperator bitmap index at blockNumber deregistrationBlockNumber was not emptyBitmap"); + +// blockNumber = deregistrationBlockNumber + 1; +// returnVal = registryCoordinator.getQuorumBitmapAtBlockNumberByIndex(operatorId, blockNumber, index); +// assertEq(returnVal, emptyBitmap, "defaultOperator bitmap index at blockNumber deregistrationBlockNumber + 1 was not emptyBitmap"); + +// // try an incorrect index input and confirm reversion +// index = 0; +// cheats.expectRevert(QuorumBitmapHistoryLib.NextBitmapUpdateIsBeforeBlockNumber.selector); +// returnVal = registryCoordinator.getQuorumBitmapAtBlockNumberByIndex(operatorId, blockNumber, index); +// } +// } + +// contract RegistryCoordinatorUnitTests_RegisterOperatorWithChurn is RegistryCoordinatorUnitTests { +// // @notice registers an operator for a single quorum, with a fuzzed pubkey, churning out another operator from the quorum +// function testFuzz_registerOperatorWithChurn(uint256 pseudoRandomNumber) public { +// uint32 numOperators = defaultMaxOperatorCount; +// uint32 kickRegistrationBlockNumber = 100; +// uint32 registrationBlockNumber = 200; + +// bytes memory quorumNumbers = new bytes(1); +// quorumNumbers[0] = bytes1(defaultQuorumNumber); + +// uint256 quorumBitmap = BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers); + +// cheats.roll(kickRegistrationBlockNumber); + +// for (uint i = 0; i < numOperators - 1; i++) { +// BN254.G1Point memory pubKey = BN254.hashToG1(keccak256(abi.encodePacked(pseudoRandomNumber, i))); +// address operator = _incrementAddress(defaultOperator, i); + +// _registerOperatorWithCoordinator(operator, quorumBitmap, pubKey); +// } + +// address operatorToRegister = _incrementAddress(defaultOperator, numOperators); +// BN254.G1Point memory operatorToRegisterPubKey = BN254.hashToG1(keccak256(abi.encodePacked(pseudoRandomNumber, numOperators))); +// bytes32 operatorToRegisterId = BN254.hashG1Point(operatorToRegisterPubKey); +// bytes32 operatorToKickId; +// address operatorToKick; + +// // register last operator before kick +// ISlashingRegistryCoordinator.OperatorKickParam[] memory operatorKickParams = new ISlashingRegistryCoordinator.OperatorKickParam[](1); +// { +// BN254.G1Point memory pubKey = BN254.hashToG1(keccak256(abi.encodePacked(pseudoRandomNumber, numOperators - 1))); +// operatorToKickId = BN254.hashG1Point(pubKey); +// operatorToKick = _incrementAddress(defaultOperator, numOperators - 1); + +// _registerOperatorWithCoordinator(operatorToKick, quorumBitmap, pubKey); + +// bytes32[] memory operatorIdsToSwap = new bytes32[](1); +// // operatorIdsToSwap[0] = operatorToRegisterId +// operatorIdsToSwap[0] = operatorToRegisterId; + +// operatorKickParams[0] = ISlashingRegistryCoordinator.OperatorKickParam({ +// quorumNumber: defaultQuorumNumber, +// operator: operatorToKick +// }); +// } + +// blsApkRegistry.setBLSPublicKey(operatorToRegister, operatorToRegisterPubKey); + +// uint96 registeringStake = defaultKickBIPsOfOperatorStake * defaultStake; +// _setOperatorWeight(operatorToRegister, defaultQuorumNumber, registeringStake); + +// cheats.roll(registrationBlockNumber); +// cheats.expectEmit(true, true, true, true, address(registryCoordinator)); +// emit OperatorSocketUpdate(operatorToRegisterId, defaultSocket); +// cheats.expectEmit(true, true, true, true, address(registryCoordinator)); +// emit OperatorRegistered(operatorToRegister, operatorToRegisterId); + +// cheats.expectEmit(true, true, true, true, address(blsApkRegistry)); +// emit OperatorAddedToQuorums(operatorToRegister, operatorToRegisterId, quorumNumbers); +// cheats.expectEmit(true, true, true, false, address(stakeRegistry)); +// emit OperatorStakeUpdate(operatorToRegisterId, defaultQuorumNumber, registeringStake - 1); +// cheats.expectEmit(true, true, true, true, address(indexRegistry)); +// emit QuorumIndexUpdate(operatorToRegisterId, defaultQuorumNumber, numOperators); + + +// cheats.expectEmit(true, true, true, true, address(registryCoordinator)); +// emit OperatorDeregistered(operatorKickParams[0].operator, operatorToKickId); +// cheats.expectEmit(true, true, true, true, address(blsApkRegistry)); +// emit OperatorRemovedFromQuorums(operatorKickParams[0].operator, operatorToKickId, quorumNumbers); +// cheats.expectEmit(true, true, true, true, address(stakeRegistry)); +// emit OperatorStakeUpdate(operatorToKickId, defaultQuorumNumber, 0); +// cheats.expectEmit(true, true, true, true, address(indexRegistry)); +// emit QuorumIndexUpdate(operatorToRegisterId, defaultQuorumNumber, numOperators - 1); + +// { +// ISignatureUtils.SignatureWithSaltAndExpiry memory emptyAVSRegSig; +// ISignatureUtils.SignatureWithSaltAndExpiry memory signatureWithExpiry = +// _signOperatorChurnApproval(operatorToRegister, operatorToRegisterId, operatorKickParams, defaultSalt, block.timestamp + 10); +// cheats.prank(operatorToRegister); +// uint256 gasBefore = gasleft(); +// registryCoordinator.registerOperatorWithChurn( +// quorumNumbers, +// defaultSocket, +// pubkeyRegistrationParams, +// operatorKickParams, +// signatureWithExpiry, +// emptyAVSRegSig +// ); +// uint256 gasAfter = gasleft(); +// emit log_named_uint("gasUsed", gasBefore - gasAfter); +// } + +// assertEq( +// keccak256(abi.encode(registryCoordinator.getOperator(operatorToRegister))), +// keccak256(abi.encode(ISlashingRegistryCoordinator.OperatorInfo({ +// operatorId: operatorToRegisterId, +// status: ISlashingRegistryCoordinator.OperatorStatus.REGISTERED +// }))) +// ); +// assertEq( +// keccak256(abi.encode(registryCoordinator.getOperator(operatorToKick))), +// keccak256(abi.encode(ISlashingRegistryCoordinator.OperatorInfo({ +// operatorId: operatorToKickId, +// status: ISlashingRegistryCoordinator.OperatorStatus.DEREGISTERED +// }))) +// ); +// assertEq( +// keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(operatorToKickId, 0))), +// keccak256(abi.encode(ISlashingRegistryCoordinator.QuorumBitmapUpdate({ +// quorumBitmap: uint192(quorumBitmap), +// updateBlockNumber: kickRegistrationBlockNumber, +// nextUpdateBlockNumber: registrationBlockNumber +// }))) +// ); +// } + +// function test_registerOperatorWithChurn_revert_lessThanKickBIPsOfOperatorStake(uint256 pseudoRandomNumber) public { +// bytes memory quorumNumbers = new bytes(1); +// quorumNumbers[0] = bytes1(defaultQuorumNumber); +// ISignatureUtils.SignatureWithSaltAndExpiry memory emptyAVSRegSig; + +// ( +// address operatorToRegister, +// BN254.G1Point memory operatorToRegisterPubKey, +// ISlashingRegistryCoordinator.OperatorKickParam[] memory operatorKickParams +// ) = _test_registerOperatorWithChurn_SetUp(pseudoRandomNumber, quorumNumbers, defaultStake); +// bytes32 operatorToRegisterId = BN254.hashG1Point(operatorToRegisterPubKey); + +// _setOperatorWeight(operatorToRegister, defaultQuorumNumber, defaultStake); + +// cheats.roll(registrationBlockNumber); +// ISignatureUtils.SignatureWithSaltAndExpiry memory signatureWithExpiry = +// _signOperatorChurnApproval(operatorToRegister, operatorToRegisterId, operatorKickParams, defaultSalt, block.timestamp + 10); +// cheats.prank(operatorToRegister); +// cheats.expectRevert(bytes4(keccak256("InsufficientStakeForChurn()"))); +// registryCoordinator.registerOperatorWithChurn( +// quorumNumbers, +// defaultSocket, +// pubkeyRegistrationParams, +// operatorKickParams, +// signatureWithExpiry, +// emptyAVSRegSig +// ); +// } + +// function test_registerOperatorWithChurn_revert_lessThanKickBIPsOfTotalStake(uint256 pseudoRandomNumber) public { +// bytes memory quorumNumbers = new bytes(1); +// quorumNumbers[0] = bytes1(defaultQuorumNumber); +// ISignatureUtils.SignatureWithSaltAndExpiry memory emptyAVSRegSig; + +// uint96 operatorToKickStake = defaultMaxOperatorCount * defaultStake; +// ( +// address operatorToRegister, +// BN254.G1Point memory operatorToRegisterPubKey, +// ISlashingRegistryCoordinator.OperatorKickParam[] memory operatorKickParams +// ) = _test_registerOperatorWithChurn_SetUp(pseudoRandomNumber, quorumNumbers, operatorToKickStake); +// bytes32 operatorToRegisterId = BN254.hashG1Point(operatorToRegisterPubKey); + + +// // set the stake of the operator to register to the defaultKickBIPsOfOperatorStake multiple of the operatorToKickStake +// _setOperatorWeight(operatorToRegister, defaultQuorumNumber, operatorToKickStake * defaultKickBIPsOfOperatorStake / 10000 + 1); + +// cheats.roll(registrationBlockNumber); +// ISignatureUtils.SignatureWithSaltAndExpiry memory signatureWithExpiry = +// _signOperatorChurnApproval(operatorToRegister, operatorToRegisterId, operatorKickParams, defaultSalt, block.timestamp + 10); +// cheats.prank(operatorToRegister); +// cheats.expectRevert(bytes4(keccak256("CannotKickOperatorAboveThreshold()"))); +// registryCoordinator.registerOperatorWithChurn( +// quorumNumbers, +// defaultSocket, +// pubkeyRegistrationParams, +// operatorKickParams, +// signatureWithExpiry, +// emptyAVSRegSig +// ); +// } + +// function test_registerOperatorWithChurn_revert_invalidChurnApproverSignature(uint256 pseudoRandomNumber) public { +// bytes memory quorumNumbers = new bytes(1); +// quorumNumbers[0] = bytes1(defaultQuorumNumber); +// ISignatureUtils.SignatureWithSaltAndExpiry memory emptyAVSRegSig; + +// ( +// address operatorToRegister, +// , +// ISlashingRegistryCoordinator.OperatorKickParam[] memory operatorKickParams +// ) = _test_registerOperatorWithChurn_SetUp(pseudoRandomNumber, quorumNumbers, defaultStake); + +// uint96 registeringStake = defaultKickBIPsOfOperatorStake * defaultStake; +// _setOperatorWeight(operatorToRegister, defaultQuorumNumber, registeringStake); + +// cheats.roll(registrationBlockNumber); +// ISignatureUtils.SignatureWithSaltAndExpiry memory signatureWithSaltAndExpiry; +// signatureWithSaltAndExpiry.expiry = block.timestamp + 10; +// signatureWithSaltAndExpiry.signature = +// hex"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001B"; +// signatureWithSaltAndExpiry.salt = defaultSalt; +// cheats.prank(operatorToRegister); +// cheats.expectRevert(bytes4(keccak256("InvalidSignature()"))); +// registryCoordinator.registerOperatorWithChurn( +// quorumNumbers, +// defaultSocket, +// pubkeyRegistrationParams, +// operatorKickParams, +// signatureWithSaltAndExpiry, +// emptyAVSRegSig +// ); +// } + +// function test_registerOperatorWithChurn_revert_expiredChurnApproverSignature(uint256 pseudoRandomNumber) public { +// bytes memory quorumNumbers = new bytes(1); +// quorumNumbers[0] = bytes1(defaultQuorumNumber); +// ISignatureUtils.SignatureWithSaltAndExpiry memory emptyAVSRegSig; + +// ( +// address operatorToRegister, +// BN254.G1Point memory operatorToRegisterPubKey, +// ISlashingRegistryCoordinator.OperatorKickParam[] memory operatorKickParams +// ) = _test_registerOperatorWithChurn_SetUp(pseudoRandomNumber, quorumNumbers, defaultStake); +// bytes32 operatorToRegisterId = BN254.hashG1Point(operatorToRegisterPubKey); + +// uint96 registeringStake = defaultKickBIPsOfOperatorStake * defaultStake; +// _setOperatorWeight(operatorToRegister, defaultQuorumNumber, registeringStake); + +// cheats.roll(registrationBlockNumber); +// ISignatureUtils.SignatureWithSaltAndExpiry memory signatureWithSaltAndExpiry = +// _signOperatorChurnApproval(operatorToRegister, operatorToRegisterId, operatorKickParams, defaultSalt, block.timestamp - 1); +// cheats.prank(operatorToRegister); +// cheats.expectRevert(bytes4(keccak256("SignatureExpired()"))); +// registryCoordinator.registerOperatorWithChurn( +// quorumNumbers, +// defaultSocket, +// pubkeyRegistrationParams, +// operatorKickParams, +// signatureWithSaltAndExpiry, +// emptyAVSRegSig +// ); +// } +// } + +// contract RegistryCoordinatorUnitTests_UpdateOperators is RegistryCoordinatorUnitTests { +// function test_updateOperators_revert_paused() public { +// cheats.prank(pauser); +// registryCoordinator.pause(2 ** PAUSED_UPDATE_OPERATOR); + +// address[] memory operatorsToUpdate = new address[](1); +// operatorsToUpdate[0] = defaultOperator; + +// cheats.expectRevert(bytes4(keccak256("CurrentlyPaused()"))); +// registryCoordinator.updateOperators(operatorsToUpdate); +// } + +// // @notice tests the `updateOperators` function with a single registered operator as input +// function test_updateOperators_singleOperator() public { +// // register the default operator +// ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; +// uint32 registrationBlockNumber = 100; +// bytes memory quorumNumbers = new bytes(1); +// quorumNumbers[0] = bytes1(defaultQuorumNumber); +// _setOperatorWeight(defaultOperator, uint8(quorumNumbers[0]), defaultStake); +// cheats.startPrank(defaultOperator); +// cheats.roll(registrationBlockNumber); +// registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); + +// address[] memory operatorsToUpdate = new address[](1); +// operatorsToUpdate[0] = defaultOperator; + +// registryCoordinator.updateOperators(operatorsToUpdate); +// } + +// // @notice tests the `updateOperators` function with a single registered operator as input +// // @dev also sets up return data from the StakeRegistry +// function testFuzz_updateOperators_singleOperator(uint192 registrationBitmap, uint192 mockReturnData) public { +// // filter fuzzed inputs to only valid inputs +// cheats.assume(registrationBitmap != 0); +// mockReturnData = (mockReturnData & registrationBitmap); +// emit log_named_uint("mockReturnData", mockReturnData); + +// // register the default operator +// ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; +// uint32 registrationBlockNumber = 100; +// bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(registrationBitmap); +// for (uint256 i = 0; i < quorumNumbers.length; ++i) { +// _setOperatorWeight(defaultOperator, uint8(quorumNumbers[i]), defaultStake); +// } +// cheats.startPrank(defaultOperator); +// cheats.roll(registrationBlockNumber); +// registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); + +// address[] memory operatorsToUpdate = new address[](1); +// operatorsToUpdate[0] = defaultOperator; + +// uint192 quorumBitmapBefore = registryCoordinator.getCurrentQuorumBitmap(defaultOperatorId); +// assertEq(quorumBitmapBefore, registrationBitmap, "operator bitmap somehow incorrect"); + +// // make the stake registry return info that the operator should be removed from quorums +// uint192 quorumBitmapToRemove = mockReturnData; +// bytes memory quorumNumbersToRemove = BitmapUtils.bitmapToBytesArray(quorumBitmapToRemove); +// for (uint256 i = 0; i < quorumNumbersToRemove.length; ++i) { +// _setOperatorWeight(defaultOperator, uint8(quorumNumbersToRemove[i]), 0); +// } +// uint256 expectedQuorumBitmap = BitmapUtils.minus(quorumBitmapBefore, quorumBitmapToRemove); + +// registryCoordinator.updateOperators(operatorsToUpdate); +// uint192 quorumBitmapAfter = registryCoordinator.getCurrentQuorumBitmap(defaultOperatorId); +// assertEq(expectedQuorumBitmap, quorumBitmapAfter, "quorum bitmap did not update correctly"); +// } + +// // @notice tests the `updateOperators` function with a single *un*registered operator as input +// function test_updateOperators_unregisteredOperator() public view { +// address[] memory operatorsToUpdate = new address[](1); +// operatorsToUpdate[0] = defaultOperator; + +// // force a staticcall to the `updateOperators` function -- this should *pass* because the call should be a strict no-op! +// (bool success, ) = address(registryCoordinator).staticcall(abi.encodeWithSignature("updateOperators(address[])", operatorsToUpdate)); +// require(success, "staticcall failed!"); +// } + +// function test_updateOperatorsForQuorum_revert_paused() public { +// cheats.prank(pauser); +// registryCoordinator.pause(2 ** PAUSED_UPDATE_OPERATOR); + +// address[][] memory operatorsToUpdate = new address[][](1); +// address[] memory operatorArray = new address[](1); +// operatorArray[0] = defaultOperator; +// operatorsToUpdate[0] = operatorArray; +// bytes memory quorumNumbers = new bytes(1); +// quorumNumbers[0] = bytes1(defaultQuorumNumber); + +// cheats.expectRevert(bytes4(keccak256("CurrentlyPaused()"))); +// registryCoordinator.updateOperatorsForQuorum(operatorsToUpdate, quorumNumbers); +// } + +// function test_updateOperatorsForQuorum_revert_nonexistentQuorum() public { +// _deployMockEigenLayerAndAVS(10); +// bytes memory quorumNumbersNotCreated = new bytes(1); +// quorumNumbersNotCreated[0] = 0x0B; +// address[][] memory operatorsToUpdate = new address[][](1); + +// cheats.prank(defaultOperator); +// cheats.expectRevert(BitmapUtils.BitmapValueTooLarge.selector); +// registryCoordinator.updateOperatorsForQuorum(operatorsToUpdate, quorumNumbersNotCreated); +// } + +// function test_updateOperatorsForQuorum_revert_inputLengthMismatch() public { +// address[][] memory operatorsToUpdate = new address[][](2); +// bytes memory quorumNumbers = new bytes(1); +// quorumNumbers[0] = bytes1(defaultQuorumNumber); + +// cheats.expectRevert(bytes4(keccak256("InputLengthMismatch()"))); +// registryCoordinator.updateOperatorsForQuorum(operatorsToUpdate, quorumNumbers); +// } + +// function test_updateOperatorsForQuorum_revert_incorrectNumberOfOperators() public { +// address[][] memory operatorsToUpdate = new address[][](1); +// address[] memory operatorArray = new address[](1); +// operatorArray[0] = defaultOperator; +// operatorsToUpdate[0] = operatorArray; +// bytes memory quorumNumbers = new bytes(1); +// quorumNumbers[0] = bytes1(defaultQuorumNumber); + +// cheats.expectRevert(bytes4(keccak256("QuorumOperatorCountMismatch()"))); +// registryCoordinator.updateOperatorsForQuorum(operatorsToUpdate, quorumNumbers); +// } + +// function test_updateOperatorsForQuorum_revert_unregisteredOperator() public { +// // register the default operator +// ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; +// uint32 registrationBlockNumber = 100; +// bytes memory quorumNumbers = new bytes(1); +// quorumNumbers[0] = bytes1(defaultQuorumNumber); +// _setOperatorWeight(defaultOperator, uint8(quorumNumbers[0]), defaultStake); +// cheats.startPrank(defaultOperator); +// cheats.roll(registrationBlockNumber); +// registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); + +// address[][] memory operatorsToUpdate = new address[][](1); +// address[] memory operatorArray = new address[](1); +// // use an unregistered operator address as input +// operatorArray[0] = _incrementAddress(defaultOperator, 1); +// operatorsToUpdate[0] = operatorArray; + +// cheats.expectRevert(bytes4(keccak256("NotRegisteredForQuorum()"))); +// registryCoordinator.updateOperatorsForQuorum(operatorsToUpdate, quorumNumbers); +// } + +// // note: there is not an explicit check for duplicates, as checking for explicit ordering covers this +// function test_updateOperatorsForQuorum_revert_duplicateOperator(uint256 pseudoRandomNumber) public { +// // register 2 operators +// uint32 numOperators = 2; +// uint32 registrationBlockNumber = 200; +// bytes memory quorumNumbers = new bytes(1); +// quorumNumbers[0] = bytes1(defaultQuorumNumber); +// uint256 quorumBitmap = BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers); +// cheats.roll(registrationBlockNumber); +// for (uint i = 0; i < numOperators; i++) { +// BN254.G1Point memory pubKey = BN254.hashToG1(keccak256(abi.encodePacked(pseudoRandomNumber, i))); +// address operator = _incrementAddress(defaultOperator, i); + +// _registerOperatorWithCoordinator(operator, quorumBitmap, pubKey); +// } + +// address[][] memory operatorsToUpdate = new address[][](1); +// address[] memory operatorArray = new address[](2); +// // use the same operator address twice as input +// operatorArray[0] = defaultOperator; +// operatorArray[1] = defaultOperator; +// operatorsToUpdate[0] = operatorArray; + +// // note: there is not an explicit check for duplicates, as checking for explicit ordering covers this +// cheats.expectRevert(bytes4(keccak256("NotSorted()"))); +// registryCoordinator.updateOperatorsForQuorum(operatorsToUpdate, quorumNumbers); +// } + +// function test_updateOperatorsForQuorum_revert_incorrectListOrder(uint256 pseudoRandomNumber) public { +// // register 2 operators +// uint32 numOperators = 2; +// uint32 registrationBlockNumber = 200; +// bytes memory quorumNumbers = new bytes(1); +// quorumNumbers[0] = bytes1(defaultQuorumNumber); +// uint256 quorumBitmap = BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers); +// cheats.roll(registrationBlockNumber); +// for (uint i = 0; i < numOperators; i++) { +// BN254.G1Point memory pubKey = BN254.hashToG1(keccak256(abi.encodePacked(pseudoRandomNumber, i))); +// address operator = _incrementAddress(defaultOperator, i); + +// _registerOperatorWithCoordinator(operator, quorumBitmap, pubKey); +// } + +// address[][] memory operatorsToUpdate = new address[][](1); +// address[] memory operatorArray = new address[](2); +// // order the operator addresses in descending order, instead of ascending order +// operatorArray[0] = _incrementAddress(defaultOperator, 1); +// operatorArray[1] = defaultOperator; +// operatorsToUpdate[0] = operatorArray; + +// cheats.expectRevert(bytes4(keccak256("NotSorted()"))); +// registryCoordinator.updateOperatorsForQuorum(operatorsToUpdate, quorumNumbers); +// } + +// function test_updateOperatorsForQuorum_singleOperator() public { +// // register the default operator +// ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; +// uint32 registrationBlockNumber = 100; +// bytes memory quorumNumbers = new bytes(1); +// quorumNumbers[0] = bytes1(defaultQuorumNumber); +// _setOperatorWeight(defaultOperator, uint8(quorumNumbers[0]), defaultStake); +// cheats.startPrank(defaultOperator); +// cheats.roll(registrationBlockNumber); +// registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); + +// address[][] memory operatorsToUpdate = new address[][](1); +// address[] memory operatorArray = new address[](1); +// operatorArray[0] = defaultOperator; +// operatorsToUpdate[0] = operatorArray; + +// uint256 quorumUpdateBlockNumberBefore = registryCoordinator.quorumUpdateBlockNumber(defaultQuorumNumber); +// require(quorumUpdateBlockNumberBefore != block.number, "bad test setup!"); + +// cheats.expectEmit(true, true, true, true, address(registryCoordinator)); +// emit QuorumBlockNumberUpdated(defaultQuorumNumber, block.number); +// registryCoordinator.updateOperatorsForQuorum(operatorsToUpdate, quorumNumbers); + +// uint256 quorumUpdateBlockNumberAfter = registryCoordinator.quorumUpdateBlockNumber(defaultQuorumNumber); +// assertEq(quorumUpdateBlockNumberAfter, block.number, "quorumUpdateBlockNumber not set correctly"); +// } + +// function test_updateOperatorsForQuorum_twoOperators(uint256 pseudoRandomNumber) public { +// // register 2 operators +// uint32 numOperators = 2; +// uint32 registrationBlockNumber = 200; +// bytes memory quorumNumbers = new bytes(1); +// quorumNumbers[0] = bytes1(defaultQuorumNumber); +// uint256 quorumBitmap = BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers); +// cheats.roll(registrationBlockNumber); +// for (uint i = 0; i < numOperators; i++) { +// BN254.G1Point memory pubKey = BN254.hashToG1(keccak256(abi.encodePacked(pseudoRandomNumber, i))); +// address operator = _incrementAddress(defaultOperator, i); + +// _registerOperatorWithCoordinator(operator, quorumBitmap, pubKey); +// } + +// address[][] memory operatorsToUpdate = new address[][](1); +// address[] memory operatorArray = new address[](2); +// // order the operator addresses in descending order, instead of ascending order +// operatorArray[0] = defaultOperator; +// operatorArray[1] = _incrementAddress(defaultOperator, 1); +// operatorsToUpdate[0] = operatorArray; + +// uint256 quorumUpdateBlockNumberBefore = registryCoordinator.quorumUpdateBlockNumber(defaultQuorumNumber); +// require(quorumUpdateBlockNumberBefore != block.number, "bad test setup!"); + +// cheats.expectEmit(true, true, true, true, address(registryCoordinator)); +// emit QuorumBlockNumberUpdated(defaultQuorumNumber, block.number); +// registryCoordinator.updateOperatorsForQuorum(operatorsToUpdate, quorumNumbers); + +// uint256 quorumUpdateBlockNumberAfter = registryCoordinator.quorumUpdateBlockNumber(defaultQuorumNumber); +// assertEq(quorumUpdateBlockNumberAfter, block.number, "quorumUpdateBlockNumber not set correctly"); +// } + +// // @notice tests that the internal `_updateOperatorBitmap` function works as expected, for fuzzed inputs +// function testFuzz_updateOperatorBitmapInternal_noPreviousEntries(uint192 newBitmap) public { +// registryCoordinator._updateOperatorBitmapExternal(defaultOperatorId, newBitmap); +// assertEq( +// keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0))), +// keccak256(abi.encode(ISlashingRegistryCoordinator.QuorumBitmapUpdate({ +// quorumBitmap: uint192(newBitmap), +// updateBlockNumber: uint32(block.number), +// nextUpdateBlockNumber: 0 +// }))) +// ); +// } + +// // @notice tests that the internal `_updateOperatorBitmap` function works as expected, for fuzzed inputs +// function testFuzz_updateOperatorBitmapInternal_previousEntryInCurrentBlock(uint192 newBitmap) public { +// uint192 pastBitmap = 1; +// testFuzz_updateOperatorBitmapInternal_noPreviousEntries(pastBitmap); + +// registryCoordinator._updateOperatorBitmapExternal(defaultOperatorId, newBitmap); +// assertEq( +// keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0))), +// keccak256(abi.encode(ISlashingRegistryCoordinator.QuorumBitmapUpdate({ +// quorumBitmap: uint192(newBitmap), +// updateBlockNumber: uint32(block.number), +// nextUpdateBlockNumber: 0 +// }))) +// ); +// } + +// // @notice tests that the internal `_updateOperatorBitmap` function works as expected, for fuzzed inputs +// function testFuzz_updateOperatorBitmapInternal_previousEntryInPastBlock(uint192 newBitmap) public { +// uint192 pastBitmap = 1; +// testFuzz_updateOperatorBitmapInternal_noPreviousEntries(pastBitmap); + +// // advance the block number +// uint256 previousBlockNumber = block.number; +// cheats.roll(previousBlockNumber + 1); + +// registryCoordinator._updateOperatorBitmapExternal(defaultOperatorId, newBitmap); +// assertEq( +// keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0))), +// keccak256(abi.encode(ISlashingRegistryCoordinator.QuorumBitmapUpdate({ +// quorumBitmap: uint192(pastBitmap), +// updateBlockNumber: uint32(previousBlockNumber), +// nextUpdateBlockNumber: uint32(block.number) +// }))) +// ); +// assertEq( +// keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 1))), +// keccak256(abi.encode(ISlashingRegistryCoordinator.QuorumBitmapUpdate({ +// quorumBitmap: uint192(newBitmap), +// updateBlockNumber: uint32(block.number), +// nextUpdateBlockNumber: 0 +// }))) +// ); +// } +// } + +// contract RegistryCoordinatorUnitTests_BeforeMigration is RegistryCoordinatorUnitTests { +// function test_registerALMHook_Reverts() public { +// cheats.prank(address(registryCoordinator.allocationManager())); +// cheats.expectRevert(); +// registryCoordinator.registerOperator(defaultOperator, new uint32[](0), abi.encode(defaultSocket, pubkeyRegistrationParams)); +// } + +// function test_deregisterALMHook_Reverts() public { +// uint32[] memory operatorSetIds = new uint32[](1); +// operatorSetIds[0] = 0; +// cheats.prank(address(registryCoordinator.allocationManager())); +// cheats.expectRevert(); +// registryCoordinator.deregisterOperator(defaultOperator, operatorSetIds); +// } + +// function test_CreateTotalDelegatedStakeQuorum() public { +// _deployMockEigenLayerAndAVS(0); +// // Set up test params +// ISlashingRegistryCoordinator.OperatorSetParam memory operatorSetParams = ISlashingRegistryCoordinator.OperatorSetParam({ +// maxOperatorCount: 10, +// kickBIPsOfOperatorStake: 0, +// kickBIPsOfTotalStake: 0 +// }); +// uint96 minimumStake = 100; +// IStakeRegistry.StrategyParams[] memory strategyParams = new IStakeRegistry.StrategyParams[](1); +// strategyParams[0] = IStakeRegistry.StrategyParams({ +// strategy: IStrategy(address(0x1)), +// multiplier: 1000 +// }); + +// // Get initial quorum count +// uint8 initialQuorumCount = registryCoordinator.quorumCount(); + +// // Create quorum with total delegated stake type +// cheats.prank(registryCoordinatorOwner); +// registryCoordinator.createTotalDelegatedStakeQuorum( +// operatorSetParams, +// minimumStake, +// strategyParams +// ); + +// // Verify quorum was created +// assertEq(registryCoordinator.quorumCount(), initialQuorumCount + 1); + +// // Verify quorum params were set correctly +// ISlashingRegistryCoordinator.OperatorSetParam memory storedParams = registryCoordinator.getOperatorSetParams(initialQuorumCount); +// assertEq(storedParams.maxOperatorCount, operatorSetParams.maxOperatorCount); +// assertEq(storedParams.kickBIPsOfOperatorStake, operatorSetParams.kickBIPsOfOperatorStake); +// assertEq(storedParams.kickBIPsOfTotalStake, operatorSetParams.kickBIPsOfTotalStake); +// } + +// function test_CreateSlashableStakeQuorum_Reverts() public { +// _deployMockEigenLayerAndAVS(0); +// ISlashingRegistryCoordinator.OperatorSetParam memory operatorSetParams = ISlashingRegistryCoordinator.OperatorSetParam({ +// maxOperatorCount: 10, +// kickBIPsOfOperatorStake: 0, +// kickBIPsOfTotalStake: 0 +// }); +// uint96 minimumStake = 100; +// IStakeRegistry.StrategyParams[] memory strategyParams = new IStakeRegistry.StrategyParams[](1); +// strategyParams[0] = IStakeRegistry.StrategyParams({ +// strategy: IStrategy(address(0x1)), +// multiplier: 1000 +// }); +// uint32 lookAheadPeriod = 100; + +// // Attempt to create quorum with slashable stake type before enabling operator sets +// cheats.prank(registryCoordinatorOwner); +// cheats.expectRevert(); +// registryCoordinator.createSlashableStakeQuorum( +// operatorSetParams, +// minimumStake, +// strategyParams, +// lookAheadPeriod +// ); +// } + +// function test_MigrateToOperatorSets() public { +// _deployMockEigenLayerAndAVS(0); +// cheats.prank(registryCoordinatorOwner); +// registryCoordinator.enableOperatorSets(); +// assertTrue(registryCoordinator.isOperatorSetAVS()); +// } +// } + +// contract RegistryCoordinatorUnitTests_AfterMigration is RegistryCoordinatorUnitTests { +// function test_MigrateToOperatorSets() public { +// cheats.prank(registryCoordinatorOwner); +// registryCoordinator.enableOperatorSets(); +// assertTrue(registryCoordinator.isOperatorSetAVS()); +// } + +// function test_M2_Deregister() public { +// vm.skip(true); +// /// Create 2 M2 quorums +// _deployMockEigenLayerAndAVS(2); + +// address operatorToRegister = address(420); + +// ISignatureUtils.SignatureWithSaltAndExpiry memory emptySignature = ISignatureUtils.SignatureWithSaltAndExpiry({ +// signature: new bytes(0), +// salt: bytes32(0), +// expiry: 0 +// }); + +// IBLSApkRegistry.PubkeyRegistrationParams memory operatorRegisterApkParams = IBLSApkRegistry.PubkeyRegistrationParams({ +// pubkeyRegistrationSignature: BN254.G1Point({ +// X: 0, +// Y: 0 +// }), +// pubkeyG1: BN254.G1Point({ +// X: 0, +// Y: 0 +// }), +// pubkeyG2: BN254.G2Point({ +// X: [uint256(0), uint256(0)], +// Y: [uint256(0), uint256(0)] +// }) +// }); + +// string memory socket = "socket"; + +// // register for quorum 0 +// vm.prank(operatorToRegister); +// registryCoordinator.registerOperator( +// new bytes(1), // Convert 0 to bytes1 first +// socket, +// operatorRegisterApkParams, +// emptySignature +// ); + +// /// migrate to operator sets +// registryCoordinator.enableOperatorSets(); + +// /// Deregistration for m2 should for the first two operator sets +// vm.prank(defaultOperator); +// registryCoordinator.deregisterOperator(new bytes(1)); + +// // Verify operator was deregistered by checking their bitmap is empty +// bytes32 operatorId = registryCoordinator.getOperatorId(operatorToRegister); +// uint192 bitmap = registryCoordinator.getCurrentQuorumBitmap(operatorId); +// assertEq(bitmap, 0, "Operator bitmap should be empty after deregistration"); + +// // Verify operator status is NEVER_REGISTERED +// ISlashingRegistryCoordinator.OperatorStatus status = registryCoordinator.getOperatorStatus(operatorToRegister); +// assertEq(uint8(status), uint8(ISlashingRegistryCoordinator.OperatorStatus.NEVER_REGISTERED), "Operator status should be NEVER_REGISTERED"); +// } + +// function test_M2_Register_Reverts() public { +// cheats.prank(registryCoordinatorOwner); +// registryCoordinator.enableOperatorSets(); + +// bytes memory quorumNumbers = new bytes(1); +// quorumNumbers[0] = bytes1(uint8(0)); +// IBLSApkRegistry.PubkeyRegistrationParams memory params; +// ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature; + +// cheats.expectRevert(); +// registryCoordinator.registerOperator( +// quorumNumbers, +// defaultSocket, +// params, +// operatorSignature +// ); +// } + +// function test_createSlashableStakeQuorum() public { +// // Deploy with 0 quorums +// _deployMockEigenLayerAndAVS(0); + +// // Enable operator sets first +// cheats.prank(registryCoordinatorOwner); +// registryCoordinator.enableOperatorSets(); + +// // Create quorum params +// ISlashingRegistryCoordinator.OperatorSetParam memory operatorSetParams = ISlashingRegistryCoordinator.OperatorSetParam({ +// maxOperatorCount: 10, +// kickBIPsOfOperatorStake: 1000, +// kickBIPsOfTotalStake: 100 +// }); +// uint96 minimumStake = 100; +// IStakeRegistry.StrategyParams[] memory strategyParams = new IStakeRegistry.StrategyParams[](1); +// strategyParams[0] = IStakeRegistry.StrategyParams({ +// strategy: IStrategy(address(1)), +// multiplier: 1 +// }); +// uint32 lookAheadPeriod = 100; + +// // Create slashable stake quorum +// cheats.prank(registryCoordinatorOwner); +// registryCoordinator.createSlashableStakeQuorum( +// operatorSetParams, +// minimumStake, +// strategyParams, +// lookAheadPeriod +// ); +// } + +// function test_createTotalDelegatedStakeQuorum() public { +// // Deploy with 0 quorums +// _deployMockEigenLayerAndAVS(0); + +// // Enable operator sets first +// cheats.prank(registryCoordinatorOwner); +// registryCoordinator.enableOperatorSets(); + +// // Create quorum params +// ISlashingRegistryCoordinator.OperatorSetParam memory operatorSetParams = ISlashingRegistryCoordinator.OperatorSetParam({ +// maxOperatorCount: 10, +// kickBIPsOfOperatorStake: 1000, +// kickBIPsOfTotalStake: 100 +// }); +// uint96 minimumStake = 100; +// IStakeRegistry.StrategyParams[] memory strategyParams = new IStakeRegistry.StrategyParams[](1); +// strategyParams[0] = IStakeRegistry.StrategyParams({ +// strategy: IStrategy(address(1)), +// multiplier: 10000 +// }); + +// // Create total delegated stake quorum +// cheats.prank(registryCoordinatorOwner); +// registryCoordinator.createTotalDelegatedStakeQuorum( +// operatorSetParams, +// minimumStake, +// strategyParams +// ); +// } + +// function test_registerHook() public { +// vm.skip(true); + +// _deployMockEigenLayerAndAVS(0); +// // Enable operator sets first +// cheats.prank(registryCoordinatorOwner); +// registryCoordinator.enableOperatorSets(); + +// // Create quorum params +// ISlashingRegistryCoordinator.OperatorSetParam memory operatorSetParams = ISlashingRegistryCoordinator.OperatorSetParam({ +// maxOperatorCount: 10, +// kickBIPsOfOperatorStake: 1000, +// kickBIPsOfTotalStake: 100 +// }); + +// uint96 minimumStake = 100; +// IStakeRegistry.StrategyParams[] memory strategyParams = new IStakeRegistry.StrategyParams[](1); +// strategyParams[0] = IStakeRegistry.StrategyParams({ +// strategy: IStrategy(address(1)), +// multiplier: 10000 +// }); + +// // Create total delegated stake quorum +// cheats.prank(registryCoordinatorOwner); +// registryCoordinator.createTotalDelegatedStakeQuorum( +// operatorSetParams, +// 0, +// strategyParams +// ); + + +// uint32[] memory operatorSetIds = new uint32[](1); +// operatorSetIds[0] = 0; + +// string memory socket = "socket"; +// IBLSApkRegistry.PubkeyRegistrationParams memory params; +// // TODO: +// // params = IBLSApkRegistry.PubkeyRegistrationParams({ +// // pubkeyG1: defaultPubKey, +// // pubkeyG2: defaultPubKeyG2, +// // pubkeySignature: defaultPubKeySignature +// // }); + +// bytes memory data = abi.encode(socket, params); + +// cheats.prank(address(registryCoordinator.allocationManager())); +// registryCoordinator.registerOperator(defaultOperator, operatorSetIds, data); +// } + +// function test_registerHook_WithChurn() public { +// _deployMockEigenLayerAndAVS(0); +// // Enable operator sets first +// cheats.prank(registryCoordinatorOwner); +// registryCoordinator.enableOperatorSets(); + +// // Create quorum params +// ISlashingRegistryCoordinator.OperatorSetParam memory operatorSetParams = ISlashingRegistryCoordinator.OperatorSetParam({ +// maxOperatorCount: 10, +// kickBIPsOfOperatorStake: 1000, +// kickBIPsOfTotalStake: 100 +// }); + +// uint96 minimumStake = 100; +// IStakeRegistry.StrategyParams[] memory strategyParams = new IStakeRegistry.StrategyParams[](1); +// strategyParams[0] = IStakeRegistry.StrategyParams({ +// strategy: IStrategy(address(1)), +// multiplier: 10000 +// }); + +// // Create total delegated stake quorum +// cheats.prank(registryCoordinatorOwner); +// registryCoordinator.createTotalDelegatedStakeQuorum( +// operatorSetParams, +// 0, +// strategyParams +// ); + +// uint32[] memory operatorSetIds = new uint32[](1); +// operatorSetIds[0] = 0; + +// string memory socket = "socket"; +// IBLSApkRegistry.PubkeyRegistrationParams memory params; +// // TODO: +// // params = IBLSApkRegistry.PubkeyRegistrationParams({ +// // pubkeyG1: defaultPubKey, +// // pubkeyG2: defaultPubKeyG2, +// // pubkeySignature: defaultPubKeySignature +// // }); + +// ISlashingRegistryCoordinator.OperatorKickParam[] memory operatorKickParams = new ISlashingRegistryCoordinator.OperatorKickParam[](1); +// operatorKickParams[0] = ISlashingRegistryCoordinator.OperatorKickParam({ +// operator: address(0x1), +// quorumNumber: 0 +// }); + +// ISignatureUtils.SignatureWithSaltAndExpiry memory churnApproverSignature; +// ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature; + +// bytes memory registerParams = abi.encode( +// socket, +// params, +// operatorKickParams, +// churnApproverSignature, +// operatorSignature +// ); + +// // Prank as allocation manager and call register hook +// cheats.prank(address(registryCoordinator.allocationManager())); +// registryCoordinator.registerOperator(defaultOperator, operatorSetIds, registerParams); +// } + +// function test_updateStakesForQuorum() public { +// vm.skip(true); +// _deployMockEigenLayerAndAVS(0); + +// ISlashingRegistryCoordinator.OperatorSetParam memory operatorSetParams = ISlashingRegistryCoordinator.OperatorSetParam({ +// maxOperatorCount: defaultMaxOperatorCount, +// kickBIPsOfOperatorStake: defaultKickBIPsOfOperatorStake, +// kickBIPsOfTotalStake: defaultKickBIPsOfTotalStake +// }); + +// uint96 minimumStake = 100; +// IStakeRegistry.StrategyParams[] memory strategyParams = new IStakeRegistry.StrategyParams[](1); +// strategyParams[0] = IStakeRegistry.StrategyParams({ +// strategy: IStrategy(address(1)), +// multiplier: 10000 +// }); + +// cheats.prank(registryCoordinatorOwner); +// registryCoordinator.createTotalDelegatedStakeQuorum( +// operatorSetParams, +// minimumStake, +// strategyParams +// ); + +// uint256 quorumBitmap = 0; + +// // TODO: register actually and update stakes +// } + +// function test_deregisterHook() public { + +// _deployMockEigenLayerAndAVS(0); +// // Enable operator sets first +// cheats.prank(registryCoordinatorOwner); +// registryCoordinator.enableOperatorSets(); + +// // Create quorum params +// ISlashingRegistryCoordinator.OperatorSetParam memory operatorSetParams = ISlashingRegistryCoordinator.OperatorSetParam({ +// maxOperatorCount: 10, +// kickBIPsOfOperatorStake: 1000, +// kickBIPsOfTotalStake: 100 +// }); + +// uint96 minimumStake = 100; +// IStakeRegistry.StrategyParams[] memory strategyParams = new IStakeRegistry.StrategyParams[](1); +// strategyParams[0] = IStakeRegistry.StrategyParams({ +// strategy: IStrategy(address(1)), +// multiplier: 10000 +// }); + +// // Create total delegated stake quorum +// cheats.prank(registryCoordinatorOwner); +// registryCoordinator.createTotalDelegatedStakeQuorum( +// operatorSetParams, +// 0, +// strategyParams +// ); + +// // Prank as allocation manager and call register hook +// uint32[] memory operatorSetIds = new uint32[](1); +// operatorSetIds[0] = 0; + +// string memory socket = "socket"; +// IBLSApkRegistry.PubkeyRegistrationParams memory params; +// // TODO: +// // params = IBLSApkRegistry.PubkeyRegistrationParams({ +// // pubkeyG1: defaultPubKey, +// // pubkeyG2: defaultPubKeyG2, +// // pubkeySignature: defaultPubKeySignature +// // }); + +// bytes memory data = abi.encode(socket, params); + + +// cheats.startPrank(address(registryCoordinator.allocationManager())); +// registryCoordinator.registerOperator(defaultOperator, operatorSetIds, data); + +// registryCoordinator.deregisterOperator(defaultOperator, operatorSetIds); + +// cheats.stopPrank(); +// } + +// function test_registerHook_Reverts_WhenNotALM() public { + +// _deployMockEigenLayerAndAVS(0); +// // Enable operator sets first +// cheats.prank(registryCoordinatorOwner); +// registryCoordinator.enableOperatorSets(); + +// // Create quorum params +// ISlashingRegistryCoordinator.OperatorSetParam memory operatorSetParams = ISlashingRegistryCoordinator.OperatorSetParam({ +// maxOperatorCount: 10, +// kickBIPsOfOperatorStake: 1000, +// kickBIPsOfTotalStake: 100 +// }); + +// uint96 minimumStake = 100; +// IStakeRegistry.StrategyParams[] memory strategyParams = new IStakeRegistry.StrategyParams[](1); +// strategyParams[0] = IStakeRegistry.StrategyParams({ +// strategy: IStrategy(address(1)), +// multiplier: 10000 +// }); + +// // Create total delegated stake quorum +// cheats.prank(registryCoordinatorOwner); +// registryCoordinator.createTotalDelegatedStakeQuorum( +// operatorSetParams, +// 0, +// strategyParams +// ); + + +// uint32[] memory operatorSetIds = new uint32[](1); +// operatorSetIds[0] = 0; + +// string memory socket = "socket"; +// IBLSApkRegistry.PubkeyRegistrationParams memory params; +// // TODO: +// // params = IBLSApkRegistry.PubkeyRegistrationParams({ +// // pubkeyG1: defaultPubKey, +// // pubkeyG2: defaultPubKeyG2, +// // pubkeySignature: defaultPubKeySignature +// // }); + +// bytes memory data = abi.encode(socket, params); + +// vm.expectRevert(); +// registryCoordinator.registerOperator(defaultOperator, operatorSetIds, data); +// } + +// function test_deregisterHook_Reverts_WhenNotALM() public { + +// _deployMockEigenLayerAndAVS(0); +// // Enable operator sets first +// cheats.prank(registryCoordinatorOwner); +// registryCoordinator.enableOperatorSets(); + +// // Create quorum params +// ISlashingRegistryCoordinator.OperatorSetParam memory operatorSetParams = ISlashingRegistryCoordinator.OperatorSetParam({ +// maxOperatorCount: 10, +// kickBIPsOfOperatorStake: 1000, +// kickBIPsOfTotalStake: 100 +// }); + +// uint96 minimumStake = 100; +// IStakeRegistry.StrategyParams[] memory strategyParams = new IStakeRegistry.StrategyParams[](1); +// strategyParams[0] = IStakeRegistry.StrategyParams({ +// strategy: IStrategy(address(1)), +// multiplier: 10000 +// }); + +// // Create total delegated stake quorum +// cheats.prank(registryCoordinatorOwner); +// registryCoordinator.createTotalDelegatedStakeQuorum( +// operatorSetParams, +// 0, +// strategyParams +// ); + +// // Prank as allocation manager and call register hook +// uint32[] memory operatorSetIds = new uint32[](1); +// operatorSetIds[0] = 0; + +// string memory socket = "socket"; +// IBLSApkRegistry.PubkeyRegistrationParams memory params; +// // TODO: +// // params = IBLSApkRegistry.PubkeyRegistrationParams({ +// // pubkeyG1: defaultPubKey, +// // pubkeyG2: defaultPubKeyG2, +// // pubkeySignature: defaultPubKeySignature +// // }); + +// bytes memory data = abi.encode(socket, params); + + +// cheats.prank(address(registryCoordinator.allocationManager())); +// registryCoordinator.registerOperator(defaultOperator, operatorSetIds, data); +// cheats.stopPrank(); + +// cheats.expectRevert(); +// registryCoordinator.deregisterOperator(defaultOperator, operatorSetIds); + +// } + +// function test_DeregisterHook_Reverts_WhenM2Quorum() public { +// vm.skip(true); +// } + +// function test_registerHook_Reverts_WhenM2Quorum() public { +// vm.skip(true); +// } + +// } diff --git a/test/unit/StakeRegistryUnit.t.sol b/test/unit/StakeRegistryUnit.t.sol index ef479d7e..2352dfeb 100644 --- a/test/unit/StakeRegistryUnit.t.sol +++ b/test/unit/StakeRegistryUnit.t.sol @@ -56,11 +56,7 @@ contract StakeRegistryUnitTests is MockAVSDeployer, IStakeRegistryEvents { ); stakeRegistryImplementation = new StakeRegistryHarness( - IRegistryCoordinator(address(registryCoordinator)), - delegationMock, - avsDirectoryMock, - allocationManager, - serviceManager + ISlashingRegistryCoordinator(address(registryCoordinator)), delegationMock, avsDirectoryMock, allocationManager ); stakeRegistry = StakeRegistryHarness( @@ -589,7 +585,7 @@ contract StakeRegistryUnitTests_Config is StakeRegistryUnitTests { uint96 minimumStake, IStakeRegistry.StrategyParams[] memory strategyParams ) public { - cheats.expectRevert(IStakeRegistryErrors.OnlyRegistryCoordinator.selector); + cheats.expectRevert(IStakeRegistryErrors.OnlySlashingRegistryCoordinator.selector); stakeRegistry.initializeDelegatedStakeQuorum(quorumNumber, minimumStake, strategyParams); } @@ -732,7 +728,7 @@ contract StakeRegistryUnitTests_Config is StakeRegistryUnitTests { uint8 quorumNumber, uint96 minimumStakeForQuorum ) public fuzzOnlyInitializedQuorums(quorumNumber) { - cheats.expectRevert(IStakeRegistryErrors.OnlyRegistryCoordinatorOwner.selector); + cheats.expectRevert(IStakeRegistryErrors.OnlySlashingRegistryCoordinatorOwner.selector); stakeRegistry.setMinimumStakeForQuorum(quorumNumber, minimumStakeForQuorum); } @@ -770,7 +766,7 @@ contract StakeRegistryUnitTests_Config is StakeRegistryUnitTests { uint8 quorumNumber, IStakeRegistry.StrategyParams[] memory strategyParams ) public fuzzOnlyInitializedQuorums(quorumNumber) { - cheats.expectRevert(IStakeRegistryErrors.OnlyRegistryCoordinatorOwner.selector); + cheats.expectRevert(IStakeRegistryErrors.OnlySlashingRegistryCoordinatorOwner.selector); stakeRegistry.addStrategies(quorumNumber, strategyParams); } @@ -869,7 +865,7 @@ contract StakeRegistryUnitTests_Config is StakeRegistryUnitTests { uint8 quorumNumber, uint256[] memory indicesToRemove ) public fuzzOnlyInitializedQuorums(quorumNumber) { - cheats.expectRevert(IStakeRegistryErrors.OnlyRegistryCoordinatorOwner.selector); + cheats.expectRevert(IStakeRegistryErrors.OnlySlashingRegistryCoordinatorOwner.selector); stakeRegistry.removeStrategies(quorumNumber, indicesToRemove); } @@ -970,7 +966,7 @@ contract StakeRegistryUnitTests_Config is StakeRegistryUnitTests { uint256[] calldata strategyIndices, uint96[] calldata newMultipliers ) public fuzzOnlyInitializedQuorums(quorumNumber) { - cheats.expectRevert(IStakeRegistryErrors.OnlyRegistryCoordinatorOwner.selector); + cheats.expectRevert(IStakeRegistryErrors.OnlySlashingRegistryCoordinatorOwner.selector); stakeRegistry.modifyStrategyParams(quorumNumber, strategyIndices, newMultipliers); } @@ -1062,7 +1058,7 @@ contract StakeRegistryUnitTests_Register is StakeRegistryUnitTests { function test_registerOperator_Revert_WhenNotRegistryCoordinator() public { (address operator, bytes32 operatorId) = _selectNewOperator(); - cheats.expectRevert(IStakeRegistryErrors.OnlyRegistryCoordinator.selector); + cheats.expectRevert(IStakeRegistryErrors.OnlySlashingRegistryCoordinator.selector); stakeRegistry.registerOperator(operator, operatorId, initializedQuorumBytes); } @@ -1413,7 +1409,7 @@ contract StakeRegistryUnitTests_Deregister is StakeRegistryUnitTests { fuzzy_addtlStake: 0 }); - cheats.expectRevert(IStakeRegistryErrors.OnlyRegistryCoordinator.selector); + cheats.expectRevert(IStakeRegistryErrors.OnlySlashingRegistryCoordinator.selector); stakeRegistry.deregisterOperator(setup.operatorId, setup.quorumsToRemove); } @@ -1777,7 +1773,7 @@ contract StakeRegistryUnitTests_StakeUpdates is StakeRegistryUnitTests { UpdateSetup memory setup = _fuzz_setupUpdateOperatorStake({registeredFor: initializedQuorumBitmap, fuzzy_Delta: 0}); - cheats.expectRevert(IStakeRegistryErrors.OnlyRegistryCoordinator.selector); + cheats.expectRevert(IStakeRegistryErrors.OnlySlashingRegistryCoordinator.selector); stakeRegistry.updateOperatorStake(setup.operator, setup.operatorId, setup.quorumNumbers); } diff --git a/test/utils/MockAVSDeployer.sol b/test/utils/MockAVSDeployer.sol index a593e23e..ab28b26c 100644 --- a/test/utils/MockAVSDeployer.sol +++ b/test/utils/MockAVSDeployer.sol @@ -11,6 +11,7 @@ import {BitmapUtils} from "../../src/libraries/BitmapUtils.sol"; import {BN254} from "../../src/libraries/BN254.sol"; import {OperatorStateRetriever} from "../../src/OperatorStateRetriever.sol"; +import {SlashingRegistryCoordinator} from "../../src/SlashingRegistryCoordinator.sol"; import {RegistryCoordinator} from "../../src/RegistryCoordinator.sol"; import {RegistryCoordinatorHarness} from "../harnesses/RegistryCoordinatorHarness.t.sol"; import {BLSApkRegistry} from "../../src/BLSApkRegistry.sol"; @@ -21,6 +22,8 @@ import {IBLSApkRegistry} from "../../src/interfaces/IBLSApkRegistry.sol"; import {IStakeRegistry} from "../../src/interfaces/IStakeRegistry.sol"; import {IIndexRegistry} from "../../src/interfaces/IIndexRegistry.sol"; import {IRegistryCoordinator} from "../../src/interfaces/IRegistryCoordinator.sol"; + +import {ISlashingRegistryCoordinator} from "../../src/interfaces/ISlashingRegistryCoordinator.sol"; import {IServiceManager} from "../../src/interfaces/IServiceManager.sol"; import {StrategyManagerMock} from "eigenlayer-contracts/src/test/mocks/StrategyManagerMock.sol"; @@ -115,7 +118,7 @@ contract MockAVSDeployer is Test { uint16 defaultKickBIPsOfTotalStake = 150; uint8 numQuorums = 192; - IRegistryCoordinator.OperatorSetParam[] operatorSetParams; + ISlashingRegistryCoordinator.OperatorSetParam[] operatorSetParams; uint8 maxQuorumsToRegisterFor = 4; uint256 maxOperatorsToRegister = 4; @@ -208,13 +211,8 @@ contract MockAVSDeployer is Test { cheats.startPrank(proxyAdminOwner); - stakeRegistryImplementation = new StakeRegistryHarness( - IRegistryCoordinator(registryCoordinator), - delegationMock, - avsDirectory, - allocationManagerMock, - serviceManager - ); + stakeRegistryImplementation = + new StakeRegistryHarness(ISlashingRegistryCoordinator(registryCoordinator), delegationMock, avsDirectory, allocationManagerMock); proxyAdmin.upgrade( TransparentUpgradeableProxy(payable(address(stakeRegistry))), address(stakeRegistryImplementation) @@ -290,11 +288,26 @@ contract MockAVSDeployer is Test { pauserRegistry ); { + proxyAdmin.upgradeAndCall( + TransparentUpgradeableProxy(payable(address(registryCoordinator))), + address(registryCoordinatorImplementation), + abi.encodeCall( + SlashingRegistryCoordinator.initialize, + ( + registryCoordinatorOwner, // _initialOwner + churnApprover, // _churnApprover + ejector, // _ejector + 0, // _initialPausedStatus + address(serviceManager) // _accountIdentifier + ) + ) + ); + delete operatorSetParams; for (uint256 i = 0; i < numQuorumsToAdd; i++) { // hard code these for now operatorSetParams.push( - IRegistryCoordinator.OperatorSetParam({ + ISlashingRegistryCoordinator.OperatorSetParam({ maxOperatorCount: defaultMaxOperatorCount, kickBIPsOfOperatorStake: defaultKickBIPsOfOperatorStake, kickBIPsOfTotalStake: defaultKickBIPsOfTotalStake @@ -302,39 +315,23 @@ contract MockAVSDeployer is Test { ); } - // Create arrays for quorum types and lookahead periods - StakeType[] memory quorumStakeTypes = new StakeType[](numQuorumsToAdd); - uint32[] memory slashableStakeQuorumLookAheadPeriods = new uint32[](numQuorumsToAdd); + cheats.stopPrank(); - // Set all quorums to TOTAL_DELEGATED type with 0 lookahead period + // add TOTAL_DELEGATED stake type quorums for (uint256 i = 0; i < numQuorumsToAdd; i++) { - quorumStakeTypes[i] = StakeType.TOTAL_DELEGATED; - slashableStakeQuorumLookAheadPeriods[i] = 0; + cheats.prank(registryCoordinator.owner()); + registryCoordinator.createTotalDelegatedStakeQuorum( + operatorSetParams[i], + minimumStakeForQuorum[i], + quorumStrategiesConsideredAndMultipliers[i] + ); } - proxyAdmin.upgradeAndCall( - TransparentUpgradeableProxy(payable(address(registryCoordinator))), - address(registryCoordinatorImplementation), - abi.encodeCall( - RegistryCoordinator.initialize, - ( - registryCoordinatorOwner, // _initialOwner - churnApprover, // _churnApprover - ejector, // _ejector - 0, // _initialPausedStatus - operatorSetParams, // _operatorSetParams - minimumStakeForQuorum, // _minimumStakes - quorumStrategiesConsideredAndMultipliers, // _strategyParams - quorumStakeTypes, // _stakeTypes - slashableStakeQuorumLookAheadPeriods // _lookAheadPeriods - ) - ) - ); } operatorStateRetriever = new OperatorStateRetriever(); - cheats.stopPrank(); + _setIsOperatorSetAVS(false); } function _labelContracts() internal { @@ -362,6 +359,24 @@ contract MockAVSDeployer is Test { vm.label(address(allocationManagerImplementation), "AllocationManagerImplementation"); } + /// @notice Overwrite RegistryCoorduinator.isOperatorSetAVS to false since by default + /// RegistryCoordinator is deployed and intitialized with isOperatorSetAVS set to true + /// This is to enable testing of RegistryCoordinator in non operator set mode. + function _setIsOperatorSetAVS(bool isOperatorSetAVS) internal { + // 1. First read the current value of the entire slot + // which holds isOperatorSetAVS and accountIdentifier + bytes32 currentSlot = cheats.load(address(registryCoordinator), bytes32(uint256(161))); + + // 2. Clear only the first byte (isOperatorSetAVS) while keeping the rest (accountIdentifier) + // We can do this by: + // i. Masking out the first byte of the current slot (keep accountIdentifier) + // ii. OR it with 0 in the first byte position (set isOperatorSetAVS to false) + bytes32 newSlot = (currentSlot & ~bytes32(uint256(0xff))) | bytes32(uint256(isOperatorSetAVS ? 0x01 : 0x00)); + + // 3. Store the modified slot + cheats.store(address(registryCoordinator), bytes32(uint256(161)), newSlot); + } + /** * @notice registers operator with coordinator */ @@ -510,7 +525,7 @@ contract MockAVSDeployer is Test { function _signOperatorChurnApproval( address registeringOperator, bytes32 registeringOperatorId, - IRegistryCoordinator.OperatorKickParam[] memory operatorKickParams, + ISlashingRegistryCoordinator.OperatorKickParam[] memory operatorKickParams, bytes32 salt, uint256 expiry ) internal view returns (ISignatureUtils.SignatureWithSaltAndExpiry memory) {