Skip to content

Commit

Permalink
Merge pull request #102 from Layr-Labs/test-sig-checker
Browse files Browse the repository at this point in the history
Test sig checker
  • Loading branch information
gpsanant authored Aug 1, 2023
2 parents 2f25da7 + a013a64 commit 64ace4e
Show file tree
Hide file tree
Showing 9 changed files with 591 additions and 107 deletions.
75 changes: 75 additions & 0 deletions src/contracts/interfaces/IBLSSignatureChecker.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.12;

import "../interfaces/IBLSRegistryCoordinatorWithIndices.sol";
import "../libraries/MiddlewareUtils.sol";
import "../libraries/BN254.sol";
import "../libraries/BitmapUtils.sol";

/**
* @title Used for checking BLS aggregate signatures from the operators of a EigenLayer AVS with the RegistryCoordinator/BLSPubkeyRegistry/StakeRegistry architechture.
* @author Layr Labs, Inc.
* @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service
* @notice This is the contract for checking the validity of aggregate operator signatures.
*/
interface IBLSSignatureChecker {
// DATA STRUCTURES

struct NonSignerStakesAndSignature {
uint32[] nonSignerQuorumBitmapIndices;
BN254.G1Point[] nonSignerPubkeys;
BN254.G1Point[] quorumApks;
BN254.G2Point apkG2;
BN254.G1Point sigma;
uint32[] quorumApkIndices;
uint32[] totalStakeIndices;
uint32[][] nonSignerStakeIndices; // nonSignerStakeIndices[quorumNumberIndex][nonSignerIndex]
}

/**
* @notice this data structure is used for recording the details on the total stake of the registered
* operators and those operators who are part of the quorum for a particular taskNumber
*/

struct QuorumStakeTotals {
// total stake of the operators in each quorum
uint96[] signedStakeForQuorum;
// total amount staked by all operators in each quorum
uint96[] totalStakeForQuorum;
}

// CONSTANTS & IMMUTABLES

function registryCoordinator() external view returns (IRegistryCoordinator);
function stakeRegistry() external view returns (IStakeRegistry);
function blsPubkeyRegistry() external view returns (IBLSPubkeyRegistry);

/**
* @notice This function is called by disperser when it has aggregated all the signatures of the operators
* that are part of the quorum for a particular taskNumber and is asserting them into onchain. The function
* checks that the claim for aggregated signatures are valid.
*
* The thesis of this procedure entails:
* - getting the aggregated pubkey of all registered nodes at the time of pre-commit by the
* disperser (represented by apk in the parameters),
* - subtracting the pubkeys of all the signers not in the quorum (nonSignerPubkeys) and storing
* the output in apk to get aggregated pubkey of all operators that are part of quorum.
* - use this aggregated pubkey to verify the aggregated signature under BLS scheme.
*
* @dev Before signature verification, the function verifies operator stake information. This includes ensuring that the provided `referenceBlockNumber`
* is correct, i.e., ensure that the stake returned from the specified block number is recent enough and that the stake is either the most recent update
* for the total stake (or the operator) or latest before the referenceBlockNumber.
*/
function checkSignatures(
bytes32 msgHash,
bytes calldata quorumNumbers,
uint32 referenceBlockNumber,
NonSignerStakesAndSignature memory nonSignerStakesAndSignature
)
external
view
returns (
QuorumStakeTotals memory,
bytes32
);
}
45 changes: 29 additions & 16 deletions src/contracts/middleware/BLSOperatorStateRetriever.sol
Original file line number Diff line number Diff line change
Expand Up @@ -100,35 +100,48 @@ contract BLSOperatorStateRetriever {
IStakeRegistry stakeRegistry = registryCoordinator.stakeRegistry();
CheckSignaturesIndices memory checkSignaturesIndices;

// get the indices of the quorumBitmap updates for each of the operators in the nonSignerOperatorIds array
checkSignaturesIndices.nonSignerQuorumBitmapIndices = registryCoordinator.getQuorumBitmapIndicesByOperatorIdsAtBlockNumber(referenceBlockNumber, nonSignerOperatorIds);
// get the indices of the totalStake updates for each of the quorums in the quorumNumbers array
checkSignaturesIndices.totalStakeIndices = stakeRegistry.getTotalStakeIndicesByQuorumNumbersAtBlockNumber(referenceBlockNumber, quorumNumbers);

checkSignaturesIndices.nonSignerStakeIndices = new uint32[][](nonSignerOperatorIds.length);
for (uint i = 0; i < nonSignerOperatorIds.length; i++) {
uint192 nonSignerQuorumBitmap =
registryCoordinator.getQuorumBitmapByOperatorIdAtBlockNumberByIndex(
nonSignerOperatorIds[i],
referenceBlockNumber,
checkSignaturesIndices.nonSignerQuorumBitmapIndices[i]
);
// the number of quorums the operator was a part of that are also part of the provided quorumNumbers
checkSignaturesIndices.nonSignerStakeIndices[i] = new uint32[](BitmapUtils.countNumOnes(nonSignerQuorumBitmap & BitmapUtils.bytesArrayToBitmap(quorumNumbers)));
checkSignaturesIndices.nonSignerStakeIndices = new uint32[][](quorumNumbers.length);
for (uint8 quorumNumberIndex = 0; quorumNumberIndex < quorumNumbers.length; quorumNumberIndex++) {
uint256 numNonSignersForQuorum = 0;
// this array's length will be at most the number of nonSignerOperatorIds, this will be trimmed after it is filled
checkSignaturesIndices.nonSignerStakeIndices[quorumNumberIndex] = new uint32[](nonSignerOperatorIds.length);

uint256 stakeIndexIndex = 0;
for (uint8 j = 0; j < 192; j++) {
for (uint i = 0; i < nonSignerOperatorIds.length; i++) {
// get the quorumBitmap for the operator at the given blocknumber and index
uint192 nonSignerQuorumBitmap =
registryCoordinator.getQuorumBitmapByOperatorIdAtBlockNumberByIndex(
nonSignerOperatorIds[i],
referenceBlockNumber,
checkSignaturesIndices.nonSignerQuorumBitmapIndices[i]
);

// if the operator was a part of the quorum and the quorum is a part of the provided quorumNumbers
if (nonSignerQuorumBitmap >> j & (BitmapUtils.bytesArrayToBitmap(quorumNumbers) >> j & 1) == 1) {
checkSignaturesIndices.nonSignerStakeIndices[i][stakeIndexIndex] = stakeRegistry.getStakeUpdateIndexForOperatorIdForQuorumAtBlockNumber(
if ((nonSignerQuorumBitmap >> uint8(quorumNumbers[quorumNumberIndex])) & 1 == 1) {
// get the index of the stake update for the operator at the given blocknumber and quorum number
checkSignaturesIndices.nonSignerStakeIndices[quorumNumberIndex][numNonSignersForQuorum] = stakeRegistry.getStakeUpdateIndexForOperatorIdForQuorumAtBlockNumber(
nonSignerOperatorIds[i],
uint8(j),
uint8(quorumNumbers[quorumNumberIndex]),
referenceBlockNumber
);
stakeIndexIndex++;
numNonSignersForQuorum++;
}
}

// resize the array to the number of nonSigners for this quorum
uint32[] memory nonSignerStakeIndicesForQuorum = new uint32[](numNonSignersForQuorum);
for (uint i = 0; i < numNonSignersForQuorum; i++) {
nonSignerStakeIndicesForQuorum[i] = checkSignaturesIndices.nonSignerStakeIndices[quorumNumberIndex][i];
}
checkSignaturesIndices.nonSignerStakeIndices[quorumNumberIndex] = nonSignerStakeIndicesForQuorum;
}

IBLSPubkeyRegistry blsPubkeyRegistry = registryCoordinator.blsPubkeyRegistry();
// get the indices of the quorum apks for each of the provided quorums at the given blocknumber
checkSignaturesIndices.quorumApkIndices = blsPubkeyRegistry.getApkIndicesForQuorumsAtBlockNumber(quorumNumbers, referenceBlockNumber);

return checkSignaturesIndices;
Expand Down
25 changes: 13 additions & 12 deletions src/contracts/middleware/BLSSignatureChecker.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import "../libraries/BitmapUtils.sol";
* @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service
* @notice This is the contract for checking the validity of aggregate operator signatures.
*/
abstract contract BLSSignatureChecker {
contract BLSSignatureChecker {
using BN254 for BN254.G1Point;

// DATA STRUCTURES
Expand Down Expand Up @@ -44,7 +44,7 @@ abstract contract BLSSignatureChecker {

// gas cost of multiplying 2 pairings
// TODO: verify this
uint256 constant PAIRING_EQUALITY_CHECK_GAS = 113000;
uint256 constant PAIRING_EQUALITY_CHECK_GAS = 120000;

IRegistryCoordinator public immutable registryCoordinator;
IStakeRegistry public immutable stakeRegistry;
Expand Down Expand Up @@ -78,7 +78,8 @@ abstract contract BLSSignatureChecker {
uint32 referenceBlockNumber,
NonSignerStakesAndSignature memory nonSignerStakesAndSignature
)
public view
public
view
returns (
QuorumStakeTotals memory,
bytes32
Expand All @@ -95,7 +96,7 @@ abstract contract BLSSignatureChecker {
referenceBlockNumber,
nonSignerStakesAndSignature.quorumApkIndices[i]
),
"BLSSignatureChecker.checkSignatures: quorumApkIndex does not match quorum apk"
"BLSSignatureChecker.checkSignatures: quorumApk hash in storage does not match provided quorum apk"
);
apk = apk.plus(nonSignerStakesAndSignature.quorumApks[i]);
}
Expand All @@ -115,26 +116,26 @@ abstract contract BLSSignatureChecker {

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");
}
// if (i != 0) {
// require(uint256(nonSignerPubkeyHashes[i]) > uint256(nonSignerPubkeyHashes[i - 1]), "BLSSignatureChecker.checkSignatures: nonSignerPubkeys not sorted");
// }
nonSignerQuorumBitmaps[i] =
registryCoordinator.getQuorumBitmapByOperatorIdAtBlockNumberByIndex(
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) // we subtract the nonSignerPubkey from each quorum that they are a part of
.scalar_mul(
BitmapUtils.countNumOnes(nonSignerQuorumBitmaps[i] & signingQuorumBitmap) // we subtract the nonSignerPubkey from each quorum that they are a part of, TODO:
)
);
}
}

// loop through each quorum number
for (uint8 quorumNumberIndex = 0; quorumNumberIndex < quorumNumbers.length;) {
// get the quorum number
Expand All @@ -143,7 +144,7 @@ abstract contract BLSSignatureChecker {
quorumStakeTotals.totalStakeForQuorum[quorumNumberIndex] =
stakeRegistry.getTotalStakeAtBlockNumberFromIndex(quorumNumber, referenceBlockNumber, nonSignerStakesAndSignature.totalStakeIndices[quorumNumberIndex]);
// copy total stake to signed stake
quorumStakeTotals.signedStakeForQuorum[quorumNumberIndex] = quorumStakeTotals.totalStakeForQuorum[quorumNumber];
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++) {
Expand All @@ -156,7 +157,7 @@ abstract contract BLSSignatureChecker {
quorumNumber,
referenceBlockNumber,
nonSignerPubkeyHashes[i],
nonSignerStakesAndSignature.nonSignerStakeIndices[quorumNumber][nonSignerForQuorumIndex]
nonSignerStakesAndSignature.nonSignerStakeIndices[quorumNumberIndex][nonSignerForQuorumIndex]
);
unchecked {
++nonSignerForQuorumIndex;
Expand Down
31 changes: 31 additions & 0 deletions src/test/harnesses/BLSRegistryCoordinatorWithIndicesHarness.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.12;

import "../../contracts/middleware/BLSRegistryCoordinatorWithIndices.sol";

// wrapper around the BLSRegistryCoordinatorWithIndices contract that exposes the internal functions for unit testing.
contract BLSRegistryCoordinatorWithIndicesHarness is BLSRegistryCoordinatorWithIndices {
constructor(
ISlasher _slasher,
IServiceManager _serviceManager,
IStakeRegistry _stakeRegistry,
IBLSPubkeyRegistry _blsPubkeyRegistry,
IIndexRegistry _indexRegistry
) BLSRegistryCoordinatorWithIndices(_slasher, _serviceManager, _stakeRegistry, _blsPubkeyRegistry, _indexRegistry) {
}

function recordOperatorQuorumBitmapUpdate(bytes32 operatorId, uint192 quorumBitmap) external {
uint256 operatorQuorumBitmapHistoryLength = _operatorIdToQuorumBitmapHistory[operatorId].length;
if (operatorQuorumBitmapHistoryLength != 0) {
_operatorIdToQuorumBitmapHistory[operatorId][operatorQuorumBitmapHistoryLength - 1].nextUpdateBlockNumber = uint32(block.number);
}

_operatorIdToQuorumBitmapHistory[operatorId].push(QuorumBitmapUpdate({
updateBlockNumber: uint32(block.number),
nextUpdateBlockNumber: 0,
quorumBitmap: quorumBitmap
}));
}


}
Loading

0 comments on commit 64ace4e

Please sign in to comment.