From 69fae396d919e7ded3df91583d6a7e167e12c131 Mon Sep 17 00:00:00 2001 From: Yash Patil <40046473+ypatil12@users.noreply.github.com> Date: Wed, 24 Sep 2025 13:25:35 -0400 Subject: [PATCH 1/4] chore: push --- CHANGELOG/CHANGELOG-1.4.0.md | 4 +- CHANGELOG/CHANGELOG-1.5.0-testnet-final.md | 58 +++++ CHANGELOG/CHANGELOG-1.5.0.md | 66 ++++- lib/eigenlayer-contracts | 2 +- .../unaudited/BN254TableCalculatorLegacy.sol | 246 ++++++++++++++++++ 5 files changed, 368 insertions(+), 8 deletions(-) create mode 100644 CHANGELOG/CHANGELOG-1.5.0-testnet-final.md create mode 100644 src/middlewareV2/tableCalculator/unaudited/BN254TableCalculatorLegacy.sol diff --git a/CHANGELOG/CHANGELOG-1.4.0.md b/CHANGELOG/CHANGELOG-1.4.0.md index 7245c0ce..a0b3600d 100644 --- a/CHANGELOG/CHANGELOG-1.4.0.md +++ b/CHANGELOG/CHANGELOG-1.4.0.md @@ -6,7 +6,7 @@ The multichain/middlewareV2 release enables AVSs to launch their services and ma 2. AVS Contracts 3. Offchain Infrastructure -The below release notes cover AVS Contracts. For more information on the end to end protocol, see our [docs](../docs/middlewareV2/README.md), [core contract docs](https://github.com/Layr-Labs/eigenlayer-contracts/tree/main/docs/multichain), and [ELIP-008](https://github.com/eigenfoundation/ELIPs/blob/elip-008v1/ELIPs/ELIP-008.md). +The below release notes cover AVS Contracts. For more information on the end to end protocol, see our [docs](../docs/middlewareV2/README.md), [core contract docs](https://github.com/Layr-Labs/eigenlayer-contracts/tree/main/docs/multichain), and [ELIP-008](https://github.com/eigenfoundation/ELIPs/blob/main/ELIPs/ELIP-008.md). ## Release Manager @@ -19,7 +19,7 @@ This multichain release only introduces new standards and contracts. As a result 🚀 New Features – Highlight major new functionality - `AVSRegistrar`: The primary interface for managing operator registration and deregistration within an AVS. It integrates with core EigenLayer contracts to ensure operators have valid keys and are properly registered in operator sets -- `OperatorTableCalculator`: Responsible for calculating stake weights of operator. These stake weights are aggregated and transported using the [Eigenlayer Multichain Protocol](https://github.com/eigenfoundation/ELIPs/blob/elip-008v1/ELIPs/ELIP-008.md). In order to utilize the multichain protocol, an AVS *MUST* deploy an `OperatorTableCalculator` and register it in the `CrossChainRegistry` in EigenLayer core. See our [core documentation](https://github.com/Layr-Labs/eigenlayer-contracts/tree/main/docs/multichain#common-user-flows) for this process. +- `OperatorTableCalculator`: Responsible for calculating stake weights of operator. These stake weights are aggregated and transported using the [Eigenlayer Multichain Protocol](https://github.com/eigenfoundation/ELIPs/blob/main/ELIPs/ELIP-008.md). In order to utilize the multichain protocol, an AVS *MUST* deploy an `OperatorTableCalculator` and register it in the `CrossChainRegistry` in EigenLayer core. See our [core documentation](https://github.com/Layr-Labs/eigenlayer-contracts/tree/main/docs/multichain#common-user-flows) for this process. 🔧 Improvements – Enhancements to existing features. diff --git a/CHANGELOG/CHANGELOG-1.5.0-testnet-final.md b/CHANGELOG/CHANGELOG-1.5.0-testnet-final.md new file mode 100644 index 00000000..2e00cf6c --- /dev/null +++ b/CHANGELOG/CHANGELOG-1.5.0-testnet-final.md @@ -0,0 +1,58 @@ +# 1.5.0-testnet-final + +The below release notes cover the updated version release candidate for multichain and hourglass + +# Release Manager + +@ypatil12 @eigenmikem @rajathalex + +# Multichain + +## Highlights + +🚀 New Features – Highlight major new functionality +- Add new table calculator modules - with stake caps and custom stake weights. **These contracts are unaudited.**. See [PR #514](https://github.com/layr-labs/eigenlayer-middleware/pull/514) + +⛔ Breaking Changes – Call out backward-incompatible changes. +- The `BN254CertificateVerifier` now salts the `operatorInfoleaf` via the core [`LeafCalculatorMixin`](https://github.com/Layr-Labs/eigenlayer-contracts/blob/main/src/contracts/mixins/LeafCalculatorMixin.sol) contract. **BN254 OperatorSets MUST update their table calculators to use the new `BN254TableCalculatorBase`. Failure to do so can result in certificates unable to be confirmed.** +- All AVSs in the [presets](../src/middlewareV2/registrar/presets/) now take in the `avs` as a parameter in the `initialize` function, rather than the constructor + +🔧 Improvements – Enhancements to existing features. +- Added add UAM support to `SocketRegistry`. See [PR #532](https://github.com/layr-labs/eigenlayer-middleware/pull/532) +- Updated core contract submodule to point to `v1.8.0-testnet-final` RC. See [PR #534](https://github.com/layr-labs/eigenlayer-middleware/pull/534) +- Clear up natspec and docs. See [PR #526](https://github.com/layr-labs/eigenlayer-middleware/pull/526) +- Remove unused imports. See [PR #513](https://github.com/layr-labs/eigenlayer-middleware/pull/513) + +🐛 Bug Fixes – List resolved issues. +- Fix array indexing in `BN254TableCalculatorBase`. See [PR #504](https://github.com/layr-labs/eigenlayer-middleware/pull/504) +- Make `avs` var stateful instead of immutable. See [PR #512](https://github.com/layr-labs/eigenlayer-middleware/pull/512) + +# Hourglass + +## Changelog + +- chore: update core submodule + ReadMe [PR #534](https://github.com/layr-labs/eigenlayer-middleware/pull/534) +- fix: add allowlist to TaskAVSRegistarBase [PR #533](https://github.com/layr-labs/eigenlayer-middleware/pull/533) +- feat: add UAM to `SocketRegistry` [PR #532](https://github.com/layr-labs/eigenlayer-middleware/pull/532) +- chore: bump core submodule [PR #531](https://github.com/layr-labs/eigenlayer-middleware/pull/531) +- chore: rev submodule +- fix(m-01): add salt to merkle leaf hashing [PR #527](https://github.com/layr-labs/eigenlayer-middleware/pull/527) +- docs: natspec review updates [PR #526](https://github.com/layr-labs/eigenlayer-middleware/pull/526) +- fix(I-05): remove unused import [PR #529](https://github.com/layr-labs/eigenlayer-middleware/pull/529) +- feat: tablecalc modules [PR #514](https://github.com/layr-labs/eigenlayer-middleware/pull/514) +- fix(I-07): add onlyInitializing modifier to initializing function [PR #525](https://github.com/layr-labs/eigenlayer-middleware/pull/525) +- fix: make storage variable internal due to existing getter [PR #524](https://github.com/layr-labs/eigenlayer-middleware/pull/524) +- chore: update taskavsregistrar base forpt1 findings +- chore: rev submodule +- fix(I-04): make avs var stateful [PR #512](https://github.com/layr-labs/eigenlayer-middleware/pull/512) +- fix(I5): natspec [PR #516](https://github.com/layr-labs/eigenlayer-middleware/pull/516) +- fix(I-07): clarify expectation on `weights` array structure [PR #510](https://github.com/layr-labs/eigenlayer-middleware/pull/510) +- fix(I-03): rename `ISocketRegistry` -> `ISocketRegistryV2` [PR #511](https://github.com/layr-labs/eigenlayer-middleware/pull/511) +- fix(I-06): remove many unused imports [PR #513](https://github.com/layr-labs/eigenlayer-middleware/pull/513) +- chore: updated core contracts dependencies +- chore: bump up core deps +- docs: changelog +- chore: bump up core deps +- feat: hourglass [PR #507](https://github.com/layr-labs/eigenlayer-middleware/pull/507) +- fix(H-1): correct array indexing for BN254TableCalculatorBase._calculateOperatorTable [PR #504](https://github.com/layr-labs/eigenlayer-middleware/pull/504) +- docs: add new table calculators [PR #508](https://github.com/layr-labs/eigenlayer-middleware/pull/508) \ No newline at end of file diff --git a/CHANGELOG/CHANGELOG-1.5.0.md b/CHANGELOG/CHANGELOG-1.5.0.md index c463db83..3d430541 100644 --- a/CHANGELOG/CHANGELOG-1.5.0.md +++ b/CHANGELOG/CHANGELOG-1.5.0.md @@ -1,3 +1,37 @@ +# v1.5.0 Multichain/MiddlewareV2/Hourglass + +These are the combined release notes of multichain and hourglass. + +# v1.4.0 MultiChain/MiddlewareV2 + +The multichain/middlewareV2 release enables AVSs to launch their services and make verified Operator outputs available on any EVM chain, meeting their customers where they are. AVSs can specify custom operator weights to be transported to any destination chain. The release has 3 components: + +1. Core Contracts +2. AVS Contracts +3. Offchain Infrastructure + +The below release notes cover AVS Contracts. For more information on the end to end protocol, see our [docs](../docs/middlewareV2/README.md), [core contract docs](https://github.com/Layr-Labs/eigenlayer-contracts/tree/main/docs/multichain), and [ELIP-008](https://github.com/eigenfoundation/ELIPs/blob/main/ELIPs/ELIP-008.md). + +## Release Manager + +@ypatil12 @eigenmikem + +## Highlights + +This multichain release only introduces new standards and contracts. As a result, there are **no breaking changes or deprecations**. All new contracts are in the [middlewareV2 folder](../src/middlewareV2/). + +🚀 New Features – Highlight major new functionality + +- `AVSRegistrar`: The primary interface for managing operator registration and deregistration within an AVS. It integrates with core EigenLayer contracts to ensure operators have valid keys and are properly registered in operator sets +- `OperatorTableCalculator`: Responsible for calculating stake weights of operator. These stake weights are aggregated and transported using the [Eigenlayer Multichain Protocol](https://github.com/eigenfoundation/ELIPs/blob/main/ELIPs/ELIP-008.md). In order to utilize the multichain protocol, an AVS *MUST* deploy an `OperatorTableCalculator` and register it in the `CrossChainRegistry` in EigenLayer core. See our [core documentation](https://github.com/Layr-Labs/eigenlayer-contracts/tree/main/docs/multichain#common-user-flows) for this process. + +🔧 Improvements – Enhancements to existing features. + +- The multichain protocol has protocol-ized several AVS-deployed contracts, enabling an simpler AVS developer experience. These include: + - `KeyRegistrar`: Manages BLS and ECDSA signing keys. AVSs no longer have to deploy a `BLSAPKRegistry` + - `CertificateVerifier`: Handles signature verification for BLS and ECDSA keys. AVSs no longer have to deploy a `BLSSignatureChecker` + - Offchain Multichain Transport: AVSs no longer have to maintain [avs-sync](https://github.com/Layr-Labs/avs-sync) to keep operator stakes fresh + # v1.5.0 Hourglass The Hourglass release consists of a framework that supports the creation of task-based AVSs. The task-based AVSs are enabled through a `TaskMailbox` core contract deployed to all chains that support a `CertificateVerifier`. Additionally AVSs deploy their `TaskAVSRegistrar`. The release has 3 components: @@ -6,7 +40,7 @@ The Hourglass release consists of a framework that supports the creation of task 2. AVS Contracts 3. Offchain Infrastructure -The below release notes cover AVS Contracts. For more information on the end to end protocol, see our [docs](https://github.com/Layr-Labs/hourglass-monorepo/blob/master/README.md). +The below release notes cover AVS Contracts. For more information on the end to end protocol, see our [docs](https://github.com/Layr-Labs/hourglass-monorepo/blob/master/README.md) and [ELIP-010](https://github.com/eigenfoundation/ELIPs/blob/main/ELIPs/ELIP-010.md). ## Release Manager @@ -20,7 +54,29 @@ This hourglass release only introduces new contracts. As a result, there are no - `TaskAVSRegistrar`: An instanced (per-AVS) eigenlayer middleware contract on L1 that is responsible for handling operator registration for specific operator sets of your AVS and providing the offchain components with socket endpoints for the Aggregator and Executor operators. It also keeps track of which operator sets are the aggregator and executors. It works by default, but can be extended to include additional onchain logic for your AVS. -## Changelog - -- chore: bump up core deps -- feat: hourglass [PR #507](https://github.com/layr-labs/eigenlayer-middleware/pull/507) +# Changelog +* fix: bls sig operator state retriever sorting by @RonTuretzky in https://github.com/Layr-Labs/eigenlayer-middleware/pull/480 +* chore: update core submodule by @ypatil12 in https://github.com/Layr-Labs/eigenlayer-middleware/pull/497 +* test: table calc by @ypatil12 in https://github.com/Layr-Labs/eigenlayer-middleware/pull/499 +* test: ecdsa stake reg/bls sig checker utils by @ypatil12 in https://github.com/Layr-Labs/eigenlayer-middleware/pull/500 +* release: middlewareV2/multichain by @ypatil12 in https://github.com/Layr-Labs/eigenlayer-middleware/pull/496 +* fix: broken link by @ypatil12 in https://github.com/Layr-Labs/eigenlayer-middleware/pull/501 +* chore: update `AVSRegistrar` for new `KeyRegistrar` interface by @ypatil12 in https://github.com/Layr-Labs/eigenlayer-middleware/pull/502 +* chore: update submodule by @ypatil12 in https://github.com/Layr-Labs/eigenlayer-middleware/pull/503 +* feat(redistribution): update slashers by @0xClandestine in https://github.com/Layr-Labs/eigenlayer-middleware/pull/492 +* docs: format table in `OperatorTableCalculator` by @ypatil12 in https://github.com/Layr-Labs/eigenlayer-middleware/pull/505 +* docs: add new table calculators by @ypatil12 in https://github.com/Layr-Labs/eigenlayer-middleware/pull/508 +* fix(H-1): correct array indexing for BN254TableCalculatorBase._calculateOperatorTable by @nadir-akhtar in https://github.com/Layr-Labs/eigenlayer-middleware/pull/504 +* feat: hourglass by @0xrajath in https://github.com/Layr-Labs/eigenlayer-middleware/pull/515 +* fix: multichain pt1 audit fixes by @ypatil12 in https://github.com/Layr-Labs/eigenlayer-middleware/pull/520 +* fix: make storage variable internal due to existing getter by @nadir-akhtar in https://github.com/Layr-Labs/eigenlayer-middleware/pull/524 +* fix(I-07): add onlyInitializing modifier to initializing function by @nadir-akhtar in https://github.com/Layr-Labs/eigenlayer-middleware/pull/525 +* feat: tablecalc modules by @eigenmikem in https://github.com/Layr-Labs/eigenlayer-middleware/pull/514 +* fix(I-05): remove unused import by @ypatil12 in https://github.com/Layr-Labs/eigenlayer-middleware/pull/529 +* fix: multichain pt2 audit fixes by @ypatil12 in https://github.com/Layr-Labs/eigenlayer-middleware/pull/528 +* chore: bump core submodule by @ypatil12 in https://github.com/Layr-Labs/eigenlayer-middleware/pull/531 +* feat: add UAM to `SocketRegistry` by @ypatil12 in https://github.com/Layr-Labs/eigenlayer-middleware/pull/532 +* fix: add allowlist to TaskAVSRegistarBase by @nadir-akhtar in https://github.com/Layr-Labs/eigenlayer-middleware/pull/533 +* chore: update core submodule + ReadME by @ypatil12 in https://github.com/Layr-Labs/eigenlayer-middleware/pull/534 +* fix: task replay directed at same operator set by @0xrajath in https://github.com/Layr-Labs/eigenlayer-middleware/pull/535 +* chore: update core bindings by @0xrajath in https://github.com/Layr-Labs/eigenlayer-middleware/pull/536 \ No newline at end of file diff --git a/lib/eigenlayer-contracts b/lib/eigenlayer-contracts index 73644e20..7ecc83c7 160000 --- a/lib/eigenlayer-contracts +++ b/lib/eigenlayer-contracts @@ -1 +1 @@ -Subproject commit 73644e201541dc350a52b0f7a70fa125295a4128 +Subproject commit 7ecc83c7b180850531bc5b8b953a7340adeecd43 diff --git a/src/middlewareV2/tableCalculator/unaudited/BN254TableCalculatorLegacy.sol b/src/middlewareV2/tableCalculator/unaudited/BN254TableCalculatorLegacy.sol new file mode 100644 index 00000000..58b242b5 --- /dev/null +++ b/src/middlewareV2/tableCalculator/unaudited/BN254TableCalculatorLegacy.sol @@ -0,0 +1,246 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.27; + +import {OperatorSet} from "eigenlayer-contracts/src/contracts/libraries/OperatorSetLib.sol"; +import {IOperatorTableCalculator} from + "eigenlayer-contracts/src/contracts/interfaces/IOperatorTableCalculator.sol"; +import {Merkle} from "eigenlayer-contracts/src/contracts/libraries/Merkle.sol"; +import {BN254} from "eigenlayer-contracts/src/contracts/libraries/BN254.sol"; +import {LeafCalculatorMixin} from + "eigenlayer-contracts/src/contracts/mixins/LeafCalculatorMixin.sol"; +import {IBN254TableCalculator} from "../../../interfaces/IBN254TableCalculator.sol"; + +import {IBLSApkRegistry} from "../../../interfaces/IBLSAPKRegistry.sol"; +import {IStakeRegistry} from "../../../interfaces/IStakeRegistry.sol"; +import {IIndexRegistry, IIndexRegistryTypes} from "../../../interfaces/IIndexRegistry.sol"; + +/** + * @title BN254TableCalculatorLegacy + * @notice BN254 table calculator that works with the middlewareV1 architecture + * @dev Extends the basic table calculator to work with the middlewareV1 architecture. Specifically we use: + * - `BLSAPKRegistry` for key introspection + * - `StakeRegistry` for quorum membership + * @dev We use `operatorSet` everywhere instead of `quorumNumber`. We have to do this in order + * to be compliant with the table calculator interface. Developers should use the + * `quorumNumber` as the `id` of the `operatorSet` + * // TODO: update all natspec. Maybe look into making the key getters in the base contract abstract? + */ +contract BN254TableCalculatorLegacy is IBN254TableCalculator, LeafCalculatorMixin { + using Merkle for bytes32[]; + using BN254 for BN254.G1Point; + + // Immutables + IBLSApkRegistry public immutable blsApkRegistry; + IStakeRegistry public immutable stakeRegistry; + IIndexRegistry public immutable indexRegistry; + + /** + * @notice Constructor to initialize the BN254TableCalculatorBase + * @param _blsApkRegistry The BLSAPKRegistry contract for key introspection + * @param _stakeRegistry The StakeRegistry contract for stakes + */ + constructor( + IBLSApkRegistry _blsApkRegistry, + IStakeRegistry _stakeRegistry, + IIndexRegistry _indexRegistry + ) { + blsApkRegistry = _blsApkRegistry; + stakeRegistry = _stakeRegistry; + indexRegistry = _indexRegistry; + } + + /// @inheritdoc IBN254TableCalculator + function calculateOperatorTable( + OperatorSet calldata operatorSet + ) external view virtual returns (BN254OperatorSetInfo memory operatorSetInfo) { + return _calculateOperatorTable(operatorSet); + } + + /// @inheritdoc IOperatorTableCalculator + function calculateOperatorTableBytes( + OperatorSet calldata operatorSet + ) external view virtual returns (bytes memory operatorTableBytes) { + return abi.encode(_calculateOperatorTable(operatorSet)); + } + + /// @inheritdoc IOperatorTableCalculator + function getOperatorSetWeights( + OperatorSet calldata operatorSet + ) external view virtual returns (address[] memory operators, uint256[][] memory weights) { + return _getOperatorWeights(operatorSet); + } + + /// @inheritdoc IOperatorTableCalculator + function getOperatorWeights( + OperatorSet calldata operatorSet, + address operator + ) external view virtual returns (uint256[] memory) { + (address[] memory operators, uint256[][] memory weights) = _getOperatorWeights(operatorSet); + + // Find the index of the operator in the operators array + for (uint256 i = 0; i < operators.length; i++) { + if (operators[i] == operator) { + return weights[i]; + } + } + + return new uint256[](0); + } + + /// @inheritdoc IBN254TableCalculator + function getOperatorInfos( + OperatorSet calldata operatorSet + ) external view virtual returns (BN254OperatorInfo[] memory) { + // Get the weights for all operators + (address[] memory operators, uint256[][] memory weights) = _getOperatorWeights(operatorSet); + + BN254OperatorInfo[] memory operatorInfos = new BN254OperatorInfo[](operators.length); + + for (uint256 i = 0; i < operators.length; i++) { + // Skip if the operator has not registered their key - we can check the operator's pubkeyHash + if (blsApkRegistry.getOperatorId(operators[i]) == bytes32(0)) { + continue; + } + + // TODO: fix types + (BN254.G1Point memory g1Point,) = blsApkRegistry.getRegisteredPubkey(operators[i]); + + operatorInfos[i] = BN254OperatorInfo({pubkey: g1Point, weights: weights[i]}); + } + + return operatorInfos; + } + + /** + * @notice Abstract function to get the operator weights for a given operatorSet + * @param operatorSet The operatorSet to get the weights for + * @return operators The addresses of the operators in the operatorSet + * @return weights The weights for each operator in the operatorSet, this is a 2D array where the first index is the operator + * and the second index is the type of weight + * @dev Each single `weights` array is as a list of arbitrary stake types. For example, + * it can be [slashable_stake, delegated_stake, strategy_i_stake, ...]. Each stake type is an index in the array + * @dev Must be implemented by derived contracts to define specific weight calculation logic + * @dev The certificate verification assumes the composition weights array for each operator is the same. + * If the length of the array is different or the stake types are different, then verification issues can arise, including + * verification failing silently for multiple operators with different weights structures + */ + /** + * @notice Get operator weights with caps applied + * @param operatorSet The operator set to calculate weights for + * @return operators Array of operator addresses + * @return weights Array of weights per operator + */ + function _getOperatorWeights( + OperatorSet calldata operatorSet + ) internal view returns (address[] memory operators, uint256[][] memory weights) { + // Get the latest operator list for the quorum + // TODO: is it valid to get the latest quorum update here? + IIndexRegistryTypes.QuorumUpdate memory latestQuorumUpdate = indexRegistry.getLatestQuorumUpdate(uint8(operatorSet.id)); + bytes32[] memory operatorIds = indexRegistry.getOperatorListAtBlockNumber(uint8(operatorSet.id), latestQuorumUpdate.fromBlockNumber); + + operators = new address[](operatorIds.length); + weights = new uint256[][](operatorIds.length); + uint256 operatorCount = 0; + for (uint256 i = 0; i < operatorIds.length; ++i) { + uint256 totalWeight = stakeRegistry.getCurrentStake(operatorIds[i], uint8(operatorSet.id)); + + if (totalWeight > 0) { + weights[operatorCount] = new uint256[](1); + weights[operatorCount][0] = totalWeight; + operators[operatorCount] = blsApkRegistry.getOperatorFromPubkeyHash(operatorIds[i]); + operatorCount++; + } + } + + assembly { + mstore(operators, operatorCount) + mstore(weights, operatorCount) + } + + return (operators, weights); + } + + /** + * @notice Calculates the operator table for a given operatorSet, also calculates the aggregate pubkey for the operatorSet + * @param operatorSet The operatorSet to calculate the operator table for + * @return operatorSetInfo The BN254OperatorSetInfo containing merkle root, operator count, aggregate pubkey, and total weights + * @dev This function: + * 1. Gets operator weights from the weight calculator + * 2. Collates weights into total weights + * 3. Creates a merkle tree of operator info + * - assumes that the operator has a registered BN254 key + * 4. Calculates the aggregate public key + * @dev Returns empty operator set info if no operators have registered keys or non-zero weights + */ + function _calculateOperatorTable( + OperatorSet calldata operatorSet + ) internal view returns (BN254OperatorSetInfo memory operatorSetInfo) { + // Get the weights for all operators in the operatorSet + (address[] memory operators, uint256[][] memory weights) = _getOperatorWeights(operatorSet); + + // If there are no weights, return an empty operator set info + if (weights.length == 0) { + return BN254OperatorSetInfo({ + operatorInfoTreeRoot: bytes32(0), + numOperators: 0, + aggregatePubkey: BN254.G1Point(0, 0), + totalWeights: new uint256[](0) + }); + } + + // Initialize arrays + uint256 subArrayLength = weights[0].length; + uint256[] memory totalWeights = new uint256[](subArrayLength); + bytes32[] memory operatorInfoLeaves = new bytes32[](operators.length); + BN254.G1Point memory aggregatePubkey; + uint256 operatorCount = 0; + + for (uint256 i = 0; i < operators.length; i++) { + // Skip if the operator has not registered their key + if (blsApkRegistry.getOperatorId(operators[i]) == bytes32(0)) { + continue; + } + + // Read the weights for the operator and encode them into the operatorInfoLeaves + // for all weights, add them to the total weights. The ith index returns the weights array for the ith operator + for (uint256 j = 0; j < subArrayLength; j++) { + totalWeights[j] += weights[i][j]; + } + (BN254.G1Point memory g1Point,) = blsApkRegistry.getRegisteredPubkey(operators[i]); + + // Use `LeafCalculatorMixin` to calculate the leaf hash for the operator info + operatorInfoLeaves[operatorCount] = + calculateOperatorInfoLeaf(BN254OperatorInfo({pubkey: g1Point, weights: weights[i]})); + + // Add the operator's G1 point to the aggregate pubkey + aggregatePubkey = aggregatePubkey.plus(g1Point); + + // Increment the operator count + operatorCount++; + } + + // If there are no operators, return an empty operator set info + if (operatorCount == 0) { + return BN254OperatorSetInfo({ + operatorInfoTreeRoot: bytes32(0), + numOperators: 0, + aggregatePubkey: BN254.G1Point(0, 0), + totalWeights: new uint256[](0) + }); + } + + // Resize the operatorInfoLeaves array to the number of operators and merkleize + assembly { + mstore(operatorInfoLeaves, operatorCount) + } + + bytes32 operatorInfoTreeRoot = operatorInfoLeaves.merkleizeKeccak(); + + return BN254OperatorSetInfo({ + operatorInfoTreeRoot: operatorInfoTreeRoot, + numOperators: operatorCount, + aggregatePubkey: aggregatePubkey, + totalWeights: totalWeights + }); + } +} \ No newline at end of file From e6c9f5868d4982ef4c06c8dc60beb586b56d0b3b Mon Sep 17 00:00:00 2001 From: Yash Patil <40046473+ypatil12@users.noreply.github.com> Date: Wed, 24 Sep 2025 13:25:58 -0400 Subject: [PATCH 2/4] chore: remove file --- .../unaudited/BN254TableCalculatorLegacy.sol | 246 ------------------ 1 file changed, 246 deletions(-) delete mode 100644 src/middlewareV2/tableCalculator/unaudited/BN254TableCalculatorLegacy.sol diff --git a/src/middlewareV2/tableCalculator/unaudited/BN254TableCalculatorLegacy.sol b/src/middlewareV2/tableCalculator/unaudited/BN254TableCalculatorLegacy.sol deleted file mode 100644 index 58b242b5..00000000 --- a/src/middlewareV2/tableCalculator/unaudited/BN254TableCalculatorLegacy.sol +++ /dev/null @@ -1,246 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.27; - -import {OperatorSet} from "eigenlayer-contracts/src/contracts/libraries/OperatorSetLib.sol"; -import {IOperatorTableCalculator} from - "eigenlayer-contracts/src/contracts/interfaces/IOperatorTableCalculator.sol"; -import {Merkle} from "eigenlayer-contracts/src/contracts/libraries/Merkle.sol"; -import {BN254} from "eigenlayer-contracts/src/contracts/libraries/BN254.sol"; -import {LeafCalculatorMixin} from - "eigenlayer-contracts/src/contracts/mixins/LeafCalculatorMixin.sol"; -import {IBN254TableCalculator} from "../../../interfaces/IBN254TableCalculator.sol"; - -import {IBLSApkRegistry} from "../../../interfaces/IBLSAPKRegistry.sol"; -import {IStakeRegistry} from "../../../interfaces/IStakeRegistry.sol"; -import {IIndexRegistry, IIndexRegistryTypes} from "../../../interfaces/IIndexRegistry.sol"; - -/** - * @title BN254TableCalculatorLegacy - * @notice BN254 table calculator that works with the middlewareV1 architecture - * @dev Extends the basic table calculator to work with the middlewareV1 architecture. Specifically we use: - * - `BLSAPKRegistry` for key introspection - * - `StakeRegistry` for quorum membership - * @dev We use `operatorSet` everywhere instead of `quorumNumber`. We have to do this in order - * to be compliant with the table calculator interface. Developers should use the - * `quorumNumber` as the `id` of the `operatorSet` - * // TODO: update all natspec. Maybe look into making the key getters in the base contract abstract? - */ -contract BN254TableCalculatorLegacy is IBN254TableCalculator, LeafCalculatorMixin { - using Merkle for bytes32[]; - using BN254 for BN254.G1Point; - - // Immutables - IBLSApkRegistry public immutable blsApkRegistry; - IStakeRegistry public immutable stakeRegistry; - IIndexRegistry public immutable indexRegistry; - - /** - * @notice Constructor to initialize the BN254TableCalculatorBase - * @param _blsApkRegistry The BLSAPKRegistry contract for key introspection - * @param _stakeRegistry The StakeRegistry contract for stakes - */ - constructor( - IBLSApkRegistry _blsApkRegistry, - IStakeRegistry _stakeRegistry, - IIndexRegistry _indexRegistry - ) { - blsApkRegistry = _blsApkRegistry; - stakeRegistry = _stakeRegistry; - indexRegistry = _indexRegistry; - } - - /// @inheritdoc IBN254TableCalculator - function calculateOperatorTable( - OperatorSet calldata operatorSet - ) external view virtual returns (BN254OperatorSetInfo memory operatorSetInfo) { - return _calculateOperatorTable(operatorSet); - } - - /// @inheritdoc IOperatorTableCalculator - function calculateOperatorTableBytes( - OperatorSet calldata operatorSet - ) external view virtual returns (bytes memory operatorTableBytes) { - return abi.encode(_calculateOperatorTable(operatorSet)); - } - - /// @inheritdoc IOperatorTableCalculator - function getOperatorSetWeights( - OperatorSet calldata operatorSet - ) external view virtual returns (address[] memory operators, uint256[][] memory weights) { - return _getOperatorWeights(operatorSet); - } - - /// @inheritdoc IOperatorTableCalculator - function getOperatorWeights( - OperatorSet calldata operatorSet, - address operator - ) external view virtual returns (uint256[] memory) { - (address[] memory operators, uint256[][] memory weights) = _getOperatorWeights(operatorSet); - - // Find the index of the operator in the operators array - for (uint256 i = 0; i < operators.length; i++) { - if (operators[i] == operator) { - return weights[i]; - } - } - - return new uint256[](0); - } - - /// @inheritdoc IBN254TableCalculator - function getOperatorInfos( - OperatorSet calldata operatorSet - ) external view virtual returns (BN254OperatorInfo[] memory) { - // Get the weights for all operators - (address[] memory operators, uint256[][] memory weights) = _getOperatorWeights(operatorSet); - - BN254OperatorInfo[] memory operatorInfos = new BN254OperatorInfo[](operators.length); - - for (uint256 i = 0; i < operators.length; i++) { - // Skip if the operator has not registered their key - we can check the operator's pubkeyHash - if (blsApkRegistry.getOperatorId(operators[i]) == bytes32(0)) { - continue; - } - - // TODO: fix types - (BN254.G1Point memory g1Point,) = blsApkRegistry.getRegisteredPubkey(operators[i]); - - operatorInfos[i] = BN254OperatorInfo({pubkey: g1Point, weights: weights[i]}); - } - - return operatorInfos; - } - - /** - * @notice Abstract function to get the operator weights for a given operatorSet - * @param operatorSet The operatorSet to get the weights for - * @return operators The addresses of the operators in the operatorSet - * @return weights The weights for each operator in the operatorSet, this is a 2D array where the first index is the operator - * and the second index is the type of weight - * @dev Each single `weights` array is as a list of arbitrary stake types. For example, - * it can be [slashable_stake, delegated_stake, strategy_i_stake, ...]. Each stake type is an index in the array - * @dev Must be implemented by derived contracts to define specific weight calculation logic - * @dev The certificate verification assumes the composition weights array for each operator is the same. - * If the length of the array is different or the stake types are different, then verification issues can arise, including - * verification failing silently for multiple operators with different weights structures - */ - /** - * @notice Get operator weights with caps applied - * @param operatorSet The operator set to calculate weights for - * @return operators Array of operator addresses - * @return weights Array of weights per operator - */ - function _getOperatorWeights( - OperatorSet calldata operatorSet - ) internal view returns (address[] memory operators, uint256[][] memory weights) { - // Get the latest operator list for the quorum - // TODO: is it valid to get the latest quorum update here? - IIndexRegistryTypes.QuorumUpdate memory latestQuorumUpdate = indexRegistry.getLatestQuorumUpdate(uint8(operatorSet.id)); - bytes32[] memory operatorIds = indexRegistry.getOperatorListAtBlockNumber(uint8(operatorSet.id), latestQuorumUpdate.fromBlockNumber); - - operators = new address[](operatorIds.length); - weights = new uint256[][](operatorIds.length); - uint256 operatorCount = 0; - for (uint256 i = 0; i < operatorIds.length; ++i) { - uint256 totalWeight = stakeRegistry.getCurrentStake(operatorIds[i], uint8(operatorSet.id)); - - if (totalWeight > 0) { - weights[operatorCount] = new uint256[](1); - weights[operatorCount][0] = totalWeight; - operators[operatorCount] = blsApkRegistry.getOperatorFromPubkeyHash(operatorIds[i]); - operatorCount++; - } - } - - assembly { - mstore(operators, operatorCount) - mstore(weights, operatorCount) - } - - return (operators, weights); - } - - /** - * @notice Calculates the operator table for a given operatorSet, also calculates the aggregate pubkey for the operatorSet - * @param operatorSet The operatorSet to calculate the operator table for - * @return operatorSetInfo The BN254OperatorSetInfo containing merkle root, operator count, aggregate pubkey, and total weights - * @dev This function: - * 1. Gets operator weights from the weight calculator - * 2. Collates weights into total weights - * 3. Creates a merkle tree of operator info - * - assumes that the operator has a registered BN254 key - * 4. Calculates the aggregate public key - * @dev Returns empty operator set info if no operators have registered keys or non-zero weights - */ - function _calculateOperatorTable( - OperatorSet calldata operatorSet - ) internal view returns (BN254OperatorSetInfo memory operatorSetInfo) { - // Get the weights for all operators in the operatorSet - (address[] memory operators, uint256[][] memory weights) = _getOperatorWeights(operatorSet); - - // If there are no weights, return an empty operator set info - if (weights.length == 0) { - return BN254OperatorSetInfo({ - operatorInfoTreeRoot: bytes32(0), - numOperators: 0, - aggregatePubkey: BN254.G1Point(0, 0), - totalWeights: new uint256[](0) - }); - } - - // Initialize arrays - uint256 subArrayLength = weights[0].length; - uint256[] memory totalWeights = new uint256[](subArrayLength); - bytes32[] memory operatorInfoLeaves = new bytes32[](operators.length); - BN254.G1Point memory aggregatePubkey; - uint256 operatorCount = 0; - - for (uint256 i = 0; i < operators.length; i++) { - // Skip if the operator has not registered their key - if (blsApkRegistry.getOperatorId(operators[i]) == bytes32(0)) { - continue; - } - - // Read the weights for the operator and encode them into the operatorInfoLeaves - // for all weights, add them to the total weights. The ith index returns the weights array for the ith operator - for (uint256 j = 0; j < subArrayLength; j++) { - totalWeights[j] += weights[i][j]; - } - (BN254.G1Point memory g1Point,) = blsApkRegistry.getRegisteredPubkey(operators[i]); - - // Use `LeafCalculatorMixin` to calculate the leaf hash for the operator info - operatorInfoLeaves[operatorCount] = - calculateOperatorInfoLeaf(BN254OperatorInfo({pubkey: g1Point, weights: weights[i]})); - - // Add the operator's G1 point to the aggregate pubkey - aggregatePubkey = aggregatePubkey.plus(g1Point); - - // Increment the operator count - operatorCount++; - } - - // If there are no operators, return an empty operator set info - if (operatorCount == 0) { - return BN254OperatorSetInfo({ - operatorInfoTreeRoot: bytes32(0), - numOperators: 0, - aggregatePubkey: BN254.G1Point(0, 0), - totalWeights: new uint256[](0) - }); - } - - // Resize the operatorInfoLeaves array to the number of operators and merkleize - assembly { - mstore(operatorInfoLeaves, operatorCount) - } - - bytes32 operatorInfoTreeRoot = operatorInfoLeaves.merkleizeKeccak(); - - return BN254OperatorSetInfo({ - operatorInfoTreeRoot: operatorInfoTreeRoot, - numOperators: operatorCount, - aggregatePubkey: aggregatePubkey, - totalWeights: totalWeights - }); - } -} \ No newline at end of file From ba3ebb7f6667883e49ece8cbb76d0e5ed5baf80b Mon Sep 17 00:00:00 2001 From: Yash Patil <40046473+ypatil12@users.noreply.github.com> Date: Wed, 24 Sep 2025 13:28:34 -0400 Subject: [PATCH 3/4] chore: update lib --- lib/eigenlayer-contracts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/eigenlayer-contracts b/lib/eigenlayer-contracts index 7ecc83c7..31aade2f 160000 --- a/lib/eigenlayer-contracts +++ b/lib/eigenlayer-contracts @@ -1 +1 @@ -Subproject commit 7ecc83c7b180850531bc5b8b953a7340adeecd43 +Subproject commit 31aade2fc3bf6e2c0160cc2e7c7be1a6017296e5 From 154eb93cc7b9808480b2ef48253a1542e44ab9a4 Mon Sep 17 00:00:00 2001 From: Yash Patil <40046473+ypatil12@users.noreply.github.com> Date: Wed, 24 Sep 2025 13:30:47 -0400 Subject: [PATCH 4/4] chore: lock --- foundry.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/foundry.lock b/foundry.lock index cb38043a..e503ae3c 100644 --- a/foundry.lock +++ b/foundry.lock @@ -1,6 +1,6 @@ { "lib/eigenlayer-contracts": { - "rev": "73644e201541dc350a52b0f7a70fa125295a4128" + "rev": "31aade2fc3bf6e2c0160cc2e7c7be1a6017296e5" }, "lib/forge-std": { "rev": "77041d2ce690e692d6e03cc812b57d1ddaa4d505"