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

Optimize BLSSignatureChecker #76

Closed
wants to merge 5 commits into from
Closed
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
11 changes: 8 additions & 3 deletions src/BLSPubkeyRegistry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -215,8 +215,13 @@ contract BLSPubkeyRegistry is BLSPubkeyRegistryStorage {
return pubkeyCompendium.pubkeyHashToOperator(pubkeyHash);
}

function getOperatorId(address operator) public view returns (bytes32) {
(, bytes32 pubkeyHash) = pubkeyCompendium.getRegisteredPubkey(operator);
return pubkeyHash;
/// @notice Returns the pubkeyHash for the given `operator` address
function getOperatorId(address operator) public view returns (bytes32 pubkeyHash) {
(,pubkeyHash) = pubkeyCompendium.getRegisteredPubkey(operator);
}

/// @notice Returns the pubkey for the given `operator` address
function getOperatorPubkey(address operator) public view returns (BN254.G1Point memory pubkey) {
(pubkey,) = pubkeyCompendium.getRegisteredPubkey(operator);
}
}
100 changes: 38 additions & 62 deletions src/BLSSignatureChecker.sol
Original file line number Diff line number Diff line change
Expand Up @@ -69,96 +69,74 @@ contract BLSSignatureChecker is IBLSSignatureChecker {
bytes32
)
{
// verify the provided apk was the apk at referenceBlockNumber
// loop through every quorumNumber and keep track of the apk
QuorumStakeTotals memory quorumStakeTotals;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we have estimates of how much gas this saves

quorumStakeTotals.totalStakeForQuorum = new uint96[](quorumNumbers.length);
quorumStakeTotals.signedStakeForQuorum = new uint96[](quorumNumbers.length);
BN254.G1Point memory apk = BN254.G1Point(0, 0);
for (uint i = 0; i < quorumNumbers.length; i++) {
require(
bytes24(nonSignerStakesAndSignature.quorumApks[i].hashG1Point()) ==
IBLSPubkeyRegistry(blsPubkeyRegistry).getApkHashAtBlockNumberAndIndex(
blsPubkeyRegistry.getApkHashAtBlockNumberAndIndex(
uint8(quorumNumbers[i]),
referenceBlockNumber,
nonSignerStakesAndSignature.quorumApkIndices[i]
),
"BLSSignatureChecker.checkSignatures: quorumApk hash in storage does not match provided quorum apk"
);
apk = apk.plus(nonSignerStakesAndSignature.quorumApks[i]);

quorumStakeTotals.totalStakeForQuorum[i] =
stakeRegistry.getTotalStakeAtBlockNumberFromIndex(uint8(quorumNumbers[i]), referenceBlockNumber, nonSignerStakesAndSignature.totalStakeIndices[i]);
quorumStakeTotals.signedStakeForQuorum[i] = quorumStakeTotals.totalStakeForQuorum[i];
}

// initialize memory for the quorumStakeTotals
QuorumStakeTotals memory quorumStakeTotals;
quorumStakeTotals.totalStakeForQuorum = new uint96[](quorumNumbers.length);
quorumStakeTotals.signedStakeForQuorum = new uint96[](quorumNumbers.length);
// the pubkeyHashes of the nonSigners
bytes32[] memory nonSignerPubkeyHashes = new bytes32[](nonSignerStakesAndSignature.nonSignerPubkeys.length);
{
// the quorumBitmaps of the nonSigners
uint256[] memory nonSignerQuorumBitmaps = new uint256[](nonSignerStakesAndSignature.nonSignerPubkeys.length);
{
// the bitmap of the quorumNumbers
uint256 signingQuorumBitmap = BitmapUtils.bytesArrayToBitmap(quorumNumbers);
uint32[] memory nonSignerForQuorumIndices = new uint32[](quorumNumbers.length);
uint256 signingQuorumBitmap = BitmapUtils.bytesArrayToBitmap(quorumNumbers);

for (uint i = 0; i < nonSignerStakesAndSignature.nonSignerPubkeys.length; i++) {
nonSignerPubkeyHashes[i] = nonSignerStakesAndSignature.nonSignerPubkeys[i].hashG1Point();

// check that the nonSignerPubkeys are sorted and free of duplicates
if (i != 0) {
require(uint256(nonSignerPubkeyHashes[i]) > uint256(nonSignerPubkeyHashes[i - 1]), "BLSSignatureChecker.checkSignatures: nonSignerPubkeys not sorted");
}
for (uint i = 0; i < nonSignerStakesAndSignature.nonSignerPubkeys.length; i++) {
nonSignerPubkeyHashes[i] = nonSignerStakesAndSignature.nonSignerPubkeys[i].hashG1Point();

if (i != 0) {
require(uint256(nonSignerPubkeyHashes[i]) > uint256(nonSignerPubkeyHashes[i - 1]), "BLSSignatureChecker.checkSignatures: nonSignerPubkeys not sorted");
}

nonSignerQuorumBitmaps[i] =
registryCoordinator.getQuorumBitmapAtBlockNumberByIndex(
nonSignerPubkeyHashes[i],
referenceBlockNumber,
nonSignerStakesAndSignature.nonSignerQuorumBitmapIndices[i]
);

// subtract the nonSignerPubkey from the running apk to get the apk of all signers
apk = apk.plus(
nonSignerStakesAndSignature.nonSignerPubkeys[i]
.negate()
.scalar_mul_tiny(
BitmapUtils.countNumOnes(nonSignerQuorumBitmaps[i] & signingQuorumBitmap)
)
uint256 nonSignerQuorumBitmap =
registryCoordinator.getQuorumBitmapAtBlockNumberByIndex(
nonSignerPubkeyHashes[i],
referenceBlockNumber,
nonSignerStakesAndSignature.nonSignerQuorumBitmapIndices[i]
);
}
}
// loop through each quorum number
for (uint8 quorumNumberIndex = 0; quorumNumberIndex < quorumNumbers.length;) {
// get the quorum number
uint8 quorumNumber = uint8(quorumNumbers[quorumNumberIndex]);
// get the totalStake for the quorum at the referenceBlockNumber
quorumStakeTotals.totalStakeForQuorum[quorumNumberIndex] =
stakeRegistry.getTotalStakeAtBlockNumberFromIndex(quorumNumber, referenceBlockNumber, nonSignerStakesAndSignature.totalStakeIndices[quorumNumberIndex]);
// copy total stake to signed stake
quorumStakeTotals.signedStakeForQuorum[quorumNumberIndex] = quorumStakeTotals.totalStakeForQuorum[quorumNumberIndex];
// loop through all nonSigners, checking that they are a part of the quorum via their quorumBitmap
// if so, load their stake at referenceBlockNumber and subtract it from running stake signed
for (uint32 i = 0; i < nonSignerStakesAndSignature.nonSignerPubkeys.length; i++) {
// keep track of the nonSigners index in the quorum
uint32 nonSignerForQuorumIndex = 0;
// if the nonSigner is a part of the quorum, subtract their stake from the running total
if (BitmapUtils.numberIsInBitmap(nonSignerQuorumBitmaps[i], quorumNumber)) {

apk = apk.plus(
nonSignerStakesAndSignature.nonSignerPubkeys[i]
.negate()
.scalar_mul_tiny(
BitmapUtils.countNumOnes(nonSignerQuorumBitmap & signingQuorumBitmap)
)
);

for (uint8 quorumNumberIndex = 0; quorumNumberIndex < quorumNumbers.length;) {
if (BitmapUtils.numberIsInBitmap(nonSignerQuorumBitmap, uint8(quorumNumbers[quorumNumberIndex]))) {
quorumStakeTotals.signedStakeForQuorum[quorumNumberIndex] -=
stakeRegistry.getStakeAtBlockNumberAndIndex(
quorumNumber,
uint8(quorumNumbers[quorumNumberIndex]),
referenceBlockNumber,
nonSignerPubkeyHashes[i],
nonSignerStakesAndSignature.nonSignerStakeIndices[quorumNumberIndex][nonSignerForQuorumIndex]
nonSignerStakesAndSignature.nonSignerStakeIndices[quorumNumberIndex][nonSignerForQuorumIndices[quorumNumberIndex]]
);
unchecked {
++nonSignerForQuorumIndex;
++nonSignerForQuorumIndices[quorumNumberIndex];
}
}
}

unchecked {
++quorumNumberIndex;
unchecked {
++quorumNumberIndex;
}
}
}
}
{
// verify the signature
(bool pairingSuccessful, bool signatureIsValid) = trySignatureAndApkVerification(
msgHash,
apk,
Expand All @@ -168,10 +146,8 @@ contract BLSSignatureChecker is IBLSSignatureChecker {
require(pairingSuccessful, "BLSSignatureChecker.checkSignatures: pairing precompile call failed");
require(signatureIsValid, "BLSSignatureChecker.checkSignatures: signature is invalid");
}
// set signatoryRecordHash variable used for fraudproofs
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why deleting comments

bytes32 signatoryRecordHash = keccak256(abi.encodePacked(referenceBlockNumber, nonSignerPubkeyHashes));

// return the total stakes that signed for each quorum, and a hash of the information required to prove the exact signers and stake
return (quorumStakeTotals, signatoryRecordHash);
}

Expand Down
2 changes: 2 additions & 0 deletions src/interfaces/IBLSPubkeyRegistry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -87,4 +87,6 @@ interface IBLSPubkeyRegistry is IRegistry {
function getApkHashAtBlockNumberAndIndex(uint8 quorumNumber, uint32 blockNumber, uint256 index) external view returns (bytes24);

function getOperatorId(address operator) external view returns (bytes32);

function getOperatorPubkey(address operator) external view returns (BN254.G1Point memory);
}
2 changes: 1 addition & 1 deletion src/libraries/BN254.sol
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ library BN254 {
uint8 i = 0;

//loop until we reach the most significant bit
while(s > m){
while(s >= m){
unchecked {
// if the current bit is 1, add the 2^n*p to the accumulated product
if ((s >> i) & 1 == 1) {
Expand Down