Skip to content

Commit 05ec37d

Browse files
committed
fix: tests and _afterDeregisterOperator hook
1 parent e07f2bc commit 05ec37d

7 files changed

+70
-35
lines changed

src/RegistryCoordinator.sol

+27-13
Original file line numberDiff line numberDiff line change
@@ -126,9 +126,11 @@ contract RegistryCoordinator is RegistryCoordinatorStorage {
126126
bytes memory quorumNumbers
127127
) external override onlyWhenNotPaused(PAUSED_DEREGISTER_OPERATOR) {
128128
// Check that the quorum numbers are M2 quorums
129-
for (uint256 i = 0; i < quorumNumbers.length; i++) {
130-
require(_isM2Quorum(uint8(quorumNumbers[i])), OperatorSetQuorum());
131-
}
129+
require(
130+
quorumNumbers.orderedBytesArrayToBitmap().isSubsetOf(m2QuorumBitmap()),
131+
OnlyM2QuorumsAllowed()
132+
);
133+
132134
_deregisterOperator({operator: msg.sender, quorumNumbers: quorumNumbers});
133135
}
134136

@@ -154,7 +156,8 @@ contract RegistryCoordinator is RegistryCoordinatorStorage {
154156
bytes memory quorumNumbers
155157
) internal virtual override {
156158
// filter out M2 quorums from the quorum numbers
157-
uint256 operatorSetBitmap = quorumNumbers.orderedBytesArrayToBitmap().minus(m2QuorumBitmap());
159+
uint256 operatorSetBitmap =
160+
quorumNumbers.orderedBytesArrayToBitmap().minus(m2QuorumBitmap());
158161
if (!operatorSetBitmap.isEmpty()) {
159162
// call the parent _forceDeregisterOperator function for operator sets quorums
160163
super._forceDeregisterOperator(operator, operatorSetBitmap.bitmapToBytesArray());
@@ -180,22 +183,22 @@ contract RegistryCoordinator is RegistryCoordinatorStorage {
180183
bytes memory,
181184
uint192 newBitmap
182185
) internal virtual override {
183-
uint256 operatorM2QuorumBitmap = newBitmap.minus(m2QuorumBitmap());
186+
// Bitmap representing all quorums including M2 and OperatorSet quorums
187+
uint256 totalQuorumBitmap = _getTotalQuorumBitmap();
188+
// Bitmap representing only OperatorSet quorums. Equal to 0 if operatorSets not enabled
189+
uint256 operatorSetQuorumBitmap = totalQuorumBitmap.minus(m2QuorumBitmap());
190+
// Operators updated M2 quorum bitmap, clear all the bits of operatorSetQuorumBitmap which gives the
191+
// operator's M2 quorum bitmap.
192+
uint256 operatorM2QuorumBitmap = newBitmap.minus(operatorSetQuorumBitmap);
184193
// If the operator is no longer registered for any M2 quorums, update their status and deregister
185194
// them from the AVS via the EigenLayer core contracts
186195
if (operatorM2QuorumBitmap.isEmpty()) {
187196
serviceManager.deregisterOperatorFromAVS(operator);
188197
}
189198
}
190199

191-
/// @dev Returns a bitmap with all bits set up to `quorumCount`. Used for bit-masking quorum numbers
192-
/// and differentiating between operator sets and M2 quorums
193-
function m2QuorumBitmap() public view returns (uint256) {
194-
// If operator sets are enabled, return the current m2 quorum bitmap
195-
if (operatorSetsEnabled) {
196-
return _m2QuorumBitmap;
197-
}
198-
200+
/// @notice Return bitmap representing all quorums(Legacy M2 and OperatorSet) quorums
201+
function _getTotalQuorumBitmap() internal view returns (uint256) {
199202
// This creates a number where all bits up to quorumCount are set to 1
200203
// For example:
201204
// quorumCount = 3 -> 0111 (7 in decimal)
@@ -218,6 +221,17 @@ contract RegistryCoordinator is RegistryCoordinatorStorage {
218221
*
219222
*/
220223

224+
/// @dev Returns a bitmap with all bits set up to `quorumCount`. Used for bit-masking quorum numbers
225+
/// and differentiating between operator sets and M2 quorums
226+
function m2QuorumBitmap() public view returns (uint256) {
227+
// If operator sets are enabled, return the current m2 quorum bitmap
228+
if (operatorSetsEnabled) {
229+
return _m2QuorumBitmap;
230+
}
231+
232+
return _getTotalQuorumBitmap();
233+
}
234+
221235
/// @notice Returns true if the quorum number is an M2 quorum
222236
function isM2Quorum(
223237
uint8 quorumNumber

src/SlashingRegistryCoordinator.sol

+5-4
Original file line numberDiff line numberDiff line change
@@ -241,9 +241,10 @@ contract SlashingRegistryCoordinator is
241241
for (uint256 j = 0; j < quorumNumbers.length; j++) {
242242
// update the operator's stake for each quorum
243243
uint8 quorumNumber = uint8(quorumNumbers[j]);
244-
bool[] memory shouldBeDeregistered =
245-
stakeRegistry.updateOperatorsStake(singleOperator, singleOperatorId, quorumNumber);
246-
244+
bool[] memory shouldBeDeregistered = stakeRegistry.updateOperatorsStake(
245+
singleOperator, singleOperatorId, quorumNumber
246+
);
247+
247248
if (shouldBeDeregistered[0]) {
248249
bytes memory singleQuorumNumber = new bytes(1);
249250
singleQuorumNumber[0] = quorumNumbers[j];
@@ -303,7 +304,7 @@ contract SlashingRegistryCoordinator is
303304
stakeRegistry.updateOperatorsStake(currQuorumOperators, operatorIds, quorumNumber);
304305
for (uint256 j = 0; j < currQuorumOperators.length; ++j) {
305306
if (shouldBeDeregistered[j]) {
306-
_deregisterOperator(currQuorumOperators[j], quorumNumbers[i:i+1]);
307+
_deregisterOperator(currQuorumOperators[j], quorumNumbers[i:i + 1]);
307308
}
308309
}
309310

test/integration/CoreRegistration.t.sol

+8-2
Original file line numberDiff line numberDiff line change
@@ -188,14 +188,20 @@ contract Test_CoreRegistration is MockAVSDeployer {
188188
emit log_named_bytes("quorumNumbers", quorumNumbers);
189189
_registerOperator(quorumNumbers);
190190

191+
IAVSDirectoryTypes.OperatorAVSRegistrationStatus operatorStatus =
192+
avsDirectory.avsOperatorStatus(address(serviceManager), operator);
193+
assertEq(
194+
uint8(operatorStatus),
195+
uint8(IAVSDirectoryTypes.OperatorAVSRegistrationStatus.REGISTERED)
196+
);
197+
191198
// Deregister Operator with single quorum
192199
quorumNumbers = new bytes(1);
193200
cheats.prank(operator);
194201
registryCoordinator.deregisterOperator(quorumNumbers);
195202

196203
// Check operator is still registered
197-
IAVSDirectoryTypes.OperatorAVSRegistrationStatus operatorStatus =
198-
avsDirectory.avsOperatorStatus(address(serviceManager), operator);
204+
operatorStatus = avsDirectory.avsOperatorStatus(address(serviceManager), operator);
199205
assertEq(
200206
uint8(operatorStatus),
201207
uint8(IAVSDirectoryTypes.OperatorAVSRegistrationStatus.REGISTERED)

test/integration/IntegrationConfig.t.sol

+7
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,13 @@ contract IntegrationConfig is IntegrationDeployer, G2Operations, Constants {
185185
});
186186
}
187187

188+
/// Setup the RegistryCoordinator as M2 RegistryCoordinator with M2 quorums
189+
/// TODO: refactor Integration framework to test both M2 upgrade path and new
190+
/// registration/deregistration flow of operatorSets
191+
_setOperatorSetsEnabled(false);
192+
_setM2QuorumsDisabled(false);
193+
_setM2QuorumBitmap(0);
194+
188195
// Decide how many operators to register for each quorum initially
189196
uint256 initialOperators = _randInitialOperators(operatorSet);
190197
emit log(

test/integration/IntegrationDeployer.t.sol

+13-9
Original file line numberDiff line numberDiff line change
@@ -419,11 +419,7 @@ abstract contract IntegrationDeployer is Test, IUserDeployer {
419419
churnApprover,
420420
ejector,
421421
0, /*initialPausedStatus*/
422-
new IRegistryCoordinator.OperatorSetParam[](0),
423-
new uint96[](0),
424-
new IStakeRegistryTypes.StrategyParams[][](0),
425-
quorumStakeTypes,
426-
slashableStakeQuorumLookAheadPeriods
422+
address(serviceManager) /* accountIdentifier */
427423
)
428424
);
429425

@@ -465,6 +461,7 @@ abstract contract IntegrationDeployer is Test, IUserDeployer {
465461

466462
_setOperatorSetsEnabled(false);
467463
_setM2QuorumsDisabled(false);
464+
_setM2QuorumBitmap(0);
468465
}
469466

470467
/// @notice Overwrite RegistryCoordinator.operatorSetsEnabled to the specified value.
@@ -474,14 +471,14 @@ abstract contract IntegrationDeployer is Test, IUserDeployer {
474471
) internal {
475472
// 1. First read the current value of the entire slot
476473
// which holds operatorSetsEnabled, m2QuorumsDisabled, and accountIdentifier
477-
bytes32 currentSlot = cheats.load(address(registryCoordinator), bytes32(uint256(161)));
474+
bytes32 currentSlot = cheats.load(address(registryCoordinator), bytes32(uint256(200)));
478475

479476
// 2. Clear only the first byte (operatorSetsEnabled) while keeping the rest
480477
bytes32 newSlot = (currentSlot & ~bytes32(uint256(0xff)))
481478
| bytes32(uint256(operatorSetsEnabled ? 0x01 : 0x00));
482479

483480
// 3. Store the modified slot
484-
cheats.store(address(registryCoordinator), bytes32(uint256(161)), newSlot);
481+
cheats.store(address(registryCoordinator), bytes32(uint256(200)), newSlot);
485482
}
486483

487484
/// @notice Overwrite RegistryCoordinator.m2QuorumsDisabled to the specified value.
@@ -490,14 +487,21 @@ abstract contract IntegrationDeployer is Test, IUserDeployer {
490487
) internal {
491488
// 1. First read the current value of the entire slot
492489
// which holds operatorSetsEnabled, m2QuorumsDisabled, and accountIdentifier
493-
bytes32 currentSlot = cheats.load(address(registryCoordinator), bytes32(uint256(161)));
490+
bytes32 currentSlot = cheats.load(address(registryCoordinator), bytes32(uint256(200)));
494491

495492
// 2. Clear only the second byte (m2QuorumsDisabled) while keeping the rest
496493
bytes32 newSlot = (currentSlot & ~bytes32(uint256(0xff) << 8))
497494
| bytes32(uint256(m2QuorumsDisabled ? 0x01 : 0x00) << 8);
498495

499496
// 3. Store the modified slot
500-
cheats.store(address(registryCoordinator), bytes32(uint256(161)), newSlot);
497+
cheats.store(address(registryCoordinator), bytes32(uint256(200)), newSlot);
498+
}
499+
500+
/// @notice Overwrite RegistryCoordinator._m2QuorumBitmap to the specified value
501+
function _setM2QuorumBitmap(uint256 m2QuorumBitmap) internal {
502+
bytes32 currentSlot = cheats.load(address(registryCoordinator), bytes32(uint256(200)));
503+
504+
cheats.store(address(registryCoordinator), bytes32(uint256(200)), bytes32(m2QuorumBitmap));
501505
}
502506

503507
/// @dev Deploy a strategy and its underlying token, push to global lists of tokens/strategies, and whitelist

test/integration/User.t.sol

+2
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,8 @@ contract User is Test {
254254
for (uint256 j = 0; j < operatorIds.length; j++) {
255255
operatorsPerQuorum[i][j] = blsApkRegistry.pubkeyHashToOperator(operatorIds[j]);
256256
}
257+
258+
operatorsPerQuorum[i] = Sort.sortAddresses(operatorsPerQuorum[i]);
257259
}
258260
registryCoordinator.updateOperatorsForQuorum(operatorsPerQuorum, quorumNumbers);
259261
}

test/unit/StakeRegistryUnit.t.sol

+8-7
Original file line numberDiff line numberDiff line change
@@ -1817,11 +1817,12 @@ contract StakeRegistryUnitTests_StakeUpdates is StakeRegistryUnitTests {
18171817

18181818
// Get a list of valid quorums ending in an invalid quorum number
18191819
bytes memory invalidQuorums = _fuzz_getInvalidQuorums(rand);
1820+
uint256 length = invalidQuorums.length;
18201821

18211822
cheats.expectRevert(IStakeRegistryErrors.QuorumDoesNotExist.selector);
18221823
cheats.prank(address(registryCoordinator));
18231824
stakeRegistry.updateOperatorsStake(
1824-
_wrap(setup.operator), _wrap(setup.operatorId), uint8(invalidQuorums[0])
1825+
_wrap(setup.operator), _wrap(setup.operatorId), uint8(invalidQuorums[length - 1])
18251826
);
18261827
}
18271828

@@ -1848,13 +1849,13 @@ contract StakeRegistryUnitTests_StakeUpdates is StakeRegistryUnitTests {
18481849
_getLatestTotalStakeUpdates(setup.quorumNumbers);
18491850

18501851
// updateOperatorStake
1851-
cheats.prank(address(registryCoordinator));
18521852
bool[] memory shouldBeDeregistered = new bool[](setup.quorumNumbers.length);
18531853
for (uint256 i = 0; i < setup.quorumNumbers.length; i++) {
1854+
cheats.prank(address(registryCoordinator));
18541855
bool[] memory shouldBeDeregisteredForQuorum = stakeRegistry.updateOperatorsStake(
18551856
_wrap(setup.operator), _wrap(setup.operatorId), uint8(setup.quorumNumbers[i])
18561857
);
1857-
shouldBeDeregistered[i] = shouldBeDeregisteredForQuorum[i];
1858+
shouldBeDeregistered[i] = shouldBeDeregisteredForQuorum[0];
18581859
}
18591860

18601861
// Get ending state
@@ -1971,8 +1972,8 @@ contract StakeRegistryUnitTests_StakeUpdates is StakeRegistryUnitTests {
19711972
UpdateSetup memory setup = setups[i];
19721973

19731974
// updateOperatorStake
1974-
cheats.prank(address(registryCoordinator));
19751975
for (uint256 j = 0; j < setup.quorumNumbers.length; j++) {
1976+
cheats.prank(address(registryCoordinator));
19761977
stakeRegistry.updateOperatorsStake(
19771978
_wrap(setup.operator), _wrap(setup.operatorId), uint8(setup.quorumNumbers[j])
19781979
);
@@ -2065,14 +2066,14 @@ contract StakeRegistryUnitTests_StakeUpdates is StakeRegistryUnitTests {
20652066
uint256 currBlock = startBlock + j;
20662067
cheats.roll(currBlock);
20672068

2068-
// updateOperatorStake
2069-
cheats.prank(address(registryCoordinator));
2069+
// updateOperatorsStake
20702070
bool[] memory shouldBeDeregistered = new bool[](setup.quorumNumbers.length);
20712071
for (uint256 i = 0; i < setup.quorumNumbers.length; i++) {
2072+
cheats.prank(address(registryCoordinator));
20722073
bool[] memory shouldBeDeregisteredForQuorum = stakeRegistry.updateOperatorsStake(
20732074
_wrap(setup.operator), _wrap(setup.operatorId), uint8(setup.quorumNumbers[i])
20742075
);
2075-
shouldBeDeregistered[i] = shouldBeDeregisteredForQuorum[i];
2076+
shouldBeDeregistered[i] = shouldBeDeregisteredForQuorum[0];
20762077
}
20772078

20782079
// Get ending state

0 commit comments

Comments
 (0)