Skip to content

Commit d0f40bd

Browse files
committed
fix: tests and _afterDeregisterOperator hook
1 parent 812860c commit d0f40bd

7 files changed

+70
-35
lines changed

src/RegistryCoordinator.sol

Lines changed: 27 additions & 13 deletions
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

Lines changed: 5 additions & 4 deletions
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

Lines changed: 8 additions & 2 deletions
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

Lines changed: 7 additions & 0 deletions
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

Lines changed: 13 additions & 9 deletions
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

Lines changed: 2 additions & 0 deletions
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

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1821,11 +1821,12 @@ contract StakeRegistryUnitTests_StakeUpdates is StakeRegistryUnitTests {
18211821

18221822
// Get a list of valid quorums ending in an invalid quorum number
18231823
bytes memory invalidQuorums = _fuzz_getInvalidQuorums(rand);
1824+
uint256 length = invalidQuorums.length;
18241825

18251826
cheats.expectRevert(IStakeRegistryErrors.QuorumDoesNotExist.selector);
18261827
cheats.prank(address(registryCoordinator));
18271828
stakeRegistry.updateOperatorsStake(
1828-
_wrap(setup.operator), _wrap(setup.operatorId), uint8(invalidQuorums[0])
1829+
_wrap(setup.operator), _wrap(setup.operatorId), uint8(invalidQuorums[length - 1])
18291830
);
18301831
}
18311832

@@ -1852,13 +1853,13 @@ contract StakeRegistryUnitTests_StakeUpdates is StakeRegistryUnitTests {
18521853
_getLatestTotalStakeUpdates(setup.quorumNumbers);
18531854

18541855
// updateOperatorStake
1855-
cheats.prank(address(registryCoordinator));
18561856
bool[] memory shouldBeDeregistered = new bool[](setup.quorumNumbers.length);
18571857
for (uint256 i = 0; i < setup.quorumNumbers.length; i++) {
1858+
cheats.prank(address(registryCoordinator));
18581859
bool[] memory shouldBeDeregisteredForQuorum = stakeRegistry.updateOperatorsStake(
18591860
_wrap(setup.operator), _wrap(setup.operatorId), uint8(setup.quorumNumbers[i])
18601861
);
1861-
shouldBeDeregistered[i] = shouldBeDeregisteredForQuorum[i];
1862+
shouldBeDeregistered[i] = shouldBeDeregisteredForQuorum[0];
18621863
}
18631864

18641865
// Get ending state
@@ -1975,8 +1976,8 @@ contract StakeRegistryUnitTests_StakeUpdates is StakeRegistryUnitTests {
19751976
UpdateSetup memory setup = setups[i];
19761977

19771978
// updateOperatorStake
1978-
cheats.prank(address(registryCoordinator));
19791979
for (uint256 j = 0; j < setup.quorumNumbers.length; j++) {
1980+
cheats.prank(address(registryCoordinator));
19801981
stakeRegistry.updateOperatorsStake(
19811982
_wrap(setup.operator), _wrap(setup.operatorId), uint8(setup.quorumNumbers[j])
19821983
);
@@ -2069,14 +2070,14 @@ contract StakeRegistryUnitTests_StakeUpdates is StakeRegistryUnitTests {
20692070
uint256 currBlock = startBlock + j;
20702071
cheats.roll(currBlock);
20712072

2072-
// updateOperatorStake
2073-
cheats.prank(address(registryCoordinator));
2073+
// updateOperatorsStake
20742074
bool[] memory shouldBeDeregistered = new bool[](setup.quorumNumbers.length);
20752075
for (uint256 i = 0; i < setup.quorumNumbers.length; i++) {
2076+
cheats.prank(address(registryCoordinator));
20762077
bool[] memory shouldBeDeregisteredForQuorum = stakeRegistry.updateOperatorsStake(
20772078
_wrap(setup.operator), _wrap(setup.operatorId), uint8(setup.quorumNumbers[i])
20782079
);
2079-
shouldBeDeregistered[i] = shouldBeDeregisteredForQuorum[i];
2080+
shouldBeDeregistered[i] = shouldBeDeregisteredForQuorum[0];
20802081
}
20812082

20822083
// Get ending state

0 commit comments

Comments
 (0)