-
Notifications
You must be signed in to change notification settings - Fork 109
refactor: slashing #379
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
Merged
Merged
refactor: slashing #379
Changes from all commits
Commits
Show all changes
16 commits
Select commit
Hold shift + click to select a range
090c5b1
chore: start refactor
gpsanant 6f7603b
fix: quorum creation
gpsanant ce20d8c
fix: bug in registration
gpsanant 27668d5
feat: refactor avs sync
gpsanant 85c6748
chore: respond to review
gpsanant f882e24
Update src/SlashingRegistryCoordinator.sol
gpsanant 06634ae
fix: m2 registration validity
gpsanant d7ca39f
feat: lazy migration
gpsanant 8f52f24
test: fixes and refactor of weightOfOperators
8sunyuan 1e40a4a
feat: add updateOperators
gpsanant 11817b7
fix: tests and `_afterDeregisterOperator` hook
8sunyuan 441cff4
refactor: remove operatorSetEnabled as require
8sunyuan b3df455
chore: fmt
8sunyuan d73f483
chore: fmt test files
8sunyuan 6261e2f
fix: failing ci test
8sunyuan e47a0fb
chore: test fmt
8sunyuan File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
8sunyuan marked this conversation as resolved.
Show resolved
Hide resolved
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,6 +15,7 @@ import {BitmapUtils} from "./libraries/BitmapUtils.sol"; | |
import {SlashingRegistryCoordinator} from "./SlashingRegistryCoordinator.sol"; | ||
import {ISlashingRegistryCoordinator} from "./interfaces/ISlashingRegistryCoordinator.sol"; | ||
import {OwnableUpgradeable} from "@openzeppelin-upgrades/contracts/access/OwnableUpgradeable.sol"; | ||
import {RegistryCoordinatorStorage} from "./RegistryCoordinatorStorage.sol"; | ||
|
||
/** | ||
* @title A `RegistryCoordinator` that has three registries: | ||
|
@@ -24,12 +25,9 @@ import {OwnableUpgradeable} from "@openzeppelin-upgrades/contracts/access/Ownabl | |
* | ||
* @author Layr Labs, Inc. | ||
*/ | ||
contract RegistryCoordinator is IRegistryCoordinator, SlashingRegistryCoordinator { | ||
contract RegistryCoordinator is RegistryCoordinatorStorage { | ||
using BitmapUtils for *; | ||
|
||
/// @notice the ServiceManager for this AVS, which forwards calls onto EigenLayer's core contracts | ||
IServiceManager public immutable serviceManager; | ||
|
||
constructor( | ||
IServiceManager _serviceManager, | ||
IStakeRegistry _stakeRegistry, | ||
|
@@ -39,17 +37,16 @@ contract RegistryCoordinator is IRegistryCoordinator, SlashingRegistryCoordinato | |
IAllocationManager _allocationManager, | ||
IPauserRegistry _pauserRegistry | ||
) | ||
SlashingRegistryCoordinator( | ||
RegistryCoordinatorStorage( | ||
_serviceManager, | ||
_stakeRegistry, | ||
_blsApkRegistry, | ||
_indexRegistry, | ||
_socketRegistry, | ||
_allocationManager, | ||
_pauserRegistry | ||
) | ||
{ | ||
serviceManager = _serviceManager; | ||
} | ||
{} | ||
|
||
/** | ||
* | ||
|
@@ -64,44 +61,28 @@ contract RegistryCoordinator is IRegistryCoordinator, SlashingRegistryCoordinato | |
IBLSApkRegistryTypes.PubkeyRegistrationParams memory params, | ||
SignatureWithSaltAndExpiry memory operatorSignature | ||
) external onlyWhenNotPaused(PAUSED_REGISTER_OPERATOR) { | ||
require(!m2QuorumsDisabled, M2QuorumsAlreadyDisabled()); | ||
/** | ||
* 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(msg.sender, params); | ||
|
||
// Register the operator in each of the registry contracts and update the operator's | ||
// quorum bitmap and registration status | ||
uint32[] memory numOperatorsPerQuorum = _registerOperator({ | ||
require(!isM2QuorumRegistrationDisabled, M2QuorumRegistrationIsDisabled()); | ||
require( | ||
quorumNumbers.orderedBytesArrayToBitmap().isSubsetOf(m2QuorumBitmap()), | ||
OnlyM2QuorumsAllowed() | ||
); | ||
|
||
// Check if the operator has registered before | ||
bool operatorRegisteredBefore = | ||
_operatorInfo[msg.sender].status == OperatorStatus.REGISTERED; | ||
|
||
// register the operator with the registry coordinator | ||
_registerOperator({ | ||
operator: msg.sender, | ||
operatorId: operatorId, | ||
operatorId: _getOrCreateOperatorId(msg.sender, params), | ||
quorumNumbers: quorumNumbers, | ||
socket: socket | ||
}).numOperatorsPerQuorum; | ||
|
||
// For each quorum, validate that the new operator count does not exceed the maximum | ||
// (If it does, an operator needs to be replaced -- see `registerOperatorWithChurn`) | ||
for (uint256 i = 0; i < quorumNumbers.length; i++) { | ||
uint8 quorumNumber = uint8(quorumNumbers[i]); | ||
|
||
require( | ||
numOperatorsPerQuorum[i] <= _quorumParams[quorumNumber].maxOperatorCount, | ||
MaxQuorumsReached() | ||
); | ||
} | ||
|
||
// If the operator wasn't registered for any quorums, update their status | ||
// and register them with this AVS in EigenLayer core (DelegationManager) | ||
if (_operatorInfo[msg.sender].status != OperatorStatus.REGISTERED) { | ||
_operatorInfo[msg.sender] = | ||
OperatorInfo({operatorId: operatorId, status: OperatorStatus.REGISTERED}); | ||
socket: socket, | ||
checkMaxOperatorCount: true | ||
}); | ||
|
||
// If the operator has never registered before, register them with the AVSDirectory | ||
if (!operatorRegisteredBefore) { | ||
serviceManager.registerOperatorToAVS(msg.sender, operatorSignature); | ||
emit OperatorRegistered(msg.sender, operatorId); | ||
} | ||
} | ||
|
||
|
@@ -114,34 +95,29 @@ contract RegistryCoordinator is IRegistryCoordinator, SlashingRegistryCoordinato | |
SignatureWithSaltAndExpiry memory churnApproverSignature, | ||
SignatureWithSaltAndExpiry memory operatorSignature | ||
) external onlyWhenNotPaused(PAUSED_REGISTER_OPERATOR) { | ||
require(!m2QuorumsDisabled, M2QuorumsAlreadyDisabled()); | ||
require(!isM2QuorumRegistrationDisabled, M2QuorumRegistrationIsDisabled()); | ||
require( | ||
quorumNumbers.orderedBytesArrayToBitmap().isSubsetOf(m2QuorumBitmap()), | ||
OnlyM2QuorumsAllowed() | ||
); | ||
|
||
/** | ||
* 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(msg.sender, params); | ||
// Check if the operator has registered before | ||
bool operatorRegisteredBefore = | ||
_operatorInfo[msg.sender].status == OperatorStatus.REGISTERED; | ||
|
||
// register the operator with the registry coordinator with churn | ||
_registerOperatorWithChurn({ | ||
operator: msg.sender, | ||
operatorId: operatorId, | ||
operatorId: _getOrCreateOperatorId(msg.sender, params), | ||
quorumNumbers: quorumNumbers, | ||
socket: socket, | ||
operatorKickParams: operatorKickParams, | ||
churnApproverSignature: churnApproverSignature | ||
}); | ||
|
||
// If the operator wasn't registered for any quorums, update their status | ||
// and register them with this AVS in EigenLayer core (DelegationManager) | ||
if (_operatorInfo[msg.sender].status != OperatorStatus.REGISTERED) { | ||
_operatorInfo[msg.sender] = | ||
OperatorInfo({operatorId: operatorId, status: OperatorStatus.REGISTERED}); | ||
|
||
// If the operator has never registered before, register them with the AVSDirectory | ||
if (!operatorRegisteredBefore) { | ||
serviceManager.registerOperatorToAVS(msg.sender, operatorSignature); | ||
emit OperatorRegistered(msg.sender, operatorId); | ||
} | ||
} | ||
|
||
|
@@ -150,56 +126,78 @@ contract RegistryCoordinator is IRegistryCoordinator, SlashingRegistryCoordinato | |
bytes memory quorumNumbers | ||
) external override onlyWhenNotPaused(PAUSED_DEREGISTER_OPERATOR) { | ||
// Check that the quorum numbers are M2 quorums | ||
for (uint256 i = 0; i < quorumNumbers.length; i++) { | ||
require( | ||
!operatorSetsEnabled || _isM2Quorum(uint8(quorumNumbers[i])), OperatorSetQuorum() | ||
); | ||
} | ||
require( | ||
quorumNumbers.orderedBytesArrayToBitmap().isSubsetOf(m2QuorumBitmap()), | ||
OnlyM2QuorumsAllowed() | ||
); | ||
|
||
_deregisterOperator({operator: msg.sender, quorumNumbers: quorumNumbers}); | ||
} | ||
|
||
/// @inheritdoc IRegistryCoordinator | ||
function enableOperatorSets() external onlyOwner { | ||
require(!operatorSetsEnabled, OperatorSetsAlreadyEnabled()); | ||
|
||
// Set the bitmap for M2 quorums | ||
M2quorumBitmap = _getQuorumBitmap(quorumCount); | ||
function disableM2QuorumRegistration() external onlyOwner { | ||
require(!isM2QuorumRegistrationDisabled, M2QuorumRegistrationIsDisabled()); | ||
|
||
// Enable operator sets mode | ||
operatorSetsEnabled = true; | ||
isM2QuorumRegistrationDisabled = true; | ||
|
||
emit OperatorSetsEnabled(); | ||
emit M2QuorumRegistrationDisabled(); | ||
} | ||
|
||
/// @inheritdoc IRegistryCoordinator | ||
function disableM2QuorumRegistration() external onlyOwner { | ||
require(operatorSetsEnabled, OperatorSetsNotEnabled()); | ||
/** | ||
* | ||
* INTERNAL FUNCTIONS | ||
* | ||
*/ | ||
|
||
m2QuorumsDisabled = true; | ||
/// @dev override the _forceDeregisterOperator function to handle M2 quorum deregistration | ||
function _forceDeregisterOperator( | ||
address operator, | ||
bytes memory quorumNumbers | ||
) internal virtual override { | ||
// filter out M2 quorums from the quorum numbers | ||
uint256 operatorSetBitmap = | ||
quorumNumbers.orderedBytesArrayToBitmap().minus(m2QuorumBitmap()); | ||
if (!operatorSetBitmap.isEmpty()) { | ||
// call the parent _forceDeregisterOperator function for operator sets quorums | ||
super._forceDeregisterOperator(operator, operatorSetBitmap.bitmapToBytesArray()); | ||
} | ||
} | ||
|
||
emit M2QuorumsDisabled(); | ||
/// @dev Hook to prevent any new quorums from being created if operator sets are not enabled | ||
function _beforeCreateQuorum( | ||
uint8 | ||
) internal virtual override { | ||
// If operator sets are not enabled, set the m2 quorum bitmap to the current m2 quorum bitmap | ||
// and enable operator sets | ||
if (!operatorSetsEnabled) { | ||
_m2QuorumBitmap = m2QuorumBitmap(); | ||
operatorSetsEnabled = true; | ||
} | ||
} | ||
|
||
/// @dev Hook to allow for any post-deregister logic | ||
function _afterDeregisterOperator( | ||
address operator, | ||
bytes32 operatorId, | ||
bytes memory quorumNumbers, | ||
bytes32, | ||
bytes memory, | ||
uint192 newBitmap | ||
) internal virtual override { | ||
uint256 operatorM2QuorumBitmap = newBitmap.minus(M2quorumBitmap); | ||
// Bitmap representing all quorums including M2 and OperatorSet quorums | ||
uint256 totalQuorumBitmap = _getTotalQuorumBitmap(); | ||
// Bitmap representing only OperatorSet quorums. Equal to 0 if operatorSets not enabled | ||
uint256 operatorSetQuorumBitmap = totalQuorumBitmap.minus(m2QuorumBitmap()); | ||
// Operators updated M2 quorum bitmap, clear all the bits of operatorSetQuorumBitmap which gives the | ||
// operator's M2 quorum bitmap. | ||
uint256 operatorM2QuorumBitmap = newBitmap.minus(operatorSetQuorumBitmap); | ||
// If the operator is no longer registered for any M2 quorums, update their status and deregister | ||
// them from the AVS via the EigenLayer core contracts | ||
if (operatorM2QuorumBitmap.isEmpty()) { | ||
serviceManager.deregisterOperatorFromAVS(operator); | ||
} | ||
} | ||
|
||
/// @dev Returns a bitmap with all bits set up to `quorumCount`. Used for bit-masking quorum numbers | ||
/// and differentiating between operator sets and M2 quorums | ||
function _getQuorumBitmap( | ||
uint256 quorumCount | ||
) internal pure returns (uint256) { | ||
/// @notice Return bitmap representing all quorums(Legacy M2 and OperatorSet) quorums | ||
function _getTotalQuorumBitmap() internal view returns (uint256) { | ||
// This creates a number where all bits up to quorumCount are set to 1 | ||
// For example: | ||
// quorumCount = 3 -> 0111 (7 in decimal) | ||
|
@@ -208,6 +206,38 @@ contract RegistryCoordinator is IRegistryCoordinator, SlashingRegistryCoordinato | |
return (1 << quorumCount) - 1; | ||
} | ||
|
||
/// @notice Returns true if the quorum number is an M2 quorum | ||
/// @dev We use bitwise and to check if the quorum number is an M2 quorum | ||
function _isM2Quorum( | ||
uint8 quorumNumber | ||
) internal view returns (bool) { | ||
return m2QuorumBitmap().isSet(quorumNumber); | ||
} | ||
|
||
/** | ||
* | ||
* VIEW FUNCTIONS | ||
* | ||
*/ | ||
|
||
/// @dev Returns a bitmap with all bits set up to `quorumCount`. Used for bit-masking quorum numbers | ||
/// and differentiating between operator sets and M2 quorums | ||
function m2QuorumBitmap() public view returns (uint256) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. "avsDirectoryQuorum"? |
||
// If operator sets are enabled, return the current m2 quorum bitmap | ||
if (operatorSetsEnabled) { | ||
return _m2QuorumBitmap; | ||
} | ||
|
||
return _getTotalQuorumBitmap(); | ||
} | ||
|
||
/// @notice Returns true if the quorum number is an M2 quorum | ||
function isM2Quorum( | ||
uint8 quorumNumber | ||
) external view returns (bool) { | ||
return _isM2Quorum(quorumNumber); | ||
} | ||
|
||
/** | ||
* @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 | ||
|
@@ -217,14 +247,4 @@ contract RegistryCoordinator is IRegistryCoordinator, SlashingRegistryCoordinato | |
) public view returns (bytes32) { | ||
return _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(SlashingRegistryCoordinator, ISlashingRegistryCoordinator) | ||
returns (address) | ||
{ | ||
return OwnableUpgradeable.owner(); | ||
} | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is RC interface inheriting IOwnable?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just the
owner()
interface is needed here but theSlashingRegistryCoordinator
does inheritOwnableUpgradeable