-
Notifications
You must be signed in to change notification settings - Fork 114
Feat: generate parameters for checkSignatures by referencing OperatorStateRetriever on-chain #461
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
Merged
Merged
Changes from all commits
Commits
Show all changes
44 commits
Select commit
Hold shift + click to select a range
d4fff29
feat: getNonSignerStakesAndSignature
rubydusa f1186b6
chore: add BN256G2
rubydusa 55ef0ed
refactor: BN256G2 ^0.4.24 -> ^0.8.27
rubydusa 595b824
fix: compute g2 apk
rubydusa b322318
test: inital test of getNonSignerStakesAndSignature
hudsonhrh d050b75
test: getNonSignerStakesAndSignature tests now use real G2 points
hudsonhrh cd0de93
test: added tests for diffferent revert scenariois
hudsonhrh 89aac24
fix: _makeG2Point comments
rubydusa f65636d
chore: use G2 coordinates from BN254
rubydusa 1bb0c3f
fix: compute quorum APKs by timestamp
rubydusa 22b355a
refactor: safe gaurd against invalid sigmas
rubydusa 22810cd
test: getNonSignerStakesAndSignature changing Quorum set
rubydusa 973e278
fix: convert blockNumber to uint32
rubydusa 2d7c073
chore: clarify operatorIds -> signingOperatorIds
rubydusa f310f09
Merge branch 'latest-dev' into on-chain-check-signatures
rubydusa 76ffef7
chore: delete non-sensical no signers test
rubydusa e758ffa
chore: add natspec to new functions in OperatorStateRetriever
rubydusa 021d32c
docs: fix capitalization of comments and natspec for getNonSignerStak…
bagelface a294309
revert comment changes
hudsonhrh e0bd81f
Update isOnCurve function as pure
c3e1ba1
docs: fix "getNonSignerStakesAndSignature" natspec comment
diterra-code 4f6b395
fix: formatting
cathschmidt beffb0b
fix: clean up
Astodialo 59a4b02
Merge branch 'dev' of https://github.com/Layr-Labs/eigenlayer-middlew…
rubydusa 463166c
fix: type in `_computeG1APK` natspec
rubydusa 0530e77
fix: incorrect curve equation in doc comment
rubydusa 2899e89
fix: typo
rubydusa 235a7d1
chore: add comment on g2 apk loop
rubydusa b07ad3a
Merge branch 'dev' into on-chain-check-signatures
RonTuretzky ff9b6b9
chore: explain `InvalidSigma()`
rubydusa dd8aaff
chore: add @dev comment about sigma
rubydusa 1e57364
fix: check for correctness of indices and pubkeys in tests
rubydusa 32c57d9
chore: forge fmt for CI
rubydusa 3cf04f3
refactor: moving lib
RonTuretzky 76a8c5c
chore: modularize bls operator state retriever
ypatil12 2230a52
chore: format
ypatil12 28c3384
chore: clenanup RPCs
ypatil12 bae50c0
chore: revert naming
ypatil12 84fb838
Merge pull request #460 from BreadchainCoop/on-chain-check-signatures
ypatil12 9907543
refactor: move `_isOnCurve` to a library
rubydusa e6adbaa
fix: check that operators' bitmaps interesect with quorum numbers
rubydusa 1cc5c63
chore: remove unecessary first for loop
rubydusa a4801d7
chore: `forge fmt`
rubydusa 2f11818
Merge pull request #469 from BreadchainCoop/on-chain-check-signatures
ypatil12 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,197 @@ | ||
| // SPDX-License-Identifier: BUSL-1.1 | ||
| pragma solidity ^0.8.27; | ||
|
|
||
| import {IBLSApkRegistry} from "../interfaces/IBLSApkRegistry.sol"; | ||
| import {IBLSSignatureCheckerTypes} from "../interfaces/IBLSSignatureChecker.sol"; | ||
| import {IStakeRegistry} from "../interfaces/IStakeRegistry.sol"; | ||
| import {IIndexRegistry} from "../interfaces/IIndexRegistry.sol"; | ||
| import {ISlashingRegistryCoordinator} from "../interfaces/ISlashingRegistryCoordinator.sol"; | ||
| import {BitmapUtils} from "../libraries/BitmapUtils.sol"; | ||
| import {BN254} from "../libraries/BN254.sol"; | ||
| import {BN256G2} from "./BN256G2.sol"; | ||
| import {OperatorStateRetriever} from "../OperatorStateRetriever.sol"; | ||
| import {ECUtils} from "./ECUtils.sol"; | ||
|
|
||
| /** | ||
| * @title BLSSigCheckOperatorStateRetriever with view functions that allow to retrieve the state of an AVSs registry system. | ||
| * @dev This contract inherits from OperatorStateRetriever and adds the getNonSignerStakesAndSignature function. | ||
| * @author Bread coop | ||
| */ | ||
| contract BLSSigCheckOperatorStateRetriever is OperatorStateRetriever { | ||
| using ECUtils for BN254.G1Point; | ||
| using BN254 for BN254.G1Point; | ||
| using BitmapUtils for uint256; | ||
|
|
||
| /// @dev Thrown when the signature is not on the curve. | ||
| error InvalidSigma(); | ||
| // avoid stack too deep | ||
|
|
||
| struct GetNonSignerStakesAndSignatureMemory { | ||
| BN254.G1Point[] quorumApks; | ||
| BN254.G2Point apkG2; | ||
| IIndexRegistry indexRegistry; | ||
| IBLSApkRegistry blsApkRegistry; | ||
| bytes32[] signingOperatorIds; | ||
| } | ||
|
|
||
| /** | ||
| * @notice Returns the stakes and signature information for non-signing operators in specified quorums | ||
| * @param registryCoordinator The registry coordinator contract to fetch operator information from | ||
| * @param quorumNumbers Array of quorum numbers to check for non-signers | ||
| * @param sigma The aggregate BLS signature to verify | ||
| * @param operators Array of operator addresses that signed the message | ||
| * @param blockNumber Is the block number to get the indices for | ||
| * @return NonSignerStakesAndSignature Struct containing: | ||
| * - nonSignerQuorumBitmapIndices: Indices for retrieving quorum bitmaps of non-signers | ||
| * - nonSignerPubkeys: BLS public keys of operators that did not sign | ||
| * - quorumApks: Aggregate public keys for each quorum | ||
| * - apkG2: Aggregate public key of all signing operators in G2 | ||
| * - sigma: The provided signature | ||
| * - quorumApkIndices: Indices for retrieving quorum APKs | ||
| * - totalStakeIndices: Indices for retrieving total stake info | ||
| * - nonSignerStakeIndices: Indices for retrieving non-signer stake info | ||
| * @dev Computes the indices of operators that did not sign across all specified quorums | ||
| * @dev This function does not validate the signature matches the provided parameters, only that it's in a valid format | ||
| */ | ||
| function getNonSignerStakesAndSignature( | ||
| ISlashingRegistryCoordinator registryCoordinator, | ||
| bytes calldata quorumNumbers, | ||
| BN254.G1Point calldata sigma, | ||
| address[] calldata operators, | ||
| uint32 blockNumber | ||
| ) external view returns (IBLSSignatureCheckerTypes.NonSignerStakesAndSignature memory) { | ||
| GetNonSignerStakesAndSignatureMemory memory m; | ||
| m.quorumApks = new BN254.G1Point[](quorumNumbers.length); | ||
| m.indexRegistry = registryCoordinator.indexRegistry(); | ||
| m.blsApkRegistry = registryCoordinator.blsApkRegistry(); | ||
|
|
||
| // Safe guard AVSs from generating NonSignerStakesAndSignature with invalid sigma | ||
| require(sigma.isOnCurve(), InvalidSigma()); | ||
|
|
||
| // Compute the g2 APK of the signing operator set | ||
| m.signingOperatorIds = new bytes32[](operators.length); | ||
| for (uint256 i = 0; i < operators.length; i++) { | ||
| m.signingOperatorIds[i] = registryCoordinator.getOperatorId(operators[i]); | ||
| BN254.G2Point memory operatorG2Pk = m.blsApkRegistry.getOperatorPubkeyG2(operators[i]); | ||
| (m.apkG2.X[1], m.apkG2.X[0], m.apkG2.Y[1], m.apkG2.Y[0]) = BN256G2.ECTwistAdd( | ||
| m.apkG2.X[1], | ||
| m.apkG2.X[0], | ||
| m.apkG2.Y[1], | ||
| m.apkG2.Y[0], | ||
| operatorG2Pk.X[1], | ||
| operatorG2Pk.X[0], | ||
| operatorG2Pk.Y[1], | ||
| operatorG2Pk.Y[0] | ||
| ); | ||
| } | ||
|
|
||
| // Extra scope for stack limit | ||
| { | ||
| uint32[] memory signingOperatorQuorumBitmapIndices = registryCoordinator | ||
| .getQuorumBitmapIndicesAtBlockNumber(blockNumber, m.signingOperatorIds); | ||
| uint256 bitmap = BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers); | ||
| // Check that all operators are registered (this is like the check in getCheckSignaturesIndices, but we check against _signing_ operators) | ||
| for (uint256 i = 0; i < operators.length; i++) { | ||
| uint192 signingOperatorQuorumBitmap = registryCoordinator | ||
| .getQuorumBitmapAtBlockNumberByIndex( | ||
| m.signingOperatorIds[i], blockNumber, signingOperatorQuorumBitmapIndices[i] | ||
| ); | ||
| require( | ||
| !uint256(signingOperatorQuorumBitmap).noBitsInCommon(bitmap), | ||
| OperatorNotRegistered() | ||
| ); | ||
| } | ||
| } | ||
|
|
||
| // We use this as a dynamic array | ||
| uint256 nonSignerOperatorsCount = 0; | ||
| bytes32[] memory nonSignerOperatorIds = new bytes32[](16); | ||
| // For every quorum | ||
| for (uint256 i = 0; i < quorumNumbers.length; i++) { | ||
| bytes32[] memory operatorIdsInQuorum = | ||
| m.indexRegistry.getOperatorListAtBlockNumber(uint8(quorumNumbers[i]), blockNumber); | ||
| // Operator IDs are computed from the hash of the BLS public keys, so an operatorId's public key can't change over time | ||
| // This lets us compute the APK at the given block number | ||
| m.quorumApks[i] = _computeG1Apk(registryCoordinator, operatorIdsInQuorum); | ||
| // We check for every operator in the quorum | ||
| for (uint256 j = 0; j < operatorIdsInQuorum.length; j++) { | ||
| bool isNewNonSigner = true; | ||
| // If it is in the signing operators array | ||
| for (uint256 k = 0; k < m.signingOperatorIds.length; k++) { | ||
| if (operatorIdsInQuorum[j] == m.signingOperatorIds[k]) { | ||
| isNewNonSigner = false; | ||
| break; | ||
| } | ||
| } | ||
| // Or already in the non-signing operators array | ||
| for (uint256 l = 0; l < nonSignerOperatorsCount; l++) { | ||
| if (nonSignerOperatorIds[l] == operatorIdsInQuorum[j]) { | ||
| isNewNonSigner = false; | ||
| break; | ||
| } | ||
| } | ||
| // And if not, we add it to the non-signing operators array | ||
| if (isNewNonSigner) { | ||
| // If we are at the end of the array, we need to resize it | ||
| if (nonSignerOperatorsCount == nonSignerOperatorIds.length) { | ||
| uint256 newCapacity = nonSignerOperatorIds.length * 2; | ||
| bytes32[] memory newNonSignerOperatorIds = new bytes32[](newCapacity); | ||
| for (uint256 l = 0; l < nonSignerOperatorIds.length; l++) { | ||
| newNonSignerOperatorIds[l] = nonSignerOperatorIds[l]; | ||
| } | ||
| nonSignerOperatorIds = newNonSignerOperatorIds; | ||
| } | ||
|
|
||
| nonSignerOperatorIds[nonSignerOperatorsCount] = operatorIdsInQuorum[j]; | ||
| nonSignerOperatorsCount++; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| // Trim the nonSignerOperatorIds array to the actual count | ||
| bytes32[] memory trimmedNonSignerOperatorIds = new bytes32[](nonSignerOperatorsCount); | ||
| BN254.G1Point[] memory nonSignerPubkeys = new BN254.G1Point[](nonSignerOperatorsCount); | ||
| for (uint256 i = 0; i < nonSignerOperatorsCount; i++) { | ||
| trimmedNonSignerOperatorIds[i] = nonSignerOperatorIds[i]; | ||
| address nonSignerOperator = | ||
| registryCoordinator.getOperatorFromId(trimmedNonSignerOperatorIds[i]); | ||
| (nonSignerPubkeys[i],) = m.blsApkRegistry.getRegisteredPubkey(nonSignerOperator); | ||
| } | ||
|
|
||
| CheckSignaturesIndices memory checkSignaturesIndices = getCheckSignaturesIndices( | ||
| registryCoordinator, blockNumber, quorumNumbers, trimmedNonSignerOperatorIds | ||
| ); | ||
| return IBLSSignatureCheckerTypes.NonSignerStakesAndSignature({ | ||
| nonSignerQuorumBitmapIndices: checkSignaturesIndices.nonSignerQuorumBitmapIndices, | ||
| nonSignerPubkeys: nonSignerPubkeys, | ||
| quorumApks: m.quorumApks, | ||
| apkG2: m.apkG2, | ||
| sigma: sigma, | ||
| quorumApkIndices: checkSignaturesIndices.quorumApkIndices, | ||
| totalStakeIndices: checkSignaturesIndices.totalStakeIndices, | ||
| nonSignerStakeIndices: checkSignaturesIndices.nonSignerStakeIndices | ||
| }); | ||
| } | ||
|
|
||
| /** | ||
| * @notice Computes the aggregate public key (APK) in G1 for a list of operators | ||
| * @dev Aggregates the individual G1 public keys of operators by adding them together | ||
| * @param registryCoordinator The registry coordinator contract to fetch operator info from | ||
| * @param operatorIds Array of operator IDs to compute the aggregate key for | ||
| * @return The aggregate public key as a G1 point, computed by summing individual operator pubkeys | ||
| */ | ||
| function _computeG1Apk( | ||
| ISlashingRegistryCoordinator registryCoordinator, | ||
| bytes32[] memory operatorIds | ||
| ) internal view returns (BN254.G1Point memory) { | ||
| BN254.G1Point memory apk = BN254.G1Point(0, 0); | ||
| IBLSApkRegistry blsApkRegistry = registryCoordinator.blsApkRegistry(); | ||
| for (uint256 i = 0; i < operatorIds.length; i++) { | ||
| address operator = registryCoordinator.getOperatorFromId(operatorIds[i]); | ||
| BN254.G1Point memory operatorPk; | ||
| (operatorPk.X, operatorPk.Y) = blsApkRegistry.operatorToPubkey(operator); | ||
| apk = apk.plus(operatorPk); | ||
| } | ||
| return apk; | ||
| } | ||
| } | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i think people want to call this with operatorIds, not addresses?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I highly doubt that they will... we can duplicate this method to add another entrypoint with operator ids though