Skip to content

On chain check signatures #3

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 35 commits into
base: latest-dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
d4fff29
feat: getNonSignerStakesAndSignature
rubydusa Mar 11, 2025
f1186b6
chore: add BN256G2
rubydusa Mar 11, 2025
55ef0ed
refactor: BN256G2 ^0.4.24 -> ^0.8.27
rubydusa Mar 11, 2025
595b824
fix: compute g2 apk
rubydusa Mar 11, 2025
b322318
test: inital test of getNonSignerStakesAndSignature
hudsonhrh Mar 14, 2025
d050b75
test: getNonSignerStakesAndSignature tests now use real G2 points
hudsonhrh Mar 14, 2025
cd0de93
test: added tests for diffferent revert scenariois
hudsonhrh Mar 14, 2025
89aac24
fix: _makeG2Point comments
rubydusa Mar 15, 2025
f65636d
chore: use G2 coordinates from BN254
rubydusa Mar 15, 2025
1bb0c3f
fix: compute quorum APKs by timestamp
rubydusa Mar 15, 2025
22b355a
refactor: safe gaurd against invalid sigmas
rubydusa Mar 15, 2025
22810cd
test: getNonSignerStakesAndSignature changing Quorum set
rubydusa Mar 15, 2025
973e278
fix: convert blockNumber to uint32
rubydusa Mar 15, 2025
2d7c073
chore: clarify operatorIds -> signingOperatorIds
rubydusa Mar 17, 2025
f310f09
Merge branch 'latest-dev' into on-chain-check-signatures
rubydusa Mar 18, 2025
76ffef7
chore: delete non-sensical no signers test
rubydusa Mar 18, 2025
e758ffa
chore: add natspec to new functions in OperatorStateRetriever
rubydusa Mar 26, 2025
021d32c
docs: fix capitalization of comments and natspec for getNonSignerStak…
bagelface Mar 27, 2025
a294309
revert comment changes
hudsonhrh Mar 27, 2025
e0bd81f
Update isOnCurve function as pure
Mar 30, 2025
c3e1ba1
docs: fix "getNonSignerStakesAndSignature" natspec comment
diterra-code Mar 30, 2025
4f6b395
fix: formatting
cathschmidt Apr 2, 2025
beffb0b
fix: clean up
Astodialo Apr 5, 2025
59a4b02
Merge branch 'dev' of https://github.com/Layr-Labs/eigenlayer-middlew…
rubydusa Apr 6, 2025
463166c
fix: type in `_computeG1APK` natspec
rubydusa Apr 6, 2025
0530e77
fix: incorrect curve equation in doc comment
rubydusa Apr 6, 2025
f5adbca
test: upgrade on eigenda holesky (#447)
stevennevins Apr 7, 2025
2899e89
fix: typo
rubydusa Apr 9, 2025
235a7d1
chore: add comment on g2 apk loop
rubydusa Apr 9, 2025
b07ad3a
Merge branch 'dev' into on-chain-check-signatures
RonTuretzky Apr 9, 2025
ff9b6b9
chore: explain `InvalidSigma()`
rubydusa Apr 10, 2025
dd8aaff
chore: add @dev comment about sigma
rubydusa Apr 10, 2025
1e57364
fix: check for correctness of indices and pubkeys in tests
rubydusa Apr 10, 2025
32c57d9
chore: forge fmt for CI
rubydusa Apr 10, 2025
3cf04f3
refactor: moving lib
RonTuretzky Apr 22, 2025
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
159 changes: 156 additions & 3 deletions src/OperatorStateRetriever.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,13 @@ pragma solidity ^0.8.27;

import {ISlashingRegistryCoordinator} from "./interfaces/ISlashingRegistryCoordinator.sol";
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 {BitmapUtils} from "./libraries/BitmapUtils.sol";
import {BN254} from "./libraries/BN254.sol";
import {BN256G2} from "./libraries/BN256G2.sol";

/**
* @title OperatorStateRetriever with view functions that allow to retrieve the state of an AVSs registry system.
Expand All @@ -27,6 +30,7 @@ contract OperatorStateRetriever {
}

error OperatorNotRegistered();
error InvalidSigma();

/**
* @notice This function is intended to to be called by AVS operators every time a new task is created (i.e.)
Expand Down Expand Up @@ -111,9 +115,9 @@ contract OperatorStateRetriever {
function getCheckSignaturesIndices(
ISlashingRegistryCoordinator registryCoordinator,
uint32 referenceBlockNumber,
bytes calldata quorumNumbers,
bytes32[] calldata nonSignerOperatorIds
) external view returns (CheckSignaturesIndices memory) {
bytes memory quorumNumbers,
bytes32[] memory nonSignerOperatorIds
) public view returns (CheckSignaturesIndices memory) {
IStakeRegistry stakeRegistry = registryCoordinator.stakeRegistry();
CheckSignaturesIndices memory checkSignaturesIndices;

Expand Down Expand Up @@ -231,4 +235,153 @@ contract OperatorStateRetriever {
operators[i] = registryCoordinator.getOperatorFromId(operatorIds[i]);
}
}

// avoid stack too deep
struct GetNontSignerStakesAndSignatureMemory {
BN254.G1Point[] quorumApks;
BN254.G2Point apkG2;
IIndexRegistry indexRegistry;
IBLSApkRegistry blsApkRegistry;
bytes32[] signingOperatorIds;
}
function getNonSignerStakesAndSignature(
ISlashingRegistryCoordinator registryCoordinator,
bytes calldata quorumNumbers,
BN254.G1Point calldata sigma,
address[] calldata operators,
uint32 blockNumber
) external view returns (IBLSSignatureCheckerTypes.NonSignerStakesAndSignature memory) {
GetNontSignerStakesAndSignatureMemory 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(_isOnCurve(sigma), InvalidSigma());

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);
// 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(signingOperatorQuorumBitmap != 0, 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);
for (uint256 i = 0; i < nonSignerOperatorsCount; i++) {
trimmedNonSignerOperatorIds[i] = nonSignerOperatorIds[i];
}

BN254.G1Point[] memory nonSignerPubkeys = new BN254.G1Point[](nonSignerOperatorsCount);
for (uint256 i = 0; i < nonSignerOperatorsCount; 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
});
}

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 = BN254.plus(apk, operatorPk);
}
return apk;
}

function _isOnCurve(BN254.G1Point memory p) internal view returns (bool) {
uint256 y2 = mulmod(p.Y, p.Y, BN254.FP_MODULUS);
uint256 x2 = mulmod(p.X, p.X, BN254.FP_MODULUS);
uint256 x3 = mulmod(p.X, x2, BN254.FP_MODULUS);
uint256 rhs = addmod(x3, 3, BN254.FP_MODULUS);
return y2 == rhs;
}
}
Loading