diff --git a/src/RegistryCoordinator.sol b/src/RegistryCoordinator.sol index 325722ad..c1970c08 100644 --- a/src/RegistryCoordinator.sol +++ b/src/RegistryCoordinator.sol @@ -208,6 +208,16 @@ contract RegistryCoordinator is IRegistryCoordinator, SlashingRegistryCoordinato return (1 << quorumCount) - 1; } + /** + * @notice Returns the message hash that an operator must sign to register their BLS public key. + * @param operator is the address of the operator registering their BLS public key + */ + function calculatePubkeyRegistrationMessageHash( + address operator + ) public view returns (bytes32) { + return _hashTypedDataV4(keccak256(abi.encode(PUBKEY_REGISTRATION_TYPEHASH, operator))); + } + /// @dev need to override function here since its defined in both these contracts function owner() public diff --git a/test/unit/RegistryCoordinatorUnit.t.sol b/test/unit/RegistryCoordinatorUnit.t.sol index a734171a..3e0208bc 100644 --- a/test/unit/RegistryCoordinatorUnit.t.sol +++ b/test/unit/RegistryCoordinatorUnit.t.sol @@ -12,6 +12,12 @@ import {IBLSApkRegistryTypes} from "../../src/interfaces/IBLSApkRegistry.sol"; import {QuorumBitmapHistoryLib} from "../../src/libraries/QuorumBitmapHistoryLib.sol"; import {BitmapUtils} from "../../src/libraries/BitmapUtils.sol"; import {console} from "forge-std/console.sol"; +import { + OperatorWalletLib, + SigningKeyOperationsLib, + OperatorKeyOperationsLib, + Operator +} from "../utils/OperatorWalletLib.sol"; contract RegistryCoordinatorUnitTests is MockAVSDeployer { using BN254 for BN254.G1Point; @@ -2418,52 +2424,68 @@ contract RegistryCoordinatorUnitTests_AfterMigration is RegistryCoordinatorUnitT assertTrue(registryCoordinator.operatorSetsEnabled()); } + /// function test_M2_Deregister() public { - vm.skip(true); - /// Create 2 M2 quorums _deployMockEigenLayerAndAVS(2); - address operatorToRegister = address(420); + Operator memory operatorToRegister = OperatorWalletLib.createOperator("4"); - ISignatureUtils.SignatureWithSaltAndExpiry memory emptySignature = ISignatureUtils - .SignatureWithSaltAndExpiry({signature: new bytes(0), salt: bytes32(0), expiry: 0}); + /// NOTE: resolves stack too deep + { + // Set operator shares for quorum 0 + (IStrategy strategy,) = stakeRegistry.strategyParams(0, 0); + delegationMock.setOperatorShares(operatorToRegister.key.addr, strategy, 1 ether); + } + bytes32 salt = bytes32(uint256(1)); + uint256 expiry = block.timestamp + 1 days; + bytes32 digestHash = avsDirectory.calculateOperatorAVSRegistrationDigestHash( + operatorToRegister.key.addr, address(registryCoordinator), salt, expiry + ); + bytes memory signature = OperatorKeyOperationsLib.sign(operatorToRegister.key, digestHash); + ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature = ISignatureUtils + .SignatureWithSaltAndExpiry({signature: signature, salt: salt, expiry: expiry}); + bytes32 messageHash = + registryCoordinator.calculatePubkeyRegistrationMessageHash(operatorToRegister.key.addr); IBLSApkRegistryTypes.PubkeyRegistrationParams memory operatorRegisterApkParams = IBLSApkRegistryTypes.PubkeyRegistrationParams({ - pubkeyRegistrationSignature: BN254.G1Point({X: 0, Y: 0}), - pubkeyG1: BN254.G1Point({X: 0, Y: 0}), - pubkeyG2: BN254.G2Point({X: [uint256(0), uint256(0)], Y: [uint256(0), uint256(0)]}) + pubkeyRegistrationSignature: SigningKeyOperationsLib.sign( + operatorToRegister.signingKey, messageHash + ), + pubkeyG1: operatorToRegister.signingKey.publicKeyG1, + pubkeyG2: operatorToRegister.signingKey.publicKeyG2 }); string memory socket = "socket"; // register for quorum 0 - vm.prank(operatorToRegister); + vm.prank(operatorToRegister.key.addr); registryCoordinator.registerOperator( new bytes(1), // Convert 0 to bytes1 first socket, operatorRegisterApkParams, - emptySignature + operatorSignature ); /// migrate to operator sets + vm.prank(registryCoordinatorOwner); registryCoordinator.enableOperatorSets(); /// Deregistration for m2 should for the first two operator sets - vm.prank(defaultOperator); + vm.prank(operatorToRegister.key.addr); registryCoordinator.deregisterOperator(new bytes(1)); // Verify operator was deregistered by checking their bitmap is empty - bytes32 operatorId = registryCoordinator.getOperatorId(operatorToRegister); + bytes32 operatorId = registryCoordinator.getOperatorId(operatorToRegister.key.addr); uint192 bitmap = registryCoordinator.getCurrentQuorumBitmap(operatorId); assertEq(bitmap, 0, "Operator bitmap should be empty after deregistration"); // Verify operator status is NEVER_REGISTERED ISlashingRegistryCoordinatorTypes.OperatorStatus status = - registryCoordinator.getOperatorStatus(operatorToRegister); + registryCoordinator.getOperatorStatus(operatorToRegister.key.addr); assertEq( uint8(status), - uint8(ISlashingRegistryCoordinatorTypes.OperatorStatus.NEVER_REGISTERED), + uint8(ISlashingRegistryCoordinatorTypes.OperatorStatus.DEREGISTERED), "Operator status should be NEVER_REGISTERED" ); } diff --git a/test/utils/BN256G2.sol b/test/utils/BN256G2.sol new file mode 100644 index 00000000..9d407b41 --- /dev/null +++ b/test/utils/BN256G2.sol @@ -0,0 +1,337 @@ +pragma solidity ^0.8.0; + +/** + * @title Elliptic curve operations on twist points for alt_bn128 + * @author Mustafa Al-Bassam (mus@musalbas.com) + * @dev Homepage: https://github.com/musalbas/solidity-BN256G2 + */ +library BN256G2 { + uint256 internal constant FIELD_MODULUS = + 0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47; + uint256 internal constant TWISTBX = + 0x2b149d40ceb8aaae81be18991be06ac3b5b4c5e559dbefa33267e6dc24a138e5; + uint256 internal constant TWISTBY = + 0x9713b03af0fed4cd2cafadeed8fdf4a74fa084e52d1852e4a2bd0685c315d2; + uint256 internal constant PTXX = 0; + uint256 internal constant PTXY = 1; + uint256 internal constant PTYX = 2; + uint256 internal constant PTYY = 3; + uint256 internal constant PTZX = 4; + uint256 internal constant PTZY = 5; + + /** + * @notice Add two twist points + * @param pt1xx Coefficient 1 of x on point 1 + * @param pt1xy Coefficient 2 of x on point 1 + * @param pt1yx Coefficient 1 of y on point 1 + * @param pt1yy Coefficient 2 of y on point 1 + * @param pt2xx Coefficient 1 of x on point 2 + * @param pt2xy Coefficient 2 of x on point 2 + * @param pt2yx Coefficient 1 of y on point 2 + * @param pt2yy Coefficient 2 of y on point 2 + * @return (pt3xx, pt3xy, pt3yx, pt3yy) + */ + function ECTwistAdd( + uint256 pt1xx, + uint256 pt1xy, + uint256 pt1yx, + uint256 pt1yy, + uint256 pt2xx, + uint256 pt2xy, + uint256 pt2yx, + uint256 pt2yy + ) public view returns (uint256, uint256, uint256, uint256) { + if (pt1xx == 0 && pt1xy == 0 && pt1yx == 0 && pt1yy == 0) { + if (!(pt2xx == 0 && pt2xy == 0 && pt2yx == 0 && pt2yy == 0)) { + assert(_isOnCurve(pt2xx, pt2xy, pt2yx, pt2yy)); + } + return (pt2xx, pt2xy, pt2yx, pt2yy); + } else if (pt2xx == 0 && pt2xy == 0 && pt2yx == 0 && pt2yy == 0) { + assert(_isOnCurve(pt1xx, pt1xy, pt1yx, pt1yy)); + return (pt1xx, pt1xy, pt1yx, pt1yy); + } + + assert(_isOnCurve(pt1xx, pt1xy, pt1yx, pt1yy)); + assert(_isOnCurve(pt2xx, pt2xy, pt2yx, pt2yy)); + + uint256[6] memory pt3 = + _ECTwistAddJacobian(pt1xx, pt1xy, pt1yx, pt1yy, 1, 0, pt2xx, pt2xy, pt2yx, pt2yy, 1, 0); + + return _fromJacobian(pt3[PTXX], pt3[PTXY], pt3[PTYX], pt3[PTYY], pt3[PTZX], pt3[PTZY]); + } + + /** + * @notice Multiply a twist point by a scalar + * @param s Scalar to multiply by + * @param pt1xx Coefficient 1 of x + * @param pt1xy Coefficient 2 of x + * @param pt1yx Coefficient 1 of y + * @param pt1yy Coefficient 2 of y + * @return (pt2xx, pt2xy, pt2yx, pt2yy) + */ + function ECTwistMul( + uint256 s, + uint256 pt1xx, + uint256 pt1xy, + uint256 pt1yx, + uint256 pt1yy + ) public view returns (uint256, uint256, uint256, uint256) { + uint256 pt1zx = 1; + if (pt1xx == 0 && pt1xy == 0 && pt1yx == 0 && pt1yy == 0) { + pt1xx = 1; + pt1yx = 1; + pt1zx = 0; + } else { + assert(_isOnCurve(pt1xx, pt1xy, pt1yx, pt1yy)); + } + + uint256[6] memory pt2 = _ECTwistMulJacobian(s, pt1xx, pt1xy, pt1yx, pt1yy, pt1zx, 0); + + return _fromJacobian(pt2[PTXX], pt2[PTXY], pt2[PTYX], pt2[PTYY], pt2[PTZX], pt2[PTZY]); + } + + /** + * @notice Get the field modulus + * @return The field modulus + */ + function GetFieldModulus() public pure returns (uint256) { + return FIELD_MODULUS; + } + + function submod(uint256 a, uint256 b, uint256 n) internal pure returns (uint256) { + return addmod(a, n - b, n); + } + + function _FQ2Mul( + uint256 xx, + uint256 xy, + uint256 yx, + uint256 yy + ) internal pure returns (uint256, uint256) { + return ( + submod(mulmod(xx, yx, FIELD_MODULUS), mulmod(xy, yy, FIELD_MODULUS), FIELD_MODULUS), + addmod(mulmod(xx, yy, FIELD_MODULUS), mulmod(xy, yx, FIELD_MODULUS), FIELD_MODULUS) + ); + } + + function _FQ2Muc(uint256 xx, uint256 xy, uint256 c) internal pure returns (uint256, uint256) { + return (mulmod(xx, c, FIELD_MODULUS), mulmod(xy, c, FIELD_MODULUS)); + } + + function _FQ2Add( + uint256 xx, + uint256 xy, + uint256 yx, + uint256 yy + ) internal pure returns (uint256, uint256) { + return (addmod(xx, yx, FIELD_MODULUS), addmod(xy, yy, FIELD_MODULUS)); + } + + function _FQ2Sub( + uint256 xx, + uint256 xy, + uint256 yx, + uint256 yy + ) internal pure returns (uint256 rx, uint256 ry) { + return (submod(xx, yx, FIELD_MODULUS), submod(xy, yy, FIELD_MODULUS)); + } + + function _FQ2Div( + uint256 xx, + uint256 xy, + uint256 yx, + uint256 yy + ) internal view returns (uint256, uint256) { + (yx, yy) = _FQ2Inv(yx, yy); + return _FQ2Mul(xx, xy, yx, yy); + } + + function _FQ2Inv(uint256 x, uint256 y) internal view returns (uint256, uint256) { + uint256 inv = _modInv( + addmod(mulmod(y, y, FIELD_MODULUS), mulmod(x, x, FIELD_MODULUS), FIELD_MODULUS), + FIELD_MODULUS + ); + return (mulmod(x, inv, FIELD_MODULUS), FIELD_MODULUS - mulmod(y, inv, FIELD_MODULUS)); + } + + function _isOnCurve( + uint256 xx, + uint256 xy, + uint256 yx, + uint256 yy + ) internal pure returns (bool) { + uint256 yyx; + uint256 yyy; + uint256 xxxx; + uint256 xxxy; + (yyx, yyy) = _FQ2Mul(yx, yy, yx, yy); + (xxxx, xxxy) = _FQ2Mul(xx, xy, xx, xy); + (xxxx, xxxy) = _FQ2Mul(xxxx, xxxy, xx, xy); + (yyx, yyy) = _FQ2Sub(yyx, yyy, xxxx, xxxy); + (yyx, yyy) = _FQ2Sub(yyx, yyy, TWISTBX, TWISTBY); + return yyx == 0 && yyy == 0; + } + + function _modInv(uint256 a, uint256 n) internal view returns (uint256 result) { + bool success; + assembly ("memory-safe") { + let freemem := mload(0x40) + mstore(freemem, 0x20) + mstore(add(freemem, 0x20), 0x20) + mstore(add(freemem, 0x40), 0x20) + mstore(add(freemem, 0x60), a) + mstore(add(freemem, 0x80), sub(n, 2)) + mstore(add(freemem, 0xA0), n) + success := staticcall(sub(gas(), 2000), 5, freemem, 0xC0, freemem, 0x20) + result := mload(freemem) + } + require(success); + } + + function _fromJacobian( + uint256 pt1xx, + uint256 pt1xy, + uint256 pt1yx, + uint256 pt1yy, + uint256 pt1zx, + uint256 pt1zy + ) internal view returns (uint256 pt2xx, uint256 pt2xy, uint256 pt2yx, uint256 pt2yy) { + uint256 invzx; + uint256 invzy; + (invzx, invzy) = _FQ2Inv(pt1zx, pt1zy); + (pt2xx, pt2xy) = _FQ2Mul(pt1xx, pt1xy, invzx, invzy); + (pt2yx, pt2yy) = _FQ2Mul(pt1yx, pt1yy, invzx, invzy); + } + + function _ECTwistAddJacobian( + uint256 pt1xx, + uint256 pt1xy, + uint256 pt1yx, + uint256 pt1yy, + uint256 pt1zx, + uint256 pt1zy, + uint256 pt2xx, + uint256 pt2xy, + uint256 pt2yx, + uint256 pt2yy, + uint256 pt2zx, + uint256 pt2zy + ) internal pure returns (uint256[6] memory pt3) { + if (pt1zx == 0 && pt1zy == 0) { + (pt3[PTXX], pt3[PTXY], pt3[PTYX], pt3[PTYY], pt3[PTZX], pt3[PTZY]) = + (pt2xx, pt2xy, pt2yx, pt2yy, pt2zx, pt2zy); + return pt3; + } else if (pt2zx == 0 && pt2zy == 0) { + (pt3[PTXX], pt3[PTXY], pt3[PTYX], pt3[PTYY], pt3[PTZX], pt3[PTZY]) = + (pt1xx, pt1xy, pt1yx, pt1yy, pt1zx, pt1zy); + return pt3; + } + + (pt2yx, pt2yy) = _FQ2Mul(pt2yx, pt2yy, pt1zx, pt1zy); // U1 = y2 * z1 + (pt3[PTYX], pt3[PTYY]) = _FQ2Mul(pt1yx, pt1yy, pt2zx, pt2zy); // U2 = y1 * z2 + (pt2xx, pt2xy) = _FQ2Mul(pt2xx, pt2xy, pt1zx, pt1zy); // V1 = x2 * z1 + (pt3[PTZX], pt3[PTZY]) = _FQ2Mul(pt1xx, pt1xy, pt2zx, pt2zy); // V2 = x1 * z2 + + if (pt2xx == pt3[PTZX] && pt2xy == pt3[PTZY]) { + if (pt2yx == pt3[PTYX] && pt2yy == pt3[PTYY]) { + (pt3[PTXX], pt3[PTXY], pt3[PTYX], pt3[PTYY], pt3[PTZX], pt3[PTZY]) = + _ECTwistDoubleJacobian(pt1xx, pt1xy, pt1yx, pt1yy, pt1zx, pt1zy); + return pt3; + } + (pt3[PTXX], pt3[PTXY], pt3[PTYX], pt3[PTYY], pt3[PTZX], pt3[PTZY]) = (1, 0, 1, 0, 0, 0); + return pt3; + } + + (pt2zx, pt2zy) = _FQ2Mul(pt1zx, pt1zy, pt2zx, pt2zy); // W = z1 * z2 + (pt1xx, pt1xy) = _FQ2Sub(pt2yx, pt2yy, pt3[PTYX], pt3[PTYY]); // U = U1 - U2 + (pt1yx, pt1yy) = _FQ2Sub(pt2xx, pt2xy, pt3[PTZX], pt3[PTZY]); // V = V1 - V2 + (pt1zx, pt1zy) = _FQ2Mul(pt1yx, pt1yy, pt1yx, pt1yy); // V_squared = V * V + (pt2yx, pt2yy) = _FQ2Mul(pt1zx, pt1zy, pt3[PTZX], pt3[PTZY]); // V_squared_times_V2 = V_squared * V2 + (pt1zx, pt1zy) = _FQ2Mul(pt1zx, pt1zy, pt1yx, pt1yy); // V_cubed = V * V_squared + (pt3[PTZX], pt3[PTZY]) = _FQ2Mul(pt1zx, pt1zy, pt2zx, pt2zy); // newz = V_cubed * W + (pt2xx, pt2xy) = _FQ2Mul(pt1xx, pt1xy, pt1xx, pt1xy); // U * U + (pt2xx, pt2xy) = _FQ2Mul(pt2xx, pt2xy, pt2zx, pt2zy); // U * U * W + (pt2xx, pt2xy) = _FQ2Sub(pt2xx, pt2xy, pt1zx, pt1zy); // U * U * W - V_cubed + (pt2zx, pt2zy) = _FQ2Muc(pt2yx, pt2yy, 2); // 2 * V_squared_times_V2 + (pt2xx, pt2xy) = _FQ2Sub(pt2xx, pt2xy, pt2zx, pt2zy); // A = U * U * W - V_cubed - 2 * V_squared_times_V2 + (pt3[PTXX], pt3[PTXY]) = _FQ2Mul(pt1yx, pt1yy, pt2xx, pt2xy); // newx = V * A + (pt1yx, pt1yy) = _FQ2Sub(pt2yx, pt2yy, pt2xx, pt2xy); // V_squared_times_V2 - A + (pt1yx, pt1yy) = _FQ2Mul(pt1xx, pt1xy, pt1yx, pt1yy); // U * (V_squared_times_V2 - A) + (pt1xx, pt1xy) = _FQ2Mul(pt1zx, pt1zy, pt3[PTYX], pt3[PTYY]); // V_cubed * U2 + (pt3[PTYX], pt3[PTYY]) = _FQ2Sub(pt1yx, pt1yy, pt1xx, pt1xy); // newy = U * (V_squared_times_V2 - A) - V_cubed * U2 + } + + function _ECTwistDoubleJacobian( + uint256 pt1xx, + uint256 pt1xy, + uint256 pt1yx, + uint256 pt1yy, + uint256 pt1zx, + uint256 pt1zy + ) + internal + pure + returns ( + uint256 pt2xx, + uint256 pt2xy, + uint256 pt2yx, + uint256 pt2yy, + uint256 pt2zx, + uint256 pt2zy + ) + { + (pt2xx, pt2xy) = _FQ2Muc(pt1xx, pt1xy, 3); // 3 * x + (pt2xx, pt2xy) = _FQ2Mul(pt2xx, pt2xy, pt1xx, pt1xy); // W = 3 * x * x + (pt1zx, pt1zy) = _FQ2Mul(pt1yx, pt1yy, pt1zx, pt1zy); // S = y * z + (pt2yx, pt2yy) = _FQ2Mul(pt1xx, pt1xy, pt1yx, pt1yy); // x * y + (pt2yx, pt2yy) = _FQ2Mul(pt2yx, pt2yy, pt1zx, pt1zy); // B = x * y * S + (pt1xx, pt1xy) = _FQ2Mul(pt2xx, pt2xy, pt2xx, pt2xy); // W * W + (pt2zx, pt2zy) = _FQ2Muc(pt2yx, pt2yy, 8); // 8 * B + (pt1xx, pt1xy) = _FQ2Sub(pt1xx, pt1xy, pt2zx, pt2zy); // H = W * W - 8 * B + (pt2zx, pt2zy) = _FQ2Mul(pt1zx, pt1zy, pt1zx, pt1zy); // S_squared = S * S + (pt2yx, pt2yy) = _FQ2Muc(pt2yx, pt2yy, 4); // 4 * B + (pt2yx, pt2yy) = _FQ2Sub(pt2yx, pt2yy, pt1xx, pt1xy); // 4 * B - H + (pt2yx, pt2yy) = _FQ2Mul(pt2yx, pt2yy, pt2xx, pt2xy); // W * (4 * B - H) + (pt2xx, pt2xy) = _FQ2Muc(pt1yx, pt1yy, 8); // 8 * y + (pt2xx, pt2xy) = _FQ2Mul(pt2xx, pt2xy, pt1yx, pt1yy); // 8 * y * y + (pt2xx, pt2xy) = _FQ2Mul(pt2xx, pt2xy, pt2zx, pt2zy); // 8 * y * y * S_squared + (pt2yx, pt2yy) = _FQ2Sub(pt2yx, pt2yy, pt2xx, pt2xy); // newy = W * (4 * B - H) - 8 * y * y * S_squared + (pt2xx, pt2xy) = _FQ2Muc(pt1xx, pt1xy, 2); // 2 * H + (pt2xx, pt2xy) = _FQ2Mul(pt2xx, pt2xy, pt1zx, pt1zy); // newx = 2 * H * S + (pt2zx, pt2zy) = _FQ2Mul(pt1zx, pt1zy, pt2zx, pt2zy); // S * S_squared + (pt2zx, pt2zy) = _FQ2Muc(pt2zx, pt2zy, 8); // newz = 8 * S * S_squared + } + + function _ECTwistMulJacobian( + uint256 d, + uint256 pt1xx, + uint256 pt1xy, + uint256 pt1yx, + uint256 pt1yy, + uint256 pt1zx, + uint256 pt1zy + ) internal pure returns (uint256[6] memory pt2) { + while (d != 0) { + if ((d & 1) != 0) { + pt2 = _ECTwistAddJacobian( + pt2[PTXX], + pt2[PTXY], + pt2[PTYX], + pt2[PTYY], + pt2[PTZX], + pt2[PTZY], + pt1xx, + pt1xy, + pt1yx, + pt1yy, + pt1zx, + pt1zy + ); + } + (pt1xx, pt1xy, pt1yx, pt1yy, pt1zx, pt1zy) = + _ECTwistDoubleJacobian(pt1xx, pt1xy, pt1yx, pt1yy, pt1zx, pt1zy); + + d = d / 2; + } + } +} diff --git a/test/utils/CoreDeployLib.sol b/test/utils/CoreDeployLib.sol index d25dd95a..c0f474e7 100644 --- a/test/utils/CoreDeployLib.sol +++ b/test/utils/CoreDeployLib.sol @@ -1,271 +1,345 @@ -// // SPDX-License-Identifier: UNLICENSED -// pragma solidity ^0.8.0; - -// import {ProxyAdmin} from "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; -// import {TransparentUpgradeableProxy} from -// "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; -// import {UpgradeableBeacon} from "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol"; -// import {DelegationManager} from "eigenlayer-contracts/src/contracts/core/DelegationManager.sol"; -// import {StrategyManager} from "eigenlayer-contracts/src/contracts/core/StrategyManager.sol"; -// import {AVSDirectory} from "eigenlayer-contracts/src/contracts/core/AVSDirectory.sol"; -// import {EigenPodManager} from "eigenlayer-contracts/src/contracts/pods/EigenPodManager.sol"; -// import {RewardsCoordinator} from "eigenlayer-contracts/src/contracts/core/RewardsCoordinator.sol"; -// import {StrategyBase} from "eigenlayer-contracts/src/contracts/strategies/StrategyBase.sol"; -// import {EigenPod} from "eigenlayer-contracts/src/contracts/pods/EigenPod.sol"; -// import {IETHPOSDeposit} from "eigenlayer-contracts/src/contracts/interfaces/IETHPOSDeposit.sol"; -// import {StrategyBaseTVLLimits} from "eigenlayer-contracts/src/contracts/strategies/StrategyBaseTVLLimits.sol"; -// import {PauserRegistry} from "eigenlayer-contracts/src/contracts/permissions/PauserRegistry.sol"; -// import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; -// import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -// import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol"; -// import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; -// import {IBeacon} from "@openzeppelin/contracts/proxy/beacon/IBeacon.sol"; -// import {IStrategyManager} from "eigenlayer-contracts/src/contracts/interfaces/IStrategyManager.sol"; -// import {IEigenPodManager} from "eigenlayer-contracts/src/contracts/interfaces/IEigenPodManager.sol"; -// import {IAVSDirectory} from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol"; -// import {IPauserRegistry} from "eigenlayer-contracts/src/contracts/interfaces/IPauserRegistry.sol"; -// import {StrategyFactory} from "eigenlayer-contracts/src/contracts/strategies/StrategyFactory.sol"; - -// import {UpgradeableProxyLib} from "../unit/UpgradeableProxyLib.sol"; - -// library CoreDeploymentLib { -// using UpgradeableProxyLib for address; - -// struct StrategyManagerConfig { -// uint256 initPausedStatus; -// uint256 initWithdrawalDelayBlocks; -// } - -// struct DelegationManagerConfig { -// uint256 initPausedStatus; -// IStrategy[] strategies; -// uint256 minWithdrawalDelayBlocks; -// uint256[] withdrawalDelayBlocks; - -// } - -// struct EigenPodManagerConfig { -// uint256 initPausedStatus; -// } - -// struct RewardsCoordinatorConfig { -// uint256 initPausedStatus; -// uint256 maxRewardsDuration; -// uint256 maxRetroactiveLength; -// uint256 maxFutureLength; -// uint256 genesisRewardsTimestamp; -// address updater; -// uint256 activationDelay; -// uint256 calculationIntervalSeconds; -// uint256 globalOperatorCommissionBips; -// } - -// struct StrategyFactoryConfig { -// uint256 initPausedStatus; -// } - -// struct DeploymentConfigData { -// StrategyManagerConfig strategyManager; -// DelegationManagerConfig delegationManager; -// EigenPodManagerConfig eigenPodManager; -// RewardsCoordinatorConfig rewardsCoordinator; -// StrategyFactoryConfig strategyFactory; -// } - -// struct DeploymentData { -// address delegationManager; -// address avsDirectory; -// address strategyManager; -// address eigenPodManager; -// address rewardsCoordinator; -// address eigenPodBeacon; -// address pauserRegistry; -// address strategyFactory; -// address strategyBeacon; -// } - -// function deployContracts( -// address proxyAdmin, -// DeploymentConfigData memory configData -// ) internal returns (DeploymentData memory) { -// DeploymentData memory result; - -// result.delegationManager = UpgradeableProxyLib.setUpEmptyProxy(proxyAdmin); -// result.avsDirectory = UpgradeableProxyLib.setUpEmptyProxy(proxyAdmin); -// result.strategyManager = UpgradeableProxyLib.setUpEmptyProxy(proxyAdmin); -// result.eigenPodManager = UpgradeableProxyLib.setUpEmptyProxy(proxyAdmin); -// result.rewardsCoordinator = UpgradeableProxyLib.setUpEmptyProxy(proxyAdmin); -// result.eigenPodBeacon = UpgradeableProxyLib.setUpEmptyProxy(proxyAdmin); -// result.pauserRegistry = UpgradeableProxyLib.setUpEmptyProxy(proxyAdmin); -// result.strategyFactory = UpgradeableProxyLib.setUpEmptyProxy(proxyAdmin); - -// // Deploy the implementation contracts, using the proxy contracts as inputs -// address delegationManagerImpl = address( -// new DelegationManager( -// IStrategyManager(result.strategyManager), -// IEigenPodManager(result.eigenPodManager) -// ) -// ); -// address avsDirectoryImpl = -// address(new AVSDirectory(IDelegationManager(result.delegationManager))); - -// address strategyManagerImpl = address( -// new StrategyManager( -// IDelegationManager(result.delegationManager), -// IEigenPodManager(result.eigenPodManager) -// ) -// ); - -// address strategyFactoryImpl = -// address(new StrategyFactory(IStrategyManager(result.strategyManager))); - -// address ethPOSDeposit; -// if (block.chainid == 1) { -// ethPOSDeposit = 0x00000000219ab540356cBB839Cbe05303d7705Fa; -// } else { -// // For non-mainnet chains, you might want to deploy a mock or read from a config -// // This assumes you have a similar config setup as in M2_Deploy_From_Scratch.s.sol -// /// TODO: Handle Eth pos -// } - -// address eigenPodManagerImpl = address( -// new EigenPodManager( -// IETHPOSDeposit(ethPOSDeposit), -// IBeacon(result.eigenPodBeacon), -// IStrategyManager(result.strategyManager), -// IDelegationManager(result.delegationManager) -// ) -// ); - -// /// TODO: Get actual values -// uint32 CALCULATION_INTERVAL_SECONDS = 1 days; -// uint32 MAX_REWARDS_DURATION = 1 days; -// uint32 MAX_RETROACTIVE_LENGTH = 1; -// uint32 MAX_FUTURE_LENGTH = 1; -// uint32 GENESIS_REWARDS_TIMESTAMP = 10 days; -// address rewardsCoordinatorImpl = address( -// new RewardsCoordinator( -// IDelegationManager(result.delegationManager), -// IStrategyManager(result.strategyManager), -// CALCULATION_INTERVAL_SECONDS, -// MAX_REWARDS_DURATION, -// MAX_RETROACTIVE_LENGTH, -// MAX_FUTURE_LENGTH, -// GENESIS_REWARDS_TIMESTAMP -// ) -// ); - -// /// TODO: Get actual genesis time -// uint64 GENESIS_TIME = 1_564_000; - -// address eigenPodImpl = address( -// new EigenPod( -// IETHPOSDeposit(ethPOSDeposit), -// IEigenPodManager(result.eigenPodManager), -// GENESIS_TIME -// ) -// ); -// address eigenPodBeaconImpl = address(new UpgradeableBeacon(eigenPodImpl)); -// address baseStrategyImpl = -// address(new StrategyBase(IStrategyManager(result.strategyManager))); -// /// TODO: PauserRegistry isn't upgradeable -// address pauserRegistryImpl = address( -// new PauserRegistry( -// new address[](0), // Empty array for pausers -// proxyAdmin // ProxyAdmin as the unpauser -// ) -// ); - -// // Deploy and configure the strategy beacon -// result.strategyBeacon = address(new UpgradeableBeacon(baseStrategyImpl)); - -// // Upgrade contracts -// /// TODO: Get from config -// bytes memory upgradeCall = abi.encodeWithSelector( /// TODO: Fix abi.encodeCall was failing Cannot implicitly convert component at position 4 from "IStrategy[]" to "IStrategy[]" -// DelegationManager.initialize.selector, -// proxyAdmin, // initialOwner -// IPauserRegistry(result.pauserRegistry), // _pauserRegistry -// configData.delegationManager.initPausedStatus, // initialPausedStatus -// configData.delegationManager.minWithdrawalDelayBlocks, // _minWithdrawalDelayBlocks -// configData.delegationManager.strategies, // _strategies -// configData.delegationManager.withdrawalDelayBlocks // _withdrawalDelayBlocks -// ); -// UpgradeableProxyLib.upgradeAndCall( -// result.delegationManager, delegationManagerImpl, upgradeCall -// ); - -// // Upgrade StrategyManager contract -// upgradeCall = abi.encodeCall( -// StrategyManager.initialize, -// ( -// proxyAdmin, // initialOwner -// result.strategyFactory, // initialStrategyWhitelister -// IPauserRegistry(result.pauserRegistry), // _pauserRegistry -// configData.strategyManager.initPausedStatus // initialPausedStatus -// ) -// ); -// UpgradeableProxyLib.upgradeAndCall(result.strategyManager, strategyManagerImpl, upgradeCall); - -// // Upgrade StrategyFactory contract -// upgradeCall = abi.encodeCall( -// StrategyFactory.initialize, -// ( -// proxyAdmin, // initialOwner -// IPauserRegistry(result.pauserRegistry), // _pauserRegistry -// configData.strategyFactory.initPausedStatus, // initialPausedStatus -// IBeacon(result.strategyBeacon) -// ) -// ); -// UpgradeableProxyLib.upgradeAndCall(result.strategyFactory, strategyFactoryImpl, upgradeCall); - -// // Upgrade EigenPodManager contract -// upgradeCall = abi.encodeCall( -// EigenPodManager.initialize, -// ( -// proxyAdmin, // initialOwner -// IPauserRegistry(result.pauserRegistry), // _pauserRegistry -// configData.eigenPodManager.initPausedStatus // initialPausedStatus -// ) -// ); -// UpgradeableProxyLib.upgradeAndCall(result.eigenPodManager, eigenPodManagerImpl, upgradeCall); - -// // Upgrade AVSDirectory contract -// upgradeCall = abi.encodeCall( -// AVSDirectory.initialize, -// ( -// proxyAdmin, // initialOwner -// IPauserRegistry(result.pauserRegistry), // _pauserRegistry -// 0 // TODO: AVS Missing configinitialPausedStatus -// ) -// ); -// UpgradeableProxyLib.upgradeAndCall(result.avsDirectory, avsDirectoryImpl, upgradeCall); - -// // Upgrade RewardsCoordinator contract -// upgradeCall = abi.encodeCall( -// RewardsCoordinator.initialize, -// ( -// proxyAdmin, // initialOwner -// IPauserRegistry(result.pauserRegistry), // _pauserRegistry -// configData.rewardsCoordinator.initPausedStatus, // initialPausedStatus -// /// TODO: is there a setter and is this expected? -// address(0), // rewards updater -// uint32(configData.rewardsCoordinator.activationDelay), // _activationDelay -// uint16(configData.rewardsCoordinator.globalOperatorCommissionBips) // _globalCommissionBips -// ) -// ); -// UpgradeableProxyLib.upgradeAndCall( -// result.rewardsCoordinator, rewardsCoordinatorImpl, upgradeCall -// ); - -// // Upgrade EigenPod contract -// upgradeCall = abi.encodeCall( -// EigenPod.initialize, -// // TODO: Double check this -// (address(result.eigenPodManager)) // _podOwner -// ); -// UpgradeableProxyLib.upgradeAndCall(result.eigenPodBeacon, eigenPodImpl, upgradeCall); - -// return result; -// } - -// } +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.0; + +import {ProxyAdmin} from "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; +import {TransparentUpgradeableProxy} from + "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +import {UpgradeableBeacon} from "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol"; +import {DelegationManager} from "eigenlayer-contracts/src/contracts/core/DelegationManager.sol"; +import {StrategyManager} from "eigenlayer-contracts/src/contracts/core/StrategyManager.sol"; +import {AVSDirectory} from "eigenlayer-contracts/src/contracts/core/AVSDirectory.sol"; +import {EigenPodManager} from "eigenlayer-contracts/src/contracts/pods/EigenPodManager.sol"; +import {RewardsCoordinator} from "eigenlayer-contracts/src/contracts/core/RewardsCoordinator.sol"; +import {StrategyBase} from "eigenlayer-contracts/src/contracts/strategies/StrategyBase.sol"; +import {EigenPod} from "eigenlayer-contracts/src/contracts/pods/EigenPod.sol"; +import {IETHPOSDeposit} from "eigenlayer-contracts/src/contracts/interfaces/IETHPOSDeposit.sol"; +import {StrategyBaseTVLLimits} from + "eigenlayer-contracts/src/contracts/strategies/StrategyBaseTVLLimits.sol"; +import {PauserRegistry} from "eigenlayer-contracts/src/contracts/permissions/PauserRegistry.sol"; +import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; +import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol"; +import {IDelegationManager} from + "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; +import {IBeacon} from "@openzeppelin/contracts/proxy/beacon/IBeacon.sol"; +import {IStrategyManager} from "eigenlayer-contracts/src/contracts/interfaces/IStrategyManager.sol"; +import {IEigenPodManager} from "eigenlayer-contracts/src/contracts/interfaces/IEigenPodManager.sol"; +import {IAVSDirectory} from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol"; +import {IPauserRegistry} from "eigenlayer-contracts/src/contracts/interfaces/IPauserRegistry.sol"; +import {StrategyFactory} from "eigenlayer-contracts/src/contracts/strategies/StrategyFactory.sol"; +import {IPermissionController} from + "eigenlayer-contracts/src/contracts/interfaces/IPermissionController.sol"; +import {IAllocationManager} from + "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; +import {AllocationManager} from "eigenlayer-contracts/src/contracts/core/AllocationManager.sol"; +import {PermissionController} from + "eigenlayer-contracts/src/contracts/permissions/PermissionController.sol"; + +import {UpgradeableProxyLib} from "../unit/UpgradeableProxyLib.sol"; + +library CoreDeploymentLib { + using UpgradeableProxyLib for address; + + struct StrategyManagerConfig { + uint256 initPausedStatus; + address initialOwner; + address initialStrategyWhitelister; + } + + struct DelegationManagerConfig { + uint256 initPausedStatus; + address initialOwner; + uint32 minWithdrawalDelayBlocks; + } + + struct EigenPodManagerConfig { + uint256 initPausedStatus; + address initialOwner; + } + + struct AllocationManagerConfig { + uint256 initPausedStatus; + address initialOwner; + uint32 deallocationDelay; + uint32 allocationConfigurationDelay; + } + + struct StrategyFactoryConfig { + uint256 initPausedStatus; + address initialOwner; + } + + struct AVSDirectoryConfig { + uint256 initPausedStatus; + address initialOwner; + } + + struct RewardsCoordinatorConfig { + uint256 initPausedStatus; + address initialOwner; + address rewardsUpdater; + uint32 activationDelay; + uint16 defaultSplitBips; + uint32 calculationIntervalSeconds; + uint32 maxRewardsDuration; + uint32 maxRetroactiveLength; + uint32 maxFutureLength; + uint32 genesisRewardsTimestamp; + } + + struct ETHPOSDepositConfig { + address ethPOSDepositAddress; + } + + struct EigenPodConfig { + uint64 genesisTimestamp; + } + + struct DeploymentConfigData { + StrategyManagerConfig strategyManager; + DelegationManagerConfig delegationManager; + EigenPodManagerConfig eigenPodManager; + AllocationManagerConfig allocationManager; + StrategyFactoryConfig strategyFactory; + RewardsCoordinatorConfig rewardsCoordinator; + AVSDirectoryConfig avsDirectory; + ETHPOSDepositConfig ethPOSDeposit; + EigenPodConfig eigenPod; + } + + struct DeploymentData { + address delegationManager; + address avsDirectory; + address strategyManager; + address eigenPodManager; + address allocationManager; + address eigenPodBeacon; + address pauserRegistry; + address strategyFactory; + address strategyBeacon; + address rewardsCoordinator; + address permissionController; + } + + function deployContracts( + address proxyAdmin, + DeploymentConfigData memory configData + ) internal returns (DeploymentData memory result) { + result = deployEmptyProxies(proxyAdmin); + + deployAndConfigureCore(result, configData); + deployAndConfigurePods(result, configData); + deployAndConfigureStrategies(result, configData); + deployAndConfigureRewards(result, configData); + + return result; + } + + function deployEmptyProxies( + address proxyAdmin + ) internal returns (DeploymentData memory proxies) { + proxies.delegationManager = UpgradeableProxyLib.setUpEmptyProxy(proxyAdmin); + proxies.avsDirectory = UpgradeableProxyLib.setUpEmptyProxy(proxyAdmin); + proxies.strategyManager = UpgradeableProxyLib.setUpEmptyProxy(proxyAdmin); + proxies.eigenPodManager = UpgradeableProxyLib.setUpEmptyProxy(proxyAdmin); + proxies.allocationManager = UpgradeableProxyLib.setUpEmptyProxy(proxyAdmin); + proxies.eigenPodBeacon = UpgradeableProxyLib.setUpEmptyProxy(proxyAdmin); + proxies.pauserRegistry = UpgradeableProxyLib.setUpEmptyProxy(proxyAdmin); + proxies.strategyFactory = UpgradeableProxyLib.setUpEmptyProxy(proxyAdmin); + proxies.rewardsCoordinator = UpgradeableProxyLib.setUpEmptyProxy(proxyAdmin); + proxies.permissionController = UpgradeableProxyLib.setUpEmptyProxy(proxyAdmin); + return proxies; + } + + function deployAndConfigureCore( + DeploymentData memory deployments, + DeploymentConfigData memory config + ) internal { + // Deploy core implementations + address permissionControllerImpl = address(new PermissionController()); + + address strategyManagerImpl = address( + new StrategyManager( + IDelegationManager(deployments.delegationManager), + IPauserRegistry(deployments.pauserRegistry) + ) + ); + + address allocationManagerImpl = address( + new AllocationManager( + IDelegationManager(deployments.delegationManager), + IPauserRegistry(deployments.pauserRegistry), + IPermissionController(deployments.permissionController), + config.allocationManager.deallocationDelay, + config.allocationManager.allocationConfigurationDelay + ) + ); + + address delegationManagerImpl = address( + new DelegationManager( + IStrategyManager(deployments.strategyManager), + IEigenPodManager(deployments.eigenPodManager), + IAllocationManager(deployments.allocationManager), + IPauserRegistry(deployments.pauserRegistry), + IPermissionController(deployments.permissionController), + config.delegationManager.minWithdrawalDelayBlocks + ) + ); + + address avsDirectoryImpl = address( + new AVSDirectory( + IDelegationManager(deployments.delegationManager), + IPauserRegistry(deployments.pauserRegistry) + ) + ); + + // Initialize core contracts + UpgradeableProxyLib.upgrade(deployments.permissionController, permissionControllerImpl); + + bytes memory upgradeCall = abi.encodeCall( + StrategyManager.initialize, + ( + config.strategyManager.initialOwner, + config.strategyManager.initialStrategyWhitelister, + config.strategyManager.initPausedStatus + ) + ); + UpgradeableProxyLib.upgradeAndCall( + deployments.strategyManager, strategyManagerImpl, upgradeCall + ); + + upgradeCall = abi.encodeCall( + DelegationManager.initialize, + (config.delegationManager.initialOwner, config.delegationManager.initPausedStatus) + ); + UpgradeableProxyLib.upgradeAndCall( + deployments.delegationManager, delegationManagerImpl, upgradeCall + ); + + upgradeCall = abi.encodeCall( + AllocationManager.initialize, + (config.allocationManager.initialOwner, config.allocationManager.initPausedStatus) + ); + UpgradeableProxyLib.upgradeAndCall( + deployments.allocationManager, allocationManagerImpl, upgradeCall + ); + + upgradeCall = abi.encodeCall( + AVSDirectory.initialize, + (config.avsDirectory.initialOwner, config.avsDirectory.initPausedStatus) + ); + UpgradeableProxyLib.upgradeAndCall(deployments.avsDirectory, avsDirectoryImpl, upgradeCall); + } + + function deployAndConfigurePods( + DeploymentData memory deployments, + DeploymentConfigData memory config + ) internal { + address ethPOSDeposit = config.ethPOSDeposit.ethPOSDepositAddress; + if (ethPOSDeposit == address(0)) { + if (block.chainid == 1) { + ethPOSDeposit = 0x00000000219ab540356cBB839Cbe05303d7705Fa; + } else { + revert("DEPLOY_MOCK_ETHPOS_CONTRACT"); + } + } + + address eigenPodImpl = address( + new EigenPod( + IETHPOSDeposit(ethPOSDeposit), + IEigenPodManager(deployments.eigenPodManager), + config.eigenPod.genesisTimestamp == 0 + ? uint64(block.timestamp) + : config.eigenPod.genesisTimestamp + ) + ); + + address eigenPodBeaconImpl = address(new UpgradeableBeacon(eigenPodImpl)); + UpgradeableProxyLib.upgrade(deployments.eigenPodBeacon, eigenPodBeaconImpl); + + address eigenPodManagerImpl = address( + new EigenPodManager( + IETHPOSDeposit(ethPOSDeposit), + IBeacon(deployments.eigenPodBeacon), + IDelegationManager(deployments.delegationManager), + IPauserRegistry(deployments.pauserRegistry) + ) + ); + + bytes memory upgradeCall = abi.encodeCall( + EigenPodManager.initialize, + (config.eigenPodManager.initialOwner, config.eigenPodManager.initPausedStatus) + ); + UpgradeableProxyLib.upgradeAndCall( + deployments.eigenPodManager, eigenPodManagerImpl, upgradeCall + ); + } + + function deployAndConfigureStrategies( + DeploymentData memory deployments, + DeploymentConfigData memory config + ) internal { + address baseStrategyImpl = address( + new StrategyBase( + IStrategyManager(deployments.strategyManager), + IPauserRegistry(deployments.pauserRegistry) + ) + ); + + deployments.strategyBeacon = address(new UpgradeableBeacon(baseStrategyImpl)); + + address strategyFactoryImpl = address( + new StrategyFactory( + IStrategyManager(deployments.strategyManager), + IPauserRegistry(deployments.pauserRegistry) + ) + ); + + bytes memory upgradeCall = abi.encodeCall( + StrategyFactory.initialize, + ( + config.strategyFactory.initialOwner, + config.strategyFactory.initPausedStatus, + IBeacon(deployments.strategyBeacon) + ) + ); + UpgradeableProxyLib.upgradeAndCall( + deployments.strategyFactory, strategyFactoryImpl, upgradeCall + ); + } + + function deployAndConfigureRewards( + DeploymentData memory deployments, + DeploymentConfigData memory config + ) internal { + address rewardsCoordinatorImpl = address( + new RewardsCoordinator( + IDelegationManager(deployments.delegationManager), + IStrategyManager(deployments.strategyManager), + IAllocationManager(deployments.allocationManager), + IPauserRegistry(deployments.pauserRegistry), + IPermissionController(deployments.permissionController), + config.rewardsCoordinator.calculationIntervalSeconds, + config.rewardsCoordinator.maxRewardsDuration, + config.rewardsCoordinator.maxRetroactiveLength, + config.rewardsCoordinator.maxFutureLength, + config.rewardsCoordinator.genesisRewardsTimestamp + ) + ); + + bytes memory upgradeCall = abi.encodeCall( + RewardsCoordinator.initialize, + ( + config.rewardsCoordinator.initialOwner, + config.rewardsCoordinator.initPausedStatus, + config.rewardsCoordinator.rewardsUpdater, + config.rewardsCoordinator.activationDelay, + config.rewardsCoordinator.defaultSplitBips + ) + ); + + UpgradeableProxyLib.upgradeAndCall( + deployments.rewardsCoordinator, rewardsCoordinatorImpl, upgradeCall + ); + } +} diff --git a/test/utils/MockAVSDeployer.sol b/test/utils/MockAVSDeployer.sol index e527e0f8..1de4029b 100644 --- a/test/utils/MockAVSDeployer.sol +++ b/test/utils/MockAVSDeployer.sol @@ -53,6 +53,7 @@ import {BLSApkRegistryHarness} from "../harnesses/BLSApkRegistryHarness.sol"; import {EmptyContract} from "eigenlayer-contracts/src/test/mocks/EmptyContract.sol"; import {StakeRegistryHarness} from "../harnesses/StakeRegistryHarness.sol"; +import {OperatorWalletLib, Operator} from "../utils/OperatorWalletLib.sol"; import "forge-std/Test.sol"; @@ -547,4 +548,17 @@ contract MockAVSDeployer is Test { salt: salt }); } + + function _createOperators( + uint256 numOperators, + uint256 startIndex + ) internal returns (Operator[] memory) { + Operator[] memory operators = new Operator[](numOperators); + for (uint256 i = 0; i < numOperators; i++) { + operators[i] = OperatorWalletLib.createOperator( + string(abi.encodePacked("operator-", i + startIndex)) + ); + } + return operators; + } } diff --git a/test/utils/OperatorWalletLib.sol b/test/utils/OperatorWalletLib.sol new file mode 100644 index 00000000..d46127fc --- /dev/null +++ b/test/utils/OperatorWalletLib.sol @@ -0,0 +1,119 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {Vm} from "forge-std/Vm.sol"; +import {BN254} from "src/libraries/BN254.sol"; +import {BN256G2} from "./BN256G2.sol"; +import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; + +struct Wallet { + uint256 privateKey; + address addr; +} + +struct BLSWallet { + uint256 privateKey; + BN254.G2Point publicKeyG2; + BN254.G1Point publicKeyG1; +} + +struct Operator { + Wallet key; + BLSWallet signingKey; +} + +library OperatorKeyOperationsLib { + Vm private constant vm = Vm(address(uint160(uint256(keccak256("hevm cheat code"))))); + + function sign(Wallet memory wallet, bytes32 digest) internal pure returns (bytes memory) { + (uint8 v, bytes32 r, bytes32 s) = vm.sign(wallet.privateKey, digest); + return abi.encodePacked(r, s, v); + } +} + +library SigningKeyOperationsLib { + using BN254 for BN254.G1Point; + + function sign( + BLSWallet memory blsWallet, + bytes32 messageHash + ) internal view returns (BN254.G1Point memory) { + // Hash the message to a point on G1 + BN254.G1Point memory messagePoint = BN254.hashToG1(messageHash); + + // Sign by multiplying the hashed message point with the private key + return messagePoint.scalar_mul(blsWallet.privateKey); + } + + function aggregate( + BN254.G2Point memory pk1, + BN254.G2Point memory pk2 + ) internal view returns (BN254.G2Point memory apk) { + (apk.X[0], apk.X[1], apk.Y[0], apk.Y[1]) = BN256G2.ECTwistAdd( + pk1.X[0], pk1.X[1], pk1.Y[0], pk1.Y[1], pk2.X[0], pk2.X[1], pk2.Y[0], pk2.Y[1] + ); + } +} + +library OperatorWalletLib { + using BN254 for *; + using Strings for uint256; + + Vm private constant vm = Vm(address(uint160(uint256(keccak256("hevm cheat code"))))); + + function createBLSWallet( + uint256 salt + ) internal returns (BLSWallet memory) { + uint256 privateKey = uint256(keccak256(abi.encodePacked(salt))); + BN254.G1Point memory publicKeyG1 = BN254.generatorG1().scalar_mul(privateKey); + BN254.G2Point memory publicKeyG2 = mul(privateKey); + + return + BLSWallet({privateKey: privateKey, publicKeyG2: publicKeyG2, publicKeyG1: publicKeyG1}); + } + + function createWallet( + uint256 salt + ) internal pure returns (Wallet memory) { + uint256 privateKey = uint256(keccak256(abi.encodePacked(salt))); + address addr = vm.addr(privateKey); + + return Wallet({privateKey: privateKey, addr: addr}); + } + + function createOperator( + string memory name + ) internal returns (Operator memory) { + uint256 salt = uint256(keccak256(abi.encodePacked(name))); + Wallet memory vmWallet = createWallet(salt); + BLSWallet memory blsWallet = createBLSWallet(salt); + + return Operator({key: vmWallet, signingKey: blsWallet}); + } + + function mul( + uint256 x + ) internal returns (BN254.G2Point memory g2Point) { + string[] memory inputs = new string[](5); + inputs[0] = "go"; + inputs[1] = "run"; + inputs[2] = "test/ffi/go/g2mul.go"; + inputs[3] = x.toString(); + + inputs[4] = "1"; + bytes memory res = vm.ffi(inputs); + g2Point.X[1] = abi.decode(res, (uint256)); + + inputs[4] = "2"; + res = vm.ffi(inputs); + g2Point.X[0] = abi.decode(res, (uint256)); + + inputs[4] = "3"; + res = vm.ffi(inputs); + g2Point.Y[1] = abi.decode(res, (uint256)); + + inputs[4] = "4"; + res = vm.ffi(inputs); + g2Point.Y[0] = abi.decode(res, (uint256)); + } +}