Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Diff Aligned Middleware with Eigen Base Middleware #1

Draft
wants to merge 7 commits into
base: dev
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 14 additions & 20 deletions src/BLSSignatureChecker.sol
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ contract BLSSignatureChecker is IBLSSignatureChecker {
using BN254 for BN254.G1Point;

// CONSTANTS & IMMUTABLES
bytes internal constant ALIGNED_QUORUM_NUMBER = hex"00";

// gas cost of multiplying 2 pairings
uint256 internal constant PAIRING_EQUALITY_CHECK_GAS = 120_000;
Expand Down Expand Up @@ -79,28 +80,21 @@ contract BLSSignatureChecker is IBLSSignatureChecker {
* @dev NOTE: Be careful to ensure `msgHash` is collision-resistant! This method does not hash
* `msgHash` in any way, so if an attacker is able to pass in an arbitrary value, they may be able
* to tamper with signature verification.
* @param quorumNumbers is the bytes array of quorum numbers that are being signed for
* @param referenceBlockNumber is the block number at which the stake information is being verified
* @param params is the struct containing information on nonsigners, stakes, quorum apks, and the aggregate signature
* @return quorumStakeTotals is the struct containing the total and signed stake for each quorum
* @return signatoryRecordHash is the hash of the signatory record, which is used for fraud proofs
*/
function checkSignatures(
bytes32 msgHash,
bytes calldata quorumNumbers,
uint32 referenceBlockNumber,
NonSignerStakesAndSignature memory params
) public view returns (QuorumStakeTotals memory, bytes32) {
require(
quorumNumbers.length != 0,
"BLSSignatureChecker.checkSignatures: empty quorum input"
);

require(
(quorumNumbers.length == params.quorumApks.length) &&
(quorumNumbers.length == params.quorumApkIndices.length) &&
(quorumNumbers.length == params.totalStakeIndices.length) &&
(quorumNumbers.length == params.nonSignerStakeIndices.length),
(ALIGNED_QUORUM_NUMBER.length == params.quorumApks.length) &&
(ALIGNED_QUORUM_NUMBER.length == params.quorumApkIndices.length) &&
(ALIGNED_QUORUM_NUMBER.length == params.totalStakeIndices.length) &&
(ALIGNED_QUORUM_NUMBER.length == params.nonSignerStakeIndices.length),
"BLSSignatureChecker.checkSignatures: input quorum length mismatch"
);

Expand All @@ -127,8 +121,8 @@ contract BLSSignatureChecker is IBLSSignatureChecker {
// at the referenceBlockNumber, and derive the stake held by signers by subtracting out
// stakes held by nonsigners.
QuorumStakeTotals memory stakeTotals;
stakeTotals.totalStakeForQuorum = new uint96[](quorumNumbers.length);
stakeTotals.signedStakeForQuorum = new uint96[](quorumNumbers.length);
stakeTotals.totalStakeForQuorum = new uint96[](ALIGNED_QUORUM_NUMBER.length);
stakeTotals.signedStakeForQuorum = new uint96[](ALIGNED_QUORUM_NUMBER.length);

NonSignerInfo memory nonSigners;
nonSigners.quorumBitmaps = new uint256[](
Expand All @@ -140,7 +134,7 @@ contract BLSSignatureChecker is IBLSSignatureChecker {
// Get a bitmap of the quorums signing the message, and validate that
// quorumNumbers contains only unique, valid quorum numbers
uint256 signingQuorumBitmap = BitmapUtils.orderedBytesArrayToBitmap(
quorumNumbers,
ALIGNED_QUORUM_NUMBER,
registryCoordinator.quorumCount()
);

Expand Down Expand Up @@ -197,13 +191,13 @@ contract BLSSignatureChecker is IBLSSignatureChecker {
? delegation.minWithdrawalDelayBlocks()
: 0;

for (uint256 i = 0; i < quorumNumbers.length; i++) {
for (uint256 i = 0; i < ALIGNED_QUORUM_NUMBER.length; i++) {
// If we're disallowing stale stake updates, check that each quorum's last update block
// is within withdrawalDelayBlocks
if (_staleStakesForbidden) {
require(
registryCoordinator.quorumUpdateBlockNumber(
uint8(quorumNumbers[i])
uint8(ALIGNED_QUORUM_NUMBER[i])
) +
withdrawalDelayBlocks >
referenceBlockNumber,
Expand All @@ -216,7 +210,7 @@ contract BLSSignatureChecker is IBLSSignatureChecker {
require(
bytes24(params.quorumApks[i].hashG1Point()) ==
blsApkRegistry.getApkHashAtBlockNumberAndIndex({
quorumNumber: uint8(quorumNumbers[i]),
quorumNumber: uint8(ALIGNED_QUORUM_NUMBER[i]),
blockNumber: referenceBlockNumber,
index: params.quorumApkIndices[i]
}),
Expand All @@ -227,7 +221,7 @@ contract BLSSignatureChecker is IBLSSignatureChecker {
// Get the total and starting signed stake for the quorum at referenceBlockNumber
stakeTotals.totalStakeForQuorum[i] = stakeRegistry
.getTotalStakeAtBlockNumberFromIndex({
quorumNumber: uint8(quorumNumbers[i]),
quorumNumber: uint8(ALIGNED_QUORUM_NUMBER[i]),
blockNumber: referenceBlockNumber,
index: params.totalStakeIndices[i]
});
Expand All @@ -244,12 +238,12 @@ contract BLSSignatureChecker is IBLSSignatureChecker {
if (
BitmapUtils.isSet(
nonSigners.quorumBitmaps[j],
uint8(quorumNumbers[i])
uint8(ALIGNED_QUORUM_NUMBER[i])
)
) {
stakeTotals.signedStakeForQuorum[i] -= stakeRegistry
.getStakeAtBlockNumberAndIndex({
quorumNumber: uint8(quorumNumbers[i]),
quorumNumber: uint8(ALIGNED_QUORUM_NUMBER[i]),
blockNumber: referenceBlockNumber,
operatorId: nonSigners.pubkeyHashes[j],
index: params.nonSignerStakeIndices[i][
Expand Down
108 changes: 53 additions & 55 deletions src/RegistryCoordinator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import {EIP712} from "@openzeppelin/contracts/utils/cryptography/draft-EIP712.so
import {Pausable} from "eigenlayer-contracts/src/contracts/permissions/Pausable.sol";
import {RegistryCoordinatorStorage} from "./RegistryCoordinatorStorage.sol";

import {Whitelist} from "./Whitelist.sol";

/**
* @title A `RegistryCoordinator` that has three registries:
* 1) a `StakeRegistry` that keeps track of operators' stakes
Expand All @@ -36,13 +38,14 @@ contract RegistryCoordinator is
OwnableUpgradeable,
RegistryCoordinatorStorage,
ISocketUpdater,
ISignatureUtils
ISignatureUtils,
Whitelist
{
using BitmapUtils for *;
using BN254 for BN254.G1Point;

modifier onlyEjector {
require(msg.sender == ejector, "RegistryCoordinator.onlyEjector: caller is not the ejector");
require(msg.sender == ejector, "RC!ejector");
_;
}

Expand All @@ -51,7 +54,7 @@ contract RegistryCoordinator is
modifier quorumExists(uint8 quorumNumber) {
require(
quorumNumber < quorumCount,
"RegistryCoordinator.quorumExists: quorum does not exist"
"RC:q!E"
);
_;
}
Expand Down Expand Up @@ -91,7 +94,7 @@ contract RegistryCoordinator is
) external initializer {
require(
_operatorSetParams.length == _minimumStakes.length && _minimumStakes.length == _strategyParams.length,
"RegistryCoordinator.initialize: input length mismatch"
"IE"
);

// Initialize roles
Expand Down Expand Up @@ -130,7 +133,7 @@ contract RegistryCoordinator is
string calldata socket,
IBLSApkRegistry.PubkeyRegistrationParams calldata params,
SignatureWithSaltAndExpiry memory operatorSignature
) external onlyWhenNotPaused(PAUSED_REGISTER_OPERATOR) {
) external onlyWhenNotPaused(PAUSED_REGISTER_OPERATOR) onlyWhitelisted {
/**
* If the operator has NEVER registered a pubkey before, use `params` to register
* their pubkey in blsApkRegistry
Expand All @@ -152,14 +155,10 @@ contract RegistryCoordinator is

// For each quorum, validate that the new operator count does not exceed the maximum
// (If it does, an operator needs to be replaced -- see `registerOperatorWithChurn`)
for (uint256 i = 0; i < quorumNumbers.length; i++) {
uint8 quorumNumber = uint8(quorumNumbers[i]);

require(
numOperatorsPerQuorum[i] <= _quorumParams[quorumNumber].maxOperatorCount,
"RegistryCoordinator.registerOperator: operator count exceeds maximum"
);
}
require(
numOperatorsPerQuorum[0] <= _quorumParams[0].maxOperatorCount,
"qMaxOp"
);
}

/**
Expand All @@ -181,8 +180,8 @@ contract RegistryCoordinator is
OperatorKickParam[] calldata operatorKickParams,
SignatureWithSaltAndExpiry memory churnApproverSignature,
SignatureWithSaltAndExpiry memory operatorSignature
) external onlyWhenNotPaused(PAUSED_REGISTER_OPERATOR) {
require(operatorKickParams.length == quorumNumbers.length, "RegistryCoordinator.registerOperatorWithChurn: input length mismatch");
) external onlyWhenNotPaused(PAUSED_REGISTER_OPERATOR) onlyWhitelisted {
require(operatorKickParams.length == quorumNumbers.length, "qLen");

/**
* If the operator has NEVER registered a pubkey before, use `params` to register
Expand Down Expand Up @@ -213,26 +212,25 @@ contract RegistryCoordinator is

// Check that each quorum's operator count is below the configured maximum. If the max
// is exceeded, use `operatorKickParams` to deregister an existing operator to make space
for (uint256 i = 0; i < quorumNumbers.length; i++) {
OperatorSetParam memory operatorSetParams = _quorumParams[uint8(quorumNumbers[i])];

/**
* If the new operator count for any quorum exceeds the maximum, validate
* that churn can be performed, then deregister the specified operator
*/
if (results.numOperatorsPerQuorum[i] > operatorSetParams.maxOperatorCount) {
_validateChurn({
quorumNumber: uint8(quorumNumbers[i]),
totalQuorumStake: results.totalStakes[i],
newOperator: msg.sender,
newOperatorStake: results.operatorStakes[i],
kickParams: operatorKickParams[i],
setParams: operatorSetParams
});

_deregisterOperator(operatorKickParams[i].operator, quorumNumbers[i:i+1]);
}
OperatorSetParam memory operatorSetParams = _quorumParams[uint8(quorumNumbers[0])];

/**
* If the new operator count for any quorum exceeds the maximum, validate
* that churn can be performed, then deregister the specified operator
*/
if (results.numOperatorsPerQuorum[0] > operatorSetParams.maxOperatorCount) {
_validateChurn({
quorumNumber: uint8(quorumNumbers[0]),
totalQuorumStake: results.totalStakes[0],
newOperator: msg.sender,
newOperatorStake: results.operatorStakes[0],
kickParams: operatorKickParams[0],
setParams: operatorSetParams
});

_deregisterOperator(operatorKickParams[0].operator, quorumNumbers[0:1]);
}

}

/**
Expand Down Expand Up @@ -292,7 +290,7 @@ contract RegistryCoordinator is
uint192 quorumBitmap = uint192(BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers, quorumCount));
require(
operatorsPerQuorum.length == quorumNumbers.length,
"RegistryCoordinator.updateOperatorsForQuorum: input length mismatch"
"rcQLen"
);

// For each quorum, update ALL registered operators
Expand All @@ -303,7 +301,7 @@ contract RegistryCoordinator is
address[] calldata currQuorumOperators = operatorsPerQuorum[i];
require(
currQuorumOperators.length == indexRegistry.totalOperatorsForQuorum(quorumNumber),
"RegistryCoordinator.updateOperatorsForQuorum: number of updated operators does not match quorum total"
"rcQOpLen"
);

address prevOperatorAddress = address(0);
Expand All @@ -322,12 +320,12 @@ contract RegistryCoordinator is
// Check that the operator is registered
require(
BitmapUtils.isSet(currentBitmap, quorumNumber),
"RegistryCoordinator.updateOperatorsForQuorum: operator not in quorum"
"rcOp!Reg"
);
// Prevent duplicate operators
require(
operator > prevOperatorAddress,
"RegistryCoordinator.updateOperatorsForQuorum: operators array must be sorted in ascending address order"
"rcDupOp"
);
}

Expand All @@ -347,7 +345,7 @@ contract RegistryCoordinator is
* @param socket is the new socket of the operator
*/
function updateSocket(string memory socket) external {
require(_operatorInfo[msg.sender].status == OperatorStatus.REGISTERED, "RegistryCoordinator.updateSocket: operator is not registered");
require(_operatorInfo[msg.sender].status == OperatorStatus.REGISTERED, "o!Reg");
emit OperatorSocketUpdate(_operatorInfo[msg.sender].operatorId, socket);
}

Expand Down Expand Up @@ -476,12 +474,12 @@ contract RegistryCoordinator is
*/
uint192 quorumsToAdd = uint192(BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers, quorumCount));
uint192 currentBitmap = _currentOperatorBitmap(operatorId);
require(!quorumsToAdd.isEmpty(), "RegistryCoordinator._registerOperator: bitmap cannot be 0");
require(quorumsToAdd.noBitsInCommon(currentBitmap), "RegistryCoordinator._registerOperator: operator already registered for some quorums being registered for");
require(!quorumsToAdd.isEmpty(), "qEmp");
require(quorumsToAdd.noBitsInCommon(currentBitmap), "!Bit");
uint192 newBitmap = uint192(currentBitmap.plus(quorumsToAdd));

// Check that the operator can reregister if ejected
require(lastEjectionTimestamp[operator] + ejectionCooldown < block.timestamp, "RegistryCoordinator._registerOperator: operator cannot reregister yet");
require(lastEjectionTimestamp[operator] + ejectionCooldown < block.timestamp, "!opEj");

/**
* Update operator's bitmap, socket, and status. Only update operatorInfo if needed:
Expand Down Expand Up @@ -564,18 +562,18 @@ contract RegistryCoordinator is
) internal view {
address operatorToKick = kickParams.operator;
bytes32 idToKick = _operatorInfo[operatorToKick].operatorId;
require(newOperator != operatorToKick, "RegistryCoordinator._validateChurn: cannot churn self");
require(kickParams.quorumNumber == quorumNumber, "RegistryCoordinator._validateChurn: quorumNumber not the same as signed");
require(newOperator != operatorToKick, "ch0");
require(kickParams.quorumNumber == quorumNumber, "ch1");

// Get the target operator's stake and check that it is below the kick thresholds
uint96 operatorToKickStake = stakeRegistry.getCurrentStake(idToKick, quorumNumber);
require(
newOperatorStake > _individualKickThreshold(operatorToKickStake, setParams),
"RegistryCoordinator._validateChurn: incoming operator has insufficient stake for churn"
"ch2"
);
require(
operatorToKickStake < _totalKickThreshold(totalQuorumStake, setParams),
"RegistryCoordinator._validateChurn: cannot kick operator with more than kickBIPsOfTotalStake"
"ch3"
);
}

Expand All @@ -591,7 +589,7 @@ contract RegistryCoordinator is
// Fetch the operator's info and ensure they are registered
OperatorInfo storage operatorInfo = _operatorInfo[operator];
bytes32 operatorId = operatorInfo.operatorId;
require(operatorInfo.status == OperatorStatus.REGISTERED, "RegistryCoordinator._deregisterOperator: operator is not registered");
require(operatorInfo.status == OperatorStatus.REGISTERED, "o!Reg");

/**
* Get bitmap of quorums to deregister from and operator's current bitmap. Validate that:
Expand All @@ -602,8 +600,8 @@ contract RegistryCoordinator is
*/
uint192 quorumsToRemove = uint192(BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers, quorumCount));
uint192 currentBitmap = _currentOperatorBitmap(operatorId);
require(!quorumsToRemove.isEmpty(), "RegistryCoordinator._deregisterOperator: bitmap cannot be 0");
require(quorumsToRemove.isSubsetOf(currentBitmap), "RegistryCoordinator._deregisterOperator: operator is not registered for specified quorums");
require(!quorumsToRemove.isEmpty(), "qEmp");
require(quorumsToRemove.isSubsetOf(currentBitmap), "!Sub");
uint192 newBitmap = uint192(currentBitmap.minus(quorumsToRemove));

// Update operator's bitmap and status
Expand Down Expand Up @@ -675,8 +673,8 @@ contract RegistryCoordinator is
SignatureWithSaltAndExpiry memory churnApproverSignature
) internal {
// make sure the salt hasn't been used already
require(!isChurnApproverSaltUsed[churnApproverSignature.salt], "RegistryCoordinator._verifyChurnApproverSignature: churnApprover salt already used");
require(churnApproverSignature.expiry >= block.timestamp, "RegistryCoordinator._verifyChurnApproverSignature: churnApprover signature expired");
require(!isChurnApproverSaltUsed[churnApproverSignature.salt], "chSalt");
require(churnApproverSignature.expiry >= block.timestamp, "chExp");

// set salt used to true
isChurnApproverSaltUsed[churnApproverSignature.salt] = true;
Expand Down Expand Up @@ -704,7 +702,7 @@ contract RegistryCoordinator is
) internal {
// Increment the total quorum count. Fails if we're already at the max
uint8 prevQuorumCount = quorumCount;
require(prevQuorumCount < MAX_QUORUM_COUNT, "RegistryCoordinator.createQuorum: max quorums reached");
require(prevQuorumCount < MAX_QUORUM_COUNT, "qMax");
quorumCount = prevQuorumCount + 1;

// The previous count is the new quorum's number
Expand Down Expand Up @@ -786,7 +784,7 @@ contract RegistryCoordinator is
}

revert(
"RegistryCoordinator.getQuorumBitmapIndexAtBlockNumber: no bitmap update found for operatorId at block number"
"O!Reg"
);
}

Expand Down Expand Up @@ -870,11 +868,11 @@ contract RegistryCoordinator is
*/
require(
blockNumber >= quorumBitmapUpdate.updateBlockNumber,
"RegistryCoordinator.getQuorumBitmapAtBlockNumberByIndex: quorumBitmapUpdate is from after blockNumber"
"bn<"
);
require(
quorumBitmapUpdate.nextUpdateBlockNumber == 0 || blockNumber < quorumBitmapUpdate.nextUpdateBlockNumber,
"RegistryCoordinator.getQuorumBitmapAtBlockNumberByIndex: quorumBitmapUpdate is from before blockNumber"
"bn<2"
);

return quorumBitmapUpdate.quorumBitmap;
Expand Down
Loading