Skip to content

docs: middlewareV2/multichain #489

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

Draft
wants to merge 9 commits into
base: release-dev/multichain
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions docs/middlewareV2/AVSRegistrar.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
TODO
255 changes: 255 additions & 0 deletions docs/middlewareV2/OperatorTableCalculator.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,255 @@
## OperatorTableCalculator

| File | Type | Notes |
| -------- | -------- |
| [`ECDSATableCalculatorBase.sol`](../../src/middlewareV2/tableCalculator/ECDSATableCalculatorBase.sol) | Abstract | Base functionality for ECDSA operator tables |
| [`BN254TableCalculatorBase.sol`](../../src/middlewareV2/tableCalculator/BN254TableCalculatorBase.sol) | Abstract | Base functionality for BN254 operator tables |

Interfaces:

| File | Notes |
| -------- | -------- |
| [`IOperatorTableCalculator.sol`](../../lib/eigenlayer-contracts/src/contracts/interfaces/IOperatorTableCalculator.sol) | Base interface for all calculators (in core repo) |
| [`IECDSATableCalculator.sol`](../../src/interfaces/IECDSATableCalculator.sol) | ECDSA-specific interface |
| [`IBN254TableCalculator.sol`](../../src/interfaces/IBN254TableCalculator.sol) | BN254-specific interface |

---

## Overview

The OperatorTableCalculator contracts are 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/release-dev/multichain/docs/multichain#common-user-flows) for this process.

The base contracts (`ECDSATableCalculatorBase` and `BN254TableCalculatorBase`) provide the core logic for table calculation to be consumed by EigenLayer core, while leaving weight calculation as an unimplemented method to be implemented by derived contracts.

### Stake Weighting

It is up to the AVS to define each operator's weights array in an operatorSet. Some examples include:

- A single array evaluated purely on slashable stake `[slashable_stake]`
- An array of 2 values can be used for evaluating on slashable and delegated stake `[slashable_stake, delegated_stake]`
- An array of several values can be used for evaluating stake on multiple strategies `[slashable_stake_stETH, slashable_stake_USDC, slashable_stake_EIGEN]`

In addition, an AVS can build custom calculation methodologies that include:
- Capping the stake of an operator
- Using oracles to price stake

The [`ECDSATableCalculator`](../../src/middlewareV2/tableCalculator/ECDSATableCalculator.sol) and [`BN254TableCalculator`](../../src/middlewareV2/tableCalculator/BN254TableCalculator.sol) value slashable stake equally across all strategies. For example, if an operator allocates 100 stETH, 100 wETH, and 100 the calculator would return 300 for the stake weight of the operator.


---

## ECDSATableCalculatorBase

The `ECDSATableCalculatorBase` provides base functionality for calculating ECDSA operator tables. It handles operator key retrieval and table construction.

### Core Functions

#### `calculateOperatorTable`

```solidity
/**
* @notice A struct that contains information about a single operator
* @param pubkey The address of the signing ECDSA key of the operator and not the operator address itself.
* This is read from the KeyRegistrar contract.
* @param weights The weights of the operator for a single operatorSet
* @dev The `weights` array can be defined as a list of arbitrary groupings. For example,
* it can be [slashable_stake, delegated_stake, strategy_i_stake, ...]
* @dev The `weights` array should be the same length for each operator in the operatorSet.
*/
struct ECDSAOperatorInfo {
address pubkey;
uint256[] weights;
}

/**
* @notice calculates the operatorInfos for a given operatorSet
* @param operatorSet the operatorSet to calculate the operator table for
* @return operatorInfos the list of operatorInfos for the given operatorSet
* @dev The output of this function is converted to bytes via the `calculateOperatorTableBytes` function
*/
function calculateOperatorTable(
OperatorSet calldata operatorSet
) external view returns (ECDSAOperatorInfo[] memory operatorInfos);
```

Calculates and returns an array of `ECDSAOperatorInfo` structs containing public keys and weights for all operators in the operatorSet who have registered ECDSA keys.

*Effects*:
* None (view function)

*Process*:
* Calls `_getOperatorWeights` to retrieve operator addresses and their weights
* For each operator with a registered ECDSA key:
* Retrieves the ECDSA address (public key) from the `KeyRegistrar`
* Creates an `ECDSAOperatorInfo` struct with the public key and weights
* Returns only operators with registered keys

#### `calculateOperatorTableBytes`

```solidity
/**
* @notice Calculates the operator table bytes for a given operatorSet
* @param operatorSet The operatorSet to calculate the operator table for
* @return operatorTableBytes The encoded operator table bytes
*/
function calculateOperatorTableBytes(
OperatorSet calldata operatorSet
) external view returns (bytes memory operatorTableBytes);
```

Returns the ABI-encoded bytes representation of the operator table, which is used by the `CrossChainRegistry` to calculate the operatorTable.

*Returns*:
* ABI-encoded array of `ECDSAOperatorInfo` structs

### Abstract Methods

#### `_getOperatorWeights`

```solidity
/**
* @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
*/
function _getOperatorWeights(
OperatorSet calldata operatorSet
) internal view virtual returns (address[] memory operators, uint256[][] memory weights);
```

Must be implemented by derived contracts to define the weight calculation strategy. See [stakeWeighting](#stake-weighting) for more information.

An example integration is in [`ECDSATableCalculator`](../../src/middlewareV2/tableCalculator/ECDSATableCalculator.sol)

---

## BN254TableCalculatorBase

The `BN254TableCalculatorBase` provides base functionality for calculating BN254 operator tables.

### Core Functions

#### `calculateOperatorTable`

```solidity
/**
* @notice A struct that contains information about a single operator
* @param pubkey The G1 public key of the operator.
* @param weights The weights of the operator for a single operatorSet.
* @dev The `weights` array can be defined as a list of arbitrary groupings. For example,
* it can be [slashable_stake, delegated_stake, strategy_i_stake, ...]
*/
struct BN254OperatorInfo {
BN254.G1Point pubkey;
uint256[] weights;
}

/**
* @notice A struct that contains information about all operators for a given operatorSet
* @param operatorInfoTreeRoot The root of the operatorInfo tree. Each leaf is a `BN254OperatorInfo` struct
* @param numOperators The number of operators in the operatorSet.
* @param aggregatePubkey The aggregate G1 public key of the operators in the operatorSet.
* @param totalWeights The total weights of the operators in the operatorSet.
*
* @dev The operatorInfoTreeRoot is the root of a merkle tree that contains the operatorInfos for each operator in the operatorSet.
* It is calculated in this function and used by the `IBN254CertificateVerifier` to verify stakes against the non-signing operators
*
* @dev Retrieval of the `aggregatePubKey` depends on maintaining a key registry contract, see `BN254TableCalculatorBase` for an example implementation.
*
* @dev The `totalWeights` array should be the same length as each individual `weights` array in `operatorInfos`.
*/
struct BN254OperatorSetInfo {
bytes32 operatorInfoTreeRoot;
uint256 numOperators;
BN254.G1Point aggregatePubkey;
uint256[] totalWeights;
}

/**
* @notice calculates the operatorInfos for a given operatorSet
* @param operatorSet the operatorSet to calculate the operator table for
* @return operatorSetInfo the operatorSetInfo for the given operatorSet
* @dev The output of this function is converted to bytes via the `calculateOperatorTableBytes` function
*/
function calculateOperatorTable(
OperatorSet calldata operatorSet
) external view returns (BN254OperatorSetInfo memory operatorSetInfo);
```

Calculates and returns a `BN254OperatorSetInfo` struct containing:
- A merkle tree root of operator information
- The total number of operators
- An aggregate BN254 public key
- Total weights across all operators

*Effects*:
* None (view function)

*Process*:
* Calls `_getOperatorWeights` to retrieve operator addresses and their weights
* For each operator with a registered BN254 key:
* Retrieves the BN254 G1 point from the `KeyRegistrar`
* Adds the operator's weights to the total weights
* Creates a merkle leaf from the operator info
* Adds the G1 point to the aggregate public key
* Constructs a merkle tree from all operator info leaves
* Returns the complete operator set information

BN254 tables take advantage of signature aggregation. As such, we add operator's weights to the total weights. We generate a merkle root that contains individual operator stakes (`BN254OperatorInfo`) to lower transport costs. See the core [`BN254CertificateVerifier`](https://github.com/Layr-Labs/eigenlayer-contracts/blob/release-dev/multichain/docs/multichain/destination/CertificateVerifier.md) for more information on the caching and verification scheme.

#### `calculateOperatorTableBytes`

```solidity
/**
* @notice Calculates the operator table bytes for a given operatorSet
* @param operatorSet The operatorSet to calculate the operator table for
* @return operatorTableBytes The encoded operator table bytes
*/
function calculateOperatorTableBytes(
OperatorSet calldata operatorSet
) external view returns (bytes memory operatorTableBytes);
```

Returns the ABI-encoded bytes representation of the operator table, which is used by the `CrossChainRegistry` to calculate the operatorTable.

*Returns*:
* ABI-encoded `BN254OperatorSetInfo` struct

#### `getOperatorInfos`

```solidity
/**
* @notice Get the operatorInfos for a given operatorSet
* @param operatorSet the operatorSet to get the operatorInfos for
* @return operatorInfos the operatorInfos for the given operatorSet
*/
function getOperatorInfos(
OperatorSet calldata operatorSet
) external view returns (BN254OperatorInfo[] memory operatorInfos);
```

Returns an array of `BN254OperatorInfo` structs for all operators in the operatorSet who have registered BN254 keys.

*Effects*:
* None (view function)

### Abstract Methods

#### `_getOperatorWeights`

```solidity
/**
* @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
*/
function _getOperatorWeights(
OperatorSet calldata operatorSet
) internal view virtual returns (address[] memory operators, uint256[][] memory weights);
```

Must be implemented by derived contracts to define the weight calculation strategy. Similar to ECDSA, weights are a 2D array supporting multiple weight types per operator.

An example integration is defined in [`BN254TableCalculator`](../../src/middlewareV2/tableCalculator/BN254TableCalculator.sol).
64 changes: 64 additions & 0 deletions docs/middlewareV2/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
[elip-008]: https://github.com/eigenfoundation/ELIPs/blob/main/ELIPs/ELIP-008.md
[core-multichain-docs]: https://github.com/Layr-Labs/eigenlayer-contracts/tree/release-dev/multichain/docs/multichain

## MiddlewareV2

The middlewareV2 architecture utilizes EigenLayer core contracts as a base for new multichain contracts.

---

### Contents

* [System Diagram](#system-diagram)
* [AVS Registrar](#avs-registrar)
* [Operator Table Calculator](#operator-table-calculator)
* [`ECSDATableCalculator`](#ecdsatablecalculator)
* [`BN254TableCalculator`](#bn254tablecalculator)
* [Core Contract Integrations](#core-contract-integrations)
* [`KeyRegistrar`](#key-registrar)
* [`AllocationManager`](#allocation-manager)
* [`CertificateVerifier`](#certificate-verifier)
* [Roles and Actors](#roles-and-actors)
* [Migration](#migration)

---

### System Diagram

---

### AVS Registrar

---

### Operator Table Calculator

| File | Type |
| -------- | -------- |
| [`BN254TableCalculatorBase.sol`](../../src/middlewareV2/tableCalculator/BN254TableCalculatorBase.sol) | Abstract Contract |
| [`BN254TableCalculator.sol`](../../src/middlewareV2/tableCalculator/BN254TableCalculator.sol) | Basic table calculator that sums slashable stake across all strategies |
| [`ECDSATableCalculatorBase.sol`](../../src/middlewareV2/tableCalculator/ECDSATableCalculator.sol) | Abstract Contract |
| [`ECDSATableCalculator.sol`](../../src/middlewareV2/tableCalculator/ECDSATableCalculator.sol) | Basic table calculator that sums slashable stake across all strategies |


These contracts define custom stake weights of operators in an operatorSet. They are segmented by key-type.

See full documentation in [`/operatorTableCalculator.md`](./OperatorTableCalculator.md).

---

### Core Contract Integrations

#### Key Registrar

#### Allocation Manager

#### Certificate Verifier

---

### Roles and Actors

---

### Migration
2 changes: 1 addition & 1 deletion lib/eigenlayer-contracts
Submodule eigenlayer-contracts updated 53 files
+2 −2 pkg/bindings/BN254CertificateVerifier/binding.go
+1 −1 pkg/bindings/BN254CertificateVerifierStorage/binding.go
+1 −1 pkg/bindings/BN254TableCalculator/binding.go
+1 −1 pkg/bindings/CrossChainRegistry/binding.go
+2 −2 pkg/bindings/ECDSACertificateVerifier/binding.go
+1 −1 pkg/bindings/ECDSACertificateVerifierStorage/binding.go
+1 −1 pkg/bindings/ECDSATableCalculator/binding.go
+1 −1 pkg/bindings/IBN254CertificateVerifier/binding.go
+1 −1 pkg/bindings/IBaseCertificateVerifier/binding.go
+1 −1 pkg/bindings/IECDSACertificateVerifier/binding.go
+1 −1 pkg/bindings/IKeyRegistrar/binding.go
+263 −1 pkg/bindings/IOperatorTableUpdater/binding.go
+2 −2 pkg/bindings/KeyRegistrar/binding.go
+1 −1 pkg/bindings/KeyRegistrarStorage/binding.go
+250 −2 pkg/bindings/OperatorTableUpdater/binding.go
+263 −1 pkg/bindings/OperatorTableUpdaterStorage/binding.go
+0 −150 script/deploy/devnet/mutlichain/deploy_globalRootConfirmerSet.s.sol
+0 −115 script/deploy/devnet/mutlichain/deploy_multichain_l1.s.sol
+0 −170 script/deploy/devnet/mutlichain/deploy_multichain_l2.s.sol
+0 −5 script/utils/ExistingDeploymentParser.sol
+2 −2 src/contracts/interfaces/IBN254CertificateVerifier.sol
+0 −62 src/contracts/interfaces/IBN254TableCalculator.sol
+2 −0 src/contracts/interfaces/IBaseCertificateVerifier.sol
+2 −2 src/contracts/interfaces/IECDSACertificateVerifier.sol
+0 −47 src/contracts/interfaces/IECDSATableCalculator.sol
+52 −0 src/contracts/interfaces/IOperatorTableCalculator.sol
+51 −3 src/contracts/interfaces/IOperatorTableUpdater.sol
+3 −0 src/contracts/multichain/BN254CertificateVerifier.sol
+0 −1 src/contracts/multichain/BN254CertificateVerifierStorage.sol
+0 −79 src/contracts/multichain/BN254TableCalculator.sol
+0 −180 src/contracts/multichain/BN254TableCalculatorBase.sol
+3 −0 src/contracts/multichain/ECDSACertificateVerifier.sol
+0 −1 src/contracts/multichain/ECDSACertificateVerifierStorage.sol
+0 −79 src/contracts/multichain/ECDSATableCalculator.sol
+0 −127 src/contracts/multichain/ECDSATableCalculatorBase.sol
+55 −5 src/contracts/multichain/OperatorTableUpdater.sol
+4 −1 src/contracts/multichain/OperatorTableUpdaterStorage.sol
+22 −22 src/contracts/permissions/KeyRegistrar.sol
+3 −3 src/contracts/permissions/KeyRegistrarStorage.sol
+0 −2 src/test/integration/IntegrationDeployer.t.sol
+0 −324 src/test/integration/tests/multichain/BN254OperatorSet_CalculateWeights.t.sol
+11 −0 src/test/mocks/OperatorTableUpdaterMock.sol
+0 −31 src/test/tree/BN254TableCalculatorBase.tree
+0 −34 src/test/tree/ECDSATableCalculatorBase.tree
+115 −0 src/test/tree/KeyRegistrar.tree
+19 −1 src/test/tree/OperatorTableUpdaterUnit.tree
+14 −1 src/test/unit/BN254CertificateVerifierUnit.t.sol
+0 −694 src/test/unit/BN254TableCalculatorBaseUnit.t.sol
+13 −0 src/test/unit/ECDSACertificateVerifierUnit.t.sol
+0 −742 src/test/unit/ECDSATableCalculatorBaseUnit.t.sol
+599 −435 src/test/unit/KeyRegistrarUnit.t.sol
+892 −0 src/test/unit/Old.t.sol
+260 −7 src/test/unit/OperatorTableUpdaterUnit.t.sol
29 changes: 29 additions & 0 deletions src/interfaces/IBN254TableCalculator.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.5.0;

import {OperatorSet} from "eigenlayer-contracts/src/contracts/libraries/OperatorSetLib.sol";
import {
IOperatorTableCalculator,
IOperatorTableCalculatorTypes
} from "eigenlayer-contracts/src/contracts/interfaces/IOperatorTableCalculator.sol";

interface IBN254TableCalculator is IOperatorTableCalculator, IOperatorTableCalculatorTypes {
/**
* @notice calculates the operatorInfos for a given operatorSet
* @param operatorSet the operatorSet to calculate the operator table for
* @return operatorSetInfo the operatorSetInfo for the given operatorSet
* @dev The output of this function is converted to bytes via the `calculateOperatorTableBytes` function
*/
function calculateOperatorTable(
OperatorSet calldata operatorSet
) external view returns (BN254OperatorSetInfo memory operatorSetInfo);

/**
* @notice Get the operatorInfos for a given operatorSet
* @param operatorSet the operatorSet to get the operatorInfos for
* @return operatorInfos the operatorInfos for the given operatorSet
*/
function getOperatorInfos(
OperatorSet calldata operatorSet
) external view returns (BN254OperatorInfo[] memory operatorInfos);
}
20 changes: 20 additions & 0 deletions src/interfaces/IECDSATableCalculator.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.5.0;

import {OperatorSet} from "eigenlayer-contracts/src/contracts/libraries/OperatorSetLib.sol";
import {
IOperatorTableCalculator,
IOperatorTableCalculatorTypes
} from "eigenlayer-contracts/src/contracts/interfaces/IOperatorTableCalculator.sol";

interface IECDSATableCalculator is IOperatorTableCalculator, IOperatorTableCalculatorTypes {
/**
* @notice calculates the operatorInfos for a given operatorSet
* @param operatorSet the operatorSet to calculate the operator table for
* @return operatorInfos the list of operatorInfos for the given operatorSet
* @dev The output of this function is converted to bytes via the `calculateOperatorTableBytes` function
*/
function calculateOperatorTable(
OperatorSet calldata operatorSet
) external view returns (ECDSAOperatorInfo[] memory operatorInfos);
}
Loading
Loading