Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(op sets): register and deregister #301

Merged
Merged
61 changes: 42 additions & 19 deletions src/RegistryCoordinator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ pragma solidity ^0.8.12;

import {IPauserRegistry} from "eigenlayer-contracts/src/contracts/interfaces/IPauserRegistry.sol";
import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol";
import {AVSDirectory} from "eigenlayer-contracts/src/contracts/core/AVSDirectory.sol";
import {IAVSDirectory} from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol";
import {ISocketUpdater} from "./interfaces/ISocketUpdater.sol";
import {IBLSApkRegistry} from "./interfaces/IBLSApkRegistry.sol";
import {IStakeRegistry} from "./interfaces/IStakeRegistry.sol";
Expand Down Expand Up @@ -158,7 +158,7 @@ contract RegistryCoordinator is

require(
numOperatorsPerQuorum[i] <= _quorumParams[quorumNumber].maxOperatorCount,
"RegistryCoordinator.registerOperator: operator count exceeds maximum"
"RegistryCoordinator.registerOperator: operator exceeds max"
);
}
}
Expand Down Expand Up @@ -333,7 +333,7 @@ contract RegistryCoordinator is
// Prevent duplicate operators
require(
operator > prevOperatorAddress,
"RegistryCoordinator.updateOperatorsForQuorum: operators array must be sorted in ascending address order"
"RegistryCoordinator.updateOperatorsForQuorum: operators must be sorted"
);
}

Expand All @@ -355,7 +355,7 @@ contract RegistryCoordinator is
function updateSocket(string memory socket) external {
require(
_operatorInfo[msg.sender].status == OperatorStatus.REGISTERED,
"RegistryCoordinator.updateSocket: operator is not registered"
"RegistryCoordinator.updateSocket: not registered"
);
emit OperatorSocketUpdate(_operatorInfo[msg.sender].operatorId, socket);
}
Expand Down Expand Up @@ -486,7 +486,7 @@ contract RegistryCoordinator is
uint192(BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers, quorumCount));
uint192 currentBitmap = _currentOperatorBitmap(operatorId);
require(
!quorumsToAdd.isEmpty(), "RegistryCoordinator._registerOperator: bitmap cannot be 0"
!quorumsToAdd.isEmpty(), "RegistryCoordinator._registerOperator: bitmap empty"
);
require(
quorumsToAdd.noBitsInCommon(currentBitmap),
Expand Down Expand Up @@ -515,7 +515,18 @@ contract RegistryCoordinator is
OperatorInfo({operatorId: operatorId, status: OperatorStatus.REGISTERED});

// Register the operator with the EigenLayer core contracts via this AVS's ServiceManager
serviceManager.registerOperatorToAVS(operator, operatorSignature);
bool operatorSetAVS = IAVSDirectory(serviceManager.avsDirectory()).isOperatorSetAVS(address(serviceManager)); /// TODO: call avsdirectory
stevennevins marked this conversation as resolved.
Show resolved Hide resolved
if (operatorSetAVS){
bytes memory quorumBytes = BitmapUtils.bitmapToBytesArray(quorumsToAdd);
uint32[] memory operatorSetIds = new uint32[](quorumBytes.length);
for (uint256 i = 0; i < quorumBytes.length; i++) {
operatorSetIds[i] = uint8(quorumBytes[i]);
stevennevins marked this conversation as resolved.
Show resolved Hide resolved
}
serviceManager.registerOperatorToOperatorSets(operator, operatorSetIds, operatorSignature);

} else {
serviceManager.registerOperatorToAVS(operator, operatorSignature);
}

emit OperatorRegistered(operator, operatorId);
}
Expand All @@ -534,7 +545,7 @@ contract RegistryCoordinator is
* @dev Reverts if the caller is not the ejector
*/
function _checkEjector() internal view {
require(msg.sender == ejector, "RegistryCoordinator.onlyEjector: caller is not the ejector");
require(msg.sender == ejector, "RegistryCoordinator.onlyEjector: not ejector");
}

/**
Expand Down Expand Up @@ -628,7 +639,7 @@ contract RegistryCoordinator is
bytes32 operatorId = operatorInfo.operatorId;
require(
operatorInfo.status == OperatorStatus.REGISTERED,
"RegistryCoordinator._deregisterOperator: operator is not registered"
"RegistryCoordinator._deregisterOperator: not registered"
);

/**
Expand All @@ -647,19 +658,31 @@ contract RegistryCoordinator is
);
require(
quorumsToRemove.isSubsetOf(currentBitmap),
"RegistryCoordinator._deregisterOperator: operator is not registered for specified quorums"
"RegistryCoordinator._deregisterOperator: not registered for quorum"
);
uint192 newBitmap = uint192(currentBitmap.minus(quorumsToRemove));

// Update operator's bitmap and status
_updateOperatorBitmap({operatorId: operatorId, newBitmap: newBitmap});

// If the operator is no longer registered for any quorums, update their status and deregister
// them from the AVS via the EigenLayer core contracts
if (newBitmap.isEmpty()) {
operatorInfo.status = OperatorStatus.DEREGISTERED;
serviceManager.deregisterOperatorFromAVS(operator);
emit OperatorDeregistered(operator, operatorId);

bool operatorSetAVS = IAVSDirectory(serviceManager.avsDirectory()).isOperatorSetAVS(address(serviceManager)); /// TODO: call avsdirectory
stevennevins marked this conversation as resolved.
Show resolved Hide resolved
if (operatorSetAVS){
bytes memory quorumBytes = BitmapUtils.bitmapToBytesArray(quorumsToRemove);
uint32[] memory operatorSetIds = new uint32[](quorumBytes.length);
for (uint256 i = 0; i < quorumBytes.length; i++) {
operatorSetIds[i] = uint8(quorumBytes[i]);
}
serviceManager.deregisterOperatorFromOperatorSets(operator, operatorSetIds);

} else {
// If the operator is no longer registered for any quorums, update their status and deregister
// them from the AVS via the EigenLayer core contracts
if (newBitmap.isEmpty()) {
operatorInfo.status = OperatorStatus.DEREGISTERED;
serviceManager.deregisterOperatorFromAVS(operator);
emit OperatorDeregistered(operator, operatorId);
stevennevins marked this conversation as resolved.
Show resolved Hide resolved
}
}

// Deregister operator with each of the registry contracts
Expand Down Expand Up @@ -726,11 +749,11 @@ contract RegistryCoordinator is
// make sure the salt hasn't been used already
require(
!isChurnApproverSaltUsed[churnApproverSignature.salt],
"RegistryCoordinator._verifyChurnApproverSignature: churnApprover salt already used"
"RegistryCoordinator._verifyChurnApproverSignature: salt spent"
);
require(
churnApproverSignature.expiry >= block.timestamp,
"RegistryCoordinator._verifyChurnApproverSignature: churnApprover signature expired"
"RegistryCoordinator._verifyChurnApproverSignature: signature expired"
);

// set salt used to true
Expand Down Expand Up @@ -780,7 +803,7 @@ contract RegistryCoordinator is
indexRegistry.initializeQuorum(quorumNumber);
blsApkRegistry.initializeQuorum(quorumNumber);
// Check if the AVS has migrated to operator sets
AVSDirectory avsDirectory = AVSDirectory(serviceManager.avsDirectory());
IAVSDirectory avsDirectory = IAVSDirectory(serviceManager.avsDirectory());
if (avsDirectory.isOperatorSetAVS(address(serviceManager))) {
// Create an operator set for the new quorum
uint32[] memory operatorSetIds = new uint32[](1);
Expand Down Expand Up @@ -862,7 +885,7 @@ contract RegistryCoordinator is
}

revert(
"RegistryCoordinator.getQuorumBitmapIndexAtBlockNumber: no bitmap update found for operatorId at block number"
"RegistryCoordinator.getQuorumBitmapIndexAtBlockNumber: no bitmap update found for operatorId"
);
}

Expand Down
26 changes: 26 additions & 0 deletions src/ServiceManagerBase.sol
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,32 @@ abstract contract ServiceManagerBase is ServiceManagerBaseStorage {
_avsDirectory.deregisterOperatorFromAVS(operator);
}

/**
* @notice Forwards a call to EigenLayer's AVSDirectory contract to register an operator to operator sets
* @param operator The address of the operator to register.
* @param operatorSetIds The IDs of the operator sets.
* @param operatorSignature The signature, salt, and expiry of the operator's signature.
*/
function registerOperatorToOperatorSets(
address operator,
uint32[] calldata operatorSetIds,
ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature
) public virtual onlyRegistryCoordinator {
_avsDirectory.registerOperatorToOperatorSets(operator, operatorSetIds, operatorSignature);
}

/**
* @notice Forwards a call to EigenLayer's AVSDirectory contract to deregister an operator from operator sets
* @param operator The address of the operator to deregister.
* @param operatorSetIds The IDs of the operator sets.
*/
function deregisterOperatorFromOperatorSets(
address operator,
uint32[] calldata operatorSetIds
) public virtual onlyRegistryCoordinator {
_avsDirectory.deregisterOperatorFromOperatorSets(operator, operatorSetIds);
}

/**
* @notice Sets the rewards initiator address
* @param newRewardsInitiator The new rewards initiator address
Expand Down
2 changes: 1 addition & 1 deletion src/interfaces/IRegistryCoordinator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ interface IRegistryCoordinator {
function registries(uint256) external view returns (address);

/// @notice Returns the number of registries
function numRegistries() external view returns (uint256);
// function numRegistries() external view returns (uint256);
stevennevins marked this conversation as resolved.
Show resolved Hide resolved

/**
* @notice Returns the message hash that an operator must sign to register their BLS public key.
Expand Down
20 changes: 20 additions & 0 deletions src/interfaces/IServiceManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ pragma solidity >=0.5.0;

import {IRewardsCoordinator} from "eigenlayer-contracts/src/contracts/interfaces/IRewardsCoordinator.sol";
import {IServiceManagerUI} from "./IServiceManagerUI.sol";
import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol";

/**
* @title Minimal interface for a ServiceManager-type contract that forms the single point for an AVS to push updates to EigenLayer
Expand All @@ -24,6 +25,25 @@ interface IServiceManager is IServiceManagerUI {

function createOperatorSets(uint32[] memory operatorSetIds) external ;

/**
* @notice Forwards a call to EigenLayer's AVSDirectory contract to register an operator to operator sets
* @param operator The address of the operator to register.
* @param operatorSetIds The IDs of the operator sets.
* @param operatorSignature The signature, salt, and expiry of the operator's signature.
*/
function registerOperatorToOperatorSets(
address operator,
uint32[] calldata operatorSetIds,
ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature
) external;

/**
* @notice Forwards a call to EigenLayer's AVSDirectory contract to deregister an operator from operator sets
* @param operator The address of the operator to deregister.
* @param operatorSetIds The IDs of the operator sets.
*/
function deregisterOperatorFromOperatorSets(address operator, uint32[] calldata operatorSetIds) external;

// EVENTS
event RewardsInitiatorUpdated(address prevRewardsInitiator, address newRewardsInitiator);
}
18 changes: 9 additions & 9 deletions src/libraries/BN254.sol
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ library BN254 {
uint256[2] Y;
}

function generatorG1() internal pure returns (G1Point memory) {
function generatorG1() external pure returns (G1Point memory) {
return G1Point(1, 2);
}

Expand All @@ -62,7 +62,7 @@ library BN254 {
/// this is because of the (unknown to us) convention used in the bn254 pairing precompile contract
/// "Elements a * i + b of F_p^2 are encoded as two elements of F_p, (a, b)."
/// https://github.com/ethereum/EIPs/blob/master/EIPS/eip-197.md#encoding
function generatorG2() internal pure returns (G2Point memory) {
function generatorG2() external pure returns (G2Point memory) {
ypatil12 marked this conversation as resolved.
Show resolved Hide resolved
return G2Point([G2x1, G2x0], [G2y1, G2y0]);
}

Expand All @@ -73,7 +73,7 @@ library BN254 {
uint256 internal constant nG2y1 = 17805874995975841540914202342111839520379459829704422454583296818431106115052;
uint256 internal constant nG2y0 = 13392588948715843804641432497768002650278120570034223513918757245338268106653;

function negGeneratorG2() internal pure returns (G2Point memory) {
function negGeneratorG2() external pure returns (G2Point memory) {
return G2Point([nG2x1, nG2x0], [nG2y1, nG2y0]);
}

Expand All @@ -84,7 +84,7 @@ library BN254 {
* @param p Some point in G1.
* @return The negation of `p`, i.e. p.plus(p.negate()) should be zero.
*/
function negate(G1Point memory p) internal pure returns (G1Point memory) {
function negate(G1Point memory p) external pure returns (G1Point memory) {
// The prime q in the base field F_q for G1
if (p.X == 0 && p.Y == 0) {
return G1Point(0, 0);
Expand Down Expand Up @@ -194,7 +194,7 @@ library BN254 {
G2Point memory a2,
G1Point memory b1,
G2Point memory b2
) internal view returns (bool) {
) external view returns (bool) {
G1Point[2] memory p1 = [a1, b1];
G2Point[2] memory p2 = [a2, b2];

Expand Down Expand Up @@ -238,7 +238,7 @@ library BN254 {
G1Point memory b1,
G2Point memory b2,
uint256 pairingGas
) internal view returns (bool, bool) {
) external view returns (bool, bool) {
G1Point[2] memory p1 = [a1, b1];
G2Point[2] memory p2 = [a2, b2];

Expand Down Expand Up @@ -270,7 +270,7 @@ library BN254 {

/// @return hashedG1 the keccak256 hash of the G1 Point
/// @dev used for BLS signatures
function hashG1Point(BN254.G1Point memory pk) internal pure returns (bytes32 hashedG1) {
function hashG1Point(BN254.G1Point memory pk) external pure returns (bytes32 hashedG1) {
assembly {
mstore(0, mload(pk))
mstore(0x20, mload(add(0x20, pk)))
Expand All @@ -282,14 +282,14 @@ library BN254 {
/// @dev used for BLS signatures
function hashG2Point(
BN254.G2Point memory pk
) internal pure returns (bytes32) {
) external pure returns (bytes32) {
return keccak256(abi.encodePacked(pk.X[0], pk.X[1], pk.Y[0], pk.Y[1]));
}

/**
* @notice adapted from https://github.com/HarryR/solcrypto/blob/master/contracts/altbn128.sol
*/
function hashToG1(bytes32 _x) internal view returns (G1Point memory) {
function hashToG1(bytes32 _x) external view returns (G1Point memory) {
uint256 beta = 0;
uint256 y = 0;

Expand Down
8 changes: 8 additions & 0 deletions test/mocks/ECDSAServiceManagerMock.sol
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,12 @@ contract ECDSAServiceManagerMock is ECDSAServiceManagerBase {
}

function createOperatorSets(uint32[] memory) external {}

function registerOperatorToOperatorSets(
address operator,
uint32[] calldata operatorSetIds,
ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature
) external {}

function deregisterOperatorFromOperatorSets(address operator, uint32[] calldata operatorSetIds) external{}
}
8 changes: 4 additions & 4 deletions test/unit/OperatorStateRetrieverUnit.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ contract OperatorStateRetrieverUnitTests is MockAVSDeployer {

function test_getOperatorState_revert_neverRegistered() public {
cheats.expectRevert(
"RegistryCoordinator.getQuorumBitmapIndexAtBlockNumber: no bitmap update found for operatorId at block number"
"RegistryCoordinator.getQuorumBitmapIndexAtBlockNumber: no bitmap update found for operatorId"
);
operatorStateRetriever.getOperatorState(
registryCoordinator, defaultOperatorId, uint32(block.number)
Expand All @@ -26,7 +26,7 @@ contract OperatorStateRetrieverUnitTests is MockAVSDeployer {

// should revert because the operator was registered for the first time after the reference block number
cheats.expectRevert(
"RegistryCoordinator.getQuorumBitmapIndexAtBlockNumber: no bitmap update found for operatorId at block number"
"RegistryCoordinator.getQuorumBitmapIndexAtBlockNumber: no bitmap update found for operatorId"
);
operatorStateRetriever.getOperatorState(
registryCoordinator, defaultOperatorId, registrationBlockNumber - 1
Expand Down Expand Up @@ -143,7 +143,7 @@ contract OperatorStateRetrieverUnitTests is MockAVSDeployer {
nonSignerOperatorIds[0] = defaultOperatorId;

cheats.expectRevert(
"RegistryCoordinator.getQuorumBitmapIndexAtBlockNumber: no bitmap update found for operatorId at block number"
"RegistryCoordinator.getQuorumBitmapIndexAtBlockNumber: no bitmap update found for operatorId"
);
operatorStateRetriever.getCheckSignaturesIndices(
registryCoordinator,
Expand All @@ -164,7 +164,7 @@ contract OperatorStateRetrieverUnitTests is MockAVSDeployer {

// should revert because the operator was registered for the first time after the reference block number
cheats.expectRevert(
"RegistryCoordinator.getQuorumBitmapIndexAtBlockNumber: no bitmap update found for operatorId at block number"
"RegistryCoordinator.getQuorumBitmapIndexAtBlockNumber: no bitmap update found for operatorId"
);
operatorStateRetriever.getCheckSignaturesIndices(
registryCoordinator,
Expand Down
Loading
Loading