Skip to content

Commit e07f2bc

Browse files
gpsanant8sunyuan
authored andcommitted
feat: add updateOperators
1 parent bde687d commit e07f2bc

File tree

3 files changed

+126
-88
lines changed

3 files changed

+126
-88
lines changed

src/SlashingRegistryCoordinator.sol

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,34 @@ contract SlashingRegistryCoordinator is
225225
_deregisterOperator(operator, quorumNumbers);
226226
}
227227

228+
/// @inheritdoc ISlashingRegistryCoordinator
229+
function updateOperators(
230+
address[] memory operators
231+
) external override onlyWhenNotPaused(PAUSED_UPDATE_OPERATOR) {
232+
for (uint256 i = 0; i < operators.length; i++) {
233+
// create single-element arrays for the operator and operatorId
234+
address[] memory singleOperator = new address[](1);
235+
singleOperator[0] = operators[i];
236+
bytes32[] memory singleOperatorId = new bytes32[](1);
237+
singleOperatorId[0] = _operatorInfo[operators[i]].operatorId;
238+
239+
uint192 currentBitmap = _currentOperatorBitmap(singleOperatorId[0]);
240+
bytes memory quorumNumbers = currentBitmap.bitmapToBytesArray();
241+
for (uint256 j = 0; j < quorumNumbers.length; j++) {
242+
// update the operator's stake for each quorum
243+
uint8 quorumNumber = uint8(quorumNumbers[j]);
244+
bool[] memory shouldBeDeregistered =
245+
stakeRegistry.updateOperatorsStake(singleOperator, singleOperatorId, quorumNumber);
246+
247+
if (shouldBeDeregistered[0]) {
248+
bytes memory singleQuorumNumber = new bytes(1);
249+
singleQuorumNumber[0] = quorumNumbers[j];
250+
_deregisterOperator(operators[i], singleQuorumNumber);
251+
}
252+
}
253+
}
254+
}
255+
228256
/// @inheritdoc ISlashingRegistryCoordinator
229257
function updateOperatorsForQuorum(
230258
address[][] memory operatorsPerQuorum,
@@ -270,13 +298,12 @@ contract SlashingRegistryCoordinator is
270298

271299
prevOperatorAddress = operator;
272300
}
273-
bytes memory quorumNumberBytes = new bytes(1);
274-
quorumNumberBytes[0] = bytes1(quorumNumber);
301+
275302
bool[] memory shouldBeDeregistered =
276303
stakeRegistry.updateOperatorsStake(currQuorumOperators, operatorIds, quorumNumber);
277304
for (uint256 j = 0; j < currQuorumOperators.length; ++j) {
278305
if (shouldBeDeregistered[j]) {
279-
_deregisterOperator(currQuorumOperators[j], quorumNumberBytes);
306+
_deregisterOperator(currQuorumOperators[j], quorumNumbers[i:i+1]);
280307
}
281308
}
282309

src/interfaces/ISlashingRegistryCoordinator.sol

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,17 @@ interface ISlashingRegistryCoordinator is
321321
*/
322322
function deregisterOperator(address operator, uint32[] memory operatorSetIds) external;
323323

324+
/**
325+
* @notice Updates stake weights for specified operators. If any operator is found to be below
326+
* the minimum stake for their registered quorums, they are deregistered from those quorums.
327+
* @param operators The operators whose stakes should be updated.
328+
* @dev Stakes are queried from the Eigenlayer core DelegationManager contract.
329+
* @dev WILL BE DEPRECATED IN FAVOR OF updateOperatorsForQuorum
330+
*/
331+
function updateOperators(
332+
address[] memory operators
333+
) external;
334+
324335
/**
325336
* @notice For each quorum in `quorumNumbers`, updates the StakeRegistry's view of ALL its registered operators' stakes.
326337
* Each quorum's `quorumUpdateBlockNumber` is also updated, which tracks the most recent block number when ALL registered

test/unit/RegistryCoordinatorUnit.t.sol

Lines changed: 85 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -1955,91 +1955,91 @@ contract RegistryCoordinatorUnitTests_RegisterOperatorWithChurn is RegistryCoord
19551955
}
19561956

19571957
contract RegistryCoordinatorUnitTests_UpdateOperators is RegistryCoordinatorUnitTests {
1958-
// function test_updateOperators_revert_paused() public {
1959-
// cheats.prank(pauser);
1960-
// registryCoordinator.pause(2 ** PAUSED_UPDATE_OPERATOR);
1961-
1962-
// address[] memory operatorsToUpdate = new address[](1);
1963-
// operatorsToUpdate[0] = defaultOperator;
1964-
1965-
// cheats.expectRevert(bytes4(keccak256("CurrentlyPaused()")));
1966-
// registryCoordinator.updateOperators(operatorsToUpdate);
1967-
// }
1968-
1969-
// // @notice tests the `updateOperators` function with a single registered operator as input
1970-
// function test_updateOperators_singleOperator() public {
1971-
// // register the default operator
1972-
// ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig;
1973-
// uint32 registrationBlockNumber = 100;
1974-
// bytes memory quorumNumbers = new bytes(1);
1975-
// quorumNumbers[0] = bytes1(defaultQuorumNumber);
1976-
// _setOperatorWeight(defaultOperator, uint8(quorumNumbers[0]), defaultStake);
1977-
// cheats.startPrank(defaultOperator);
1978-
// cheats.roll(registrationBlockNumber);
1979-
// registryCoordinator.registerOperator(
1980-
// quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig
1981-
// );
1982-
1983-
// address[] memory operatorsToUpdate = new address[](1);
1984-
// operatorsToUpdate[0] = defaultOperator;
1985-
1986-
// registryCoordinator.updateOperators(operatorsToUpdate);
1987-
// }
1988-
1989-
// // @notice tests the `updateOperators` function with a single registered operator as input
1990-
// // @dev also sets up return data from the StakeRegistry
1991-
// function testFuzz_updateOperators_singleOperator(
1992-
// uint192 registrationBitmap,
1993-
// uint192 mockReturnData
1994-
// ) public {
1995-
// // filter fuzzed inputs to only valid inputs
1996-
// cheats.assume(registrationBitmap != 0);
1997-
// mockReturnData = (mockReturnData & registrationBitmap);
1998-
// emit log_named_uint("mockReturnData", mockReturnData);
1999-
2000-
// // register the default operator
2001-
// ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig;
2002-
// uint32 registrationBlockNumber = 100;
2003-
// bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(registrationBitmap);
2004-
// for (uint256 i = 0; i < quorumNumbers.length; ++i) {
2005-
// _setOperatorWeight(defaultOperator, uint8(quorumNumbers[i]), defaultStake);
2006-
// }
2007-
// cheats.startPrank(defaultOperator);
2008-
// cheats.roll(registrationBlockNumber);
2009-
// registryCoordinator.registerOperator(
2010-
// quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig
2011-
// );
2012-
2013-
// address[] memory operatorsToUpdate = new address[](1);
2014-
// operatorsToUpdate[0] = defaultOperator;
2015-
2016-
// uint192 quorumBitmapBefore = registryCoordinator.getCurrentQuorumBitmap(defaultOperatorId);
2017-
// assertEq(quorumBitmapBefore, registrationBitmap, "operator bitmap somehow incorrect");
2018-
2019-
// // make the stake registry return info that the operator should be removed from quorums
2020-
// uint192 quorumBitmapToRemove = mockReturnData;
2021-
// bytes memory quorumNumbersToRemove = BitmapUtils.bitmapToBytesArray(quorumBitmapToRemove);
2022-
// for (uint256 i = 0; i < quorumNumbersToRemove.length; ++i) {
2023-
// _setOperatorWeight(defaultOperator, uint8(quorumNumbersToRemove[i]), 0);
2024-
// }
2025-
// uint256 expectedQuorumBitmap = BitmapUtils.minus(quorumBitmapBefore, quorumBitmapToRemove);
2026-
2027-
// registryCoordinator.updateOperators(operatorsToUpdate);
2028-
// uint192 quorumBitmapAfter = registryCoordinator.getCurrentQuorumBitmap(defaultOperatorId);
2029-
// assertEq(expectedQuorumBitmap, quorumBitmapAfter, "quorum bitmap did not update correctly");
2030-
// }
2031-
2032-
// // @notice tests the `updateOperators` function with a single *un*registered operator as input
2033-
// function test_updateOperators_unregisteredOperator() public view {
2034-
// address[] memory operatorsToUpdate = new address[](1);
2035-
// operatorsToUpdate[0] = defaultOperator;
2036-
2037-
// // force a staticcall to the `updateOperators` function -- this should *pass* because the call should be a strict no-op!
2038-
// (bool success,) = address(registryCoordinator).staticcall(
2039-
// abi.encodeWithSignature("updateOperators(address[])", operatorsToUpdate)
2040-
// );
2041-
// require(success, "staticcall failed!");
2042-
// }
1958+
function test_updateOperators_revert_paused() public {
1959+
cheats.prank(pauser);
1960+
registryCoordinator.pause(2 ** PAUSED_UPDATE_OPERATOR);
1961+
1962+
address[] memory operatorsToUpdate = new address[](1);
1963+
operatorsToUpdate[0] = defaultOperator;
1964+
1965+
cheats.expectRevert(bytes4(keccak256("CurrentlyPaused()")));
1966+
registryCoordinator.updateOperators(operatorsToUpdate);
1967+
}
1968+
1969+
// @notice tests the `updateOperators` function with a single registered operator as input
1970+
function test_updateOperators_singleOperator() public {
1971+
// register the default operator
1972+
ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig;
1973+
uint32 registrationBlockNumber = 100;
1974+
bytes memory quorumNumbers = new bytes(1);
1975+
quorumNumbers[0] = bytes1(defaultQuorumNumber);
1976+
_setOperatorWeight(defaultOperator, uint8(quorumNumbers[0]), defaultStake);
1977+
cheats.startPrank(defaultOperator);
1978+
cheats.roll(registrationBlockNumber);
1979+
registryCoordinator.registerOperator(
1980+
quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig
1981+
);
1982+
1983+
address[] memory operatorsToUpdate = new address[](1);
1984+
operatorsToUpdate[0] = defaultOperator;
1985+
1986+
registryCoordinator.updateOperators(operatorsToUpdate);
1987+
}
1988+
1989+
// @notice tests the `updateOperators` function with a single registered operator as input
1990+
// @dev also sets up return data from the StakeRegistry
1991+
function testFuzz_updateOperators_singleOperator(
1992+
uint192 registrationBitmap,
1993+
uint192 mockReturnData
1994+
) public {
1995+
// filter fuzzed inputs to only valid inputs
1996+
cheats.assume(registrationBitmap != 0);
1997+
mockReturnData = (mockReturnData & registrationBitmap);
1998+
emit log_named_uint("mockReturnData", mockReturnData);
1999+
2000+
// register the default operator
2001+
ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig;
2002+
uint32 registrationBlockNumber = 100;
2003+
bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(registrationBitmap);
2004+
for (uint256 i = 0; i < quorumNumbers.length; ++i) {
2005+
_setOperatorWeight(defaultOperator, uint8(quorumNumbers[i]), defaultStake);
2006+
}
2007+
cheats.startPrank(defaultOperator);
2008+
cheats.roll(registrationBlockNumber);
2009+
registryCoordinator.registerOperator(
2010+
quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig
2011+
);
2012+
2013+
address[] memory operatorsToUpdate = new address[](1);
2014+
operatorsToUpdate[0] = defaultOperator;
2015+
2016+
uint192 quorumBitmapBefore = registryCoordinator.getCurrentQuorumBitmap(defaultOperatorId);
2017+
assertEq(quorumBitmapBefore, registrationBitmap, "operator bitmap somehow incorrect");
2018+
2019+
// make the stake registry return info that the operator should be removed from quorums
2020+
uint192 quorumBitmapToRemove = mockReturnData;
2021+
bytes memory quorumNumbersToRemove = BitmapUtils.bitmapToBytesArray(quorumBitmapToRemove);
2022+
for (uint256 i = 0; i < quorumNumbersToRemove.length; ++i) {
2023+
_setOperatorWeight(defaultOperator, uint8(quorumNumbersToRemove[i]), 0);
2024+
}
2025+
uint256 expectedQuorumBitmap = BitmapUtils.minus(quorumBitmapBefore, quorumBitmapToRemove);
2026+
2027+
registryCoordinator.updateOperators(operatorsToUpdate);
2028+
uint192 quorumBitmapAfter = registryCoordinator.getCurrentQuorumBitmap(defaultOperatorId);
2029+
assertEq(expectedQuorumBitmap, quorumBitmapAfter, "quorum bitmap did not update correctly");
2030+
}
2031+
2032+
// @notice tests the `updateOperators` function with a single *un*registered operator as input
2033+
function test_updateOperators_unregisteredOperator() public view {
2034+
address[] memory operatorsToUpdate = new address[](1);
2035+
operatorsToUpdate[0] = defaultOperator;
2036+
2037+
// force a staticcall to the `updateOperators` function -- this should *pass* because the call should be a strict no-op!
2038+
(bool success,) = address(registryCoordinator).staticcall(
2039+
abi.encodeWithSignature("updateOperators(address[])", operatorsToUpdate)
2040+
);
2041+
require(success, "staticcall failed!");
2042+
}
20432043

20442044
function test_updateOperatorsForQuorum_revert_paused() public {
20452045
cheats.prank(pauser);

0 commit comments

Comments
 (0)