Skip to content

Commit 9994210

Browse files
gpsanant8sunyuan
andauthored
refactor: slashing (#379)
* chore: start refactor * fix: quorum creation * fix: bug in registration * feat: refactor avs sync * chore: respond to review * Update src/SlashingRegistryCoordinator.sol Co-authored-by: Michael Sun <[email protected]> * fix: m2 registration validity * feat: lazy migration * test: fixes and refactor of weightOfOperators * feat: add updateOperators * fix: tests and `_afterDeregisterOperator` hook * refactor: remove operatorSetEnabled as require * chore: fmt * chore: fmt test files * fix: failing ci test * chore: test fmt --------- Co-authored-by: Michael Sun <[email protected]> Co-authored-by: Michael Sun <[email protected]>
1 parent 6c1eeb2 commit 9994210

23 files changed

+611
-517
lines changed

src/BLSSignatureChecker.sol

+6-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
// SPDX-License-Identifier: BUSL-1.1
22
pragma solidity ^0.8.27;
33

4+
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
5+
46
import {BitmapUtils} from "./libraries/BitmapUtils.sol";
57
import {BN254} from "./libraries/BN254.sol";
68

@@ -18,7 +20,10 @@ contract BLSSignatureChecker is BLSSignatureCheckerStorage {
1820
/// MODIFIERS
1921

2022
modifier onlyCoordinatorOwner() {
21-
require(msg.sender == registryCoordinator.owner(), OnlyRegistryCoordinatorOwner());
23+
require(
24+
msg.sender == Ownable(address(registryCoordinator)).owner(),
25+
OnlyRegistryCoordinatorOwner()
26+
);
2227
_;
2328
}
2429

src/RegistryCoordinator.sol

+115-95
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {BitmapUtils} from "./libraries/BitmapUtils.sol";
1515
import {SlashingRegistryCoordinator} from "./SlashingRegistryCoordinator.sol";
1616
import {ISlashingRegistryCoordinator} from "./interfaces/ISlashingRegistryCoordinator.sol";
1717
import {OwnableUpgradeable} from "@openzeppelin-upgrades/contracts/access/OwnableUpgradeable.sol";
18+
import {RegistryCoordinatorStorage} from "./RegistryCoordinatorStorage.sol";
1819

1920
/**
2021
* @title A `RegistryCoordinator` that has three registries:
@@ -24,12 +25,9 @@ import {OwnableUpgradeable} from "@openzeppelin-upgrades/contracts/access/Ownabl
2425
*
2526
* @author Layr Labs, Inc.
2627
*/
27-
contract RegistryCoordinator is IRegistryCoordinator, SlashingRegistryCoordinator {
28+
contract RegistryCoordinator is RegistryCoordinatorStorage {
2829
using BitmapUtils for *;
2930

30-
/// @notice the ServiceManager for this AVS, which forwards calls onto EigenLayer's core contracts
31-
IServiceManager public immutable serviceManager;
32-
3331
constructor(
3432
IServiceManager _serviceManager,
3533
IStakeRegistry _stakeRegistry,
@@ -39,17 +37,16 @@ contract RegistryCoordinator is IRegistryCoordinator, SlashingRegistryCoordinato
3937
IAllocationManager _allocationManager,
4038
IPauserRegistry _pauserRegistry
4139
)
42-
SlashingRegistryCoordinator(
40+
RegistryCoordinatorStorage(
41+
_serviceManager,
4342
_stakeRegistry,
4443
_blsApkRegistry,
4544
_indexRegistry,
4645
_socketRegistry,
4746
_allocationManager,
4847
_pauserRegistry
4948
)
50-
{
51-
serviceManager = _serviceManager;
52-
}
49+
{}
5350

5451
/**
5552
*
@@ -64,44 +61,28 @@ contract RegistryCoordinator is IRegistryCoordinator, SlashingRegistryCoordinato
6461
IBLSApkRegistryTypes.PubkeyRegistrationParams memory params,
6562
SignatureWithSaltAndExpiry memory operatorSignature
6663
) external onlyWhenNotPaused(PAUSED_REGISTER_OPERATOR) {
67-
require(!m2QuorumsDisabled, M2QuorumsAlreadyDisabled());
68-
/**
69-
* If the operator has NEVER registered a pubkey before, use `params` to register
70-
* their pubkey in blsApkRegistry
71-
*
72-
* If the operator HAS registered a pubkey, `params` is ignored and the pubkey hash
73-
* (operatorId) is fetched instead
74-
*/
75-
bytes32 operatorId = _getOrCreateOperatorId(msg.sender, params);
76-
77-
// Register the operator in each of the registry contracts and update the operator's
78-
// quorum bitmap and registration status
79-
uint32[] memory numOperatorsPerQuorum = _registerOperator({
64+
require(!isM2QuorumRegistrationDisabled, M2QuorumRegistrationIsDisabled());
65+
require(
66+
quorumNumbers.orderedBytesArrayToBitmap().isSubsetOf(m2QuorumBitmap()),
67+
OnlyM2QuorumsAllowed()
68+
);
69+
70+
// Check if the operator has registered before
71+
bool operatorRegisteredBefore =
72+
_operatorInfo[msg.sender].status == OperatorStatus.REGISTERED;
73+
74+
// register the operator with the registry coordinator
75+
_registerOperator({
8076
operator: msg.sender,
81-
operatorId: operatorId,
77+
operatorId: _getOrCreateOperatorId(msg.sender, params),
8278
quorumNumbers: quorumNumbers,
83-
socket: socket
84-
}).numOperatorsPerQuorum;
85-
86-
// For each quorum, validate that the new operator count does not exceed the maximum
87-
// (If it does, an operator needs to be replaced -- see `registerOperatorWithChurn`)
88-
for (uint256 i = 0; i < quorumNumbers.length; i++) {
89-
uint8 quorumNumber = uint8(quorumNumbers[i]);
90-
91-
require(
92-
numOperatorsPerQuorum[i] <= _quorumParams[quorumNumber].maxOperatorCount,
93-
MaxQuorumsReached()
94-
);
95-
}
96-
97-
// If the operator wasn't registered for any quorums, update their status
98-
// and register them with this AVS in EigenLayer core (DelegationManager)
99-
if (_operatorInfo[msg.sender].status != OperatorStatus.REGISTERED) {
100-
_operatorInfo[msg.sender] =
101-
OperatorInfo({operatorId: operatorId, status: OperatorStatus.REGISTERED});
79+
socket: socket,
80+
checkMaxOperatorCount: true
81+
});
10282

83+
// If the operator has never registered before, register them with the AVSDirectory
84+
if (!operatorRegisteredBefore) {
10385
serviceManager.registerOperatorToAVS(msg.sender, operatorSignature);
104-
emit OperatorRegistered(msg.sender, operatorId);
10586
}
10687
}
10788

@@ -114,34 +95,29 @@ contract RegistryCoordinator is IRegistryCoordinator, SlashingRegistryCoordinato
11495
SignatureWithSaltAndExpiry memory churnApproverSignature,
11596
SignatureWithSaltAndExpiry memory operatorSignature
11697
) external onlyWhenNotPaused(PAUSED_REGISTER_OPERATOR) {
117-
require(!m2QuorumsDisabled, M2QuorumsAlreadyDisabled());
98+
require(!isM2QuorumRegistrationDisabled, M2QuorumRegistrationIsDisabled());
99+
require(
100+
quorumNumbers.orderedBytesArrayToBitmap().isSubsetOf(m2QuorumBitmap()),
101+
OnlyM2QuorumsAllowed()
102+
);
118103

119-
/**
120-
* If the operator has NEVER registered a pubkey before, use `params` to register
121-
* their pubkey in blsApkRegistry
122-
*
123-
* If the operator HAS registered a pubkey, `params` is ignored and the pubkey hash
124-
* (operatorId) is fetched instead
125-
*/
126-
bytes32 operatorId = _getOrCreateOperatorId(msg.sender, params);
104+
// Check if the operator has registered before
105+
bool operatorRegisteredBefore =
106+
_operatorInfo[msg.sender].status == OperatorStatus.REGISTERED;
127107

108+
// register the operator with the registry coordinator with churn
128109
_registerOperatorWithChurn({
129110
operator: msg.sender,
130-
operatorId: operatorId,
111+
operatorId: _getOrCreateOperatorId(msg.sender, params),
131112
quorumNumbers: quorumNumbers,
132113
socket: socket,
133114
operatorKickParams: operatorKickParams,
134115
churnApproverSignature: churnApproverSignature
135116
});
136117

137-
// If the operator wasn't registered for any quorums, update their status
138-
// and register them with this AVS in EigenLayer core (DelegationManager)
139-
if (_operatorInfo[msg.sender].status != OperatorStatus.REGISTERED) {
140-
_operatorInfo[msg.sender] =
141-
OperatorInfo({operatorId: operatorId, status: OperatorStatus.REGISTERED});
142-
118+
// If the operator has never registered before, register them with the AVSDirectory
119+
if (!operatorRegisteredBefore) {
143120
serviceManager.registerOperatorToAVS(msg.sender, operatorSignature);
144-
emit OperatorRegistered(msg.sender, operatorId);
145121
}
146122
}
147123

@@ -150,56 +126,78 @@ contract RegistryCoordinator is IRegistryCoordinator, SlashingRegistryCoordinato
150126
bytes memory quorumNumbers
151127
) external override onlyWhenNotPaused(PAUSED_DEREGISTER_OPERATOR) {
152128
// Check that the quorum numbers are M2 quorums
153-
for (uint256 i = 0; i < quorumNumbers.length; i++) {
154-
require(
155-
!operatorSetsEnabled || _isM2Quorum(uint8(quorumNumbers[i])), OperatorSetQuorum()
156-
);
157-
}
129+
require(
130+
quorumNumbers.orderedBytesArrayToBitmap().isSubsetOf(m2QuorumBitmap()),
131+
OnlyM2QuorumsAllowed()
132+
);
133+
158134
_deregisterOperator({operator: msg.sender, quorumNumbers: quorumNumbers});
159135
}
160136

161137
/// @inheritdoc IRegistryCoordinator
162-
function enableOperatorSets() external onlyOwner {
163-
require(!operatorSetsEnabled, OperatorSetsAlreadyEnabled());
164-
165-
// Set the bitmap for M2 quorums
166-
M2quorumBitmap = _getQuorumBitmap(quorumCount);
138+
function disableM2QuorumRegistration() external onlyOwner {
139+
require(!isM2QuorumRegistrationDisabled, M2QuorumRegistrationIsDisabled());
167140

168-
// Enable operator sets mode
169-
operatorSetsEnabled = true;
141+
isM2QuorumRegistrationDisabled = true;
170142

171-
emit OperatorSetsEnabled();
143+
emit M2QuorumRegistrationDisabled();
172144
}
173145

174-
/// @inheritdoc IRegistryCoordinator
175-
function disableM2QuorumRegistration() external onlyOwner {
176-
require(operatorSetsEnabled, OperatorSetsNotEnabled());
146+
/**
147+
*
148+
* INTERNAL FUNCTIONS
149+
*
150+
*/
177151

178-
m2QuorumsDisabled = true;
152+
/// @dev override the _forceDeregisterOperator function to handle M2 quorum deregistration
153+
function _forceDeregisterOperator(
154+
address operator,
155+
bytes memory quorumNumbers
156+
) internal virtual override {
157+
// filter out M2 quorums from the quorum numbers
158+
uint256 operatorSetBitmap =
159+
quorumNumbers.orderedBytesArrayToBitmap().minus(m2QuorumBitmap());
160+
if (!operatorSetBitmap.isEmpty()) {
161+
// call the parent _forceDeregisterOperator function for operator sets quorums
162+
super._forceDeregisterOperator(operator, operatorSetBitmap.bitmapToBytesArray());
163+
}
164+
}
179165

180-
emit M2QuorumsDisabled();
166+
/// @dev Hook to prevent any new quorums from being created if operator sets are not enabled
167+
function _beforeCreateQuorum(
168+
uint8
169+
) internal virtual override {
170+
// If operator sets are not enabled, set the m2 quorum bitmap to the current m2 quorum bitmap
171+
// and enable operator sets
172+
if (!operatorSetsEnabled) {
173+
_m2QuorumBitmap = m2QuorumBitmap();
174+
operatorSetsEnabled = true;
175+
}
181176
}
182177

183178
/// @dev Hook to allow for any post-deregister logic
184179
function _afterDeregisterOperator(
185180
address operator,
186-
bytes32 operatorId,
187-
bytes memory quorumNumbers,
181+
bytes32,
182+
bytes memory,
188183
uint192 newBitmap
189184
) internal virtual override {
190-
uint256 operatorM2QuorumBitmap = newBitmap.minus(M2quorumBitmap);
185+
// Bitmap representing all quorums including M2 and OperatorSet quorums
186+
uint256 totalQuorumBitmap = _getTotalQuorumBitmap();
187+
// Bitmap representing only OperatorSet quorums. Equal to 0 if operatorSets not enabled
188+
uint256 operatorSetQuorumBitmap = totalQuorumBitmap.minus(m2QuorumBitmap());
189+
// Operators updated M2 quorum bitmap, clear all the bits of operatorSetQuorumBitmap which gives the
190+
// operator's M2 quorum bitmap.
191+
uint256 operatorM2QuorumBitmap = newBitmap.minus(operatorSetQuorumBitmap);
191192
// If the operator is no longer registered for any M2 quorums, update their status and deregister
192193
// them from the AVS via the EigenLayer core contracts
193194
if (operatorM2QuorumBitmap.isEmpty()) {
194195
serviceManager.deregisterOperatorFromAVS(operator);
195196
}
196197
}
197198

198-
/// @dev Returns a bitmap with all bits set up to `quorumCount`. Used for bit-masking quorum numbers
199-
/// and differentiating between operator sets and M2 quorums
200-
function _getQuorumBitmap(
201-
uint256 quorumCount
202-
) internal pure returns (uint256) {
199+
/// @notice Return bitmap representing all quorums(Legacy M2 and OperatorSet) quorums
200+
function _getTotalQuorumBitmap() internal view returns (uint256) {
203201
// This creates a number where all bits up to quorumCount are set to 1
204202
// For example:
205203
// quorumCount = 3 -> 0111 (7 in decimal)
@@ -208,6 +206,38 @@ contract RegistryCoordinator is IRegistryCoordinator, SlashingRegistryCoordinato
208206
return (1 << quorumCount) - 1;
209207
}
210208

209+
/// @notice Returns true if the quorum number is an M2 quorum
210+
/// @dev We use bitwise and to check if the quorum number is an M2 quorum
211+
function _isM2Quorum(
212+
uint8 quorumNumber
213+
) internal view returns (bool) {
214+
return m2QuorumBitmap().isSet(quorumNumber);
215+
}
216+
217+
/**
218+
*
219+
* VIEW FUNCTIONS
220+
*
221+
*/
222+
223+
/// @dev Returns a bitmap with all bits set up to `quorumCount`. Used for bit-masking quorum numbers
224+
/// and differentiating between operator sets and M2 quorums
225+
function m2QuorumBitmap() public view returns (uint256) {
226+
// If operator sets are enabled, return the current m2 quorum bitmap
227+
if (operatorSetsEnabled) {
228+
return _m2QuorumBitmap;
229+
}
230+
231+
return _getTotalQuorumBitmap();
232+
}
233+
234+
/// @notice Returns true if the quorum number is an M2 quorum
235+
function isM2Quorum(
236+
uint8 quorumNumber
237+
) external view returns (bool) {
238+
return _isM2Quorum(quorumNumber);
239+
}
240+
211241
/**
212242
* @notice Returns the message hash that an operator must sign to register their BLS public key.
213243
* @param operator is the address of the operator registering their BLS public key
@@ -217,14 +247,4 @@ contract RegistryCoordinator is IRegistryCoordinator, SlashingRegistryCoordinato
217247
) public view returns (bytes32) {
218248
return _hashTypedDataV4(keccak256(abi.encode(PUBKEY_REGISTRATION_TYPEHASH, operator)));
219249
}
220-
221-
/// @dev need to override function here since its defined in both these contracts
222-
function owner()
223-
public
224-
view
225-
override(SlashingRegistryCoordinator, ISlashingRegistryCoordinator)
226-
returns (address)
227-
{
228-
return OwnableUpgradeable.owner();
229-
}
230250
}

0 commit comments

Comments
 (0)