Skip to content

feat: enable multiple quorums on BLSSignatureChecker #6

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

Open
wants to merge 3 commits into
base: yac_one_quorum_prod_payments
Choose a base branch
from
Open
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
37 changes: 22 additions & 15 deletions src/BLSSignatureChecker.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -80,21 +80,28 @@ 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(
(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"
);

Expand All @@ -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[](
Expand All @@ -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()
);

Expand Down Expand Up @@ -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,
Expand All @@ -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]
}),
Expand All @@ -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]
});
Expand All @@ -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][
Expand Down
47 changes: 26 additions & 21 deletions src/RegistryCoordinator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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"
);
}
}

/**
Expand Down Expand Up @@ -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]);
}
}

}

/**
Expand Down
3 changes: 2 additions & 1 deletion src/interfaces/IBLSSignatureChecker.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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
)
Expand Down
Loading