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

onchain socket #307

Merged
merged 3 commits into from
Oct 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 38 additions & 33 deletions src/RegistryCoordinator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ pragma solidity ^0.8.12;

import {IPauserRegistry} from "eigenlayer-contracts/src/contracts/interfaces/IPauserRegistry.sol";
import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol";
import {ISocketUpdater} from "./interfaces/ISocketUpdater.sol";
import {IBLSApkRegistry} from "./interfaces/IBLSApkRegistry.sol";
import {IStakeRegistry} from "./interfaces/IStakeRegistry.sol";
import {IIndexRegistry} from "./interfaces/IIndexRegistry.sol";
import {IServiceManager} from "./interfaces/IServiceManager.sol";
import {IRegistryCoordinator} from "./interfaces/IRegistryCoordinator.sol";
import {ISocketRegistry} from "./interfaces/ISocketRegistry.sol";

import {EIP1271SignatureUtils} from "eigenlayer-contracts/src/contracts/libraries/EIP1271SignatureUtils.sol";
import {BitmapUtils} from "./libraries/BitmapUtils.sol";
Expand All @@ -35,7 +35,6 @@ contract RegistryCoordinator is
Pausable,
OwnableUpgradeable,
RegistryCoordinatorStorage,
ISocketUpdater,
ISignatureUtils
{
using BitmapUtils for *;
Expand All @@ -57,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 @@ -88,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 @@ -154,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 @@ -179,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 @@ -289,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 @@ -300,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 @@ -319,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 @@ -344,8 +344,8 @@ contract RegistryCoordinator is
* @param socket is the new socket of the operator
*/
function updateSocket(string memory socket) external {
require(_operatorInfo[msg.sender].status == OperatorStatus.REGISTERED, "RegistryCoordinator.updateSocket: operator is not registered");
emit OperatorSocketUpdate(_operatorInfo[msg.sender].operatorId, socket);
require(_operatorInfo[msg.sender].status == OperatorStatus.REGISTERED, "RegCoord.updateSocket: operator not registered");
_setOperatorSocket(_operatorInfo[msg.sender].operatorId, socket);
}

/*******************************************************************************
Expand Down Expand Up @@ -473,12 +473,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 being registered for");
require(!quorumsToAdd.isEmpty(), "RegCoord._registerOperator: bitmap cannot be 0");
require(quorumsToAdd.noBitsInCommon(currentBitmap), "RegCoord._registerOperator: operator already registered for some quorums");
uint192 newBitmap = uint192(currentBitmap.plus(quorumsToAdd));

// Check that the operator can reregister if ejected
require(lastEjectionTimestamp[operator] + ejectionCooldown < block.timestamp, "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 @@ -489,8 +489,6 @@ contract RegistryCoordinator is
newBitmap: newBitmap
});

emit OperatorSocketUpdate(operatorId, socket);

// If the operator wasn't registered for any quorums, update their status
// and register them with this AVS in EigenLayer core (DelegationManager)
if (_operatorInfo[operator].status != OperatorStatus.REGISTERED) {
Expand All @@ -502,6 +500,8 @@ contract RegistryCoordinator is
// Register the operator with the EigenLayer core contracts via this AVS's ServiceManager
serviceManager.registerOperatorToAVS(operator, operatorSignature);

_setOperatorSocket(operatorId, socket);

emit OperatorRegistered(operator, operatorId);
}

Expand All @@ -519,7 +519,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 @@ -530,7 +530,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 @@ -581,18 +581,18 @@ contract RegistryCoordinator is
) internal view {
address operatorToKick = kickParams.operator;
bytes32 idToKick = _operatorInfo[operatorToKick].operatorId;
require(newOperator != operatorToKick, "RegistryCoordinator._validateChurn: cannot churn self");
require(kickParams.quorumNumber == quorumNumber, "RegistryCoordinator._validateChurn: quorumNumber not the same as signed");
require(newOperator != operatorToKick, "RegCoord._validateChurn: cannot churn self");
require(kickParams.quorumNumber == quorumNumber, "RegCoord._validateChurn: quorumNumber not the same as signed");

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

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

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

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

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

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

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

Expand All @@ -822,6 +822,11 @@ contract RegistryCoordinator is
ejector = newEjector;
}

function _setOperatorSocket(bytes32 operatorId, string memory socket) internal {
socketRegistry.setOperatorSocket(operatorId, socket);
emit OperatorSocketUpdate(operatorId, socket);
}

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

return quorumBitmapUpdate.quorumBitmap;
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
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 {
0x0aa0 marked this conversation as resolved.
Show resolved Hide resolved
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;
}
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);
}
20 changes: 0 additions & 20 deletions src/interfaces/ISocketUpdater.sol

This file was deleted.

Loading
Loading