From 3ed9ef6ed3eb72c46ce3050eb84af28f0afdfae2 Mon Sep 17 00:00:00 2001 From: quaq <56312047+0x0aa0@users.noreply.github.com> Date: Wed, 11 Dec 2024 11:50:02 -0600 Subject: [PATCH] V2 Blob Verification (#781) --- contracts/compile.sh | 2 +- contracts/script/EigenDADeployer.s.sol | 64 ++- contracts/script/MockRollupDeployer.s.sol | 2 +- contracts/src/core/EigenDABlobVerifier.sol | 209 +++++++++ contracts/src/core/EigenDARelayRegistry.sol | 38 ++ .../src/core/EigenDARelayRegistryStorage.sol | 20 + contracts/src/core/EigenDAServiceManager.sol | 16 +- .../src/core/EigenDAServiceManagerStorage.sol | 1 + .../src/core/EigenDAThresholdRegistry.sol | 109 +++++ .../core/EigenDAThresholdRegistryStorage.sol | 27 ++ .../src/interfaces/IEigenDABlobVerifier.sol | 76 +--- .../src/interfaces/IEigenDARelayRegistry.sol | 12 +- contracts/src/interfaces/IEigenDAStructs.sol | 6 +- .../interfaces/IEigenDAThresholdRegistry.sol | 18 +- .../EigenDABlobVerificationUtils.sol | 410 ++++++++++++++++++ contracts/src/libraries/EigenDAHasher.sol | 13 +- .../harnesses/EigenDABlobUtilsHarness.sol | 26 -- .../rollup}/EigenDARollupUtils.sol | 8 +- contracts/{src => test}/rollup/MockRollup.sol | 10 +- .../test/{unit => rollup}/MockRollup.t.sol | 126 ++++-- contracts/test/unit/EigenDABlobUtils.t.sol | 265 +++++------ .../test/unit/EigenDAServiceManagerUnit.t.sol | 106 ++++- 22 files changed, 1232 insertions(+), 332 deletions(-) create mode 100644 contracts/src/core/EigenDABlobVerifier.sol create mode 100644 contracts/src/core/EigenDARelayRegistry.sol create mode 100644 contracts/src/core/EigenDARelayRegistryStorage.sol create mode 100644 contracts/src/core/EigenDAThresholdRegistry.sol create mode 100644 contracts/src/core/EigenDAThresholdRegistryStorage.sol create mode 100644 contracts/src/libraries/EigenDABlobVerificationUtils.sol delete mode 100644 contracts/test/harnesses/EigenDABlobUtilsHarness.sol rename contracts/{src/libraries => test/rollup}/EigenDARollupUtils.sol (98%) rename contracts/{src => test}/rollup/MockRollup.sol (89%) rename contracts/test/{unit => rollup}/MockRollup.t.sol (64%) diff --git a/contracts/compile.sh b/contracts/compile.sh index aa925b6f6e..f6db2e0b0e 100755 --- a/contracts/compile.sh +++ b/contracts/compile.sh @@ -21,7 +21,7 @@ function create_binding { forge clean forge build -contracts="PaymentVault SocketRegistry AVSDirectory DelegationManager BitmapUtils OperatorStateRetriever RegistryCoordinator BLSApkRegistry IndexRegistry StakeRegistry BN254 EigenDAServiceManager IEigenDAServiceManager MockRollup EjectionManager IEigenDARelayRegistry" +contracts="PaymentVault SocketRegistry AVSDirectory DelegationManager BitmapUtils OperatorStateRetriever RegistryCoordinator BLSApkRegistry IndexRegistry StakeRegistry BN254 EigenDAServiceManager IEigenDAServiceManager MockRollup EjectionManager EigenDABlobVerifier EigenDAThresholdRegistry EigenDARelayRegistry" for contract in $contracts; do create_binding ./ $contract ./bindings done diff --git a/contracts/script/EigenDADeployer.s.sol b/contracts/script/EigenDADeployer.s.sol index 245fc8145b..9c63cf4c31 100644 --- a/contracts/script/EigenDADeployer.s.sol +++ b/contracts/script/EigenDADeployer.s.sol @@ -16,14 +16,20 @@ import {IServiceManager} from "eigenlayer-middleware/interfaces/IServiceManager. import {IBLSApkRegistry} from "eigenlayer-middleware/interfaces/IBLSApkRegistry.sol"; import {EigenDAServiceManager, IAVSDirectory, IRewardsCoordinator} from "../src/core/EigenDAServiceManager.sol"; import {EigenDAHasher} from "../src/libraries/EigenDAHasher.sol"; -import {ISocketRegistry, SocketRegistry} from "eigenlayer-middleware/SocketRegistry.sol"; +import {EigenDAThresholdRegistry} from "../src/core/EigenDAThresholdRegistry.sol"; +import {EigenDABlobVerifier} from "../src/core/EigenDABlobVerifier.sol"; import {IEigenDAThresholdRegistry} from "../src/interfaces/IEigenDAThresholdRegistry.sol"; +import {IEigenDABatchMetadataStorage} from "../src/interfaces/IEigenDABatchMetadataStorage.sol"; +import {IEigenDASignatureVerifier} from "../src/interfaces/IEigenDASignatureVerifier.sol"; import {IEigenDARelayRegistry} from "../src/interfaces/IEigenDARelayRegistry.sol"; +import {EigenDARelayRegistry} from "../src/core/EigenDARelayRegistry.sol"; +import {ISocketRegistry, SocketRegistry} from "eigenlayer-middleware/SocketRegistry.sol"; import {DeployOpenEigenLayer, ProxyAdmin, ERC20PresetFixedSupply, TransparentUpgradeableProxy, IPauserRegistry} from "./DeployOpenEigenLayer.s.sol"; import "forge-std/Test.sol"; import "forge-std/Script.sol"; import "forge-std/StdJson.sol"; +import "../src/interfaces/IEigenDAStructs.sol"; // # To load the variables in the .env file // source .env @@ -36,17 +42,22 @@ contract EigenDADeployer is DeployOpenEigenLayer { BLSApkRegistry public apkRegistry; EigenDAServiceManager public eigenDAServiceManager; + EigenDAThresholdRegistry public eigenDAThresholdRegistry; + EigenDABlobVerifier public eigenDABlobVerifier; RegistryCoordinator public registryCoordinator; IIndexRegistry public indexRegistry; IStakeRegistry public stakeRegistry; ISocketRegistry public socketRegistry; OperatorStateRetriever public operatorStateRetriever; + EigenDARelayRegistry public eigenDARelayRegistry; BLSApkRegistry public apkRegistryImplementation; EigenDAServiceManager public eigenDAServiceManagerImplementation; IRegistryCoordinator public registryCoordinatorImplementation; IIndexRegistry public indexRegistryImplementation; IStakeRegistry public stakeRegistryImplementation; + EigenDAThresholdRegistry public eigenDAThresholdRegistryImplementation; + EigenDARelayRegistry public eigenDARelayRegistryImplementation; ISocketRegistry public socketRegistryImplementation; struct AddressConfig { @@ -93,9 +104,7 @@ contract EigenDADeployer is DeployOpenEigenLayer { } emptyContract = new EmptyContract(); - - // hard-coded inputs - + /** * First, deploy upgradeable proxy contracts that **will point** to the implementations. Since the implementation contracts are * not yet deployed, we give these proxies an empty contract as the initial implementation, to act as if they have no code. @@ -103,6 +112,13 @@ contract EigenDADeployer is DeployOpenEigenLayer { eigenDAServiceManager = EigenDAServiceManager( address(new TransparentUpgradeableProxy(address(emptyContract), address(eigenDAProxyAdmin), "")) ); + eigenDAThresholdRegistry = EigenDAThresholdRegistry( + address(new TransparentUpgradeableProxy(address(emptyContract), address(eigenDAProxyAdmin), "")) + ); + eigenDARelayRegistry = EigenDARelayRegistry( + address(new TransparentUpgradeableProxy(address(emptyContract), address(eigenDAProxyAdmin), "")) + ); + registryCoordinator = RegistryCoordinator( address(new TransparentUpgradeableProxy(address(emptyContract), address(eigenDAProxyAdmin), "")) ); @@ -205,8 +221,8 @@ contract EigenDADeployer is DeployOpenEigenLayer { rewardsCoordinator, registryCoordinator, stakeRegistry, - IEigenDAThresholdRegistry(address(0)), - IEigenDARelayRegistry(address(0)) + eigenDAThresholdRegistry, + eigenDARelayRegistry ); address[] memory confirmers = new address[](1); @@ -226,6 +242,42 @@ contract EigenDADeployer is DeployOpenEigenLayer { ) ); + eigenDAThresholdRegistryImplementation = new EigenDAThresholdRegistry(); + + VersionedBlobParams[] memory versionedBlobParams = new VersionedBlobParams[](0); + SecurityThresholds memory defaultSecurityThresholds = SecurityThresholds(33, 55); + + eigenDAProxyAdmin.upgradeAndCall( + TransparentUpgradeableProxy(payable(address(eigenDAThresholdRegistry))), + address(eigenDAThresholdRegistryImplementation), + abi.encodeWithSelector( + EigenDAThresholdRegistry.initialize.selector, + addressConfig.eigenDACommunityMultisig, + hex"212121", + hex"373737", + hex"0001", + versionedBlobParams, + defaultSecurityThresholds + ) + ); + operatorStateRetriever = new OperatorStateRetriever(); + + eigenDABlobVerifier = new EigenDABlobVerifier( + IEigenDAThresholdRegistry(address(eigenDAThresholdRegistry)), + IEigenDABatchMetadataStorage(address(eigenDAServiceManager)), + IEigenDASignatureVerifier(address(eigenDAServiceManager)), + IEigenDARelayRegistry(address(eigenDARelayRegistry)), + OperatorStateRetriever(address(operatorStateRetriever)), + IRegistryCoordinator(address(registryCoordinator)) + ); + + eigenDARelayRegistryImplementation = new EigenDARelayRegistry(); + + eigenDAProxyAdmin.upgradeAndCall( + TransparentUpgradeableProxy(payable(address(eigenDARelayRegistry))), + address(eigenDARelayRegistryImplementation), + abi.encodeWithSelector(EigenDARelayRegistry.initialize.selector, addressConfig.eigenDACommunityMultisig) + ); } } \ No newline at end of file diff --git a/contracts/script/MockRollupDeployer.s.sol b/contracts/script/MockRollupDeployer.s.sol index c6950fd28c..011f7bbe6f 100644 --- a/contracts/script/MockRollupDeployer.s.sol +++ b/contracts/script/MockRollupDeployer.s.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.9; import "forge-std/Script.sol"; -import "../src/rollup/MockRollup.sol"; +import "../test/rollup/MockRollup.sol"; import {IEigenDAServiceManager} from "../src/interfaces/IEigenDAServiceManager.sol"; contract MockRollupDeployer is Script { diff --git a/contracts/src/core/EigenDABlobVerifier.sol b/contracts/src/core/EigenDABlobVerifier.sol new file mode 100644 index 0000000000..34c93e9d6d --- /dev/null +++ b/contracts/src/core/EigenDABlobVerifier.sol @@ -0,0 +1,209 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.9; + +import {IEigenDABlobVerifier} from "../interfaces/IEigenDABlobVerifier.sol"; +import {IEigenDAThresholdRegistry} from "../interfaces/IEigenDAThresholdRegistry.sol"; +import {IEigenDABatchMetadataStorage} from "../interfaces/IEigenDABatchMetadataStorage.sol"; +import {IEigenDASignatureVerifier} from "../interfaces/IEigenDASignatureVerifier.sol"; +import {EigenDABlobVerificationUtils} from "../libraries/EigenDABlobVerificationUtils.sol"; +import {OperatorStateRetriever} from "lib/eigenlayer-middleware/src/OperatorStateRetriever.sol"; +import {IRegistryCoordinator} from "lib/eigenlayer-middleware/src/RegistryCoordinator.sol"; +import {IEigenDARelayRegistry} from "../interfaces/IEigenDARelayRegistry.sol"; +import "../interfaces/IEigenDAStructs.sol"; + +contract EigenDABlobVerifier is IEigenDABlobVerifier { + + IEigenDAThresholdRegistry public immutable eigenDAThresholdRegistry; + IEigenDABatchMetadataStorage public immutable eigenDABatchMetadataStorage; + IEigenDASignatureVerifier public immutable eigenDASignatureVerifier; + IEigenDARelayRegistry public immutable eigenDARelayRegistry; + + OperatorStateRetriever public immutable operatorStateRetriever; + IRegistryCoordinator public immutable registryCoordinator; + + constructor( + IEigenDAThresholdRegistry _eigenDAThresholdRegistry, + IEigenDABatchMetadataStorage _eigenDABatchMetadataStorage, + IEigenDASignatureVerifier _eigenDASignatureVerifier, + IEigenDARelayRegistry _eigenDARelayRegistry, + OperatorStateRetriever _operatorStateRetriever, + IRegistryCoordinator _registryCoordinator + ) { + eigenDAThresholdRegistry = _eigenDAThresholdRegistry; + eigenDABatchMetadataStorage = _eigenDABatchMetadataStorage; + eigenDASignatureVerifier = _eigenDASignatureVerifier; + eigenDARelayRegistry = _eigenDARelayRegistry; + operatorStateRetriever = _operatorStateRetriever; + registryCoordinator = _registryCoordinator; + } + + ///////////////////////// V1 /////////////////////////////// + + /** + * @notice Verifies a the blob is valid for the required quorums + * @param blobHeader The blob header to verify + * @param blobVerificationProof The blob verification proof to verify the blob against + */ + function verifyBlobV1( + BlobHeader calldata blobHeader, + BlobVerificationProof calldata blobVerificationProof + ) external view { + EigenDABlobVerificationUtils._verifyBlobV1ForQuorums( + eigenDAThresholdRegistry, + eigenDABatchMetadataStorage, + blobHeader, + blobVerificationProof, + quorumNumbersRequired() + ); + } + + /** + * @notice Verifies a batch of blobs for the required quorums + * @param blobHeaders The blob headers to verify + * @param blobVerificationProofs The blob verification proofs to verify the blobs against + */ + function verifyBlobsV1( + BlobHeader[] calldata blobHeaders, + BlobVerificationProof[] calldata blobVerificationProofs + ) external view { + EigenDABlobVerificationUtils._verifyBlobsV1ForQuorums( + eigenDAThresholdRegistry, + eigenDABatchMetadataStorage, + blobHeaders, + blobVerificationProofs, + quorumNumbersRequired() + ); + } + + ///////////////////////// V2 /////////////////////////////// + + /** + * @notice Verifies a blob for the base required quorums and the default security thresholds + * @param batchHeader The batch header of the blob + * @param blobVerificationProof The blob verification proof for the blob + * @param nonSignerStakesAndSignature The nonSignerStakesAndSignature for the blob + */ + function verifyBlobV2( + BatchHeaderV2 calldata batchHeader, + BlobVerificationProofV2 calldata blobVerificationProof, + NonSignerStakesAndSignature calldata nonSignerStakesAndSignature + ) external view { + EigenDABlobVerificationUtils._verifyBlobV2ForQuorums( + eigenDAThresholdRegistry, + eigenDASignatureVerifier, + eigenDARelayRegistry, + batchHeader, + blobVerificationProof, + nonSignerStakesAndSignature, + getDefaultSecurityThresholdsV2(), + quorumNumbersRequired() + ); + } + + /** + * @notice Verifies a blob for the base required quorums and the default security thresholds + * @param signedBatch The signed batch to verify the blob against + * @param blobVerificationProof The blob verification proof for the blob + */ + function verifyBlobV2FromSignedBatch( + SignedBatch calldata signedBatch, + BlobVerificationProofV2 calldata blobVerificationProof + ) external view { + EigenDABlobVerificationUtils._verifyBlobV2ForQuorumsFromSignedBatch( + eigenDAThresholdRegistry, + eigenDASignatureVerifier, + eigenDARelayRegistry, + operatorStateRetriever, + registryCoordinator, + signedBatch, + blobVerificationProof, + getDefaultSecurityThresholdsV2(), + quorumNumbersRequired() + ); + } + + ///////////////////////// HELPER FUNCTIONS /////////////////////////////// + + /** + * @notice Returns the nonSignerStakesAndSignature for a given blob and signed batch + * @param signedBatch The signed batch to get the nonSignerStakesAndSignature for + */ + function getNonSignerStakesAndSignature( + SignedBatch calldata signedBatch + ) external view returns (NonSignerStakesAndSignature memory) { + return EigenDABlobVerificationUtils._getNonSignerStakesAndSignature( + operatorStateRetriever, + registryCoordinator, + signedBatch.attestation + ); + } + + /** + * @notice Verifies the security parameters for a blob + * @param blobParams The blob params to verify + * @param securityThresholds The security thresholds to verify against + */ + function verifyBlobSecurityParams( + VersionedBlobParams memory blobParams, + SecurityThresholds memory securityThresholds + ) external view { + EigenDABlobVerificationUtils._verifyBlobSecurityParams(blobParams, securityThresholds); + } + + /** + * @notice Verifies the security parameters for a blob + * @param version The version of the blob to verify + * @param securityThresholds The security thresholds to verify against + */ + function verifyBlobSecurityParams( + uint16 version, + SecurityThresholds memory securityThresholds + ) external view { + EigenDABlobVerificationUtils._verifyBlobSecurityParams(getBlobParams(version), securityThresholds); + } + + /// @notice Returns an array of bytes where each byte represents the adversary threshold percentage of the quorum at that index + function quorumAdversaryThresholdPercentages() external view returns (bytes memory) { + return eigenDAThresholdRegistry.quorumAdversaryThresholdPercentages(); + } + + /// @notice Returns an array of bytes where each byte represents the confirmation threshold percentage of the quorum at that index + function quorumConfirmationThresholdPercentages() external view returns (bytes memory) { + return eigenDAThresholdRegistry.quorumConfirmationThresholdPercentages(); + } + + /// @notice Returns an array of bytes where each byte represents the number of a required quorum + function quorumNumbersRequired() public view returns (bytes memory) { + return eigenDAThresholdRegistry.quorumNumbersRequired(); + } + + function getQuorumAdversaryThresholdPercentage( + uint8 quorumNumber + ) external view returns (uint8){ + return eigenDAThresholdRegistry.getQuorumAdversaryThresholdPercentage(quorumNumber); + } + + /// @notice Gets the confirmation threshold percentage for a quorum + function getQuorumConfirmationThresholdPercentage( + uint8 quorumNumber + ) external view returns (uint8){ + return eigenDAThresholdRegistry.getQuorumConfirmationThresholdPercentage(quorumNumber); + } + + /// @notice Checks if a quorum is required + function getIsQuorumRequired( + uint8 quorumNumber + ) external view returns (bool){ + return eigenDAThresholdRegistry.getIsQuorumRequired(quorumNumber); + } + + /// @notice Returns the blob params for a given blob version + function getBlobParams(uint16 version) public view returns (VersionedBlobParams memory) { + return eigenDAThresholdRegistry.getBlobParams(version); + } + + /// @notice Gets the default security thresholds for V2 + function getDefaultSecurityThresholdsV2() public view returns (SecurityThresholds memory) { + return eigenDAThresholdRegistry.getDefaultSecurityThresholdsV2(); + } +} diff --git a/contracts/src/core/EigenDARelayRegistry.sol b/contracts/src/core/EigenDARelayRegistry.sol new file mode 100644 index 0000000000..bb1e0bdfb5 --- /dev/null +++ b/contracts/src/core/EigenDARelayRegistry.sol @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.9; + +import {OwnableUpgradeable} from "@openzeppelin-upgrades/contracts/access/OwnableUpgradeable.sol"; +import {EigenDARelayRegistryStorage} from "./EigenDARelayRegistryStorage.sol"; +import {IEigenDARelayRegistry} from "../interfaces/IEigenDARelayRegistry.sol"; +import "../interfaces/IEigenDAStructs.sol"; + +/** + * @title Registry for EigenDA relay keys + * @author Layr Labs, Inc. + */ +contract EigenDARelayRegistry is OwnableUpgradeable, EigenDARelayRegistryStorage, IEigenDARelayRegistry { + + constructor() { + _disableInitializers(); + } + + function initialize( + address _initialOwner + ) external initializer { + _transferOwnership(_initialOwner); + } + + function addRelayInfo(RelayInfo memory relayInfo) external onlyOwner returns (uint32) { + relayKeyToInfo[nextRelayKey] = relayInfo; + emit RelayAdded(relayInfo.relayAddress, nextRelayKey, relayInfo.relayURL); + return nextRelayKey++; + } + + function relayKeyToAddress(uint32 key) external view returns (address) { + return relayKeyToInfo[key].relayAddress; + } + + function relayKeyToUrl(uint32 key) external view returns (string memory) { + return relayKeyToInfo[key].relayURL; + } +} diff --git a/contracts/src/core/EigenDARelayRegistryStorage.sol b/contracts/src/core/EigenDARelayRegistryStorage.sol new file mode 100644 index 0000000000..d0f37f22aa --- /dev/null +++ b/contracts/src/core/EigenDARelayRegistryStorage.sol @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.9; + +import "../interfaces/IEigenDAStructs.sol"; + +/** + * @title Storage variables for the `EigenDARelayRegistry` contract. + * @author Layr Labs, Inc. + * @notice This storage contract is separate from the logic to simplify the upgrade process. + */ +abstract contract EigenDARelayRegistryStorage { + + mapping(uint32 => RelayInfo) public relayKeyToInfo; + + uint32 public nextRelayKey; + + // storage gap for upgradeability + // slither-disable-next-line shadowing-state + uint256[48] private __GAP; +} \ No newline at end of file diff --git a/contracts/src/core/EigenDAServiceManager.sol b/contracts/src/core/EigenDAServiceManager.sol index f49c3206df..fcbfb2895a 100644 --- a/contracts/src/core/EigenDAServiceManager.sol +++ b/contracts/src/core/EigenDAServiceManager.sol @@ -152,24 +152,19 @@ contract EigenDAServiceManager is EigenDAServiceManagerStorage, ServiceManagerBa return referenceBlockNumber + STORE_DURATION_BLOCKS + BLOCK_STALE_MEASURE; } - /// @notice Returns the blob params for a given blob version - function getBlobParams(uint16 version) external view returns (VersionedBlobParams memory) { - return eigenDAThresholdRegistry.getBlobParams(version); - } - /// @notice Returns the bytes array of quorumAdversaryThresholdPercentages function quorumAdversaryThresholdPercentages() external view returns (bytes memory) { - return hex"212121"; + return eigenDAThresholdRegistry.quorumAdversaryThresholdPercentages(); } /// @notice Returns the bytes array of quorumAdversaryThresholdPercentages function quorumConfirmationThresholdPercentages() external view returns (bytes memory) { - return hex"373737"; + return eigenDAThresholdRegistry.quorumConfirmationThresholdPercentages(); } /// @notice Returns the bytes array of quorumsNumbersRequired function quorumNumbersRequired() external view returns (bytes memory) { - return hex"0001"; + return eigenDAThresholdRegistry.quorumNumbersRequired(); } function getQuorumAdversaryThresholdPercentage( @@ -196,4 +191,9 @@ contract EigenDAServiceManager is EigenDAServiceManagerStorage, ServiceManagerBa function getDefaultSecurityThresholdsV2() external view returns (SecurityThresholds memory) { return eigenDAThresholdRegistry.getDefaultSecurityThresholdsV2(); } + + /// @notice Returns the blob params for a given blob version + function getBlobParams(uint16 version) external view returns (VersionedBlobParams memory) { + return eigenDAThresholdRegistry.getBlobParams(version); + } } \ No newline at end of file diff --git a/contracts/src/core/EigenDAServiceManagerStorage.sol b/contracts/src/core/EigenDAServiceManagerStorage.sol index 843a1107c7..d98cdbbdb5 100644 --- a/contracts/src/core/EigenDAServiceManagerStorage.sol +++ b/contracts/src/core/EigenDAServiceManagerStorage.sol @@ -4,6 +4,7 @@ pragma solidity ^0.8.9; import {IEigenDAServiceManager} from "../interfaces/IEigenDAServiceManager.sol"; import {IEigenDAThresholdRegistry} from "../interfaces/IEigenDAThresholdRegistry.sol"; import {IEigenDARelayRegistry} from "../interfaces/IEigenDARelayRegistry.sol"; + /** * @title Storage variables for the `EigenDAServiceManager` contract. * @author Layr Labs, Inc. diff --git a/contracts/src/core/EigenDAThresholdRegistry.sol b/contracts/src/core/EigenDAThresholdRegistry.sol new file mode 100644 index 0000000000..871d995287 --- /dev/null +++ b/contracts/src/core/EigenDAThresholdRegistry.sol @@ -0,0 +1,109 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.9; + +import {EigenDAThresholdRegistryStorage} from "./EigenDAThresholdRegistryStorage.sol"; +import {IEigenDAThresholdRegistry} from "../interfaces/IEigenDAThresholdRegistry.sol"; +import {OwnableUpgradeable} from "@openzeppelin-upgrades/contracts/access/OwnableUpgradeable.sol"; +import {BitmapUtils} from "eigenlayer-middleware/libraries/BitmapUtils.sol"; +import "../interfaces/IEigenDAStructs.sol"; + +/** + * @title The `EigenDAThresholdRegistry` contract. + * @author Layr Labs, Inc. + */ +contract EigenDAThresholdRegistry is EigenDAThresholdRegistryStorage, OwnableUpgradeable { + + constructor() { + _disableInitializers(); + } + + function initialize( + address _initialOwner, + bytes memory _quorumAdversaryThresholdPercentages, + bytes memory _quorumConfirmationThresholdPercentages, + bytes memory _quorumNumbersRequired, + VersionedBlobParams[] memory _versionedBlobParams, + SecurityThresholds memory _defaultSecurityThresholdsV2 + ) external initializer { + _transferOwnership(_initialOwner); + + quorumAdversaryThresholdPercentages = _quorumAdversaryThresholdPercentages; + quorumConfirmationThresholdPercentages = _quorumConfirmationThresholdPercentages; + quorumNumbersRequired = _quorumNumbersRequired; + defaultSecurityThresholdsV2 = _defaultSecurityThresholdsV2; + + for (uint256 i = 0; i < _versionedBlobParams.length; ++i) { + _addVersionedBlobParams(_versionedBlobParams[i]); + } + } + + function updateQuorumAdversaryThresholdPercentages(bytes memory _quorumAdversaryThresholdPercentages) external onlyOwner { + emit QuorumAdversaryThresholdPercentagesUpdated(quorumAdversaryThresholdPercentages, _quorumAdversaryThresholdPercentages); + quorumAdversaryThresholdPercentages = _quorumAdversaryThresholdPercentages; + } + + function updateQuorumConfirmationThresholdPercentages(bytes memory _quorumConfirmationThresholdPercentages) external onlyOwner { + emit QuorumConfirmationThresholdPercentagesUpdated(quorumConfirmationThresholdPercentages, _quorumConfirmationThresholdPercentages); + quorumConfirmationThresholdPercentages = _quorumConfirmationThresholdPercentages; + } + + function updateQuorumNumbersRequired(bytes memory _quorumNumbersRequired) external onlyOwner { + emit QuorumNumbersRequiredUpdated(quorumNumbersRequired, _quorumNumbersRequired); + quorumNumbersRequired = _quorumNumbersRequired; + } + + function updateDefaultSecurityThresholdsV2(SecurityThresholds memory _defaultSecurityThresholdsV2) external onlyOwner { + emit DefaultSecurityThresholdsV2Updated(defaultSecurityThresholdsV2, _defaultSecurityThresholdsV2); + defaultSecurityThresholdsV2 = _defaultSecurityThresholdsV2; + } + + function addVersionedBlobParams(VersionedBlobParams memory _versionedBlobParams) external onlyOwner returns (uint16) { + return _addVersionedBlobParams(_versionedBlobParams); + } + + function _addVersionedBlobParams(VersionedBlobParams memory _versionedBlobParams) internal returns (uint16) { + versionedBlobParams[nextBlobVersion] = _versionedBlobParams; + emit VersionedBlobParamsAdded(nextBlobVersion, _versionedBlobParams); + return nextBlobVersion++; + } + + ///////////////////////// V1 /////////////////////////////// + + /// @notice Gets the adversary threshold percentage for a quorum + function getQuorumAdversaryThresholdPercentage( + uint8 quorumNumber + ) public view virtual returns (uint8 adversaryThresholdPercentage) { + if(quorumAdversaryThresholdPercentages.length > quorumNumber){ + adversaryThresholdPercentage = uint8(quorumAdversaryThresholdPercentages[quorumNumber]); + } + } + + /// @notice Gets the confirmation threshold percentage for a quorum + function getQuorumConfirmationThresholdPercentage( + uint8 quorumNumber + ) public view virtual returns (uint8 confirmationThresholdPercentage) { + if(quorumConfirmationThresholdPercentages.length > quorumNumber){ + confirmationThresholdPercentage = uint8(quorumConfirmationThresholdPercentages[quorumNumber]); + } + } + + /// @notice Checks if a quorum is required + function getIsQuorumRequired( + uint8 quorumNumber + ) public view virtual returns (bool) { + uint256 quorumBitmap = BitmapUtils.setBit(0, quorumNumber); + return (quorumBitmap & BitmapUtils.orderedBytesArrayToBitmap(quorumNumbersRequired) == quorumBitmap); + } + + ///////////////////////// V2 /////////////////////////////// + + /// @notice Gets the default security thresholds for V2 + function getDefaultSecurityThresholdsV2() external view returns (SecurityThresholds memory) { + return defaultSecurityThresholdsV2; + } + + /// @notice Returns the blob params for a given blob version + function getBlobParams(uint16 version) external view returns (VersionedBlobParams memory) { + return versionedBlobParams[version]; + } +} \ No newline at end of file diff --git a/contracts/src/core/EigenDAThresholdRegistryStorage.sol b/contracts/src/core/EigenDAThresholdRegistryStorage.sol new file mode 100644 index 0000000000..377fb8a037 --- /dev/null +++ b/contracts/src/core/EigenDAThresholdRegistryStorage.sol @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.9; + +import {IEigenDAThresholdRegistry} from "../interfaces/IEigenDAThresholdRegistry.sol"; +import "../interfaces/IEigenDAStructs.sol"; + +/** + * @title Storage variables for the `EigenDAThresholdRegistry` contract. + * @author Layr Labs, Inc. + * @notice This storage contract is separate from the logic to simplify the upgrade process. + */ +abstract contract EigenDAThresholdRegistryStorage is IEigenDAThresholdRegistry { + + bytes public quorumAdversaryThresholdPercentages; + + bytes public quorumConfirmationThresholdPercentages; + + bytes public quorumNumbersRequired; + + uint16 public nextBlobVersion; + + mapping(uint16 => VersionedBlobParams) public versionedBlobParams; + + SecurityThresholds public defaultSecurityThresholdsV2; + + uint256[44] private __GAP; +} \ No newline at end of file diff --git a/contracts/src/interfaces/IEigenDABlobVerifier.sol b/contracts/src/interfaces/IEigenDABlobVerifier.sol index 7cca2aa71c..31faeb4239 100644 --- a/contracts/src/interfaces/IEigenDABlobVerifier.sol +++ b/contracts/src/interfaces/IEigenDABlobVerifier.sol @@ -1,8 +1,8 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.9; +import {IEigenDAThresholdRegistry} from "./IEigenDAThresholdRegistry.sol"; import "./IEigenDAStructs.sol"; -import "./IEigenDAThresholdRegistry.sol"; interface IEigenDABlobVerifier is IEigenDAThresholdRegistry { @@ -16,17 +16,6 @@ interface IEigenDABlobVerifier is IEigenDAThresholdRegistry { BlobVerificationProof calldata blobVerificationProof ) external view; - /** - * @notice Verifies that a blob is valid for the required quorums and additional quorums - * @param blobHeader The blob header to verify - * @param blobVerificationProof The blob verification proof to verify the blob against - * @param additionalQuorumNumbersRequired The additional required quorum numbers - */ - function verifyBlobV1( - BlobHeader calldata blobHeader, - BlobVerificationProof calldata blobVerificationProof, - bytes calldata additionalQuorumNumbersRequired - ) external view; /** * @notice Verifies a batch of blobs for the required quorums @@ -38,19 +27,6 @@ interface IEigenDABlobVerifier is IEigenDAThresholdRegistry { BlobVerificationProof[] calldata blobVerificationProofs ) external view; - /** - * @notice Verifies a batch of blobs for the required quorums and additional quorums - * @param blobHeaders The blob headers to verify - * @param blobVerificationProofs The blob verification proofs to verify the blobs against - * @param additionalQuorumNumbersRequired The additional required quorum numbers - */ - function verifyBlobsV1( - BlobHeader[] calldata blobHeaders, - BlobVerificationProof[] calldata blobVerificationProofs, - bytes calldata additionalQuorumNumbersRequired - ) external view; - - /** * @notice Verifies a blob for the required quorums and the default security thresholds * @param batchHeader The batch header of the blob @@ -64,59 +40,21 @@ interface IEigenDABlobVerifier is IEigenDAThresholdRegistry { ) external view; /** - * @notice Verifies a blob for the required quorums and additional quorums - * @param batchHeader The batch header of the blob - * @param blobVerificationProof The blob verification proof for the blob - * @param nonSignerStakesAndSignature The nonSignerStakesAndSignature to verify the blob against - * @param additionalQuorumNumbersRequired The additional required quorum numbers - */ - function verifyBlobV2( - BatchHeaderV2 calldata batchHeader, - BlobVerificationProofV2 calldata blobVerificationProof, - NonSignerStakesAndSignature calldata nonSignerStakesAndSignature, - bytes calldata additionalQuorumNumbersRequired - ) external view; - - /** - * @notice Verifies a blob for the required quorums and additional quorums and a custom security threshold - * @param batchHeader The batch header of the blob + * @notice Verifies a blob for the base required quorums and the default security thresholds + * @param signedBatch The signed batch to verify the blob against * @param blobVerificationProof The blob verification proof for the blob - * @param nonSignerStakesAndSignature The nonSignerStakesAndSignature to verify the blob against - * @param securityThreshold The custom security threshold to verify the blob against - * @param additionalQuorumNumbersRequired The additional required quorum numbers - */ - function verifyBlobV2( - BatchHeaderV2 calldata batchHeader, - BlobVerificationProofV2 calldata blobVerificationProof, - NonSignerStakesAndSignature calldata nonSignerStakesAndSignature, - SecurityThresholds memory securityThreshold, - bytes calldata additionalQuorumNumbersRequired - ) external view; - - /** - * @notice Verifies a batch of blobs for the required quorums and additional quorums and a set of custom security threshold - * @param batchHeader The batch headers of the blobs - * @param blobVerificationProof The blob verification proofs for the blobs - * @param nonSignerStakesAndSignature The nonSignerStakesAndSignatures to verify the blobs against - * @param securityThresholds The set of custom security thresholds to verify the blobs against - * @param additionalQuorumNumbersRequired The additional required quorum numbers */ - function verifyBlobV2( - BatchHeaderV2 calldata batchHeader, - BlobVerificationProofV2 calldata blobVerificationProof, - NonSignerStakesAndSignature calldata nonSignerStakesAndSignature, - SecurityThresholds[] memory securityThresholds, - bytes calldata additionalQuorumNumbersRequired + function verifyBlobV2FromSignedBatch( + SignedBatch calldata signedBatch, + BlobVerificationProofV2 calldata blobVerificationProof ) external view; /** * @notice Returns the nonSignerStakesAndSignature for a given blob and signed batch * @param signedBatch The signed batch to get the nonSignerStakesAndSignature for - * @param blobHeader The blob header of the blob in the signed batch */ function getNonSignerStakesAndSignature( - SignedBatch calldata signedBatch, - BlobHeaderV2 calldata blobHeader + SignedBatch calldata signedBatch ) external view returns (NonSignerStakesAndSignature memory); /** diff --git a/contracts/src/interfaces/IEigenDARelayRegistry.sol b/contracts/src/interfaces/IEigenDARelayRegistry.sol index 7450e82e68..f99b9323ed 100644 --- a/contracts/src/interfaces/IEigenDARelayRegistry.sol +++ b/contracts/src/interfaces/IEigenDARelayRegistry.sol @@ -1,15 +1,15 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.9; -interface IEigenDARelayRegistry { +import "./IEigenDAStructs.sol"; - event RelayAdded(address indexed relay, uint32 indexed id, string relayURL); +interface IEigenDARelayRegistry { - function setRelayURL(address relay, uint32 id, string memory relayURL) external; + event RelayAdded(address indexed relay, uint32 indexed key, string relayURL); - function getRelayURL(uint32 id) external view returns (string memory); + function addRelayInfo(RelayInfo memory relayInfo) external returns (uint32); - function getRelayId(address relay) external view returns (uint32); + function relayKeyToAddress(uint32 key) external view returns (address); - function getRelayAddress(uint32 id) external view returns (address); + function relayKeyToUrl(uint32 key) external view returns (string memory); } diff --git a/contracts/src/interfaces/IEigenDAStructs.sol b/contracts/src/interfaces/IEigenDAStructs.sol index 445f4b3be8..faa5eab441 100644 --- a/contracts/src/interfaces/IEigenDAStructs.sol +++ b/contracts/src/interfaces/IEigenDAStructs.sol @@ -57,6 +57,11 @@ struct SecurityThresholds { uint8 adversaryThreshold; } +struct RelayInfo { + address relayAddress; + string relayURL; +} + struct BlobVerificationProofV2 { BlobCertificate blobCertificate; uint32 blobIndex; @@ -65,7 +70,6 @@ struct BlobVerificationProofV2 { struct BlobCertificate { BlobHeaderV2 blobHeader; - uint32 referenceBlockNumber; uint32[] relayKeys; } diff --git a/contracts/src/interfaces/IEigenDAThresholdRegistry.sol b/contracts/src/interfaces/IEigenDAThresholdRegistry.sol index 64ec0c60af..e022e9ca1e 100644 --- a/contracts/src/interfaces/IEigenDAThresholdRegistry.sol +++ b/contracts/src/interfaces/IEigenDAThresholdRegistry.sol @@ -5,8 +5,17 @@ import "../interfaces/IEigenDAStructs.sol"; interface IEigenDAThresholdRegistry { - /// @notice Returns the blob params for a given blob version - function getBlobParams(uint16 version) external view returns (VersionedBlobParams memory); + event VersionedBlobParamsAdded(uint16 indexed version, VersionedBlobParams versionedBlobParams); + + event QuorumAdversaryThresholdPercentagesUpdated(bytes previousQuorumAdversaryThresholdPercentages, bytes newQuorumAdversaryThresholdPercentages); + + event QuorumConfirmationThresholdPercentagesUpdated(bytes previousQuorumConfirmationThresholdPercentages, bytes newQuorumConfirmationThresholdPercentages); + + event QuorumNumbersRequiredUpdated(bytes previousQuorumNumbersRequired, bytes newQuorumNumbersRequired); + + event DefaultSecurityThresholdsV2Updated(SecurityThresholds previousDefaultSecurityThresholdsV2, SecurityThresholds newDefaultSecurityThresholdsV2); + + ///////////////////////// V1 /////////////////////////////// /// @notice Returns an array of bytes where each byte represents the adversary threshold percentage of the quorum at that index function quorumAdversaryThresholdPercentages() external view returns (bytes memory); @@ -32,6 +41,11 @@ interface IEigenDAThresholdRegistry { uint8 quorumNumber ) external view returns (bool); + ///////////////////////// V2 /////////////////////////////// + /// @notice Gets the default security thresholds for V2 function getDefaultSecurityThresholdsV2() external view returns (SecurityThresholds memory); + + /// @notice Returns the blob params for a given blob version + function getBlobParams(uint16 version) external view returns (VersionedBlobParams memory); } \ No newline at end of file diff --git a/contracts/src/libraries/EigenDABlobVerificationUtils.sol b/contracts/src/libraries/EigenDABlobVerificationUtils.sol new file mode 100644 index 0000000000..694c01d5d4 --- /dev/null +++ b/contracts/src/libraries/EigenDABlobVerificationUtils.sol @@ -0,0 +1,410 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.9; + +import {Merkle} from "eigenlayer-core/contracts/libraries/Merkle.sol"; +import {BN254} from "eigenlayer-middleware/libraries/BN254.sol"; +import {EigenDAHasher} from "./EigenDAHasher.sol"; +import {BitmapUtils} from "eigenlayer-middleware/libraries/BitmapUtils.sol"; +import {IEigenDABatchMetadataStorage} from "../interfaces/IEigenDABatchMetadataStorage.sol"; +import {IEigenDAThresholdRegistry} from "../interfaces/IEigenDAThresholdRegistry.sol"; +import {IEigenDASignatureVerifier} from "../interfaces/IEigenDASignatureVerifier.sol"; +import {OperatorStateRetriever} from "lib/eigenlayer-middleware/src/OperatorStateRetriever.sol"; +import {IRegistryCoordinator} from "lib/eigenlayer-middleware/src/RegistryCoordinator.sol"; +import {IEigenDARelayRegistry} from "../interfaces/IEigenDARelayRegistry.sol"; +import "../interfaces/IEigenDAStructs.sol"; + +/** + * @title Library of functions to be used by smart contracts wanting to verify submissions of blobs on EigenDA. + * @author Layr Labs, Inc. + */ +library EigenDABlobVerificationUtils { + using BN254 for BN254.G1Point; + + uint256 public constant THRESHOLD_DENOMINATOR = 100; + + function _verifyBlobV1ForQuorums( + IEigenDAThresholdRegistry eigenDAThresholdRegistry, + IEigenDABatchMetadataStorage batchMetadataStorage, + BlobHeader calldata blobHeader, + BlobVerificationProof calldata blobVerificationProof, + bytes memory requiredQuorumNumbers + ) internal view { + require( + EigenDAHasher.hashBatchMetadata(blobVerificationProof.batchMetadata) == + IEigenDABatchMetadataStorage(batchMetadataStorage).batchIdToBatchMetadataHash(blobVerificationProof.batchId), + "EigenDABlobVerificationUtils._verifyBlobForQuorums: batchMetadata does not match stored metadata" + ); + + require( + Merkle.verifyInclusionKeccak( + blobVerificationProof.inclusionProof, + blobVerificationProof.batchMetadata.batchHeader.blobHeadersRoot, + keccak256(abi.encodePacked(EigenDAHasher.hashBlobHeader(blobHeader))), + blobVerificationProof.blobIndex + ), + "EigenDABlobVerificationUtils._verifyBlobForQuorums: inclusion proof is invalid" + ); + + uint256 confirmedQuorumsBitmap; + + for (uint i = 0; i < blobHeader.quorumBlobParams.length; i++) { + + require( + uint8(blobVerificationProof.batchMetadata.batchHeader.quorumNumbers[uint8(blobVerificationProof.quorumIndices[i])]) == + blobHeader.quorumBlobParams[i].quorumNumber, + "EigenDABlobVerificationUtils._verifyBlobForQuorums: quorumNumber does not match" + ); + + require( + blobHeader.quorumBlobParams[i].confirmationThresholdPercentage > + blobHeader.quorumBlobParams[i].adversaryThresholdPercentage, + "EigenDABlobVerificationUtils._verifyBlobForQuorums: threshold percentages are not valid" + ); + + require( + blobHeader.quorumBlobParams[i].confirmationThresholdPercentage >= + eigenDAThresholdRegistry.getQuorumConfirmationThresholdPercentage(blobHeader.quorumBlobParams[i].quorumNumber), + "EigenDABlobVerificationUtils._verifyBlobForQuorums: confirmationThresholdPercentage is not met" + ); + + require( + uint8(blobVerificationProof.batchMetadata.batchHeader.signedStakeForQuorums[uint8(blobVerificationProof.quorumIndices[i])]) >= + blobHeader.quorumBlobParams[i].confirmationThresholdPercentage, + "EigenDABlobVerificationUtils._verifyBlobForQuorums: confirmationThresholdPercentage is not met" + ); + + confirmedQuorumsBitmap = BitmapUtils.setBit(confirmedQuorumsBitmap, blobHeader.quorumBlobParams[i].quorumNumber); + } + + require( + BitmapUtils.isSubsetOf( + BitmapUtils.orderedBytesArrayToBitmap(requiredQuorumNumbers), + confirmedQuorumsBitmap + ), + "EigenDABlobVerificationUtils._verifyBlobForQuorums: required quorums are not a subset of the confirmed quorums" + ); + } + + function _verifyBlobsV1ForQuorums( + IEigenDAThresholdRegistry eigenDAThresholdRegistry, + IEigenDABatchMetadataStorage batchMetadataStorage, + BlobHeader[] calldata blobHeaders, + BlobVerificationProof[] calldata blobVerificationProofs, + bytes memory requiredQuorumNumbers + ) internal view { + require( + blobHeaders.length == blobVerificationProofs.length, + "EigenDABlobVerificationUtils._verifyBlobsForQuorums: blobHeaders and blobVerificationProofs length mismatch" + ); + + bytes memory confirmationThresholdPercentages = eigenDAThresholdRegistry.quorumConfirmationThresholdPercentages(); + + for (uint i = 0; i < blobHeaders.length; ++i) { + require( + EigenDAHasher.hashBatchMetadata(blobVerificationProofs[i].batchMetadata) == + IEigenDABatchMetadataStorage(batchMetadataStorage).batchIdToBatchMetadataHash(blobVerificationProofs[i].batchId), + "EigenDABlobVerificationUtils._verifyBlobForQuorums: batchMetadata does not match stored metadata" + ); + + require( + Merkle.verifyInclusionKeccak( + blobVerificationProofs[i].inclusionProof, + blobVerificationProofs[i].batchMetadata.batchHeader.blobHeadersRoot, + keccak256(abi.encodePacked(EigenDAHasher.hashBlobHeader(blobHeaders[i]))), + blobVerificationProofs[i].blobIndex + ), + "EigenDABlobVerificationUtils._verifyBlobForQuorums: inclusion proof is invalid" + ); + + uint256 confirmedQuorumsBitmap; + + for (uint j = 0; j < blobHeaders[i].quorumBlobParams.length; j++) { + + require( + uint8(blobVerificationProofs[i].batchMetadata.batchHeader.quorumNumbers[uint8(blobVerificationProofs[i].quorumIndices[j])]) == + blobHeaders[i].quorumBlobParams[j].quorumNumber, + "EigenDABlobVerificationUtils._verifyBlobForQuorums: quorumNumber does not match" + ); + + require( + blobHeaders[i].quorumBlobParams[j].confirmationThresholdPercentage > + blobHeaders[i].quorumBlobParams[j].adversaryThresholdPercentage, + "EigenDABlobVerificationUtils._verifyBlobForQuorums: threshold percentages are not valid" + ); + + require( + blobHeaders[i].quorumBlobParams[j].confirmationThresholdPercentage >= + uint8(confirmationThresholdPercentages[blobHeaders[i].quorumBlobParams[j].quorumNumber]), + "EigenDABlobVerificationUtils._verifyBlobForQuorums: confirmationThresholdPercentage is not met" + ); + + require( + uint8(blobVerificationProofs[i].batchMetadata.batchHeader.signedStakeForQuorums[uint8(blobVerificationProofs[i].quorumIndices[j])]) >= + blobHeaders[i].quorumBlobParams[j].confirmationThresholdPercentage, + "EigenDABlobVerificationUtils._verifyBlobForQuorums: confirmationThresholdPercentage is not met" + ); + + confirmedQuorumsBitmap = BitmapUtils.setBit(confirmedQuorumsBitmap, blobHeaders[i].quorumBlobParams[j].quorumNumber); + } + + require( + BitmapUtils.isSubsetOf( + BitmapUtils.orderedBytesArrayToBitmap(requiredQuorumNumbers), + confirmedQuorumsBitmap + ), + "EigenDABlobVerificationUtils._verifyBlobForQuorums: required quorums are not a subset of the confirmed quorums" + ); + + } + } + + function _verifyBlobV2ForQuorums( + IEigenDAThresholdRegistry eigenDAThresholdRegistry, + IEigenDASignatureVerifier signatureVerifier, + IEigenDARelayRegistry eigenDARelayRegistry, + BatchHeaderV2 memory batchHeader, + BlobVerificationProofV2 memory blobVerificationProof, + NonSignerStakesAndSignature memory nonSignerStakesAndSignature, + SecurityThresholds memory securityThresholds, + bytes memory requiredQuorumNumbers + ) internal view { + require( + Merkle.verifyInclusionKeccak( + blobVerificationProof.inclusionProof, + batchHeader.batchRoot, + keccak256(abi.encodePacked(EigenDAHasher.hashBlobHeaderV2(blobVerificationProof.blobCertificate.blobHeader))), + blobVerificationProof.blobIndex + ), + "EigenDABlobVerificationUtils._verifyBlobV2ForQuorums: inclusion proof is invalid" + ); + + ( + QuorumStakeTotals memory quorumStakeTotals, + bytes32 signatoryRecordHash + ) = signatureVerifier.checkSignatures( + EigenDAHasher.hashBatchHeaderV2(batchHeader), + blobVerificationProof.blobCertificate.blobHeader.quorumNumbers, + batchHeader.referenceBlockNumber, + nonSignerStakesAndSignature + ); + + _verifyRelayKeysSet( + eigenDARelayRegistry, + blobVerificationProof.blobCertificate.relayKeys + ); + + _verifyBlobSecurityParams( + eigenDAThresholdRegistry.getBlobParams(blobVerificationProof.blobCertificate.blobHeader.version), + securityThresholds + ); + + uint256 confirmedQuorumsBitmap; + + for (uint i = 0; i < blobVerificationProof.blobCertificate.blobHeader.quorumNumbers.length; i++) { + require( + quorumStakeTotals.signedStakeForQuorum[i] * THRESHOLD_DENOMINATOR >= + quorumStakeTotals.totalStakeForQuorum[i] * securityThresholds.confirmationThreshold, + "EigenDABlobVerificationUtils._verifyBlobV2ForQuorums: signatories do not own at least threshold percentage of a quorum" + ); + + confirmedQuorumsBitmap = BitmapUtils.setBit( + confirmedQuorumsBitmap, + uint8(blobVerificationProof.blobCertificate.blobHeader.quorumNumbers[i]) + ); + } + + require( + BitmapUtils.isSubsetOf( + BitmapUtils.orderedBytesArrayToBitmap(requiredQuorumNumbers), + confirmedQuorumsBitmap + ), + "EigenDABlobVerificationUtils._verifyBlobV2ForQuorums: required quorums are not a subset of the confirmed quorums" + ); + } + + function _verifyBlobV2ForQuorumsForThresholds( + IEigenDAThresholdRegistry eigenDAThresholdRegistry, + IEigenDASignatureVerifier signatureVerifier, + IEigenDARelayRegistry eigenDARelayRegistry, + BatchHeaderV2 memory batchHeader, + BlobVerificationProofV2 memory blobVerificationProof, + NonSignerStakesAndSignature memory nonSignerStakesAndSignature, + SecurityThresholds[] memory securityThresholds, + bytes memory requiredQuorumNumbers + ) internal view { + require( + securityThresholds.length == blobVerificationProof.blobCertificate.blobHeader.quorumNumbers.length, + "EigenDABlobVerificationUtils._verifyBlobV2ForQuorums: securityThresholds length does not match quorumNumbers" + ); + + require( + Merkle.verifyInclusionKeccak( + blobVerificationProof.inclusionProof, + batchHeader.batchRoot, + keccak256(abi.encodePacked(EigenDAHasher.hashBlobHeaderV2(blobVerificationProof.blobCertificate.blobHeader))), + blobVerificationProof.blobIndex + ), + "EigenDABlobVerificationUtils._verifyBlobV2ForQuorums: inclusion proof is invalid" + ); + + ( + QuorumStakeTotals memory quorumStakeTotals, + bytes32 signatoryRecordHash + ) = signatureVerifier.checkSignatures( + EigenDAHasher.hashBatchHeaderV2(batchHeader), + blobVerificationProof.blobCertificate.blobHeader.quorumNumbers, + batchHeader.referenceBlockNumber, + nonSignerStakesAndSignature + ); + + _verifyRelayKeysSet( + eigenDARelayRegistry, + blobVerificationProof.blobCertificate.relayKeys + ); + + uint256 confirmedQuorumsBitmap; + VersionedBlobParams memory blobParams = eigenDAThresholdRegistry.getBlobParams(blobVerificationProof.blobCertificate.blobHeader.version); + + for (uint i = 0; i < blobVerificationProof.blobCertificate.blobHeader.quorumNumbers.length; i++) { + _verifyBlobSecurityParams( + blobParams, + securityThresholds[i] + ); + + require( + quorumStakeTotals.signedStakeForQuorum[i] * THRESHOLD_DENOMINATOR >= + quorumStakeTotals.totalStakeForQuorum[i] * securityThresholds[i].confirmationThreshold, + "EigenDABlobVerificationUtils._verifyBlobV2ForQuorums: signatories do not own at least threshold percentage of a quorum" + ); + + confirmedQuorumsBitmap = BitmapUtils.setBit( + confirmedQuorumsBitmap, + uint8(blobVerificationProof.blobCertificate.blobHeader.quorumNumbers[i]) + ); + } + + require( + BitmapUtils.isSubsetOf( + BitmapUtils.orderedBytesArrayToBitmap(requiredQuorumNumbers), + confirmedQuorumsBitmap + ), + "EigenDABlobVerificationUtils._verifyBlobV2ForQuorums: required quorums are not a subset of the confirmed quorums" + ); + } + + function _verifyBlobV2ForQuorumsFromSignedBatch( + IEigenDAThresholdRegistry eigenDAThresholdRegistry, + IEigenDASignatureVerifier signatureVerifier, + IEigenDARelayRegistry eigenDARelayRegistry, + OperatorStateRetriever operatorStateRetriever, + IRegistryCoordinator registryCoordinator, + SignedBatch memory signedBatch, + BlobVerificationProofV2 memory blobVerificationProof, + SecurityThresholds memory securityThresholds, + bytes memory requiredQuorumNumbers + ) internal view { + NonSignerStakesAndSignature memory nonSignerStakesAndSignature = _getNonSignerStakesAndSignature( + operatorStateRetriever, + registryCoordinator, + signedBatch.attestation + ); + + _verifyBlobV2ForQuorums( + eigenDAThresholdRegistry, + signatureVerifier, + eigenDARelayRegistry, + signedBatch.batchHeader, + blobVerificationProof, + nonSignerStakesAndSignature, + securityThresholds, + requiredQuorumNumbers + ); + } + + function _verifyBlobV2ForQuorumsForThresholdsFromSignedBatch( + IEigenDAThresholdRegistry eigenDAThresholdRegistry, + IEigenDASignatureVerifier signatureVerifier, + IEigenDARelayRegistry eigenDARelayRegistry, + OperatorStateRetriever operatorStateRetriever, + IRegistryCoordinator registryCoordinator, + SignedBatch memory signedBatch, + BlobVerificationProofV2 memory blobVerificationProof, + SecurityThresholds[] memory securityThresholds, + bytes memory requiredQuorumNumbers + ) internal view { + NonSignerStakesAndSignature memory nonSignerStakesAndSignature = _getNonSignerStakesAndSignature( + operatorStateRetriever, + registryCoordinator, + signedBatch.attestation + ); + + _verifyBlobV2ForQuorumsForThresholds( + eigenDAThresholdRegistry, + signatureVerifier, + eigenDARelayRegistry, + signedBatch.batchHeader, + blobVerificationProof, + nonSignerStakesAndSignature, + securityThresholds, + requiredQuorumNumbers + ); + } + + function _getNonSignerStakesAndSignature( + OperatorStateRetriever operatorStateRetriever, + IRegistryCoordinator registryCoordinator, + Attestation memory attestation + ) internal view returns (NonSignerStakesAndSignature memory nonSignerStakesAndSignature) { + bytes32[] memory nonSignerOperatorIds = new bytes32[](attestation.nonSignerPubkeys.length); + for (uint i = 0; i < attestation.nonSignerPubkeys.length; ++i) { + nonSignerOperatorIds[i] = BN254.hashG1Point(attestation.nonSignerPubkeys[i]); + } + + bytes memory quorumNumbers; + for (uint i = 0; i < attestation.quorumNumbers.length; ++i) { + quorumNumbers = abi.encodePacked(quorumNumbers, uint8(attestation.quorumNumbers[i])); + } + + OperatorStateRetriever.CheckSignaturesIndices memory checkSignaturesIndices = operatorStateRetriever.getCheckSignaturesIndices( + registryCoordinator, + attestation.referenceBlockNumber, + quorumNumbers, + nonSignerOperatorIds + ); + + nonSignerStakesAndSignature.nonSignerQuorumBitmapIndices = checkSignaturesIndices.nonSignerQuorumBitmapIndices; + nonSignerStakesAndSignature.nonSignerPubkeys = attestation.nonSignerPubkeys; + nonSignerStakesAndSignature.quorumApks = attestation.quorumApks; + nonSignerStakesAndSignature.apkG2 = attestation.apkG2; + nonSignerStakesAndSignature.sigma = attestation.sigma; + nonSignerStakesAndSignature.quorumApkIndices = checkSignaturesIndices.quorumApkIndices; + nonSignerStakesAndSignature.totalStakeIndices = checkSignaturesIndices.totalStakeIndices; + nonSignerStakesAndSignature.nonSignerStakeIndices = checkSignaturesIndices.nonSignerStakeIndices; + } + + function _verifyBlobSecurityParams( + VersionedBlobParams memory blobParams, + SecurityThresholds memory securityThresholds + ) internal pure { + require( + securityThresholds.confirmationThreshold > securityThresholds.adversaryThreshold, + "EigenDABlobVerificationUtils._verifyBlobSecurityParams: confirmationThreshold must be greater than adversaryThreshold" + ); + uint256 gamma = securityThresholds.confirmationThreshold - securityThresholds.adversaryThreshold; + uint256 n = (10000 - ((1_000_000 / gamma) / uint256(blobParams.codingRate))) * uint256(blobParams.numChunks); + require(n >= blobParams.maxNumOperators * 10000, "EigenDABlobVerificationUtils._verifyBlobSecurityParams: security assumptions are not met"); + } + + function _verifyRelayKeysSet( + IEigenDARelayRegistry eigenDARelayRegistry, + uint32[] memory relayKeys + ) internal view { + for (uint i = 0; i < relayKeys.length; ++i) { + require( + eigenDARelayRegistry.relayKeyToAddress(relayKeys[i]) != address(0), + "EigenDABlobVerificationUtils._verifyRelayKeysSet: relay key is not set" + ); + } + } +} diff --git a/contracts/src/libraries/EigenDAHasher.sol b/contracts/src/libraries/EigenDAHasher.sol index 914e9721bf..ca517296bf 100644 --- a/contracts/src/libraries/EigenDAHasher.sol +++ b/contracts/src/libraries/EigenDAHasher.sol @@ -117,6 +117,17 @@ library EigenDAHasher { * @param blobHeader the V2 blob header to hash */ function hashBlobHeaderV2(BlobHeaderV2 memory blobHeader) internal pure returns(bytes32) { - return keccak256(abi.encode(blobHeader)); + return keccak256( + abi.encode( + keccak256( + abi.encode( + blobHeader.version, + blobHeader.quorumNumbers, + blobHeader.commitment + ) + ), + blobHeader.paymentHeaderHash + ) + ); } } \ No newline at end of file diff --git a/contracts/test/harnesses/EigenDABlobUtilsHarness.sol b/contracts/test/harnesses/EigenDABlobUtilsHarness.sol deleted file mode 100644 index c08914ab7d..0000000000 --- a/contracts/test/harnesses/EigenDABlobUtilsHarness.sol +++ /dev/null @@ -1,26 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED - -pragma solidity ^0.8.9; - -import "../../src/libraries/EigenDARollupUtils.sol"; -import "forge-std/Test.sol"; -import "../../src/interfaces/IEigenDAStructs.sol"; - -contract EigenDABlobUtilsHarness is Test { - - function verifyBlob( - BlobHeader calldata blobHeader, - IEigenDAServiceManager eigenDAServiceManager, - BlobVerificationProof calldata blobVerificationProof - ) external view { - EigenDARollupUtils.verifyBlob(blobHeader, eigenDAServiceManager, blobVerificationProof); - } - - function verifyBlobs( - BlobHeader[] calldata blobHeaders, - IEigenDAServiceManager eigenDAServiceManager, - BlobVerificationProof[] calldata blobVerificationProofs - ) external view { - EigenDARollupUtils.verifyBlobs(blobHeaders, eigenDAServiceManager, blobVerificationProofs); - } -} diff --git a/contracts/src/libraries/EigenDARollupUtils.sol b/contracts/test/rollup/EigenDARollupUtils.sol similarity index 98% rename from contracts/src/libraries/EigenDARollupUtils.sol rename to contracts/test/rollup/EigenDARollupUtils.sol index f659c77401..b2abecd008 100644 --- a/contracts/src/libraries/EigenDARollupUtils.sol +++ b/contracts/test/rollup/EigenDARollupUtils.sol @@ -4,10 +4,10 @@ pragma solidity ^0.8.9; import {Merkle} from "eigenlayer-core/contracts/libraries/Merkle.sol"; import {BN254} from "eigenlayer-middleware/libraries/BN254.sol"; -import {EigenDAHasher} from "./EigenDAHasher.sol"; -import {IEigenDAServiceManager} from "../interfaces/IEigenDAServiceManager.sol"; +import {EigenDAHasher} from "../../src/libraries/EigenDAHasher.sol"; +import {IEigenDAServiceManager} from "../../src/interfaces/IEigenDAServiceManager.sol"; import {BitmapUtils} from "eigenlayer-middleware/libraries/BitmapUtils.sol"; -import "../interfaces/IEigenDAStructs.sol"; +import "../../src/interfaces/IEigenDAStructs.sol"; /** * @title Library of functions to be used by smart contracts wanting to prove blobs on EigenDA and open KZG commitments. @@ -207,4 +207,4 @@ library EigenDARollupUtils { BN254.negGeneratorG2() ); } -} +} \ No newline at end of file diff --git a/contracts/src/rollup/MockRollup.sol b/contracts/test/rollup/MockRollup.sol similarity index 89% rename from contracts/src/rollup/MockRollup.sol rename to contracts/test/rollup/MockRollup.sol index fb8f24d586..ddd6d2fb68 100644 --- a/contracts/src/rollup/MockRollup.sol +++ b/contracts/test/rollup/MockRollup.sol @@ -1,11 +1,11 @@ -// SPDX-License-Identifier: UNLICENSED +// SPDX-License-Identifier: MIT pragma solidity ^0.8.9; -import {EigenDARollupUtils} from "../libraries/EigenDARollupUtils.sol"; -import {EigenDAServiceManager} from "../core/EigenDAServiceManager.sol"; -import {IEigenDAServiceManager} from "../interfaces/IEigenDAServiceManager.sol"; +import {EigenDARollupUtils} from "./EigenDARollupUtils.sol"; +import {EigenDAServiceManager} from "../../src/core/EigenDAServiceManager.sol"; +import {IEigenDAServiceManager} from "../../src/interfaces/IEigenDAServiceManager.sol"; import {BN254} from "eigenlayer-middleware/libraries/BN254.sol"; -import "../interfaces/IEigenDAStructs.sol"; +import "../../src/interfaces/IEigenDAStructs.sol"; struct Commitment { address confirmer; // confirmer who posted the commitment diff --git a/contracts/test/unit/MockRollup.t.sol b/contracts/test/rollup/MockRollup.t.sol similarity index 64% rename from contracts/test/unit/MockRollup.t.sol rename to contracts/test/rollup/MockRollup.t.sol index 0150c56a88..bb676e1a1b 100644 --- a/contracts/test/unit/MockRollup.t.sol +++ b/contracts/test/rollup/MockRollup.t.sol @@ -1,19 +1,25 @@ -// SPDX-License-Identifier: UNLICENSED +// SPDX-License-Identifier: MIT pragma solidity ^0.8.9; import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; import {BLSMockAVSDeployer} from "../../lib/eigenlayer-middleware/test/utils/BLSMockAVSDeployer.sol"; -import {MockRollup} from "../../src/rollup/MockRollup.sol"; +import {MockRollup} from "./MockRollup.sol"; import {EigenDAHasher} from "../../src/libraries/EigenDAHasher.sol"; import {EigenDAServiceManager, IRewardsCoordinator} from "../../src/core/EigenDAServiceManager.sol"; import {IEigenDAServiceManager} from "../../src/interfaces/IEigenDAServiceManager.sol"; -import {EigenDARollupUtils} from "../../src/libraries/EigenDARollupUtils.sol"; +import {EigenDABlobVerificationUtils} from "../../src/libraries/EigenDABlobVerificationUtils.sol"; import {BN254} from "eigenlayer-middleware/libraries/BN254.sol"; -import "../../src/interfaces/IEigenDAStructs.sol"; -import {IEigenDAThresholdRegistry} from "../../src/interfaces/IEigenDAThresholdRegistry.sol"; +import {EigenDABlobVerifier} from "../../src/core/EigenDABlobVerifier.sol"; +import {EigenDAThresholdRegistry, IEigenDAThresholdRegistry} from "../../src/core/EigenDAThresholdRegistry.sol"; +import {IEigenDABatchMetadataStorage} from "../../src/interfaces/IEigenDABatchMetadataStorage.sol"; +import {IEigenDASignatureVerifier} from "../../src/interfaces/IEigenDASignatureVerifier.sol"; +import {OperatorStateRetriever} from "../../lib/eigenlayer-middleware/src/OperatorStateRetriever.sol"; import {IEigenDARelayRegistry} from "../../src/interfaces/IEigenDARelayRegistry.sol"; +import {EigenDARelayRegistry} from "../../src/core/EigenDARelayRegistry.sol"; +import {IRegistryCoordinator} from "../../lib/eigenlayer-middleware/src/interfaces/IRegistryCoordinator.sol"; +import "../../src/interfaces/IEigenDAStructs.sol"; import "forge-std/StdStorage.sol"; contract MockRollupTest is BLSMockAVSDeployer { @@ -26,6 +32,16 @@ contract MockRollupTest is BLSMockAVSDeployer { EigenDAServiceManager eigenDAServiceManager; EigenDAServiceManager eigenDAServiceManagerImplementation; + EigenDABlobVerifier eigenDABlobVerifier; + EigenDARelayRegistry eigenDARelayRegistry; + EigenDARelayRegistry eigenDARelayRegistryImplementation; + + EigenDAThresholdRegistry eigenDAThresholdRegistry; + EigenDAThresholdRegistry eigenDAThresholdRegistryImplementation; + bytes quorumAdversaryThresholdPercentages = hex"212121"; + bytes quorumConfirmationThresholdPercentages = hex"373737"; + bytes quorumNumbersRequired = hex"0001"; + SecurityThresholds defaultSecurityThresholds = SecurityThresholds(33, 55); uint8 defaultCodingRatioPercentage = 10; uint32 defaultReferenceBlockNumber = 100; @@ -54,44 +70,94 @@ contract MockRollupTest is BLSMockAVSDeployer { function setUp() public { _setUpBLSMockAVSDeployer(); + eigenDAServiceManager = EigenDAServiceManager( + address( + new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), "") + ) + ); + + eigenDARelayRegistry = EigenDARelayRegistry( + address( + new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), "") + ) + ); + + eigenDAThresholdRegistry = EigenDAThresholdRegistry( + address( + new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), "") + ) + ); + eigenDAServiceManagerImplementation = new EigenDAServiceManager( avsDirectory, rewardsCoordinator, registryCoordinator, stakeRegistry, - IEigenDAThresholdRegistry(address(0)), - IEigenDARelayRegistry(address(0)) + eigenDAThresholdRegistry, + eigenDARelayRegistry + ); + + eigenDAThresholdRegistryImplementation = new EigenDAThresholdRegistry(); + + VersionedBlobParams[] memory versionedBlobParams = new VersionedBlobParams[](0); + + cheats.prank(proxyAdminOwner); + proxyAdmin.upgradeAndCall( + TransparentUpgradeableProxy(payable(address(eigenDAThresholdRegistry))), + address(eigenDAThresholdRegistryImplementation), + abi.encodeWithSelector( + EigenDAThresholdRegistry.initialize.selector, + registryCoordinatorOwner, + quorumAdversaryThresholdPercentages, + quorumConfirmationThresholdPercentages, + quorumNumbersRequired, + versionedBlobParams, + defaultSecurityThresholds + ) ); address[] memory confirmers = new address[](1); confirmers[0] = registryCoordinatorOwner; - // Third, upgrade the proxy contracts to use the correct implementation contracts and initialize them. - eigenDAServiceManager = EigenDAServiceManager( - address( - new TransparentUpgradeableProxy( - address(eigenDAServiceManagerImplementation), - address(proxyAdmin), - abi.encodeWithSelector( - EigenDAServiceManager.initialize.selector, - pauserRegistry, - 0, - registryCoordinatorOwner, - confirmers, - registryCoordinatorOwner - ) - ) + cheats.prank(proxyAdminOwner); + proxyAdmin.upgradeAndCall( + TransparentUpgradeableProxy(payable(address(eigenDAServiceManager))), + address(eigenDAServiceManagerImplementation), + abi.encodeWithSelector( + EigenDAServiceManager.initialize.selector, + pauserRegistry, + 0, + registryCoordinatorOwner, + confirmers, + registryCoordinatorOwner ) ); - mockRollup = new MockRollup(eigenDAServiceManager, s1); + eigenDABlobVerifier = new EigenDABlobVerifier( + IEigenDAThresholdRegistry(address(eigenDAThresholdRegistry)), + IEigenDABatchMetadataStorage(address(eigenDAServiceManager)), + IEigenDASignatureVerifier(address(eigenDAServiceManager)), + IEigenDARelayRegistry(address(eigenDARelayRegistry)), + OperatorStateRetriever(address(operatorStateRetriever)), + IRegistryCoordinator(address(registryCoordinator)) + ); + + eigenDARelayRegistryImplementation = new EigenDARelayRegistry(); + + cheats.prank(proxyAdminOwner); + proxyAdmin.upgradeAndCall( + TransparentUpgradeableProxy(payable(address(eigenDARelayRegistry))), + address(eigenDARelayRegistryImplementation), + abi.encodeWithSelector(EigenDARelayRegistry.initialize.selector, registryCoordinatorOwner) + ); + + mockRollup = new MockRollup(IEigenDAServiceManager(address(eigenDAServiceManager)), s1); //hardcode g2 proof illegalProof.X[1] = 11151623676041303181597631684634074376466382703418354161831688442589830350329; illegalProof.X[0] = 21587740443732524623985464356760343072434825248946003815467233999912459579351; illegalProof.Y[1] = 4222041728992406478862708226745479381252734858741080790666424175645694456140; illegalProof.Y[0] = 17511259870083276759899704237100059449000397154439723516103658719937845846446; - } function testChallenge(uint256 pseudoRandomNumber) public { @@ -125,7 +191,7 @@ contract MockRollupTest is BLSMockAVSDeployer { // add dummy quorum numbers and quorum threshold percentages making sure confirmationThresholdPercentage = adversaryThresholdPercentage + defaultCodingRatioPercentage for (uint i = 0; i < blobHeader[1].quorumBlobParams.length; i++) { batchHeader.quorumNumbers = abi.encodePacked(batchHeader.quorumNumbers, blobHeader[1].quorumBlobParams[i].quorumNumber); - batchHeader.signedStakeForQuorums = abi.encodePacked(batchHeader.signedStakeForQuorums, blobHeader[1].quorumBlobParams[i].adversaryThresholdPercentage + defaultCodingRatioPercentage); + batchHeader.signedStakeForQuorums = abi.encodePacked(batchHeader.signedStakeForQuorums, blobHeader[1].quorumBlobParams[i].confirmationThresholdPercentage); } batchHeader.referenceBlockNumber = uint32(block.number); @@ -177,9 +243,9 @@ contract MockRollupTest is BLSMockAVSDeployer { } quorumNumbersUsed[blobHeader.quorumBlobParams[i].quorumNumber] = true; } - blobHeader.quorumBlobParams[i].adversaryThresholdPercentage = EigenDARollupUtils.getQuorumAdversaryThreshold(eigenDAServiceManager, blobHeader.quorumBlobParams[i].quorumNumber); + blobHeader.quorumBlobParams[i].adversaryThresholdPercentage = eigenDABlobVerifier.getQuorumAdversaryThresholdPercentage(blobHeader.quorumBlobParams[i].quorumNumber); blobHeader.quorumBlobParams[i].chunkLength = uint32(uint256(keccak256(abi.encodePacked(pseudoRandomNumber, "blobHeader.quorumBlobParams[i].chunkLength", i)))); - blobHeader.quorumBlobParams[i].confirmationThresholdPercentage = blobHeader.quorumBlobParams[i].adversaryThresholdPercentage + 1; + blobHeader.quorumBlobParams[i].confirmationThresholdPercentage = eigenDABlobVerifier.getQuorumConfirmationThresholdPercentage(blobHeader.quorumBlobParams[i].quorumNumber); } // mark all quorum numbers as unused for (uint i = 0; i < numQuorumsBlobParams; i++) { @@ -188,10 +254,4 @@ contract MockRollupTest is BLSMockAVSDeployer { return blobHeader; } - - function testGetQuorumAdversaryThreshold () public { - require(EigenDARollupUtils.getQuorumAdversaryThreshold(eigenDAServiceManager, 0) == 33, "getQuorumAdversaryThreshold failed"); - //require(EigenDARollupUtils.getQuorumAdversaryThreshold(eigenDAServiceManager, 1) == 33, "getQuorumAdversaryThreshold failed"); - } - } \ No newline at end of file diff --git a/contracts/test/unit/EigenDABlobUtils.t.sol b/contracts/test/unit/EigenDABlobUtils.t.sol index 292a2ac347..3eca831bc4 100644 --- a/contracts/test/unit/EigenDABlobUtils.t.sol +++ b/contracts/test/unit/EigenDABlobUtils.t.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: BUSL-1.1 +// SPDX-License-Identifier: MIT pragma solidity =0.8.12; import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; @@ -6,13 +6,17 @@ import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.so import "../../lib/eigenlayer-middleware/test/utils/BLSMockAVSDeployer.sol"; import {EigenDAHasher} from "../../src/libraries/EigenDAHasher.sol"; import {EigenDAServiceManager, IRewardsCoordinator} from "../../src/core/EigenDAServiceManager.sol"; -import {EigenDARollupUtils} from "../../src/libraries/EigenDARollupUtils.sol"; +import {EigenDABlobVerificationUtils} from "../../src/libraries/EigenDABlobVerificationUtils.sol"; import {EigenDAHasher} from "../../src/libraries/EigenDAHasher.sol"; -import {EigenDABlobUtilsHarness} from "../harnesses/EigenDABlobUtilsHarness.sol"; import {EigenDAServiceManager} from "../../src/core/EigenDAServiceManager.sol"; import {IEigenDAServiceManager} from "../../src/interfaces/IEigenDAServiceManager.sol"; -import {IEigenDAThresholdRegistry} from "../../src/interfaces/IEigenDAThresholdRegistry.sol"; +import {EigenDABlobVerifier} from "../../src/core/EigenDABlobVerifier.sol"; +import {EigenDAThresholdRegistry, IEigenDAThresholdRegistry} from "../../src/core/EigenDAThresholdRegistry.sol"; +import {IEigenDABatchMetadataStorage} from "../../src/interfaces/IEigenDABatchMetadataStorage.sol"; +import {IEigenDASignatureVerifier} from "../../src/interfaces/IEigenDASignatureVerifier.sol"; +import {IRegistryCoordinator} from "../../lib/eigenlayer-middleware/src/interfaces/IRegistryCoordinator.sol"; import {IEigenDARelayRegistry} from "../../src/interfaces/IEigenDARelayRegistry.sol"; +import {EigenDARelayRegistry} from "../../src/core/EigenDARelayRegistry.sol"; import "../../src/interfaces/IEigenDAStructs.sol"; import "forge-std/StdStorage.sol"; @@ -29,12 +33,18 @@ contract EigenDABlobUtilsUnit is BLSMockAVSDeployer { address notConfirmer = address(uint160(uint256(keccak256(abi.encodePacked("notConfirmer"))))); address rewardsInitiator = address(uint160(uint256(keccak256(abi.encodePacked("rewardsInitiator"))))); - EigenDABlobUtilsHarness eigenDABlobUtilsHarness; - EigenDAServiceManager eigenDAServiceManager; EigenDAServiceManager eigenDAServiceManagerImplementation; + EigenDABlobVerifier eigenDABlobVerifier; + EigenDARelayRegistry eigenDARelayRegistry; + EigenDARelayRegistry eigenDARelayRegistryImplementation; + EigenDAThresholdRegistry eigenDAThresholdRegistry; + EigenDAThresholdRegistry eigenDAThresholdRegistryImplementation; + bytes quorumAdversaryThresholdPercentages = hex"212121"; + bytes quorumConfirmationThresholdPercentages = hex"373737"; + bytes quorumNumbersRequired = hex"0001"; + SecurityThresholds defaultSecurityThresholds = SecurityThresholds(33, 55); - uint8 defaultCodingRatioPercentage = 10; uint32 defaultReferenceBlockNumber = 100; uint32 defaultConfirmationBlockNumber = 1000; uint32 defaultBatchId = 0; @@ -44,37 +54,86 @@ contract EigenDABlobUtilsUnit is BLSMockAVSDeployer { function setUp() virtual public { _setUpBLSMockAVSDeployer(); + eigenDAServiceManager = EigenDAServiceManager( + address( + new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), "") + ) + ); + + eigenDAThresholdRegistry = EigenDAThresholdRegistry( + address( + new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), "") + ) + ); + + eigenDARelayRegistry = EigenDARelayRegistry( + address( + new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), "") + ) + ); + eigenDAServiceManagerImplementation = new EigenDAServiceManager( avsDirectory, rewardsCoordinator, registryCoordinator, stakeRegistry, - IEigenDAThresholdRegistry(address(0)), - IEigenDARelayRegistry(address(0)) + eigenDAThresholdRegistry, + eigenDARelayRegistry ); + eigenDAThresholdRegistryImplementation = new EigenDAThresholdRegistry(); + address[] memory confirmers = new address[](1); - confirmers[0] = confirmer; + confirmers[0] = registryCoordinatorOwner; + + cheats.prank(proxyAdminOwner); + proxyAdmin.upgradeAndCall( + TransparentUpgradeableProxy(payable(address(eigenDAServiceManager))), + address(eigenDAServiceManagerImplementation), + abi.encodeWithSelector( + EigenDAServiceManager.initialize.selector, + pauserRegistry, + 0, + registryCoordinatorOwner, + confirmers, + registryCoordinatorOwner + ) + ); - // Third, upgrade the proxy contracts to use the correct implementation contracts and initialize them. - eigenDAServiceManager = EigenDAServiceManager( - address( - new TransparentUpgradeableProxy( - address(eigenDAServiceManagerImplementation), - address(proxyAdmin), - abi.encodeWithSelector( - EigenDAServiceManager.initialize.selector, - pauserRegistry, - 0, - registryCoordinatorOwner, - confirmers, - rewardsInitiator - ) - ) + VersionedBlobParams[] memory versionedBlobParams = new VersionedBlobParams[](0); + + cheats.prank(proxyAdminOwner); + proxyAdmin.upgradeAndCall( + TransparentUpgradeableProxy(payable(address(eigenDAThresholdRegistry))), + address(eigenDAThresholdRegistryImplementation), + abi.encodeWithSelector( + EigenDAThresholdRegistry.initialize.selector, + registryCoordinatorOwner, + quorumAdversaryThresholdPercentages, + quorumConfirmationThresholdPercentages, + quorumNumbersRequired, + versionedBlobParams, + defaultSecurityThresholds ) ); - eigenDABlobUtilsHarness = new EigenDABlobUtilsHarness(); + eigenDABlobVerifier = new EigenDABlobVerifier( + IEigenDAThresholdRegistry(address(eigenDAThresholdRegistry)), + IEigenDABatchMetadataStorage(address(eigenDAServiceManager)), + IEigenDASignatureVerifier(address(eigenDAServiceManager)), + IEigenDARelayRegistry(address(eigenDARelayRegistry)), + OperatorStateRetriever(address(operatorStateRetriever)), + IRegistryCoordinator(address(registryCoordinator)) + ); + + eigenDARelayRegistryImplementation = new EigenDARelayRegistry(); + + cheats.prank(proxyAdminOwner); + proxyAdmin.upgradeAndCall( + TransparentUpgradeableProxy(payable(address(eigenDARelayRegistry))), + address(eigenDARelayRegistryImplementation), + abi.encodeWithSelector(EigenDARelayRegistry.initialize.selector, registryCoordinatorOwner) + ); } function testVerifyBlob_TwoQuorums(uint256 pseudoRandomNumber) public { @@ -88,10 +147,9 @@ contract EigenDABlobUtilsUnit is BLSMockAVSDeployer { bytes memory firstBlobHash = abi.encodePacked(blobHeader[0].hashBlobHeader()); bytes memory secondBlobHash = abi.encodePacked(blobHeader[1].hashBlobHeader()); batchHeader.blobHeadersRoot = keccak256(abi.encodePacked(keccak256(firstBlobHash), keccak256(secondBlobHash))); - // add dummy quorum numbers and quorum threshold percentages making sure confirmationThresholdPercentage = adversaryThresholdPercentage + defaultCodingRatioPercentage for (uint i = 0; i < blobHeader[1].quorumBlobParams.length; i++) { batchHeader.quorumNumbers = abi.encodePacked(batchHeader.quorumNumbers, blobHeader[1].quorumBlobParams[i].quorumNumber); - batchHeader.signedStakeForQuorums = abi.encodePacked(batchHeader.signedStakeForQuorums, blobHeader[1].quorumBlobParams[i].adversaryThresholdPercentage + defaultCodingRatioPercentage); + batchHeader.signedStakeForQuorums = abi.encodePacked(batchHeader.signedStakeForQuorums, blobHeader[1].quorumBlobParams[i].confirmationThresholdPercentage); } batchHeader.referenceBlockNumber = uint32(block.number); @@ -118,7 +176,7 @@ contract EigenDABlobUtilsUnit is BLSMockAVSDeployer { } uint256 gasBefore = gasleft(); - eigenDABlobUtilsHarness.verifyBlob(blobHeader[1], eigenDAServiceManager, blobVerificationProof); + eigenDABlobVerifier.verifyBlobV1(blobHeader[1], blobVerificationProof); uint256 gasAfter = gasleft(); emit log_named_uint("gas used", gasBefore - gasAfter); } @@ -129,7 +187,6 @@ contract EigenDABlobUtilsUnit is BLSMockAVSDeployer { blobHeader[0] = _generateRandomBlobHeader(pseudoRandomNumber, numQuorumBlobParams); uint256 anotherPseudoRandomNumber = uint256(keccak256(abi.encodePacked(pseudoRandomNumber))); blobHeader[1] = _generateRandomBlobHeader(anotherPseudoRandomNumber, numQuorumBlobParams); - BatchHeader memory batchHeader; bytes memory firstBlobHash = abi.encodePacked(blobHeader[0].hashBlobHeader()); bytes memory secondBlobHash = abi.encodePacked(blobHeader[1].hashBlobHeader()); @@ -137,23 +194,19 @@ contract EigenDABlobUtilsUnit is BLSMockAVSDeployer { // add dummy quorum numbers and quorum threshold percentages making sure confirmationThresholdPercentage = adversaryThresholdPercentage + defaultCodingRatioPercentage for (uint i = 0; i < blobHeader[1].quorumBlobParams.length; i++) { batchHeader.quorumNumbers = abi.encodePacked(batchHeader.quorumNumbers, blobHeader[1].quorumBlobParams[i].quorumNumber); - batchHeader.signedStakeForQuorums = abi.encodePacked(batchHeader.signedStakeForQuorums, blobHeader[1].quorumBlobParams[i].adversaryThresholdPercentage + defaultCodingRatioPercentage); - } + batchHeader.signedStakeForQuorums = abi.encodePacked(batchHeader.signedStakeForQuorums, blobHeader[1].quorumBlobParams[i].confirmationThresholdPercentage); } batchHeader.referenceBlockNumber = uint32(block.number); - // add dummy batch metadata BatchMetadata memory batchMetadata; batchMetadata.batchHeader = batchHeader; batchMetadata.signatoryRecordHash = keccak256(abi.encodePacked("signatoryRecordHash")); batchMetadata.confirmationBlockNumber = defaultConfirmationBlockNumber; - stdstore .target(address(eigenDAServiceManager)) .sig("batchIdToBatchMetadataHash(uint32)") .with_key(defaultBatchId) .checked_write(batchMetadata.hashBatchMetadata()); - - BlobVerificationProof[] memory blobVerificationProofs = new BlobVerificationProof[](2); + BlobVerificationProof[] memory blobVerificationProofs = new BlobVerificationProof[](2); blobVerificationProofs[0].batchId = defaultBatchId; blobVerificationProofs[1].batchId = defaultBatchId; blobVerificationProofs[0].batchMetadata = batchMetadata; @@ -168,9 +221,8 @@ contract EigenDABlobUtilsUnit is BLSMockAVSDeployer { blobVerificationProofs[0].quorumIndices[i] = bytes1(uint8(i)); blobVerificationProofs[1].quorumIndices[i] = bytes1(uint8(i)); } - uint256 gasBefore = gasleft(); - eigenDABlobUtilsHarness.verifyBlobs(blobHeader, eigenDAServiceManager, blobVerificationProofs); + eigenDABlobVerifier.verifyBlobsV1(blobHeader, blobVerificationProofs); uint256 gasAfter = gasleft(); emit log_named_uint("gas used", gasBefore - gasAfter); } @@ -185,8 +237,8 @@ contract EigenDABlobUtilsUnit is BLSMockAVSDeployer { BlobVerificationProof memory blobVerificationProof; blobVerificationProof.batchId = defaultBatchId; - cheats.expectRevert("EigenDARollupUtils.verifyBlob: batchMetadata does not match stored metadata"); - eigenDABlobUtilsHarness.verifyBlob(blobHeader[1], eigenDAServiceManager, blobVerificationProof); + cheats.expectRevert("EigenDABlobVerificationUtils._verifyBlobForQuorums: batchMetadata does not match stored metadata"); + eigenDABlobVerifier.verifyBlobV1(blobHeader[1], blobVerificationProof); } function testVerifyBlob_InvalidMerkleProof(uint256 pseudoRandomNumber) public { @@ -211,54 +263,8 @@ contract EigenDABlobUtilsUnit is BLSMockAVSDeployer { blobVerificationProof.inclusionProof = abi.encodePacked(bytes32(0)); blobVerificationProof.blobIndex = 1; - cheats.expectRevert("EigenDARollupUtils.verifyBlob: inclusion proof is invalid"); - eigenDABlobUtilsHarness.verifyBlob(blobHeader[1], eigenDAServiceManager, blobVerificationProof); - } - - function testVerifyBlob_RandomNumberOfQuorums(uint256 pseudoRandomNumber) public { - uint256 numQuorumBlobParams = 2 + (pseudoRandomNumber % 192); - BlobHeader[] memory blobHeader = new BlobHeader[](2); - blobHeader[0] = _generateRandomBlobHeader(pseudoRandomNumber, numQuorumBlobParams); - uint256 anotherPseudoRandomNumber = uint256(keccak256(abi.encodePacked(pseudoRandomNumber))); - blobHeader[1] = _generateRandomBlobHeader(anotherPseudoRandomNumber, numQuorumBlobParams); - - BatchHeader memory batchHeader; - bytes memory firstBlobHash = abi.encodePacked(blobHeader[0].hashBlobHeader()); - bytes memory secondBlobHash = abi.encodePacked(blobHeader[1].hashBlobHeader()); - batchHeader.blobHeadersRoot = keccak256(abi.encodePacked(keccak256(firstBlobHash), keccak256(secondBlobHash))); - // add dummy quorum numbers and quorum threshold percentages making sure confirmationThresholdPercentage = adversaryThresholdPercentage + defaultCodingRatioPercentage - for (uint i = 0; i < blobHeader[1].quorumBlobParams.length; i++) { - batchHeader.quorumNumbers = abi.encodePacked(batchHeader.quorumNumbers, blobHeader[1].quorumBlobParams[i].quorumNumber); - batchHeader.signedStakeForQuorums = abi.encodePacked(batchHeader.signedStakeForQuorums, blobHeader[1].quorumBlobParams[i].adversaryThresholdPercentage + defaultCodingRatioPercentage); - } - batchHeader.referenceBlockNumber = uint32(block.number); - - // add dummy batch metadata - BatchMetadata memory batchMetadata; - batchMetadata.batchHeader = batchHeader; - batchMetadata.signatoryRecordHash = keccak256(abi.encodePacked("signatoryRecordHash")); - batchMetadata.confirmationBlockNumber = defaultConfirmationBlockNumber; - - stdstore - .target(address(eigenDAServiceManager)) - .sig("batchIdToBatchMetadataHash(uint32)") - .with_key(defaultBatchId) - .checked_write(batchMetadata.hashBatchMetadata()); - - BlobVerificationProof memory blobVerificationProof; - blobVerificationProof.batchId = defaultBatchId; - blobVerificationProof.batchMetadata = batchMetadata; - blobVerificationProof.inclusionProof = abi.encodePacked(keccak256(firstBlobHash)); - blobVerificationProof.blobIndex = 1; - blobVerificationProof.quorumIndices = new bytes(batchHeader.quorumNumbers.length); - for (uint i = 0; i < batchHeader.quorumNumbers.length; i++) { - blobVerificationProof.quorumIndices[i] = bytes1(uint8(i)); - } - - uint256 gasBefore = gasleft(); - eigenDABlobUtilsHarness.verifyBlob(blobHeader[1], eigenDAServiceManager, blobVerificationProof); - uint256 gasAfter = gasleft(); - emit log_named_uint("gas used", gasBefore - gasAfter); + cheats.expectRevert("EigenDABlobVerificationUtils._verifyBlobForQuorums: inclusion proof is invalid"); + eigenDABlobVerifier.verifyBlobV1(blobHeader[1], blobVerificationProof); } function testVerifyBlob_RequiredQuorumsNotMet(uint256 pseudoRandomNumber) public { @@ -272,10 +278,9 @@ contract EigenDABlobUtilsUnit is BLSMockAVSDeployer { bytes memory firstBlobHash = abi.encodePacked(blobHeader[0].hashBlobHeader()); bytes memory secondBlobHash = abi.encodePacked(blobHeader[1].hashBlobHeader()); batchHeader.blobHeadersRoot = keccak256(abi.encodePacked(keccak256(firstBlobHash), keccak256(secondBlobHash))); - // add dummy quorum numbers and quorum threshold percentages making sure confirmationThresholdPercentage = adversaryThresholdPercentage + defaultCodingRatioPercentage for (uint i = 0; i < blobHeader[1].quorumBlobParams.length; i++) { batchHeader.quorumNumbers = abi.encodePacked(batchHeader.quorumNumbers, blobHeader[1].quorumBlobParams[i].quorumNumber); - batchHeader.signedStakeForQuorums = abi.encodePacked(batchHeader.signedStakeForQuorums, blobHeader[1].quorumBlobParams[i].adversaryThresholdPercentage + defaultCodingRatioPercentage); + batchHeader.signedStakeForQuorums = abi.encodePacked(batchHeader.signedStakeForQuorums, blobHeader[1].quorumBlobParams[i].confirmationThresholdPercentage); } batchHeader.referenceBlockNumber = uint32(block.number); @@ -301,57 +306,8 @@ contract EigenDABlobUtilsUnit is BLSMockAVSDeployer { blobVerificationProof.quorumIndices[i] = bytes1(uint8(i)); } - cheats.expectRevert("EigenDARollupUtils.verifyBlob: required quorums are not a subset of the confirmed quorums"); - eigenDABlobUtilsHarness.verifyBlob(blobHeader[1], eigenDAServiceManager, blobVerificationProof); - } - - function testVerifyBlob_AdversayThresholdNotMet(uint256 pseudoRandomNumber) public { - uint256 numQuorumBlobParams = 2; - BlobHeader[] memory blobHeader = new BlobHeader[](2); - blobHeader[0] = _generateRandomBlobHeader(pseudoRandomNumber, numQuorumBlobParams); - uint256 anotherPseudoRandomNumber = uint256(keccak256(abi.encodePacked(pseudoRandomNumber))); - blobHeader[1] = _generateRandomBlobHeader(anotherPseudoRandomNumber, numQuorumBlobParams); - - for (uint i = 0; i < numQuorumBlobParams; i++) { - blobHeader[0].quorumBlobParams[i].adversaryThresholdPercentage = EigenDARollupUtils.getQuorumAdversaryThreshold(eigenDAServiceManager, blobHeader[0].quorumBlobParams[i].quorumNumber) - 1; - blobHeader[1].quorumBlobParams[i].adversaryThresholdPercentage = EigenDARollupUtils.getQuorumAdversaryThreshold(eigenDAServiceManager, blobHeader[1].quorumBlobParams[i].quorumNumber) - 1; - } - - BatchHeader memory batchHeader; - bytes memory firstBlobHash = abi.encodePacked(blobHeader[0].hashBlobHeader()); - bytes memory secondBlobHash = abi.encodePacked(blobHeader[1].hashBlobHeader()); - batchHeader.blobHeadersRoot = keccak256(abi.encodePacked(keccak256(firstBlobHash), keccak256(secondBlobHash))); - // add dummy quorum numbers and quorum threshold percentages making sure confirmationThresholdPercentage = adversaryThresholdPercentage + defaultCodingRatioPercentage - for (uint i = 0; i < blobHeader[1].quorumBlobParams.length; i++) { - batchHeader.quorumNumbers = abi.encodePacked(batchHeader.quorumNumbers, blobHeader[1].quorumBlobParams[i].quorumNumber); - batchHeader.signedStakeForQuorums = abi.encodePacked(batchHeader.signedStakeForQuorums, blobHeader[1].quorumBlobParams[i].adversaryThresholdPercentage + defaultCodingRatioPercentage); - } - batchHeader.referenceBlockNumber = uint32(block.number); - - // add dummy batch metadata - BatchMetadata memory batchMetadata; - batchMetadata.batchHeader = batchHeader; - batchMetadata.signatoryRecordHash = keccak256(abi.encodePacked("signatoryRecordHash")); - batchMetadata.confirmationBlockNumber = defaultConfirmationBlockNumber; - - stdstore - .target(address(eigenDAServiceManager)) - .sig("batchIdToBatchMetadataHash(uint32)") - .with_key(defaultBatchId) - .checked_write(batchMetadata.hashBatchMetadata()); - - BlobVerificationProof memory blobVerificationProof; - blobVerificationProof.batchId = defaultBatchId; - blobVerificationProof.batchMetadata = batchMetadata; - blobVerificationProof.inclusionProof = abi.encodePacked(keccak256(firstBlobHash)); - blobVerificationProof.blobIndex = 1; - blobVerificationProof.quorumIndices = new bytes(batchHeader.quorumNumbers.length); - for (uint i = 0; i < batchHeader.quorumNumbers.length; i++) { - blobVerificationProof.quorumIndices[i] = bytes1(uint8(i)); - } - - cheats.expectRevert("EigenDARollupUtils.verifyBlob: adversaryThresholdPercentage is not met"); - eigenDABlobUtilsHarness.verifyBlob(blobHeader[1], eigenDAServiceManager, blobVerificationProof); + cheats.expectRevert("EigenDABlobVerificationUtils._verifyBlobForQuorums: required quorums are not a subset of the confirmed quorums"); + eigenDABlobVerifier.verifyBlobV1(blobHeader[1], blobVerificationProof); } function testVerifyBlob_QuorumNumberMismatch(uint256 pseudoRandomNumber) public { @@ -365,10 +321,9 @@ contract EigenDABlobUtilsUnit is BLSMockAVSDeployer { bytes memory firstBlobHash = abi.encodePacked(blobHeader[0].hashBlobHeader()); bytes memory secondBlobHash = abi.encodePacked(blobHeader[1].hashBlobHeader()); batchHeader.blobHeadersRoot = keccak256(abi.encodePacked(keccak256(firstBlobHash), keccak256(secondBlobHash))); - // add dummy quorum numbers and quorum threshold percentages making sure confirmationThresholdPercentage = adversaryThresholdPercentage + defaultCodingRatioPercentage for (uint i = 0; i < blobHeader[1].quorumBlobParams.length; i++) { batchHeader.quorumNumbers = abi.encodePacked(batchHeader.quorumNumbers, blobHeader[1].quorumBlobParams[i].quorumNumber); - batchHeader.signedStakeForQuorums = abi.encodePacked(batchHeader.signedStakeForQuorums, blobHeader[1].quorumBlobParams[i].adversaryThresholdPercentage + defaultCodingRatioPercentage); + batchHeader.signedStakeForQuorums = abi.encodePacked(batchHeader.signedStakeForQuorums, blobHeader[1].quorumBlobParams[i].confirmationThresholdPercentage); } batchHeader.referenceBlockNumber = uint32(block.number); @@ -395,8 +350,8 @@ contract EigenDABlobUtilsUnit is BLSMockAVSDeployer { blobVerificationProof.quorumIndices[i] = bytes1(uint8(batchHeader.quorumNumbers.length - 1 - i)); } - cheats.expectRevert("EigenDARollupUtils.verifyBlob: quorumNumber does not match"); - eigenDABlobUtilsHarness.verifyBlob(blobHeader[1], eigenDAServiceManager, blobVerificationProof); + cheats.expectRevert("EigenDABlobVerificationUtils._verifyBlobForQuorums: quorumNumber does not match"); + eigenDABlobVerifier.verifyBlobV1(blobHeader[1], blobVerificationProof); } function testVerifyBlob_QuorumThresholdNotMet(uint256 pseudoRandomNumber) public { @@ -440,8 +395,20 @@ contract EigenDABlobUtilsUnit is BLSMockAVSDeployer { blobVerificationProof.quorumIndices[i] = bytes1(uint8(i)); } - cheats.expectRevert("EigenDARollupUtils.verifyBlob: confirmationThresholdPercentage is not met"); - eigenDABlobUtilsHarness.verifyBlob(blobHeader[1], eigenDAServiceManager, blobVerificationProof); + cheats.expectRevert("EigenDABlobVerificationUtils._verifyBlobForQuorums: confirmationThresholdPercentage is not met"); + eigenDABlobVerifier.verifyBlobV1(blobHeader[1], blobVerificationProof); + } + + function testThresholds() public { + require(eigenDABlobVerifier.getQuorumAdversaryThresholdPercentage(0) == 33, "getQuorumAdversaryThresholdPercentage failed"); + require(eigenDABlobVerifier.getQuorumAdversaryThresholdPercentage(1) == 33, "getQuorumAdversaryThresholdPercentage failed"); + require(eigenDABlobVerifier.getQuorumAdversaryThresholdPercentage(2) == 33, "getQuorumAdversaryThresholdPercentage failed"); + require(eigenDABlobVerifier.getQuorumConfirmationThresholdPercentage(0) == 55, "getQuorumConfirmationThresholdPercentage failed"); + require(eigenDABlobVerifier.getQuorumConfirmationThresholdPercentage(1) == 55, "getQuorumConfirmationThresholdPercentage failed"); + require(eigenDABlobVerifier.getQuorumConfirmationThresholdPercentage(2) == 55, "getQuorumConfirmationThresholdPercentage failed"); + require(eigenDABlobVerifier.getIsQuorumRequired(0) == true, "getIsQuorumRequired failed"); + require(eigenDABlobVerifier.getIsQuorumRequired(1) == true, "getIsQuorumRequired failed"); + require(eigenDABlobVerifier.getIsQuorumRequired(2) == false, "getIsQuorumRequired failed"); } // generates a random blob header with the given coding ratio percentage as the ratio of original data to encoded data @@ -471,9 +438,9 @@ contract EigenDABlobUtilsUnit is BLSMockAVSDeployer { quorumNumbersUsed[blobHeader.quorumBlobParams[i].quorumNumber] = true; } - blobHeader.quorumBlobParams[i].adversaryThresholdPercentage = EigenDARollupUtils.getQuorumAdversaryThreshold(eigenDAServiceManager, blobHeader.quorumBlobParams[i].quorumNumber); + blobHeader.quorumBlobParams[i].adversaryThresholdPercentage = eigenDABlobVerifier.getQuorumAdversaryThresholdPercentage(blobHeader.quorumBlobParams[i].quorumNumber); blobHeader.quorumBlobParams[i].chunkLength = uint32(uint256(keccak256(abi.encodePacked(pseudoRandomNumber, "blobHeader.quorumBlobParams[i].chunkLength", i)))); - blobHeader.quorumBlobParams[i].confirmationThresholdPercentage = blobHeader.quorumBlobParams[i].adversaryThresholdPercentage + 1; + blobHeader.quorumBlobParams[i].confirmationThresholdPercentage = eigenDABlobVerifier.getQuorumConfirmationThresholdPercentage(blobHeader.quorumBlobParams[i].quorumNumber); } // mark all quorum numbers as unused for (uint i = 0; i < numQuorumsBlobParams; i++) { diff --git a/contracts/test/unit/EigenDAServiceManagerUnit.t.sol b/contracts/test/unit/EigenDAServiceManagerUnit.t.sol index cf94677511..669edf49f6 100644 --- a/contracts/test/unit/EigenDAServiceManagerUnit.t.sol +++ b/contracts/test/unit/EigenDAServiceManagerUnit.t.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: BUSL-1.1 +// SPDX-License-Identifier: MIT pragma solidity =0.8.12; import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; @@ -8,9 +8,14 @@ import {EigenDAServiceManager, IRewardsCoordinator} from "../../src/core/EigenDA import {EigenDAHasher} from "../../src/libraries/EigenDAHasher.sol"; import {EigenDAServiceManager} from "../../src/core/EigenDAServiceManager.sol"; import {IEigenDAServiceManager} from "../../src/interfaces/IEigenDAServiceManager.sol"; -import "../../src/interfaces/IEigenDAStructs.sol"; -import {IEigenDAThresholdRegistry} from "../../src/interfaces/IEigenDAThresholdRegistry.sol"; +import {EigenDABlobVerifier} from "../../src/core/EigenDABlobVerifier.sol"; +import {EigenDAThresholdRegistry, IEigenDAThresholdRegistry} from "../../src/core/EigenDAThresholdRegistry.sol"; +import {IEigenDABatchMetadataStorage} from "../../src/interfaces/IEigenDABatchMetadataStorage.sol"; +import {IEigenDASignatureVerifier} from "../../src/interfaces/IEigenDASignatureVerifier.sol"; +import {IRegistryCoordinator} from "../../lib/eigenlayer-middleware/src/interfaces/IRegistryCoordinator.sol"; import {IEigenDARelayRegistry} from "../../src/interfaces/IEigenDARelayRegistry.sol"; +import {EigenDARelayRegistry} from "../../src/core/EigenDARelayRegistry.sol"; +import "../../src/interfaces/IEigenDAStructs.sol"; contract EigenDAServiceManagerUnit is BLSMockAVSDeployer { using BN254 for BN254.G1Point; @@ -24,6 +29,16 @@ contract EigenDAServiceManagerUnit is BLSMockAVSDeployer { EigenDAServiceManager eigenDAServiceManager; EigenDAServiceManager eigenDAServiceManagerImplementation; + EigenDABlobVerifier eigenDABlobVerifier; + EigenDARelayRegistry eigenDARelayRegistry; + EigenDARelayRegistry eigenDARelayRegistryImplementation; + + EigenDAThresholdRegistry eigenDAThresholdRegistry; + EigenDAThresholdRegistry eigenDAThresholdRegistryImplementation; + bytes quorumAdversaryThresholdPercentages = hex"212121"; + bytes quorumConfirmationThresholdPercentages = hex"373737"; + bytes quorumNumbersRequired = hex"0001"; + SecurityThresholds defaultSecurityThresholds = SecurityThresholds(33, 55); uint256 feePerBytePerTime = 0; @@ -34,35 +49,86 @@ contract EigenDAServiceManagerUnit is BLSMockAVSDeployer { function setUp() virtual public { _setUpBLSMockAVSDeployer(); + eigenDAServiceManager = EigenDAServiceManager( + address( + new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), "") + ) + ); + + eigenDAThresholdRegistry = EigenDAThresholdRegistry( + address( + new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), "") + ) + ); + + eigenDARelayRegistry = EigenDARelayRegistry( + address( + new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), "") + ) + ); + + eigenDAThresholdRegistryImplementation = new EigenDAThresholdRegistry(); + eigenDAServiceManagerImplementation = new EigenDAServiceManager( avsDirectory, rewardsCoordinator, registryCoordinator, stakeRegistry, - IEigenDAThresholdRegistry(address(0)), - IEigenDARelayRegistry(address(0)) + eigenDAThresholdRegistry, + eigenDARelayRegistry ); address[] memory confirmers = new address[](1); confirmers[0] = confirmer; - // Third, upgrade the proxy contracts to use the correct implementation contracts and initialize them. - eigenDAServiceManager = EigenDAServiceManager( - address( - new TransparentUpgradeableProxy( - address(eigenDAServiceManagerImplementation), - address(proxyAdmin), - abi.encodeWithSelector( - EigenDAServiceManager.initialize.selector, - pauserRegistry, - 0, - registryCoordinatorOwner, - confirmers, - rewardsInitiator - ) - ) + cheats.prank(proxyAdminOwner); + proxyAdmin.upgradeAndCall( + TransparentUpgradeableProxy(payable(address(eigenDAServiceManager))), + address(eigenDAServiceManagerImplementation), + abi.encodeWithSelector( + EigenDAServiceManager.initialize.selector, + pauserRegistry, + 0, + registryCoordinatorOwner, + confirmers, + registryCoordinatorOwner ) ); + + VersionedBlobParams[] memory versionedBlobParams = new VersionedBlobParams[](0); + + cheats.prank(proxyAdminOwner); + proxyAdmin.upgradeAndCall( + TransparentUpgradeableProxy(payable(address(eigenDAThresholdRegistry))), + address(eigenDAThresholdRegistryImplementation), + abi.encodeWithSelector( + EigenDAThresholdRegistry.initialize.selector, + registryCoordinatorOwner, + quorumAdversaryThresholdPercentages, + quorumConfirmationThresholdPercentages, + quorumNumbersRequired, + versionedBlobParams, + defaultSecurityThresholds + ) + ); + + eigenDABlobVerifier = new EigenDABlobVerifier( + IEigenDAThresholdRegistry(address(eigenDAThresholdRegistry)), + IEigenDABatchMetadataStorage(address(eigenDAServiceManager)), + IEigenDASignatureVerifier(address(eigenDAServiceManager)), + IEigenDARelayRegistry(address(eigenDARelayRegistry)), + OperatorStateRetriever(address(operatorStateRetriever)), + IRegistryCoordinator(address(registryCoordinator)) + ); + + eigenDARelayRegistryImplementation = new EigenDARelayRegistry(); + + cheats.prank(proxyAdminOwner); + proxyAdmin.upgradeAndCall( + TransparentUpgradeableProxy(payable(address(eigenDARelayRegistry))), + address(eigenDARelayRegistryImplementation), + abi.encodeWithSelector(EigenDARelayRegistry.initialize.selector, registryCoordinatorOwner) + ); } function testConfirmBatch_AllSigning_Valid(uint256 pseudoRandomNumber) public {