Skip to content

Commit d06ea7d

Browse files
committed
feat: slashing audit fixes
**Motivation:** Middleware slashing audit fixes from Hexens and Dedaub. **Modifications:** **Medium Sev** - Layr-Labs/eigenlayer-middleware#467 **Low Sev** - Layr-Labs/eigenlayer-middleware#462 - Layr-Labs/eigenlayer-middleware#463 - Layr-Labs/eigenlayer-middleware#468 - Layr-Labs/eigenlayer-middleware#474 - note: this removes stale stakes checks from the `BLSSignatureChecker` **Informational/Docs** - Layr-Labs/eigenlayer-middleware#465 - Layr-Labs/eigenlayer-middleware#466 - Layr-Labs/eigenlayer-middleware#472 **Result:** Audit fixes applied to slashing.
2 parents 40e6c55 + 311c049 commit d06ea7d

20 files changed

+152
-215
lines changed
600 KB
Binary file not shown.
7.32 MB
Binary file not shown.

docs/BLSSignatureChecker.md

Lines changed: 1 addition & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,6 @@ This method performs the following steps. Note that each step involves lookups o
9090
* `quorumNumbers` MUST be an ordered list of valid, initialized quorums
9191
* `params.nonSignerPubkeys` MUST ONLY contain unique pubkeys, in ascending order of their pubkey hash
9292
* For each quorum:
93-
* If stale stakes are forbidden (see [`BLSSignatureChecker.setStaleStakesForbidden`](#blssignaturecheckersetstalestakesforbidden)), check the last `quorumUpdateBlockNumber` is within `DelegationManager.minWithdrawalDelayBlocks` of `referenceBlockNumber`. This references a value in the EigenLayer core contracts - see [EigenLayer core docs][core-docs-dev] for more info.
9493
* Validate that each `params.quorumApks` corresponds to the quorum's apk at the `referenceBlockNumber`
9594
* For each historical state lookup, the `referenceBlockNumber` and provided index MUST point to a valid historical entry:
9695
* `referenceBlockNumber` MUST come after the entry's `updateBlockNumber`
@@ -172,28 +171,4 @@ For each quorum, this returns:
172171
* `uint32[] nonSignerQuorumBitmapIndices`: The indices in `RegistryCoordinator._operatorBitmapHistory` where each nonsigner's registered quorum bitmap can be found at `referenceBlockNumber`. Length is equal to the number of nonsigners included in `nonSignerOperatorIds`
173172
* `uint32[] quorumApkIndices`: The indices in `BLSApkRegistry.apkHistory` where the quorum's apk can be found at `referenceBlockNumber`. Length is equal to the number of quorums in `quorumNumbers`.
174173
* `uint32[] totalStakeIndices`: The indices in `StakeRegistry._totalStakeHistory` where each quorum's total stake can be found at `referenceBlockNumber`. Length is equal to the number of quorums in `quorumNumbers`.
175-
* `uint32[][] nonSignerStakeIndices`: For each quorum, a list of the indices of each nonsigner's `StakeRegistry.operatorStakeHistory` entry at `referenceBlockNumber`. Length is equal to the number of quorums in `quorumNumbers`, and each sub-list is equal in length to the number of nonsigners in `nonSignerOperatorIds` registered for that quorum at `referenceBlockNumber`
176-
177-
---
178-
179-
### System Configuration
180-
181-
#### `BLSSignatureChecker.setStaleStakesForbidden`
182-
183-
```solidity
184-
function setStaleStakesForbidden(
185-
bool value
186-
)
187-
external
188-
onlyCoordinatorOwner
189-
```
190-
191-
This method allows the `RegistryCoordinator` Owner to update `staleStakesForbidden` in the `BLSSignatureChecker`. If stale stakes are forbidden, `BLSSignatureChecker.checkSignatures` will perform an additional check when querying each quorum's apk, Operator stakes, and total stakes.
192-
193-
This additional check requires that each quorum was updated within a certain block window of the `referenceBlockNumber` passed into `BLSSignatureChecker.checkSignatures`.
194-
195-
*Effects*:
196-
* Sets `staleStakesForbidden` to `value`
197-
198-
*Requirements*:
199-
* Caller MUST be the `RegistryCoordinator` Owner
174+
* `uint32[][] nonSignerStakeIndices`: For each quorum, a list of the indices of each nonsigner's `StakeRegistry.operatorStakeHistory` entry at `referenceBlockNumber`. Length is equal to the number of quorums in `quorumNumbers`, and each sub-list is equal in length to the number of nonsigners in `nonSignerOperatorIds` registered for that quorum at `referenceBlockNumber`

src/BLSApkRegistry.sol

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ contract BLSApkRegistry is BLSApkRegistryStorage {
104104
)
105105
) % BN254.FR_MODULUS;
106106

107-
// e(sigma + P * gamma, [-1]_2) = e(H(m) + [1]_1 * gamma, P')
107+
// e(sigma + P * gamma, [1]_2) = e(H(m) + [1]_1 * gamma, P')
108108
require(
109109
BN254.pairing(
110110
params.pubkeyRegistrationSignature.plus(params.pubkeyG1.scalar_mul(gamma)),
@@ -314,7 +314,7 @@ contract BLSApkRegistry is BLSApkRegistryStorage {
314314
}
315315

316316
function _checkRegistryCoordinator() internal view {
317-
require(msg.sender == address(registryCoordinator), OnlyRegistryCoordinatorOwner());
317+
require(msg.sender == address(registryCoordinator), OnlyRegistryCoordinator());
318318
}
319319

320320
function _checkRegistryCoordinatorOwner() internal view {

src/BLSSignatureChecker.sol

Lines changed: 0 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -33,15 +33,6 @@ contract BLSSignatureChecker is BLSSignatureCheckerStorage {
3333
ISlashingRegistryCoordinator _registryCoordinator
3434
) BLSSignatureCheckerStorage(_registryCoordinator) {}
3535

36-
/// ACTIONS
37-
38-
/// @inheritdoc IBLSSignatureChecker
39-
function setStaleStakesForbidden(
40-
bool value
41-
) external onlyCoordinatorOwner {
42-
_setStaleStakesForbidden(value);
43-
}
44-
4536
/// VIEW
4637

4738
/// @inheritdoc IBLSSignatureChecker
@@ -138,21 +129,7 @@ contract BLSSignatureChecker is BLSSignatureCheckerStorage {
138129
* - subtract the stake for each nonsigner to calculate the stake belonging to signers
139130
*/
140131
{
141-
bool _staleStakesForbidden = staleStakesForbidden;
142-
uint256 withdrawalDelayBlocks =
143-
_staleStakesForbidden ? delegation.minWithdrawalDelayBlocks() : 0;
144-
145132
for (uint256 i = 0; i < quorumNumbers.length; i++) {
146-
// If we're disallowing stale stake updates, check that each quorum's last update block
147-
// is within withdrawalDelayBlocks
148-
if (_staleStakesForbidden) {
149-
require(
150-
registryCoordinator.quorumUpdateBlockNumber(uint8(quorumNumbers[i]))
151-
+ withdrawalDelayBlocks > referenceBlockNumber,
152-
StaleStakesForbidden()
153-
);
154-
}
155-
156133
// Validate params.quorumApks is correct for this quorum at the referenceBlockNumber,
157134
// then add it to the total apk
158135
require(
@@ -244,11 +221,4 @@ contract BLSSignatureChecker is BLSSignatureCheckerStorage {
244221
PAIRING_EQUALITY_CHECK_GAS
245222
);
246223
}
247-
248-
function _setStaleStakesForbidden(
249-
bool value
250-
) internal {
251-
staleStakesForbidden = value;
252-
emit StaleStakesForbiddenUpdate(value);
253-
}
254224
}

src/BLSSignatureCheckerStorage.sol

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ abstract contract BLSSignatureCheckerStorage is IBLSSignatureChecker {
2121

2222
/// STATE
2323

24-
/// @inheritdoc IBLSSignatureChecker
25-
bool public staleStakesForbidden;
24+
/// @dev Deprecated storage for the staleStakesForbidden flag.
25+
bool internal __deprecated_staleStakesForbidden;
2626

2727
constructor(
2828
ISlashingRegistryCoordinator _registryCoordinator

src/EjectionManager.sol

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -62,16 +62,6 @@ contract EjectionManager is OwnableUpgradeable, EjectionManagerStorage {
6262
) {
6363
ratelimitHit = true;
6464

65-
stakeForEjection += operatorStake;
66-
++ejectedOperators;
67-
68-
slashingRegistryCoordinator.ejectOperator(
69-
slashingRegistryCoordinator.getOperatorFromId(operatorIds[i][j]),
70-
abi.encodePacked(quorumNumber)
71-
);
72-
73-
emit OperatorEjected(operatorIds[i][j], quorumNumber);
74-
7565
break;
7666
}
7767

src/RegistryCoordinator.sol

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -187,10 +187,9 @@ contract RegistryCoordinator is SlashingRegistryCoordinator, RegistryCoordinator
187187
// filter out M2 quorums from the quorum numbers
188188
uint256 operatorSetBitmap =
189189
quorumNumbers.orderedBytesArrayToBitmap().minus(m2QuorumBitmap());
190-
if (!operatorSetBitmap.isEmpty()) {
191-
// call the parent _forceDeregisterOperator function for operator sets quorums
192-
super._forceDeregisterOperator(operator, operatorSetBitmap.bitmapToBytesArray());
193-
}
190+
191+
// call the parent _forceDeregisterOperator function for operator sets quorums
192+
super._forceDeregisterOperator(operator, operatorSetBitmap.bitmapToBytesArray());
194193
}
195194

196195
/// @dev Hook to prevent any new quorums from being created if operator sets are not enabled
@@ -235,10 +234,7 @@ contract RegistryCoordinator is SlashingRegistryCoordinator, RegistryCoordinator
235234

236235
/**
237236
* @dev Helper function to update operator stakes and deregister operators with insufficient stake
238-
* This function handles two cases:
239-
* 1. Operators who no longer meet the minimum stake requirement for a quorum
240-
* 2. Operators who have been force-deregistered from the AllocationManager but not from this contract
241-
* (e.g. due to out of gas errors in the deregistration callback)
237+
* This function handles Operators who no longer meet the minimum stake requirement for a quorum
242238
* @param operators The list of operators to check and update
243239
* @param operatorIds The corresponding operator IDs
244240
* @param quorumNumber The quorum number to check stakes for

src/ServiceManagerBase.sol

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ import {ISlashingRegistryCoordinator} from "./interfaces/ISlashingRegistryCoordi
2828
import {IStakeRegistry} from "./interfaces/IStakeRegistry.sol";
2929

3030
import {BitmapUtils} from "./libraries/BitmapUtils.sol";
31-
import {LibMergeSort} from "./libraries/LibMergeSort.sol";
3231

3332
/**
3433
* @title Minimal implementation of a ServiceManager-type contract.

src/SlashingRegistryCoordinator.sol

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -175,24 +175,13 @@ contract SlashingRegistryCoordinator is
175175
bytes32 operatorId = _getOrCreateOperatorId(operator, params);
176176

177177
if (registrationType == RegistrationType.NORMAL) {
178-
uint32[] memory numOperatorsPerQuorum = _registerOperator({
178+
_registerOperator({
179179
operator: operator,
180180
operatorId: operatorId,
181181
quorumNumbers: quorumNumbers,
182182
socket: socket,
183183
checkMaxOperatorCount: true
184184
}).numOperatorsPerQuorum;
185-
186-
// For each quorum, validate that the new operator count does not exceed the maximum
187-
// (If it does, an operator needs to be replaced -- see `registerOperatorWithChurn`)
188-
for (uint256 i = 0; i < quorumNumbers.length; i++) {
189-
uint8 quorumNumber = uint8(quorumNumbers[i]);
190-
191-
require(
192-
numOperatorsPerQuorum[i] <= _quorumParams[quorumNumber].maxOperatorCount,
193-
MaxOperatorCountReached()
194-
);
195-
}
196185
} else if (registrationType == RegistrationType.CHURN) {
197186
// Decode registration data from bytes
198187
(
@@ -677,6 +666,7 @@ contract SlashingRegistryCoordinator is
677666
/**
678667
* @notice Validates that an incoming operator is eligible to replace an existing
679668
* operator based on the stake of both
669+
* @dev In order to be churned out, the existing operator must be registered for the quorum
680670
* @dev In order to churn, the incoming operator needs to have more stake than the
681671
* existing operator by a proportion given by `kickBIPsOfOperatorStake`
682672
* @dev In order to be churned out, the existing operator needs to have a proportion
@@ -705,6 +695,13 @@ contract SlashingRegistryCoordinator is
705695
require(newOperator != operatorToKick, CannotChurnSelf());
706696
require(kickParams.quorumNumber == quorumNumber, QuorumOperatorCountMismatch());
707697

698+
uint192 quorumBitmap;
699+
quorumBitmap = uint192(BitmapUtils.setBit(quorumBitmap, quorumNumber));
700+
require(
701+
quorumBitmap.isSubsetOf(_currentOperatorBitmap(idToKick)),
702+
OperatorNotRegisteredForQuorum()
703+
);
704+
708705
// Get the target operator's stake and check that it is below the kick thresholds
709706
uint96 operatorToKickStake = stakeRegistry.getCurrentStake(idToKick, quorumNumber);
710707
require(
@@ -830,7 +827,8 @@ contract SlashingRegistryCoordinator is
830827
} else if (stakeType == IStakeRegistryTypes.StakeType.TOTAL_SLASHABLE) {
831828
// For slashable stake quorums, ensure lookAheadPeriod is less than DEALLOCATION_DELAY
832829
require(
833-
AllocationManager(address(allocationManager)).DEALLOCATION_DELAY() > lookAheadPeriod,
830+
lookAheadPeriod
831+
<= AllocationManager(address(allocationManager)).DEALLOCATION_DELAY(),
834832
LookAheadPeriodTooLong()
835833
);
836834
stakeRegistry.initializeSlashableStakeQuorum(

0 commit comments

Comments
 (0)