Skip to content

Commit

Permalink
feat: socket registry
Browse files Browse the repository at this point in the history
- move storage to new registry
- leave event in RegCoord
- trim bytecode from error strings
  • Loading branch information
0x0aa0 committed Sep 26, 2024
1 parent 83a444a commit fbb9e0a
Show file tree
Hide file tree
Showing 18 changed files with 190 additions and 94 deletions.
65 changes: 35 additions & 30 deletions src/RegistryCoordinator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {IStakeRegistry} from "./interfaces/IStakeRegistry.sol";
import {IIndexRegistry} from "./interfaces/IIndexRegistry.sol";
import {IServiceManager} from "./interfaces/IServiceManager.sol";
import {IRegistryCoordinator} from "./interfaces/IRegistryCoordinator.sol";
import {ISocketRegistry} from "./interfaces/ISocketRegistry.sol";

import {EIP1271SignatureUtils} from "eigenlayer-contracts/src/contracts/libraries/EIP1271SignatureUtils.sol";
import {BitmapUtils} from "./libraries/BitmapUtils.sol";
Expand Down Expand Up @@ -55,9 +56,10 @@ contract RegistryCoordinator is
IServiceManager _serviceManager,
IStakeRegistry _stakeRegistry,
IBLSApkRegistry _blsApkRegistry,
IIndexRegistry _indexRegistry
IIndexRegistry _indexRegistry,
ISocketRegistry _socketRegistry
)
RegistryCoordinatorStorage(_serviceManager, _stakeRegistry, _blsApkRegistry, _indexRegistry)
RegistryCoordinatorStorage(_serviceManager, _stakeRegistry, _blsApkRegistry, _indexRegistry, _socketRegistry)
EIP712("AVSRegistryCoordinator", "v0.0.1")
{
_disableInitializers();
Expand Down Expand Up @@ -86,7 +88,7 @@ contract RegistryCoordinator is
) external initializer {
require(
_operatorSetParams.length == _minimumStakes.length && _minimumStakes.length == _strategyParams.length,
"RegistryCoordinator.initialize: input length mismatch"
"RegCoord.initialize: input length mismatch"
);

// Initialize roles
Expand Down Expand Up @@ -152,7 +154,7 @@ contract RegistryCoordinator is

require(
numOperatorsPerQuorum[i] <= _quorumParams[quorumNumber].maxOperatorCount,
"RegistryCoordinator.registerOperator: operator count exceeds maximum"
"RegCoord.registerOperator: operator count exceeds maximum"
);
}
}
Expand All @@ -177,7 +179,7 @@ contract RegistryCoordinator is
SignatureWithSaltAndExpiry memory churnApproverSignature,
SignatureWithSaltAndExpiry memory operatorSignature
) external onlyWhenNotPaused(PAUSED_REGISTER_OPERATOR) {
require(operatorKickParams.length == quorumNumbers.length, "RegistryCoordinator.registerOperatorWithChurn: input length mismatch");
require(operatorKickParams.length == quorumNumbers.length, "RegCoord.registerOperatorWithChurn: input length mismatch");

/**
* If the operator has NEVER registered a pubkey before, use `params` to register
Expand Down Expand Up @@ -287,7 +289,7 @@ contract RegistryCoordinator is
uint192 quorumBitmap = uint192(BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers, quorumCount));
require(
operatorsPerQuorum.length == quorumNumbers.length,
"RegistryCoordinator.updateOperatorsForQuorum: input length mismatch"
"RegCoord.updateOperatorsForQuorum: input length mismatch"
);

// For each quorum, update ALL registered operators
Expand All @@ -298,7 +300,7 @@ contract RegistryCoordinator is
address[] calldata currQuorumOperators = operatorsPerQuorum[i];
require(
currQuorumOperators.length == indexRegistry.totalOperatorsForQuorum(quorumNumber),
"RegistryCoordinator.updateOperatorsForQuorum: number of updated operators does not match quorum total"
"RegCoord.updateOperatorsForQuorum: number of updated operators does not match quorum total"
);

address prevOperatorAddress = address(0);
Expand All @@ -317,12 +319,12 @@ contract RegistryCoordinator is
// Check that the operator is registered
require(
BitmapUtils.isSet(currentBitmap, quorumNumber),
"RegistryCoordinator.updateOperatorsForQuorum: operator not in quorum"
"RegCoord.updateOperatorsForQuorum: operator not in quorum"
);
// Prevent duplicate operators
require(
operator > prevOperatorAddress,
"RegistryCoordinator.updateOperatorsForQuorum: operators array must be sorted in ascending address order"
"RegCoord.updateOperatorsForQuorum: operators array must be sorted in ascending address order"
);
}

Expand All @@ -342,8 +344,9 @@ contract RegistryCoordinator is
* @param socket is the new socket of the operator
*/
function updateSocket(string memory socket) external {
require(_operatorInfo[msg.sender].status == OperatorStatus.REGISTERED, "RegistryCoordinator.updateSocket: operator not registered");
serviceManager.setOperatorSocket(_operatorInfo[msg.sender].operatorId, socket);
require(_operatorInfo[msg.sender].status == OperatorStatus.REGISTERED, "RegCoord.updateSocket: operator not registered");
socketRegistry.setOperatorSocket(_operatorInfo[msg.sender].operatorId, socket);
emit OperatorSocketUpdate(_operatorInfo[msg.sender].operatorId, socket);
}

/*******************************************************************************
Expand Down Expand Up @@ -471,12 +474,12 @@ contract RegistryCoordinator is
*/
uint192 quorumsToAdd = uint192(BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers, quorumCount));
uint192 currentBitmap = _currentOperatorBitmap(operatorId);
require(!quorumsToAdd.isEmpty(), "RegistryCoordinator._registerOperator: bitmap cannot be 0");
require(quorumsToAdd.noBitsInCommon(currentBitmap), "RegistryCoordinator._registerOperator: operator already registered for some quorums");
require(!quorumsToAdd.isEmpty(), "RegCoord._registerOperator: bitmap cannot be 0");
require(quorumsToAdd.noBitsInCommon(currentBitmap), "RegCoord._registerOperator: operator already registered for some quorums");
uint192 newBitmap = uint192(currentBitmap.plus(quorumsToAdd));

// Check that the operator can reregister if ejected
require(lastEjectionTimestamp[operator] + ejectionCooldown < block.timestamp, "RegistryCoordinator._registerOperator: operator cannot reregister yet");
require(lastEjectionTimestamp[operator] + ejectionCooldown < block.timestamp, "RegCoord._registerOperator: operator cannot reregister yet");

/**
* Update operator's bitmap, socket, and status. Only update operatorInfo if needed:
Expand All @@ -497,7 +500,9 @@ contract RegistryCoordinator is

// Register the operator with the EigenLayer core contracts via this AVS's ServiceManager
serviceManager.registerOperatorToAVS(operator, operatorSignature);
serviceManager.setOperatorSocket(operatorId, socket);

socketRegistry.setOperatorSocket(operatorId, socket);
emit OperatorSocketUpdate(operatorId, socket);

emit OperatorRegistered(operator, operatorId);
}
Expand All @@ -516,7 +521,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, "RegCoord.onlyEjector: caller is not the ejector");
}

/**
Expand All @@ -527,7 +532,7 @@ contract RegistryCoordinator is
function _checkQuorumExists(uint8 quorumNumber) internal view {
require(
quorumNumber < quorumCount,
"RegistryCoordinator.quorumExists: quorum does not exist"
"RegCoord.quorumExists: quorum does not exist"
);
}

Expand Down Expand Up @@ -578,18 +583,18 @@ contract RegistryCoordinator is
) internal view {
address operatorToKick = kickParams.operator;
bytes32 idToKick = _operatorInfo[operatorToKick].operatorId;
require(newOperator != operatorToKick, "RegistryCoordinator._validateChurn: cannot churn self");
require(kickParams.quorumNumber == quorumNumber, "RegistryCoordinator._validateChurn: quorum not the same as signed");
require(newOperator != operatorToKick, "RegCoord._validateChurn: cannot churn self");
require(kickParams.quorumNumber == quorumNumber, "RegCoord._validateChurn: quorumNumber not the same as signed");

// Get the target operator's stake and check that it is below the kick thresholds
uint96 operatorToKickStake = stakeRegistry.getCurrentStake(idToKick, quorumNumber);
require(
newOperatorStake > _individualKickThreshold(operatorToKickStake, setParams),
"RegistryCoordinator._validateChurn: incoming operator has insufficient stake for churn"
"RegCoord._validateChurn: incoming operator has insufficient stake for churn"
);
require(
operatorToKickStake < _totalKickThreshold(totalQuorumStake, setParams),
"RegistryCoordinator._validateChurn: cannot kick operator with more than kickBIPsOfTotalStake"
"RegCoord._validateChurn: cannot kick operator with more than kickBIPsOfTotalStake"
);
}

Expand All @@ -605,7 +610,7 @@ contract RegistryCoordinator is
// Fetch the operator's info and ensure they are registered
OperatorInfo storage operatorInfo = _operatorInfo[operator];
bytes32 operatorId = operatorInfo.operatorId;
require(operatorInfo.status == OperatorStatus.REGISTERED, "RegistryCoordinator._deregisterOperator: operator is not registered");
require(operatorInfo.status == OperatorStatus.REGISTERED, "RegCoord._deregisterOperator: operator is not registered");

/**
* Get bitmap of quorums to deregister from and operator's current bitmap. Validate that:
Expand All @@ -616,8 +621,8 @@ contract RegistryCoordinator is
*/
uint192 quorumsToRemove = uint192(BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers, quorumCount));
uint192 currentBitmap = _currentOperatorBitmap(operatorId);
require(!quorumsToRemove.isEmpty(), "RegistryCoordinator._deregisterOperator: bitmap cannot be 0");
require(quorumsToRemove.isSubsetOf(currentBitmap), "RegistryCoordinator._deregisterOperator: operator is not registered for quorums");
require(!quorumsToRemove.isEmpty(), "RegCoord._deregisterOperator: bitmap cannot be 0");
require(quorumsToRemove.isSubsetOf(currentBitmap), "RegCoord._deregisterOperator: operator is not registered for quorums");
uint192 newBitmap = uint192(currentBitmap.minus(quorumsToRemove));

// Update operator's bitmap and status
Expand Down Expand Up @@ -689,8 +694,8 @@ contract RegistryCoordinator is
SignatureWithSaltAndExpiry memory churnApproverSignature
) internal {
// make sure the salt hasn't been used already
require(!isChurnApproverSaltUsed[churnApproverSignature.salt], "RegistryCoordinator._verifyChurnApproverSignature: churnApprover salt already used");
require(churnApproverSignature.expiry >= block.timestamp, "RegistryCoordinator._verifyChurnApproverSignature: churnApprover signature expired");
require(!isChurnApproverSaltUsed[churnApproverSignature.salt], "RegCoord._verifyChurnApproverSignature: churnApprover salt already used");
require(churnApproverSignature.expiry >= block.timestamp, "RegCoord._verifyChurnApproverSignature: churnApprover signature expired");

// set salt used to true
isChurnApproverSaltUsed[churnApproverSignature.salt] = true;
Expand Down Expand Up @@ -718,7 +723,7 @@ contract RegistryCoordinator is
) internal {
// Increment the total quorum count. Fails if we're already at the max
uint8 prevQuorumCount = quorumCount;
require(prevQuorumCount < MAX_QUORUM_COUNT, "RegistryCoordinator.createQuorum: max quorums reached");
require(prevQuorumCount < MAX_QUORUM_COUNT, "RegCoord.createQuorum: max quorums reached");
quorumCount = prevQuorumCount + 1;

// The previous count is the new quorum's number
Expand Down Expand Up @@ -800,7 +805,7 @@ contract RegistryCoordinator is
}

revert(
"RegistryCoordinator.getQuorumBitmapIndexAtBlockNumber: no bitmap update found for operator at block"
"RegCoord.getQuorumBitmapIndexAtBlockNumber: no bitmap update found for operator at blockNumber"
);
}

Expand Down Expand Up @@ -884,11 +889,11 @@ contract RegistryCoordinator is
*/
require(
blockNumber >= quorumBitmapUpdate.updateBlockNumber,
"RegistryCoordinator.getQuorumBitmapAtBlockNumberByIndex: quorumBitmapUpdate is from after block"
"RegCoord.getQuorumBitmapAtBlockNumberByIndex: quorumBitmapUpdate is from after blockNumber"
);
require(
quorumBitmapUpdate.nextUpdateBlockNumber == 0 || blockNumber < quorumBitmapUpdate.nextUpdateBlockNumber,
"RegistryCoordinator.getQuorumBitmapAtBlockNumberByIndex: quorumBitmapUpdate is from before block"
"RegCoord.getQuorumBitmapAtBlockNumberByIndex: quorumBitmapUpdate is from before blockNumber"
);

return quorumBitmapUpdate.quorumBitmap;
Expand Down
7 changes: 6 additions & 1 deletion src/RegistryCoordinatorStorage.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {IStakeRegistry} from "./interfaces/IStakeRegistry.sol";
import {IIndexRegistry} from "./interfaces/IIndexRegistry.sol";
import {IServiceManager} from "./interfaces/IServiceManager.sol";
import {IRegistryCoordinator} from "./interfaces/IRegistryCoordinator.sol";
import {ISocketRegistry} from "./interfaces/ISocketRegistry.sol";

abstract contract RegistryCoordinatorStorage is IRegistryCoordinator {

Expand Down Expand Up @@ -39,6 +40,8 @@ abstract contract RegistryCoordinatorStorage is IRegistryCoordinator {
IStakeRegistry public immutable stakeRegistry;
/// @notice the Index Registry contract that will keep track of operators' indexes
IIndexRegistry public immutable indexRegistry;
/// @notice the Socket Registry contract that will keep track of operators' sockets
ISocketRegistry public immutable socketRegistry;

/*******************************************************************************
STATE
Expand Down Expand Up @@ -73,12 +76,14 @@ abstract contract RegistryCoordinatorStorage is IRegistryCoordinator {
IServiceManager _serviceManager,
IStakeRegistry _stakeRegistry,
IBLSApkRegistry _blsApkRegistry,
IIndexRegistry _indexRegistry
IIndexRegistry _indexRegistry,
ISocketRegistry _socketRegistry
) {
serviceManager = _serviceManager;
stakeRegistry = _stakeRegistry;
blsApkRegistry = _blsApkRegistry;
indexRegistry = _indexRegistry;
socketRegistry = _socketRegistry;
}

// storage gap for upgradeability
Expand Down
11 changes: 0 additions & 11 deletions src/ServiceManagerBase.sol
Original file line number Diff line number Diff line change
Expand Up @@ -217,15 +217,4 @@ abstract contract ServiceManagerBase is ServiceManagerBaseStorage {
function avsDirectory() external view override returns (address) {
return address(_avsDirectory);
}

function setOperatorSocket(bytes32 operatorId, string memory socket) external onlyRegistryCoordinator {
operatorIdToSocket[operatorId] = socket;
emit OperatorSocketUpdate(operatorId, socket);
}

function migrateSockets(bytes32[] calldata operatorIds, string[] calldata sockets) external onlyOwner {
for (uint256 i = 0; i < operatorIds.length; i++) {
operatorIdToSocket[operatorIds[i]] = sockets[i];
}
}
}
5 changes: 1 addition & 4 deletions src/ServiceManagerBaseStorage.sol
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,6 @@ abstract contract ServiceManagerBaseStorage is IServiceManager, OwnableUpgradeab

/// @notice The address of the entity that can initiate rewards
address public rewardsInitiator;

/// @notice maps operator id to socket address
mapping(bytes32 => string) public operatorIdToSocket;

/// @notice Sets the (immutable) `_avsDirectory`, `_rewardsCoordinator`, `_registryCoordinator`, and `_stakeRegistry` addresses
constructor(
Expand All @@ -52,5 +49,5 @@ abstract contract ServiceManagerBaseStorage is IServiceManager, OwnableUpgradeab
}

// storage gap for upgradeability
uint256[48] private __GAP;
uint256[49] private __GAP;
}
52 changes: 52 additions & 0 deletions src/SocketRegistry.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.12;

import {IRegistryCoordinator} from "./interfaces/IRegistryCoordinator.sol";
import {ISocketRegistry} from "./interfaces/ISocketRegistry.sol";

/**
* @title A `Registry` that keeps track of operator sockets.
* @author Layr Labs, Inc.
*/
contract SocketRegistry is ISocketRegistry {

/// @notice The address of the RegistryCoordinator
address public immutable registryCoordinator;

/// @notice A mapping from operator IDs to their sockets
mapping(bytes32 => string) public operatorIdToSocket;

/// @notice A modifier that only allows the RegistryCoordinator to call a function
modifier onlyRegistryCoordinator() {
require(msg.sender == address(registryCoordinator), "SocketRegistry.onlyRegistryCoordinator: caller is not the RegistryCoordinator");
_;
}

/// @notice A modifier that only allows the owner of the RegistryCoordinator to call a function
modifier onlyCoordinatorOwner() {
require(msg.sender == IRegistryCoordinator(registryCoordinator).owner(), "SocketRegistry.onlyCoordinatorOwner: caller is not the owner of the registryCoordinator");
_;
}

constructor(IRegistryCoordinator _registryCoordinator) {
registryCoordinator = address(_registryCoordinator);
}

/// @notice sets the socket for an operator only callable by the RegistryCoordinator
function setOperatorSocket(bytes32 _operatorId, string memory _socket) external onlyRegistryCoordinator {
operatorIdToSocket[_operatorId] = _socket;
}

/// @notice migrates the sockets for a list of operators only callable by the owner of the RegistryCoordinator
function migrateOperatorSockets(bytes32[] memory _operatorIds, string[] memory _sockets) external onlyCoordinatorOwner {
for (uint256 i = 0; i < _operatorIds.length; i++) {
operatorIdToSocket[_operatorIds[i]] = _sockets[i];
}
}

/// @notice gets the stored socket for an operator
function getOperatorSocket(bytes32 _operatorId) external view returns (string memory) {
return operatorIdToSocket[_operatorId];
}

}
8 changes: 8 additions & 0 deletions src/interfaces/IRegistryCoordinator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ interface IRegistryCoordinator {

event EjectorUpdated(address prevEjector, address newEjector);

event OperatorSocketUpdate(bytes32 indexed operatorId, string socket);

/// @notice emitted when all the operators for a quorum are updated at once
event QuorumBlockNumberUpdated(uint8 indexed quorumNumber, uint256 blocknumber);

Expand Down Expand Up @@ -150,4 +152,10 @@ interface IRegistryCoordinator {

/// @notice The owner of the registry coordinator
function owner() external view returns (address);

/**
* @notice Updates the socket of the msg.sender given they are a registered operator
* @param socket is the new socket of the operator
*/
function updateSocket(string memory socket) external;
}
5 changes: 0 additions & 5 deletions src/interfaces/IServiceManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,6 @@ interface IServiceManager is IServiceManagerUI {
*/
function createAVSRewardsSubmission(IRewardsCoordinator.RewardsSubmission[] calldata rewardsSubmissions) external;

function setOperatorSocket(bytes32 operatorId, string memory socket) external;

// EVENTS
event RewardsInitiatorUpdated(address prevRewardsInitiator, address newRewardsInitiator);

event OperatorSocketUpdate(bytes32 indexed operatorId, string socket);

}
10 changes: 10 additions & 0 deletions src/interfaces/ISocketRegistry.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface ISocketRegistry {
/// @notice sets the socket for an operator only callable by the RegistryCoordinator
function setOperatorSocket(bytes32 _operatorId, string memory _socket) external;

/// @notice gets the stored socket for an operator
function getOperatorSocket(bytes32 _operatorId) external view returns (string memory);
}
Loading

0 comments on commit fbb9e0a

Please sign in to comment.