Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
44 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
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
76a8c5c
chore: modularize bls operator state retriever
ypatil12 Apr 29, 2025
2230a52
chore: format
ypatil12 Apr 29, 2025
28c3384
chore: clenanup RPCs
ypatil12 Apr 29, 2025
bae50c0
chore: revert naming
ypatil12 Apr 29, 2025
84fb838
Merge pull request #460 from BreadchainCoop/on-chain-check-signatures
ypatil12 Apr 30, 2025
9907543
refactor: move `_isOnCurve` to a library
rubydusa May 2, 2025
e6adbaa
fix: check that operators' bitmaps interesect with quorum numbers
rubydusa May 2, 2025
1cc5c63
chore: remove unecessary first for loop
rubydusa May 2, 2025
a4801d7
chore: `forge fmt`
rubydusa May 2, 2025
2f11818
Merge pull request #469 from BreadchainCoop/on-chain-check-signatures
ypatil12 May 2, 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
36 changes: 13 additions & 23 deletions src/unaudited/BLSSigCheckOperatorStateRetriever.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,18 @@ 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
Expand Down Expand Up @@ -61,7 +66,7 @@ contract BLSSigCheckOperatorStateRetriever is OperatorStateRetriever {
m.blsApkRegistry = registryCoordinator.blsApkRegistry();

// Safe guard AVSs from generating NonSignerStakesAndSignature with invalid sigma
require(_isOnCurve(sigma), InvalidSigma());
require(sigma.isOnCurve(), InvalidSigma());

// Compute the g2 APK of the signing operator set
m.signingOperatorIds = new bytes32[](operators.length);
Expand All @@ -84,13 +89,17 @@ contract BLSSigCheckOperatorStateRetriever is OperatorStateRetriever {
{
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(signingOperatorQuorumBitmap != 0, OperatorNotRegistered());
require(
!uint256(signingOperatorQuorumBitmap).noBitsInCommon(bitmap),
OperatorNotRegistered()
);
}
}

Expand Down Expand Up @@ -141,12 +150,9 @@ contract BLSSigCheckOperatorStateRetriever is OperatorStateRetriever {

// 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++) {
trimmedNonSignerOperatorIds[i] = nonSignerOperatorIds[i];
address nonSignerOperator =
registryCoordinator.getOperatorFromId(trimmedNonSignerOperatorIds[i]);
(nonSignerPubkeys[i],) = m.blsApkRegistry.getRegisteredPubkey(nonSignerOperator);
Expand Down Expand Up @@ -184,24 +190,8 @@ contract BLSSigCheckOperatorStateRetriever is OperatorStateRetriever {
address operator = registryCoordinator.getOperatorFromId(operatorIds[i]);
BN254.G1Point memory operatorPk;
(operatorPk.X, operatorPk.Y) = blsApkRegistry.operatorToPubkey(operator);
apk = BN254.plus(apk, operatorPk);
apk = apk.plus(operatorPk);
}
return apk;
}

/**
* @notice Checks if a point lies on the BN254 elliptic curve
* @dev The curve equation is y^2 = x^3 + 3 (mod p)
* @param p The point to check, in G1
* @return true if the point lies on the curve, false otherwise
*/
function _isOnCurve(
BN254.G1Point memory p
) internal pure 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;
}
}
26 changes: 26 additions & 0 deletions src/unaudited/ECUtils.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.12;

import {BN254} from "../libraries/BN254.sol";

/**
* @title ECUtils
* @notice Library containing utility functions for elliptic curve operations
*/
library ECUtils {
/**
* @notice Checks if a point lies on the BN254 elliptic curve
* @dev The curve equation is y^2 = x^3 + 3 (mod p)
* @param p The point to check, in G1
* @return true if the point lies on the curve, false otherwise
*/
function isOnCurve(
Copy link
Contributor

Choose a reason for hiding this comment

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

should be part of the BN254.sol library? or was this done due to lack of audit?

Copy link
Contributor

Choose a reason for hiding this comment

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

Lack of audit, didn't want to change BN254

BN254.G1Point memory p
) internal pure 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;
}
}
58 changes: 54 additions & 4 deletions test/unit/BLSSigCheckOperatorStateRetriever.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -526,8 +526,9 @@ contract BLSSigCheckOperatorStateRetrieverUnitTests is
signingOperators[0] = defaultOperator;

// Try to query for a non-existent quorum (quorum 9)
bytes memory invalidQuorumNumbers = new bytes(1);
invalidQuorumNumbers[0] = bytes1(uint8(9)); // Invalid quorum number
bytes memory invalidQuorumNumbers = new bytes(2);
invalidQuorumNumbers[0] = bytes1(uint8(0));
invalidQuorumNumbers[1] = bytes1(uint8(9)); // Invalid quorum number

// Should revert because quorum 9 doesn't exist, but with a different error message
cheats.expectRevert(
Expand Down Expand Up @@ -593,8 +594,9 @@ contract BLSSigCheckOperatorStateRetrieverUnitTests is
signingOperators[0] = defaultOperator;

// Try to query for the newly created quorum but at a historical block
bytes memory newQuorumNumbers = new bytes(1);
newQuorumNumbers[0] = bytes1(uint8(numQuorums));
bytes memory newQuorumNumbers = new bytes(2);
newQuorumNumbers[0] = bytes1(uint8(0));
newQuorumNumbers[1] = bytes1(uint8(8));

// Should revert when querying for the newly created quorum at a block before it was created
cheats.expectRevert(
Expand All @@ -607,6 +609,54 @@ contract BLSSigCheckOperatorStateRetrieverUnitTests is
);
}

function test_getNonSignerStakesAndSignature_revert_operatorRegisteredToIrrelevantQuorum()
public
{
// setup
uint256 quorumBitmapOne = 1;
cheats.roll(registrationBlockNumber);

_registerOperatorWithCoordinator(defaultOperator, quorumBitmapOne, defaultPubKey);

address otherOperator = _incrementAddress(defaultOperator, 1);
BN254.G1Point memory otherPubKey = BN254.G1Point(1, 2);
_registerOperatorWithCoordinator(
otherOperator, quorumBitmapOne, otherPubKey, defaultStake - 1
);

// Generate actual G2 pubkeys
BN254.G2Point memory op1G2 = _makeG2Point(2);
BN254.G2Point memory op2G2 = _makeG2Point(3);

// Mock the registry calls so the contract sees those G2 points
vm.mockCall(
address(blsApkRegistry),
abi.encodeWithSelector(IBLSApkRegistry.getOperatorPubkeyG2.selector, defaultOperator),
abi.encode(op1G2)
);
vm.mockCall(
address(blsApkRegistry),
abi.encodeWithSelector(IBLSApkRegistry.getOperatorPubkeyG2.selector, otherOperator),
abi.encode(op2G2)
);

// Prepare inputs
BN254.G1Point memory dummySigma = BN254.scalar_mul_tiny(BN254.generatorG1(), 123);
address[] memory signingOperators = new address[](2);
signingOperators[0] = defaultOperator;
signingOperators[1] = otherOperator;

bytes memory quorumNumbers = new bytes(1);
quorumNumbers[0] = bytes1(uint8(2));

// Call the function under test
vm.expectRevert(OperatorStateRetriever.OperatorNotRegistered.selector);
IBLSSignatureCheckerTypes.NonSignerStakesAndSignature memory result =
sigCheckOperatorStateRetriever.getNonSignerStakesAndSignature(
registryCoordinator, quorumNumbers, dummySigma, signingOperators, uint32(block.number)
);
}

function _getApkAtBlocknumber(
ISlashingRegistryCoordinator registryCoordinator,
uint8 quorumNumber,
Expand Down
Loading