Skip to content

Commit

Permalink
onchain socket (#307)
Browse files Browse the repository at this point in the history
* feat: onchain socket

moves sockets onchain to SM

* feat: socket registry

- move storage to new registry
- leave event in RegCoord
- trim bytecode from error strings

* fix: internal function

internal function to update socket
  • Loading branch information
0x0aa0 authored Oct 11, 2024
1 parent cb2b334 commit b42aa0b
Show file tree
Hide file tree
Showing 15 changed files with 192 additions and 95 deletions.
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 {
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

0 comments on commit b42aa0b

Please sign in to comment.