diff --git a/src/BLSSignatureChecker.sol b/src/BLSSignatureChecker.sol index 32d83793..b071d8e9 100644 --- a/src/BLSSignatureChecker.sol +++ b/src/BLSSignatureChecker.sol @@ -19,8 +19,8 @@ contract BLSSignatureChecker is IBLSSignatureChecker { using BN254 for BN254.G1Point; // CONSTANTS & IMMUTABLES - bytes internal constant ALIGNED_QUORUM_NUMBER = hex"00"; - + bytes internal constant ALIGNED_QUORUM_NUMBER = hex"00"; // TODO check if we can remove this constant without affecting the contract storage + // gas cost of multiplying 2 pairings uint256 internal constant PAIRING_EQUALITY_CHECK_GAS = 120_000; @@ -80,6 +80,7 @@ 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 @@ -87,14 +88,20 @@ contract BLSSignatureChecker is IBLSSignatureChecker { */ function checkSignatures( bytes32 msgHash, + bytes calldata quorumNumbers, uint32 referenceBlockNumber, NonSignerStakesAndSignature memory params ) public view returns (QuorumStakeTotals memory, bytes32) { require( - (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), + 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), "BLSSignatureChecker.checkSignatures: input quorum length mismatch" ); @@ -121,8 +128,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[](ALIGNED_QUORUM_NUMBER.length); - stakeTotals.signedStakeForQuorum = new uint96[](ALIGNED_QUORUM_NUMBER.length); + stakeTotals.totalStakeForQuorum = new uint96[](quorumNumbers.length); + stakeTotals.signedStakeForQuorum = new uint96[](quorumNumbers.length); NonSignerInfo memory nonSigners; nonSigners.quorumBitmaps = new uint256[]( @@ -134,7 +141,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( - ALIGNED_QUORUM_NUMBER, + quorumNumbers, registryCoordinator.quorumCount() ); @@ -191,13 +198,13 @@ contract BLSSignatureChecker is IBLSSignatureChecker { ? delegation.minWithdrawalDelayBlocks() : 0; - for (uint256 i = 0; i < ALIGNED_QUORUM_NUMBER.length; i++) { + for (uint256 i = 0; i < quorumNumbers.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(ALIGNED_QUORUM_NUMBER[i]) + uint8(quorumNumbers[i]) ) + withdrawalDelayBlocks > referenceBlockNumber, @@ -210,7 +217,7 @@ contract BLSSignatureChecker is IBLSSignatureChecker { require( bytes24(params.quorumApks[i].hashG1Point()) == blsApkRegistry.getApkHashAtBlockNumberAndIndex({ - quorumNumber: uint8(ALIGNED_QUORUM_NUMBER[i]), + quorumNumber: uint8(quorumNumbers[i]), blockNumber: referenceBlockNumber, index: params.quorumApkIndices[i] }), @@ -221,7 +228,7 @@ contract BLSSignatureChecker is IBLSSignatureChecker { // Get the total and starting signed stake for the quorum at referenceBlockNumber stakeTotals.totalStakeForQuorum[i] = stakeRegistry .getTotalStakeAtBlockNumberFromIndex({ - quorumNumber: uint8(ALIGNED_QUORUM_NUMBER[i]), + quorumNumber: uint8(quorumNumbers[i]), blockNumber: referenceBlockNumber, index: params.totalStakeIndices[i] }); @@ -238,12 +245,12 @@ contract BLSSignatureChecker is IBLSSignatureChecker { if ( BitmapUtils.isSet( nonSigners.quorumBitmaps[j], - uint8(ALIGNED_QUORUM_NUMBER[i]) + uint8(quorumNumbers[i]) ) ) { stakeTotals.signedStakeForQuorum[i] -= stakeRegistry .getStakeAtBlockNumberAndIndex({ - quorumNumber: uint8(ALIGNED_QUORUM_NUMBER[i]), + quorumNumber: uint8(quorumNumbers[i]), blockNumber: referenceBlockNumber, operatorId: nonSigners.pubkeyHashes[j], index: params.nonSignerStakeIndices[i][ diff --git a/src/RegistryCoordinator.sol b/src/RegistryCoordinator.sol index 00b4ddea..66cb0635 100644 --- a/src/RegistryCoordinator.sol +++ b/src/RegistryCoordinator.sol @@ -155,10 +155,14 @@ 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`) - require( - numOperatorsPerQuorum[0] <= _quorumParams[0].maxOperatorCount, - "qMaxOp" - ); + for (uint256 i = 0; i < quorumNumbers.length; i++) { + uint8 quorumNumber = uint8(quorumNumbers[i]); + + require( + numOperatorsPerQuorum[i] <= _quorumParams[quorumNumber].maxOperatorCount, + "c" + ); + } } /** @@ -212,25 +216,26 @@ 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 - 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 - }); + for (uint256 i = 0; i < quorumNumbers.length; i++) { + OperatorSetParam memory operatorSetParams = _quorumParams[uint8(quorumNumbers[i])]; - _deregisterOperator(operatorKickParams[0].operator, quorumNumbers[0:1]); + /** + * 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]); + } } - } /** diff --git a/src/interfaces/IBLSSignatureChecker.sol b/src/interfaces/IBLSSignatureChecker.sol index 6e75f930..b158f4f6 100644 --- a/src/interfaces/IBLSSignatureChecker.sol +++ b/src/interfaces/IBLSSignatureChecker.sol @@ -68,7 +68,8 @@ interface IBLSSignatureChecker { * for the total stake (or the operator) or latest before the referenceBlockNumber. */ function checkSignatures( - bytes32 msgHash, + bytes32 msgHash, + bytes calldata quorumNumbers, uint32 referenceBlockNumber, NonSignerStakesAndSignature memory nonSignerStakesAndSignature )