From 62ba4b5a9dfdef80c8acd2ab6cae2f6a74d967ca Mon Sep 17 00:00:00 2001 From: steven <12021290+stevennevins@users.noreply.github.com> Date: Tue, 24 Oct 2023 10:25:11 -0400 Subject: [PATCH] Remove Middleware from Core (#260) * remove middleware from core * remove go files from ffi * use stub * delete Prover rules for `BLSRegistryCoordinatorWithIndices` (#261) these are getting migrated to a separate repo * add weird fix * move stub to mocks * remove additional interfaces from imports * port over alexs interface change * add back whitelister * add back script per PR comment --------- Co-authored-by: steven Co-authored-by: ChaoticWalrus <93558947+ChaoticWalrus@users.noreply.github.com> --- ...SRegistryCoordinatorWithIndicesHarness.sol | 54 - ...verifyBLSRegistryCoordinatorWithIndices.sh | 21 - .../BLSRegistryCoordinatorWithIndices.spec | 169 ---- go.mod | 12 - go.sum | 14 - script/AVSContractsDeploy.s.sol | 769 -------------- script/M1_Deploy.s.sol | 293 ++++-- script/middleware/DeployOpenEigenLayer.s.sol | 55 +- script/milestone/M2Deploy.sol | 130 +++ script/testing/M2_Deploy_From_Scratch.s.sol | 300 ++++-- .../interfaces/IBLSPubkeyRegistry.sol | 85 -- .../IBLSRegistryCoordinatorWithIndices.sol | 69 -- .../interfaces/IBLSSignatureChecker.sol | 74 -- src/contracts/interfaces/ISocketUpdater.sol | 10 +- src/contracts/interfaces/IVoteWeigher.sol | 26 +- src/contracts/interfaces/IWhitelister.sol | 1 - .../middleware/BLSOperatorStateRetriever.sol | 149 --- .../middleware/BLSPubkeyRegistry.sol | 233 ----- .../middleware/BLSPubkeyRegistryStorage.sol | 32 - .../middleware/BLSPublicKeyCompendium.sol | 78 -- .../BLSRegistryCoordinatorWithIndices.sol | 620 ------------ .../middleware/BLSSignatureChecker.sol | 197 ---- src/contracts/middleware/IndexRegistry.sol | 339 ------- .../middleware/IndexRegistryStorage.sol | 40 - .../middleware/ServiceManagerBase.sol | 139 --- src/contracts/middleware/StakeRegistry.sol | 580 ----------- .../middleware/StakeRegistryStorage.sol | 41 - src/contracts/middleware/VoteWeigherBase.sol | 220 ----- .../middleware/VoteWeigherBaseStorage.sol | 63 -- src/test/Delegation.t.sol | 292 +++--- src/test/EigenLayerDeployer.t.sol | 93 +- src/test/EigenLayerTestHelper.t.sol | 149 +-- src/test/EigenPod.t.sol | 901 +++++++++++------ src/test/Withdrawals.t.sol | 187 ++-- src/test/ffi/BLSPubKeyCompendiumFFI.t.sol | 46 - src/test/ffi/BLSSignatureCheckerFFI.t.sol | 183 ---- src/test/ffi/go/g2mul.go | 62 -- src/test/ffi/util/G2Operations.sol | 35 - ...SRegistryCoordinatorWithIndicesHarness.sol | 33 - src/test/harnesses/StakeRegistryHarness.sol | 43 - src/test/mocks/BLSPublicKeyCompendiumMock.sol | 46 - src/test/mocks/StakeRegistryStub.sol | 6 + .../unit/BLSOperatorStateRetrieverUnit.t.sol | 192 ---- src/test/unit/BLSPubkeyRegistryUnit.t.sol | 254 ----- .../unit/BLSPublicKeyCompendiumUnit.t.sol | 80 -- ...LSRegistryCoordinatorWithIndicesUnit.t.sol | 935 ------------------ src/test/unit/BLSSignatureCheckerUnit.t.sol | 240 ----- src/test/unit/IndexRegistryUnit.t.sol | 807 --------------- src/test/unit/StakeRegistryUnit.t.sol | 611 ------------ src/test/unit/VoteWeigherBaseUnit.t.sol | 598 ----------- src/test/utils/BLSMockAVSDeployer.sol | 130 --- src/test/utils/MockAVSDeployer.sol | 390 -------- 52 files changed, 1452 insertions(+), 9674 deletions(-) delete mode 100644 certora/harnesses/BLSRegistryCoordinatorWithIndicesHarness.sol delete mode 100644 certora/scripts/middleware/verifyBLSRegistryCoordinatorWithIndices.sh delete mode 100644 certora/specs/middleware/BLSRegistryCoordinatorWithIndices.spec delete mode 100644 go.mod delete mode 100644 go.sum delete mode 100644 script/AVSContractsDeploy.s.sol create mode 100644 script/milestone/M2Deploy.sol delete mode 100644 src/contracts/interfaces/IBLSPubkeyRegistry.sol delete mode 100644 src/contracts/interfaces/IBLSRegistryCoordinatorWithIndices.sol delete mode 100644 src/contracts/interfaces/IBLSSignatureChecker.sol delete mode 100644 src/contracts/middleware/BLSOperatorStateRetriever.sol delete mode 100644 src/contracts/middleware/BLSPubkeyRegistry.sol delete mode 100644 src/contracts/middleware/BLSPubkeyRegistryStorage.sol delete mode 100644 src/contracts/middleware/BLSPublicKeyCompendium.sol delete mode 100644 src/contracts/middleware/BLSRegistryCoordinatorWithIndices.sol delete mode 100644 src/contracts/middleware/BLSSignatureChecker.sol delete mode 100644 src/contracts/middleware/IndexRegistry.sol delete mode 100644 src/contracts/middleware/IndexRegistryStorage.sol delete mode 100644 src/contracts/middleware/ServiceManagerBase.sol delete mode 100644 src/contracts/middleware/StakeRegistry.sol delete mode 100644 src/contracts/middleware/StakeRegistryStorage.sol delete mode 100644 src/contracts/middleware/VoteWeigherBase.sol delete mode 100644 src/contracts/middleware/VoteWeigherBaseStorage.sol delete mode 100644 src/test/ffi/BLSPubKeyCompendiumFFI.t.sol delete mode 100644 src/test/ffi/BLSSignatureCheckerFFI.t.sol delete mode 100644 src/test/ffi/go/g2mul.go delete mode 100644 src/test/ffi/util/G2Operations.sol delete mode 100644 src/test/harnesses/BLSRegistryCoordinatorWithIndicesHarness.sol delete mode 100644 src/test/harnesses/StakeRegistryHarness.sol delete mode 100644 src/test/mocks/BLSPublicKeyCompendiumMock.sol create mode 100644 src/test/mocks/StakeRegistryStub.sol delete mode 100644 src/test/unit/BLSOperatorStateRetrieverUnit.t.sol delete mode 100644 src/test/unit/BLSPubkeyRegistryUnit.t.sol delete mode 100644 src/test/unit/BLSPublicKeyCompendiumUnit.t.sol delete mode 100644 src/test/unit/BLSRegistryCoordinatorWithIndicesUnit.t.sol delete mode 100644 src/test/unit/BLSSignatureCheckerUnit.t.sol delete mode 100644 src/test/unit/IndexRegistryUnit.t.sol delete mode 100644 src/test/unit/StakeRegistryUnit.t.sol delete mode 100644 src/test/unit/VoteWeigherBaseUnit.t.sol delete mode 100644 src/test/utils/BLSMockAVSDeployer.sol delete mode 100644 src/test/utils/MockAVSDeployer.sol diff --git a/certora/harnesses/BLSRegistryCoordinatorWithIndicesHarness.sol b/certora/harnesses/BLSRegistryCoordinatorWithIndicesHarness.sol deleted file mode 100644 index 70a4686f1..000000000 --- a/certora/harnesses/BLSRegistryCoordinatorWithIndicesHarness.sol +++ /dev/null @@ -1,54 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity =0.8.12; - -import "../munged/middleware/BLSRegistryCoordinatorWithIndices.sol"; - -contract BLSRegistryCoordinatorWithIndicesHarness is BLSRegistryCoordinatorWithIndices { - constructor( - ISlasher _slasher, - IServiceManager _serviceManager, - IStakeRegistry _stakeRegistry, - IBLSPubkeyRegistry _blsPubkeyRegistry, - IIndexRegistry _indexRegistry - ) - BLSRegistryCoordinatorWithIndices(_slasher, _serviceManager, _stakeRegistry, _blsPubkeyRegistry, _indexRegistry) - {} - - // @notice function based upon `BitmapUtils.bytesArrayToBitmap`, used to determine if an array contains any duplicates - function bytesArrayContainsDuplicates(bytes memory bytesArray) public pure returns (bool) { - // sanity-check on input. a too-long input would fail later on due to having duplicate entry(s) - if (bytesArray.length > 256) { - return false; - } - - // initialize the empty bitmap, to be built inside the loop - uint256 bitmap; - // initialize an empty uint256 to be used as a bitmask inside the loop - uint256 bitMask; - - // loop through each byte in the array to construct the bitmap - for (uint256 i = 0; i < bytesArray.length; ++i) { - // construct a single-bit mask from the numerical value of the next byte out of the array - bitMask = uint256(1 << uint8(bytesArray[i])); - // check that the entry is not a repeat - if (bitmap & bitMask != 0) { - return false; - } - // add the entry to the bitmap - bitmap = (bitmap | bitMask); - } - - // if the loop is completed without returning early, then the array contains no duplicates - return true; - } - - // @notice verifies that a bytes array is a (non-strict) subset of a bitmap - function bytesArrayIsSubsetOfBitmap(uint256 referenceBitmap, bytes memory arrayWhichShouldBeASubsetOfTheReference) public pure returns (bool) { - uint256 arrayWhichShouldBeASubsetOfTheReferenceBitmap = BitmapUtils.bytesArrayToBitmap(arrayWhichShouldBeASubsetOfTheReference); - if (referenceBitmap | arrayWhichShouldBeASubsetOfTheReferenceBitmap == referenceBitmap) { - return true; - } else { - return false; - } - } -} \ No newline at end of file diff --git a/certora/scripts/middleware/verifyBLSRegistryCoordinatorWithIndices.sh b/certora/scripts/middleware/verifyBLSRegistryCoordinatorWithIndices.sh deleted file mode 100644 index 42bd5332b..000000000 --- a/certora/scripts/middleware/verifyBLSRegistryCoordinatorWithIndices.sh +++ /dev/null @@ -1,21 +0,0 @@ -if [[ "$2" ]] -then - RULE="--rule $2" -fi - -solc-select use 0.8.12 - -certoraRun certora/harnesses/BLSRegistryCoordinatorWithIndicesHarness.sol \ - lib/openzeppelin-contracts/contracts/mocks/ERC1271WalletMock.sol \ - certora/munged/middleware/StakeRegistry.sol certora/munged/middleware/BLSPubkeyRegistry.sol certora/munged/middleware/IndexRegistry.sol \ - certora/munged/core/Slasher.sol \ - --verify BLSRegistryCoordinatorWithIndicesHarness:certora/specs/middleware/BLSRegistryCoordinatorWithIndices.spec \ - --optimistic_loop \ - --optimistic_hashing \ - --prover_args '-optimisticFallback true -recursionEntryLimit 2 ' \ - $RULE \ - --loop_iter 2 \ - --packages @openzeppelin=lib/openzeppelin-contracts @openzeppelin-upgrades=lib/openzeppelin-contracts-upgradeable \ - --msg "BLSRegistryCoordinatorWithIndices $1 $2" \ - -# TODO: import a ServiceManager contract \ No newline at end of file diff --git a/certora/specs/middleware/BLSRegistryCoordinatorWithIndices.spec b/certora/specs/middleware/BLSRegistryCoordinatorWithIndices.spec deleted file mode 100644 index 2ca61c0c0..000000000 --- a/certora/specs/middleware/BLSRegistryCoordinatorWithIndices.spec +++ /dev/null @@ -1,169 +0,0 @@ - -methods { - //// External Calls - // external calls to StakeRegistry - function _.quorumCount() external => DISPATCHER(true); - function _.getCurrentTotalStakeForQuorum(uint8 quorumNumber) external => DISPATCHER(true); - function _.getCurrentOperatorStakeForQuorum(bytes32 operatorId, uint8 quorumNumber) external => DISPATCHER(true); - function _.registerOperator(address, bytes32, bytes) external => DISPATCHER(true); - function _.deregisterOperator(bytes32, bytes) external => DISPATCHER(true); - - // external calls to Slasher - function _.contractCanSlashOperatorUntilBlock(address, address) external => DISPATCHER(true); - - // external calls to BLSPubkeyRegistry - function _.registerOperator(address, bytes, BN254.G1Point) external => DISPATCHER(true); - function _.deregisterOperator(address, bytes, BN254.G1Point) external => DISPATCHER(true); - - // external calls to ServiceManager - function _.latestServeUntilBlock() external => DISPATCHER(true); - function _.recordLastStakeUpdateAndRevokeSlashingAbility(address, uint256) external => DISPATCHER(true); - - // external calls to IndexRegistry - function _.registerOperator(bytes32, bytes) external => DISPATCHER(true); - function _.deregisterOperator(bytes32, bytes, bytes32[]) external => DISPATCHER(true); - - // external calls to ERC1271 (can import OpenZeppelin mock implementation) - // isValidSignature(bytes32 hash, bytes memory signature) returns (bytes4 magicValue) => DISPATCHER(true) - function _.isValidSignature(bytes32, bytes) external => DISPATCHER(true); - - // external calls to BLSPubkeyCompendium - function _.pubkeyHashToOperator(bytes32) external => DISPATCHER(true); - - //envfree functions - function OPERATOR_CHURN_APPROVAL_TYPEHASH() external returns (bytes32) envfree; - function slasher() external returns (address) envfree; - function serviceManager() external returns (address) envfree; - function blsPubkeyRegistry() external returns (address) envfree; - function stakeRegistry() external returns (address) envfree; - function indexRegistry() external returns (address) envfree; - function registries(uint256) external returns (address) envfree; - function churnApprover() external returns (address) envfree; - function isChurnApproverSaltUsed(bytes32) external returns (bool) envfree; - function getOperatorSetParams(uint8 quorumNumber) external returns (IBLSRegistryCoordinatorWithIndices.OperatorSetParam) envfree; - function getOperator(address operator) external returns (IRegistryCoordinator.Operator) envfree; - function getOperatorId(address operator) external returns (bytes32) envfree; - function getOperatorStatus(address operator) external returns (IRegistryCoordinator.OperatorStatus) envfree; - function getQuorumBitmapIndicesByOperatorIdsAtBlockNumber(uint32 blockNumber, bytes32[] operatorIds) - external returns (uint32[]) envfree; - function getQuorumBitmapByOperatorIdAtBlockNumberByIndex(bytes32 operatorId, uint32 blockNumber, uint256 index) external returns (uint192) envfree; - function getQuorumBitmapUpdateByOperatorIdByIndex(bytes32 operatorId, uint256 index) - external returns (IRegistryCoordinator.QuorumBitmapUpdate) envfree; - function getCurrentQuorumBitmapByOperatorId(bytes32 operatorId) external returns (uint192) envfree; - function getQuorumBitmapUpdateByOperatorIdLength(bytes32 operatorId) external returns (uint256) envfree; - function numRegistries() external returns (uint256) envfree; - function calculateOperatorChurnApprovalDigestHash( - bytes32 registeringOperatorId, - IBLSRegistryCoordinatorWithIndices.OperatorKickParam[] operatorKickParams, - bytes32 salt, - uint256 expiry - ) external returns (bytes32) envfree; - - // harnessed functions - function bytesArrayContainsDuplicates(bytes bytesArray) external returns (bool) envfree; - function bytesArrayIsSubsetOfBitmap(uint256 referenceBitmap, bytes arrayWhichShouldBeASubsetOfTheReference) external returns (bool) envfree; -} - -// If my Operator status is REGISTERED ⇔ my quorum bitmap MUST BE nonzero -invariant registeredOperatorsHaveNonzeroBitmaps(address operator) - getOperatorStatus(operator) == IRegistryCoordinator.OperatorStatus.REGISTERED <=> - getCurrentQuorumBitmapByOperatorId(getOperatorId(operator)) != 0; - -// if two operators have different addresses, then they have different IDs -// excludes the case in which the operator is not registered, since then they can both have ID zero (the default) -invariant operatorIdIsUnique(address operator1, address operator2) - operator1 != operator2 => - (getOperatorStatus(operator1) == IRegistryCoordinator.OperatorStatus.REGISTERED => getOperatorId(operator1) != getOperatorId(operator2)); - -definition methodCanModifyBitmap(method f) returns bool = - f.selector == sig:registerOperatorWithCoordinator(bytes, bytes).selector - || f.selector == sig:registerOperatorWithCoordinator(bytes, BN254.G1Point, string).selector - || f.selector == sig:registerOperatorWithCoordinator( - bytes, - BN254.G1Point, - string, - IBLSRegistryCoordinatorWithIndices.OperatorKickParam[], - ISignatureUtils.SignatureWithSaltAndExpiry - ).selector - || f.selector == sig:deregisterOperatorWithCoordinator(bytes, bytes).selector - || f.selector == sig:deregisterOperatorWithCoordinator(bytes, BN254.G1Point, bytes32[]).selector; - -definition methodCanAddToBitmap(method f) returns bool = - f.selector == sig:registerOperatorWithCoordinator(bytes, bytes).selector - || f.selector == sig:registerOperatorWithCoordinator(bytes, BN254.G1Point, string).selector - || f.selector == sig:registerOperatorWithCoordinator( - bytes, - BN254.G1Point, - string, - IBLSRegistryCoordinatorWithIndices.OperatorKickParam[], - ISignatureUtils.SignatureWithSaltAndExpiry - ).selector; - -// `registerOperatorWithCoordinator` with kick params also meets this definition due to the 'churn' mechanism -definition methodCanRemoveFromBitmap(method f) returns bool = - f.selector == sig:registerOperatorWithCoordinator( - bytes, - BN254.G1Point, - string, - IBLSRegistryCoordinatorWithIndices.OperatorKickParam[], - ISignatureUtils.SignatureWithSaltAndExpiry - ).selector - || f.selector == sig:deregisterOperatorWithCoordinator(bytes, bytes).selector - || f.selector == sig:deregisterOperatorWithCoordinator(bytes, BN254.G1Point, bytes32[]).selector; - -// verify that quorumNumbers provided as an input to deregister operator MUST BE a subset of the operator’s current quorums -rule canOnlyDeregisterFromExistingQuorums(address operator) { - requireInvariant registeredOperatorsHaveNonzeroBitmaps(operator); - - // TODO: store this status, verify that all calls to `deregisterOperatorWithCoordinator` *fail* if the operator is not registered first! - require(getOperatorStatus(operator) == IRegistryCoordinator.OperatorStatus.REGISTERED); - - uint256 bitmapBefore = getCurrentQuorumBitmapByOperatorId(getOperatorId(operator)); - - bytes quorumNumbers; - BN254.G1Point pubkey; - bytes32[] operatorIdsToSwap; - env e; - - deregisterOperatorWithCoordinator(e, quorumNumbers, pubkey, operatorIdsToSwap); - - // if deregistration is successful, verify that `quorumNumbers` input was proper - if (getOperatorStatus(operator) != IRegistryCoordinator.OperatorStatus.REGISTERED) { - assert(bytesArrayIsSubsetOfBitmap(bitmapBefore, quorumNumbers)); - } else { - assert(true); - } -} - -/* TODO: this is a Work In Progress -rule canOnlyModifyBitmapWithSpecificFunctions(address operator) { - requireInvariant registeredOperatorsHaveNonzeroBitmaps(operator); - uint256 bitmapBefore = getCurrentQuorumBitmapByOperatorId(getOperatorId(operator)); - // prepare to perform arbitrary function call - method f; - env e; - // TODO: need to ensure that if the function can modify the bitmap, then we are using the operator as an input - if (!methodCanModifyBitmap(f)) { - // perform arbitrary function call - calldataarg arg; - f(e, arg); - uint256 bitmapAfter = getCurrentQuorumBitmapByOperatorId(getOperatorId(operator)); - assert(bitmapAfter == bitmapBefore); - } else if ( - f.selector == sig:registerOperatorWithCoordinator(bytes, bytes).selector - ) { - if (e.msg.sender != operator) { - uint256 bitmapAfter = getCurrentQuorumBitmapByOperatorId(getOperatorId(operator)); - assert(bitmapAfter == bitmapBefore); - } - } - - // if method did not remove from bitmap, it must have added - if (bitmapAfter & bitmapBefore == bitmapBefore) { - assert(methodCanAddToBitmap(f)); - } else { - assert(methodCanRemoveFromBitmap(f)); - } - } -} -*/ \ No newline at end of file diff --git a/go.mod b/go.mod deleted file mode 100644 index e3390d9c4..000000000 --- a/go.mod +++ /dev/null @@ -1,12 +0,0 @@ -module github.com/Layr-Labs/ffi - -go 1.20 - -require ( - github.com/bits-and-blooms/bitset v1.5.0 // indirect - github.com/consensys/bavard v0.1.13 // indirect - github.com/consensys/gnark-crypto v0.11.1 // indirect - github.com/mmcloughlin/addchain v0.4.0 // indirect - golang.org/x/sys v0.2.0 // indirect - rsc.io/tmplfunc v0.0.3 // indirect -) diff --git a/go.sum b/go.sum deleted file mode 100644 index 0bc3f8d7a..000000000 --- a/go.sum +++ /dev/null @@ -1,14 +0,0 @@ -github.com/bits-and-blooms/bitset v1.5.0 h1:NpE8frKRLGHIcEzkR+gZhiioW1+WbYV6fKwD6ZIpQT8= -github.com/bits-and-blooms/bitset v1.5.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= -github.com/consensys/bavard v0.1.13 h1:oLhMLOFGTLdlda/kma4VOJazblc7IM5y5QPd2A/YjhQ= -github.com/consensys/bavard v0.1.13/go.mod h1:9ItSMtA/dXMAiL7BG6bqW2m3NdSEObYWoH223nGHukI= -github.com/consensys/gnark-crypto v0.11.1 h1:pt2nLbntYZA5IXnSw21vcQgoUCRPn6J/xylWQpK8gtM= -github.com/consensys/gnark-crypto v0.11.1/go.mod h1:Iq/P3HHl0ElSjsg2E1gsMwhAyxnxoKK5nVyZKd+/KhU= -github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= -github.com/mmcloughlin/addchain v0.4.0 h1:SobOdjm2xLj1KkXN5/n0xTIWyZA2+s99UCY1iPfkHRY= -github.com/mmcloughlin/addchain v0.4.0/go.mod h1:A86O+tHqZLMNO4w6ZZ4FlVQEadcoqkyU72HC5wJ4RlU= -github.com/mmcloughlin/profile v0.1.1/go.mod h1:IhHD7q1ooxgwTgjxQYkACGA77oFTDdFVejUS1/tS/qU= -golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A= -golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -rsc.io/tmplfunc v0.0.3 h1:53XFQh69AfOa8Tw0Jm7t+GV7KZhOi6jzsCzTtKbMvzU= -rsc.io/tmplfunc v0.0.3/go.mod h1:AG3sTPzElb1Io3Yg4voV9AGZJuleGAwaVRxL9M49PhA= diff --git a/script/AVSContractsDeploy.s.sol b/script/AVSContractsDeploy.s.sol deleted file mode 100644 index 17bf69804..000000000 --- a/script/AVSContractsDeploy.s.sol +++ /dev/null @@ -1,769 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity =0.8.12; - -import "@openzeppelin/contracts/token/ERC20/presets/ERC20PresetFixedSupply.sol"; -import "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; -import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; -import "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol"; - -import "../src/contracts/interfaces/IETHPOSDeposit.sol"; -import "../src/contracts/interfaces/IBeaconChainOracle.sol"; - -import "../src/contracts/core/StrategyManager.sol"; -import "../src/contracts/core/Slasher.sol"; -import "../src/contracts/core/DelegationManager.sol"; - -import "../src/contracts/strategies/StrategyBaseTVLLimits.sol"; - -import "../src/contracts/pods/EigenPod.sol"; -import "../src/contracts/pods/EigenPodManager.sol"; -import "../src/contracts/pods/DelayedWithdrawalRouter.sol"; - -import "../src/contracts/permissions/PauserRegistry.sol"; -import "../src/contracts/middleware/BLSPublicKeyCompendium.sol"; - -import "../src/test/mocks/EmptyContract.sol"; -import "../src/test/mocks/ETHDepositMock.sol"; -import "../src/test/mocks/ERC20Mock.sol"; - -import "forge-std/Script.sol"; -import "forge-std/Test.sol"; - -// # To load the variables in the .env file -// source .env - -// # To deploy and verify our contract -// forge script script/EigenLayerDeploy.s.sol --rpc-url $RPC_URL --private-key $PRIVATE_KEY --broadcast -vvvv -contract EigenLayerDeploy is Script, Test { - Vm cheats = Vm(HEVM_ADDRESS); - - // struct used to encode token info in config file - struct StrategyConfig { - uint256 maxDeposits; - uint256 maxPerDeposit; - address tokenAddress; - string tokenSymbol; - } - - // EigenLayer Contracts - ProxyAdmin public eigenLayerProxyAdmin; - PauserRegistry public eigenLayerPauserReg; - Slasher public slasher; - Slasher public slasherImplementation; - DelegationManager public delegation; - DelegationManager public delegationImplementation; - StrategyManager public strategyManager; - StrategyManager public strategyManagerImplementation; - EigenPodManager public eigenPodManager; - EigenPodManager public eigenPodManagerImplementation; - DelayedWithdrawalRouter public delayedWithdrawalRouter; - DelayedWithdrawalRouter public delayedWithdrawalRouterImplementation; - UpgradeableBeacon public eigenPodBeacon; - EigenPod public eigenPodImplementation; - StrategyBase public ERC20MockStrategy; - StrategyBase public ERC20MockStrategyImplementation; - - EmptyContract public emptyContract; - - address alphaMultisig; - - // the ETH2 deposit contract -- if not on mainnet, we deploy a mock as stand-in - IETHPOSDeposit public ethPOSDeposit; - - // strategies deployed - - // IMMUTABLES TO SET - uint256 REQUIRED_BALANCE_WEI; - - // OTHER DEPLOYMENT PARAMETERS - uint256 STRATEGY_MANAGER_INIT_PAUSED_STATUS; - uint256 SLASHER_INIT_PAUSED_STATUS; - uint256 DELEGATION_INIT_PAUSED_STATUS; - uint256 EIGENPOD_MANAGER_INIT_PAUSED_STATUS; - uint256 EIGENPOD_MANAGER_MAX_PODS; - uint256 DELAYED_WITHDRAWAL_ROUTER_INIT_PAUSED_STATUS; - - // one week in blocks -- 50400 - uint32 STRATEGY_MANAGER_INIT_WITHDRAWAL_DELAY_BLOCKS; - uint32 DELAYED_WITHDRAWAL_ROUTER_INIT_WITHDRAWAL_DELAY_BLOCKS; - string public deployConfigPath = string(bytes("script/configs/AVSContractsDeploy.json")); - - function run() external { - // read and log the chainID - uint256 chainId = block.chainid; - emit log_named_uint("You are deploying on ChainID", chainId); - - // READ JSON CONFIG DATA - string memory config_data = vm.readFile(deployConfigPath); - - STRATEGY_MANAGER_INIT_PAUSED_STATUS = stdJson.readUint( - config_data, - ".strategyManager.init_paused_status" - ); - SLASHER_INIT_PAUSED_STATUS = stdJson.readUint( - config_data, - ".slasher.init_paused_status" - ); - DELEGATION_INIT_PAUSED_STATUS = stdJson.readUint( - config_data, - ".delegation.init_paused_status" - ); - EIGENPOD_MANAGER_MAX_PODS = stdJson.readUint( - config_data, - ".eigenPodManager.max_pods" - ); - EIGENPOD_MANAGER_INIT_PAUSED_STATUS = stdJson.readUint( - config_data, - ".eigenPodManager.init_paused_status" - ); - DELAYED_WITHDRAWAL_ROUTER_INIT_PAUSED_STATUS = stdJson.readUint( - config_data, - ".delayedWithdrawalRouter.init_paused_status" - ); - - STRATEGY_MANAGER_INIT_WITHDRAWAL_DELAY_BLOCKS = uint32( - stdJson.readUint( - config_data, - ".strategyManager.init_withdrawal_delay_blocks" - ) - ); - DELAYED_WITHDRAWAL_ROUTER_INIT_WITHDRAWAL_DELAY_BLOCKS = uint32( - stdJson.readUint( - config_data, - ".strategyManager.init_withdrawal_delay_blocks" - ) - ); - - REQUIRED_BALANCE_WEI = stdJson.readUint( - config_data, - ".eigenPod.REQUIRED_BALANCE_WEI" - ); - - - alphaMultisig = stdJson.readAddress( - config_data, - ".multisig_addresses.alphaMultisig" - ); - - require( - alphaMultisig != address(0), - "alphaMultisig address not configured correctly!" - ); - - // START RECORDING TRANSACTIONS FOR DEPLOYMENT - vm.startBroadcast(); - - // deploy proxy admin for ability to upgrade proxy contracts - eigenLayerProxyAdmin = new ProxyAdmin(); - - //deploy pauser registry - { - address[] memory pausers = new address[](1); - pausers[0] = alphaMultisig; - eigenLayerPauserReg = new PauserRegistry(pausers, alphaMultisig); - } - - /** - * 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. - */ - emptyContract = new EmptyContract(); - delegation = DelegationManager( - address( - new TransparentUpgradeableProxy( - address(emptyContract), - address(eigenLayerProxyAdmin), - "" - ) - ) - ); - strategyManager = StrategyManager( - address( - new TransparentUpgradeableProxy( - address(emptyContract), - address(eigenLayerProxyAdmin), - "" - ) - ) - ); - slasher = Slasher( - address( - new TransparentUpgradeableProxy( - address(emptyContract), - address(eigenLayerProxyAdmin), - "" - ) - ) - ); - eigenPodManager = EigenPodManager( - address( - new TransparentUpgradeableProxy( - address(emptyContract), - address(eigenLayerProxyAdmin), - "" - ) - ) - ); - delayedWithdrawalRouter = DelayedWithdrawalRouter( - address( - new TransparentUpgradeableProxy( - address(emptyContract), - address(eigenLayerProxyAdmin), - "" - ) - ) - ); - - // if on mainnet, use the ETH2 deposit contract address - if (chainId == 1) { - ethPOSDeposit = IETHPOSDeposit( - 0x00000000219ab540356cBB839Cbe05303d7705Fa - ); - // if not on mainnet, deploy a mock - } else { - ethPOSDeposit = IETHPOSDeposit( - stdJson.readAddress(config_data, ".ethPOSDepositAddress") - ); - } - eigenPodImplementation = new EigenPod( - ethPOSDeposit, - delayedWithdrawalRouter, - eigenPodManager, - uint64(REQUIRED_BALANCE_WEI), - uint64(REQUIRED_BALANCE_WEI), - 1000 // temp genesis time, TODO: set if needed - ); - - eigenPodBeacon = new UpgradeableBeacon(address(eigenPodImplementation)); - - // Second, deploy the *implementation* contracts, using the *proxy contracts* as inputs - delegationImplementation = new DelegationManager( - strategyManager, - slasher, - eigenPodManager - ); - strategyManagerImplementation = new StrategyManager( - delegation, - eigenPodManager, - slasher - ); - slasherImplementation = new Slasher(strategyManager, delegation); - eigenPodManagerImplementation = new EigenPodManager( - ethPOSDeposit, - eigenPodBeacon, - strategyManager, - slasher, - delegation - ); - delayedWithdrawalRouterImplementation = new DelayedWithdrawalRouter( - eigenPodManager - ); - - // Third, upgrade the proxy contracts to use the correct implementation contracts and initialize them. - eigenLayerProxyAdmin.upgradeAndCall( - TransparentUpgradeableProxy(payable(address(delegation))), - address(delegationImplementation), - abi.encodeWithSelector( - DelegationManager.initialize.selector, - alphaMultisig, - eigenLayerPauserReg, - DELEGATION_INIT_PAUSED_STATUS - ) - ); - eigenLayerProxyAdmin.upgradeAndCall( - TransparentUpgradeableProxy(payable(address(strategyManager))), - address(strategyManagerImplementation), - abi.encodeWithSelector( - StrategyManager.initialize.selector, - alphaMultisig, - alphaMultisig, - eigenLayerPauserReg, - STRATEGY_MANAGER_INIT_PAUSED_STATUS, - STRATEGY_MANAGER_INIT_WITHDRAWAL_DELAY_BLOCKS - ) - ); - eigenLayerProxyAdmin.upgradeAndCall( - TransparentUpgradeableProxy(payable(address(slasher))), - address(slasherImplementation), - abi.encodeWithSelector( - Slasher.initialize.selector, - alphaMultisig, - eigenLayerPauserReg, - SLASHER_INIT_PAUSED_STATUS - ) - ); - eigenLayerProxyAdmin.upgradeAndCall( - TransparentUpgradeableProxy(payable(address(eigenPodManager))), - address(eigenPodManagerImplementation), - abi.encodeWithSelector( - EigenPodManager.initialize.selector, - EIGENPOD_MANAGER_MAX_PODS, - IBeaconChainOracle(address(0)), - alphaMultisig, - eigenLayerPauserReg, - EIGENPOD_MANAGER_INIT_PAUSED_STATUS - ) - ); - eigenLayerProxyAdmin.upgradeAndCall( - TransparentUpgradeableProxy( - payable(address(delayedWithdrawalRouter)) - ), - address(delayedWithdrawalRouterImplementation), - abi.encodeWithSelector( - DelayedWithdrawalRouter.initialize.selector, - alphaMultisig, - eigenLayerPauserReg, - DELAYED_WITHDRAWAL_ROUTER_INIT_PAUSED_STATUS, - DELAYED_WITHDRAWAL_ROUTER_INIT_WITHDRAWAL_DELAY_BLOCKS - ) - ); - - IERC20 mockToken = new ERC20Mock(); - - // ERC20MockStrategy = StrategyBase( - // address(new TransparentUpgradeableProxy(address(emptyContract), address(eigenLayerProxyAdmin), "")) - // ); - - // deploy StrategyBaseTVLLimits contract implementation - ERC20MockStrategyImplementation = new StrategyBase(strategyManager); - // create upgradeable proxies that each point to the implementation and initialize them - - // eigenLayerProxyAdmin.upgradeAndCall( - // TransparentUpgradeableProxy(payable(address(ERC20MockStrategy))), - // address(ERC20MockStrategyImplementation), - // abi.encodeWithSelector( - // EigenPodManager.initialize.selector, - // EIGENPOD_MANAGER_MAX_PODS, - // IBeaconChainOracle(address(0)), - // alphaMultisig, - // eigenLayerPauserReg, - // EIGENPOD_MANAGER_INIT_PAUSED_STATUS - // ) - // ); - - ERC20MockStrategy = StrategyBase( - address( - new TransparentUpgradeableProxy( - address(ERC20MockStrategyImplementation), - address(eigenLayerProxyAdmin), - abi.encodeWithSelector( - StrategyBase.initialize.selector, - mockToken, - eigenLayerPauserReg - ) - ) - ) - ); - - eigenLayerProxyAdmin.transferOwnership(alphaMultisig); - eigenPodBeacon.transferOwnership(alphaMultisig); - - // STOP RECORDING TRANSACTIONS FOR DEPLOYMENT - vm.stopBroadcast(); - - // CHECK CORRECTNESS OF DEPLOYMENT - _verifyContractsPointAtOneAnother( - delegationImplementation, - strategyManagerImplementation, - slasherImplementation, - eigenPodManagerImplementation, - delayedWithdrawalRouterImplementation - ); - _verifyContractsPointAtOneAnother( - delegation, - strategyManager, - slasher, - eigenPodManager, - delayedWithdrawalRouter - ); - _verifyImplementationsSetCorrectly(); - _verifyInitialOwners(); - _checkPauserInitializations(); - _verifyInitializationParams(); - - // WRITE JSON DATA - string memory parent_object = "parent object"; - - string memory deployed_addresses = "addresses"; - vm.serializeAddress( - deployed_addresses, - "eigenLayerProxyAdmin", - address(eigenLayerProxyAdmin) - ); - vm.serializeAddress( - deployed_addresses, - "eigenLayerPauserReg", - address(eigenLayerPauserReg) - ); - vm.serializeAddress(deployed_addresses, "slasher", address(slasher)); - vm.serializeAddress( - deployed_addresses, - "slasherImplementation", - address(slasherImplementation) - ); - vm.serializeAddress( - deployed_addresses, - "delegation", - address(delegation) - ); - vm.serializeAddress( - deployed_addresses, - "delegationImplementation", - address(delegationImplementation) - ); - vm.serializeAddress( - deployed_addresses, - "strategyManager", - address(strategyManager) - ); - vm.serializeAddress( - deployed_addresses, - "strategyManagerImplementation", - address(strategyManagerImplementation) - ); - vm.serializeAddress( - deployed_addresses, - "eigenPodManager", - address(eigenPodManager) - ); - vm.serializeAddress( - deployed_addresses, - "eigenPodManagerImplementation", - address(eigenPodManagerImplementation) - ); - vm.serializeAddress( - deployed_addresses, - "delayedWithdrawalRouter", - address(delayedWithdrawalRouter) - ); - vm.serializeAddress( - deployed_addresses, - "delayedWithdrawalRouterImplementation", - address(delayedWithdrawalRouterImplementation) - ); - vm.serializeAddress( - deployed_addresses, - "eigenPodBeacon", - address(eigenPodBeacon) - ); - vm.serializeAddress( - deployed_addresses, - "eigenPodImplementation", - address(eigenPodImplementation) - ); - vm.serializeAddress( - deployed_addresses, - "ERC20MockStrategy", - address(ERC20MockStrategy) - ); - vm.serializeAddress( - deployed_addresses, - "ERC20MockStrategyImplementation", - address(ERC20MockStrategyImplementation) - ); - vm.serializeAddress( - deployed_addresses, - "ERC20Mock", - address(mockToken) - ); - string memory deployed_addresses_output = vm.serializeAddress( - deployed_addresses, - "emptyContract", - address(emptyContract) - ); - - string memory parameters = "parameters"; - - string memory parameters_output = vm.serializeAddress( - parameters, - "alphaMultisig", - alphaMultisig - ); - - string memory chain_info = "chainInfo"; - vm.serializeUint(chain_info, "deploymentBlock", block.number); - string memory chain_info_output = vm.serializeUint( - chain_info, - "chainId", - chainId - ); - - // serialize all the data - vm.serializeString( - parent_object, - deployed_addresses, - deployed_addresses_output - ); - vm.serializeString( - parent_object, - chain_info, - chain_info_output - ); - string memory finalJson = vm.serializeString( - parent_object, - parameters, - parameters_output - ); - vm.writeJson(finalJson, "script/output/deployment_output.json"); - } - - function _verifyContractsPointAtOneAnother( - DelegationManager delegationContract, - StrategyManager strategyManagerContract, - Slasher slasherContract, - EigenPodManager eigenPodManagerContract, - DelayedWithdrawalRouter delayedWithdrawalRouterContract - ) internal view { - require( - delegationContract.slasher() == slasher, - "delegation: slasher address not set correctly" - ); - require( - delegationContract.strategyManager() == strategyManager, - "delegation: strategyManager address not set correctly" - ); - - require( - strategyManagerContract.slasher() == slasher, - "strategyManager: slasher address not set correctly" - ); - require( - strategyManagerContract.delegation() == delegation, - "strategyManager: delegation address not set correctly" - ); - require( - strategyManagerContract.eigenPodManager() == eigenPodManager, - "strategyManager: eigenPodManager address not set correctly" - ); - - require( - slasherContract.strategyManager() == strategyManager, - "slasher: strategyManager not set correctly" - ); - require( - slasherContract.delegation() == delegation, - "slasher: delegation not set correctly" - ); - - require( - eigenPodManagerContract.ethPOS() == ethPOSDeposit, - " eigenPodManager: ethPOSDeposit contract address not set correctly" - ); - require( - eigenPodManagerContract.eigenPodBeacon() == eigenPodBeacon, - "eigenPodManager: eigenPodBeacon contract address not set correctly" - ); - require( - eigenPodManagerContract.strategyManager() == strategyManager, - "eigenPodManager: strategyManager contract address not set correctly" - ); - require( - eigenPodManagerContract.slasher() == slasher, - "eigenPodManager: slasher contract address not set correctly" - ); - - require( - delayedWithdrawalRouterContract.eigenPodManager() == - eigenPodManager, - "delayedWithdrawalRouterContract: eigenPodManager address not set correctly" - ); - } - - function _verifyImplementationsSetCorrectly() internal view { - require( - eigenLayerProxyAdmin.getProxyImplementation( - TransparentUpgradeableProxy(payable(address(delegation))) - ) == address(delegationImplementation), - "delegation: implementation set incorrectly" - ); - require( - eigenLayerProxyAdmin.getProxyImplementation( - TransparentUpgradeableProxy(payable(address(strategyManager))) - ) == address(strategyManagerImplementation), - "strategyManager: implementation set incorrectly" - ); - require( - eigenLayerProxyAdmin.getProxyImplementation( - TransparentUpgradeableProxy(payable(address(slasher))) - ) == address(slasherImplementation), - "slasher: implementation set incorrectly" - ); - require( - eigenLayerProxyAdmin.getProxyImplementation( - TransparentUpgradeableProxy(payable(address(eigenPodManager))) - ) == address(eigenPodManagerImplementation), - "eigenPodManager: implementation set incorrectly" - ); - require( - eigenLayerProxyAdmin.getProxyImplementation( - TransparentUpgradeableProxy( - payable(address(delayedWithdrawalRouter)) - ) - ) == address(delayedWithdrawalRouterImplementation), - "delayedWithdrawalRouter: implementation set incorrectly" - ); - - require( - eigenLayerProxyAdmin.getProxyImplementation( - TransparentUpgradeableProxy(payable(address(ERC20MockStrategy))) - ) == address(ERC20MockStrategyImplementation), - "strategy: implementation set incorrectly" - ); - - require( - eigenPodBeacon.implementation() == address(eigenPodImplementation), - "eigenPodBeacon: implementation set incorrectly" - ); - } - - function _verifyInitialOwners() internal view { - require( - strategyManager.owner() == alphaMultisig, - "strategyManager: owner not set correctly" - ); - require( - delegation.owner() == alphaMultisig, - "delegation: owner not set correctly" - ); - require( - slasher.owner() == alphaMultisig, - "slasher: owner not set correctly" - ); - require( - eigenPodManager.owner() == alphaMultisig, - "delegation: owner not set correctly" - ); - - require( - eigenLayerProxyAdmin.owner() == alphaMultisig, - "eigenLayerProxyAdmin: owner not set correctly" - ); - require( - eigenPodBeacon.owner() == alphaMultisig, - "eigenPodBeacon: owner not set correctly" - ); - require( - delayedWithdrawalRouter.owner() == alphaMultisig, - "delayedWithdrawalRouter: owner not set correctly" - ); - } - - function _checkPauserInitializations() internal view { - require( - delegation.pauserRegistry() == eigenLayerPauserReg, - "delegation: pauser registry not set correctly" - ); - require( - strategyManager.pauserRegistry() == eigenLayerPauserReg, - "strategyManager: pauser registry not set correctly" - ); - require( - slasher.pauserRegistry() == eigenLayerPauserReg, - "slasher: pauser registry not set correctly" - ); - require( - eigenPodManager.pauserRegistry() == eigenLayerPauserReg, - "eigenPodManager: pauser registry not set correctly" - ); - require( - delayedWithdrawalRouter.pauserRegistry() == eigenLayerPauserReg, - "delayedWithdrawalRouter: pauser registry not set correctly" - ); - - require( - eigenLayerPauserReg.isPauser(alphaMultisig), - "pauserRegistry: alphaMultisig is not pauser" - ); - - require( - eigenLayerPauserReg.unpauser() == alphaMultisig, - "pauserRegistry: unpauser not set correctly" - ); - - require( - ERC20MockStrategy.pauserRegistry() == eigenLayerPauserReg, - "StrategyBaseTVLLimits: pauser registry not set correctly" - ); - require( - ERC20MockStrategy.paused() == 0, - "StrategyBaseTVLLimits: init paused status set incorrectly" - ); - - // // pause *nothing* - // uint256 STRATEGY_MANAGER_INIT_PAUSED_STATUS = 0; - // // pause *everything* - // uint256 SLASHER_INIT_PAUSED_STATUS = type(uint256).max; - // // pause *everything* - // uint256 DELEGATION_INIT_PAUSED_STATUS = type(uint256).max; - // // pause *all of the proof-related functionality* (everything that can be paused other than creation of EigenPods) - // uint256 EIGENPOD_MANAGER_INIT_PAUSED_STATUS = (2**1) + (2**2) + (2**3) + (2**4); /* = 30 */ - // // pause *nothing* - // uint256 DELAYED_WITHDRAWAL_ROUTER_INIT_PAUSED_STATUS = 0; - require( - strategyManager.paused() == 0, - "strategyManager: init paused status set incorrectly" - ); - require( - slasher.paused() == 0, - "slasher: init paused status set incorrectly" - ); - require( - delegation.paused() == 0, - "delegation: init paused status set incorrectly" - ); - require( - eigenPodManager.paused() == 30, - "eigenPodManager: init paused status set incorrectly" - ); - require( - delayedWithdrawalRouter.paused() == 0, - "delayedWithdrawalRouter: init paused status set incorrectly" - ); - } - - function _verifyInitializationParams() internal view { - // // one week in blocks -- 50400 - // uint32 STRATEGY_MANAGER_INIT_WITHDRAWAL_DELAY_BLOCKS = 7 days / 12 seconds; - // uint32 DELAYED_WITHDRAWAL_ROUTER_INIT_WITHDRAWAL_DELAY_BLOCKS = 7 days / 12 seconds; - // require(strategyManager.withdrawalDelayBlocks() == 7 days / 12 seconds, - // "strategyManager: withdrawalDelayBlocks initialized incorrectly"); - // require(delayedWithdrawalRouter.withdrawalDelayBlocks() == 7 days / 12 seconds, - // "delayedWithdrawalRouter: withdrawalDelayBlocks initialized incorrectly"); - // uint256 REQUIRED_BALANCE_WEI = 31 ether; - - require( - strategyManager.strategyWhitelister() == alphaMultisig, - "strategyManager: strategyWhitelister address not set correctly" - ); - - require( - eigenPodManager.beaconChainOracle() == - IBeaconChainOracle(address(0)), - "eigenPodManager: eigenPodBeacon contract address not set correctly" - ); - - require( - delayedWithdrawalRouter.eigenPodManager() == eigenPodManager, - "delayedWithdrawalRouter: eigenPodManager set incorrectly" - ); - - require( - ERC20MockStrategyImplementation.strategyManager() == strategyManager, - "ERC20MockStrategyImplementation: strategyManager set incorrectly" - ); - - require( - eigenPodImplementation.ethPOS() == ethPOSDeposit, - "eigenPodImplementation: ethPOSDeposit contract address not set correctly" - ); - require( - eigenPodImplementation.eigenPodManager() == eigenPodManager, - " eigenPodImplementation: eigenPodManager contract address not set correctly" - ); - require( - eigenPodImplementation.delayedWithdrawalRouter() == - delayedWithdrawalRouter, - " eigenPodImplementation: delayedWithdrawalRouter contract address not set correctly" - ); - } -} \ No newline at end of file diff --git a/script/M1_Deploy.s.sol b/script/M1_Deploy.s.sol index 6b1c6d1c5..ac689a29a 100644 --- a/script/M1_Deploy.s.sol +++ b/script/M1_Deploy.s.sol @@ -20,7 +20,6 @@ import "../src/contracts/pods/EigenPodManager.sol"; import "../src/contracts/pods/DelayedWithdrawalRouter.sol"; import "../src/contracts/permissions/PauserRegistry.sol"; -import "../src/contracts/middleware/BLSPublicKeyCompendium.sol"; import "../src/test/mocks/EmptyContract.sol"; import "../src/test/mocks/ETHDepositMock.sol"; @@ -107,14 +106,27 @@ contract Deployer_M1 is Script, Test { DELEGATION_INIT_PAUSED_STATUS = stdJson.readUint(config_data, ".delegation.init_paused_status"); EIGENPOD_MANAGER_MAX_PODS = stdJson.readUint(config_data, ".eigenPodManager.max_pods"); EIGENPOD_MANAGER_INIT_PAUSED_STATUS = stdJson.readUint(config_data, ".eigenPodManager.init_paused_status"); - DELAYED_WITHDRAWAL_ROUTER_INIT_PAUSED_STATUS = stdJson.readUint(config_data, ".delayedWithdrawalRouter.init_paused_status"); + DELAYED_WITHDRAWAL_ROUTER_INIT_PAUSED_STATUS = stdJson.readUint( + config_data, + ".delayedWithdrawalRouter.init_paused_status" + ); - STRATEGY_MANAGER_INIT_WITHDRAWAL_DELAY_BLOCKS = uint32(stdJson.readUint(config_data, ".strategyManager.init_withdrawal_delay_blocks")); - DELAYED_WITHDRAWAL_ROUTER_INIT_WITHDRAWAL_DELAY_BLOCKS = uint32(stdJson.readUint(config_data, ".strategyManager.init_withdrawal_delay_blocks")); + STRATEGY_MANAGER_INIT_WITHDRAWAL_DELAY_BLOCKS = uint32( + stdJson.readUint(config_data, ".strategyManager.init_withdrawal_delay_blocks") + ); + DELAYED_WITHDRAWAL_ROUTER_INIT_WITHDRAWAL_DELAY_BLOCKS = uint32( + stdJson.readUint(config_data, ".strategyManager.init_withdrawal_delay_blocks") + ); REQUIRED_BALANCE_WEI = stdJson.readUint(config_data, ".eigenPod.REQUIRED_BALANCE_WEI"); - MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR = stdJson.readUint(config_data, ".eigenPod.MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR"); - EFFECTIVE_RESTAKED_BALANCE_OFFSET_GWEI = stdJson.readUint(config_data, ".eigenPod.EFFECTIVE_RESTAKED_BALANCE_OFFSET_GWEI"); + MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR = stdJson.readUint( + config_data, + ".eigenPod.MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR" + ); + EFFECTIVE_RESTAKED_BALANCE_OFFSET_GWEI = stdJson.readUint( + config_data, + ".eigenPod.EFFECTIVE_RESTAKED_BALANCE_OFFSET_GWEI" + ); // tokens to deploy strategies for StrategyConfig[] memory strategyConfigs; @@ -168,7 +180,7 @@ contract Deployer_M1 is Script, Test { // if on mainnet, use the ETH2 deposit contract address if (chainId == 1) { ethPOSDeposit = IETHPOSDeposit(0x00000000219ab540356cBB839Cbe05303d7705Fa); - // if not on mainnet, deploy a mock + // if not on mainnet, deploy a mock } else { ethPOSDeposit = IETHPOSDeposit(stdJson.readAddress(config_data, ".ethPOSDepositAddress")); } @@ -187,7 +199,13 @@ contract Deployer_M1 is Script, Test { delegationImplementation = new DelegationManager(strategyManager, slasher, eigenPodManager); strategyManagerImplementation = new StrategyManager(delegation, eigenPodManager, slasher); slasherImplementation = new Slasher(strategyManager, delegation); - eigenPodManagerImplementation = new EigenPodManager(ethPOSDeposit, eigenPodBeacon, strategyManager, slasher, delegation); + eigenPodManagerImplementation = new EigenPodManager( + ethPOSDeposit, + eigenPodBeacon, + strategyManager, + slasher, + delegation + ); delayedWithdrawalRouterImplementation = new DelayedWithdrawalRouter(eigenPodManager); // Third, upgrade the proxy contracts to use the correct implementation contracts and initialize them. @@ -237,11 +255,13 @@ contract Deployer_M1 is Script, Test { eigenLayerProxyAdmin.upgradeAndCall( TransparentUpgradeableProxy(payable(address(delayedWithdrawalRouter))), address(delayedWithdrawalRouterImplementation), - abi.encodeWithSelector(DelayedWithdrawalRouter.initialize.selector, - executorMultisig, - eigenLayerPauserReg, - DELAYED_WITHDRAWAL_ROUTER_INIT_PAUSED_STATUS, - DELAYED_WITHDRAWAL_ROUTER_INIT_WITHDRAWAL_DELAY_BLOCKS) + abi.encodeWithSelector( + DelayedWithdrawalRouter.initialize.selector, + executorMultisig, + eigenLayerPauserReg, + DELAYED_WITHDRAWAL_ROUTER_INIT_PAUSED_STATUS, + DELAYED_WITHDRAWAL_ROUTER_INIT_WITHDRAWAL_DELAY_BLOCKS + ) ); // deploy StrategyBaseTVLLimits contract implementation @@ -249,13 +269,21 @@ contract Deployer_M1 is Script, Test { // create upgradeable proxies that each point to the implementation and initialize them for (uint256 i = 0; i < strategyConfigs.length; ++i) { deployedStrategyArray.push( - StrategyBaseTVLLimits(address( - new TransparentUpgradeableProxy( - address(baseStrategyImplementation), - address(eigenLayerProxyAdmin), - abi.encodeWithSelector(StrategyBaseTVLLimits.initialize.selector, strategyConfigs[i].maxPerDeposit, strategyConfigs[i].maxDeposits, IERC20(strategyConfigs[i].tokenAddress), eigenLayerPauserReg) + StrategyBaseTVLLimits( + address( + new TransparentUpgradeableProxy( + address(baseStrategyImplementation), + address(eigenLayerProxyAdmin), + abi.encodeWithSelector( + StrategyBaseTVLLimits.initialize.selector, + strategyConfigs[i].maxPerDeposit, + strategyConfigs[i].maxDeposits, + IERC20(strategyConfigs[i].tokenAddress), + eigenLayerPauserReg + ) + ) ) - )) + ) ); } @@ -265,7 +293,6 @@ contract Deployer_M1 is Script, Test { // STOP RECORDING TRANSACTIONS FOR DEPLOYMENT vm.stopBroadcast(); - // CHECK CORRECTNESS OF DEPLOYMENT _verifyContractsPointAtOneAnother( delegationImplementation, @@ -286,7 +313,6 @@ contract Deployer_M1 is Script, Test { _checkPauserInitializations(); _verifyInitializationParams(); - // WRITE JSON DATA string memory parent_object = "parent object"; @@ -295,7 +321,8 @@ contract Deployer_M1 is Script, Test { vm.serializeAddress(deployed_strategies, strategyConfigs[i].tokenSymbol, address(deployedStrategyArray[i])); } string memory deployed_strategies_output = vm.serializeAddress( - deployed_strategies, strategyConfigs[strategyConfigs.length - 1].tokenSymbol, + deployed_strategies, + strategyConfigs[strategyConfigs.length - 1].tokenSymbol, address(deployedStrategyArray[strategyConfigs.length - 1]) ); @@ -307,16 +334,32 @@ contract Deployer_M1 is Script, Test { vm.serializeAddress(deployed_addresses, "delegation", address(delegation)); vm.serializeAddress(deployed_addresses, "delegationImplementation", address(delegationImplementation)); vm.serializeAddress(deployed_addresses, "strategyManager", address(strategyManager)); - vm.serializeAddress(deployed_addresses, "strategyManagerImplementation", address(strategyManagerImplementation)); + vm.serializeAddress( + deployed_addresses, + "strategyManagerImplementation", + address(strategyManagerImplementation) + ); vm.serializeAddress(deployed_addresses, "eigenPodManager", address(eigenPodManager)); - vm.serializeAddress(deployed_addresses, "eigenPodManagerImplementation", address(eigenPodManagerImplementation)); + vm.serializeAddress( + deployed_addresses, + "eigenPodManagerImplementation", + address(eigenPodManagerImplementation) + ); vm.serializeAddress(deployed_addresses, "delayedWithdrawalRouter", address(delayedWithdrawalRouter)); - vm.serializeAddress(deployed_addresses, "delayedWithdrawalRouterImplementation", address(delayedWithdrawalRouterImplementation)); + vm.serializeAddress( + deployed_addresses, + "delayedWithdrawalRouterImplementation", + address(delayedWithdrawalRouterImplementation) + ); vm.serializeAddress(deployed_addresses, "eigenPodBeacon", address(eigenPodBeacon)); vm.serializeAddress(deployed_addresses, "eigenPodImplementation", address(eigenPodImplementation)); vm.serializeAddress(deployed_addresses, "baseStrategyImplementation", address(baseStrategyImplementation)); vm.serializeAddress(deployed_addresses, "emptyContract", address(emptyContract)); - string memory deployed_addresses_output = vm.serializeString(deployed_addresses, "strategies", deployed_strategies_output); + string memory deployed_addresses_output = vm.serializeString( + deployed_addresses, + "strategies", + deployed_strategies_output + ); string memory parameters = "parameters"; vm.serializeAddress(parameters, "executorMultisig", executorMultisig); @@ -334,56 +377,97 @@ contract Deployer_M1 is Script, Test { } function _verifyContractsPointAtOneAnother( - DelegationManager delegationContract, - StrategyManager strategyManagerContract, - Slasher slasherContract, + DelegationManager delegationContract, + StrategyManager strategyManagerContract, + Slasher slasherContract, EigenPodManager eigenPodManagerContract, DelayedWithdrawalRouter delayedWithdrawalRouterContract ) internal view { require(delegationContract.slasher() == slasher, "delegation: slasher address not set correctly"); - require(delegationContract.strategyManager() == strategyManager, "delegation: strategyManager address not set correctly"); + require( + delegationContract.strategyManager() == strategyManager, + "delegation: strategyManager address not set correctly" + ); require(strategyManagerContract.slasher() == slasher, "strategyManager: slasher address not set correctly"); - require(strategyManagerContract.delegation() == delegation, "strategyManager: delegation address not set correctly"); - require(strategyManagerContract.eigenPodManager() == eigenPodManager, "strategyManager: eigenPodManager address not set correctly"); + require( + strategyManagerContract.delegation() == delegation, + "strategyManager: delegation address not set correctly" + ); + require( + strategyManagerContract.eigenPodManager() == eigenPodManager, + "strategyManager: eigenPodManager address not set correctly" + ); require(slasherContract.strategyManager() == strategyManager, "slasher: strategyManager not set correctly"); require(slasherContract.delegation() == delegation, "slasher: delegation not set correctly"); - require(eigenPodManagerContract.ethPOS() == ethPOSDeposit, " eigenPodManager: ethPOSDeposit contract address not set correctly"); - require(eigenPodManagerContract.eigenPodBeacon() == eigenPodBeacon, "eigenPodManager: eigenPodBeacon contract address not set correctly"); - require(eigenPodManagerContract.strategyManager() == strategyManager, "eigenPodManager: strategyManager contract address not set correctly"); - require(eigenPodManagerContract.slasher() == slasher, "eigenPodManager: slasher contract address not set correctly"); + require( + eigenPodManagerContract.ethPOS() == ethPOSDeposit, + " eigenPodManager: ethPOSDeposit contract address not set correctly" + ); + require( + eigenPodManagerContract.eigenPodBeacon() == eigenPodBeacon, + "eigenPodManager: eigenPodBeacon contract address not set correctly" + ); + require( + eigenPodManagerContract.strategyManager() == strategyManager, + "eigenPodManager: strategyManager contract address not set correctly" + ); + require( + eigenPodManagerContract.slasher() == slasher, + "eigenPodManager: slasher contract address not set correctly" + ); - require(delayedWithdrawalRouterContract.eigenPodManager() == eigenPodManager, - "delayedWithdrawalRouterContract: eigenPodManager address not set correctly"); + require( + delayedWithdrawalRouterContract.eigenPodManager() == eigenPodManager, + "delayedWithdrawalRouterContract: eigenPodManager address not set correctly" + ); } function _verifyImplementationsSetCorrectly() internal view { - require(eigenLayerProxyAdmin.getProxyImplementation( - TransparentUpgradeableProxy(payable(address(delegation)))) == address(delegationImplementation), - "delegation: implementation set incorrectly"); - require(eigenLayerProxyAdmin.getProxyImplementation( - TransparentUpgradeableProxy(payable(address(strategyManager)))) == address(strategyManagerImplementation), - "strategyManager: implementation set incorrectly"); - require(eigenLayerProxyAdmin.getProxyImplementation( - TransparentUpgradeableProxy(payable(address(slasher)))) == address(slasherImplementation), - "slasher: implementation set incorrectly"); - require(eigenLayerProxyAdmin.getProxyImplementation( - TransparentUpgradeableProxy(payable(address(eigenPodManager)))) == address(eigenPodManagerImplementation), - "eigenPodManager: implementation set incorrectly"); - require(eigenLayerProxyAdmin.getProxyImplementation( - TransparentUpgradeableProxy(payable(address(delayedWithdrawalRouter)))) == address(delayedWithdrawalRouterImplementation), - "delayedWithdrawalRouter: implementation set incorrectly"); + require( + eigenLayerProxyAdmin.getProxyImplementation(TransparentUpgradeableProxy(payable(address(delegation)))) == + address(delegationImplementation), + "delegation: implementation set incorrectly" + ); + require( + eigenLayerProxyAdmin.getProxyImplementation( + TransparentUpgradeableProxy(payable(address(strategyManager))) + ) == address(strategyManagerImplementation), + "strategyManager: implementation set incorrectly" + ); + require( + eigenLayerProxyAdmin.getProxyImplementation(TransparentUpgradeableProxy(payable(address(slasher)))) == + address(slasherImplementation), + "slasher: implementation set incorrectly" + ); + require( + eigenLayerProxyAdmin.getProxyImplementation( + TransparentUpgradeableProxy(payable(address(eigenPodManager))) + ) == address(eigenPodManagerImplementation), + "eigenPodManager: implementation set incorrectly" + ); + require( + eigenLayerProxyAdmin.getProxyImplementation( + TransparentUpgradeableProxy(payable(address(delayedWithdrawalRouter))) + ) == address(delayedWithdrawalRouterImplementation), + "delayedWithdrawalRouter: implementation set incorrectly" + ); for (uint256 i = 0; i < deployedStrategyArray.length; ++i) { - require(eigenLayerProxyAdmin.getProxyImplementation( - TransparentUpgradeableProxy(payable(address(deployedStrategyArray[i])))) == address(baseStrategyImplementation), - "strategy: implementation set incorrectly"); + require( + eigenLayerProxyAdmin.getProxyImplementation( + TransparentUpgradeableProxy(payable(address(deployedStrategyArray[i]))) + ) == address(baseStrategyImplementation), + "strategy: implementation set incorrectly" + ); } - require(eigenPodBeacon.implementation() == address(eigenPodImplementation), - "eigenPodBeacon: implementation set incorrectly"); + require( + eigenPodBeacon.implementation() == address(eigenPodImplementation), + "eigenPodBeacon: implementation set incorrectly" + ); } function _verifyInitialOwners() internal view { @@ -394,15 +478,27 @@ contract Deployer_M1 is Script, Test { require(eigenLayerProxyAdmin.owner() == executorMultisig, "eigenLayerProxyAdmin: owner not set correctly"); require(eigenPodBeacon.owner() == executorMultisig, "eigenPodBeacon: owner not set correctly"); - require(delayedWithdrawalRouter.owner() == executorMultisig, "delayedWithdrawalRouter: owner not set correctly"); + require( + delayedWithdrawalRouter.owner() == executorMultisig, + "delayedWithdrawalRouter: owner not set correctly" + ); } function _checkPauserInitializations() internal view { require(delegation.pauserRegistry() == eigenLayerPauserReg, "delegation: pauser registry not set correctly"); - require(strategyManager.pauserRegistry() == eigenLayerPauserReg, "strategyManager: pauser registry not set correctly"); + require( + strategyManager.pauserRegistry() == eigenLayerPauserReg, + "strategyManager: pauser registry not set correctly" + ); require(slasher.pauserRegistry() == eigenLayerPauserReg, "slasher: pauser registry not set correctly"); - require(eigenPodManager.pauserRegistry() == eigenLayerPauserReg, "eigenPodManager: pauser registry not set correctly"); - require(delayedWithdrawalRouter.pauserRegistry() == eigenLayerPauserReg, "delayedWithdrawalRouter: pauser registry not set correctly"); + require( + eigenPodManager.pauserRegistry() == eigenLayerPauserReg, + "eigenPodManager: pauser registry not set correctly" + ); + require( + delayedWithdrawalRouter.pauserRegistry() == eigenLayerPauserReg, + "delayedWithdrawalRouter: pauser registry not set correctly" + ); require(eigenLayerPauserReg.isPauser(operationsMultisig), "pauserRegistry: operationsMultisig is not pauser"); require(eigenLayerPauserReg.isPauser(executorMultisig), "pauserRegistry: executorMultisig is not pauser"); @@ -410,18 +506,24 @@ contract Deployer_M1 is Script, Test { require(eigenLayerPauserReg.unpauser() == executorMultisig, "pauserRegistry: unpauser not set correctly"); for (uint256 i = 0; i < deployedStrategyArray.length; ++i) { - require(deployedStrategyArray[i].pauserRegistry() == eigenLayerPauserReg, "StrategyBaseTVLLimits: pauser registry not set correctly"); - require(deployedStrategyArray[i].paused() == 0, "StrategyBaseTVLLimits: init paused status set incorrectly"); + require( + deployedStrategyArray[i].pauserRegistry() == eigenLayerPauserReg, + "StrategyBaseTVLLimits: pauser registry not set correctly" + ); + require( + deployedStrategyArray[i].paused() == 0, + "StrategyBaseTVLLimits: init paused status set incorrectly" + ); } // // pause *nothing* // uint256 STRATEGY_MANAGER_INIT_PAUSED_STATUS = 0; // // pause *everything* - // uint256 SLASHER_INIT_PAUSED_STATUS = type(uint256).max; + // uint256 SLASHER_INIT_PAUSED_STATUS = type(uint256).max; // // pause *everything* - // uint256 DELEGATION_INIT_PAUSED_STATUS = type(uint256).max; + // uint256 DELEGATION_INIT_PAUSED_STATUS = type(uint256).max; // // pause *all of the proof-related functionality* (everything that can be paused other than creation of EigenPods) - // uint256 EIGENPOD_MANAGER_INIT_PAUSED_STATUS = (2**1) + (2**2) + (2**3) + (2**4); /* = 30 */ + // uint256 EIGENPOD_MANAGER_INIT_PAUSED_STATUS = (2**1) + (2**2) + (2**3) + (2**4); /* = 30 */ // // pause *nothing* // uint256 DELAYED_WITHDRAWAL_ROUTER_INIT_PAUSED_STATUS = 0; require(strategyManager.paused() == 0, "strategyManager: init paused status set incorrectly"); @@ -441,37 +543,52 @@ contract Deployer_M1 is Script, Test { // "delayedWithdrawalRouter: withdrawalDelayBlocks initialized incorrectly"); // uint256 REQUIRED_BALANCE_WEI = 31 ether; - require(strategyManager.strategyWhitelister() == operationsMultisig, - "strategyManager: strategyWhitelister address not set correctly"); + require( + strategyManager.strategyWhitelister() == operationsMultisig, + "strategyManager: strategyWhitelister address not set correctly" + ); - require(eigenPodManager.beaconChainOracle() == IBeaconChainOracle(address(0)), - "eigenPodManager: eigenPodBeacon contract address not set correctly"); + require( + eigenPodManager.beaconChainOracle() == IBeaconChainOracle(address(0)), + "eigenPodManager: eigenPodBeacon contract address not set correctly" + ); - require(delayedWithdrawalRouter.eigenPodManager() == eigenPodManager, - "delayedWithdrawalRouter: eigenPodManager set incorrectly"); + require( + delayedWithdrawalRouter.eigenPodManager() == eigenPodManager, + "delayedWithdrawalRouter: eigenPodManager set incorrectly" + ); - require(baseStrategyImplementation.strategyManager() == strategyManager, - "baseStrategyImplementation: strategyManager set incorrectly"); + require( + baseStrategyImplementation.strategyManager() == strategyManager, + "baseStrategyImplementation: strategyManager set incorrectly" + ); - require(eigenPodImplementation.ethPOS() == ethPOSDeposit, - "eigenPodImplementation: ethPOSDeposit contract address not set correctly"); - require(eigenPodImplementation.eigenPodManager() == eigenPodManager, - " eigenPodImplementation: eigenPodManager contract address not set correctly"); - require(eigenPodImplementation.delayedWithdrawalRouter() == delayedWithdrawalRouter, - " eigenPodImplementation: delayedWithdrawalRouter contract address not set correctly"); + require( + eigenPodImplementation.ethPOS() == ethPOSDeposit, + "eigenPodImplementation: ethPOSDeposit contract address not set correctly" + ); + require( + eigenPodImplementation.eigenPodManager() == eigenPodManager, + " eigenPodImplementation: eigenPodManager contract address not set correctly" + ); + require( + eigenPodImplementation.delayedWithdrawalRouter() == delayedWithdrawalRouter, + " eigenPodImplementation: delayedWithdrawalRouter contract address not set correctly" + ); string memory config_data = vm.readFile(deployConfigPath); for (uint i = 0; i < deployedStrategyArray.length; i++) { - uint256 maxPerDeposit = stdJson.readUint(config_data, string.concat(".strategies[", vm.toString(i), "].max_per_deposit")); - uint256 maxDeposits = stdJson.readUint(config_data, string.concat(".strategies[", vm.toString(i), "].max_deposits")); + uint256 maxPerDeposit = stdJson.readUint( + config_data, + string.concat(".strategies[", vm.toString(i), "].max_per_deposit") + ); + uint256 maxDeposits = stdJson.readUint( + config_data, + string.concat(".strategies[", vm.toString(i), "].max_deposits") + ); (uint256 setMaxPerDeposit, uint256 setMaxDeposits) = deployedStrategyArray[i].getTVLLimits(); require(setMaxPerDeposit == maxPerDeposit, "setMaxPerDeposit not set correctly"); require(setMaxDeposits == maxDeposits, "setMaxDeposits not set correctly"); } } } - - - - - diff --git a/script/middleware/DeployOpenEigenLayer.s.sol b/script/middleware/DeployOpenEigenLayer.s.sol index 62fa20aa9..a793c98a7 100644 --- a/script/middleware/DeployOpenEigenLayer.s.sol +++ b/script/middleware/DeployOpenEigenLayer.s.sol @@ -20,7 +20,6 @@ import "../../src/contracts/pods/EigenPodManager.sol"; import "../../src/contracts/pods/DelayedWithdrawalRouter.sol"; import "../../src/contracts/permissions/PauserRegistry.sol"; -import "../../src/contracts/middleware/BLSPublicKeyCompendium.sol"; import "../../src/test/mocks/EmptyContract.sol"; import "../../src/test/mocks/ETHDepositMock.sol"; @@ -69,7 +68,12 @@ contract DeployOpenEigenLayer is Script, Test { // strategies deployed StrategyBaseTVLLimits[] public deployedStrategyArray; - function _deployEigenLayer(address executorMultisig, address operationsMultisig, address pauserMultisig, StrategyConfig[] memory strategyConfigs) internal { + function _deployEigenLayer( + address executorMultisig, + address operationsMultisig, + address pauserMultisig, + StrategyConfig[] memory strategyConfigs + ) internal { require(executorMultisig != address(0), "executorMultisig address not configured correctly!"); require(operationsMultisig != address(0), "operationsMultisig address not configured correctly!"); @@ -124,19 +128,20 @@ contract DeployOpenEigenLayer is Script, Test { delegationImplementation = new DelegationManager(strategyManager, slasher, eigenPodManager); strategyManagerImplementation = new StrategyManager(delegation, eigenPodManager, slasher); slasherImplementation = new Slasher(strategyManager, delegation); - eigenPodManagerImplementation = new EigenPodManager(ethPOSDeposit, eigenPodBeacon, strategyManager, slasher, delegation); + eigenPodManagerImplementation = new EigenPodManager( + ethPOSDeposit, + eigenPodBeacon, + strategyManager, + slasher, + delegation + ); delayedWithdrawalRouterImplementation = new DelayedWithdrawalRouter(eigenPodManager); // Third, upgrade the proxy contracts to use the correct implementation contracts and initialize them. eigenLayerProxyAdmin.upgradeAndCall( TransparentUpgradeableProxy(payable(address(delegation))), address(delegationImplementation), - abi.encodeWithSelector( - DelegationManager.initialize.selector, - executorMultisig, - eigenLayerPauserReg, - 0 - ) + abi.encodeWithSelector(DelegationManager.initialize.selector, executorMultisig, eigenLayerPauserReg, 0) ); eigenLayerProxyAdmin.upgradeAndCall( TransparentUpgradeableProxy(payable(address(strategyManager))), @@ -153,12 +158,7 @@ contract DeployOpenEigenLayer is Script, Test { eigenLayerProxyAdmin.upgradeAndCall( TransparentUpgradeableProxy(payable(address(slasher))), address(slasherImplementation), - abi.encodeWithSelector( - Slasher.initialize.selector, - executorMultisig, - eigenLayerPauserReg, - 0 - ) + abi.encodeWithSelector(Slasher.initialize.selector, executorMultisig, eigenLayerPauserReg, 0) ); eigenLayerProxyAdmin.upgradeAndCall( TransparentUpgradeableProxy(payable(address(eigenPodManager))), @@ -175,7 +175,8 @@ contract DeployOpenEigenLayer is Script, Test { eigenLayerProxyAdmin.upgradeAndCall( TransparentUpgradeableProxy(payable(address(delayedWithdrawalRouter))), address(delayedWithdrawalRouterImplementation), - abi.encodeWithSelector(DelayedWithdrawalRouter.initialize.selector, + abi.encodeWithSelector( + DelayedWithdrawalRouter.initialize.selector, executorMultisig, eigenLayerPauserReg, 0, @@ -188,17 +189,25 @@ contract DeployOpenEigenLayer is Script, Test { // create upgradeable proxies that each point to the implementation and initialize them for (uint256 i = 0; i < strategyConfigs.length; ++i) { deployedStrategyArray.push( - StrategyBaseTVLLimits(address( - new TransparentUpgradeableProxy( - address(baseStrategyImplementation), - address(eigenLayerProxyAdmin), - abi.encodeWithSelector(StrategyBaseTVLLimits.initialize.selector, strategyConfigs[i].maxPerDeposit, strategyConfigs[i].maxDeposits, IERC20(strategyConfigs[i].tokenAddress), eigenLayerPauserReg) + StrategyBaseTVLLimits( + address( + new TransparentUpgradeableProxy( + address(baseStrategyImplementation), + address(eigenLayerProxyAdmin), + abi.encodeWithSelector( + StrategyBaseTVLLimits.initialize.selector, + strategyConfigs[i].maxPerDeposit, + strategyConfigs[i].maxDeposits, + IERC20(strategyConfigs[i].tokenAddress), + eigenLayerPauserReg + ) + ) ) - )) + ) ); } eigenLayerProxyAdmin.transferOwnership(executorMultisig); eigenPodBeacon.transferOwnership(executorMultisig); } -} \ No newline at end of file +} diff --git a/script/milestone/M2Deploy.sol b/script/milestone/M2Deploy.sol new file mode 100644 index 000000000..0227a8b8f --- /dev/null +++ b/script/milestone/M2Deploy.sol @@ -0,0 +1,130 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity =0.8.12; + +import "@openzeppelin/contracts/token/ERC20/presets/ERC20PresetFixedSupply.sol"; +import "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; +import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +import "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol"; + +import "../../src/contracts/interfaces/IETHPOSDeposit.sol"; +import "../../src/contracts/interfaces/IBeaconChainOracle.sol"; + +import "../../src/contracts/core/StrategyManager.sol"; +import "../../src/contracts/core/Slasher.sol"; +import "../../src/contracts/core/DelegationManager.sol"; + +import "../../src/contracts/strategies/StrategyBaseTVLLimits.sol"; + +import "../../src/contracts/pods/EigenPod.sol"; +import "../../src/contracts/pods/EigenPodManager.sol"; +import "../../src/contracts/pods/DelayedWithdrawalRouter.sol"; + +import "../../src/contracts/permissions/PauserRegistry.sol"; + +import "../../src/test/mocks/EmptyContract.sol"; +import "../../src/test/mocks/ETHDepositMock.sol"; + +import "forge-std/Script.sol"; +import "forge-std/Test.sol"; + +// # To load the variables in the .env file +// source .env + +// # To deploy and verify our contract +// forge script script/upgrade/M2Deploy.s.sol:M2Deploy --rpc-url $RPC_URL --private-key $PRIVATE_KEY --broadcast -vvvv +contract M2Deploy is Script, Test { + Vm cheats = Vm(HEVM_ADDRESS); + + string public m1DeploymentOutputPath = string(bytes("script/output/M1_deployment_goerli_2023_3_23.json")); + + IETHPOSDeposit public ethPOS; + + ISlasher public slasher; + IDelegationManager public delegation; + DelegationManager public delegationImplementation; + IStrategyManager public strategyManager; + StrategyManager public strategyManagerImplementation; + IEigenPodManager public eigenPodManager; + EigenPodManager public eigenPodManagerImplementation; + IDelayedWithdrawalRouter public delayedWithdrawalRouter; + IBeacon public eigenPodBeacon; + EigenPod public eigenPodImplementation; + + function run() external { + // read and log the chainID + uint256 chainId = block.chainid; + emit log_named_uint("You are deploying on ChainID", chainId); + + if (chainId == 1) { + m1DeploymentOutputPath = string(bytes("script/output/M1_deployment_mainnet_2023_6_9.json")); + } + + // READ JSON DEPLOYMENT DATA + string memory deployment_data = vm.readFile(m1DeploymentOutputPath); + slasher = Slasher(stdJson.readAddress(deployment_data, ".addresses.slasher")); + delegation = slasher.delegation(); + strategyManager = slasher.strategyManager(); + eigenPodManager = strategyManager.eigenPodManager(); + eigenPodBeacon = eigenPodManager.eigenPodBeacon(); + ethPOS = eigenPodManager.ethPOS(); + delayedWithdrawalRouter = EigenPod(payable(eigenPodBeacon.implementation())).delayedWithdrawalRouter(); + + vm.startBroadcast(); + delegationImplementation = new DelegationManager(strategyManager, slasher, eigenPodManager); + strategyManagerImplementation = new StrategyManager(delegation, eigenPodManager, slasher); + eigenPodManagerImplementation = new EigenPodManager( + ethPOS, + eigenPodBeacon, + strategyManager, + slasher, + delegation + ); + eigenPodImplementation = new EigenPod({ + _ethPOS: ethPOS, + _delayedWithdrawalRouter: delayedWithdrawalRouter, + _eigenPodManager: eigenPodManager, + _MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR: 31 gwei, + _RESTAKED_BALANCE_OFFSET_GWEI: 0.5 gwei, + _GENESIS_TIME: 1616508000 + }); + + // write the output to a contract + // WRITE JSON DATA + string memory parent_object = "parent object"; + + string memory deployed_addresses = "addresses"; + vm.serializeAddress(deployed_addresses, "slasher", address(slasher)); + vm.serializeAddress(deployed_addresses, "delegation", address(delegation)); + vm.serializeAddress(deployed_addresses, "strategyManager", address(strategyManager)); + vm.serializeAddress(deployed_addresses, "delayedWithdrawalRouter", address(delayedWithdrawalRouter)); + vm.serializeAddress(deployed_addresses, "eigenPodManager", address(eigenPodManager)); + vm.serializeAddress(deployed_addresses, "eigenPodBeacon", address(eigenPodBeacon)); + vm.serializeAddress(deployed_addresses, "ethPOS", address(ethPOS)); + + vm.serializeAddress(deployed_addresses, "delegationImplementation", address(delegationImplementation)); + vm.serializeAddress( + deployed_addresses, + "strategyManagerImplementation", + address(strategyManagerImplementation) + ); + vm.serializeAddress( + deployed_addresses, + "eigenPodManagerImplementation", + address(eigenPodManagerImplementation) + ); + string memory deployed_addresses_output = vm.serializeAddress( + deployed_addresses, + "eigenPodImplementation", + address(eigenPodImplementation) + ); + + string memory chain_info = "chainInfo"; + vm.serializeUint(chain_info, "deploymentBlock", block.number); + string memory chain_info_output = vm.serializeUint(chain_info, "chainId", chainId); + + vm.serializeString(parent_object, deployed_addresses, deployed_addresses_output); + // serialize all the data + string memory finalJson = vm.serializeString(parent_object, chain_info, chain_info_output); + vm.writeJson(finalJson, "script/output/M2_deployment_data.json"); + } +} diff --git a/script/testing/M2_Deploy_From_Scratch.s.sol b/script/testing/M2_Deploy_From_Scratch.s.sol index e79b300a7..8bb1d312e 100644 --- a/script/testing/M2_Deploy_From_Scratch.s.sol +++ b/script/testing/M2_Deploy_From_Scratch.s.sol @@ -106,12 +106,21 @@ contract Deployer_M2 is Script, Test { DELEGATION_INIT_PAUSED_STATUS = stdJson.readUint(config_data, ".delegation.init_paused_status"); EIGENPOD_MANAGER_MAX_PODS = stdJson.readUint(config_data, ".eigenPodManager.max_pods"); EIGENPOD_MANAGER_INIT_PAUSED_STATUS = stdJson.readUint(config_data, ".eigenPodManager.init_paused_status"); - DELAYED_WITHDRAWAL_ROUTER_INIT_PAUSED_STATUS = stdJson.readUint(config_data, ".delayedWithdrawalRouter.init_paused_status"); + DELAYED_WITHDRAWAL_ROUTER_INIT_PAUSED_STATUS = stdJson.readUint( + config_data, + ".delayedWithdrawalRouter.init_paused_status" + ); - STRATEGY_MANAGER_INIT_WITHDRAWAL_DELAY_BLOCKS = uint32(stdJson.readUint(config_data, ".strategyManager.init_withdrawal_delay_blocks")); - DELAYED_WITHDRAWAL_ROUTER_INIT_WITHDRAWAL_DELAY_BLOCKS = uint32(stdJson.readUint(config_data, ".strategyManager.init_withdrawal_delay_blocks")); + STRATEGY_MANAGER_INIT_WITHDRAWAL_DELAY_BLOCKS = uint32( + stdJson.readUint(config_data, ".strategyManager.init_withdrawal_delay_blocks") + ); + DELAYED_WITHDRAWAL_ROUTER_INIT_WITHDRAWAL_DELAY_BLOCKS = uint32( + stdJson.readUint(config_data, ".strategyManager.init_withdrawal_delay_blocks") + ); - MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR = uint64(stdJson.readUint(config_data, ".eigenPod.MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR")); + MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR = uint64( + stdJson.readUint(config_data, ".eigenPod.MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR") + ); RESTAKED_BALANCE_OFFSET_GWEI = uint64(stdJson.readUint(config_data, ".eigenPod.RESTAKED_BALANCE_OFFSET_GWEI")); // tokens to deploy strategies for @@ -166,7 +175,7 @@ contract Deployer_M2 is Script, Test { // if on mainnet, use the ETH2 deposit contract address if (chainId == 1) { ethPOSDeposit = IETHPOSDeposit(0x00000000219ab540356cBB839Cbe05303d7705Fa); - // if not on mainnet, deploy a mock + // if not on mainnet, deploy a mock } else { ethPOSDeposit = IETHPOSDeposit(stdJson.readAddress(config_data, ".ethPOSDepositAddress")); } @@ -185,7 +194,13 @@ contract Deployer_M2 is Script, Test { delegationImplementation = new DelegationManager(strategyManager, slasher, eigenPodManager); strategyManagerImplementation = new StrategyManager(delegation, eigenPodManager, slasher); slasherImplementation = new Slasher(strategyManager, delegation); - eigenPodManagerImplementation = new EigenPodManager(ethPOSDeposit, eigenPodBeacon, strategyManager, slasher, delegation); + eigenPodManagerImplementation = new EigenPodManager( + ethPOSDeposit, + eigenPodBeacon, + strategyManager, + slasher, + delegation + ); delayedWithdrawalRouterImplementation = new DelayedWithdrawalRouter(eigenPodManager); // Third, upgrade the proxy contracts to use the correct implementation contracts and initialize them. @@ -235,11 +250,13 @@ contract Deployer_M2 is Script, Test { eigenLayerProxyAdmin.upgradeAndCall( TransparentUpgradeableProxy(payable(address(delayedWithdrawalRouter))), address(delayedWithdrawalRouterImplementation), - abi.encodeWithSelector(DelayedWithdrawalRouter.initialize.selector, - executorMultisig, - eigenLayerPauserReg, - DELAYED_WITHDRAWAL_ROUTER_INIT_PAUSED_STATUS, - DELAYED_WITHDRAWAL_ROUTER_INIT_WITHDRAWAL_DELAY_BLOCKS) + abi.encodeWithSelector( + DelayedWithdrawalRouter.initialize.selector, + executorMultisig, + eigenLayerPauserReg, + DELAYED_WITHDRAWAL_ROUTER_INIT_PAUSED_STATUS, + DELAYED_WITHDRAWAL_ROUTER_INIT_WITHDRAWAL_DELAY_BLOCKS + ) ); // deploy StrategyBaseTVLLimits contract implementation @@ -247,13 +264,21 @@ contract Deployer_M2 is Script, Test { // create upgradeable proxies that each point to the implementation and initialize them for (uint256 i = 0; i < strategyConfigs.length; ++i) { deployedStrategyArray.push( - StrategyBaseTVLLimits(address( - new TransparentUpgradeableProxy( - address(baseStrategyImplementation), - address(eigenLayerProxyAdmin), - abi.encodeWithSelector(StrategyBaseTVLLimits.initialize.selector, strategyConfigs[i].maxPerDeposit, strategyConfigs[i].maxDeposits, IERC20(strategyConfigs[i].tokenAddress), eigenLayerPauserReg) + StrategyBaseTVLLimits( + address( + new TransparentUpgradeableProxy( + address(baseStrategyImplementation), + address(eigenLayerProxyAdmin), + abi.encodeWithSelector( + StrategyBaseTVLLimits.initialize.selector, + strategyConfigs[i].maxPerDeposit, + strategyConfigs[i].maxDeposits, + IERC20(strategyConfigs[i].tokenAddress), + eigenLayerPauserReg + ) + ) ) - )) + ) ); } @@ -263,7 +288,6 @@ contract Deployer_M2 is Script, Test { // STOP RECORDING TRANSACTIONS FOR DEPLOYMENT vm.stopBroadcast(); - // CHECK CORRECTNESS OF DEPLOYMENT _verifyContractsPointAtOneAnother( delegationImplementation, @@ -284,7 +308,6 @@ contract Deployer_M2 is Script, Test { _checkPauserInitializations(); _verifyInitializationParams(); - // WRITE JSON DATA string memory parent_object = "parent object"; @@ -292,10 +315,13 @@ contract Deployer_M2 is Script, Test { for (uint256 i = 0; i < strategyConfigs.length; ++i) { vm.serializeAddress(deployed_strategies, strategyConfigs[i].tokenSymbol, address(deployedStrategyArray[i])); } - string memory deployed_strategies_output = strategyConfigs.length == 0 ? "" : vm.serializeAddress( - deployed_strategies, strategyConfigs[strategyConfigs.length - 1].tokenSymbol, - address(deployedStrategyArray[strategyConfigs.length - 1]) - ); + string memory deployed_strategies_output = strategyConfigs.length == 0 + ? "" + : vm.serializeAddress( + deployed_strategies, + strategyConfigs[strategyConfigs.length - 1].tokenSymbol, + address(deployedStrategyArray[strategyConfigs.length - 1]) + ); string memory deployed_addresses = "addresses"; vm.serializeAddress(deployed_addresses, "eigenLayerProxyAdmin", address(eigenLayerProxyAdmin)); @@ -305,16 +331,32 @@ contract Deployer_M2 is Script, Test { vm.serializeAddress(deployed_addresses, "delegation", address(delegation)); vm.serializeAddress(deployed_addresses, "delegationImplementation", address(delegationImplementation)); vm.serializeAddress(deployed_addresses, "strategyManager", address(strategyManager)); - vm.serializeAddress(deployed_addresses, "strategyManagerImplementation", address(strategyManagerImplementation)); + vm.serializeAddress( + deployed_addresses, + "strategyManagerImplementation", + address(strategyManagerImplementation) + ); vm.serializeAddress(deployed_addresses, "eigenPodManager", address(eigenPodManager)); - vm.serializeAddress(deployed_addresses, "eigenPodManagerImplementation", address(eigenPodManagerImplementation)); + vm.serializeAddress( + deployed_addresses, + "eigenPodManagerImplementation", + address(eigenPodManagerImplementation) + ); vm.serializeAddress(deployed_addresses, "delayedWithdrawalRouter", address(delayedWithdrawalRouter)); - vm.serializeAddress(deployed_addresses, "delayedWithdrawalRouterImplementation", address(delayedWithdrawalRouterImplementation)); + vm.serializeAddress( + deployed_addresses, + "delayedWithdrawalRouterImplementation", + address(delayedWithdrawalRouterImplementation) + ); vm.serializeAddress(deployed_addresses, "eigenPodBeacon", address(eigenPodBeacon)); vm.serializeAddress(deployed_addresses, "eigenPodImplementation", address(eigenPodImplementation)); vm.serializeAddress(deployed_addresses, "baseStrategyImplementation", address(baseStrategyImplementation)); vm.serializeAddress(deployed_addresses, "emptyContract", address(emptyContract)); - string memory deployed_addresses_output = vm.serializeString(deployed_addresses, "strategies", deployed_strategies_output); + string memory deployed_addresses_output = vm.serializeString( + deployed_addresses, + "strategies", + deployed_strategies_output + ); string memory parameters = "parameters"; vm.serializeAddress(parameters, "executorMultisig", executorMultisig); @@ -334,56 +376,97 @@ contract Deployer_M2 is Script, Test { } function _verifyContractsPointAtOneAnother( - DelegationManager delegationContract, - StrategyManager strategyManagerContract, - Slasher slasherContract, + DelegationManager delegationContract, + StrategyManager strategyManagerContract, + Slasher slasherContract, EigenPodManager eigenPodManagerContract, DelayedWithdrawalRouter delayedWithdrawalRouterContract ) internal view { require(delegationContract.slasher() == slasher, "delegation: slasher address not set correctly"); - require(delegationContract.strategyManager() == strategyManager, "delegation: strategyManager address not set correctly"); + require( + delegationContract.strategyManager() == strategyManager, + "delegation: strategyManager address not set correctly" + ); require(strategyManagerContract.slasher() == slasher, "strategyManager: slasher address not set correctly"); - require(strategyManagerContract.delegation() == delegation, "strategyManager: delegation address not set correctly"); - require(strategyManagerContract.eigenPodManager() == eigenPodManager, "strategyManager: eigenPodManager address not set correctly"); + require( + strategyManagerContract.delegation() == delegation, + "strategyManager: delegation address not set correctly" + ); + require( + strategyManagerContract.eigenPodManager() == eigenPodManager, + "strategyManager: eigenPodManager address not set correctly" + ); require(slasherContract.strategyManager() == strategyManager, "slasher: strategyManager not set correctly"); require(slasherContract.delegation() == delegation, "slasher: delegation not set correctly"); - require(eigenPodManagerContract.ethPOS() == ethPOSDeposit, " eigenPodManager: ethPOSDeposit contract address not set correctly"); - require(eigenPodManagerContract.eigenPodBeacon() == eigenPodBeacon, "eigenPodManager: eigenPodBeacon contract address not set correctly"); - require(eigenPodManagerContract.strategyManager() == strategyManager, "eigenPodManager: strategyManager contract address not set correctly"); - require(eigenPodManagerContract.slasher() == slasher, "eigenPodManager: slasher contract address not set correctly"); + require( + eigenPodManagerContract.ethPOS() == ethPOSDeposit, + " eigenPodManager: ethPOSDeposit contract address not set correctly" + ); + require( + eigenPodManagerContract.eigenPodBeacon() == eigenPodBeacon, + "eigenPodManager: eigenPodBeacon contract address not set correctly" + ); + require( + eigenPodManagerContract.strategyManager() == strategyManager, + "eigenPodManager: strategyManager contract address not set correctly" + ); + require( + eigenPodManagerContract.slasher() == slasher, + "eigenPodManager: slasher contract address not set correctly" + ); - require(delayedWithdrawalRouterContract.eigenPodManager() == eigenPodManager, - "delayedWithdrawalRouterContract: eigenPodManager address not set correctly"); + require( + delayedWithdrawalRouterContract.eigenPodManager() == eigenPodManager, + "delayedWithdrawalRouterContract: eigenPodManager address not set correctly" + ); } function _verifyImplementationsSetCorrectly() internal view { - require(eigenLayerProxyAdmin.getProxyImplementation( - TransparentUpgradeableProxy(payable(address(delegation)))) == address(delegationImplementation), - "delegation: implementation set incorrectly"); - require(eigenLayerProxyAdmin.getProxyImplementation( - TransparentUpgradeableProxy(payable(address(strategyManager)))) == address(strategyManagerImplementation), - "strategyManager: implementation set incorrectly"); - require(eigenLayerProxyAdmin.getProxyImplementation( - TransparentUpgradeableProxy(payable(address(slasher)))) == address(slasherImplementation), - "slasher: implementation set incorrectly"); - require(eigenLayerProxyAdmin.getProxyImplementation( - TransparentUpgradeableProxy(payable(address(eigenPodManager)))) == address(eigenPodManagerImplementation), - "eigenPodManager: implementation set incorrectly"); - require(eigenLayerProxyAdmin.getProxyImplementation( - TransparentUpgradeableProxy(payable(address(delayedWithdrawalRouter)))) == address(delayedWithdrawalRouterImplementation), - "delayedWithdrawalRouter: implementation set incorrectly"); + require( + eigenLayerProxyAdmin.getProxyImplementation(TransparentUpgradeableProxy(payable(address(delegation)))) == + address(delegationImplementation), + "delegation: implementation set incorrectly" + ); + require( + eigenLayerProxyAdmin.getProxyImplementation( + TransparentUpgradeableProxy(payable(address(strategyManager))) + ) == address(strategyManagerImplementation), + "strategyManager: implementation set incorrectly" + ); + require( + eigenLayerProxyAdmin.getProxyImplementation(TransparentUpgradeableProxy(payable(address(slasher)))) == + address(slasherImplementation), + "slasher: implementation set incorrectly" + ); + require( + eigenLayerProxyAdmin.getProxyImplementation( + TransparentUpgradeableProxy(payable(address(eigenPodManager))) + ) == address(eigenPodManagerImplementation), + "eigenPodManager: implementation set incorrectly" + ); + require( + eigenLayerProxyAdmin.getProxyImplementation( + TransparentUpgradeableProxy(payable(address(delayedWithdrawalRouter))) + ) == address(delayedWithdrawalRouterImplementation), + "delayedWithdrawalRouter: implementation set incorrectly" + ); for (uint256 i = 0; i < deployedStrategyArray.length; ++i) { - require(eigenLayerProxyAdmin.getProxyImplementation( - TransparentUpgradeableProxy(payable(address(deployedStrategyArray[i])))) == address(baseStrategyImplementation), - "strategy: implementation set incorrectly"); + require( + eigenLayerProxyAdmin.getProxyImplementation( + TransparentUpgradeableProxy(payable(address(deployedStrategyArray[i]))) + ) == address(baseStrategyImplementation), + "strategy: implementation set incorrectly" + ); } - require(eigenPodBeacon.implementation() == address(eigenPodImplementation), - "eigenPodBeacon: implementation set incorrectly"); + require( + eigenPodBeacon.implementation() == address(eigenPodImplementation), + "eigenPodBeacon: implementation set incorrectly" + ); } function _verifyInitialOwners() internal view { @@ -394,15 +477,27 @@ contract Deployer_M2 is Script, Test { require(eigenLayerProxyAdmin.owner() == executorMultisig, "eigenLayerProxyAdmin: owner not set correctly"); require(eigenPodBeacon.owner() == executorMultisig, "eigenPodBeacon: owner not set correctly"); - require(delayedWithdrawalRouter.owner() == executorMultisig, "delayedWithdrawalRouter: owner not set correctly"); + require( + delayedWithdrawalRouter.owner() == executorMultisig, + "delayedWithdrawalRouter: owner not set correctly" + ); } function _checkPauserInitializations() internal view { require(delegation.pauserRegistry() == eigenLayerPauserReg, "delegation: pauser registry not set correctly"); - require(strategyManager.pauserRegistry() == eigenLayerPauserReg, "strategyManager: pauser registry not set correctly"); + require( + strategyManager.pauserRegistry() == eigenLayerPauserReg, + "strategyManager: pauser registry not set correctly" + ); require(slasher.pauserRegistry() == eigenLayerPauserReg, "slasher: pauser registry not set correctly"); - require(eigenPodManager.pauserRegistry() == eigenLayerPauserReg, "eigenPodManager: pauser registry not set correctly"); - require(delayedWithdrawalRouter.pauserRegistry() == eigenLayerPauserReg, "delayedWithdrawalRouter: pauser registry not set correctly"); + require( + eigenPodManager.pauserRegistry() == eigenLayerPauserReg, + "eigenPodManager: pauser registry not set correctly" + ); + require( + delayedWithdrawalRouter.pauserRegistry() == eigenLayerPauserReg, + "delayedWithdrawalRouter: pauser registry not set correctly" + ); require(eigenLayerPauserReg.isPauser(operationsMultisig), "pauserRegistry: operationsMultisig is not pauser"); require(eigenLayerPauserReg.isPauser(executorMultisig), "pauserRegistry: executorMultisig is not pauser"); @@ -410,18 +505,24 @@ contract Deployer_M2 is Script, Test { require(eigenLayerPauserReg.unpauser() == executorMultisig, "pauserRegistry: unpauser not set correctly"); for (uint256 i = 0; i < deployedStrategyArray.length; ++i) { - require(deployedStrategyArray[i].pauserRegistry() == eigenLayerPauserReg, "StrategyBaseTVLLimits: pauser registry not set correctly"); - require(deployedStrategyArray[i].paused() == 0, "StrategyBaseTVLLimits: init paused status set incorrectly"); + require( + deployedStrategyArray[i].pauserRegistry() == eigenLayerPauserReg, + "StrategyBaseTVLLimits: pauser registry not set correctly" + ); + require( + deployedStrategyArray[i].paused() == 0, + "StrategyBaseTVLLimits: init paused status set incorrectly" + ); } // // pause *nothing* // uint256 STRATEGY_MANAGER_INIT_PAUSED_STATUS = 0; // // pause *everything* - // uint256 SLASHER_INIT_PAUSED_STATUS = type(uint256).max; + // uint256 SLASHER_INIT_PAUSED_STATUS = type(uint256).max; // // pause *everything* - // uint256 DELEGATION_INIT_PAUSED_STATUS = type(uint256).max; + // uint256 DELEGATION_INIT_PAUSED_STATUS = type(uint256).max; // // pause *all of the proof-related functionality* (everything that can be paused other than creation of EigenPods) - // uint256 EIGENPOD_MANAGER_INIT_PAUSED_STATUS = (2**1) + (2**2) + (2**3) + (2**4); /* = 30 */ + // uint256 EIGENPOD_MANAGER_INIT_PAUSED_STATUS = (2**1) + (2**2) + (2**3) + (2**4); /* = 30 */ // // pause *nothing* // uint256 DELAYED_WITHDRAWAL_ROUTER_INIT_PAUSED_STATUS = 0; // require(strategyManager.paused() == 0, "strategyManager: init paused status set incorrectly"); @@ -440,40 +541,57 @@ contract Deployer_M2 is Script, Test { // require(delayedWithdrawalRouter.withdrawalDelayBlocks() == 7 days / 12 seconds, // "delayedWithdrawalRouter: withdrawalDelayBlocks initialized incorrectly"); // uint256 MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR = 31 ether; - require(eigenPodImplementation.MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR() == 31 gwei, - "eigenPod: MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR initialized incorrectly"); + require( + eigenPodImplementation.MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR() == 31 gwei, + "eigenPod: MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR initialized incorrectly" + ); - require(strategyManager.strategyWhitelister() == operationsMultisig, - "strategyManager: strategyWhitelister address not set correctly"); + require( + strategyManager.strategyWhitelister() == operationsMultisig, + "strategyManager: strategyWhitelister address not set correctly" + ); - require(eigenPodManager.beaconChainOracle() == IBeaconChainOracle(address(0)), - "eigenPodManager: eigenPodBeacon contract address not set correctly"); + require( + eigenPodManager.beaconChainOracle() == IBeaconChainOracle(address(0)), + "eigenPodManager: eigenPodBeacon contract address not set correctly" + ); - require(delayedWithdrawalRouter.eigenPodManager() == eigenPodManager, - "delayedWithdrawalRouter: eigenPodManager set incorrectly"); + require( + delayedWithdrawalRouter.eigenPodManager() == eigenPodManager, + "delayedWithdrawalRouter: eigenPodManager set incorrectly" + ); - require(baseStrategyImplementation.strategyManager() == strategyManager, - "baseStrategyImplementation: strategyManager set incorrectly"); + require( + baseStrategyImplementation.strategyManager() == strategyManager, + "baseStrategyImplementation: strategyManager set incorrectly" + ); - require(eigenPodImplementation.ethPOS() == ethPOSDeposit, - "eigenPodImplementation: ethPOSDeposit contract address not set correctly"); - require(eigenPodImplementation.eigenPodManager() == eigenPodManager, - " eigenPodImplementation: eigenPodManager contract address not set correctly"); - require(eigenPodImplementation.delayedWithdrawalRouter() == delayedWithdrawalRouter, - " eigenPodImplementation: delayedWithdrawalRouter contract address not set correctly"); + require( + eigenPodImplementation.ethPOS() == ethPOSDeposit, + "eigenPodImplementation: ethPOSDeposit contract address not set correctly" + ); + require( + eigenPodImplementation.eigenPodManager() == eigenPodManager, + " eigenPodImplementation: eigenPodManager contract address not set correctly" + ); + require( + eigenPodImplementation.delayedWithdrawalRouter() == delayedWithdrawalRouter, + " eigenPodImplementation: delayedWithdrawalRouter contract address not set correctly" + ); string memory config_data = vm.readFile(deployConfigPath); for (uint i = 0; i < deployedStrategyArray.length; i++) { - uint256 maxPerDeposit = stdJson.readUint(config_data, string.concat(".strategies[", vm.toString(i), "].max_per_deposit")); - uint256 maxDeposits = stdJson.readUint(config_data, string.concat(".strategies[", vm.toString(i), "].max_deposits")); + uint256 maxPerDeposit = stdJson.readUint( + config_data, + string.concat(".strategies[", vm.toString(i), "].max_per_deposit") + ); + uint256 maxDeposits = stdJson.readUint( + config_data, + string.concat(".strategies[", vm.toString(i), "].max_deposits") + ); (uint256 setMaxPerDeposit, uint256 setMaxDeposits) = deployedStrategyArray[i].getTVLLimits(); require(setMaxPerDeposit == maxPerDeposit, "setMaxPerDeposit not set correctly"); require(setMaxDeposits == maxDeposits, "setMaxDeposits not set correctly"); } } } - - - - - diff --git a/src/contracts/interfaces/IBLSPubkeyRegistry.sol b/src/contracts/interfaces/IBLSPubkeyRegistry.sol deleted file mode 100644 index fcae418fd..000000000 --- a/src/contracts/interfaces/IBLSPubkeyRegistry.sol +++ /dev/null @@ -1,85 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity =0.8.12; - -import "./IRegistry.sol"; -import "../libraries/BN254.sol"; - -/** - * @title Minimal interface for a registry that keeps track of aggregate operator public keys for among many quorums. - * @author Layr Labs, Inc. - */ -interface IBLSPubkeyRegistry is IRegistry { - // EVENTS - // Emitted when a new operator pubkey is registered for a set of quorums - event OperatorAddedToQuorums( - address operator, - bytes quorumNumbers - ); - - // Emitted when an operator pubkey is removed from a set of quorums - event OperatorRemovedFromQuorums( - address operator, - bytes quorumNumbers - ); - - /// @notice Data structure used to track the history of the Aggregate Public Key of all operators - struct ApkUpdate { - // first 24 bytes of keccak256(apk_x0, apk_x1, apk_y0, apk_y1) - bytes24 apkHash; - // block number at which the update occurred - uint32 updateBlockNumber; - // block number at which the next update occurred - uint32 nextUpdateBlockNumber; - } - - /** - * @notice Registers the `operator`'s pubkey for the specified `quorumNumbers`. - * @param operator The address of the operator to register. - * @param quorumNumbers The quorum numbers the operator is registering for, where each byte is an 8 bit integer quorumNumber. - * @param pubkey The operator's BLS public key. - * @dev access restricted to the RegistryCoordinator - * @dev Preconditions (these are assumed, not validated in this contract): - * 1) `quorumNumbers` has no duplicates - * 2) `quorumNumbers.length` != 0 - * 3) `quorumNumbers` is ordered in ascending order - * 4) the operator is not already registered - */ - function registerOperator(address operator, bytes calldata quorumNumbers, BN254.G1Point memory pubkey) external returns(bytes32); - - /** - * @notice Deregisters the `operator`'s pubkey for the specified `quorumNumbers`. - * @param operator The address of the operator to deregister. - * @param quorumNumbers The quorum numbers the operator is deregistering from, where each byte is an 8 bit integer quorumNumber. - * @param pubkey The public key of the operator. - * @dev access restricted to the RegistryCoordinator - * @dev Preconditions (these are assumed, not validated in this contract): - * 1) `quorumNumbers` has no duplicates - * 2) `quorumNumbers.length` != 0 - * 3) `quorumNumbers` is ordered in ascending order - * 4) the operator is not already deregistered - * 5) `quorumNumbers` is a subset of the quorumNumbers that the operator is registered for - * 6) `pubkey` is the same as the parameter used when registering - */ - function deregisterOperator(address operator, bytes calldata quorumNumbers, BN254.G1Point memory pubkey) external; - - /// @notice Returns the current APK for the provided `quorumNumber ` - function getApkForQuorum(uint8 quorumNumber) external view returns (BN254.G1Point memory); - - /// @notice Returns the index of the quorumApk index at `blockNumber` for the provided `quorumNumber` - function getApkIndicesForQuorumsAtBlockNumber(bytes calldata quorumNumbers, uint256 blockNumber) external view returns(uint32[] memory); - - /// @notice Returns the `ApkUpdate` struct at `index` in the list of APK updates for the `quorumNumber` - function getApkUpdateForQuorumByIndex(uint8 quorumNumber, uint256 index) external view returns (ApkUpdate memory); - - /// @notice Returns the operator address for the given `pubkeyHash` - function getOperatorFromPubkeyHash(bytes32 pubkeyHash) external view returns (address); - - /** - * @notice get 24 byte hash of the apk of `quorumNumber` at `blockNumber` using the provided `index`; - * called by checkSignatures in BLSSignatureChecker.sol. - * @param quorumNumber is the quorum whose ApkHash is being retrieved - * @param blockNumber is the number of the block for which the latest ApkHash will be retrieved - * @param index is the index of the apkUpdate being retrieved from the list of quorum apkUpdates in storage - */ - function getApkHashForQuorumAtBlockNumberFromIndex(uint8 quorumNumber, uint32 blockNumber, uint256 index) external view returns (bytes24); -} \ No newline at end of file diff --git a/src/contracts/interfaces/IBLSRegistryCoordinatorWithIndices.sol b/src/contracts/interfaces/IBLSRegistryCoordinatorWithIndices.sol deleted file mode 100644 index 24967de9d..000000000 --- a/src/contracts/interfaces/IBLSRegistryCoordinatorWithIndices.sol +++ /dev/null @@ -1,69 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity =0.8.12; - -import "./ISignatureUtils.sol"; -import "./IRegistryCoordinator.sol"; -import "./IStakeRegistry.sol"; -import "./IBLSPubkeyRegistry.sol"; -import "./IIndexRegistry.sol"; - -/** - * @title Minimal interface for the `IBLSStakeRegistryCoordinator` contract. - * @author Layr Labs, Inc. - */ -interface IBLSRegistryCoordinatorWithIndices is ISignatureUtils, IRegistryCoordinator { - // STRUCT - - /** - * @notice Data structure for storing operator set params for a given quorum. Specifically the - * `maxOperatorCount` is the maximum number of operators that can be registered for the quorum, - * `kickBIPsOfOperatorStake` is the basis points of a new operator needs to have of an operator they are trying to kick from the quorum, - * and `kickBIPsOfTotalStake` is the basis points of the total stake of the quorum that an operator needs to be below to be kicked. - */ - struct OperatorSetParam { - uint32 maxOperatorCount; - uint16 kickBIPsOfOperatorStake; - uint16 kickBIPsOfTotalStake; - } - - /** - * @notice Data structure for the parameters needed to kick an operator from a quorum with number `quorumNumber`, used during registration churn. - * Specifically the `operator` is the address of the operator to kick, `pubkey` is the BLS public key of the operator, - */ - struct OperatorKickParam { - uint8 quorumNumber; - address operator; - BN254.G1Point pubkey; - } - - // EVENTS - - event OperatorSetParamsUpdated(uint8 indexed quorumNumber, OperatorSetParam operatorSetParams); - - event ChurnApproverUpdated(address prevChurnApprover, address newChurnApprover); - - event EjectorUpdated(address prevEjector, address newEjector); - - /// @notice Returns the operator set params for the given `quorumNumber` - function getOperatorSetParams(uint8 quorumNumber) external view returns (OperatorSetParam memory); - /// @notice the Stake registry contract that will keep track of operators' stakes - function stakeRegistry() external view returns (IStakeRegistry); - /// @notice the BLS Pubkey Registry contract that will keep track of operators' BLS public keys - function blsPubkeyRegistry() external view returns (IBLSPubkeyRegistry); - /// @notice the Index Registry contract that will keep track of operators' indexes - function indexRegistry() external view returns (IIndexRegistry); - - /** - * @notice Ejects the provided operator from the provided quorums from the AVS - * @param operator is the operator to eject - * @param quorumNumbers are the quorum numbers to eject the operator from - * @param pubkey is the BLS public key of the operator - * @param operatorIdsToSwap is the list of the operator ids tho swap the index of the operator with in each - */ - function ejectOperatorFromCoordinator( - address operator, - bytes calldata quorumNumbers, - BN254.G1Point memory pubkey, - bytes32[] memory operatorIdsToSwap - ) external; -} \ No newline at end of file diff --git a/src/contracts/interfaces/IBLSSignatureChecker.sol b/src/contracts/interfaces/IBLSSignatureChecker.sol deleted file mode 100644 index e69202fcd..000000000 --- a/src/contracts/interfaces/IBLSSignatureChecker.sol +++ /dev/null @@ -1,74 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity =0.8.12; - -import "../interfaces/IBLSRegistryCoordinatorWithIndices.sol"; -import "../libraries/BN254.sol"; -import "../libraries/BitmapUtils.sol"; - -/** - * @title Used for checking BLS aggregate signatures from the operators of a EigenLayer AVS with the RegistryCoordinator/BLSPubkeyRegistry/StakeRegistry architechture. - * @author Layr Labs, Inc. - * @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service - * @notice This is the contract for checking the validity of aggregate operator signatures. - */ -interface IBLSSignatureChecker { - // DATA STRUCTURES - - struct NonSignerStakesAndSignature { - uint32[] nonSignerQuorumBitmapIndices; // is the indices of all nonsigner quorum bitmaps - BN254.G1Point[] nonSignerPubkeys; // is the G1 pubkeys of all nonsigners - BN254.G1Point[] quorumApks; // is the aggregate G1 pubkey of each quorum - BN254.G2Point apkG2; // is the aggregate G2 pubkey of all signers and non signers - BN254.G1Point sigma; // is the aggregate G1 signature of all signers - uint32[] quorumApkIndices; // is the indices of each quorum aggregate pubkey - uint32[] totalStakeIndices; // is the indices of each quorums total stake - uint32[][] nonSignerStakeIndices; // is the indices of each non signers stake within a quorum - } - - /** - * @notice this data structure is used for recording the details on the total stake of the registered - * operators and those operators who are part of the quorum for a particular taskNumber - */ - - struct QuorumStakeTotals { - // total stake of the operators in each quorum - uint96[] signedStakeForQuorum; - // total amount staked by all operators in each quorum - uint96[] totalStakeForQuorum; - } - - // CONSTANTS & IMMUTABLES - - function registryCoordinator() external view returns (IRegistryCoordinator); - function stakeRegistry() external view returns (IStakeRegistry); - function blsPubkeyRegistry() external view returns (IBLSPubkeyRegistry); - - /** - * @notice This function is called by disperser when it has aggregated all the signatures of the operators - * that are part of the quorum for a particular taskNumber and is asserting them into onchain. The function - * checks that the claim for aggregated signatures are valid. - * - * The thesis of this procedure entails: - * - getting the aggregated pubkey of all registered nodes at the time of pre-commit by the - * disperser (represented by apk in the parameters), - * - subtracting the pubkeys of all the signers not in the quorum (nonSignerPubkeys) and storing - * the output in apk to get aggregated pubkey of all operators that are part of quorum. - * - use this aggregated pubkey to verify the aggregated signature under BLS scheme. - * - * @dev Before signature verification, the function verifies operator stake information. This includes ensuring that the provided `referenceBlockNumber` - * is correct, i.e., ensure that the stake returned from the specified block number is recent enough and that the stake is either the most recent update - * for the total stake (or the operator) or latest before the referenceBlockNumber. - */ - function checkSignatures( - bytes32 msgHash, - bytes calldata quorumNumbers, - uint32 referenceBlockNumber, - NonSignerStakesAndSignature memory nonSignerStakesAndSignature - ) - external - view - returns ( - QuorumStakeTotals memory, - bytes32 - ); -} \ No newline at end of file diff --git a/src/contracts/interfaces/ISocketUpdater.sol b/src/contracts/interfaces/ISocketUpdater.sol index 500633c41..7c3de7a21 100644 --- a/src/contracts/interfaces/ISocketUpdater.sol +++ b/src/contracts/interfaces/ISocketUpdater.sol @@ -1,11 +1,6 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity =0.8.12; -import "./IRegistryCoordinator.sol"; -import "./IStakeRegistry.sol"; -import "./IBLSPubkeyRegistry.sol"; -import "./IIndexRegistry.sol"; - /** * @title Interface for an `ISocketUpdater` where operators can update their sockets. * @author Layr Labs, Inc. @@ -16,10 +11,11 @@ interface ISocketUpdater { event OperatorSocketUpdate(bytes32 indexed operatorId, string socket); // FUNCTIONS - + /** * @notice Updates the socket of the msg.sender given they are a registered operator * @param socket is the new socket of the operator */ function updateSocket(string memory socket) external; -} \ No newline at end of file +} + diff --git a/src/contracts/interfaces/IVoteWeigher.sol b/src/contracts/interfaces/IVoteWeigher.sol index a25e300f9..26d145b52 100644 --- a/src/contracts/interfaces/IVoteWeigher.sol +++ b/src/contracts/interfaces/IVoteWeigher.sol @@ -36,10 +36,13 @@ interface IVoteWeigher { /// @notice Returns the EigenLayer strategy manager contract. function strategyManager() external view returns (IStrategyManager); + /// @notice Returns the EigenLayer slasher contract. function slasher() external view returns (ISlasher); + /// @notice Returns the EigenLayer delegation manager contract. function delegation() external view returns (IDelegationManager); + /// @notice Returns the AVS service manager contract. function serviceManager() external view returns (IServiceManager); @@ -47,25 +50,19 @@ interface IVoteWeigher { * @notice This function computes the total weight of the @param operator in the quorum @param quorumNumber. * @dev reverts in the case that `quorumNumber` is greater than or equal to `quorumCount` */ - function weightOfOperatorForQuorumView(uint8 quorumNumber, address operator) external view returns (uint96); - - /** - * @notice This function computes the total weight of the @param operator in the quorum @param quorumNumber. - * @dev reverts in the case that `quorumNumber` is greater than or equal to `quorumCount` - * @dev a version of weightOfOperatorForQuorumView that can change state if needed - */ - function weightOfOperatorForQuorum(uint8 quorumNumber, address operator) external returns (uint96); + function weightOfOperatorForQuorum(uint8 quorumNumber, address operator) external view returns (uint96); /// @notice Number of quorums that are being used by the middleware. function quorumCount() external view returns (uint16); /// @notice Returns the strategy and weight multiplier for the `index`'th strategy in the quorum `quorumNumber` - function strategyAndWeightingMultiplierForQuorumByIndex(uint8 quorumNumber, uint256 index) external view returns (StrategyAndWeightingMultiplier memory); + function strategyAndWeightingMultiplierForQuorumByIndex( + uint8 quorumNumber, + uint256 index + ) external view returns (StrategyAndWeightingMultiplier memory); /// @notice Create a new quorum and add the strategies and their associated weights to the quorum. - function createQuorum( - StrategyAndWeightingMultiplier[] memory _strategiesConsideredAndMultipliers - ) external; + function createQuorum(StrategyAndWeightingMultiplier[] memory _strategiesConsideredAndMultipliers) external; /// @notice Adds new strategies and the associated multipliers to the @param quorumNumber. function addStrategiesConsideredAndMultipliers( @@ -79,10 +76,7 @@ interface IVoteWeigher { * @dev higher indices should be *first* in the list of @param indicesToRemove, since otherwise * the removal of lower index entries will cause a shift in the indices of the other strategiesToRemove */ - function removeStrategiesConsideredAndMultipliers( - uint8 quorumNumber, - uint256[] calldata indicesToRemove - ) external; + function removeStrategiesConsideredAndMultipliers(uint8 quorumNumber, uint256[] calldata indicesToRemove) external; /** * @notice This function is used for modifying the weights of strategies that are already in the diff --git a/src/contracts/interfaces/IWhitelister.sol b/src/contracts/interfaces/IWhitelister.sol index 7bdd1f6ed..da68992c8 100644 --- a/src/contracts/interfaces/IWhitelister.sol +++ b/src/contracts/interfaces/IWhitelister.sol @@ -4,7 +4,6 @@ pragma solidity >=0.5.0; import "../../contracts/interfaces/IStrategyManager.sol"; import "../../contracts/interfaces/IStrategy.sol"; import "../../contracts/interfaces/IDelegationManager.sol"; -import "../../contracts/interfaces/IBLSRegistry.sol"; import "../../../script/whitelist/Staker.sol"; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; diff --git a/src/contracts/middleware/BLSOperatorStateRetriever.sol b/src/contracts/middleware/BLSOperatorStateRetriever.sol deleted file mode 100644 index 3cc8d87a8..000000000 --- a/src/contracts/middleware/BLSOperatorStateRetriever.sol +++ /dev/null @@ -1,149 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity =0.8.12; - -import "./BLSRegistryCoordinatorWithIndices.sol"; - -import "../interfaces/IStakeRegistry.sol"; -import "../interfaces/IBLSPubkeyRegistry.sol"; -import "../interfaces/IIndexRegistry.sol"; -import "../interfaces/IBLSRegistryCoordinatorWithIndices.sol"; - -/** - * @title BLSOperatorStateRetriever with view functions that allow to retrieve the state of an AVSs registry system. - * @author Layr Labs Inc. - */ -contract BLSOperatorStateRetriever { - struct Operator { - bytes32 operatorId; - uint96 stake; - } - - struct CheckSignaturesIndices { - uint32[] nonSignerQuorumBitmapIndices; - uint32[] quorumApkIndices; - uint32[] totalStakeIndices; - uint32[][] nonSignerStakeIndices; // nonSignerStakeIndices[quorumNumberIndex][nonSignerIndex] - } - - /** - * @notice This function is intended to to be called by AVS operators every time a new task is created (i.e.) - * the AVS coordinator makes a request to AVS operators. Since all of the crucial information is kept onchain, - * operators don't need to run indexers to fetch the data. - * @param registryCoordinator is the registry coordinator to fetch the AVS registry information from - * @param operatorId the id of the operator to fetch the quorums lists - * @param blockNumber is the block number to get the operator state for - * @return 1) the quorumBitmap of the operator at the given blockNumber - * 2) 2d array of Operator structs. For each quorum the provided operator - * was a part of at `blockNumber`, an ordered list of operators. - */ - function getOperatorState(IBLSRegistryCoordinatorWithIndices registryCoordinator, bytes32 operatorId, uint32 blockNumber) external view returns (uint256, Operator[][] memory) { - bytes32[] memory operatorIds = new bytes32[](1); - operatorIds[0] = operatorId; - uint256 index = registryCoordinator.getQuorumBitmapIndicesByOperatorIdsAtBlockNumber(blockNumber, operatorIds)[0]; - - uint256 quorumBitmap = registryCoordinator.getQuorumBitmapByOperatorIdAtBlockNumberByIndex(operatorId, blockNumber, index); - - bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); - - return (quorumBitmap, getOperatorState(registryCoordinator, quorumNumbers, blockNumber)); - } - - /** - * @notice returns the ordered list of operators (id and stake) for each quorum. The AVS coordinator - * may call this function directly to get the operator state for a given block number - * @param registryCoordinator is the registry coordinator to fetch the AVS registry information from - * @param quorumNumbers are the ids of the quorums to get the operator state for - * @param blockNumber is the block number to get the operator state for - * @return 2d array of operators. For each quorum, an ordered list of operators - */ - function getOperatorState(IBLSRegistryCoordinatorWithIndices registryCoordinator, bytes memory quorumNumbers, uint32 blockNumber) public view returns(Operator[][] memory) { - IStakeRegistry stakeRegistry = registryCoordinator.stakeRegistry(); - IIndexRegistry indexRegistry = registryCoordinator.indexRegistry(); - - Operator[][] memory operators = new Operator[][](quorumNumbers.length); - for (uint256 i = 0; i < quorumNumbers.length; i++) { - uint8 quorumNumber = uint8(quorumNumbers[i]); - bytes32[] memory operatorIds = indexRegistry.getOperatorListForQuorumAtBlockNumber(quorumNumber, blockNumber); - operators[i] = new Operator[](operatorIds.length); - for (uint256 j = 0; j < operatorIds.length; j++) { - bytes32 operatorId = bytes32(operatorIds[j]); - operators[i][j] = Operator({ - operatorId: operatorId, - stake: stakeRegistry.getStakeForOperatorIdForQuorumAtBlockNumber(operatorId, quorumNumber, blockNumber) - }); - } - } - - return operators; - } - - /** - * @notice this is called by the AVS operator to get the relevant indices for the checkSignatures function - * if they are not running an indexer - * @param registryCoordinator is the registry coordinator to fetch the AVS registry information from - * @param referenceBlockNumber is the block number to get the indices for - * @param quorumNumbers are the ids of the quorums to get the operator state for - * @param nonSignerOperatorIds are the ids of the nonsigning operators - * @return 1) the indices of the quorumBitmaps for each of the operators in the @param nonSignerOperatorIds array at the given blocknumber - * 2) the indices of the total stakes entries for the given quorums at the given blocknumber - * 3) the indices of the stakes of each of the nonsigners in each of the quorums they were a - * part of (for each nonsigner, an array of length the number of quorums they were a part of - * that are also part of the provided quorumNumbers) at the given blocknumber - * 4) the indices of the quorum apks for each of the provided quorums at the given blocknumber - */ - function getCheckSignaturesIndices( - IBLSRegistryCoordinatorWithIndices registryCoordinator, - uint32 referenceBlockNumber, - bytes calldata quorumNumbers, - bytes32[] calldata nonSignerOperatorIds - ) external view returns (CheckSignaturesIndices memory) { - IStakeRegistry stakeRegistry = registryCoordinator.stakeRegistry(); - CheckSignaturesIndices memory checkSignaturesIndices; - - // get the indices of the quorumBitmap updates for each of the operators in the nonSignerOperatorIds array - checkSignaturesIndices.nonSignerQuorumBitmapIndices = registryCoordinator.getQuorumBitmapIndicesByOperatorIdsAtBlockNumber(referenceBlockNumber, nonSignerOperatorIds); - // get the indices of the totalStake updates for each of the quorums in the quorumNumbers array - checkSignaturesIndices.totalStakeIndices = stakeRegistry.getTotalStakeIndicesByQuorumNumbersAtBlockNumber(referenceBlockNumber, quorumNumbers); - - checkSignaturesIndices.nonSignerStakeIndices = new uint32[][](quorumNumbers.length); - for (uint8 quorumNumberIndex = 0; quorumNumberIndex < quorumNumbers.length; quorumNumberIndex++) { - uint256 numNonSignersForQuorum = 0; - // this array's length will be at most the number of nonSignerOperatorIds, this will be trimmed after it is filled - checkSignaturesIndices.nonSignerStakeIndices[quorumNumberIndex] = new uint32[](nonSignerOperatorIds.length); - - for (uint i = 0; i < nonSignerOperatorIds.length; i++) { - // get the quorumBitmap for the operator at the given blocknumber and index - uint192 nonSignerQuorumBitmap = - registryCoordinator.getQuorumBitmapByOperatorIdAtBlockNumberByIndex( - nonSignerOperatorIds[i], - referenceBlockNumber, - checkSignaturesIndices.nonSignerQuorumBitmapIndices[i] - ); - - // if the operator was a part of the quorum and the quorum is a part of the provided quorumNumbers - if ((nonSignerQuorumBitmap >> uint8(quorumNumbers[quorumNumberIndex])) & 1 == 1) { - // get the index of the stake update for the operator at the given blocknumber and quorum number - checkSignaturesIndices.nonSignerStakeIndices[quorumNumberIndex][numNonSignersForQuorum] = stakeRegistry.getStakeUpdateIndexForOperatorIdForQuorumAtBlockNumber( - nonSignerOperatorIds[i], - uint8(quorumNumbers[quorumNumberIndex]), - referenceBlockNumber - ); - numNonSignersForQuorum++; - } - } - - // resize the array to the number of nonSigners for this quorum - uint32[] memory nonSignerStakeIndicesForQuorum = new uint32[](numNonSignersForQuorum); - for (uint i = 0; i < numNonSignersForQuorum; i++) { - nonSignerStakeIndicesForQuorum[i] = checkSignaturesIndices.nonSignerStakeIndices[quorumNumberIndex][i]; - } - checkSignaturesIndices.nonSignerStakeIndices[quorumNumberIndex] = nonSignerStakeIndicesForQuorum; - } - - IBLSPubkeyRegistry blsPubkeyRegistry = registryCoordinator.blsPubkeyRegistry(); - // get the indices of the quorum apks for each of the provided quorums at the given blocknumber - checkSignaturesIndices.quorumApkIndices = blsPubkeyRegistry.getApkIndicesForQuorumsAtBlockNumber(quorumNumbers, referenceBlockNumber); - - return checkSignaturesIndices; - } -} diff --git a/src/contracts/middleware/BLSPubkeyRegistry.sol b/src/contracts/middleware/BLSPubkeyRegistry.sol deleted file mode 100644 index 4dc60c698..000000000 --- a/src/contracts/middleware/BLSPubkeyRegistry.sol +++ /dev/null @@ -1,233 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity =0.8.12; - -import "./BLSPubkeyRegistryStorage.sol"; -import "../libraries/BN254.sol"; - -contract BLSPubkeyRegistry is BLSPubkeyRegistryStorage { - using BN254 for BN254.G1Point; - - /// @notice when applied to a function, only allows the RegistryCoordinator to call it - modifier onlyRegistryCoordinator() { - require( - msg.sender == address(registryCoordinator), - "BLSPubkeyRegistry.onlyRegistryCoordinator: caller is not the registry coordinator" - ); - _; - } - - /// @notice Sets the (immutable) `registryCoordinator` and `pubkeyCompendium` addresses - constructor( - IRegistryCoordinator _registryCoordinator, - IBLSPublicKeyCompendium _pubkeyCompendium - ) BLSPubkeyRegistryStorage(_registryCoordinator, _pubkeyCompendium) {} - - /** - * @notice Registers the `operator`'s pubkey for the specified `quorumNumbers`. - * @param operator The address of the operator to register. - * @param quorumNumbers The quorum numbers the operator is registering for, where each byte is an 8 bit integer quorumNumber. - * @param pubkey The operator's BLS public key. - * @return pubkeyHash of the operator's pubkey - * @dev access restricted to the RegistryCoordinator - * @dev Preconditions (these are assumed, not validated in this contract): - * 1) `quorumNumbers` has no duplicates - * 2) `quorumNumbers.length` != 0 - * 3) `quorumNumbers` is ordered in ascending order - * 4) the operator is not already registered - */ - function registerOperator( - address operator, - bytes memory quorumNumbers, - BN254.G1Point memory pubkey - ) external onlyRegistryCoordinator returns (bytes32) { - _beforeRegisterOperator(operator, quorumNumbers); - //calculate hash of the operator's pubkey - bytes32 pubkeyHash = BN254.hashG1Point(pubkey); - - require(pubkeyHash != ZERO_PK_HASH, "BLSPubkeyRegistry.registerOperator: cannot register zero pubkey"); - //ensure that the operator owns their public key by referencing the BLSPubkeyCompendium - require( - getOperatorFromPubkeyHash(pubkeyHash) == operator, - "BLSPubkeyRegistry.registerOperator: operator does not own pubkey" - ); - // update each quorum's aggregate pubkey - _processQuorumApkUpdate(quorumNumbers, pubkey); - - _afterRegisterOperator(operator, quorumNumbers); - // emit event so offchain actors can update their state - emit OperatorAddedToQuorums(operator, quorumNumbers); - return pubkeyHash; - } - - /** - * @notice Deregisters the `operator`'s pubkey for the specified `quorumNumbers`. - * @param operator The address of the operator to deregister. - * @param quorumNumbers The quorum numbers the operator is deregistering from, where each byte is an 8 bit integer quorumNumber. - * @param pubkey The public key of the operator. - * @dev access restricted to the RegistryCoordinator - * @dev Preconditions (these are assumed, not validated in this contract): - * 1) `quorumNumbers` has no duplicates - * 2) `quorumNumbers.length` != 0 - * 3) `quorumNumbers` is ordered in ascending order - * 4) the operator is not already deregistered - * 5) `quorumNumbers` is a subset of the quorumNumbers that the operator is registered for - * 6) `pubkey` is the same as the parameter used when registering - */ - function deregisterOperator( - address operator, - bytes memory quorumNumbers, - BN254.G1Point memory pubkey - ) external onlyRegistryCoordinator { - _beforeDeregisterOperator(operator, quorumNumbers); - bytes32 pubkeyHash = BN254.hashG1Point(pubkey); - - require( - getOperatorFromPubkeyHash(pubkeyHash) == operator, - "BLSPubkeyRegistry.registerOperator: operator does not own pubkey" - ); - - // update each quorum's aggregate pubkey - _processQuorumApkUpdate(quorumNumbers, pubkey.negate()); - - _afterDeregisterOperator(operator, quorumNumbers); - - emit OperatorRemovedFromQuorums(operator, quorumNumbers); - } - - /** - * @notice Returns the indices of the quorumApks index at `blockNumber` for the provided `quorumNumbers` - * @dev Returns the current indices if `blockNumber >= block.number` - */ - function getApkIndicesForQuorumsAtBlockNumber( - bytes calldata quorumNumbers, - uint256 blockNumber - ) external view returns (uint32[] memory) { - uint32[] memory indices = new uint32[](quorumNumbers.length); - for (uint i = 0; i < quorumNumbers.length; i++) { - uint8 quorumNumber = uint8(quorumNumbers[i]); - uint32 quorumApkUpdatesLength = uint32(quorumApkUpdates[quorumNumber].length); - - if (quorumApkUpdatesLength == 0 || blockNumber < quorumApkUpdates[quorumNumber][0].updateBlockNumber) { - revert( - "BLSPubkeyRegistry.getApkIndicesForQuorumsAtBlockNumber: blockNumber is before the first update" - ); - } - - for (uint32 j = 0; j < quorumApkUpdatesLength; j++) { - if (quorumApkUpdates[quorumNumber][quorumApkUpdatesLength - j - 1].updateBlockNumber <= blockNumber) { - indices[i] = quorumApkUpdatesLength - j - 1; - break; - } - } - } - return indices; - } - - /// @notice Returns the current APK for the provided `quorumNumber ` - function getApkForQuorum(uint8 quorumNumber) external view returns (BN254.G1Point memory) { - return quorumApk[quorumNumber]; - } - - /// @notice Returns the `ApkUpdate` struct at `index` in the list of APK updates for the `quorumNumber` - function getApkUpdateForQuorumByIndex(uint8 quorumNumber, uint256 index) external view returns (ApkUpdate memory) { - return quorumApkUpdates[quorumNumber][index]; - } - - /** - * @notice get hash of the apk of `quorumNumber` at `blockNumber` using the provided `index`; - * called by checkSignatures in BLSSignatureChecker.sol. - * @param quorumNumber is the quorum whose ApkHash is being retrieved - * @param blockNumber is the number of the block for which the latest ApkHash will be retrieved - * @param index is the index of the apkUpdate being retrieved from the list of quorum apkUpdates in storage - */ - function getApkHashForQuorumAtBlockNumberFromIndex( - uint8 quorumNumber, - uint32 blockNumber, - uint256 index - ) external view returns (bytes24) { - ApkUpdate memory quorumApkUpdate = quorumApkUpdates[quorumNumber][index]; - _validateApkHashForQuorumAtBlockNumber(quorumApkUpdate, blockNumber); - return quorumApkUpdate.apkHash; - } - - /// @notice Returns the length of ApkUpdates for the provided `quorumNumber` - function getQuorumApkHistoryLength(uint8 quorumNumber) external view returns (uint32) { - return uint32(quorumApkUpdates[quorumNumber].length); - } - - /// @notice Returns the operator address for the given `pubkeyHash` - function getOperatorFromPubkeyHash(bytes32 pubkeyHash) public view returns (address) { - return pubkeyCompendium.pubkeyHashToOperator(pubkeyHash); - } - - function _processQuorumApkUpdate(bytes memory quorumNumbers, BN254.G1Point memory point) internal { - BN254.G1Point memory apkAfterUpdate; - - for (uint i = 0; i < quorumNumbers.length; ) { - uint8 quorumNumber = uint8(quorumNumbers[i]); - - uint256 quorumApkUpdatesLength = quorumApkUpdates[quorumNumber].length; - if (quorumApkUpdatesLength > 0) { - // update nextUpdateBlockNumber of the current latest ApkUpdate - quorumApkUpdates[quorumNumber][quorumApkUpdatesLength - 1].nextUpdateBlockNumber = uint32(block.number); - } - - apkAfterUpdate = quorumApk[quorumNumber].plus(point); - - //update aggregate public key for this quorum - quorumApk[quorumNumber] = apkAfterUpdate; - //create new ApkUpdate to add to the mapping - ApkUpdate memory latestApkUpdate; - latestApkUpdate.apkHash = bytes24(BN254.hashG1Point(apkAfterUpdate)); - latestApkUpdate.updateBlockNumber = uint32(block.number); - quorumApkUpdates[quorumNumber].push(latestApkUpdate); - - unchecked { - ++i; - } - } - } - - /** - * @dev Hook that is called before any operator registration to insert additional logic. - * @param operator The address of the operator to register. - * @param quorumNumbers The quorum numbers the operator is registering for, where each byte is an 8 bit integer quorumNumber. - */ - function _beforeRegisterOperator(address operator, bytes memory quorumNumbers) internal virtual {} - - /** - * @dev Hook that is called after any operator registration to insert additional logic. - * @param operator The address of the operator to register. - * @param quorumNumbers The quorum numbers the operator is registering for, where each byte is an 8 bit integer quorumNumber. - */ - function _afterRegisterOperator(address operator, bytes memory quorumNumbers) internal virtual {} - - /** - * @dev Hook that is called before any operator deregistration to insert additional logic. - * @param operator The address of the operator to deregister. - * @param quorumNumbers The quorum numbers the operator is registering for, where each byte is an 8 bit integer quorumNumber. - */ - function _beforeDeregisterOperator(address operator, bytes memory quorumNumbers) internal virtual {} - - /** - * @dev Hook that is called after any operator deregistration to insert additional logic. - * @param operator The address of the operator to deregister. - * @param quorumNumbers The quorum numbers the operator is registering for, where each byte is an 8 bit integer quorumNumber. - */ - function _afterDeregisterOperator(address operator, bytes memory quorumNumbers) internal virtual {} - - function _validateApkHashForQuorumAtBlockNumber(ApkUpdate memory apkUpdate, uint32 blockNumber) internal pure { - require( - blockNumber >= apkUpdate.updateBlockNumber, - "BLSPubkeyRegistry._validateApkHashForQuorumAtBlockNumber: index too recent" - ); - /** - * if there is a next update, check that the blockNumber is before the next update or if - * there is no next update, then apkUpdate.nextUpdateBlockNumber is 0. - */ - require( - apkUpdate.nextUpdateBlockNumber == 0 || blockNumber < apkUpdate.nextUpdateBlockNumber, - "BLSPubkeyRegistry._validateApkHashForQuorumAtBlockNumber: not latest apk update" - ); - } -} diff --git a/src/contracts/middleware/BLSPubkeyRegistryStorage.sol b/src/contracts/middleware/BLSPubkeyRegistryStorage.sol deleted file mode 100644 index 0254d8105..000000000 --- a/src/contracts/middleware/BLSPubkeyRegistryStorage.sol +++ /dev/null @@ -1,32 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity =0.8.12; - -import "../interfaces/IBLSPubkeyRegistry.sol"; -import "../interfaces/IRegistryCoordinator.sol"; -import "../interfaces/IBLSPublicKeyCompendium.sol"; - -import "@openzeppelin-upgrades/contracts/proxy/utils/Initializable.sol"; - -abstract contract BLSPubkeyRegistryStorage is Initializable, IBLSPubkeyRegistry { - /// @notice the hash of the zero pubkey aka BN254.G1Point(0,0) - bytes32 internal constant ZERO_PK_HASH = hex"ad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5"; - /// @notice the registry coordinator contract - IRegistryCoordinator public immutable registryCoordinator; - /// @notice the BLSPublicKeyCompendium contract against which pubkey ownership is checked - IBLSPublicKeyCompendium public immutable pubkeyCompendium; - - /// @notice mapping of quorumNumber => ApkUpdate[], tracking the aggregate pubkey updates of every quorum - mapping(uint8 => ApkUpdate[]) public quorumApkUpdates; - /// @notice mapping of quorumNumber => current aggregate pubkey of quorum - mapping(uint8 => BN254.G1Point) public quorumApk; - - constructor(IRegistryCoordinator _registryCoordinator, IBLSPublicKeyCompendium _pubkeyCompendium) { - registryCoordinator = _registryCoordinator; - pubkeyCompendium = _pubkeyCompendium; - // disable initializers so that the implementation contract cannot be initialized - _disableInitializers(); - } - - // storage gap for upgradeability - uint256[48] private __GAP; -} diff --git a/src/contracts/middleware/BLSPublicKeyCompendium.sol b/src/contracts/middleware/BLSPublicKeyCompendium.sol deleted file mode 100644 index 4af4a14d0..000000000 --- a/src/contracts/middleware/BLSPublicKeyCompendium.sol +++ /dev/null @@ -1,78 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity =0.8.12; - -import "../interfaces/IBLSPublicKeyCompendium.sol"; -import "../libraries/BN254.sol"; - -/** - * @title A shared contract for EigenLayer operators to register their BLS public keys. - * @author Layr Labs, Inc. - * @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service - */ -contract BLSPublicKeyCompendium is IBLSPublicKeyCompendium { - using BN254 for BN254.G1Point; - - /// @notice mapping from operator address to pubkey hash - mapping(address => bytes32) public operatorToPubkeyHash; - /// @notice mapping from pubkey hash to operator address - mapping(bytes32 => address) public pubkeyHashToOperator; - - /** - * @notice Called by an operator to register themselves as the owner of a BLS public key and reveal their G1 and G2 public key. - * @param signedMessageHash is the registration message hash signed by the private key of the operator - * @param pubkeyG1 is the corresponding G1 public key of the operator - * @param pubkeyG2 is the corresponding G2 public key of the operator - */ - function registerBLSPublicKey(BN254.G1Point memory signedMessageHash, BN254.G1Point memory pubkeyG1, BN254.G2Point memory pubkeyG2) external { - bytes32 pubkeyHash = BN254.hashG1Point(pubkeyG1); - require( - operatorToPubkeyHash[msg.sender] == bytes32(0), - "BLSPublicKeyCompendium.registerBLSPublicKey: operator already registered pubkey" - ); - require( - pubkeyHashToOperator[pubkeyHash] == address(0), - "BLSPublicKeyCompendium.registerBLSPublicKey: public key already registered" - ); - - // H(m) - BN254.G1Point memory messageHash = getMessageHash(msg.sender); - - // gamma = h(sigma, P, P', H(m)) - uint256 gamma = uint256(keccak256(abi.encodePacked( - signedMessageHash.X, - signedMessageHash.Y, - pubkeyG1.X, - pubkeyG1.Y, - pubkeyG2.X, - pubkeyG2.Y, - messageHash.X, - messageHash.Y - ))) % BN254.FR_MODULUS; - - // e(sigma + P * gamma, [-1]_2) = e(H(m) + [1]_1 * gamma, P') - require(BN254.pairing( - signedMessageHash.plus(pubkeyG1.scalar_mul(gamma)), - BN254.negGeneratorG2(), - messageHash.plus(BN254.generatorG1().scalar_mul(gamma)), - pubkeyG2 - ), "BLSPublicKeyCompendium.registerBLSPublicKey: G1 and G2 private key do not match"); - - operatorToPubkeyHash[msg.sender] = pubkeyHash; - pubkeyHashToOperator[pubkeyHash] = msg.sender; - - emit NewPubkeyRegistration(msg.sender, pubkeyG1, pubkeyG2); - } - - /** - * @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 getMessageHash(address operator) public view returns (BN254.G1Point memory) { - return BN254.hashToG1(keccak256(abi.encodePacked( - operator, - address(this), - block.chainid, - "EigenLayer_BN254_Pubkey_Registration" - ))); - } -} diff --git a/src/contracts/middleware/BLSRegistryCoordinatorWithIndices.sol b/src/contracts/middleware/BLSRegistryCoordinatorWithIndices.sol deleted file mode 100644 index dcf53752e..000000000 --- a/src/contracts/middleware/BLSRegistryCoordinatorWithIndices.sol +++ /dev/null @@ -1,620 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity =0.8.12; - -import "@openzeppelin/contracts/utils/cryptography/draft-EIP712.sol"; -import "@openzeppelin-upgrades/contracts/proxy/utils/Initializable.sol"; - -import "../interfaces/IBLSRegistryCoordinatorWithIndices.sol"; -import "../interfaces/ISocketUpdater.sol"; -import "../interfaces/IServiceManager.sol"; -import "../interfaces/IBLSPubkeyRegistry.sol"; -import "../interfaces/IVoteWeigher.sol"; -import "../interfaces/IStakeRegistry.sol"; -import "../interfaces/IIndexRegistry.sol"; -import "../interfaces/IPauserRegistry.sol"; - -import "../libraries/EIP1271SignatureUtils.sol"; -import "../libraries/BitmapUtils.sol"; - -import "../permissions/Pausable.sol"; - -/** - * @title A `RegistryCoordinator` that has three registries: - * 1) a `StakeRegistry` that keeps track of operators' stakes - * 2) a `BLSPubkeyRegistry` that keeps track of operators' BLS public keys and aggregate BLS public keys for each quorum - * 3) an `IndexRegistry` that keeps track of an ordered list of operators for each quorum - * - * @author Layr Labs, Inc. - */ -contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistryCoordinatorWithIndices, ISocketUpdater, Pausable { - using BN254 for BN254.G1Point; - - /// @notice The EIP-712 typehash for the `DelegationApproval` struct used by the contract - bytes32 public constant OPERATOR_CHURN_APPROVAL_TYPEHASH = - keccak256("OperatorChurnApproval(bytes32 registeringOperatorId,OperatorKickParam[] operatorKickParams)OperatorKickParam(address operator,BN254.G1Point pubkey,bytes32[] operatorIdsToSwap)BN254.G1Point(uint256 x,uint256 y)"); - /// @notice The maximum value of a quorum bitmap - uint256 internal constant MAX_QUORUM_BITMAP = type(uint192).max; - /// @notice The basis point denominator - uint16 internal constant BIPS_DENOMINATOR = 10000; - /// @notice Index for flag that pauses operator registration - uint8 internal constant PAUSED_REGISTER_OPERATOR = 0; - /// @notice Index for flag that pauses operator deregistration - uint8 internal constant PAUSED_DEREGISTER_OPERATOR = 1; - - /// @notice the EigenLayer Slasher - ISlasher public immutable slasher; - /// @notice the Service Manager for the service that this contract is coordinating - IServiceManager public immutable serviceManager; - /// @notice the BLS Pubkey Registry contract that will keep track of operators' BLS public keys - IBLSPubkeyRegistry public immutable blsPubkeyRegistry; - /// @notice the Stake Registry contract that will keep track of operators' stakes - IStakeRegistry public immutable stakeRegistry; - /// @notice the Index Registry contract that will keep track of operators' indexes - IIndexRegistry public immutable indexRegistry; - - /// @notice the mapping from quorum number to a quorums operator cap and kick parameters - mapping(uint8 => OperatorSetParam) internal _quorumOperatorSetParams; - /// @notice the mapping from operator's operatorId to the updates of the bitmap of quorums they are registered for - mapping(bytes32 => QuorumBitmapUpdate[]) internal _operatorIdToQuorumBitmapHistory; - /// @notice the mapping from operator's address to the operator struct - mapping(address => Operator) internal _operators; - /// @notice whether the salt has been used for an operator churn approval - mapping(bytes32 => bool) public isChurnApproverSaltUsed; - - /// @notice the dynamic-length array of the registries this coordinator is coordinating - address[] public registries; - /// @notice the address of the entity allowed to sign off on operators getting kicked out of the AVS during registration - address public churnApprover; - /// @notice the address of the entity allowed to eject operators from the AVS - address public ejector; - - modifier onlyServiceManagerOwner { - require(msg.sender == serviceManager.owner(), "BLSRegistryCoordinatorWithIndices.onlyServiceManagerOwner: caller is not the service manager owner"); - _; - } - - modifier onlyEjector { - require(msg.sender == ejector, "BLSRegistryCoordinatorWithIndices.onlyEjector: caller is not the ejector"); - _; - } - - constructor( - ISlasher _slasher, - IServiceManager _serviceManager, - IStakeRegistry _stakeRegistry, - IBLSPubkeyRegistry _blsPubkeyRegistry, - IIndexRegistry _indexRegistry - ) EIP712("AVSRegistryCoordinator", "v0.0.1") { - slasher = _slasher; - serviceManager = _serviceManager; - stakeRegistry = _stakeRegistry; - blsPubkeyRegistry = _blsPubkeyRegistry; - indexRegistry = _indexRegistry; - } - - function initialize( - address _churnApprover, - address _ejector, - OperatorSetParam[] memory _operatorSetParams, - IPauserRegistry _pauserRegistry, - uint256 _initialPausedStatus - ) external initializer { - // set initial paused status - _initializePauser(_pauserRegistry, _initialPausedStatus); - // set the churnApprover - _setChurnApprover(_churnApprover); - // set the ejector - _setEjector(_ejector); - // add registry contracts to the registries array - registries.push(address(stakeRegistry)); - registries.push(address(blsPubkeyRegistry)); - registries.push(address(indexRegistry)); - - // set the operator set params - require(IVoteWeigher(address(stakeRegistry)).quorumCount() == _operatorSetParams.length, "BLSRegistryCoordinatorWithIndices: operator set params length mismatch"); - for (uint8 i = 0; i < _operatorSetParams.length; i++) { - _setOperatorSetParams(i, _operatorSetParams[i]); - } - } - - // VIEW FUNCTIONS - - /// @notice Returns the operator set params for the given `quorumNumber` - function getOperatorSetParams(uint8 quorumNumber) external view returns (OperatorSetParam memory) { - return _quorumOperatorSetParams[quorumNumber]; - } - - /// @notice Returns the operator struct for the given `operator` - function getOperator(address operator) external view returns (Operator memory) { - return _operators[operator]; - } - - /// @notice Returns the operatorId for the given `operator` - function getOperatorId(address operator) external view returns (bytes32) { - return _operators[operator].operatorId; - } - - /// @notice Returns the operator address for the given `operatorId` - function getOperatorFromId(bytes32 operatorId) external view returns (address) { - return blsPubkeyRegistry.getOperatorFromPubkeyHash(operatorId); - } - - /// @notice Returns the status for the given `operator` - function getOperatorStatus(address operator) external view returns (IRegistryCoordinator.OperatorStatus) { - return _operators[operator].status; - } - - /// @notice Returns the indices of the quorumBitmaps for the provided `operatorIds` at the given `blockNumber` - function getQuorumBitmapIndicesByOperatorIdsAtBlockNumber(uint32 blockNumber, bytes32[] memory operatorIds) external view returns (uint32[] memory) { - uint32[] memory indices = new uint32[](operatorIds.length); - for (uint256 i = 0; i < operatorIds.length; i++) { - uint32 length = uint32(_operatorIdToQuorumBitmapHistory[operatorIds[i]].length); - for (uint32 j = 0; j < length; j++) { - if (_operatorIdToQuorumBitmapHistory[operatorIds[i]][length - j - 1].updateBlockNumber <= blockNumber) { - require( - _operatorIdToQuorumBitmapHistory[operatorIds[i]][length - j - 1].nextUpdateBlockNumber == 0 || - _operatorIdToQuorumBitmapHistory[operatorIds[i]][length - j - 1].nextUpdateBlockNumber > blockNumber, - "BLSRegistryCoordinatorWithIndices.getQuorumBitmapIndicesByOperatorIdsAtBlockNumber: operatorId has no quorumBitmaps at blockNumber" - ); - indices[i] = length - j - 1; - break; - } - } - } - return indices; - } - - /** - * @notice Returns the quorum bitmap for the given `operatorId` at the given `blockNumber` via the `index` - * @dev reverts if `index` is incorrect - */ - function getQuorumBitmapByOperatorIdAtBlockNumberByIndex(bytes32 operatorId, uint32 blockNumber, uint256 index) external view returns (uint192) { - QuorumBitmapUpdate memory quorumBitmapUpdate = _operatorIdToQuorumBitmapHistory[operatorId][index]; - require( - quorumBitmapUpdate.updateBlockNumber <= blockNumber, - "BLSRegistryCoordinatorWithIndices.getQuorumBitmapByOperatorIdAtBlockNumberByIndex: quorumBitmapUpdate is from after blockNumber" - ); - // if the next update is at or before the block number, then the quorum provided index is too early - // if the nex update block number is 0, then this is the latest update - require( - quorumBitmapUpdate.nextUpdateBlockNumber > blockNumber || quorumBitmapUpdate.nextUpdateBlockNumber == 0, - "BLSRegistryCoordinatorWithIndices.getQuorumBitmapByOperatorIdAtBlockNumberByIndex: quorumBitmapUpdate is from before blockNumber" - ); - return quorumBitmapUpdate.quorumBitmap; - } - - /// @notice Returns the `index`th entry in the operator with `operatorId`'s bitmap history - function getQuorumBitmapUpdateByOperatorIdByIndex(bytes32 operatorId, uint256 index) external view returns (QuorumBitmapUpdate memory) { - return _operatorIdToQuorumBitmapHistory[operatorId][index]; - } - - /// @notice Returns the current quorum bitmap for the given `operatorId` or 0 if the operator is not registered for any quorum - function getCurrentQuorumBitmapByOperatorId(bytes32 operatorId) external view returns (uint192) { - uint256 quorumBitmapHistoryLength = _operatorIdToQuorumBitmapHistory[operatorId].length; - // the first part of this if statement is met if the operator has never registered. - // the second part is met if the operator has previously registered, but is currently deregistered - if (quorumBitmapHistoryLength == 0 || _operatorIdToQuorumBitmapHistory[operatorId][quorumBitmapHistoryLength - 1].nextUpdateBlockNumber != 0) { - return 0; - } - return _operatorIdToQuorumBitmapHistory[operatorId][quorumBitmapHistoryLength - 1].quorumBitmap; - } - - /// @notice Returns the length of the quorum bitmap history for the given `operatorId` - function getQuorumBitmapUpdateByOperatorIdLength(bytes32 operatorId) external view returns (uint256) { - return _operatorIdToQuorumBitmapHistory[operatorId].length; - } - - /// @notice Returns the number of registries - function numRegistries() external view returns (uint256) { - return registries.length; - } - - /** - * @notice Public function for the the churnApprover signature hash calculation when operators are being kicked from quorums - * @param registeringOperatorId The is of the registering operator - * @param operatorKickParams The parameters needed to kick the operator from the quorums that have reached their caps - * @param salt The salt to use for the churnApprover's signature - * @param expiry The desired expiry time of the churnApprover's signature - */ - function calculateOperatorChurnApprovalDigestHash( - bytes32 registeringOperatorId, - OperatorKickParam[] memory operatorKickParams, - bytes32 salt, - uint256 expiry - ) public view returns (bytes32) { - // calculate the digest hash - return _hashTypedDataV4(keccak256(abi.encode(OPERATOR_CHURN_APPROVAL_TYPEHASH, registeringOperatorId, operatorKickParams, salt, expiry))); - } - - // STATE CHANGING FUNCTIONS - - /** - * @notice Sets parameters of the operator set for the given `quorumNumber` - * @param quorumNumber is the quorum number to set the maximum number of operators for - * @param operatorSetParam is the parameters of the operator set for the `quorumNumber` - * @dev only callable by the service manager owner - */ - function setOperatorSetParams(uint8 quorumNumber, OperatorSetParam memory operatorSetParam) external onlyServiceManagerOwner { - _setOperatorSetParams(quorumNumber, operatorSetParam); - } - - /** - * @notice Sets the churnApprover - * @param _churnApprover is the address of the churnApprover - * @dev only callable by the service manager owner - */ - function setChurnApprover(address _churnApprover) external onlyServiceManagerOwner { - _setChurnApprover(_churnApprover); - } - - /** - * @notice Sets the ejector - * @param _ejector is the address of the ejector - * @dev only callable by the service manager owner - */ - function setEjector(address _ejector) external onlyServiceManagerOwner { - _setEjector(_ejector); - } - - /** - * @notice Registers msg.sender as an operator with the middleware - * @param quorumNumbers are the bytes representing the quorum numbers that the operator is registering for - * @param registrationData is the data that is decoded to get the operator's registration information - * @dev `registrationData` should be a G1 point representing the operator's BLS public key and their socket - */ - function registerOperatorWithCoordinator( - bytes calldata quorumNumbers, - bytes calldata registrationData - ) external onlyWhenNotPaused(PAUSED_REGISTER_OPERATOR) { - // get the operator's BLS public key - (BN254.G1Point memory pubkey, string memory socket) = abi.decode(registrationData, (BN254.G1Point, string)); - // call internal function to register the operator - _registerOperatorWithCoordinatorAndNoOverfilledQuorums(msg.sender, quorumNumbers, pubkey, socket); - } - - /** - * @notice Registers msg.sender as an operator with the middleware - * @param quorumNumbers are the bytes representing the quorum numbers that the operator is registering for - * @param pubkey is the BLS public key of the operator - * @param socket is the socket of the operator - */ - function registerOperatorWithCoordinator( - bytes calldata quorumNumbers, - BN254.G1Point memory pubkey, - string calldata socket - ) external onlyWhenNotPaused(PAUSED_REGISTER_OPERATOR) { - _registerOperatorWithCoordinatorAndNoOverfilledQuorums(msg.sender, quorumNumbers, pubkey, socket); - } - - /** - * @notice Registers msg.sender as an operator with the middleware when the quorum operator limit is full. To register - * while maintaining the limit, the operator chooses another registered operator with lower stake to kick. - * @param quorumNumbers are the bytes representing the quorum numbers that the operator is registering for - * @param pubkey is the BLS public key of the operator - * @param operatorKickParams are the parameters for the deregistration of the operator that is being kicked from each - * quorum that will be filled after the operator registers. These parameters should include an operator, their pubkey, - * and ids of the operators to swap with the kicked operator. - * @param signatureWithSaltAndExpiry is the signature of the churnApprover on the operator kick params with a salt and expiry - */ - function registerOperatorWithCoordinator( - bytes calldata quorumNumbers, - BN254.G1Point memory pubkey, - string calldata socket, - OperatorKickParam[] calldata operatorKickParams, - SignatureWithSaltAndExpiry memory signatureWithSaltAndExpiry - ) external onlyWhenNotPaused(PAUSED_REGISTER_OPERATOR) { - // register the operator - uint32[] memory numOperatorsPerQuorum = _registerOperatorWithCoordinator(msg.sender, quorumNumbers, pubkey, socket); - - // get the registering operator's operatorId and set the operatorIdsToSwap to it because the registering operator is the one with the greatest index - bytes32[] memory operatorIdsToSwap = new bytes32[](1); - operatorIdsToSwap[0] = pubkey.hashG1Point(); - - // verify the churnApprover's signature - _verifyChurnApproverSignatureOnOperatorChurnApproval(operatorIdsToSwap[0], operatorKickParams, signatureWithSaltAndExpiry); - - uint256 operatorToKickParamsIndex = 0; - // kick the operators - for (uint256 i = 0; i < quorumNumbers.length; i++) { - // check that the quorum has reached the max operator count - { - uint8 quorumNumber = uint8(quorumNumbers[i]); - OperatorSetParam memory operatorSetParam = _quorumOperatorSetParams[quorumNumber]; - // if the number of operators for the quorum is less than or equal to the max operator count, - // then the quorum has not reached the max operator count - if(numOperatorsPerQuorum[i] <= operatorSetParam.maxOperatorCount) { - continue; - } - - require( - operatorKickParams[operatorToKickParamsIndex].quorumNumber == quorumNumber, - "BLSRegistryCoordinatorWithIndices.registerOperatorWithCoordinator: quorumNumber not the same as signed" - ); - - // get the total stake for the quorum - uint96 totalStakeForQuorum = stakeRegistry.getCurrentTotalStakeForQuorum(quorumNumber); - bytes32 operatorToKickId = _operators[operatorKickParams[i].operator].operatorId; - uint96 operatorToKickStake = stakeRegistry.getCurrentOperatorStakeForQuorum(operatorToKickId, quorumNumber); - uint96 registeringOperatorStake = stakeRegistry.getCurrentOperatorStakeForQuorum(operatorIdsToSwap[0], quorumNumber); - - // check the registering operator has more than the kick BIPs of the operator to kick's stake - require( - registeringOperatorStake > operatorToKickStake * operatorSetParam.kickBIPsOfOperatorStake / BIPS_DENOMINATOR, - "BLSRegistryCoordinatorWithIndices.registerOperatorWithCoordinator: registering operator has less than kickBIPsOfOperatorStake" - ); - - // check the that the operator to kick has less than the kick BIPs of the total stake - require( - operatorToKickStake < totalStakeForQuorum * operatorSetParam.kickBIPsOfTotalStake / BIPS_DENOMINATOR, - "BLSRegistryCoordinatorWithIndices.registerOperatorWithCoordinator: operator to kick has more than kickBIPSOfTotalStake" - ); - - // increment the operatorToKickParamsIndex - operatorToKickParamsIndex++; - } - - // kick the operator - _deregisterOperatorWithCoordinator( - operatorKickParams[i].operator, - quorumNumbers[i:i+1], - operatorKickParams[i].pubkey, - operatorIdsToSwap - ); - } - } - - /** - * @notice Deregisters the msg.sender as an operator from the middleware - * @param quorumNumbers are the bytes representing the quorum numbers that the operator is registered for - * @param deregistrationData is the the data that is decoded to get the operator's deregistration information - * @dev `deregistrationData` should be a tuple of the operator's BLS public key, the list of operator ids to swap - */ - function deregisterOperatorWithCoordinator( - bytes calldata quorumNumbers, - bytes calldata deregistrationData - ) external onlyWhenNotPaused(PAUSED_DEREGISTER_OPERATOR) { - // get the operator's deregistration information - (BN254.G1Point memory pubkey, bytes32[] memory operatorIdsToSwap) - = abi.decode(deregistrationData, (BN254.G1Point, bytes32[])); - // call internal function to deregister the operator - _deregisterOperatorWithCoordinator(msg.sender, quorumNumbers, pubkey, operatorIdsToSwap); - } - - /** - * @notice Deregisters the msg.sender as an operator from the middleware - * @param quorumNumbers are the bytes representing the quorum numbers that the operator is registered for - * @param pubkey is the BLS public key of the operator - * @param operatorIdsToSwap is the list of the operator ids to swap the index of the operator with in each - * quorum when removing the operator from the quorum's ordered list. The provided operator ids should be the - * those of the operator's with the largest index in each quorum that the operator is deregistering from, in - * ascending order of quorum number. - */ - function deregisterOperatorWithCoordinator( - bytes calldata quorumNumbers, - BN254.G1Point memory pubkey, - bytes32[] memory operatorIdsToSwap - ) external onlyWhenNotPaused(PAUSED_DEREGISTER_OPERATOR) { - _deregisterOperatorWithCoordinator(msg.sender, quorumNumbers, pubkey, operatorIdsToSwap); - } - - /** - * @notice Ejects the provided operator from the provided quorums from the AVS - * @param operator is the operator to eject - * @param quorumNumbers are the quorum numbers to eject the operator from - * @param pubkey is the BLS public key of the operator - * @param operatorIdsToSwap is the list of the operator ids to swap the index of the operator with in each - * quorum when removing the operator from the quorum's ordered list. The provided operator ids should be the - * those of the operator's with the largest index in each quorum that the operator is being ejected from, in - * ascending order of quorum number. - */ - function ejectOperatorFromCoordinator( - address operator, - bytes calldata quorumNumbers, - BN254.G1Point memory pubkey, - bytes32[] memory operatorIdsToSwap - ) external onlyEjector { - _deregisterOperatorWithCoordinator(operator, quorumNumbers, pubkey, operatorIdsToSwap); - } - - /** - * @notice Updates the socket of the msg.sender given they are a registered operator - * @param socket is the new socket of the operator - */ - function updateSocket(string memory socket) external { - require(_operators[msg.sender].status == OperatorStatus.REGISTERED, "BLSRegistryCoordinatorWithIndicies.updateSocket: operator is not registered"); - emit OperatorSocketUpdate(_operators[msg.sender].operatorId, socket); - } - - // INTERNAL FUNCTIONS - - function _setOperatorSetParams(uint8 quorumNumber, OperatorSetParam memory operatorSetParam) internal { - _quorumOperatorSetParams[quorumNumber] = operatorSetParam; - emit OperatorSetParamsUpdated(quorumNumber, operatorSetParam); - } - - function _setChurnApprover(address newChurnApprover) internal { - emit ChurnApproverUpdated(churnApprover, newChurnApprover); - churnApprover = newChurnApprover; - } - - function _setEjector(address newEjector) internal { - emit EjectorUpdated(ejector, newEjector); - ejector = newEjector; - } - - /// @return numOperatorsPerQuorum is the list of number of operators per quorum in quorumNumberss - function _registerOperatorWithCoordinator(address operator, bytes calldata quorumNumbers, BN254.G1Point memory pubkey, string memory socket) internal returns(uint32[] memory) { - // require( - // slasher.contractCanSlashOperatorUntilBlock(operator, address(serviceManager)) == type(uint32).max, - // "StakeRegistry._registerOperator: operator must be opted into slashing by the serviceManager" - // ); - - _beforeRegisterOperator(operator, quorumNumbers); - // get the quorum bitmap from the quorum numbers - uint256 quorumBitmap = BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers); - require(quorumBitmap <= MAX_QUORUM_BITMAP, "BLSRegistryCoordinatorWithIndices._registerOperatorWithCoordinator: quorumBitmap exceeds of max bitmap size"); - require(quorumBitmap != 0, "BLSRegistryCoordinatorWithIndices._registerOperatorWithCoordinator: quorumBitmap cannot be 0"); - - // register the operator with the BLSPubkeyRegistry and get the operatorId (in this case, the pubkeyHash) back - // note that the operatorId is the hash of the operator's BLS public key which is irreversibly linked to their address - // in the BLSPublicKeyCompendium, so this will always be the same for the same operator - bytes32 operatorId = blsPubkeyRegistry.registerOperator(operator, quorumNumbers, pubkey); - - uint256 operatorQuorumBitmapHistoryLength = _operatorIdToQuorumBitmapHistory[operatorId].length; - if(operatorQuorumBitmapHistoryLength > 0) { - uint256 prevQuorumBitmap = _operatorIdToQuorumBitmapHistory[operatorId][operatorQuorumBitmapHistoryLength - 1].quorumBitmap; - require(prevQuorumBitmap & quorumBitmap == 0, "BLSRegistryCoordinatorWithIndices._registerOperatorWithCoordinator: operator already registered for some quorums being registered for"); - // new stored quorumBitmap is the previous quorumBitmap or'd with the new quorumBitmap to register for - quorumBitmap |= prevQuorumBitmap; - } - - // register the operator with the StakeRegistry - stakeRegistry.registerOperator(operator, operatorId, quorumNumbers); - - // register the operator with the IndexRegistry - uint32[] memory numOperatorsPerQuorum = indexRegistry.registerOperator(operatorId, quorumNumbers); - - uint256 quorumBitmapHistoryLength = _operatorIdToQuorumBitmapHistory[operatorId].length; - if(quorumBitmapHistoryLength != 0) { - // set the toBlockNumber of the previous quorum bitmap update - _operatorIdToQuorumBitmapHistory[operatorId][quorumBitmapHistoryLength - 1].nextUpdateBlockNumber = uint32(block.number); - } - - // set the operatorId to quorum bitmap history - _operatorIdToQuorumBitmapHistory[operatorId].push(QuorumBitmapUpdate({ - updateBlockNumber: uint32(block.number), - nextUpdateBlockNumber: 0, - quorumBitmap: uint192(quorumBitmap) - })); - - // if the operator is not already registered, then they are registering for the first time - if (_operators[operator].status != OperatorStatus.REGISTERED) { - _operators[operator] = Operator({ - operatorId: operatorId, - status: OperatorStatus.REGISTERED - }); - - emit OperatorRegistered(operator, operatorId); - } - - _afterRegisterOperator(operator, quorumNumbers); - - // record a stake update not bonding the operator at all (unbonded at 0), because they haven't served anything yet - // serviceManager.recordFirstStakeUpdate(operator, 0); - - emit OperatorSocketUpdate(operatorId, socket); - - return numOperatorsPerQuorum; - } - - function _registerOperatorWithCoordinatorAndNoOverfilledQuorums(address operator, bytes calldata quorumNumbers, BN254.G1Point memory pubkey, string memory socket) internal { - uint32[] memory numOperatorsPerQuorum = _registerOperatorWithCoordinator(operator, quorumNumbers, pubkey, socket); - for (uint i = 0; i < numOperatorsPerQuorum.length; i++) { - require( - numOperatorsPerQuorum[i] <= _quorumOperatorSetParams[uint8(quorumNumbers[i])].maxOperatorCount, - "BLSRegistryCoordinatorWithIndices._registerOperatorWithCoordinatorAndNoOverfilledQuorums: quorum is overfilled" - ); - } - } - - function _deregisterOperatorWithCoordinator(address operator, bytes calldata quorumNumbers, BN254.G1Point memory pubkey, bytes32[] memory operatorIdsToSwap) internal { - require(_operators[operator].status == OperatorStatus.REGISTERED, "BLSRegistryCoordinatorWithIndices._deregisterOperatorWithCoordinator: operator is not registered"); - - // get the operatorId of the operator - bytes32 operatorId = _operators[operator].operatorId; - require(operatorId == pubkey.hashG1Point(), "BLSRegistryCoordinatorWithIndices._deregisterOperatorWithCoordinator: operatorId does not match pubkey hash"); - - // get the bitmap of quorums to remove the operator from - uint256 quorumsToRemoveBitmap = BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers); - - // get the quorum bitmap before the update - uint256 operatorQuorumBitmapHistoryLengthMinusOne = _operatorIdToQuorumBitmapHistory[operatorId].length - 1; - uint192 quorumBitmapBeforeUpdate = _operatorIdToQuorumBitmapHistory[operatorId][operatorQuorumBitmapHistoryLengthMinusOne].quorumBitmap; - - // and out quorums that the operator is not a part of - quorumsToRemoveBitmap = quorumBitmapBeforeUpdate & quorumsToRemoveBitmap; - bytes memory quorumNumbersToRemove = BitmapUtils.bitmapToBytesArray(quorumsToRemoveBitmap); - - // make sure the operator is registered for at least one of the provided quorums - require(quorumNumbersToRemove.length != 0, "BLSRegistryCoordinatorWithIndices._deregisterOperatorWithCoordinator: operator is not registered for any of the provided quorums"); - - // check if the operator is completely deregistering - bool completeDeregistration = quorumBitmapBeforeUpdate == quorumsToRemoveBitmap; - - _beforeDeregisterOperator(operator, quorumNumbersToRemove); - - // deregister the operator from the BLSPubkeyRegistry - blsPubkeyRegistry.deregisterOperator(operator, quorumNumbersToRemove, pubkey); - - // deregister the operator from the StakeRegistry - stakeRegistry.deregisterOperator(operatorId, quorumNumbersToRemove); - - // deregister the operator from the IndexRegistry - indexRegistry.deregisterOperator(operatorId, quorumNumbersToRemove, operatorIdsToSwap); - - _afterDeregisterOperator(operator, quorumNumbersToRemove); - - // set the toBlockNumber of the operator's quorum bitmap update - _operatorIdToQuorumBitmapHistory[operatorId][operatorQuorumBitmapHistoryLengthMinusOne].nextUpdateBlockNumber = uint32(block.number); - - // if it is not a complete deregistration, add a new quorum bitmap update - if (!completeDeregistration) { - _operatorIdToQuorumBitmapHistory[operatorId].push(QuorumBitmapUpdate({ - updateBlockNumber: uint32(block.number), - nextUpdateBlockNumber: 0, - quorumBitmap: quorumBitmapBeforeUpdate & ~uint192(quorumsToRemoveBitmap) // this removes the quorumsToRemoveBitmap from the quorumBitmapBeforeUpdate - })); - } else { - // @notice Registrant must continue to serve until the latest block at which an active task expires. this info is used in challenges - // uint32 latestServeUntilBlock = serviceManager.latestServeUntilBlock(); - - // record a stake update unbonding the operator after `latestServeUntilBlock` - // serviceManager.recordLastStakeUpdateAndRevokeSlashingAbility(operator, latestServeUntilBlock); - // set the status of the operator to DEREGISTERED - _operators[operator].status = OperatorStatus.DEREGISTERED; - - emit OperatorDeregistered(operator, operatorId); - } - } - - /** - * @dev Hook that is called before any operator registration to insert additional logic. - * @param operator The address of the operator to register. - * @param quorumNumbers The quorum numbers the operator is registering for, where each byte is an 8 bit integer quorumNumber. - */ - function _beforeRegisterOperator(address operator, bytes memory quorumNumbers) internal virtual{} - - /** - * @dev Hook that is called after any operator registration to insert additional logic. - * @param operator The address of the operator to register. - * @param quorumNumbers The quorum numbers the operator is registering for, where each byte is an 8 bit integer quorumNumber. - */ - function _afterRegisterOperator(address operator, bytes memory quorumNumbers) internal virtual {} - - /** - * @dev Hook that is called before any operator deregistration to insert additional logic. - * @param operator The address of the operator to deregister. - * @param quorumNumbers The quorum numbers the operator is registering for, where each byte is an 8 bit integer quorumNumber. - */ - function _beforeDeregisterOperator(address operator, bytes memory quorumNumbers) internal virtual {} - - /** - * @dev Hook that is called after any operator deregistration to insert additional logic. - * @param operator The address of the operator to deregister. - * @param quorumNumbers The quorum numbers the operator is registering for, where each byte is an 8 bit integer quorumNumber. - */ - function _afterDeregisterOperator(address operator, bytes memory quorumNumbers) internal virtual {} - - /// @notice verifies churnApprover's signature on operator churn approval and increments the churnApprover nonce - function _verifyChurnApproverSignatureOnOperatorChurnApproval(bytes32 registeringOperatorId, OperatorKickParam[] memory operatorKickParams, SignatureWithSaltAndExpiry memory signatureWithSaltAndExpiry) internal { - // make sure the salt hasn't been used already - require(!isChurnApproverSaltUsed[signatureWithSaltAndExpiry.salt], "BLSRegistryCoordinatorWithIndices._verifyChurnApproverSignatureOnOperatorChurnApproval: churnApprover salt already used"); - require(signatureWithSaltAndExpiry.expiry >= block.timestamp, "BLSRegistryCoordinatorWithIndices._verifyChurnApproverSignatureOnOperatorChurnApproval: churnApprover signature expired"); - - // set salt used to true - isChurnApproverSaltUsed[signatureWithSaltAndExpiry.salt] = true; - - // check the churnApprover's signature - EIP1271SignatureUtils.checkSignature_EIP1271(churnApprover, calculateOperatorChurnApprovalDigestHash(registeringOperatorId, operatorKickParams, signatureWithSaltAndExpiry.salt, signatureWithSaltAndExpiry.expiry), signatureWithSaltAndExpiry.signature); - } -} diff --git a/src/contracts/middleware/BLSSignatureChecker.sol b/src/contracts/middleware/BLSSignatureChecker.sol deleted file mode 100644 index 694e72303..000000000 --- a/src/contracts/middleware/BLSSignatureChecker.sol +++ /dev/null @@ -1,197 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity =0.8.12; - -import "../interfaces/IBLSSignatureChecker.sol"; - -/** - * @title Used for checking BLS aggregate signatures from the operators of a `BLSRegistry`. - * @author Layr Labs, Inc. - * @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service - * @notice This is the contract for checking the validity of aggregate operator signatures. - */ -contract BLSSignatureChecker is IBLSSignatureChecker { - using BN254 for BN254.G1Point; - - // CONSTANTS & IMMUTABLES - - // gas cost of multiplying 2 pairings - uint256 internal constant PAIRING_EQUALITY_CHECK_GAS = 120000; - - IRegistryCoordinator public immutable registryCoordinator; - IStakeRegistry public immutable stakeRegistry; - IBLSPubkeyRegistry public immutable blsPubkeyRegistry; - - constructor(IBLSRegistryCoordinatorWithIndices _registryCoordinator) { - registryCoordinator = IRegistryCoordinator(_registryCoordinator); - stakeRegistry = _registryCoordinator.stakeRegistry(); - blsPubkeyRegistry = _registryCoordinator.blsPubkeyRegistry(); - } - - /** - * @notice This function is called by disperser when it has aggregated all the signatures of the operators - * that are part of the quorum for a particular taskNumber and is asserting them into onchain. The function - * checks that the claim for aggregated signatures are valid. - * - * The thesis of this procedure entails: - * - getting the aggregated pubkey of all registered nodes at the time of pre-commit by the - * disperser (represented by apk in the parameters), - * - subtracting the pubkeys of all the signers not in the quorum (nonSignerPubkeys) and storing - * the output in apk to get aggregated pubkey of all operators that are part of quorum. - * - use this aggregated pubkey to verify the aggregated signature under BLS scheme. - * - * @dev Before signature verification, the function verifies operator stake information. This includes ensuring that the provided `referenceBlockNumber` - * is correct, i.e., ensure that the stake returned from the specified block number is recent enough and that the stake is either the most recent update - * for the total stake (of the operator) or latest before the referenceBlockNumber. - * @param msgHash is the hash being signed - * @param quorumNumbers is the bytes array of quorum numbers that are being signed for - * @param referenceBlockNumber is the block number at which the stake information is being verified - * @param nonSignerStakesAndSignature is the struct containing information on nonsigners, stakes, quorum apks, and the aggregate signature - * @return quorumStakeTotals is the struct containing the total and signed stake for each quorum - * @return signatoryRecordHash is the hash of the signatory record, which is used for fraud proofs - */ - function checkSignatures( - bytes32 msgHash, - bytes calldata quorumNumbers, - uint32 referenceBlockNumber, - NonSignerStakesAndSignature memory nonSignerStakesAndSignature - ) - public - view - returns ( - QuorumStakeTotals memory, - bytes32 - ) - { - // verify the provided apk was the apk at referenceBlockNumber - // loop through every quorumNumber and keep track of the apk - BN254.G1Point memory apk = BN254.G1Point(0, 0); - for (uint i = 0; i < quorumNumbers.length; i++) { - require( - bytes24(nonSignerStakesAndSignature.quorumApks[i].hashG1Point()) == - blsPubkeyRegistry.getApkHashForQuorumAtBlockNumberFromIndex( - uint8(quorumNumbers[i]), - referenceBlockNumber, - nonSignerStakesAndSignature.quorumApkIndices[i] - ), - "BLSSignatureChecker.checkSignatures: quorumApk hash in storage does not match provided quorum apk" - ); - apk = apk.plus(nonSignerStakesAndSignature.quorumApks[i]); - } - - // initialize memory for the quorumStakeTotals - QuorumStakeTotals memory quorumStakeTotals; - quorumStakeTotals.totalStakeForQuorum = new uint96[](quorumNumbers.length); - quorumStakeTotals.signedStakeForQuorum = new uint96[](quorumNumbers.length); - // the pubkeyHashes of the nonSigners - bytes32[] memory nonSignerPubkeyHashes = new bytes32[](nonSignerStakesAndSignature.nonSignerPubkeys.length); - { - // the quorumBitmaps of the nonSigners - uint256[] memory nonSignerQuorumBitmaps = new uint256[](nonSignerStakesAndSignature.nonSignerPubkeys.length); - { - // the bitmap of the quorumNumbers - uint256 signingQuorumBitmap = BitmapUtils.bytesArrayToBitmap(quorumNumbers); - - for (uint i = 0; i < nonSignerStakesAndSignature.nonSignerPubkeys.length; i++) { - nonSignerPubkeyHashes[i] = nonSignerStakesAndSignature.nonSignerPubkeys[i].hashG1Point(); - - // check that the nonSignerPubkeys are sorted and free of duplicates - if (i != 0) { - require(uint256(nonSignerPubkeyHashes[i]) > uint256(nonSignerPubkeyHashes[i - 1]), "BLSSignatureChecker.checkSignatures: nonSignerPubkeys not sorted"); - } - - nonSignerQuorumBitmaps[i] = - registryCoordinator.getQuorumBitmapByOperatorIdAtBlockNumberByIndex( - nonSignerPubkeyHashes[i], - referenceBlockNumber, - nonSignerStakesAndSignature.nonSignerQuorumBitmapIndices[i] - ); - - // subtract the nonSignerPubkey from the running apk to get the apk of all signers - apk = apk.plus( - nonSignerStakesAndSignature.nonSignerPubkeys[i] - .negate() - .scalar_mul_tiny( - BitmapUtils.countNumOnes(nonSignerQuorumBitmaps[i] & signingQuorumBitmap) - ) - ); - } - } - // loop through each quorum number - for (uint8 quorumNumberIndex = 0; quorumNumberIndex < quorumNumbers.length;) { - // get the quorum number - uint8 quorumNumber = uint8(quorumNumbers[quorumNumberIndex]); - // get the totalStake for the quorum at the referenceBlockNumber - quorumStakeTotals.totalStakeForQuorum[quorumNumberIndex] = - stakeRegistry.getTotalStakeAtBlockNumberFromIndex(quorumNumber, referenceBlockNumber, nonSignerStakesAndSignature.totalStakeIndices[quorumNumberIndex]); - // copy total stake to signed stake - quorumStakeTotals.signedStakeForQuorum[quorumNumberIndex] = quorumStakeTotals.totalStakeForQuorum[quorumNumberIndex]; - // loop through all nonSigners, checking that they are a part of the quorum via their quorumBitmap - // if so, load their stake at referenceBlockNumber and subtract it from running stake signed - for (uint32 i = 0; i < nonSignerStakesAndSignature.nonSignerPubkeys.length; i++) { - // keep track of the nonSigners index in the quorum - uint32 nonSignerForQuorumIndex = 0; - // if the nonSigner is a part of the quorum, subtract their stake from the running total - if (BitmapUtils.numberIsInBitmap(nonSignerQuorumBitmaps[i], quorumNumber)) { - quorumStakeTotals.signedStakeForQuorum[quorumNumberIndex] -= - stakeRegistry.getStakeForQuorumAtBlockNumberFromOperatorIdAndIndex( - quorumNumber, - referenceBlockNumber, - nonSignerPubkeyHashes[i], - nonSignerStakesAndSignature.nonSignerStakeIndices[quorumNumberIndex][nonSignerForQuorumIndex] - ); - unchecked { - ++nonSignerForQuorumIndex; - } - } - } - - unchecked { - ++quorumNumberIndex; - } - } - } - { - // verify the signature - (bool pairingSuccessful, bool signatureIsValid) = trySignatureAndApkVerification( - msgHash, - apk, - nonSignerStakesAndSignature.apkG2, - nonSignerStakesAndSignature.sigma - ); - require(pairingSuccessful, "BLSSignatureChecker.checkSignatures: pairing precompile call failed"); - require(signatureIsValid, "BLSSignatureChecker.checkSignatures: signature is invalid"); - } - // set signatoryRecordHash variable used for fraudproofs - bytes32 signatoryRecordHash = keccak256(abi.encodePacked(referenceBlockNumber, nonSignerPubkeyHashes)); - - // return the total stakes that signed for each quorum, and a hash of the information required to prove the exact signers and stake - return (quorumStakeTotals, signatoryRecordHash); - } - - /** - * trySignatureAndApkVerification verifies a BLS aggregate signature and the veracity of a calculated G1 Public key - * @param msgHash is the hash being signed - * @param apk is the claimed G1 public key - * @param apkG2 is provided G2 public key - * @param sigma is the G1 point signature - * @return pairingSuccessful is true if the pairing precompile call was successful - * @return siganatureIsValid is true if the signature is valid - */ - function trySignatureAndApkVerification( - bytes32 msgHash, - BN254.G1Point memory apk, - BN254.G2Point memory apkG2, - BN254.G1Point memory sigma - ) public view returns(bool pairingSuccessful, bool siganatureIsValid) { - // gamma = keccak256(abi.encodePacked(msgHash, apk, apkG2, sigma)) - uint256 gamma = uint256(keccak256(abi.encodePacked(msgHash, apk.X, apk.Y, apkG2.X[0], apkG2.X[1], apkG2.Y[0], apkG2.Y[1], sigma.X, sigma.Y))) % BN254.FR_MODULUS; - // verify the signature - (pairingSuccessful, siganatureIsValid) = BN254.safePairing( - sigma.plus(apk.scalar_mul(gamma)), - BN254.negGeneratorG2(), - BN254.hashToG1(msgHash).plus(BN254.generatorG1().scalar_mul(gamma)), - apkG2, - PAIRING_EQUALITY_CHECK_GAS - ); - } -} diff --git a/src/contracts/middleware/IndexRegistry.sol b/src/contracts/middleware/IndexRegistry.sol deleted file mode 100644 index f4e4f0d93..000000000 --- a/src/contracts/middleware/IndexRegistry.sol +++ /dev/null @@ -1,339 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity =0.8.12; - -import "./IndexRegistryStorage.sol"; -import "../libraries/BN254.sol"; - -/** - * @title A `Registry` that keeps track of an ordered list of operators for each quorum - * @author Layr Labs, Inc. - */ -contract IndexRegistry is IndexRegistryStorage { - - /// @notice when applied to a function, only allows the RegistryCoordinator to call it - modifier onlyRegistryCoordinator() { - require(msg.sender == address(registryCoordinator), "IndexRegistry.onlyRegistryCoordinator: caller is not the registry coordinator"); - _; - } - - /// @notice sets the (immutable) `registryCoordinator` address - constructor( - IRegistryCoordinator _registryCoordinator - ) IndexRegistryStorage(_registryCoordinator) {} - - /** - * @notice Registers the operator with the specified `operatorId` for the quorums specified by `quorumNumbers`. - * @param operatorId is the id of the operator that is being registered - * @param quorumNumbers is the quorum numbers the operator is registered for - * @return numOperatorsPerQuorum is a list of the number of operators (including the registering operator) in each of the quorums the operator is registered for - * @dev access restricted to the RegistryCoordinator - * @dev Preconditions (these are assumed, not validated in this contract): - * 1) `quorumNumbers` has no duplicates - * 2) `quorumNumbers.length` != 0 - * 3) `quorumNumbers` is ordered in ascending order - * 4) the operator is not already registered - */ - function registerOperator(bytes32 operatorId, bytes calldata quorumNumbers) external onlyRegistryCoordinator returns(uint32[] memory) { - _beforeRegisterOperator(operatorId, quorumNumbers); - - uint32[] memory numOperatorsPerQuorum = new uint32[](quorumNumbers.length); - //add operator to operatorList - globalOperatorList.push(operatorId); - - for (uint256 i = 0; i < quorumNumbers.length; i++) { - uint8 quorumNumber = uint8(quorumNumbers[i]); - - //this is the would-be index of the operator being registered, the total number of operators for that quorum (which is last index + 1) - uint256 quorumHistoryLength = _totalOperatorsHistory[quorumNumber].length; - uint32 numOperators = quorumHistoryLength > 0 ? _totalOperatorsHistory[quorumNumber][quorumHistoryLength - 1].index : 0; - _updateOperatorIdToIndexHistory(operatorId, quorumNumber, numOperators); - _updateTotalOperatorHistory(quorumNumber, numOperators + 1); - numOperatorsPerQuorum[i] = numOperators + 1; - } - - _afterRegisterOperator(operatorId, quorumNumbers); - return numOperatorsPerQuorum; - } - - /** - * @notice Deregisters the operator with the specified `operatorId` for the quorums specified by `quorumNumbers`. - * @param operatorId is the id of the operator that is being deregistered - * @param quorumNumbers is the quorum numbers the operator is deregistered for - * @param operatorIdsToSwap is the list of operatorIds that have the largest indexes in each of the `quorumNumbers` - * they will be swapped with the operator's current index when the operator is removed from the list - * @dev access restricted to the RegistryCoordinator - * @dev Preconditions (these are assumed, not validated in this contract): - * 1) `quorumNumbers` has no duplicates - * 2) `quorumNumbers.length` != 0 - * 3) `quorumNumbers` is ordered in ascending order - * 4) the operator is not already deregistered - * 5) `quorumNumbers` is a subset of the quorumNumbers that the operator is registered for - */ - function deregisterOperator( - bytes32 operatorId, - bytes calldata quorumNumbers, - bytes32[] memory operatorIdsToSwap - ) external onlyRegistryCoordinator { - require( - quorumNumbers.length == operatorIdsToSwap.length, - "IndexRegistry.deregisterOperator: quorumNumbers and operatorIdsToSwap must be the same length" - ); - - _beforeDeregisterOperator(operatorId, quorumNumbers); - - for (uint256 i = 0; i < quorumNumbers.length; i++) { - uint8 quorumNumber = uint8(quorumNumbers[i]); - uint32 indexOfOperatorToRemove = _operatorIdToIndexHistory[operatorId][quorumNumber][_operatorIdToIndexHistory[operatorId][quorumNumber].length - 1].index; - _processOperatorRemoval(operatorId, quorumNumber, indexOfOperatorToRemove, operatorIdsToSwap[i]); - _updateTotalOperatorHistory(quorumNumber, _totalOperatorsHistory[quorumNumber][_totalOperatorsHistory[quorumNumber].length - 1].index - 1); - } - - _afterDeregisterOperator(operatorId, quorumNumbers); - } - - /// @notice Returns the length of the globalOperatorList - function getGlobalOperatorListLength() external view returns (uint256) { - return globalOperatorList.length; - } - - /// @notice Returns the _operatorIdToIndexHistory entry for the specified `operatorId` and `quorumNumber` at the specified `index` - function getOperatorIndexUpdateOfOperatorIdForQuorumAtIndex( - bytes32 operatorId, - uint8 quorumNumber, - uint32 index - ) external view returns (OperatorIndexUpdate memory) { - return _operatorIdToIndexHistory[operatorId][quorumNumber][index]; - } - - /// @notice Returns the _totalOperatorsHistory entry for the specified `quorumNumber` at the specified `index` - function getTotalOperatorsUpdateForQuorumAtIndex( - uint8 quorumNumber, - uint32 index - ) external view returns (OperatorIndexUpdate memory) { - return _totalOperatorsHistory[quorumNumber][index]; - } - - /** - * @notice Looks up the `operator`'s index in the set of operators for `quorumNumber` at the specified `blockNumber` using the `index`. - * @param operatorId is the id of the operator for which the index is desired - * @param quorumNumber is the quorum number for which the operator index is desired - * @param blockNumber is the block number at which the index of the operator is desired - * @param index Used to specify the entry within the dynamic array `_operatorIdToIndexHistory[operatorId]` to - * read data from - * @dev Function will revert in the event that the specified `index` input does not identify the appropriate entry in the - * array `_operatorIdToIndexHistory[operatorId][quorumNumber]` to pull the info from. - */ - function getOperatorIndexForQuorumAtBlockNumberByIndex( - bytes32 operatorId, - uint8 quorumNumber, - uint32 blockNumber, - uint32 index - ) external view returns (uint32) { - OperatorIndexUpdate memory operatorIndexToCheck = _operatorIdToIndexHistory[operatorId][quorumNumber][index]; - - // blocknumber must be at or after the "index'th" entry's fromBlockNumber - require( - blockNumber >= operatorIndexToCheck.fromBlockNumber, - "IndexRegistry.getOperatorIndexForQuorumAtBlockNumberByIndex: provided index is too far in the past for provided block number" - ); - - // if there is an index update after the "index'th" update, the blocknumber must be before the next entry's fromBlockNumber - if (index != _operatorIdToIndexHistory[operatorId][quorumNumber].length - 1) { - OperatorIndexUpdate memory nextOperatorIndex = _operatorIdToIndexHistory[operatorId][quorumNumber][index + 1]; - require( - blockNumber < nextOperatorIndex.fromBlockNumber, - "IndexRegistry.getOperatorIndexForQuorumAtBlockNumberByIndex: provided index is too far in the future for provided block number" - ); - } - return operatorIndexToCheck.index; - } - - /** - * @notice Looks up the number of total operators for `quorumNumber` at the specified `blockNumber`. - * @param quorumNumber is the quorum number for which the total number of operators is desired - * @param blockNumber is the block number at which the total number of operators is desired - * @param index is the index of the entry in the dynamic array `_totalOperatorsHistory[quorumNumber]` to read data from - * @dev Function will revert in the event that the specified `index` input is outisde the bounds of the provided `blockNumber` - */ - function getTotalOperatorsForQuorumAtBlockNumberByIndex( - uint8 quorumNumber, - uint32 blockNumber, - uint32 index - ) external view returns (uint32){ - OperatorIndexUpdate memory operatorIndexToCheck = _totalOperatorsHistory[quorumNumber][index]; - - // blocknumber must be at or after the "index'th" entry's fromBlockNumber - require( - blockNumber >= operatorIndexToCheck.fromBlockNumber, - "IndexRegistry.getTotalOperatorsForQuorumAtBlockNumberByIndex: provided index is too far in the past for provided block number" - ); - - // if there is an index update after the "index'th" update, the blocknumber must be before the next entry's fromBlockNumber - if (index != _totalOperatorsHistory[quorumNumber].length - 1){ - OperatorIndexUpdate memory nextOperatorIndex = _totalOperatorsHistory[quorumNumber][index + 1]; - require( - blockNumber < nextOperatorIndex.fromBlockNumber, - "IndexRegistry.getTotalOperatorsForQuorumAtBlockNumberByIndex: provided index is too far in the future for provided block number" - ); - } - return operatorIndexToCheck.index; - } - - /// @notice Returns an ordered list of operators of the services for the given `quorumNumber` at the given `blockNumber` - function getOperatorListForQuorumAtBlockNumber(uint8 quorumNumber, uint32 blockNumber) external view returns (bytes32[] memory){ - bytes32[] memory quorumOperatorList = new bytes32[](_getTotalOperatorsForQuorumAtBlockNumber(quorumNumber, blockNumber)); - for (uint256 i = 0; i < globalOperatorList.length; i++) { - bytes32 operatorId = globalOperatorList[i]; - uint32 index = _getIndexOfOperatorForQuorumAtBlockNumber(operatorId, quorumNumber, blockNumber); - // if the operator was not in the quorum at the given block number, skip it - if (index == OPERATOR_DEREGISTERED_INDEX) - continue; - quorumOperatorList[index] = operatorId; - } - return quorumOperatorList; - } - - /// @notice Returns the total number of operators for a given `quorumNumber` - function totalOperatorsForQuorum(uint8 quorumNumber) external view returns (uint32){ - uint256 totalOperatorsHistoryLength = _totalOperatorsHistory[quorumNumber].length; - if (totalOperatorsHistoryLength == 0) { - return 0; - } - return _totalOperatorsHistory[quorumNumber][totalOperatorsHistoryLength - 1].index; - } - - /** - * @notice updates the total numbers of operator in `quorumNumber` to `numOperators` - * @param quorumNumber is the number of the quorum to update - * @param numOperators is the number of operators in the quorum - */ - function _updateTotalOperatorHistory(uint8 quorumNumber, uint32 numOperators) internal { - OperatorIndexUpdate memory totalOperatorUpdate; - // In the case of totalOperatorsHistory, the index parameter is the number of operators in the quorum - totalOperatorUpdate.index = numOperators; - totalOperatorUpdate.fromBlockNumber = uint32(block.number); - - _totalOperatorsHistory[quorumNumber].push(totalOperatorUpdate); - } - - /** - * @param operatorId operatorId of the operator to update - * @param quorumNumber quorumNumber of the operator to update - * @param index the latest index of that operator in the list of operators registered for this quorum - */ - function _updateOperatorIdToIndexHistory(bytes32 operatorId, uint8 quorumNumber, uint32 index) internal { - OperatorIndexUpdate memory latestOperatorIndex; - latestOperatorIndex.index = index; - latestOperatorIndex.fromBlockNumber = uint32(block.number); - _operatorIdToIndexHistory[operatorId][quorumNumber].push(latestOperatorIndex); - - emit QuorumIndexUpdate(operatorId, quorumNumber, index); - } - - /** - * @notice when we remove an operator from a quorum, we simply update the operator's index history - * as well as any operatorIds we have to swap - * @param quorumNumber quorum number of the operator to remove - * @param indexOfOperatorToRemove index of the operator to remove - */ - function _processOperatorRemoval( - bytes32 operatorId, - uint8 quorumNumber, - uint32 indexOfOperatorToRemove, - bytes32 operatorIdToSwap - ) internal { - uint32 operatorIdToSwapIndex = _operatorIdToIndexHistory[operatorIdToSwap][quorumNumber][_operatorIdToIndexHistory[operatorIdToSwap][quorumNumber].length - 1].index; - require( - _totalOperatorsHistory[quorumNumber][_totalOperatorsHistory[quorumNumber].length - 1].index - 1 == operatorIdToSwapIndex, - "IndexRegistry._processOperatorRemoval: operatorIdToSwap is not the last operator in the quorum" - ); - - // if the operator is not the last in the list, we must swap the last operator into their positon - if (operatorId != operatorIdToSwap) { - //update the swapped operator's operatorIdToIndexHistory list with a new entry, as their index has now changed - _updateOperatorIdToIndexHistory(operatorIdToSwap, quorumNumber, indexOfOperatorToRemove); - } - // marking the final entry in the deregistering operator's operatorIdToIndexHistory entry with the deregistration block number, - // setting the index to OPERATOR_DEREGISTERED_INDEX. Note that this is a special meaning, and any other index value represents a real index - _updateOperatorIdToIndexHistory(operatorId, quorumNumber, OPERATOR_DEREGISTERED_INDEX); - } - - /** - * @dev Hook that is called before any operator registration to insert additional logic. - * @param operatorId The id of the operator to register. - * @param quorumNumbers The quorum numbers the operator is registering for, where each byte is an 8 bit integer quorumNumber. - */ - function _beforeRegisterOperator(bytes32 operatorId, bytes memory quorumNumbers) internal virtual{} - - /** - * @dev Hook that is called after any operator registration to insert additional logic. - * @param operatorId The id of the operator to register. - * @param quorumNumbers The quorum numbers the operator is registering for, where each byte is an 8 bit integer quorumNumber. - */ - function _afterRegisterOperator(bytes32 operatorId, bytes memory quorumNumbers) internal virtual {} - - /** - * @dev Hook that is called before any operator deregistration to insert additional logic. - * @param operatorId The id of the operator to register. - * @param quorumNumbers The quorum numbers the operator is registering for, where each byte is an 8 bit integer quorumNumber. - */ - function _beforeDeregisterOperator(bytes32 operatorId, bytes memory quorumNumbers) internal virtual {} - - /** - * @dev Hook that is called after any operator deregistration to insert additional logic. - * @param operatorId The id of the operator to register. - * @param quorumNumbers The quorum numbers the operator is registering for, where each byte is an 8 bit integer quorumNumber. - */ - function _afterDeregisterOperator(bytes32 operatorId, bytes memory quorumNumbers) internal virtual {} - - /** - * @notice Returns the total number of operators of the service for the given `quorumNumber` at the given `blockNumber` - * @dev Returns zero if the @param blockNumber is from before the @param quorumNumber existed, and returns the current number - * of total operators if the @param blockNumber is in the future. - */ - function _getTotalOperatorsForQuorumAtBlockNumber(uint8 quorumNumber, uint32 blockNumber) internal view returns (uint32){ - // store list length in memory - uint256 totalOperatorsHistoryLength = _totalOperatorsHistory[quorumNumber].length; - // if there are no entries in the total operator history, return 0 - if (totalOperatorsHistoryLength == 0) { - return 0; - } - - // if `blockNumber` is from before the `quorumNumber` existed, return `0` - if (blockNumber < _totalOperatorsHistory[quorumNumber][0].fromBlockNumber) { - return 0; - } - - // loop backwards through the total operator history to find the total number of operators at the given block number - for (uint256 i = 0; i <= totalOperatorsHistoryLength - 1; i++) { - uint256 listIndex = (totalOperatorsHistoryLength - 1) - i; - OperatorIndexUpdate memory totalOperatorUpdate = _totalOperatorsHistory[quorumNumber][listIndex]; - // look for the first update that began before or at `blockNumber` - if (totalOperatorUpdate.fromBlockNumber <= blockNumber) { - return _totalOperatorsHistory[quorumNumber][listIndex].index; - } - } - return _totalOperatorsHistory[quorumNumber][0].index; - } - - /** - * @notice Returns the index of the `operatorId` at the given `blockNumber` for the given `quorumNumber`, or - * `OPERATOR_DEREGISTERED_INDEX` if the operator was not registered for the `quorumNumber` at blockNumber - */ - function _getIndexOfOperatorForQuorumAtBlockNumber(bytes32 operatorId, uint8 quorumNumber, uint32 blockNumber) internal view returns(uint32) { - uint256 operatorIndexHistoryLength = _operatorIdToIndexHistory[operatorId][quorumNumber].length; - // loop backward through index history to find the index of the operator at the given block number - for (uint256 i = 0; i < operatorIndexHistoryLength; i++) { - uint256 listIndex = (operatorIndexHistoryLength - 1) - i; - OperatorIndexUpdate memory operatorIndexUpdate = _operatorIdToIndexHistory[operatorId][quorumNumber][listIndex]; - if (operatorIndexUpdate.fromBlockNumber <= blockNumber) { - // one special case is that this will be OPERATOR_DEREGISTERED_INDEX if the operator was deregistered from the quorum - return operatorIndexUpdate.index; - } - } - - // the operator is still active or not in the quorum, so we return the latest index or the default max uint32 - // this will be hit if `blockNumber` is before when the operator registered or the operator has never registered for the given quorum - return OPERATOR_DEREGISTERED_INDEX; - } -} diff --git a/src/contracts/middleware/IndexRegistryStorage.sol b/src/contracts/middleware/IndexRegistryStorage.sol deleted file mode 100644 index 4622a738e..000000000 --- a/src/contracts/middleware/IndexRegistryStorage.sol +++ /dev/null @@ -1,40 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity =0.8.12; - -import "../interfaces/IIndexRegistry.sol"; -import "../interfaces/IRegistryCoordinator.sol"; - -import "@openzeppelin-upgrades/contracts/proxy/utils/Initializable.sol"; - -/** - * @title Storage variables for the `IndexRegistry` contract. - * @author Layr Labs, Inc. - * @notice This storage contract is separate from the logic to simplify the upgrade process. - */ -abstract contract IndexRegistryStorage is Initializable, IIndexRegistry { - - /// @notice The value that indices of deregistered operators are set to - uint32 public constant OPERATOR_DEREGISTERED_INDEX = type(uint32).max; - - /// @notice The RegistryCoordinator contract for this middleware - IRegistryCoordinator public immutable registryCoordinator; - - /// @notice list of all operators ever registered, may include duplicates. used to avoid running an indexer on nodes - bytes32[] public globalOperatorList; - - /// @notice mapping of operatorId => quorumNumber => index history of that operator - mapping(bytes32 => mapping(uint8 => OperatorIndexUpdate[])) internal _operatorIdToIndexHistory; - /// @notice mapping of quorumNumber => history of numbers of unique registered operators - mapping(uint8 => OperatorIndexUpdate[]) internal _totalOperatorsHistory; - - constructor( - IRegistryCoordinator _registryCoordinator - ){ - registryCoordinator = _registryCoordinator; - // disable initializers so that the implementation contract cannot be initialized - _disableInitializers(); - } - - // storage gap for upgradeability - uint256[47] private __GAP; -} diff --git a/src/contracts/middleware/ServiceManagerBase.sol b/src/contracts/middleware/ServiceManagerBase.sol deleted file mode 100644 index 18960f199..000000000 --- a/src/contracts/middleware/ServiceManagerBase.sol +++ /dev/null @@ -1,139 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.9; - -import "@openzeppelin-upgrades/contracts/proxy/utils/Initializable.sol"; -import "@openzeppelin-upgrades/contracts/access/OwnableUpgradeable.sol"; -import "./BLSSignatureChecker.sol"; -import "../permissions/Pausable.sol"; - -import "../interfaces/IDelegationManager.sol"; -import "../interfaces/IServiceManager.sol"; -import "../interfaces/IStrategyManager.sol"; - -/** - * @title Base implementation of `IServiceManager` interface, designed to be inherited from by more complex ServiceManagers. - * @author Layr Labs, Inc. - * @notice This contract is used for: - * - proxying calls to the Slasher contract - * - implementing the two most important functionalities of a ServiceManager: - * - freezing operators as the result of various "challenges" - * - defining the latestServeUntilBlock which is used by the Slasher to determine whether a withdrawal can be completed - */ -abstract contract ServiceManagerBase is - IServiceManager, - Initializable, - OwnableUpgradeable, - BLSSignatureChecker, - Pausable -{ - /// @notice Called in the event of challenge resolution, in order to forward a call to the Slasher, which 'freezes' the `operator`. - /// @dev This function should contain slashing logic, to make sure operators are not needlessly being slashed - // hence it is marked as virtual and must be implemented in each avs' respective service manager contract - function freezeOperator(address operatorAddr) external virtual; - - /// @notice Returns the block until which operators must serve. - /// @dev The block until which the stake accounted for in stake updates is slashable by this middleware - function latestServeUntilBlock() public view virtual returns (uint32); - - ISlasher public immutable slasher; - - /// @notice when applied to a function, ensures that the function is only callable by the `registryCoordinator`. - modifier onlyRegistryCoordinator() { - require( - msg.sender == address(registryCoordinator), - "onlyRegistryCoordinator: not from registry coordinator" - ); - _; - } - - /// @notice when applied to a function, ensures that the function is only callable by the `registryCoordinator`. - /// or by StakeRegistry - modifier onlyRegistryCoordinatorOrStakeRegistry() { - require( - (msg.sender == address(registryCoordinator)) || - (msg.sender == - address( - IBLSRegistryCoordinatorWithIndices( - address(registryCoordinator) - ).stakeRegistry() - )), - "onlyRegistryCoordinatorOrStakeRegistry: not from registry coordinator or stake registry" - ); - _; - } - - constructor( - IBLSRegistryCoordinatorWithIndices _registryCoordinator, - ISlasher _slasher - ) BLSSignatureChecker(_registryCoordinator) { - slasher = _slasher; - _disableInitializers(); - } - - function initialize( - IPauserRegistry _pauserRegistry, - address initialOwner - ) public initializer { - _initializePauser(_pauserRegistry, UNPAUSE_ALL); - _transferOwnership(initialOwner); - } - - // PROXY CALLS TO EQUIVALENT SLASHER FUNCTIONS - - /** - * @notice Called by the Registry in the event of a new registration, to forward a call to the Slasher - * @param operator The operator whose stake is being updated - */ - function recordFirstStakeUpdate( - address operator, - uint32 serveUntilBlock - ) external virtual onlyRegistryCoordinator { - slasher.recordFirstStakeUpdate(operator, serveUntilBlock); - } - - /** - * @notice Called by the registryCoordinator, in order to forward a call to the Slasher, informing it of a stake update - * @param operator The operator whose stake is being updated - * @param updateBlock The block at which the update is being made - * @param prevElement The value of the previous element in the linked list of stake updates (generated offchain) - */ - function recordStakeUpdate( - address operator, - uint32 updateBlock, - uint32 serveUntilBlock, - uint256 prevElement - ) external virtual onlyRegistryCoordinatorOrStakeRegistry { - slasher.recordStakeUpdate( - operator, - updateBlock, - serveUntilBlock, - prevElement - ); - } - - /** - * @notice Called by the registryCoordinator in the event of deregistration, to forward a call to the Slasher - * @param operator The operator being deregistered - */ - function recordLastStakeUpdateAndRevokeSlashingAbility( - address operator, - uint32 serveUntilBlock - ) external virtual onlyRegistryCoordinator { - slasher.recordLastStakeUpdateAndRevokeSlashingAbility( - operator, - serveUntilBlock - ); - } - - // VIEW FUNCTIONS - - /// @dev need to override function here since its defined in both these contracts - function owner() - public - view - override(OwnableUpgradeable, IServiceManager) - returns (address) - { - return OwnableUpgradeable.owner(); - } -} diff --git a/src/contracts/middleware/StakeRegistry.sol b/src/contracts/middleware/StakeRegistry.sol deleted file mode 100644 index e6dc0a86e..000000000 --- a/src/contracts/middleware/StakeRegistry.sol +++ /dev/null @@ -1,580 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity =0.8.12; - -import "@openzeppelin/contracts/utils/math/Math.sol"; -import "../interfaces/IServiceManager.sol"; -import "../interfaces/IStakeRegistry.sol"; -import "../interfaces/IRegistryCoordinator.sol"; -import "../libraries/BitmapUtils.sol"; -import "./StakeRegistryStorage.sol"; - -/** - * @title A `Registry` that keeps track of stakes of operators for up to 256 quorums. - * Specifically, it keeps track of - * 1) The stake of each operator in all the quorums they are a part of for block ranges - * 2) The total stake of all operators in each quorum for block ranges - * 3) The minimum stake required to register for each quorum - * It allows an additional functionality (in addition to registering and deregistering) to update the stake of an operator. - * @author Layr Labs, Inc. - */ -contract StakeRegistry is StakeRegistryStorage { - /// @notice requires that the caller is the RegistryCoordinator - modifier onlyRegistryCoordinator() { - require( - msg.sender == address(registryCoordinator), - "StakeRegistry.onlyRegistryCoordinator: caller is not the RegistryCoordinator" - ); - _; - } - - constructor( - IRegistryCoordinator _registryCoordinator, - IStrategyManager _strategyManager, - IServiceManager _serviceManager - ) - StakeRegistryStorage(_registryCoordinator, _strategyManager, _serviceManager) - // solhint-disable-next-line no-empty-blocks - { - - } - - /** - * @notice Sets the minimum stake for each quorum and adds `_quorumStrategiesConsideredAndMultipliers` for each - * quorum the Registry is being initialized with - */ - function initialize( - uint96[] memory _minimumStakeForQuorum, - StrategyAndWeightingMultiplier[][] memory _quorumStrategiesConsideredAndMultipliers - ) external virtual initializer { - _initialize(_minimumStakeForQuorum, _quorumStrategiesConsideredAndMultipliers); - } - - function _initialize( - uint96[] memory _minimumStakeForQuorum, - StrategyAndWeightingMultiplier[][] memory _quorumStrategiesConsideredAndMultipliers - ) internal virtual onlyInitializing { - // sanity check lengths - require( - _minimumStakeForQuorum.length == _quorumStrategiesConsideredAndMultipliers.length, - "Registry._initialize: minimumStakeForQuorum length mismatch" - ); - - // add the strategies considered and multipliers for each quorum - for (uint8 quorumNumber = 0; quorumNumber < _quorumStrategiesConsideredAndMultipliers.length; ) { - _setMinimumStakeForQuorum(quorumNumber, _minimumStakeForQuorum[quorumNumber]); - _createQuorum(_quorumStrategiesConsideredAndMultipliers[quorumNumber]); - unchecked { - ++quorumNumber; - } - } - } - - /** - * @notice Returns the entire `operatorIdToStakeHistory[operatorId][quorumNumber]` array. - * @param operatorId The id of the operator of interest. - * @param quorumNumber The quorum number to get the stake for. - */ - function getOperatorIdToStakeHistory(bytes32 operatorId, uint8 quorumNumber) external view returns (OperatorStakeUpdate[] memory) { - return operatorIdToStakeHistory[operatorId][quorumNumber]; - } - - /** - * @notice Returns the `index`-th entry in the `operatorIdToStakeHistory[operatorId][quorumNumber]` array. - * @param quorumNumber The quorum number to get the stake for. - * @param operatorId The id of the operator of interest. - * @param index Array index for lookup, within the dynamic array `operatorIdToStakeHistory[operatorId][quorumNumber]`. - * @dev Function will revert if `index` is out-of-bounds. - */ - function getStakeUpdateForQuorumFromOperatorIdAndIndex( - uint8 quorumNumber, - bytes32 operatorId, - uint256 index - ) external view returns (OperatorStakeUpdate memory) { - return operatorIdToStakeHistory[operatorId][quorumNumber][index]; - } - - /** - * @notice Returns the `index`-th entry in the dynamic array of total stake, `_totalStakeHistory` for quorum `quorumNumber`. - * @param quorumNumber The quorum number to get the stake for. - * @param index Array index for lookup, within the dynamic array `_totalStakeHistory[quorumNumber]`. - */ - function getTotalStakeUpdateForQuorumFromIndex( - uint8 quorumNumber, - uint256 index - ) external view returns (OperatorStakeUpdate memory) { - return _totalStakeHistory[quorumNumber][index]; - } - - /// @notice Returns the indices of the operator stakes for the provided `quorumNumber` at the given `blockNumber` - function getStakeUpdateIndexForOperatorIdForQuorumAtBlockNumber( - bytes32 operatorId, - uint8 quorumNumber, - uint32 blockNumber - ) external view returns (uint32) { - return _getStakeUpdateIndexForOperatorIdForQuorumAtBlockNumber(operatorId, quorumNumber, blockNumber); - } - - /** - * @notice Returns the indices of the total stakes for the provided `quorumNumbers` at the given `blockNumber` - * @param blockNumber Block number to retrieve the stake indices from. - * @param quorumNumbers The quorum numbers to get the stake indices for. - * @dev Function will revert if there are no indices for the given `blockNumber` - */ - function getTotalStakeIndicesByQuorumNumbersAtBlockNumber( - uint32 blockNumber, - bytes calldata quorumNumbers - ) external view returns (uint32[] memory) { - uint32[] memory indices = new uint32[](quorumNumbers.length); - for (uint256 i = 0; i < quorumNumbers.length; i++) { - uint8 quorumNumber = uint8(quorumNumbers[i]); - require( - _totalStakeHistory[quorumNumber][0].updateBlockNumber <= blockNumber, - "StakeRegistry.getTotalStakeIndicesByQuorumNumbersAtBlockNumber: quorum has no stake history at blockNumber" - ); - uint32 length = uint32(_totalStakeHistory[quorumNumber].length); - for (uint32 j = 0; j < length; j++) { - if (_totalStakeHistory[quorumNumber][length - j - 1].updateBlockNumber <= blockNumber) { - indices[i] = length - j - 1; - break; - } - } - } - return indices; - } - - /** - * @notice Returns the stake weight corresponding to `operatorId` for quorum `quorumNumber`, at the - * `index`-th entry in the `operatorIdToStakeHistory[operatorId][quorumNumber]` array if it was the operator's - * stake at `blockNumber`. Reverts otherwise. - * @param quorumNumber The quorum number to get the stake for. - * @param operatorId The id of the operator of interest. - * @param index Array index for lookup, within the dynamic array `operatorIdToStakeHistory[operatorId][quorumNumber]`. - * @param blockNumber Block number to make sure the stake is from. - * @dev Function will revert if `index` is out-of-bounds. - */ - function getStakeForQuorumAtBlockNumberFromOperatorIdAndIndex( - uint8 quorumNumber, - uint32 blockNumber, - bytes32 operatorId, - uint256 index - ) external view returns (uint96) { - OperatorStakeUpdate memory operatorStakeUpdate = operatorIdToStakeHistory[operatorId][quorumNumber][index]; - _validateOperatorStakeUpdateAtBlockNumber(operatorStakeUpdate, blockNumber); - return operatorStakeUpdate.stake; - } - - /** - * @notice Returns the total stake weight for quorum `quorumNumber`, at the `index`-th entry in the - * `_totalStakeHistory[quorumNumber]` array if it was the stake at `blockNumber`. Reverts otherwise. - * @param quorumNumber The quorum number to get the stake for. - * @param index Array index for lookup, within the dynamic array `_totalStakeHistory[quorumNumber]`. - * @param blockNumber Block number to make sure the stake is from. - * @dev Function will revert if `index` is out-of-bounds. - */ - function getTotalStakeAtBlockNumberFromIndex( - uint8 quorumNumber, - uint32 blockNumber, - uint256 index - ) external view returns (uint96) { - OperatorStakeUpdate memory totalStakeUpdate = _totalStakeHistory[quorumNumber][index]; - _validateOperatorStakeUpdateAtBlockNumber(totalStakeUpdate, blockNumber); - return totalStakeUpdate.stake; - } - - /** - * @notice Returns the most recent stake weight for the `operatorId` for a certain quorum - * @dev Function returns an OperatorStakeUpdate struct with **every entry equal to 0** in the event that the operator has no stake history - */ - function getMostRecentStakeUpdateByOperatorId( - bytes32 operatorId, - uint8 quorumNumber - ) public view returns (OperatorStakeUpdate memory) { - uint256 historyLength = operatorIdToStakeHistory[operatorId][quorumNumber].length; - OperatorStakeUpdate memory operatorStakeUpdate; - if (historyLength == 0) { - return operatorStakeUpdate; - } else { - operatorStakeUpdate = operatorIdToStakeHistory[operatorId][quorumNumber][historyLength - 1]; - return operatorStakeUpdate; - } - } - - /** - * @notice Returns the most recent stake weight for the `operatorId` for quorum `quorumNumber` - * @dev Function returns weight of **0** in the event that the operator has no stake history - */ - function getCurrentOperatorStakeForQuorum(bytes32 operatorId, uint8 quorumNumber) external view returns (uint96) { - OperatorStakeUpdate memory operatorStakeUpdate = getMostRecentStakeUpdateByOperatorId(operatorId, quorumNumber); - return operatorStakeUpdate.stake; - } - - /// @notice Returns the stake of the operator for the provided `quorumNumber` at the given `blockNumber` - function getStakeForOperatorIdForQuorumAtBlockNumber( - bytes32 operatorId, - uint8 quorumNumber, - uint32 blockNumber - ) external view returns (uint96) { - return - operatorIdToStakeHistory[operatorId][quorumNumber][ - _getStakeUpdateIndexForOperatorIdForQuorumAtBlockNumber(operatorId, quorumNumber, blockNumber) - ].stake; - } - - /** - * @notice Returns the stake weight from the latest entry in `_totalStakeHistory` for quorum `quorumNumber`. - * @dev Will revert if `_totalStakeHistory[quorumNumber]` is empty. - */ - function getCurrentTotalStakeForQuorum(uint8 quorumNumber) external view returns (uint96) { - return _totalStakeHistory[quorumNumber][_totalStakeHistory[quorumNumber].length - 1].stake; - } - - function getLengthOfOperatorIdStakeHistoryForQuorum( - bytes32 operatorId, - uint8 quorumNumber - ) external view returns (uint256) { - return operatorIdToStakeHistory[operatorId][quorumNumber].length; - } - - function getLengthOfTotalStakeHistoryForQuorum(uint8 quorumNumber) external view returns (uint256) { - return _totalStakeHistory[quorumNumber].length; - } - - // MUTATING FUNCTIONS - - /// @notice Adjusts the `minimumStakeFirstQuorum` -- i.e. the node stake (weight) requirement for inclusion in the 1st quorum. - function setMinimumStakeForQuorum(uint8 quorumNumber, uint96 minimumStake) external onlyServiceManagerOwner { - _setMinimumStakeForQuorum(quorumNumber, minimumStake); - } - - /** - * @notice Registers the `operator` with `operatorId` for the specified `quorumNumbers`. - * @param operator The address of the operator to register. - * @param operatorId The id of the operator to register. - * @param quorumNumbers The quorum numbers the operator is registering for, where each byte is an 8 bit integer quorumNumber. - * @dev access restricted to the RegistryCoordinator - * @dev Preconditions (these are assumed, not validated in this contract): - * 1) `quorumNumbers` has no duplicates - * 2) `quorumNumbers.length` != 0 - * 3) `quorumNumbers` is ordered in ascending order - * 4) the operator is not already registered - */ - function registerOperator( - address operator, - bytes32 operatorId, - bytes calldata quorumNumbers - ) external virtual onlyRegistryCoordinator { - _beforeRegisterOperator(operator, operatorId, quorumNumbers); - - _registerOperator(operator, operatorId, quorumNumbers); - - _afterRegisterOperator(operator, operatorId, quorumNumbers); - } - - /** - * @notice Deregisters the operator with `operatorId` for the specified `quorumNumbers`. - * @param operatorId The id of the operator to deregister. - * @param quorumNumbers The quorum numbers the operator is deregistering from, where each byte is an 8 bit integer quorumNumber. - * @dev access restricted to the RegistryCoordinator - * @dev Preconditions (these are assumed, not validated in this contract): - * 1) `quorumNumbers` has no duplicates - * 2) `quorumNumbers.length` != 0 - * 3) `quorumNumbers` is ordered in ascending order - * 4) the operator is not already deregistered - * 5) `quorumNumbers` is a subset of the quorumNumbers that the operator is registered for - */ - function deregisterOperator( - bytes32 operatorId, - bytes calldata quorumNumbers - ) external virtual onlyRegistryCoordinator { - _beforeDeregisterOperator(operatorId, quorumNumbers); - - _deregisterOperator(operatorId, quorumNumbers); - - _afterDeregisterOperator(operatorId, quorumNumbers); - } - - /** - * @notice Used for updating information on deposits of nodes. - * @param operators are the addresses of the operators whose stake information is getting updated - * @dev reverts if there are no operators registered with index out of bounds - */ - function updateStakes(address[] calldata operators) external { - // for each quorum, loop through operators and see if they are a part of the quorum - // if they are, get their new weight and update their individual stake history and the - // quorum's total stake history accordingly - for (uint8 quorumNumber = 0; quorumNumber < quorumCount; ) { - OperatorStakeUpdate memory totalStakeUpdate; - // for each operator - for (uint i = 0; i < operators.length; ) { - bytes32 operatorId = registryCoordinator.getOperatorId(operators[i]); - uint192 quorumBitmap = registryCoordinator.getCurrentQuorumBitmapByOperatorId(operatorId); - // if the operator is not a part of any quorums, skip - if (quorumBitmap == 0) { - continue; - } - // if the operator is a part of the quorum - if (BitmapUtils.numberIsInBitmap(quorumBitmap, quorumNumber)) { - // if the total stake has not been loaded yet, load it - if (totalStakeUpdate.updateBlockNumber == 0) { - totalStakeUpdate = _totalStakeHistory[quorumNumber][ - _totalStakeHistory[quorumNumber].length - 1 - ]; - } - // update the operator's stake based on current state - (uint96 stakeBeforeUpdate, uint96 stakeAfterUpdate) = _updateOperatorStake( - operators[i], - operatorId, - quorumNumber - ); - // calculate the new total stake for the quorum - totalStakeUpdate.stake = totalStakeUpdate.stake - stakeBeforeUpdate + stakeAfterUpdate; - } - unchecked { - ++i; - } - } - // if the total stake for this quorum was updated, record it in storage - if (totalStakeUpdate.updateBlockNumber != 0) { - // update the total stake history for the quorum - _recordTotalStakeUpdate(quorumNumber, totalStakeUpdate); - } - unchecked { - ++quorumNumber; - } - } - - // TODO after slashing enabled: record stake updates in the EigenLayer Slasher - // for (uint i = 0; i < operators.length;) { - // serviceManager.recordStakeUpdate(operators[i], uint32(block.number), serviceManager.latestServeUntilBlock(), prevElements[i]); - // unchecked { - // ++i; - // } - // } - } - - // INTERNAL FUNCTIONS - - function _getStakeUpdateIndexForOperatorIdForQuorumAtBlockNumber( - bytes32 operatorId, - uint8 quorumNumber, - uint32 blockNumber - ) internal view returns (uint32) { - uint32 length = uint32(operatorIdToStakeHistory[operatorId][quorumNumber].length); - for (uint32 i = 0; i < length; i++) { - if (operatorIdToStakeHistory[operatorId][quorumNumber][length - i - 1].updateBlockNumber <= blockNumber) { - require( - operatorIdToStakeHistory[operatorId][quorumNumber][length - i - 1].nextUpdateBlockNumber == 0 || - operatorIdToStakeHistory[operatorId][quorumNumber][length - i - 1].nextUpdateBlockNumber > - blockNumber, - "StakeRegistry._getStakeUpdateIndexForOperatorIdForQuorumAtBlockNumber: operatorId has no stake update at blockNumber" - ); - return length - i - 1; - } - } - revert( - "StakeRegistry._getStakeUpdateIndexForOperatorIdForQuorumAtBlockNumber: no stake update found for operatorId and quorumNumber at block number" - ); - } - - function _setMinimumStakeForQuorum(uint8 quorumNumber, uint96 minimumStake) internal { - minimumStakeForQuorum[quorumNumber] = minimumStake; - emit MinimumStakeForQuorumUpdated(quorumNumber, minimumStake); - } - - /** - * @notice Updates the stake for the operator with `operatorId` for the specified `quorumNumbers`. The total stake - * for each quorum is updated accordingly in addition to the operator's individual stake history. - */ - function _registerOperator(address operator, bytes32 operatorId, bytes memory quorumNumbers) internal { - // check the operator is registering for only valid quorums - require( - uint8(quorumNumbers[quorumNumbers.length - 1]) < quorumCount, - "StakeRegistry._registerOperator: greatest quorumNumber must be less than quorumCount" - ); - OperatorStakeUpdate memory _newTotalStakeUpdate; - // add the `updateBlockNumber` info - _newTotalStakeUpdate.updateBlockNumber = uint32(block.number); - // for each quorum, evaluate stake and add to total stake - for (uint8 quorumNumbersIndex = 0; quorumNumbersIndex < quorumNumbers.length; ) { - // get the next quorumNumber - uint8 quorumNumber = uint8(quorumNumbers[quorumNumbersIndex]); - // evaluate the stake for the operator - // since we don't use the first output, this will use 1 extra sload when deregistered operator's register again - (, uint96 stake) = _updateOperatorStake(operator, operatorId, quorumNumber); - // check if minimum requirement has been met, will be 0 if not - require( - stake != 0, - "StakeRegistry._registerOperator: Operator does not meet minimum stake requirement for quorum" - ); - // add operator stakes to total stake before update (in memory) - uint256 _totalStakeHistoryLength = _totalStakeHistory[quorumNumber].length; - // add calculate the total stake for the quorum - uint96 totalStakeAfterUpdate = stake; - if (_totalStakeHistoryLength != 0) { - // only add the stake if there is a previous total stake - // overwrite `stake` variable - totalStakeAfterUpdate += _totalStakeHistory[quorumNumber][_totalStakeHistoryLength - 1].stake; - } - _newTotalStakeUpdate.stake = totalStakeAfterUpdate; - // update storage of total stake - _recordTotalStakeUpdate(quorumNumber, _newTotalStakeUpdate); - unchecked { - ++quorumNumbersIndex; - } - } - } - - /** - * @notice Removes the stakes of the operator with `operatorId` from the quorums specified in `quorumNumbers` - * the total stake of the quorums specified in `quorumNumbers` will be updated and so will the operator's individual - * stake updates. These operator's individual stake updates will have a 0 stake value for the latest update. - */ - function _deregisterOperator(bytes32 operatorId, bytes memory quorumNumbers) internal { - OperatorStakeUpdate memory _operatorStakeUpdate; - // add the `updateBlockNumber` info - _operatorStakeUpdate.updateBlockNumber = uint32(block.number); - OperatorStakeUpdate memory _newTotalStakeUpdate; - // add the `updateBlockNumber` info - _newTotalStakeUpdate.updateBlockNumber = uint32(block.number); - // loop through the operator's quorums and remove the operator's stake for each quorum - for (uint8 quorumNumbersIndex = 0; quorumNumbersIndex < quorumNumbers.length; ) { - uint8 quorumNumber = uint8(quorumNumbers[quorumNumbersIndex]); - // update the operator's stake - uint96 stakeBeforeUpdate = _recordOperatorStakeUpdate(operatorId, quorumNumber, _operatorStakeUpdate); - // subtract the amounts staked by the operator that is getting deregistered from the total stake before deregistration - // copy latest totalStakes to memory - _newTotalStakeUpdate.stake = - _totalStakeHistory[quorumNumber][_totalStakeHistory[quorumNumber].length - 1].stake - - stakeBeforeUpdate; - // update storage of total stake - _recordTotalStakeUpdate(quorumNumber, _newTotalStakeUpdate); - - emit StakeUpdate( - operatorId, - quorumNumber, - // new stakes are zero - 0 - ); - unchecked { - ++quorumNumbersIndex; - } - } - } - - /** - * @notice Finds the updated stake for `operator` for `quorumNumber`, stores it, records the update, - * and returns both the previous stake then updated stake. - * @dev **DOES NOT UPDATE `totalStake` IN ANY WAY** -- `totalStake` updates must be done elsewhere. - */ - function _updateOperatorStake( - address operator, - bytes32 operatorId, - uint8 quorumNumber - ) internal returns (uint96, uint96) { - // determine new stakes - OperatorStakeUpdate memory operatorStakeUpdate; - operatorStakeUpdate.updateBlockNumber = uint32(block.number); - operatorStakeUpdate.stake = weightOfOperatorForQuorum(quorumNumber, operator); - - // check if minimum requirements have been met - if (operatorStakeUpdate.stake < minimumStakeForQuorum[quorumNumber]) { - // set staker to 0 - operatorStakeUpdate.stake = uint96(0); - } - // get stakeBeforeUpdate and update with new stake - uint96 stakeBeforeUpdate = _recordOperatorStakeUpdate(operatorId, quorumNumber, operatorStakeUpdate); - - emit StakeUpdate(operatorId, quorumNumber, operatorStakeUpdate.stake); - - return (stakeBeforeUpdate, operatorStakeUpdate.stake); - } - - /** - * @notice Records that `operatorId`'s current stake for `quorumNumber` is now param @operatorStakeUpdate - * and returns the previous stake. - */ - function _recordOperatorStakeUpdate( - bytes32 operatorId, - uint8 quorumNumber, - OperatorStakeUpdate memory operatorStakeUpdate - ) internal returns (uint96) { - // initialize stakeBeforeUpdate to 0 - uint96 stakeBeforeUpdate; - uint256 operatorStakeHistoryLength = operatorIdToStakeHistory[operatorId][quorumNumber].length; - - if (operatorStakeHistoryLength != 0) { - // set nextUpdateBlockNumber in prev stakes - operatorIdToStakeHistory[operatorId][quorumNumber][operatorStakeHistoryLength - 1] - .nextUpdateBlockNumber = uint32(block.number); - // load stake before update into memory if it exists - stakeBeforeUpdate = operatorIdToStakeHistory[operatorId][quorumNumber][operatorStakeHistoryLength - 1] - .stake; - } - // push new stake to storage - operatorIdToStakeHistory[operatorId][quorumNumber].push(operatorStakeUpdate); - return stakeBeforeUpdate; - } - - /// @notice Records that the `totalStake` for `quorumNumber` is now equal to the input param @_totalStake - function _recordTotalStakeUpdate(uint8 quorumNumber, OperatorStakeUpdate memory _totalStake) internal { - uint256 _totalStakeHistoryLength = _totalStakeHistory[quorumNumber].length; - if (_totalStakeHistoryLength != 0) { - _totalStakeHistory[quorumNumber][_totalStakeHistoryLength - 1].nextUpdateBlockNumber = uint32(block.number); - } - _totalStake.updateBlockNumber = uint32(block.number); - _totalStakeHistory[quorumNumber].push(_totalStake); - } - - /** - * @dev Hook that is called before any operator registration to insert additional logic. - * @param operator The address of the operator to register. - * @param operatorId The id of the operator to register. - * @param quorumNumbers The quorum numbers the operator is registering for, where each byte is an 8 bit integer quorumNumber. - */ - function _beforeRegisterOperator( - address operator, - bytes32 operatorId, - bytes memory quorumNumbers - ) internal virtual {} - - /** - * @dev Hook that is called after any operator registration to insert additional logic. - * @param operator The address of the operator to register. - * @param operatorId The id of the operator to register. - * @param quorumNumbers The quorum numbers the operator is registering for, where each byte is an 8 bit integer quorumNumber. - */ - function _afterRegisterOperator( - address operator, - bytes32 operatorId, - bytes memory quorumNumbers - ) internal virtual {} - - /** - * @dev Hook that is called before any operator deregistration to insert additional logic. - * @param operatorId The id of the operator to register. - * @param quorumNumbers The quorum numbers the operator is registering for, where each byte is an 8 bit integer quorumNumber. - */ - function _beforeDeregisterOperator(bytes32 operatorId, bytes memory quorumNumbers) internal virtual {} - - /** - * @dev Hook that is called after any operator deregistration to insert additional logic. - * @param operatorId The id of the operator to register. - * @param quorumNumbers The quorum numbers the operator is registering for, where each byte is an 8 bit integer quorumNumber. - */ - function _afterDeregisterOperator(bytes32 operatorId, bytes memory quorumNumbers) internal virtual {} - - /// @notice Validates that the `operatorStake` was accurate at the given `blockNumber` - function _validateOperatorStakeUpdateAtBlockNumber( - OperatorStakeUpdate memory operatorStakeUpdate, - uint32 blockNumber - ) internal pure { - require( - operatorStakeUpdate.updateBlockNumber <= blockNumber, - "StakeRegistry._validateOperatorStakeAtBlockNumber: operatorStakeUpdate is from after blockNumber" - ); - require( - operatorStakeUpdate.nextUpdateBlockNumber == 0 || operatorStakeUpdate.nextUpdateBlockNumber > blockNumber, - "StakeRegistry._validateOperatorStakeAtBlockNumber: there is a newer operatorStakeUpdate available before blockNumber" - ); - } -} diff --git a/src/contracts/middleware/StakeRegistryStorage.sol b/src/contracts/middleware/StakeRegistryStorage.sol deleted file mode 100644 index c62757682..000000000 --- a/src/contracts/middleware/StakeRegistryStorage.sol +++ /dev/null @@ -1,41 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity =0.8.12; - -import "../interfaces/IServiceManager.sol"; -import "../interfaces/IStakeRegistry.sol"; -import "../interfaces/IRegistryCoordinator.sol"; -import "./VoteWeigherBase.sol"; - -/** - * @title Storage variables for the `StakeRegistry` contract. - * @author Layr Labs, Inc. - * @notice This storage contract is separate from the logic to simplify the upgrade process. - */ -abstract contract StakeRegistryStorage is VoteWeigherBase, IStakeRegistry { - /// @notice the coordinator contract that this registry is associated with - IRegistryCoordinator public immutable registryCoordinator; - - /// @notice In order to register for a quorum i, an operator must have at least `minimumStakeForQuorum[i]` - /// evaluated by this contract's 'VoteWeigher' logic. - uint96[256] public minimumStakeForQuorum; - - /// @notice array of the history of the total stakes for each quorum -- marked as internal since getTotalStakeFromIndex is a getter for this - OperatorStakeUpdate[][256] internal _totalStakeHistory; - - /// @notice mapping from operator's operatorId to the history of their stake updates - mapping(bytes32 => mapping(uint8 => OperatorStakeUpdate[])) internal operatorIdToStakeHistory; - - constructor( - IRegistryCoordinator _registryCoordinator, - IStrategyManager _strategyManager, - IServiceManager _serviceManager - ) VoteWeigherBase(_strategyManager, _serviceManager) - // solhint-disable-next-line no-empty-blocks - { - registryCoordinator = _registryCoordinator; - } - - // storage gap for upgradeability - // slither-disable-next-line shadowing-state - uint256[65] private __GAP; -} \ No newline at end of file diff --git a/src/contracts/middleware/VoteWeigherBase.sol b/src/contracts/middleware/VoteWeigherBase.sol deleted file mode 100644 index 275913f54..000000000 --- a/src/contracts/middleware/VoteWeigherBase.sol +++ /dev/null @@ -1,220 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity =0.8.12; - -import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import "../interfaces/IStrategyManager.sol"; -import "./VoteWeigherBaseStorage.sol"; - -/** - * @title A simple implementation of the `IVoteWeigher` interface. - * @author Layr Labs, Inc. - * @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service - * @notice This contract is used for - * - computing the total weight of an operator for any of the quorums that are considered - * by the middleware - * - addition and removal of strategies and the associated weighting criteria that are assigned - * by the middleware for each of the quorum(s) - */ -contract VoteWeigherBase is VoteWeigherBaseStorage { - /// @notice when applied to a function, ensures that the function is only callable by the current `owner` of the `serviceManager` - modifier onlyServiceManagerOwner() { - require(msg.sender == serviceManager.owner(), "VoteWeigherBase.onlyServiceManagerOwner: caller is not the owner of the serviceManager"); - _; - } - - /// @notice when applied to a function, ensures that the `quorumNumber` corresponds to a valid quorum added to the VoteWeigher - modifier validQuorumNumber(uint8 quorumNumber) { - require(quorumNumber < quorumCount, "VoteWeigherBase.validQuorumNumber: quorumNumber is not valid"); - _; - } - - /// @notice Sets the (immutable) `strategyManager` and `serviceManager` addresses - constructor( - IStrategyManager _strategyManager, - IServiceManager _serviceManager - ) VoteWeigherBaseStorage(_strategyManager, _serviceManager) {} - - /// @notice Returns the strategy and weight multiplier for the `index`'th strategy in the quorum `quorumNumber` - function strategyAndWeightingMultiplierForQuorumByIndex(uint8 quorumNumber, uint256 index) - public - view - returns (StrategyAndWeightingMultiplier memory) - { - return strategiesConsideredAndMultipliers[quorumNumber][index]; - } - - /** - * @notice This function computes the total weight of the @param operator in the quorum @param quorumNumber. - * @dev reverts in the case that `quorumNumber` is greater than or equal to `quorumCount` - */ - function weightOfOperatorForQuorumView(uint8 quorumNumber, address operator) public virtual view validQuorumNumber(quorumNumber) returns (uint96) { - uint96 weight; - uint256 stratsLength = strategiesConsideredAndMultipliersLength(quorumNumber); - StrategyAndWeightingMultiplier memory strategyAndMultiplier; - - for (uint256 i = 0; i < stratsLength;) { - // accessing i^th StrategyAndWeightingMultiplier struct for the quorumNumber - strategyAndMultiplier = strategiesConsideredAndMultipliers[quorumNumber][i]; - - // shares of the operator in the strategy - uint256 sharesAmount = delegation.operatorShares(operator, strategyAndMultiplier.strategy); - - // add the weight from the shares for this strategy to the total weight - if (sharesAmount > 0) { - weight += uint96(sharesAmount * strategyAndMultiplier.multiplier / WEIGHTING_DIVISOR); - } - - unchecked { - ++i; - } - } - - return weight; - } - - /** - * @notice This function computes the total weight of the @param operator in the quorum @param quorumNumber. - * @dev reverts in the case that `quorumNumber` is greater than or equal to `quorumCount` - * @dev a version of weightOfOperatorForQuorumView that can change state if needed - */ - function weightOfOperatorForQuorum(uint8 quorumNumber, address operator) public virtual validQuorumNumber(quorumNumber) returns (uint96) { - return weightOfOperatorForQuorumView(quorumNumber, operator); - } - - /// @notice Create a new quorum and add the strategies and their associated weights to the quorum. - function createQuorum( - StrategyAndWeightingMultiplier[] memory _strategiesConsideredAndMultipliers - ) external virtual onlyServiceManagerOwner { - _createQuorum(_strategiesConsideredAndMultipliers); - } - - /// @notice Adds new strategies and the associated multipliers to the @param quorumNumber. - function addStrategiesConsideredAndMultipliers( - uint8 quorumNumber, - StrategyAndWeightingMultiplier[] memory _newStrategiesConsideredAndMultipliers - ) external virtual onlyServiceManagerOwner validQuorumNumber(quorumNumber) { - _addStrategiesConsideredAndMultipliers(quorumNumber, _newStrategiesConsideredAndMultipliers); - } - - /** - * @notice This function is used for removing strategies and their associated weights from the - * mapping strategiesConsideredAndMultipliers for a specific @param quorumNumber. - * @dev higher indices should be *first* in the list of @param indicesToRemove, since otherwise - * the removal of lower index entries will cause a shift in the indices of the other strategiesToRemove - */ - function removeStrategiesConsideredAndMultipliers( - uint8 quorumNumber, - uint256[] calldata indicesToRemove - ) external virtual onlyServiceManagerOwner validQuorumNumber(quorumNumber) { - uint256 indicesToRemoveLength = indicesToRemove.length; - require(indicesToRemoveLength > 0, "VoteWeigherBase.removeStrategiesConsideredAndMultipliers: no indices to remove provided"); - for (uint256 i = 0; i < indicesToRemoveLength;) { - emit StrategyRemovedFromQuorum(quorumNumber, strategiesConsideredAndMultipliers[quorumNumber][indicesToRemove[i]].strategy); - emit StrategyMultiplierUpdated(quorumNumber, strategiesConsideredAndMultipliers[quorumNumber][indicesToRemove[i]].strategy, 0); - // remove strategy and its associated multiplier - strategiesConsideredAndMultipliers[quorumNumber][indicesToRemove[i]] = strategiesConsideredAndMultipliers[ - quorumNumber - ][strategiesConsideredAndMultipliers[quorumNumber].length - 1]; - strategiesConsideredAndMultipliers[quorumNumber].pop(); - - unchecked { - ++i; - } - } - } - - /** - * @notice This function is used for modifying the weights of strategies that are already in the - * mapping strategiesConsideredAndMultipliers for a specific - * @param quorumNumber is the quorum number to change the strategy for - * @param strategyIndices are the indices of the strategies to change - * @param newMultipliers are the new multipliers for the strategies - */ - function modifyStrategyWeights( - uint8 quorumNumber, - uint256[] calldata strategyIndices, - uint96[] calldata newMultipliers - ) external virtual onlyServiceManagerOwner validQuorumNumber(quorumNumber) { - uint256 numStrats = strategyIndices.length; - require(numStrats > 0, "VoteWeigherBase.modifyStrategyWeights: no strategy indices provided"); - // sanity check on input lengths - require(newMultipliers.length == numStrats, "VoteWeigherBase.modifyStrategyWeights: input length mismatch"); - - for (uint256 i = 0; i < numStrats; ) { - // change the strategy's associated multiplier - strategiesConsideredAndMultipliers[quorumNumber][strategyIndices[i]].multiplier = newMultipliers[i]; - emit StrategyMultiplierUpdated(quorumNumber, strategiesConsideredAndMultipliers[quorumNumber][strategyIndices[i]].strategy, newMultipliers[i]); - unchecked { - ++i; - } - } - } - - /// @notice Returns the length of the dynamic array stored in `strategiesConsideredAndMultipliers[quorumNumber]`. - function strategiesConsideredAndMultipliersLength(uint8 quorumNumber) public view returns (uint256) { - return strategiesConsideredAndMultipliers[quorumNumber].length; - } - - /** - * @notice Creates a quorum with the given_strategiesConsideredAndMultipliers. - */ - function _createQuorum( - StrategyAndWeightingMultiplier[] memory _strategiesConsideredAndMultipliers - ) internal { - uint16 quorumCountMem = quorumCount; - require(quorumCountMem < MAX_QUORUM_COUNT, "VoteWeigherBase._createQuorum: number of quorums cannot exceed MAX_QUORUM_COUNT"); - uint8 quorumNumber = uint8(quorumCountMem); - // increment quorumCount - quorumCount = quorumCountMem + 1; - // add the strategies and their associated weights to the quorum - _addStrategiesConsideredAndMultipliers(quorumNumber, _strategiesConsideredAndMultipliers); - // emit event - emit QuorumCreated(quorumNumber); - } - - /** - * @notice Adds `_newStrategiesConsideredAndMultipliers` to the `quorumNumber`-th quorum. - * @dev Checks to make sure that the *same* strategy cannot be added multiple times (checks against both against existing and new strategies). - * @dev This function has no check to make sure that the strategies for a single quorum have the same underlying asset. This is a concious choice, - * since a middleware may want, e.g., a stablecoin quorum that accepts USDC, USDT, DAI, etc. as underlying assets and trades them as "equivalent". - */ - function _addStrategiesConsideredAndMultipliers( - uint8 quorumNumber, - StrategyAndWeightingMultiplier[] memory _newStrategiesConsideredAndMultipliers - ) internal { - require(_newStrategiesConsideredAndMultipliers.length > 0, "VoteWeigherBase._addStrategiesConsideredAndMultipliers: no strategies provided"); - uint256 numStratsToAdd = _newStrategiesConsideredAndMultipliers.length; - uint256 numStratsExisting = strategiesConsideredAndMultipliers[quorumNumber].length; - require( - numStratsExisting + numStratsToAdd <= MAX_WEIGHING_FUNCTION_LENGTH, - "VoteWeigherBase._addStrategiesConsideredAndMultipliers: exceed MAX_WEIGHING_FUNCTION_LENGTH" - ); - for (uint256 i = 0; i < numStratsToAdd; ) { - // fairly gas-expensive internal loop to make sure that the *same* strategy cannot be added multiple times - for (uint256 j = 0; j < (numStratsExisting + i); ) { - require( - strategiesConsideredAndMultipliers[quorumNumber][j].strategy != - _newStrategiesConsideredAndMultipliers[i].strategy, - "VoteWeigherBase._addStrategiesConsideredAndMultipliers: cannot add same strategy 2x" - ); - unchecked { - ++j; - } - } - require( - _newStrategiesConsideredAndMultipliers[i].multiplier > 0, - "VoteWeigherBase._addStrategiesConsideredAndMultipliers: cannot add strategy with zero weight" - ); - strategiesConsideredAndMultipliers[quorumNumber].push(_newStrategiesConsideredAndMultipliers[i]); - emit StrategyAddedToQuorum(quorumNumber, _newStrategiesConsideredAndMultipliers[i].strategy); - emit StrategyMultiplierUpdated( - quorumNumber, - _newStrategiesConsideredAndMultipliers[i].strategy, - _newStrategiesConsideredAndMultipliers[i].multiplier - ); - unchecked { - ++i; - } - } - } -} diff --git a/src/contracts/middleware/VoteWeigherBaseStorage.sol b/src/contracts/middleware/VoteWeigherBaseStorage.sol deleted file mode 100644 index 0babb54c2..000000000 --- a/src/contracts/middleware/VoteWeigherBaseStorage.sol +++ /dev/null @@ -1,63 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity =0.8.12; - -import "../interfaces/IDelegationManager.sol"; -import "../interfaces/IStrategyManager.sol"; -import "../interfaces/IVoteWeigher.sol"; -import "../interfaces/IServiceManager.sol"; - -import "@openzeppelin-upgrades/contracts/proxy/utils/Initializable.sol"; - -/** - * @title Storage variables for the `VoteWeigherBase` contract. - * @author Layr Labs, Inc. - * @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service - * @notice This storage contract is separate from the logic to simplify the upgrade process. - */ -abstract contract VoteWeigherBaseStorage is Initializable, IVoteWeigher { - /// @notice Constant used as a divisor in calculating weights. - uint256 public constant WEIGHTING_DIVISOR = 1e18; - /// @notice Maximum length of dynamic arrays in the `strategiesConsideredAndMultipliers` mapping. - uint8 public constant MAX_WEIGHING_FUNCTION_LENGTH = 32; - /// @notice Constant used as a divisor in dealing with BIPS amounts. - uint256 internal constant MAX_BIPS = 10000; - /// @notice The maximum number of quorums that the VoteWeigher is considering. - uint8 public constant MAX_QUORUM_COUNT = 192; - - /// @notice The address of the Delegation contract for EigenLayer. - IDelegationManager public immutable delegation; - - /// @notice The address of the StrategyManager contract for EigenLayer. - IStrategyManager public immutable strategyManager; - - /// @notice The address of the Slasher contract for EigenLayer. - ISlasher public immutable slasher; - - /// @notice The ServiceManager contract for this middleware, where tasks are created / initiated. - IServiceManager public immutable serviceManager; - - /// @notice The number of quorums that the VoteWeigher is considering. - uint16 public quorumCount; - - /** - * @notice mapping from quorum number to the list of strategies considered and their - * corresponding multipliers for that specific quorum - */ - mapping(uint8 => StrategyAndWeightingMultiplier[]) public strategiesConsideredAndMultipliers; - - constructor( - IStrategyManager _strategyManager, - IServiceManager _serviceManager - ) { - strategyManager = _strategyManager; - delegation = _strategyManager.delegation(); - slasher = _strategyManager.slasher(); - serviceManager = _serviceManager; - // disable initializers so that the implementation contract cannot be initialized - _disableInitializers(); - } - - // storage gap for upgradeability - // slither-disable-next-line shadowing-state - uint256[49] private __GAP; -} diff --git a/src/test/Delegation.t.sol b/src/test/Delegation.t.sol index 6db316a1f..14cb1c059 100644 --- a/src/test/Delegation.t.sol +++ b/src/test/Delegation.t.sol @@ -6,22 +6,16 @@ import "src/contracts/interfaces/ISignatureUtils.sol"; import "../test/EigenLayerTestHelper.t.sol"; -import "./mocks/MiddlewareRegistryMock.sol"; import "./mocks/ServiceManagerMock.sol"; -import "./harnesses/StakeRegistryHarness.sol"; - contract DelegationTests is EigenLayerTestHelper { - using Math for uint256; - uint256 public PRIVATE_KEY = 420; uint32 serveUntil = 100; address public registryCoordinator = address(uint160(uint256(keccak256("registryCoordinator")))); ServiceManagerMock public serviceManager; - StakeRegistryHarness public stakeRegistry; - StakeRegistryHarness public stakeRegistryImplementation; + StakeRegistryStub public stakeRegistry; uint8 defaultQuorumNumber = 0; bytes32 defaultOperatorId = bytes32(uint256(0)); @@ -40,14 +34,7 @@ contract DelegationTests is EigenLayerTestHelper { function initializeMiddlewares() public { serviceManager = new ServiceManagerMock(slasher); - stakeRegistry = StakeRegistryHarness( - address(new TransparentUpgradeableProxy(address(emptyContract), address(eigenLayerProxyAdmin), "")) - ); - stakeRegistryImplementation = new StakeRegistryHarness( - IRegistryCoordinator(registryCoordinator), - strategyManager, - serviceManager - ); + stakeRegistry = new StakeRegistryStub(); { uint96 multiplier = 1e18; @@ -58,7 +45,7 @@ contract DelegationTests is EigenLayerTestHelper { // _quorumBips[1] = 4000; // IVoteWeigher.StrategyAndWeightingMultiplier[] memory ethStratsAndMultipliers = // new IVoteWeigher.StrategyAndWeightingMultiplier[](1); - // ethStratsAndMultipliers[0].strategy = wethStrat; + // ethStratsAndMultipliers[0].strategy = wethStrat; // ethStratsAndMultipliers[0].multiplier = multiplier; // IVoteWeigher.StrategyAndWeightingMultiplier[] memory eigenStratsAndMultipliers = // new IVoteWeigher.StrategyAndWeightingMultiplier[](1); @@ -70,12 +57,14 @@ contract DelegationTests is EigenLayerTestHelper { // setup the dummy minimum stake for quorum uint96[] memory minimumStakeForQuorum = new uint96[](_NUMBER_OF_QUORUMS); for (uint256 i = 0; i < minimumStakeForQuorum.length; i++) { - minimumStakeForQuorum[i] = uint96(i+1); + minimumStakeForQuorum[i] = uint96(i + 1); } // setup the dummy quorum strategies - IVoteWeigher.StrategyAndWeightingMultiplier[][] memory quorumStrategiesConsideredAndMultipliers = - new IVoteWeigher.StrategyAndWeightingMultiplier[][](2); + IVoteWeigher.StrategyAndWeightingMultiplier[][] + memory quorumStrategiesConsideredAndMultipliers = new IVoteWeigher.StrategyAndWeightingMultiplier[][]( + 2 + ); quorumStrategiesConsideredAndMultipliers[0] = new IVoteWeigher.StrategyAndWeightingMultiplier[](1); quorumStrategiesConsideredAndMultipliers[0][0] = IVoteWeigher.StrategyAndWeightingMultiplier( wethStrat, @@ -87,13 +76,7 @@ contract DelegationTests is EigenLayerTestHelper { multiplier ); - eigenLayerProxyAdmin.upgradeAndCall( - TransparentUpgradeableProxy(payable(address(stakeRegistry))), - address(stakeRegistryImplementation), - abi.encodeWithSelector(StakeRegistry.initialize.selector, minimumStakeForQuorum, quorumStrategiesConsideredAndMultipliers) - ); cheats.stopPrank(); - } } @@ -124,41 +107,38 @@ contract DelegationTests is EigenLayerTestHelper { /// and checks that the delegate's voteWeights increase properly /// @param operator is the operator being delegated to. /// @param staker is the staker delegating stake to the operator. - function testDelegation(address operator, address staker, uint96 ethAmount, uint96 eigenAmount) - public - fuzzedAddress(operator) - fuzzedAddress(staker) - fuzzedAmounts(ethAmount, eigenAmount) - { + function testDelegation( + address operator, + address staker, + uint96 ethAmount, + uint96 eigenAmount + ) public fuzzedAddress(operator) fuzzedAddress(staker) fuzzedAmounts(ethAmount, eigenAmount) { cheats.assume(staker != operator); // base strategy will revert if these amounts are too small on first deposit cheats.assume(ethAmount >= 1); cheats.assume(eigenAmount >= 2); - + // Set weights ahead of the helper function call bytes memory quorumNumbers = new bytes(2); quorumNumbers[0] = bytes1(uint8(0)); quorumNumbers[0] = bytes1(uint8(1)); - stakeRegistry.setOperatorWeight(0, operator, ethAmount); - stakeRegistry.setOperatorWeight(1, operator, eigenAmount); - stakeRegistry.registerOperatorNonCoordinator(operator, defaultOperatorId, quorumNumbers); _testDelegation(operator, staker, ethAmount, eigenAmount, stakeRegistry); } /// @notice tests that a when an operator is delegated to, that delegation is properly accounted for. - function testDelegationReceived(address _operator, address staker, uint64 ethAmount, uint64 eigenAmount) - public - fuzzedAddress(_operator) - fuzzedAddress(staker) - fuzzedAmounts(ethAmount, eigenAmount) - { + function testDelegationReceived( + address _operator, + address staker, + uint64 ethAmount, + uint64 eigenAmount + ) public fuzzedAddress(_operator) fuzzedAddress(staker) fuzzedAmounts(ethAmount, eigenAmount) { cheats.assume(staker != _operator); cheats.assume(ethAmount >= 1); cheats.assume(eigenAmount >= 2); // use storage to solve stack-too-deep operator = _operator; - + IDelegationManager.OperatorDetails memory operatorDetails = IDelegationManager.OperatorDetails({ earningsReceiver: operator, delegationApprover: address(0), @@ -168,64 +148,46 @@ contract DelegationTests is EigenLayerTestHelper { _testRegisterAsOperator(operator, operatorDetails); } - uint256[3] memory amountsBefore; - amountsBefore[0] = stakeRegistry.weightOfOperatorForQuorum(0, operator); - amountsBefore[1] = stakeRegistry.weightOfOperatorForQuorum(1, operator); - amountsBefore[2] = delegation.operatorShares(operator, wethStrat); + uint256 amountBefore = delegation.operatorShares(operator, wethStrat); //making additional deposits to the strategies assertTrue(!delegation.isDelegated(staker) == true, "testDelegation: staker is not delegate"); _testDepositWeth(staker, ethAmount); _testDepositEigen(staker, eigenAmount); _testDelegateToOperator(staker, operator); - stakeRegistry.setOperatorWeight(0, operator, ethAmount); - stakeRegistry.setOperatorWeight(1, operator, eigenAmount); assertTrue(delegation.isDelegated(staker) == true, "testDelegation: staker is not delegate"); - (IStrategy[] memory updatedStrategies, uint256[] memory updatedShares) = - strategyManager.getDeposits(staker); + (IStrategy[] memory updatedStrategies, uint256[] memory updatedShares) = strategyManager.getDeposits(staker); - { - uint256 stakerEthWeight = strategyManager.stakerStrategyShares(staker, updatedStrategies[0]); - uint256 stakerEigenWeight = strategyManager.stakerStrategyShares(staker, updatedStrategies[1]); - - uint256 operatorEthWeightAfter = stakeRegistry.weightOfOperatorForQuorum(0, operator); - uint256 operatorEigenWeightAfter = stakeRegistry.weightOfOperatorForQuorum(1, operator); - - assertTrue( - operatorEthWeightAfter - amountsBefore[0] == stakerEthWeight, - "testDelegation: operatorEthWeight did not increment by the right amount" - ); - assertTrue( - operatorEigenWeightAfter - amountsBefore[1] == stakerEigenWeight, - "Eigen weights did not increment by the right amount" - ); - } + uint256 stakerEthWeight = strategyManager.stakerStrategyShares(staker, updatedStrategies[0]); + uint256 stakerEigenWeight = strategyManager.stakerStrategyShares(staker, updatedStrategies[1]); { IStrategy _strat = wethStrat; // IStrategy _strat = strategyManager.stakerStrats(staker, 0); assertTrue(address(_strat) != address(0), "stakerStrats not updated correctly"); assertTrue( - delegation.operatorShares(operator, _strat) - updatedShares[0] == amountsBefore[2], + delegation.operatorShares(operator, _strat) - updatedShares[0] == amountBefore, "ETH operatorShares not updated correctly" ); cheats.startPrank(address(strategyManager)); IDelegationManager.OperatorDetails memory expectedOperatorDetails = delegation.operatorDetails(operator); - assertTrue(keccak256(abi.encode(expectedOperatorDetails)) == keccak256(abi.encode(operatorDetails)), - "failed to set correct operator details"); + assertTrue( + keccak256(abi.encode(expectedOperatorDetails)) == keccak256(abi.encode(operatorDetails)), + "failed to set correct operator details" + ); } } /// @notice tests that a when an operator is undelegated from, that the staker is properly classified as undelegated. - function testUndelegation(address operator, address staker, uint96 ethAmount, uint96 eigenAmount) - public - fuzzedAddress(operator) - fuzzedAddress(staker) - fuzzedAmounts(ethAmount, eigenAmount) - { + function testUndelegation( + address operator, + address staker, + uint96 ethAmount, + uint96 eigenAmount + ) public fuzzedAddress(operator) fuzzedAddress(staker) fuzzedAmounts(ethAmount, eigenAmount) { cheats.assume(staker != operator); // base strategy will revert if these amounts are too small on first deposit cheats.assume(ethAmount >= 1); @@ -236,13 +198,7 @@ contract DelegationTests is EigenLayerTestHelper { uint256[] memory strategyIndexes = new uint256[](strategyArray.length); // withdraw shares - _testQueueWithdrawal( - staker, - strategyIndexes, - strategyArray, - shareAmounts, - staker /*withdrawer*/ - ); + _testQueueWithdrawal(staker, strategyIndexes, strategyArray, shareAmounts, staker /*withdrawer*/); cheats.startPrank(staker); delegation.undelegate(staker); @@ -251,17 +207,21 @@ contract DelegationTests is EigenLayerTestHelper { require(delegation.delegatedTo(staker) == address(0), "undelegation unsuccessful"); } - /// @notice tests delegation from a staker to operator via ECDSA signature. - function testDelegateToBySignature(address operator, uint96 ethAmount, uint96 eigenAmount, uint256 expiry) - public - fuzzedAddress(operator) - { + /// @notice tests delegation from a staker to operator via ECDSA signature. + function testDelegateToBySignature( + address operator, + uint96 ethAmount, + uint96 eigenAmount, + uint256 expiry + ) public fuzzedAddress(operator) { address staker = cheats.addr(PRIVATE_KEY); - _registerOperatorAndDepositFromStaker(operator, staker, ethAmount, eigenAmount); + _registerOperatorAndDepositFromStaker(operator, staker, ethAmount, eigenAmount); uint256 nonceBefore = delegation.stakerNonce(staker); - bytes32 structHash = keccak256(abi.encode(delegation.STAKER_DELEGATION_TYPEHASH(), staker, operator, nonceBefore, expiry)); + bytes32 structHash = keccak256( + abi.encode(delegation.STAKER_DELEGATION_TYPEHASH(), staker, operator, nonceBefore, expiry) + ); bytes32 digestHash = keccak256(abi.encodePacked("\x19\x01", delegation.domainSeparator(), structHash)); bytes memory signature; @@ -269,7 +229,7 @@ contract DelegationTests is EigenLayerTestHelper { (uint8 v, bytes32 r, bytes32 s) = cheats.sign(PRIVATE_KEY, digestHash); signature = abi.encodePacked(r, s, v); } - + if (expiry < block.timestamp) { cheats.expectRevert("DelegationManager.delegateToBySignature: staker signature expired"); } @@ -281,15 +241,16 @@ contract DelegationTests is EigenLayerTestHelper { if (expiry >= block.timestamp) { assertTrue(delegation.isDelegated(staker) == true, "testDelegation: staker is not delegate"); assertTrue(nonceBefore + 1 == delegation.stakerNonce(staker), "nonce not incremented correctly"); - assertTrue(delegation.delegatedTo(staker) == operator, "staker delegated to wrong operator"); + assertTrue(delegation.delegatedTo(staker) == operator, "staker delegated to wrong operator"); } } /// @notice tries delegating using a signature and an EIP 1271 compliant wallet - function testDelegateToBySignature_WithContractWallet_Successfully(address operator, uint96 ethAmount, uint96 eigenAmount) - public - fuzzedAddress(operator) - { + function testDelegateToBySignature_WithContractWallet_Successfully( + address operator, + uint96 ethAmount, + uint96 eigenAmount + ) public fuzzedAddress(operator) { address staker = cheats.addr(PRIVATE_KEY); // deploy ERC1271WalletMock for staker to use @@ -298,11 +259,13 @@ contract DelegationTests is EigenLayerTestHelper { cheats.stopPrank(); staker = address(wallet); - _registerOperatorAndDepositFromStaker(operator, staker, ethAmount, eigenAmount); - + _registerOperatorAndDepositFromStaker(operator, staker, ethAmount, eigenAmount); + uint256 nonceBefore = delegation.stakerNonce(staker); - bytes32 structHash = keccak256(abi.encode(delegation.STAKER_DELEGATION_TYPEHASH(), staker, operator, nonceBefore, type(uint256).max)); + bytes32 structHash = keccak256( + abi.encode(delegation.STAKER_DELEGATION_TYPEHASH(), staker, operator, nonceBefore, type(uint256).max) + ); bytes32 digestHash = keccak256(abi.encodePacked("\x19\x01", delegation.domainSeparator(), structHash)); bytes memory signature; @@ -310,7 +273,7 @@ contract DelegationTests is EigenLayerTestHelper { (uint8 v, bytes32 r, bytes32 s) = cheats.sign(PRIVATE_KEY, digestHash); signature = abi.encodePacked(r, s, v); } - + ISignatureUtils.SignatureWithExpiry memory signatureWithExpiry = ISignatureUtils.SignatureWithExpiry({ signature: signature, expiry: type(uint256).max @@ -322,10 +285,11 @@ contract DelegationTests is EigenLayerTestHelper { } /// @notice tries delegating using a signature and an EIP 1271 compliant wallet, *but* providing a bad signature - function testDelegateToBySignature_WithContractWallet_BadSignature(address operator, uint96 ethAmount, uint96 eigenAmount) - public - fuzzedAddress(operator) - { + function testDelegateToBySignature_WithContractWallet_BadSignature( + address operator, + uint96 ethAmount, + uint96 eigenAmount + ) public fuzzedAddress(operator) { address staker = cheats.addr(PRIVATE_KEY); // deploy ERC1271WalletMock for staker to use @@ -334,11 +298,13 @@ contract DelegationTests is EigenLayerTestHelper { cheats.stopPrank(); staker = address(wallet); - _registerOperatorAndDepositFromStaker(operator, staker, ethAmount, eigenAmount); - + _registerOperatorAndDepositFromStaker(operator, staker, ethAmount, eigenAmount); + uint256 nonceBefore = delegation.stakerNonce(staker); - bytes32 structHash = keccak256(abi.encode(delegation.STAKER_DELEGATION_TYPEHASH(), staker, operator, nonceBefore, type(uint256).max)); + bytes32 structHash = keccak256( + abi.encode(delegation.STAKER_DELEGATION_TYPEHASH(), staker, operator, nonceBefore, type(uint256).max) + ); bytes32 digestHash = keccak256(abi.encodePacked("\x19\x01", delegation.domainSeparator(), structHash)); bytes memory signature; @@ -348,8 +314,10 @@ contract DelegationTests is EigenLayerTestHelper { v = (v == 27 ? 28 : 27); signature = abi.encodePacked(r, s, v); } - - cheats.expectRevert(bytes("EIP1271SignatureUtils.checkSignature_EIP1271: ERC1271 signature verification failed")); + + cheats.expectRevert( + bytes("EIP1271SignatureUtils.checkSignature_EIP1271: ERC1271 signature verification failed") + ); ISignatureUtils.SignatureWithExpiry memory signatureWithExpiry = ISignatureUtils.SignatureWithExpiry({ signature: signature, expiry: type(uint256).max @@ -358,10 +326,14 @@ contract DelegationTests is EigenLayerTestHelper { } /// @notice tries delegating using a wallet that does not comply with EIP 1271 - function testDelegateToBySignature_WithContractWallet_NonconformingWallet(address operator, uint96 ethAmount, uint96 eigenAmount, uint8 v, bytes32 r, bytes32 s) - public - fuzzedAddress(operator) - { + function testDelegateToBySignature_WithContractWallet_NonconformingWallet( + address operator, + uint96 ethAmount, + uint96 eigenAmount, + uint8 v, + bytes32 r, + bytes32 s + ) public fuzzedAddress(operator) { address staker = cheats.addr(PRIVATE_KEY); // deploy non ERC1271-compliant wallet for staker to use @@ -370,7 +342,7 @@ contract DelegationTests is EigenLayerTestHelper { cheats.stopPrank(); staker = address(wallet); - _registerOperatorAndDepositFromStaker(operator, staker, ethAmount, eigenAmount); + _registerOperatorAndDepositFromStaker(operator, staker, ethAmount, eigenAmount); cheats.assume(staker != operator); @@ -387,22 +359,18 @@ contract DelegationTests is EigenLayerTestHelper { /// @notice tests delegation to EigenLayer via an ECDSA signatures with invalid signature /// @param operator is the operator being delegated to. function testDelegateToByInvalidSignature( - address operator, - uint96 ethAmount, - uint96 eigenAmount, + address operator, + uint96 ethAmount, + uint96 eigenAmount, uint8 v, bytes32 r, bytes32 s - ) - public - fuzzedAddress(operator) - fuzzedAmounts(ethAmount, eigenAmount) - { + ) public fuzzedAddress(operator) fuzzedAmounts(ethAmount, eigenAmount) { address staker = cheats.addr(PRIVATE_KEY); - _registerOperatorAndDepositFromStaker(operator, staker, ethAmount, eigenAmount); + _registerOperatorAndDepositFromStaker(operator, staker, ethAmount, eigenAmount); bytes memory signature = abi.encodePacked(r, s, v); - + cheats.expectRevert(); ISignatureUtils.SignatureWithExpiry memory signatureWithExpiry = ISignatureUtils.SignatureWithExpiry({ signature: signature, @@ -411,54 +379,6 @@ contract DelegationTests is EigenLayerTestHelper { delegation.delegateToBySignature(staker, operator, signatureWithExpiry, signatureWithExpiry, bytes32(0)); } - /// @notice registers a fixed address as a delegate, delegates to it from a second address, - /// and checks that the delegate's voteWeights increase properly - /// @param operator is the operator being delegated to. - /// @param staker is the staker delegating stake to the operator. - function testDelegationMultipleStrategies(uint8 numStratsToAdd, address operator, address staker) - public - fuzzedAddress(operator) - fuzzedAddress(staker) - { - cheats.assume(staker != operator); - - cheats.assume(numStratsToAdd > 0 && numStratsToAdd <= 20); - uint256 amountToDeposit = 1e18; - uint96 operatorEthWeightBefore = stakeRegistry.weightOfOperatorForQuorum(0, operator); - uint96 operatorEigenWeightBefore = stakeRegistry.weightOfOperatorForQuorum(1, operator); - IDelegationManager.OperatorDetails memory operatorDetails = IDelegationManager.OperatorDetails({ - earningsReceiver: operator, - delegationApprover: address(0), - stakerOptOutWindowBlocks: 0 - }); - _testRegisterAsOperator(operator, operatorDetails); - _testDepositStrategies(staker, amountToDeposit, numStratsToAdd); - - // add strategies to voteWeigher - uint96 multiplier = 1e18; - for (uint16 i = 0; i < numStratsToAdd; ++i) { - IVoteWeigher.StrategyAndWeightingMultiplier[] memory ethStratsAndMultipliers = - new IVoteWeigher.StrategyAndWeightingMultiplier[](1); - ethStratsAndMultipliers[0].strategy = strategies[i]; - ethStratsAndMultipliers[0].multiplier = multiplier; - cheats.startPrank(stakeRegistry.serviceManager().owner()); - stakeRegistry.addStrategiesConsideredAndMultipliers(0, ethStratsAndMultipliers); - cheats.stopPrank(); - } - _testDepositEigen(staker, amountToDeposit); - _testDelegateToOperator(staker, operator); - stakeRegistry.setOperatorWeight(0, operator, uint96(amountToDeposit)); - stakeRegistry.setOperatorWeight(1, operator, uint96(amountToDeposit)); - uint96 operatorEthWeightAfter = stakeRegistry.weightOfOperatorForQuorum(0, operator); - uint96 operatorEigenWeightAfter = stakeRegistry.weightOfOperatorForQuorum(1, operator); - assertTrue( - operatorEthWeightAfter > operatorEthWeightBefore, "testDelegation: operatorEthWeight did not increase!" - ); - assertTrue( - operatorEigenWeightAfter > operatorEigenWeightBefore, "testDelegation: operatorEthWeight did not increase!" - ); -} - /// @notice This function tests to ensure that a delegation contract /// cannot be intitialized multiple times function testCannotInitMultipleTimesDelegation() public cannotReinit { @@ -493,7 +413,6 @@ contract DelegationTests is EigenLayerTestHelper { cheats.stopPrank(); } - /// @notice This function tests to ensure that a delegation contract /// cannot be intitialized multiple times, test with different caller addresses function testCannotInitMultipleTimesDelegation(address _attacker) public { @@ -505,8 +424,10 @@ contract DelegationTests is EigenLayerTestHelper { } /// @notice This function tests that the earningsReceiver cannot be set to address(0) - function testCannotSetEarningsReceiverToZeroAddress() public{ - cheats.expectRevert(bytes("DelegationManager._setOperatorDetails: cannot set `earningsReceiver` to zero address")); + function testCannotSetEarningsReceiverToZeroAddress() public { + cheats.expectRevert( + bytes("DelegationManager._setOperatorDetails: cannot set `earningsReceiver` to zero address") + ); IDelegationManager.OperatorDetails memory operatorDetails = IDelegationManager.OperatorDetails({ earningsReceiver: address(0), delegationApprover: address(0), @@ -517,7 +438,10 @@ contract DelegationTests is EigenLayerTestHelper { } /// @notice This function tests to ensure that an address can only call registerAsOperator() once - function testCannotRegisterAsOperatorTwice(address _operator, address _dt) public fuzzedAddress(_operator) fuzzedAddress(_dt) { + function testCannotRegisterAsOperatorTwice( + address _operator, + address _dt + ) public fuzzedAddress(_operator) fuzzedAddress(_dt) { vm.assume(_dt != address(0)); vm.startPrank(_operator); IDelegationManager.OperatorDetails memory operatorDetails = IDelegationManager.OperatorDetails({ @@ -533,7 +457,10 @@ contract DelegationTests is EigenLayerTestHelper { } /// @notice this function checks that you can only delegate to an address that is already registered. - function testDelegateToInvalidOperator(address _staker, address _unregisteredoperator) public fuzzedAddress(_staker) { + function testDelegateToInvalidOperator( + address _staker, + address _unregisteredoperator + ) public fuzzedAddress(_staker) { vm.startPrank(_staker); cheats.expectRevert(bytes("DelegationManager._delegate: operator is not registered in EigenLayer")); ISignatureUtils.SignatureWithExpiry memory signatureWithExpiry; @@ -543,8 +470,7 @@ contract DelegationTests is EigenLayerTestHelper { cheats.stopPrank(); } - function testUndelegate_SigP_Version(address _operator,address _staker,address _dt) public { - + function testUndelegate_SigP_Version(address _operator, address _staker, address _dt) public { vm.assume(_operator != address(0)); vm.assume(_staker != address(0)); vm.assume(_operator != _staker); @@ -555,7 +481,7 @@ contract DelegationTests is EigenLayerTestHelper { // setup delegation vm.prank(_operator); IDelegationManager.OperatorDetails memory operatorDetails = IDelegationManager.OperatorDetails({ - earningsReceiver:_dt, + earningsReceiver: _dt, delegationApprover: address(0), stakerOptOutWindowBlocks: 0 }); @@ -605,9 +531,13 @@ contract DelegationTests is EigenLayerTestHelper { cheats.stopPrank(); } - // registers the operator if they are not already registered, and deposits "WETH" + "EIGEN" on behalf of the staker. - function _registerOperatorAndDepositFromStaker(address operator, address staker, uint96 ethAmount, uint96 eigenAmount) internal { + function _registerOperatorAndDepositFromStaker( + address operator, + address staker, + uint96 ethAmount, + uint96 eigenAmount + ) internal { cheats.assume(staker != operator); // if first deposit amount to base strategy is too small, it will revert. ignore that case here. diff --git a/src/test/EigenLayerDeployer.t.sol b/src/test/EigenLayerDeployer.t.sol index 64b71475a..6f78cbafa 100644 --- a/src/test/EigenLayerDeployer.t.sol +++ b/src/test/EigenLayerDeployer.t.sol @@ -9,7 +9,6 @@ import "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol"; import "../contracts/interfaces/IDelegationManager.sol"; import "../contracts/core/DelegationManager.sol"; -import "../contracts/middleware/StakeRegistry.sol"; import "../contracts/interfaces/IETHPOSDeposit.sol"; import "../contracts/interfaces/IBeaconChainOracle.sol"; @@ -25,7 +24,6 @@ import "../contracts/pods/DelayedWithdrawalRouter.sol"; import "../contracts/permissions/PauserRegistry.sol"; - import "./utils/Operators.sol"; import "./mocks/LiquidStakingToken.sol"; @@ -36,7 +34,6 @@ import "./mocks/BeaconChainOracleMock.sol"; import "forge-std/Test.sol"; contract EigenLayerDeployer is Operators { - Vm cheats = Vm(HEVM_ADDRESS); // EigenLayer contracts @@ -107,15 +104,12 @@ contract EigenLayerDeployer is Operators { address operationsMultisig; address executorMultisig; - uint256 public initialBeaconChainOracleThreshold = 3; string internal goerliDeploymentConfig = vm.readFile("script/output/M1_deployment_goerli_2023_3_23.json"); - // addresses excluded from fuzzing due to abnormal behavior. TODO: @Sidu28 define this better and give it a clearer name - mapping (address => bool) fuzzedAddressMapping; - + mapping(address => bool) fuzzedAddressMapping; //ensures that a passed in address is not set to true in the fuzzedAddressMapping modifier fuzzedAddress(address addr) virtual { @@ -137,7 +131,7 @@ contract EigenLayerDeployer is Operators { } else if (chainId == 5) { _deployEigenLayerContractsGoerli(); } - // If CHAIN_ID ENV is not set, assume local deployment on 31337 + // If CHAIN_ID ENV is not set, assume local deployment on 31337 } catch { _deployEigenLayerContractsLocal(); } @@ -158,7 +152,7 @@ contract EigenLayerDeployer is Operators { eigenLayerProxyAdmin = ProxyAdmin(eigenLayerProxyAdminAddress); emptyContract = new EmptyContract(); - + //deploy pauser registry eigenLayerPauserReg = PauserRegistry(eigenLayerPauserRegAddress); @@ -171,19 +165,19 @@ contract EigenLayerDeployer is Operators { beaconChainOracleAddress = address(new BeaconChainOracleMock()); ethPOSDeposit = new ETHPOSDepositMock(); - pod = new EigenPod(ethPOSDeposit, delayedWithdrawalRouter, eigenPodManager, MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR, EFFECTIVE_RESTAKED_BALANCE_OFFSET, GOERLI_GENESIS_TIME); + pod = new EigenPod( + ethPOSDeposit, + delayedWithdrawalRouter, + eigenPodManager, + MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR, + EFFECTIVE_RESTAKED_BALANCE_OFFSET, + GOERLI_GENESIS_TIME + ); eigenPodBeacon = new UpgradeableBeacon(address(pod)); - - //simple ERC20 (**NOT** WETH-like!), used in a test strategy - weth = new ERC20PresetFixedSupply( - "weth", - "WETH", - wethInitialSupply, - address(this) - ); + weth = new ERC20PresetFixedSupply("weth", "WETH", wethInitialSupply, address(this)); // deploy StrategyBase contract implementation, then create upgradeable proxy that points to implementation and initialize it baseStrategyImplementation = new StrategyBase(strategyManager); @@ -197,12 +191,7 @@ contract EigenLayerDeployer is Operators { ) ); - eigenToken = new ERC20PresetFixedSupply( - "eigen", - "EIGEN", - wethInitialSupply, - address(this) - ); + eigenToken = new ERC20PresetFixedSupply("eigen", "EIGEN", wethInitialSupply, address(this)); // deploy upgradeable proxy that points to StrategyBase implementation and initialize it eigenStrat = StrategyBase( @@ -251,7 +240,14 @@ contract EigenLayerDeployer is Operators { ); ethPOSDeposit = new ETHPOSDepositMock(); - pod = new EigenPod(ethPOSDeposit, delayedWithdrawalRouter, eigenPodManager, MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR, EFFECTIVE_RESTAKED_BALANCE_OFFSET, GOERLI_GENESIS_TIME); + pod = new EigenPod( + ethPOSDeposit, + delayedWithdrawalRouter, + eigenPodManager, + MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR, + EFFECTIVE_RESTAKED_BALANCE_OFFSET, + GOERLI_GENESIS_TIME + ); eigenPodBeacon = new UpgradeableBeacon(address(pod)); @@ -259,7 +255,13 @@ contract EigenLayerDeployer is Operators { DelegationManager delegationImplementation = new DelegationManager(strategyManager, slasher, eigenPodManager); StrategyManager strategyManagerImplementation = new StrategyManager(delegation, eigenPodManager, slasher); Slasher slasherImplementation = new Slasher(strategyManager, delegation); - EigenPodManager eigenPodManagerImplementation = new EigenPodManager(ethPOSDeposit, eigenPodBeacon, strategyManager, slasher, delegation); + EigenPodManager eigenPodManagerImplementation = new EigenPodManager( + ethPOSDeposit, + eigenPodBeacon, + strategyManager, + slasher, + delegation + ); DelayedWithdrawalRouter delayedWithdrawalRouterImplementation = new DelayedWithdrawalRouter(eigenPodManager); // Third, upgrade the proxy contracts to use the correct implementation contracts and initialize them. @@ -270,7 +272,7 @@ contract EigenLayerDeployer is Operators { DelegationManager.initialize.selector, eigenLayerReputedMultisig, eigenLayerPauserReg, - 0/*initialPausedStatus*/ + 0 /*initialPausedStatus*/ ) ); eigenLayerProxyAdmin.upgradeAndCall( @@ -281,7 +283,7 @@ contract EigenLayerDeployer is Operators { eigenLayerReputedMultisig, eigenLayerReputedMultisig, eigenLayerPauserReg, - 0/*initialPausedStatus*/ + 0 /*initialPausedStatus*/ ) ); eigenLayerProxyAdmin.upgradeAndCall( @@ -291,7 +293,7 @@ contract EigenLayerDeployer is Operators { Slasher.initialize.selector, eigenLayerReputedMultisig, eigenLayerPauserReg, - 0/*initialPausedStatus*/ + 0 /*initialPausedStatus*/ ) ); eigenLayerProxyAdmin.upgradeAndCall( @@ -303,7 +305,7 @@ contract EigenLayerDeployer is Operators { beaconChainOracleAddress, eigenLayerReputedMultisig, eigenLayerPauserReg, - 0/*initialPausedStatus*/ + 0 /*initialPausedStatus*/ ) ); uint256 initPausedStatus = 0; @@ -311,20 +313,17 @@ contract EigenLayerDeployer is Operators { eigenLayerProxyAdmin.upgradeAndCall( TransparentUpgradeableProxy(payable(address(delayedWithdrawalRouter))), address(delayedWithdrawalRouterImplementation), - abi.encodeWithSelector(DelayedWithdrawalRouter.initialize.selector, - eigenLayerReputedMultisig, - eigenLayerPauserReg, - initPausedStatus, - withdrawalDelayBlocks) + abi.encodeWithSelector( + DelayedWithdrawalRouter.initialize.selector, + eigenLayerReputedMultisig, + eigenLayerPauserReg, + initPausedStatus, + withdrawalDelayBlocks + ) ); //simple ERC20 (**NOT** WETH-like!), used in a test strategy - weth = new ERC20PresetFixedSupply( - "weth", - "WETH", - wethInitialSupply, - address(this) - ); + weth = new ERC20PresetFixedSupply("weth", "WETH", wethInitialSupply, address(this)); // deploy StrategyBase contract implementation, then create upgradeable proxy that points to implementation and initialize it baseStrategyImplementation = new StrategyBase(strategyManager); @@ -338,12 +337,7 @@ contract EigenLayerDeployer is Operators { ) ); - eigenToken = new ERC20PresetFixedSupply( - "eigen", - "EIGEN", - wethInitialSupply, - address(this) - ); + eigenToken = new ERC20PresetFixedSupply("eigen", "EIGEN", wethInitialSupply, address(this)); // deploy upgradeable proxy that points to StrategyBase implementation and initialize it eigenStrat = StrategyBase( @@ -360,16 +354,15 @@ contract EigenLayerDeployer is Operators { } function _setAddresses(string memory config) internal { - eigenLayerProxyAdminAddress = stdJson.readAddress(config, ".addresses.eigenLayerProxyAdmin"); + eigenLayerProxyAdminAddress = stdJson.readAddress(config, ".addresses.eigenLayerProxyAdmin"); eigenLayerPauserRegAddress = stdJson.readAddress(config, ".addresses.eigenLayerPauserReg"); delegationAddress = stdJson.readAddress(config, ".addresses.delegation"); strategyManagerAddress = stdJson.readAddress(config, ".addresses.strategyManager"); slasherAddress = stdJson.readAddress(config, ".addresses.slasher"); - eigenPodManagerAddress = stdJson.readAddress(config, ".addresses.eigenPodManager"); + eigenPodManagerAddress = stdJson.readAddress(config, ".addresses.eigenPodManager"); delayedWithdrawalRouterAddress = stdJson.readAddress(config, ".addresses.delayedWithdrawalRouter"); emptyContractAddress = stdJson.readAddress(config, ".addresses.emptyContract"); operationsMultisig = stdJson.readAddress(config, ".parameters.operationsMultisig"); executorMultisig = stdJson.readAddress(config, ".parameters.executorMultisig"); } - } diff --git a/src/test/EigenLayerTestHelper.t.sol b/src/test/EigenLayerTestHelper.t.sol index dde3d7b9f..1d65ff42b 100644 --- a/src/test/EigenLayerTestHelper.t.sol +++ b/src/test/EigenLayerTestHelper.t.sol @@ -4,8 +4,9 @@ pragma solidity =0.8.12; import "../test/EigenLayerDeployer.t.sol"; import "../contracts/interfaces/ISignatureUtils.sol"; -contract EigenLayerTestHelper is EigenLayerDeployer { +import "./mocks/StakeRegistryStub.sol"; +contract EigenLayerTestHelper is EigenLayerDeployer { uint8 durationToInit = 2; uint256 public SECP256K1N_MODULUS = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141; uint256 public SECP256K1N_MODULUS_HALF = 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0; @@ -25,14 +26,11 @@ contract EigenLayerTestHelper is EigenLayerDeployer { function _testInitiateDelegation( uint8 operatorIndex, - uint256 amountEigenToDeposit, - uint256 amountEthToDeposit - ) - public returns (uint256 amountEthStaked, uint256 amountEigenStaked) - { - + uint256 amountEigenToDeposit, + uint256 amountEthToDeposit + ) public returns (uint256 amountEthStaked, uint256 amountEigenStaked) { address operator = getOperatorAddress(operatorIndex); - + //setting up operator's delegation terms IDelegationManager.OperatorDetails memory operatorDetails = IDelegationManager.OperatorDetails({ earningsReceiver: operator, @@ -59,7 +57,6 @@ contract EigenLayerTestHelper is EigenLayerDeployer { delegation.operatorShares(operator, eigenStrat) - operatorEigenSharesBefore == amountEigenToDeposit ); assertTrue(delegation.operatorShares(operator, wethStrat) - operatorWETHSharesBefore == amountEthToDeposit); - } amountEthStaked += delegation.operatorShares(operator, wethStrat); amountEigenStaked += delegation.operatorShares(operator, eigenStrat); @@ -68,13 +65,16 @@ contract EigenLayerTestHelper is EigenLayerDeployer { } /** - * @notice Register 'sender' as an operator, setting their 'OperatorDetails' in DelegationManager to 'operatorDetails', verifies + * @notice Register 'sender' as an operator, setting their 'OperatorDetails' in DelegationManager to 'operatorDetails', verifies * that the storage of DelegationManager contract is updated appropriately - * + * * @param sender is the address being registered as an operator * @param operatorDetails is the `sender`'s OperatorDetails struct */ - function _testRegisterAsOperator(address sender, IDelegationManager.OperatorDetails memory operatorDetails) internal { + function _testRegisterAsOperator( + address sender, + IDelegationManager.OperatorDetails memory operatorDetails + ) internal { cheats.startPrank(sender); string memory emptyStringForMetadataURI; delegation.registerAsOperator(operatorDetails, emptyStringForMetadataURI); @@ -121,11 +121,7 @@ contract EigenLayerTestHelper is EigenLayerDeployer { uint256 amountToDeposit, IERC20 underlyingToken, IStrategy stratToDepositTo - ) - internal - returns (uint256 amountDeposited) - { - + ) internal returns (uint256 amountDeposited) { // deposits will revert when amountToDeposit is 0 cheats.assume(amountToDeposit > 0); @@ -160,12 +156,12 @@ contract EigenLayerTestHelper is EigenLayerDeployer { if (operatorSharesBefore == 0) { // check that strategy is appropriately added to dynamic array of all of sender's strategies assertTrue( - strategyManager.stakerStrategyList(sender, strategyManager.stakerStrategyListLength(sender) - 1) - == stratToDepositTo, + strategyManager.stakerStrategyList(sender, strategyManager.stakerStrategyListLength(sender) - 1) == + stratToDepositTo, "_testDepositToStrategy: stakerStrategyList array updated incorrectly" ); } - + // check that the shares out match the expected amount out assertEq( strategyManager.stakerStrategyShares(sender, stratToDepositTo) - operatorSharesBefore, @@ -177,15 +173,14 @@ contract EigenLayerTestHelper is EigenLayerDeployer { } /** - * @notice tries to delegate from 'staker' to 'operator', verifies that staker has at least some shares + * @notice tries to delegate from 'staker' to 'operator', verifies that staker has at least some shares * delegatedShares update correctly for 'operator' and delegated status is updated correctly for 'staker' * @param staker the staker address to delegate from * @param operator the operator address to delegate to */ function _testDelegateToOperator(address staker, address operator) internal { //staker-specific information - (IStrategy[] memory delegateStrategies, uint256[] memory delegateShares) = - strategyManager.getDeposits(staker); + (IStrategy[] memory delegateStrategies, uint256[] memory delegateShares) = strategyManager.getDeposits(staker); uint256 numStrats = delegateShares.length; assertTrue(numStrats != 0, "_testDelegateToOperator: delegating from address with no deposits"); @@ -203,10 +198,7 @@ contract EigenLayerTestHelper is EigenLayerDeployer { delegation.delegatedTo(staker) == operator, "_testDelegateToOperator: delegated address not set appropriately" ); - assertTrue( - delegation.isDelegated(staker), - "_testDelegateToOperator: delegated status not set appropriately" - ); + assertTrue(delegation.isDelegated(staker), "_testDelegateToOperator: delegated status not set appropriately"); for (uint256 i = 0; i < numStrats; ++i) { uint256 operatorSharesBefore = inititalSharesInStrats[i]; @@ -219,9 +211,9 @@ contract EigenLayerTestHelper is EigenLayerDeployer { } /** - * @notice deploys 'numStratsToAdd' strategies contracts and initializes them to treat `underlyingToken` as their underlying token + * @notice deploys 'numStratsToAdd' strategies contracts and initializes them to treat `underlyingToken` as their underlying token * and then deposits 'amountToDeposit' to each of them from 'sender' - * + * * @param sender address that is depositing into the strategies * @param amountToDeposit amount being deposited * @param numStratsToAdd number of strategies that are being deployed and deposited into @@ -231,16 +223,14 @@ contract EigenLayerTestHelper is EigenLayerDeployer { IERC20 underlyingToken = weth; cheats.assume(numStratsToAdd > 0 && numStratsToAdd <= 20); - IStrategy[] memory stratsToDepositTo = new IStrategy[]( - numStratsToAdd - ); + IStrategy[] memory stratsToDepositTo = new IStrategy[](numStratsToAdd); for (uint8 i = 0; i < numStratsToAdd; ++i) { stratsToDepositTo[i] = StrategyBase( address( new TransparentUpgradeableProxy( address(baseStrategyImplementation), address(eigenLayerProxyAdmin), - abi.encodeWithSelector(StrategyBase.initialize.selector, underlyingToken, eigenLayerPauserReg) + abi.encodeWithSelector(StrategyBase.initialize.selector, underlyingToken, eigenLayerPauserReg) ) ) ); @@ -259,7 +249,6 @@ contract EigenLayerTestHelper is EigenLayerDeployer { } } - /** * @notice Creates a queued withdrawal from `staker`. Begins by registering the staker as a delegate (if specified), then deposits `amountToDeposit` * into the WETH strategy, and then queues a withdrawal using @@ -278,9 +267,7 @@ contract EigenLayerTestHelper is EigenLayerDeployer { uint256[] memory shareAmounts, uint256[] memory strategyIndexes, address withdrawer - ) - internal returns(bytes32 withdrawalRoot, IDelegationManager.Withdrawal memory queuedWithdrawal) - { + ) internal returns (bytes32 withdrawalRoot, IDelegationManager.Withdrawal memory queuedWithdrawal) { require(amountToDeposit >= shareAmounts[0], "_createQueuedWithdrawal: sanity check failed"); // we do this here to ensure that `staker` is delegated if `registerAsOperator` is true @@ -293,7 +280,8 @@ contract EigenLayerTestHelper is EigenLayerDeployer { }); _testRegisterAsOperator(staker, operatorDetails); assertTrue( - delegation.isDelegated(staker), "_createQueuedWithdrawal: staker isn't delegated when they should be" + delegation.isDelegated(staker), + "_createQueuedWithdrawal: staker isn't delegated when they should be" ); } @@ -321,18 +309,18 @@ contract EigenLayerTestHelper is EigenLayerDeployer { return (withdrawalRoot, queuedWithdrawal); } - /** - * Helper for ECDSA signatures: combines V and S into VS - if S is greater than SECP256K1N_MODULUS_HALF, then we - * get the modulus, so that the leading bit of s is always 0. Then we set the leading - * bit to be either 0 or 1 based on the value of v, which is either 27 or 28 - */ - function getVSfromVandS(uint8 v, bytes32 s) internal view returns(bytes32) { + /** + * Helper for ECDSA signatures: combines V and S into VS - if S is greater than SECP256K1N_MODULUS_HALF, then we + * get the modulus, so that the leading bit of s is always 0. Then we set the leading + * bit to be either 0 or 1 based on the value of v, which is either 27 or 28 + */ + function getVSfromVandS(uint8 v, bytes32 s) internal view returns (bytes32) { if (uint256(s) > SECP256K1N_MODULUS_HALF) { s = bytes32(SECP256K1N_MODULUS - uint256(s)); } bytes32 vs = s; - if(v == 28) { + if (v == 28) { vs = bytes32(uint256(s) ^ (1 << 255)); } @@ -351,7 +339,7 @@ contract EigenLayerTestHelper is EigenLayerDeployer { address staker, uint256 ethAmount, uint256 eigenAmount, - StakeRegistry stakeRegistry + StakeRegistryStub stakeRegistry ) internal { if (!delegation.isOperator(operator)) { IDelegationManager.OperatorDetails memory operatorDetails = IDelegationManager.OperatorDetails({ @@ -362,10 +350,7 @@ contract EigenLayerTestHelper is EigenLayerDeployer { _testRegisterAsOperator(operator, operatorDetails); } - uint256[3] memory amountsBefore; - amountsBefore[0] = stakeRegistry.weightOfOperatorForQuorumView(0, operator); - amountsBefore[1] = stakeRegistry.weightOfOperatorForQuorumView(1, operator); - amountsBefore[2] = delegation.operatorShares(operator, wethStrat); + uint256 amountBefore = delegation.operatorShares(operator, wethStrat); //making additional deposits to the strategies assertTrue(!delegation.isDelegated(staker) == true, "testDelegation: staker is not delegate"); @@ -374,35 +359,19 @@ contract EigenLayerTestHelper is EigenLayerDeployer { _testDelegateToOperator(staker, operator); assertTrue(delegation.isDelegated(staker) == true, "testDelegation: staker is not delegate"); - (IStrategy[] memory updatedStrategies, uint256[] memory updatedShares) = - strategyManager.getDeposits(staker); + (IStrategy[] memory updatedStrategies, uint256[] memory updatedShares) = strategyManager.getDeposits(staker); - { - uint256 stakerEthWeight = strategyManager.stakerStrategyShares(staker, updatedStrategies[0]); - uint256 stakerEigenWeight = strategyManager.stakerStrategyShares(staker, updatedStrategies[1]); + uint256 stakerEthWeight = strategyManager.stakerStrategyShares(staker, updatedStrategies[0]); + uint256 stakerEigenWeight = strategyManager.stakerStrategyShares(staker, updatedStrategies[1]); - uint256 operatorEthWeightAfter = stakeRegistry.weightOfOperatorForQuorumView(0, operator); - uint256 operatorEigenWeightAfter = stakeRegistry.weightOfOperatorForQuorumView(1, operator); + IStrategy _strat = wethStrat; + // IStrategy _strat = strategyManager.stakerStrategyList(staker, 0); + assertTrue(address(_strat) != address(0), "stakerStrategyList not updated correctly"); - assertTrue( - operatorEthWeightAfter - amountsBefore[0] == stakerEthWeight, - "testDelegation: operatorEthWeight did not increment by the right amount" - ); - assertTrue( - operatorEigenWeightAfter - amountsBefore[1] == stakerEigenWeight, - "Eigen weights did not increment by the right amount" - ); - } - { - IStrategy _strat = wethStrat; - // IStrategy _strat = strategyManager.stakerStrategyList(staker, 0); - assertTrue(address(_strat) != address(0), "stakerStrategyList not updated correctly"); - - assertTrue( - delegation.operatorShares(operator, _strat) - updatedShares[0] == amountsBefore[2], - "ETH operatorShares not updated correctly" - ); - } + assertTrue( + delegation.operatorShares(operator, _strat) - updatedShares[0] == amountBefore, + "ETH operatorShares not updated correctly" + ); } /** @@ -426,14 +395,11 @@ contract EigenLayerTestHelper is EigenLayerDeployer { uint256 nonce, uint32 withdrawalStartBlock, uint256 middlewareTimesIndex - ) - internal - { + ) internal { cheats.startPrank(withdrawer); for (uint256 i = 0; i < strategyArray.length; i++) { sharesBefore.push(strategyManager.stakerStrategyShares(withdrawer, strategyArray[i])); - } // emit log_named_uint("strategies", strategyArray.length); // emit log_named_uint("tokens", tokensArray.length); @@ -458,8 +424,7 @@ contract EigenLayerTestHelper is EigenLayerDeployer { for (uint256 i = 0; i < strategyArray.length; i++) { require( - strategyManager.stakerStrategyShares(withdrawer, strategyArray[i]) - == sharesBefore[i] + shareAmounts[i], + strategyManager.stakerStrategyShares(withdrawer, strategyArray[i]) == sharesBefore[i] + shareAmounts[i], "_testCompleteQueuedWithdrawalShares: withdrawer shares not incremented" ); } @@ -486,9 +451,7 @@ contract EigenLayerTestHelper is EigenLayerDeployer { uint256 nonce, uint32 withdrawalStartBlock, uint256 middlewareTimesIndex - ) - internal - { + ) internal { cheats.startPrank(withdrawer); for (uint256 i = 0; i < strategyArray.length; i++) { @@ -496,7 +459,7 @@ contract EigenLayerTestHelper is EigenLayerDeployer { priorTotalShares.push(strategyArray[i].totalShares()); strategyTokenBalance.push(strategyArray[i].underlyingToken().balanceOf(address(strategyArray[i]))); } - + IDelegationManager.Withdrawal memory queuedWithdrawal = IDelegationManager.Withdrawal({ strategies: strategyArray, shares: shareAmounts, @@ -511,11 +474,10 @@ contract EigenLayerTestHelper is EigenLayerDeployer { for (uint256 i = 0; i < strategyArray.length; i++) { //uint256 strategyTokenBalance = strategyArray[i].underlyingToken().balanceOf(address(strategyArray[i])); - uint256 tokenBalanceDelta = strategyTokenBalance[i] * shareAmounts[i] / priorTotalShares[i]; + uint256 tokenBalanceDelta = (strategyTokenBalance[i] * shareAmounts[i]) / priorTotalShares[i]; require( - strategyArray[i].underlyingToken().balanceOf(withdrawer) - == balanceBefore[i] + tokenBalanceDelta, + strategyArray[i].underlyingToken().balanceOf(withdrawer) == balanceBefore[i] + tokenBalanceDelta, "_testCompleteQueuedWithdrawalTokens: withdrawer balance not incremented" ); } @@ -528,14 +490,11 @@ contract EigenLayerTestHelper is EigenLayerDeployer { IStrategy[] memory strategyArray, uint256[] memory shareAmounts, address withdrawer - ) - internal - returns (bytes32) - { + ) internal returns (bytes32) { cheats.startPrank(depositor); IDelegationManager.QueuedWithdrawalParams[] memory params = new IDelegationManager.QueuedWithdrawalParams[](1); - + params[0] = IDelegationManager.QueuedWithdrawalParams({ strategies: strategyArray, shares: shareAmounts, @@ -547,4 +506,4 @@ contract EigenLayerTestHelper is EigenLayerDeployer { cheats.stopPrank(); return withdrawalRoots[0]; } -} \ No newline at end of file +} diff --git a/src/test/EigenPod.t.sol b/src/test/EigenPod.t.sol index 4fd4a38eb..9c73e0902 100644 --- a/src/test/EigenPod.t.sol +++ b/src/test/EigenPod.t.sol @@ -2,24 +2,21 @@ pragma solidity =0.8.12; import "../contracts/interfaces/IEigenPod.sol"; -import "../contracts/interfaces/IBLSPublicKeyCompendium.sol"; -import "../contracts/middleware/BLSPublicKeyCompendium.sol"; import "../contracts/pods/DelayedWithdrawalRouter.sol"; import "./utils/ProofParsing.sol"; import "./EigenLayerDeployer.t.sol"; import "./mocks/MiddlewareRegistryMock.sol"; -import "./mocks/ServiceManagerMock.sol"; import "../contracts/libraries/BeaconChainProofs.sol"; import "./mocks/BeaconChainOracleMock.sol"; import "./utils/EigenPodHarness.sol"; - contract EigenPodTests is ProofParsing, EigenPodPausingConstants { using BytesLib for bytes; uint256 internal constant GWEI_TO_WEI = 1e9; - bytes pubkey = hex"88347ed1c492eedc97fc8c506a35d44d81f27a0c7a1c661b35913cfd15256c0cccbd34a83341f505c7de2983292f2cab"; + bytes pubkey = + hex"88347ed1c492eedc97fc8c506a35d44d81f27a0c7a1c661b35913cfd15256c0cccbd34a83341f505c7de2983292f2cab"; uint40 validatorIndex0 = 0; uint40 validatorIndex1 = 1; //hash tree root of list of validators @@ -37,7 +34,6 @@ contract EigenPodTests is ProofParsing, EigenPodPausingConstants { PauserRegistry public pauserReg; ProxyAdmin public eigenLayerProxyAdmin; - IBLSPublicKeyCompendium public blsPkCompendium; IEigenPodManager public eigenPodManager; IEigenPod public podImplementation; IDelayedWithdrawalRouter public delayedWithdrawalRouter; @@ -46,15 +42,13 @@ contract EigenPodTests is ProofParsing, EigenPodPausingConstants { EPInternalFunctions public podInternalFunctionTester; BeaconChainOracleMock public beaconChainOracle; - MiddlewareRegistryMock public generalReg1; - ServiceManagerMock public generalServiceManager1; address[] public slashingContracts; address pauser = address(69); address unpauser = address(489); address podManagerAddress = 0x212224D2F2d262cd093eE13240ca4873fcCBbA3C; address podAddress = address(123); uint256 stakeAmount = 32e18; - mapping (address => bool) fuzzedAddressMapping; + mapping(address => bool) fuzzedAddressMapping; bytes signature; bytes32 depositDataRoot; @@ -62,14 +56,13 @@ contract EigenPodTests is ProofParsing, EigenPodPausingConstants { bytes32[] validatorFields; uint32 WITHDRAWAL_DELAY_BLOCKS = 7 days / 12 seconds; - uint64 MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR = 31e9; - uint64 RESTAKED_BALANCE_OFFSET_GWEI = 75e7; + uint64 MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR = 31e9; + uint64 RESTAKED_BALANCE_OFFSET_GWEI = 75e7; uint64 internal constant GOERLI_GENESIS_TIME = 1616508000; uint64 internal constant SECONDS_PER_SLOT = 12; // bytes validatorPubkey = hex"93a0dd04ccddf3f1b419fdebf99481a2182c17d67cf14d32d6e50fc4bf8effc8db4a04b7c2f3a5975c1b9b74e2841888"; - // EIGENPODMANAGER EVENTS /// @notice Emitted to notify the update of the beaconChainOracle address event BeaconOracleUpdated(address indexed newOracleAddress); @@ -83,7 +76,6 @@ contract EigenPodTests is ProofParsing, EigenPodPausingConstants { /// @notice Emitted when `maxPods` value is updated from `previousValue` to `newValue` event MaxPodsUpdated(uint256 previousValue, uint256 newValue); - // EIGENPOD EVENTS /// @notice Emitted when an ETH validator stakes via this eigenPod event EigenPodStaked(bytes pubkey); @@ -94,12 +86,21 @@ contract EigenPodTests is ProofParsing, EigenPodPausingConstants { /// @notice Emitted when an ETH validator's balance is updated in EigenLayer event ValidatorBalanceUpdated(uint40 validatorIndex, uint64 balanceTimestamp, uint64 newBalanceGwei); - /// @notice Emitted when an ETH validator is prove to have withdrawn from the beacon chain - event FullWithdrawalRedeemed(uint40 validatorIndex, uint64 withdrawalTimestamp, address indexed recipient, uint64 withdrawalAmountGwei); + event FullWithdrawalRedeemed( + uint40 validatorIndex, + uint64 withdrawalTimestamp, + address indexed recipient, + uint64 withdrawalAmountGwei + ); /// @notice Emitted when a partial withdrawal claim is successfully redeemed - event PartialWithdrawalRedeemed(uint40 validatorIndex, uint64 withdrawalTimestamp, address indexed recipient, uint64 partialWithdrawalAmountGwei); + event PartialWithdrawalRedeemed( + uint40 validatorIndex, + uint64 withdrawalTimestamp, + address indexed recipient, + uint64 partialWithdrawalAmountGwei + ); /// @notice Emitted when restaked beacon chain ETH is withdrawn from the eigenPod. event RestakedBeaconChainETHWithdrawn(address indexed recipient, uint256 amount); @@ -127,9 +128,11 @@ contract EigenPodTests is ProofParsing, EigenPodPausingConstants { // deploy pauser registry address[] memory pausers = new address[](1); pausers[0] = pauser; - pauserReg= new PauserRegistry(pausers, unpauser); + pauserReg = new PauserRegistry(pausers, unpauser); - blsPkCompendium = new BLSPublicKeyCompendium(); + /// weird workaround: check commit before this + /// either related to foundry bug or emptty contract below this one + EmptyContract emptyContract2 = new EmptyContract(); /** * First, deploy upgradeable proxy contracts that **will point** to the implementations. Since the implementation contracts are @@ -151,14 +154,14 @@ contract EigenPodTests is ProofParsing, EigenPodPausingConstants { ethPOSDeposit = new ETHPOSDepositMock(); podImplementation = new EigenPod( - ethPOSDeposit, - delayedWithdrawalRouter, - IEigenPodManager(podManagerAddress), - MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR, - RESTAKED_BALANCE_OFFSET_GWEI, - GOERLI_GENESIS_TIME + ethPOSDeposit, + delayedWithdrawalRouter, + IEigenPodManager(podManagerAddress), + MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR, + RESTAKED_BALANCE_OFFSET_GWEI, + GOERLI_GENESIS_TIME ); - + eigenPodBeacon = new UpgradeableBeacon(address(podImplementation)); // this contract is deployed later to keep its address the same (for these tests) @@ -168,9 +171,19 @@ contract EigenPodTests is ProofParsing, EigenPodPausingConstants { // Second, deploy the *implementation* contracts, using the *proxy contracts* as inputs DelegationManager delegationImplementation = new DelegationManager(strategyManager, slasher, eigenPodManager); - StrategyManager strategyManagerImplementation = new StrategyManager(delegation, IEigenPodManager(podManagerAddress), slasher); + StrategyManager strategyManagerImplementation = new StrategyManager( + delegation, + IEigenPodManager(podManagerAddress), + slasher + ); Slasher slasherImplementation = new Slasher(strategyManager, delegation); - EigenPodManager eigenPodManagerImplementation = new EigenPodManager(ethPOSDeposit, eigenPodBeacon, strategyManager, slasher, delegation); + EigenPodManager eigenPodManagerImplementation = new EigenPodManager( + ethPOSDeposit, + eigenPodBeacon, + strategyManager, + slasher, + delegation + ); //ensuring that the address of eigenpodmanager doesn't change bytes memory code = address(eigenPodManager).code; @@ -178,7 +191,9 @@ contract EigenPodTests is ProofParsing, EigenPodPausingConstants { eigenPodManager = IEigenPodManager(podManagerAddress); beaconChainOracle = new BeaconChainOracleMock(); - DelayedWithdrawalRouter delayedWithdrawalRouterImplementation = new DelayedWithdrawalRouter(IEigenPodManager(podManagerAddress)); + DelayedWithdrawalRouter delayedWithdrawalRouterImplementation = new DelayedWithdrawalRouter( + IEigenPodManager(podManagerAddress) + ); address initialOwner = address(this); // Third, upgrade the proxy contracts to use the correct implementation contracts and initialize them. @@ -189,7 +204,7 @@ contract EigenPodTests is ProofParsing, EigenPodPausingConstants { DelegationManager.initialize.selector, initialOwner, pauserReg, - 0/*initialPausedStatus*/ + 0 /*initialPausedStatus*/ ) ); eigenLayerProxyAdmin.upgradeAndCall( @@ -200,18 +215,13 @@ contract EigenPodTests is ProofParsing, EigenPodPausingConstants { initialOwner, initialOwner, pauserReg, - 0/*initialPausedStatus*/ + 0 /*initialPausedStatus*/ ) ); eigenLayerProxyAdmin.upgradeAndCall( TransparentUpgradeableProxy(payable(address(slasher))), address(slasherImplementation), - abi.encodeWithSelector( - Slasher.initialize.selector, - initialOwner, - pauserReg, - 0/*initialPausedStatus*/ - ) + abi.encodeWithSelector(Slasher.initialize.selector, initialOwner, pauserReg, 0 /*initialPausedStatus*/) ); // TODO: add `cheats.expectEmit` calls for initialization events eigenLayerProxyAdmin.upgradeAndCall( @@ -223,7 +233,7 @@ contract EigenPodTests is ProofParsing, EigenPodPausingConstants { beaconChainOracle, initialOwner, pauserReg, - 0/*initialPausedStatus*/ + 0 /*initialPausedStatus*/ ) ); uint256 initPausedStatus = 0; @@ -231,16 +241,16 @@ contract EigenPodTests is ProofParsing, EigenPodPausingConstants { eigenLayerProxyAdmin.upgradeAndCall( TransparentUpgradeableProxy(payable(address(delayedWithdrawalRouter))), address(delayedWithdrawalRouterImplementation), - abi.encodeWithSelector(DelayedWithdrawalRouter.initialize.selector, initialOwner, pauserReg, initPausedStatus, withdrawalDelayBlocks) - ); - generalServiceManager1 = new ServiceManagerMock(slasher); - - generalReg1 = new MiddlewareRegistryMock( - generalServiceManager1, - strategyManager + abi.encodeWithSelector( + DelayedWithdrawalRouter.initialize.selector, + initialOwner, + pauserReg, + initPausedStatus, + withdrawalDelayBlocks + ) ); - cheats.deal(address(podOwner), 5*stakeAmount); + cheats.deal(address(podOwner), 5 * stakeAmount); fuzzedAddressMapping[address(0)] = true; fuzzedAddressMapping[address(eigenLayerProxyAdmin)] = true; @@ -248,8 +258,6 @@ contract EigenPodTests is ProofParsing, EigenPodPausingConstants { fuzzedAddressMapping[address(eigenPodManager)] = true; fuzzedAddressMapping[address(delegation)] = true; fuzzedAddressMapping[address(slasher)] = true; - fuzzedAddressMapping[address(generalServiceManager1)] = true; - fuzzedAddressMapping[address(generalReg1)] = true; } function testStaking() public { @@ -273,15 +281,23 @@ contract EigenPodTests is ProofParsing, EigenPodPausingConstants { cheats.deal(address(pod), stakeAmount); cheats.startPrank(podOwner); cheats.expectEmit(true, true, true, true, address(delayedWithdrawalRouter)); - emit DelayedWithdrawalCreated(podOwner, podOwner, stakeAmount, delayedWithdrawalRouter.userWithdrawalsLength(podOwner)); + emit DelayedWithdrawalCreated( + podOwner, + podOwner, + stakeAmount, + delayedWithdrawalRouter.userWithdrawalsLength(podOwner) + ); pod.withdrawBeforeRestaking(); require(_getLatestDelayedWithdrawalAmount(podOwner) == stakeAmount, "Payment amount should be stake amount"); - require(pod.mostRecentWithdrawalTimestamp() == uint64(block.timestamp), "Most recent withdrawal block number not updated"); + require( + pod.mostRecentWithdrawalTimestamp() == uint64(block.timestamp), + "Most recent withdrawal block number not updated" + ); } function testDeployEigenPodWithoutActivateRestaking() public { // ./solidityProofGen "ValidatorFieldsProof" 302913 true "data/withdrawal_proof_goerli/goerli_block_header_6399998.json" "data/withdrawal_proof_goerli/goerli_slot_6399998.json" "withdrawal_credential_proof_302913.json" - setJSON("./src/test/test-data/withdrawal_credential_proof_302913.json"); + setJSON("./src/test/test-data/withdrawal_credential_proof_302913.json"); IEigenPod newPod = eigenPodManager.getPod(podOwner); @@ -300,17 +316,23 @@ contract EigenPodTests is ProofParsing, EigenPodPausingConstants { //this simulates that hasRestaking is set to false, as would be the case for deployed pods that have not yet restaked prior to M2 cheats.store(address(newPod), bytes32(uint256(52)), bytes32(uint256(0))); - + cheats.startPrank(podOwner); cheats.warp(GOERLI_GENESIS_TIME); cheats.expectRevert(bytes("EigenPod.hasEnabledRestaking: restaking is not enabled")); - newPod.verifyWithdrawalCredentials(GOERLI_GENESIS_TIME, stateRootProofStruct, validatorIndices, proofsArray, validatorFieldsArray); + newPod.verifyWithdrawalCredentials( + GOERLI_GENESIS_TIME, + stateRootProofStruct, + validatorIndices, + proofsArray, + validatorFieldsArray + ); cheats.stopPrank(); } function testDeployEigenPodTooSoon() public { // ./solidityProofGen "ValidatorFieldsProof" 302913 true "data/withdrawal_proof_goerli/goerli_block_header_6399998.json" "data/withdrawal_proof_goerli/goerli_slot_6399998.json" "withdrawal_credential_proof_302913.json" - setJSON("./src/test/test-data/withdrawal_credential_proof_302913.json"); + setJSON("./src/test/test-data/withdrawal_credential_proof_302913.json"); IEigenPod newPod = eigenPodManager.getPod(podOwner); @@ -328,8 +350,18 @@ contract EigenPodTests is ProofParsing, EigenPodPausingConstants { validatorIndices[0] = uint40(getValidatorIndex()); cheats.startPrank(podOwner); - cheats.expectRevert(bytes("EigenPod.proofIsForValidTimestamp: beacon chain proof must be for timestamp after mostRecentWithdrawalTimestamp")); - newPod.verifyWithdrawalCredentials(timestamp, stateRootProofStruct, validatorIndices, proofsArray, validatorFieldsArray); + cheats.expectRevert( + bytes( + "EigenPod.proofIsForValidTimestamp: beacon chain proof must be for timestamp after mostRecentWithdrawalTimestamp" + ) + ); + newPod.verifyWithdrawalCredentials( + timestamp, + stateRootProofStruct, + validatorIndices, + proofsArray, + validatorFieldsArray + ); cheats.stopPrank(); } @@ -358,7 +390,7 @@ contract EigenPodTests is ProofParsing, EigenPodPausingConstants { setJSON("./src/test/test-data/fullWithdrawalProof_Latest.json"); BeaconChainProofs.WithdrawalProof memory proofs = _getWithdrawalProof(); bytes32 beaconStateRoot = getBeaconStateRoot(); - withdrawalFields = getWithdrawalFields(); + withdrawalFields = getWithdrawalFields(); validatorFields = getValidatorFields(); Relayer relay = new Relayer(); @@ -366,9 +398,13 @@ contract EigenPodTests is ProofParsing, EigenPodPausingConstants { relay.verifyWithdrawal(beaconStateRoot, withdrawalFields, proofs); } - function testFullWithdrawalProofWithWrongIndices(uint64 wrongBlockRootIndex, uint64 wrongWithdrawalIndex, uint64 wrongHistoricalSummariesIndex) public { - uint256 BLOCK_ROOTS_TREE_HEIGHT = 13; - uint256 WITHDRAWALS_TREE_HEIGHT = 4; + function testFullWithdrawalProofWithWrongIndices( + uint64 wrongBlockRootIndex, + uint64 wrongWithdrawalIndex, + uint64 wrongHistoricalSummariesIndex + ) public { + uint256 BLOCK_ROOTS_TREE_HEIGHT = 13; + uint256 WITHDRAWALS_TREE_HEIGHT = 4; uint256 HISTORICAL_SUMMARIES_TREE_HEIGHT = 24; cheats.assume(wrongBlockRootIndex > 2 ** BLOCK_ROOTS_TREE_HEIGHT); cheats.assume(wrongWithdrawalIndex > 2 ** WITHDRAWALS_TREE_HEIGHT); @@ -379,7 +415,7 @@ contract EigenPodTests is ProofParsing, EigenPodPausingConstants { setJSON("./src/test/test-data/fullWithdrawalProof_Latest.json"); bytes32 beaconStateRoot = getBeaconStateRoot(); validatorFields = getValidatorFields(); - withdrawalFields = getWithdrawalFields(); + withdrawalFields = getWithdrawalFields(); { BeaconChainProofs.WithdrawalProof memory wrongProofs = _getWithdrawalProof(); @@ -419,23 +455,29 @@ contract EigenPodTests is ProofParsing, EigenPodPausingConstants { } /** - * @notice this test is to ensure that a full withdrawal can be made once a validator has processed their first full withrawal - * This is specifically for the case where a validator has redeposited into their exited validator and needs to prove another withdrawal - * to get their funds out - */ + * @notice this test is to ensure that a full withdrawal can be made once a validator has processed their first full withrawal + * This is specifically for the case where a validator has redeposited into their exited validator and needs to prove another withdrawal + * to get their funds out + */ function testWithdrawAfterFullWithdrawal() external { IEigenPod pod = testFullWithdrawalFlow(); // ./solidityProofGen "WithdrawalFieldsProof" 302913 146 8092 true false "data/withdrawal_proof_goerli/goerli_block_header_6399998.json" "data/withdrawal_proof_goerli/goerli_slot_6399998.json" "data/withdrawal_proof_goerli/goerli_slot_6397852.json" "data/withdrawal_proof_goerli/goerli_block_header_6397852.json" "data/withdrawal_proof_goerli/goerli_block_6397852.json" "fullWithdrawalProof_Latest_1SlotAdvanced.json" true setJSON("./src/test/test-data/fullWithdrawalProof_Latest_1SlotAdvanced.json"); BeaconChainOracleMock(address(beaconChainOracle)).setOracleBlockRootAtTimestamp(getLatestBlockRoot()); - + withdrawalFields = getWithdrawalFields(); - uint64 withdrawalAmountGwei = Endian.fromLittleEndianUint64(withdrawalFields[BeaconChainProofs.WITHDRAWAL_VALIDATOR_AMOUNT_INDEX]); - uint64 leftOverBalanceWEI = uint64(withdrawalAmountGwei - _calculateRestakedBalanceGwei(pod.MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR())) * uint64(GWEI_TO_WEI); + uint64 withdrawalAmountGwei = Endian.fromLittleEndianUint64( + withdrawalFields[BeaconChainProofs.WITHDRAWAL_VALIDATOR_AMOUNT_INDEX] + ); + uint64 leftOverBalanceWEI = uint64( + withdrawalAmountGwei - _calculateRestakedBalanceGwei(pod.MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR()) + ) * uint64(GWEI_TO_WEI); cheats.deal(address(pod), leftOverBalanceWEI); - { - BeaconChainProofs.WithdrawalProof[] memory withdrawalProofsArray = new BeaconChainProofs.WithdrawalProof[](1); + { + BeaconChainProofs.WithdrawalProof[] memory withdrawalProofsArray = new BeaconChainProofs.WithdrawalProof[]( + 1 + ); withdrawalProofsArray[0] = _getWithdrawalProof(); bytes[] memory validatorFieldsProofArray = new bytes[](1); validatorFieldsProofArray[0] = abi.encodePacked(getValidatorProof()); @@ -446,16 +488,24 @@ contract EigenPodTests is ProofParsing, EigenPodPausingConstants { BeaconChainProofs.StateRootProof memory stateRootProofStruct = _getStateRootProof(); - - pod.verifyAndProcessWithdrawals(0, stateRootProofStruct, withdrawalProofsArray, validatorFieldsProofArray, validatorFieldsArray, withdrawalFieldsArray); + pod.verifyAndProcessWithdrawals( + 0, + stateRootProofStruct, + withdrawalProofsArray, + validatorFieldsProofArray, + validatorFieldsArray, + withdrawalFieldsArray + ); } } function testProvingFullWithdrawalForTheSameSlotFails() external { IEigenPod pod = testFullWithdrawalFlow(); - - { - BeaconChainProofs.WithdrawalProof[] memory withdrawalProofsArray = new BeaconChainProofs.WithdrawalProof[](1); + + { + BeaconChainProofs.WithdrawalProof[] memory withdrawalProofsArray = new BeaconChainProofs.WithdrawalProof[]( + 1 + ); withdrawalProofsArray[0] = _getWithdrawalProof(); bytes[] memory validatorFieldsProofArray = new bytes[](1); validatorFieldsProofArray[0] = abi.encodePacked(getValidatorProof()); @@ -466,22 +516,29 @@ contract EigenPodTests is ProofParsing, EigenPodPausingConstants { BeaconChainProofs.StateRootProof memory stateRootProofStruct = _getStateRootProof(); - cheats.expectRevert(bytes("EigenPod._verifyAndProcessWithdrawal: withdrawal has already been proven for this timestamp")); - pod.verifyAndProcessWithdrawals(0, stateRootProofStruct, withdrawalProofsArray, validatorFieldsProofArray, validatorFieldsArray, withdrawalFieldsArray); + cheats.expectRevert( + bytes("EigenPod._verifyAndProcessWithdrawal: withdrawal has already been proven for this timestamp") + ); + pod.verifyAndProcessWithdrawals( + 0, + stateRootProofStruct, + withdrawalProofsArray, + validatorFieldsProofArray, + validatorFieldsArray, + withdrawalFieldsArray + ); } - } /// @notice This test is to ensure that the partial withdrawal flow works correctly - function testPartialWithdrawalFlow() public returns(IEigenPod) { + function testPartialWithdrawalFlow() public returns (IEigenPod) { //this call is to ensure that validator 61068 has proven their withdrawalcreds // ./solidityProofGen "ValidatorFieldsProof" 302913 true "data/withdrawal_proof_goerli/goerli_block_header_6399998.json" "data/withdrawal_proof_goerli/goerli_slot_6399998.json" "withdrawal_credential_proof_302913.json" setJSON("./src/test/test-data/withdrawal_credential_proof_302913.json"); - setJSON("./src/test/test-data/withdrawal_credential_proof_302913.json"); + setJSON("./src/test/test-data/withdrawal_credential_proof_302913.json"); _testDeployAndVerifyNewEigenPod(podOwner, signature, depositDataRoot); IEigenPod newPod = eigenPodManager.getPod(podOwner); - - //generate partialWithdrawalProofs.json with: + //generate partialWithdrawalProofs.json with: // ./solidityProofGen "WithdrawalFieldsProof" 302913 146 8092 true true "data/withdrawal_proof_goerli/goerli_block_header_6399998.json" "data/withdrawal_proof_goerli/goerli_slot_6399998.json" "data/withdrawal_proof_goerli/goerli_slot_6397852.json" "data/withdrawal_proof_goerli/goerli_block_header_6397852.json" "data/withdrawal_proof_goerli/goerli_block_6397852.json" "partialWithdrawalProof_Latest.json" false setJSON("./src/test/test-data/partialWithdrawalProof_Latest.json"); withdrawalFields = getWithdrawalFields(); @@ -490,12 +547,18 @@ contract EigenPodTests is ProofParsing, EigenPodPausingConstants { bytes memory validatorFieldsProof = abi.encodePacked(getValidatorProof()); BeaconChainOracleMock(address(beaconChainOracle)).setOracleBlockRootAtTimestamp(getLatestBlockRoot()); - uint64 withdrawalAmountGwei = Endian.fromLittleEndianUint64(withdrawalFields[BeaconChainProofs.WITHDRAWAL_VALIDATOR_AMOUNT_INDEX]); - uint40 validatorIndex = uint40(Endian.fromLittleEndianUint64(withdrawalFields[BeaconChainProofs.WITHDRAWAL_VALIDATOR_INDEX_INDEX])); + uint64 withdrawalAmountGwei = Endian.fromLittleEndianUint64( + withdrawalFields[BeaconChainProofs.WITHDRAWAL_VALIDATOR_AMOUNT_INDEX] + ); + uint40 validatorIndex = uint40( + Endian.fromLittleEndianUint64(withdrawalFields[BeaconChainProofs.WITHDRAWAL_VALIDATOR_INDEX_INDEX]) + ); - cheats.deal(address(newPod), stakeAmount); + cheats.deal(address(newPod), stakeAmount); { - BeaconChainProofs.WithdrawalProof[] memory withdrawalProofsArray = new BeaconChainProofs.WithdrawalProof[](1); + BeaconChainProofs.WithdrawalProof[] memory withdrawalProofsArray = new BeaconChainProofs.WithdrawalProof[]( + 1 + ); withdrawalProofsArray[0] = withdrawalProofs; bytes[] memory validatorFieldsProofArray = new bytes[](1); validatorFieldsProofArray[0] = validatorFieldsProof; @@ -509,28 +572,52 @@ contract EigenPodTests is ProofParsing, EigenPodPausingConstants { BeaconChainProofs.StateRootProof memory stateRootProofStruct = _getStateRootProof(); //cheats.expectEmit(true, true, true, true, address(newPod)); - emit PartialWithdrawalRedeemed(validatorIndex, _computeTimestampAtSlot(Endian.fromLittleEndianUint64(withdrawalProofs.slotRoot)), podOwner, withdrawalAmountGwei); - newPod.verifyAndProcessWithdrawals(0, stateRootProofStruct, withdrawalProofsArray, validatorFieldsProofArray, validatorFieldsArray, withdrawalFieldsArray); - require(newPod.provenWithdrawal(validatorFields[0], _computeTimestampAtSlot(Endian.fromLittleEndianUint64(withdrawalProofs.slotRoot))), "provenPartialWithdrawal should be true"); - withdrawalAmountGwei = uint64(withdrawalAmountGwei*GWEI_TO_WEI); - require(address(delayedWithdrawalRouter).balance - delayedWithdrawalRouterContractBalanceBefore == withdrawalAmountGwei, - "pod delayed withdrawal balance hasn't been updated correctly"); + emit PartialWithdrawalRedeemed( + validatorIndex, + _computeTimestampAtSlot(Endian.fromLittleEndianUint64(withdrawalProofs.slotRoot)), + podOwner, + withdrawalAmountGwei + ); + newPod.verifyAndProcessWithdrawals( + 0, + stateRootProofStruct, + withdrawalProofsArray, + validatorFieldsProofArray, + validatorFieldsArray, + withdrawalFieldsArray + ); + require( + newPod.provenWithdrawal( + validatorFields[0], + _computeTimestampAtSlot(Endian.fromLittleEndianUint64(withdrawalProofs.slotRoot)) + ), + "provenPartialWithdrawal should be true" + ); + withdrawalAmountGwei = uint64(withdrawalAmountGwei * GWEI_TO_WEI); + require( + address(delayedWithdrawalRouter).balance - delayedWithdrawalRouterContractBalanceBefore == + withdrawalAmountGwei, + "pod delayed withdrawal balance hasn't been updated correctly" + ); } cheats.roll(block.number + WITHDRAWAL_DELAY_BLOCKS + 1); uint256 podOwnerBalanceBefore = address(podOwner).balance; delayedWithdrawalRouter.claimDelayedWithdrawals(podOwner, 1); - require(address(podOwner).balance - podOwnerBalanceBefore == withdrawalAmountGwei, "Pod owner balance hasn't been updated correctly"); + require( + address(podOwner).balance - podOwnerBalanceBefore == withdrawalAmountGwei, + "Pod owner balance hasn't been updated correctly" + ); return newPod; } /// @notice verifies that multiple partial withdrawals can be made before a full withdrawal - function testProvingMultiplePartialWithdrawalsForSameSlot(/*uint256 numPartialWithdrawals*/) public { + function testProvingMultiplePartialWithdrawalsForSameSlot() public /*uint256 numPartialWithdrawals*/ { IEigenPod newPod = testPartialWithdrawalFlow(); BeaconChainProofs.WithdrawalProof memory withdrawalProofs = _getWithdrawalProof(); bytes memory validatorFieldsProof = abi.encodePacked(getValidatorProof()); - withdrawalFields = getWithdrawalFields(); + withdrawalFields = getWithdrawalFields(); validatorFields = getValidatorFields(); BeaconChainProofs.WithdrawalProof[] memory withdrawalProofsArray = new BeaconChainProofs.WithdrawalProof[](1); @@ -544,21 +631,32 @@ contract EigenPodTests is ProofParsing, EigenPodPausingConstants { BeaconChainProofs.StateRootProof memory stateRootProofStruct = _getStateRootProof(); - cheats.expectRevert(bytes("EigenPod._verifyAndProcessWithdrawal: withdrawal has already been proven for this timestamp")); - newPod.verifyAndProcessWithdrawals(0, stateRootProofStruct, withdrawalProofsArray, validatorFieldsProofArray, validatorFieldsArray, withdrawalFieldsArray); + cheats.expectRevert( + bytes("EigenPod._verifyAndProcessWithdrawal: withdrawal has already been proven for this timestamp") + ); + newPod.verifyAndProcessWithdrawals( + 0, + stateRootProofStruct, + withdrawalProofsArray, + validatorFieldsProofArray, + validatorFieldsArray, + withdrawalFieldsArray + ); } /// @notice verifies that multiple full withdrawals for a single validator fail - function testDoubleFullWithdrawal() public returns(IEigenPod newPod) { + function testDoubleFullWithdrawal() public returns (IEigenPod newPod) { newPod = testFullWithdrawalFlow(); - uint64 withdrawalAmountGwei = Endian.fromLittleEndianUint64(withdrawalFields[BeaconChainProofs.WITHDRAWAL_VALIDATOR_AMOUNT_INDEX]); - uint64 leftOverBalanceWEI = uint64(withdrawalAmountGwei - newPod.MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR()) * uint64(GWEI_TO_WEI); + uint64 withdrawalAmountGwei = Endian.fromLittleEndianUint64( + withdrawalFields[BeaconChainProofs.WITHDRAWAL_VALIDATOR_AMOUNT_INDEX] + ); + uint64 leftOverBalanceWEI = uint64(withdrawalAmountGwei - newPod.MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR()) * + uint64(GWEI_TO_WEI); cheats.deal(address(newPod), leftOverBalanceWEI); - BeaconChainProofs.WithdrawalProof memory withdrawalProofs = _getWithdrawalProof(); bytes memory validatorFieldsProof = abi.encodePacked(getValidatorProof()); - withdrawalFields = getWithdrawalFields(); + withdrawalFields = getWithdrawalFields(); validatorFields = getValidatorFields(); BeaconChainProofs.WithdrawalProof[] memory withdrawalProofsArray = new BeaconChainProofs.WithdrawalProof[](1); @@ -572,15 +670,24 @@ contract EigenPodTests is ProofParsing, EigenPodPausingConstants { BeaconChainProofs.StateRootProof memory stateRootProofStruct = _getStateRootProof(); - cheats.expectRevert(bytes("EigenPod._verifyAndProcessWithdrawal: withdrawal has already been proven for this timestamp")); - newPod.verifyAndProcessWithdrawals(0, stateRootProofStruct, withdrawalProofsArray, validatorFieldsProofArray, validatorFieldsArray, withdrawalFieldsArray); - + cheats.expectRevert( + bytes("EigenPod._verifyAndProcessWithdrawal: withdrawal has already been proven for this timestamp") + ); + newPod.verifyAndProcessWithdrawals( + 0, + stateRootProofStruct, + withdrawalProofsArray, + validatorFieldsProofArray, + validatorFieldsArray, + withdrawalFieldsArray + ); + return newPod; } - function testDeployAndVerifyNewEigenPod() public returns(IEigenPod) { + function testDeployAndVerifyNewEigenPod() public returns (IEigenPod) { // ./solidityProofGen "ValidatorFieldsProof" 302913 true "data/withdrawal_proof_goerli/goerli_block_header_6399998.json" "data/withdrawal_proof_goerli/goerli_slot_6399998.json" "withdrawal_credential_proof_302913.json" setJSON("./src/test/test-data/withdrawal_credential_proof_302913.json"); - setJSON("./src/test/test-data/withdrawal_credential_proof_302913.json"); + setJSON("./src/test/test-data/withdrawal_credential_proof_302913.json"); return _testDeployAndVerifyNewEigenPod(podOwner, signature, depositDataRoot); } @@ -597,17 +704,19 @@ contract EigenPodTests is ProofParsing, EigenPodPausingConstants { setJSON("./src/test/test-data/balanceUpdateProof_overCommitted_302913.json"); _proveOverCommittedStake(newPod); - uint64 newValidatorBalance = BeaconChainProofs.getBalanceAtIndex(getBalanceRoot(), uint40(getValidatorIndex())); + uint64 newValidatorBalance = BeaconChainProofs.getBalanceAtIndex(getBalanceRoot(), uint40(getValidatorIndex())); int256 beaconChainETHShares = eigenPodManager.podOwnerShares(podOwner); - require(beaconChainETHShares == int256(_calculateRestakedBalanceGwei(newValidatorBalance) * GWEI_TO_WEI), - "eigenPodManager shares not updated correctly"); + require( + beaconChainETHShares == int256(_calculateRestakedBalanceGwei(newValidatorBalance) * GWEI_TO_WEI), + "eigenPodManager shares not updated correctly" + ); } //test deploying an eigen pod with mismatched withdrawal credentials between the proof and the actual pod's address function testDeployNewEigenPodWithWrongWithdrawalCreds(address wrongWithdrawalAddress) public { // ./solidityProofGen "ValidatorFieldsProof" 302913 true "data/withdrawal_proof_goerli/goerli_block_header_6399998.json" "data/withdrawal_proof_goerli/goerli_slot_6399998.json" "withdrawal_credential_proof_302913.json" setJSON("./src/test/test-data/withdrawal_credential_proof_302913.json"); - setJSON("./src/test/test-data/withdrawal_credential_proof_302913.json"); + setJSON("./src/test/test-data/withdrawal_credential_proof_302913.json"); cheats.startPrank(podOwner); eigenPodManager.stake{value: stakeAmount}(pubkey, signature, depositDataRoot); cheats.stopPrank(); @@ -630,8 +739,8 @@ contract EigenPodTests is ProofParsing, EigenPodPausingConstants { cheats.startPrank(podOwner); cheats.warp(timestamp); - if(!newPod.hasRestaked()){ - newPod.activateRestaking(); + if (!newPod.hasRestaked()) { + newPod.activateRestaking(); } // set oracle block root _setOracleBlockRoot(); @@ -640,7 +749,13 @@ contract EigenPodTests is ProofParsing, EigenPodPausingConstants { cheats.warp(timestamp += 1); cheats.expectRevert(bytes("EigenPod.verifyCorrectWithdrawalCredentials: Proof is not for this EigenPod")); - newPod.verifyWithdrawalCredentials(timestamp, stateRootProofStruct, validatorIndices, proofsArray, validatorFieldsArray); + newPod.verifyWithdrawalCredentials( + timestamp, + stateRootProofStruct, + validatorIndices, + proofsArray, + validatorFieldsArray + ); cheats.stopPrank(); } @@ -648,13 +763,13 @@ contract EigenPodTests is ProofParsing, EigenPodPausingConstants { // nonPodOwnerAddress must be different from podOwner cheats.assume(nonPodOwnerAddress != podOwner); // ./solidityProofGen "ValidatorFieldsProof" 302913 true "data/withdrawal_proof_goerli/goerli_block_header_6399998.json" "data/withdrawal_proof_goerli/goerli_slot_6399998.json" "withdrawal_credential_proof_302913.json" setJSON("./src/test/test-data/withdrawal_credential_proof_302913.json"); - setJSON("./src/test/test-data/withdrawal_credential_proof_302913.json"); + setJSON("./src/test/test-data/withdrawal_credential_proof_302913.json"); cheats.startPrank(podOwner); eigenPodManager.stake{value: stakeAmount}(pubkey, signature, depositDataRoot); cheats.stopPrank(); IEigenPod newPod = eigenPodManager.getPod(podOwner); - + uint64 timestamp = 1; bytes32[][] memory validatorFieldsArray = new bytes32[][](1); @@ -668,14 +783,20 @@ contract EigenPodTests is ProofParsing, EigenPodPausingConstants { cheats.startPrank(nonPodOwnerAddress); cheats.expectRevert(bytes("EigenPod.onlyEigenPodOwner: not podOwner")); - newPod.verifyWithdrawalCredentials(timestamp, stateRootProofStruct, validatorIndices, proofsArray, validatorFieldsArray); + newPod.verifyWithdrawalCredentials( + timestamp, + stateRootProofStruct, + validatorIndices, + proofsArray, + validatorFieldsArray + ); cheats.stopPrank(); } //test that when withdrawal credentials are verified more than once, it reverts function testDeployNewEigenPodWithActiveValidator() public { // ./solidityProofGen "ValidatorFieldsProof" 302913 true "data/withdrawal_proof_goerli/goerli_block_header_6399998.json" "data/withdrawal_proof_goerli/goerli_slot_6399998.json" "withdrawal_credential_proof_302913.json" setJSON("./src/test/test-data/withdrawal_credential_proof_302913.json"); - setJSON("./src/test/test-data/withdrawal_credential_proof_302913.json"); + setJSON("./src/test/test-data/withdrawal_credential_proof_302913.json"); IEigenPod pod = _testDeployAndVerifyNewEigenPod(podOwner, signature, depositDataRoot); uint64 timestamp = 1; @@ -691,8 +812,18 @@ contract EigenPodTests is ProofParsing, EigenPodPausingConstants { BeaconChainProofs.StateRootProof memory stateRootProofStruct = _getStateRootProof(); cheats.startPrank(podOwner); - cheats.expectRevert(bytes("EigenPod.verifyCorrectWithdrawalCredentials: Validator must be inactive to prove withdrawal credentials")); - pod.verifyWithdrawalCredentials(timestamp, stateRootProofStruct, validatorIndices, proofsArray, validatorFieldsArray); + cheats.expectRevert( + bytes( + "EigenPod.verifyCorrectWithdrawalCredentials: Validator must be inactive to prove withdrawal credentials" + ) + ); + pod.verifyWithdrawalCredentials( + timestamp, + stateRootProofStruct, + validatorIndices, + proofsArray, + validatorFieldsArray + ); cheats.stopPrank(); } @@ -702,15 +833,18 @@ contract EigenPodTests is ProofParsing, EigenPodPausingConstants { function testProveSingleWithdrawalCredential() public { // ./solidityProofGen "ValidatorFieldsProof" 302913 true "data/withdrawal_proof_goerli/goerli_block_header_6399998.json" "data/withdrawal_proof_goerli/goerli_slot_6399998.json" "withdrawal_credential_proof_302913.json" setJSON("./src/test/test-data/withdrawal_credential_proof_302913.json"); - setJSON("./src/test/test-data/withdrawal_credential_proof_302913.json"); + setJSON("./src/test/test-data/withdrawal_credential_proof_302913.json"); IEigenPod pod = _testDeployAndVerifyNewEigenPod(podOwner, signature, depositDataRoot); bytes32 validatorPubkeyHash = getValidatorPubkeyHash(); - assertTrue(pod.validatorStatus(validatorPubkeyHash) == IEigenPod.VALIDATOR_STATUS.ACTIVE, "wrong validator status"); + assertTrue( + pod.validatorStatus(validatorPubkeyHash) == IEigenPod.VALIDATOR_STATUS.ACTIVE, + "wrong validator status" + ); } // // 5. Prove overcommitted balance - // // Setup: Run (3). + // // Setup: Run (3). // // Test: Watcher proves an overcommitted balance for validator from (3). // // validator status should be marked as OVERCOMMITTED @@ -722,8 +856,9 @@ contract EigenPodTests is ProofParsing, EigenPodPausingConstants { int256 beaconChainETHBefore = eigenPodManager.podOwnerShares(podOwner); bytes32 validatorPubkeyHash = getValidatorPubkeyHash(); - uint256 validatorRestakedBalanceBefore = newPod.validatorPubkeyHashToInfo(validatorPubkeyHash).restakedBalanceGwei; - + uint256 validatorRestakedBalanceBefore = newPod + .validatorPubkeyHashToInfo(validatorPubkeyHash) + .restakedBalanceGwei; // ./solidityProofGen "BalanceUpdateProof" 302913 true 0 "data/withdrawal_proof_goerli/goerli_block_header_6399998.json" "data/withdrawal_proof_goerli/goerli_slot_6399998.json" "balanceUpdateProof_overCommitted_302913.json" setJSON("./src/test/test-data/balanceUpdateProof_overCommitted_302913.json"); @@ -731,14 +866,27 @@ contract EigenPodTests is ProofParsing, EigenPodPausingConstants { cheats.warp(GOERLI_GENESIS_TIME); _proveOverCommittedStake(newPod); - uint256 validatorRestakedBalanceAfter = newPod.validatorPubkeyHashToInfo(validatorPubkeyHash).restakedBalanceGwei; + uint256 validatorRestakedBalanceAfter = newPod + .validatorPubkeyHashToInfo(validatorPubkeyHash) + .restakedBalanceGwei; - uint64 newValidatorBalance = BeaconChainProofs.getBalanceAtIndex(getBalanceRoot(), uint40(getValidatorIndex())); + uint64 newValidatorBalance = BeaconChainProofs.getBalanceAtIndex(getBalanceRoot(), uint40(getValidatorIndex())); int256 shareDiff = beaconChainETHBefore - eigenPodManager.podOwnerShares(podOwner); - - assertTrue(eigenPodManager.podOwnerShares(podOwner) == int256(_calculateRestakedBalanceGwei(newValidatorBalance) * GWEI_TO_WEI), "hysterisis not working"); - assertTrue(beaconChainETHBefore - eigenPodManager.podOwnerShares(podOwner) == shareDiff, "BeaconChainETHShares not updated"); - assertTrue(int256(validatorRestakedBalanceBefore) - int256(validatorRestakedBalanceAfter) == shareDiff/int256(GWEI_TO_WEI), "validator restaked balance not updated"); + + assertTrue( + eigenPodManager.podOwnerShares(podOwner) == + int256(_calculateRestakedBalanceGwei(newValidatorBalance) * GWEI_TO_WEI), + "hysterisis not working" + ); + assertTrue( + beaconChainETHBefore - eigenPodManager.podOwnerShares(podOwner) == shareDiff, + "BeaconChainETHShares not updated" + ); + assertTrue( + int256(validatorRestakedBalanceBefore) - int256(validatorRestakedBalanceAfter) == + shareDiff / int256(GWEI_TO_WEI), + "validator restaked balance not updated" + ); } function testVerifyUndercommittedBalance() public { @@ -748,7 +896,9 @@ contract EigenPodTests is ProofParsing, EigenPodPausingConstants { // get beaconChainETH shares int256 beaconChainETHBefore = eigenPodManager.podOwnerShares(podOwner); bytes32 validatorPubkeyHash = getValidatorPubkeyHash(); - uint256 validatorRestakedBalanceBefore = newPod.validatorPubkeyHashToInfo(validatorPubkeyHash).restakedBalanceGwei; + uint256 validatorRestakedBalanceBefore = newPod + .validatorPubkeyHashToInfo(validatorPubkeyHash) + .restakedBalanceGwei; // ./solidityProofGen "BalanceUpdateProof" 302913 true 0 "data/withdrawal_proof_goerli/goerli_block_header_6399998.json" "data/withdrawal_proof_goerli/goerli_slot_6399998.json" "balanceUpdateProof_overCommitted_302913.json" setJSON("./src/test/test-data/balanceUpdateProof_overCommitted_302913.json"); @@ -761,35 +911,47 @@ contract EigenPodTests is ProofParsing, EigenPodPausingConstants { setJSON("./src/test/test-data/balanceUpdateProof_notOverCommitted_302913_incrementedBlockBy100.json"); _proveUnderCommittedStake(newPod); - uint256 validatorRestakedBalanceAfter = newPod.validatorPubkeyHashToInfo(validatorPubkeyHash).restakedBalanceGwei; + uint256 validatorRestakedBalanceAfter = newPod + .validatorPubkeyHashToInfo(validatorPubkeyHash) + .restakedBalanceGwei; - uint64 newValidatorBalance = BeaconChainProofs.getBalanceAtIndex(getBalanceRoot(), uint40(getValidatorIndex())); + uint64 newValidatorBalance = BeaconChainProofs.getBalanceAtIndex(getBalanceRoot(), uint40(getValidatorIndex())); int256 shareDiff = beaconChainETHBefore - eigenPodManager.podOwnerShares(podOwner); - - assertTrue(eigenPodManager.podOwnerShares(podOwner) == int256(_calculateRestakedBalanceGwei(newValidatorBalance) * GWEI_TO_WEI), - "hysterisis not working"); - assertTrue(beaconChainETHBefore - eigenPodManager.podOwnerShares(podOwner) == shareDiff, "BeaconChainETHShares not updated"); - assertTrue(int256(uint256(validatorRestakedBalanceBefore)) - int256(uint256(validatorRestakedBalanceAfter)) == shareDiff/int256(GWEI_TO_WEI), - "validator restaked balance not updated"); + + assertTrue( + eigenPodManager.podOwnerShares(podOwner) == + int256(_calculateRestakedBalanceGwei(newValidatorBalance) * GWEI_TO_WEI), + "hysterisis not working" + ); + assertTrue( + beaconChainETHBefore - eigenPodManager.podOwnerShares(podOwner) == shareDiff, + "BeaconChainETHShares not updated" + ); + assertTrue( + int256(uint256(validatorRestakedBalanceBefore)) - int256(uint256(validatorRestakedBalanceAfter)) == + shareDiff / int256(GWEI_TO_WEI), + "validator restaked balance not updated" + ); } function testTooSoonBalanceUpdate(uint64 oracleTimestamp, uint64 mostRecentBalanceUpdateTimestamp) external { cheats.assume(oracleTimestamp < mostRecentBalanceUpdateTimestamp); _deployInternalFunctionTester(); - + setJSON("./src/test/test-data/balanceUpdateProof_notOverCommitted_302913.json"); validatorFields = getValidatorFields(); uint40[] memory validatorIndices = new uint40[](1); validatorIndices[0] = uint40(getValidatorIndex()); - BeaconChainProofs.BalanceUpdateProof memory proof = _getBalanceUpdateProof(); - + BeaconChainProofs.BalanceUpdateProof memory proof = _getBalanceUpdateProof(); bytes32 newBeaconStateRoot = getBeaconStateRoot(); emit log_named_bytes32("newBeaconStateRoot", newBeaconStateRoot); BeaconChainOracleMock(address(beaconChainOracle)).setOracleBlockRootAtTimestamp(newBeaconStateRoot); - cheats.expectRevert(bytes("EigenPod.verifyBalanceUpdate: Validators balance has already been updated for this timestamp")); + cheats.expectRevert( + bytes("EigenPod.verifyBalanceUpdate: Validators balance has already been updated for this timestamp") + ); podInternalFunctionTester.verifyBalanceUpdate( oracleTimestamp, 0, @@ -798,7 +960,6 @@ contract EigenPodTests is ProofParsing, EigenPodPausingConstants { validatorFields, mostRecentBalanceUpdateTimestamp ); - } function testDeployingEigenPodRevertsWhenPaused() external { @@ -833,7 +994,7 @@ contract EigenPodTests is ProofParsing, EigenPodPausingConstants { cheats.stopPrank(); IEigenPod newPod = eigenPodManager.getPod(podOwner); - cheats.deal(nonPodManager, stakeAmount); + cheats.deal(nonPodManager, stakeAmount); cheats.startPrank(nonPodManager); cheats.expectRevert(bytes("EigenPod.onlyEigenPodManager: not eigenPodManager")); @@ -855,7 +1016,7 @@ contract EigenPodTests is ProofParsing, EigenPodPausingConstants { cheats.expectRevert(bytes("EigenPod.onlyEigenPodOwner: not podOwner")); pod.withdrawBeforeRestaking(); } - + /* test deprecated since this is checked on the EigenPodManager level, rather than the EigenPod level TODO: @Sidu28 - check whether we have adequate coverage of the correct function function testWithdrawRestakedBeaconChainETHRevertsWhenPaused() external { @@ -876,7 +1037,7 @@ contract EigenPodTests is ProofParsing, EigenPodPausingConstants { function testVerifyCorrectWithdrawalCredentialsRevertsWhenPaused() external { // ./solidityProofGen "ValidatorFieldsProof" 302913 true "data/withdrawal_proof_goerli/goerli_block_header_6399998.json" "data/withdrawal_proof_goerli/goerli_slot_6399998.json" "withdrawal_credential_proof_302913.json" setJSON("./src/test/test-data/withdrawal_credential_proof_302913.json"); - setJSON("./src/test/test-data/withdrawal_credential_proof_302913.json"); + setJSON("./src/test/test-data/withdrawal_credential_proof_302913.json"); bytes32 newBeaconStateRoot = getBeaconStateRoot(); BeaconChainOracleMock(address(beaconChainOracle)).setOracleBlockRootAtTimestamp(newBeaconStateRoot); @@ -894,7 +1055,6 @@ contract EigenPodTests is ProofParsing, EigenPodPausingConstants { eigenPodManager.pause(2 ** PAUSED_EIGENPODS_VERIFY_CREDENTIALS); cheats.stopPrank(); - bytes32[][] memory validatorFieldsArray = new bytes32[][](1); validatorFieldsArray[0] = getValidatorFields(); @@ -908,7 +1068,13 @@ contract EigenPodTests is ProofParsing, EigenPodPausingConstants { cheats.startPrank(podOwner); cheats.expectRevert(bytes("EigenPod.onlyWhenNotPaused: index is paused in EigenPodManager")); - newPod.verifyWithdrawalCredentials(timestamp, stateRootProofStruct, validatorIndices, proofsArray, validatorFieldsArray); + newPod.verifyWithdrawalCredentials( + timestamp, + stateRootProofStruct, + validatorIndices, + proofsArray, + validatorFieldsArray + ); cheats.stopPrank(); } @@ -917,7 +1083,7 @@ contract EigenPodTests is ProofParsing, EigenPodPausingConstants { setJSON("./src/test/test-data/balanceUpdateProof_notOverCommitted_302913.json"); IEigenPod newPod = _testDeployAndVerifyNewEigenPod(podOwner, signature, depositDataRoot); - // ./solidityProofGen "BalanceUpdateProof" 302913 true 0 "data/withdrawal_proof_goerli/goerli_block_header_6399998.json" "data/withdrawal_proof_goerli/goerli_slot_6399998.json" "balanceUpdateProof_overCommitted_302913.json" + // ./solidityProofGen "BalanceUpdateProof" 302913 true 0 "data/withdrawal_proof_goerli/goerli_block_header_6399998.json" "data/withdrawal_proof_goerli/goerli_slot_6399998.json" "balanceUpdateProof_overCommitted_302913.json" setJSON("./src/test/test-data/balanceUpdateProof_overCommitted_302913.json"); bytes32[][] memory validatorFieldsArray = new bytes32[][](1); validatorFieldsArray[0] = getValidatorFields(); @@ -930,7 +1096,7 @@ contract EigenPodTests is ProofParsing, EigenPodPausingConstants { bytes32 newBeaconStateRoot = getBeaconStateRoot(); BeaconChainOracleMock(address(beaconChainOracle)).setOracleBlockRootAtTimestamp(newBeaconStateRoot); - BeaconChainProofs.StateRootProof memory stateRootProofStruct = _getStateRootProof(); + BeaconChainProofs.StateRootProof memory stateRootProofStruct = _getStateRootProof(); // pause the contract cheats.startPrank(pauser); @@ -938,24 +1104,24 @@ contract EigenPodTests is ProofParsing, EigenPodPausingConstants { cheats.stopPrank(); cheats.expectRevert(bytes("EigenPod.onlyWhenNotPaused: index is paused in EigenPodManager")); - newPod.verifyBalanceUpdates(0, validatorIndices, stateRootProofStruct, proofs, validatorFieldsArray); + newPod.verifyBalanceUpdates(0, validatorIndices, stateRootProofStruct, proofs, validatorFieldsArray); } function testFullWithdrawalProofWithWrongWithdrawalFields(bytes32[] memory wrongWithdrawalFields) public { Relayer relay = new Relayer(); - uint256 WITHDRAWAL_FIELD_TREE_HEIGHT = 2; + uint256 WITHDRAWAL_FIELD_TREE_HEIGHT = 2; setJSON("./src/test/test-data/fullWithdrawalProof_Latest.json"); BeaconChainProofs.WithdrawalProof memory proofs = _getWithdrawalProof(); bytes32 beaconStateRoot = getBeaconStateRoot(); - cheats.assume(wrongWithdrawalFields.length != 2 ** WITHDRAWAL_FIELD_TREE_HEIGHT); + cheats.assume(wrongWithdrawalFields.length != 2 ** WITHDRAWAL_FIELD_TREE_HEIGHT); validatorFields = getValidatorFields(); cheats.expectRevert(bytes("BeaconChainProofs.verifyWithdrawal: withdrawalFields has incorrect length")); relay.verifyWithdrawal(beaconStateRoot, wrongWithdrawalFields, proofs); } - - function testCheckThatHasRestakedIsSetToTrue() public returns (IEigenPod){ + + function testCheckThatHasRestakedIsSetToTrue() public returns (IEigenPod) { testStaking(); IEigenPod pod = eigenPodManager.getPod(podOwner); require(pod.hasRestaked() == true, "Pod should not be restaked"); @@ -967,7 +1133,7 @@ contract EigenPodTests is ProofParsing, EigenPodPausingConstants { cheats.startPrank(podOwner); cheats.expectRevert(bytes("EigenPod.hasNeverRestaked: restaking is enabled")); pod.activateRestaking(); - cheats.stopPrank(); + cheats.stopPrank(); } function testWithdrawBeforeRestakingWithM2Pods() external { @@ -975,7 +1141,7 @@ contract EigenPodTests is ProofParsing, EigenPodPausingConstants { cheats.startPrank(podOwner); cheats.expectRevert(bytes("EigenPod.hasNeverRestaked: restaking is enabled")); pod.withdrawBeforeRestaking(); - cheats.stopPrank(); + cheats.stopPrank(); } function testAttemptedWithdrawalAfterVerifyingWithdrawalCredentials() public { @@ -993,13 +1159,12 @@ contract EigenPodTests is ProofParsing, EigenPodPausingConstants { setJSON("./src/test/test-data/balanceUpdateProof_notOverCommitted_302913.json"); IEigenPod newPod = _testDeployAndVerifyNewEigenPod(podOwner, signature, depositDataRoot); - // ./solidityProofGen "BalanceUpdateProof" 302913 true 0 "data/withdrawal_proof_goerli/goerli_slot_6399999.json" "data/withdrawal_proof_goerli/goerli_slot_6399998.json" "balanceUpdateProof_overCommitted_302913.json" + // ./solidityProofGen "BalanceUpdateProof" 302913 true 0 "data/withdrawal_proof_goerli/goerli_slot_6399999.json" "data/withdrawal_proof_goerli/goerli_slot_6399998.json" "balanceUpdateProof_overCommitted_302913.json" setJSON("./src/test/test-data/balanceUpdateProof_overCommitted_302913.json"); // prove overcommitted balance cheats.warp(timestamp); _proveOverCommittedStake(newPod); - bytes32[][] memory validatorFieldsArray = new bytes32[][](1); validatorFieldsArray[0] = getValidatorFields(); @@ -1011,10 +1176,18 @@ contract EigenPodTests is ProofParsing, EigenPodPausingConstants { bytes32 newLatestBlockRoot = getLatestBlockRoot(); BeaconChainOracleMock(address(beaconChainOracle)).setOracleBlockRootAtTimestamp(newLatestBlockRoot); - BeaconChainProofs.StateRootProof memory stateRootProofStruct = _getStateRootProof(); + BeaconChainProofs.StateRootProof memory stateRootProofStruct = _getStateRootProof(); - cheats.expectRevert(bytes("EigenPod.verifyBalanceUpdate: Validators balance has already been updated for this timestamp")); - newPod.verifyBalanceUpdates(uint64(block.timestamp - 1), validatorIndices, stateRootProofStruct, proofs, validatorFieldsArray); + cheats.expectRevert( + bytes("EigenPod.verifyBalanceUpdate: Validators balance has already been updated for this timestamp") + ); + newPod.verifyBalanceUpdates( + uint64(block.timestamp - 1), + validatorIndices, + stateRootProofStruct, + proofs, + validatorFieldsArray + ); } function testProcessFullWithdrawalForLessThanMaxRestakedBalance(uint64 withdrawalAmount) public { @@ -1028,13 +1201,15 @@ contract EigenPodTests is ProofParsing, EigenPodPausingConstants { }); uint64 balanceBefore = podInternalFunctionTester.withdrawableRestakedExecutionLayerGwei(); podInternalFunctionTester.processFullWithdrawal(0, bytes32(0), 0, podOwner, withdrawalAmount, validatorInfo); - require(podInternalFunctionTester.withdrawableRestakedExecutionLayerGwei() - balanceBefore == withdrawalAmount, "withdrawableRestakedExecutionLayerGwei hasn't been updated correctly"); + require( + podInternalFunctionTester.withdrawableRestakedExecutionLayerGwei() - balanceBefore == withdrawalAmount, + "withdrawableRestakedExecutionLayerGwei hasn't been updated correctly" + ); } - function testWithdrawBeforeRestakingAfterRestaking() public { // ./solidityProofGen "ValidatorFieldsProof" 302913 true "data/withdrawal_proof_goerli/goerli_slot_6399999.json" "data/withdrawal_proof_goerli/goerli_slot_6399998.json" "withdrawal_credential_proof_510257.json" - setJSON("./src/test/test-data/withdrawal_credential_proof_302913.json"); + setJSON("./src/test/test-data/withdrawal_credential_proof_302913.json"); IEigenPod pod = _testDeployAndVerifyNewEigenPod(podOwner, signature, depositDataRoot); @@ -1055,7 +1230,7 @@ contract EigenPodTests is ProofParsing, EigenPodPausingConstants { } function testTryToActivateRestakingAfterHasRestakedIsSet() public { - cheats.startPrank(podOwner); + cheats.startPrank(podOwner); eigenPodManager.createPod(); cheats.stopPrank(); @@ -1065,7 +1240,6 @@ contract EigenPodTests is ProofParsing, EigenPodPausingConstants { cheats.startPrank(podOwner); cheats.expectRevert(bytes("EigenPod.hasNeverRestaked: restaking is enabled")); pod.activateRestaking(); - } function testTryToWithdrawBeforeRestakingAfterHasRestakedIsSet() public { @@ -1095,7 +1269,7 @@ contract EigenPodTests is ProofParsing, EigenPodPausingConstants { } bytes32[][] memory validatorFieldsArray = new bytes32[][](numValidators); for (uint256 index = 0; index < validatorFieldsArray.length; index++) { - validatorFieldsArray[index] = getValidatorFields(); + validatorFieldsArray[index] = getValidatorFields(); } BeaconChainProofs.StateRootProof memory stateRootProofStruct = _getStateRootProof(); @@ -1105,7 +1279,14 @@ contract EigenPodTests is ProofParsing, EigenPodPausingConstants { withdrawalFieldsArray[0] = withdrawalFields; cheats.expectRevert(bytes("EigenPod.verifyAndProcessWithdrawals: inputs must be same length")); - newPod.verifyAndProcessWithdrawals(0, stateRootProofStruct, withdrawalProofsArray, validatorFieldsProofArray, validatorFieldsArray, withdrawalFieldsArray); + newPod.verifyAndProcessWithdrawals( + 0, + stateRootProofStruct, + withdrawalProofsArray, + validatorFieldsProofArray, + validatorFieldsArray, + withdrawalFieldsArray + ); } function testProveWithdrawalFromBeforeLastWithdrawBeforeRestaking() external { @@ -1128,9 +1309,8 @@ contract EigenPodTests is ProofParsing, EigenPodPausingConstants { pod.withdrawBeforeRestaking(); cheats.stopPrank(); - bytes[] memory validatorFieldsProofArray = new bytes[](1); - validatorFieldsProofArray[0] = abi.encodePacked(getValidatorProof()); + validatorFieldsProofArray[0] = abi.encodePacked(getValidatorProof()); bytes32[][] memory validatorFieldsArray = new bytes32[][](1); validatorFieldsArray[0] = getValidatorFields(); @@ -1139,8 +1319,19 @@ contract EigenPodTests is ProofParsing, EigenPodPausingConstants { withdrawalFieldsArray[0] = withdrawalFields; cheats.warp(timestampOfWithdrawal); - cheats.expectRevert(bytes("EigenPod.proofIsForValidTimestamp: beacon chain proof must be for timestamp after mostRecentWithdrawalTimestamp")); - pod.verifyAndProcessWithdrawals(0, stateRootProofStruct, withdrawalProofsArray, validatorFieldsProofArray, validatorFieldsArray, withdrawalFieldsArray); + cheats.expectRevert( + bytes( + "EigenPod.proofIsForValidTimestamp: beacon chain proof must be for timestamp after mostRecentWithdrawalTimestamp" + ) + ); + pod.verifyAndProcessWithdrawals( + 0, + stateRootProofStruct, + withdrawalProofsArray, + validatorFieldsProofArray, + validatorFieldsArray, + withdrawalFieldsArray + ); } function testPodReceiveFallBack(uint256 amountETH) external { @@ -1155,13 +1346,13 @@ contract EigenPodTests is ProofParsing, EigenPodPausingConstants { } /** - * This is a regression test for a bug (EIG-14) found by Hexens. Lets say podOwner sends 32 ETH to the EigenPod, - * the nonBeaconChainETHBalanceWei increases by 32 ETH. podOwner calls withdrawBeforeRestaking, which - * will simply send the entire ETH balance (32 ETH) to the owner. The owner activates restaking, - * creates a validator and verifies the withdrawal credentials, receiving 32 ETH in shares. - * They can exit the validator, the pod gets the 32ETH and they can call withdrawNonBeaconChainETHBalanceWei - * And simply withdraw the 32ETH because nonBeaconChainETHBalanceWei is 32ETH. This was an issue because - * nonBeaconChainETHBalanceWei was never zeroed out in _processWithdrawalBeforeRestaking + * This is a regression test for a bug (EIG-14) found by Hexens. Lets say podOwner sends 32 ETH to the EigenPod, + * the nonBeaconChainETHBalanceWei increases by 32 ETH. podOwner calls withdrawBeforeRestaking, which + * will simply send the entire ETH balance (32 ETH) to the owner. The owner activates restaking, + * creates a validator and verifies the withdrawal credentials, receiving 32 ETH in shares. + * They can exit the validator, the pod gets the 32ETH and they can call withdrawNonBeaconChainETHBalanceWei + * And simply withdraw the 32ETH because nonBeaconChainETHBalanceWei is 32ETH. This was an issue because + * nonBeaconChainETHBalanceWei was never zeroed out in _processWithdrawalBeforeRestaking */ function testValidatorBalanceCannotBeRemovedFromPodViaNonBeaconChainETHBalanceWei() external { cheats.startPrank(podOwner); @@ -1170,10 +1361,10 @@ contract EigenPodTests is ProofParsing, EigenPodPausingConstants { emit EigenPodStaked(pubkey); eigenPodManager.stake{value: stakeAmount}(pubkey, signature, depositDataRoot); cheats.stopPrank(); - + uint256 amount = 32 ether; - cheats.store(address(newPod), bytes32(uint256(52)), bytes32(0)); + cheats.store(address(newPod), bytes32(uint256(52)), bytes32(0)); cheats.deal(address(this), amount); // simulate a withdrawal processed on the beacon chain, pod balance goes to 32 ETH Address.sendValue(payable(address(newPod)), amount); @@ -1184,15 +1375,15 @@ contract EigenPodTests is ProofParsing, EigenPodPausingConstants { require(newPod.hasRestaked() == false, "Pod should be restaked"); cheats.startPrank(podOwner); newPod.activateRestaking(); - cheats.stopPrank(); + cheats.stopPrank(); require(newPod.nonBeaconChainETHBalanceWei() == 0, "nonBeaconChainETHBalanceWei should be 32 ETH"); } /** - * Regression test for a bug that allowed balance updates to be made for withdrawn validators. Thus - * the validator's balance could be maliciously proven to be 0 before the validator themselves are - * able to prove their withdrawal. - */ + * Regression test for a bug that allowed balance updates to be made for withdrawn validators. Thus + * the validator's balance could be maliciously proven to be 0 before the validator themselves are + * able to prove their withdrawal. + */ function testBalanceUpdateMadeAfterWithdrawableEpochFails() external { //make initial deposit // ./solidityProofGen "BalanceUpdateProof" 302913 false 0 "data/withdrawal_proof_goerli/goerli_slot_6399999.json" "data/withdrawal_proof_goerli/goerli_slot_6399998.json" "balanceUpdateProof_notOverCommitted_302913.json" @@ -1213,14 +1404,20 @@ contract EigenPodTests is ProofParsing, EigenPodPausingConstants { proofs[0] = _getBalanceUpdateProof(); bytes32 newLatestBlockRoot = getLatestBlockRoot(); BeaconChainOracleMock(address(beaconChainOracle)).setOracleBlockRootAtTimestamp(newLatestBlockRoot); - BeaconChainProofs.StateRootProof memory stateRootProofStruct = _getStateRootProof(); - proofs[0].balanceRoot = bytes32(uint256(0)); + BeaconChainProofs.StateRootProof memory stateRootProofStruct = _getStateRootProof(); + proofs[0].balanceRoot = bytes32(uint256(0)); validatorFieldsArray[0][7] = bytes32(uint256(0)); cheats.warp(GOERLI_GENESIS_TIME + 1 days); uint64 oracleTimestamp = uint64(block.timestamp); cheats.expectRevert(bytes("EigenPod.verifyBalanceUpdate: validator is withdrawable but has not withdrawn")); - newPod.verifyBalanceUpdates(oracleTimestamp, validatorIndices, stateRootProofStruct, proofs, validatorFieldsArray); + newPod.verifyBalanceUpdates( + oracleTimestamp, + validatorIndices, + stateRootProofStruct, + proofs, + validatorFieldsArray + ); } function testWithdrawlBeforeRestakingFromNonPodOwnerAddress(address nonPodOwner) external { @@ -1231,13 +1428,10 @@ contract EigenPodTests is ProofParsing, EigenPodPausingConstants { emit EigenPodStaked(pubkey); eigenPodManager.stake{value: stakeAmount}(pubkey, signature, depositDataRoot); cheats.stopPrank(); - - cheats.store(address(newPod), bytes32(uint256(52)), bytes32(0)); - cheats.startPrank(nonPodOwner); cheats.expectRevert(bytes("EigenPod.onlyEigenPodOwner: not podOwner")); newPod.withdrawBeforeRestaking(); - cheats.stopPrank(); + cheats.stopPrank(); } function testDelayedWithdrawalIsCreatedByWithdrawBeforeRestaking() external { @@ -1247,7 +1441,7 @@ contract EigenPodTests is ProofParsing, EigenPodPausingConstants { emit EigenPodStaked(pubkey); eigenPodManager.stake{value: stakeAmount}(pubkey, signature, depositDataRoot); cheats.stopPrank(); - + uint256 amount = 32 ether; cheats.store(address(newPod), bytes32(uint256(52)), bytes32(0)); @@ -1262,7 +1456,6 @@ contract EigenPodTests is ProofParsing, EigenPodPausingConstants { require(_getLatestDelayedWithdrawalAmount(podOwner) == amount, "Payment amount should be stake amount"); require(newPod.nonBeaconChainETHBalanceWei() == 0, "nonBeaconChainETHBalanceWei should be 32 ETH"); - } function testFullWithdrawalAmounts(bytes32 pubkeyHash, uint64 withdrawalAmount) external { @@ -1273,12 +1466,22 @@ contract EigenPodTests is ProofParsing, EigenPodPausingConstants { mostRecentBalanceUpdateTimestamp: 0, status: IEigenPod.VALIDATOR_STATUS.ACTIVE }); - IEigenPod.VerifiedWithdrawal memory vw = podInternalFunctionTester.processFullWithdrawal(0, pubkeyHash, 0, podOwner, withdrawalAmount, validatorInfo); + IEigenPod.VerifiedWithdrawal memory vw = podInternalFunctionTester.processFullWithdrawal( + 0, + pubkeyHash, + 0, + podOwner, + withdrawalAmount, + validatorInfo + ); - if(withdrawalAmount > podInternalFunctionTester.MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR()){ - require(vw.amountToSendGwei == withdrawalAmount - podInternalFunctionTester.MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR(), "newAmount should be MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR"); - } - else{ + if (withdrawalAmount > podInternalFunctionTester.MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR()) { + require( + vw.amountToSendGwei == + withdrawalAmount - podInternalFunctionTester.MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR(), + "newAmount should be MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR" + ); + } else { require(vw.amountToSendGwei == 0, "newAmount should be withdrawalAmount"); } } @@ -1291,26 +1494,21 @@ contract EigenPodTests is ProofParsing, EigenPodPausingConstants { ) external { _deployInternalFunctionTester(); cheats.expectEmit(true, true, true, true, address(podInternalFunctionTester)); - emit PartialWithdrawalRedeemed( + emit PartialWithdrawalRedeemed(validatorIndex, withdrawalTimestamp, recipient, partialWithdrawalAmountGwei); + IEigenPod.VerifiedWithdrawal memory vw = podInternalFunctionTester.processPartialWithdrawal( validatorIndex, withdrawalTimestamp, recipient, partialWithdrawalAmountGwei ); - IEigenPod.VerifiedWithdrawal memory vw = podInternalFunctionTester.processPartialWithdrawal(validatorIndex, withdrawalTimestamp, recipient, partialWithdrawalAmountGwei); require(vw.amountToSendGwei == partialWithdrawalAmountGwei, "newAmount should be partialWithdrawalAmountGwei"); } function testRecoverTokens(uint256 amount, address recipient) external { - cheats.assume(amount > 0 && amount < 1e30); + cheats.assume(amount > 0 && amount < 1e30); IEigenPod pod = testDeployAndVerifyNewEigenPod(); - IERC20 randomToken = new ERC20PresetFixedSupply( - "rand", - "RAND", - 1e30, - address(this) - ); + IERC20 randomToken = new ERC20PresetFixedSupply("rand", "RAND", 1e30, address(this)); IERC20[] memory tokens = new IERC20[](1); tokens[0] = randomToken; @@ -1321,17 +1519,20 @@ contract EigenPodTests is ProofParsing, EigenPodPausingConstants { require(randomToken.balanceOf(address(pod)) == amount, "randomToken balance should be amount"); uint256 recipientBalanceBefore = randomToken.balanceOf(recipient); - + cheats.startPrank(podOwner); pod.recoverTokens(tokens, amounts, recipient); cheats.stopPrank(); - require(randomToken.balanceOf(address(recipient)) - recipientBalanceBefore == amount, "recipient should have received amount"); + require( + randomToken.balanceOf(address(recipient)) - recipientBalanceBefore == amount, + "recipient should have received amount" + ); } function testRecoverTokensMismatchedInputs() external { uint256 tokenListLen = 5; uint256 amountsToWithdrawLen = 2; - + IEigenPod pod = testDeployAndVerifyNewEigenPod(); IERC20[] memory tokens = new IERC20[](tokenListLen); @@ -1353,11 +1554,16 @@ contract EigenPodTests is ProofParsing, EigenPodPausingConstants { BeaconChainProofs.BalanceUpdateProof[] memory proofs = new BeaconChainProofs.BalanceUpdateProof[](1); proofs[0] = _getBalanceUpdateProof(); - bytes32 newLatestBlockRoot = getLatestBlockRoot(); BeaconChainOracleMock(address(beaconChainOracle)).setOracleBlockRootAtTimestamp(newLatestBlockRoot); - BeaconChainProofs.StateRootProof memory stateRootProofStruct = _getStateRootProof(); - newPod.verifyBalanceUpdates(uint64(block.timestamp), validatorIndices, stateRootProofStruct, proofs, validatorFieldsArray); + BeaconChainProofs.StateRootProof memory stateRootProofStruct = _getStateRootProof(); + newPod.verifyBalanceUpdates( + uint64(block.timestamp), + validatorIndices, + stateRootProofStruct, + proofs, + validatorFieldsArray + ); } function _proveUnderCommittedStake(IEigenPod newPod) internal { @@ -1372,14 +1578,18 @@ contract EigenPodTests is ProofParsing, EigenPodPausingConstants { bytes32 newLatestBlockRoot = getLatestBlockRoot(); BeaconChainOracleMock(address(beaconChainOracle)).setOracleBlockRootAtTimestamp(newLatestBlockRoot); - BeaconChainProofs.StateRootProof memory stateRootProofStruct = _getStateRootProof(); - - newPod.verifyBalanceUpdates(uint64(block.timestamp), validatorIndices, stateRootProofStruct, proofs, validatorFieldsArray); + BeaconChainProofs.StateRootProof memory stateRootProofStruct = _getStateRootProof(); + + newPod.verifyBalanceUpdates( + uint64(block.timestamp), + validatorIndices, + stateRootProofStruct, + proofs, + validatorFieldsArray + ); require(newPod.validatorPubkeyHashToInfo(getValidatorPubkeyHash()).status == IEigenPod.VALIDATOR_STATUS.ACTIVE); } - - function testStake(bytes calldata _pubkey, bytes calldata _signature, bytes32 _depositDataRoot) public { // should fail if no/wrong value is provided cheats.startPrank(podOwner); @@ -1398,13 +1608,11 @@ contract EigenPodTests is ProofParsing, EigenPodPausingConstants { } /// @notice Test that the Merkle proof verification fails when the proof length is 0 - function testVerifyInclusionSha256FailsForEmptyProof( - bytes32 root, - bytes32 leaf, - uint256 index - ) public { + function testVerifyInclusionSha256FailsForEmptyProof(bytes32 root, bytes32 leaf, uint256 index) public { bytes memory emptyProof = new bytes(0); - cheats.expectRevert(bytes("Merkle.processInclusionProofSha256: proof length should be a non-zero multiple of 32")); + cheats.expectRevert( + bytes("Merkle.processInclusionProofSha256: proof length should be a non-zero multiple of 32") + ); Merkle.verifyInclusionSha256(emptyProof, root, leaf, index); } @@ -1416,22 +1624,21 @@ contract EigenPodTests is ProofParsing, EigenPodPausingConstants { bytes memory proof ) public { cheats.assume(proof.length % 32 != 0); - cheats.expectRevert(bytes("Merkle.processInclusionProofSha256: proof length should be a non-zero multiple of 32")); + cheats.expectRevert( + bytes("Merkle.processInclusionProofSha256: proof length should be a non-zero multiple of 32") + ); Merkle.verifyInclusionSha256(proof, root, leaf, index); } /// @notice Test that the Merkle proof verification fails when the proof length is empty - function testVerifyInclusionKeccakFailsForEmptyProof( - bytes32 root, - bytes32 leaf, - uint256 index - ) public { + function testVerifyInclusionKeccakFailsForEmptyProof(bytes32 root, bytes32 leaf, uint256 index) public { bytes memory emptyProof = new bytes(0); - cheats.expectRevert(bytes("Merkle.processInclusionProofKeccak: proof length should be a non-zero multiple of 32")); + cheats.expectRevert( + bytes("Merkle.processInclusionProofKeccak: proof length should be a non-zero multiple of 32") + ); Merkle.verifyInclusionKeccak(emptyProof, root, leaf, index); } - /// @notice Test that the Merkle proof verification fails when the proof length is not a multiple of 32 function testVerifyInclusionKeccakFailsForNonMultipleOf32ProofLength( bytes32 root, @@ -1440,12 +1647,18 @@ contract EigenPodTests is ProofParsing, EigenPodPausingConstants { bytes memory proof ) public { cheats.assume(proof.length % 32 != 0); - cheats.expectRevert(bytes("Merkle.processInclusionProofKeccak: proof length should be a non-zero multiple of 32")); + cheats.expectRevert( + bytes("Merkle.processInclusionProofKeccak: proof length should be a non-zero multiple of 32") + ); Merkle.verifyInclusionKeccak(proof, root, leaf, index); } // verifies that the `numPod` variable increments correctly on a succesful call to the `EigenPod.stake` function - function test_incrementNumPodsOnStake(bytes calldata _pubkey, bytes calldata _signature, bytes32 _depositDataRoot) public { + function test_incrementNumPodsOnStake( + bytes calldata _pubkey, + bytes calldata _signature, + bytes32 _depositDataRoot + ) public { uint256 numPodsBefore = EigenPodManager(address(eigenPodManager)).numPods(); testStake(_pubkey, _signature, _depositDataRoot); uint256 numPodsAfter = EigenPodManager(address(eigenPodManager)).numPods(); @@ -1453,7 +1666,11 @@ contract EigenPodTests is ProofParsing, EigenPodPausingConstants { } // verifies that the `maxPods` variable is enforced on the `EigenPod.stake` function - function test_maxPodsEnforcementOnStake(bytes calldata _pubkey, bytes calldata _signature, bytes32 _depositDataRoot) public { + function test_maxPodsEnforcementOnStake( + bytes calldata _pubkey, + bytes calldata _signature, + bytes32 _depositDataRoot + ) public { // set pod limit to current number of pods cheats.startPrank(unpauser); EigenPodManager(address(eigenPodManager)).setMaxPods(EigenPodManager(address(eigenPodManager)).numPods()); @@ -1540,7 +1757,7 @@ contract EigenPodTests is ProofParsing, EigenPodPausingConstants { cheats.stopPrank(); } -/* TODO: reimplement similar tests + /* TODO: reimplement similar tests function testQueueBeaconChainETHWithdrawalWithoutProvingFullWithdrawal() external { // ./solidityProofGen "ValidatorFieldsProof" 302913 true "data/withdrawal_proof_goerli/goerli_block_header_6399998.json" "data/withdrawal_proof_goerli/goerli_slot_6399998.json" "withdrawal_credential_proof_302913.json" setJSON("./src/test/test-data/withdrawal_credential_proof_302913.json"); setJSON("./src/test/test-data/withdrawal_credential_proof_302913.json"); @@ -1567,33 +1784,45 @@ contract EigenPodTests is ProofParsing, EigenPodPausingConstants { "withdrawableRestakedExecutionLayerGwei not decremented correctly"); } */ - function _verifyEigenPodBalanceSharesInvariant(address podowner, IEigenPod pod, bytes32 validatorPubkeyHash) internal view { + function _verifyEigenPodBalanceSharesInvariant( + address podowner, + IEigenPod pod, + bytes32 validatorPubkeyHash + ) internal view { int256 shares = eigenPodManager.podOwnerShares(podowner); uint64 withdrawableRestakedExecutionLayerGwei = pod.withdrawableRestakedExecutionLayerGwei(); - + EigenPod.ValidatorInfo memory info = pod.validatorPubkeyHashToInfo(validatorPubkeyHash); uint64 validatorBalanceGwei = info.restakedBalanceGwei; - require(shares/int256(GWEI_TO_WEI) == int256(uint256(validatorBalanceGwei)) + int256(uint256(withdrawableRestakedExecutionLayerGwei)), - "EigenPod invariant violated: sharesInSM != withdrawableRestakedExecutionLayerGwei"); + require( + shares / int256(GWEI_TO_WEI) == + int256(uint256(validatorBalanceGwei)) + int256(uint256(withdrawableRestakedExecutionLayerGwei)), + "EigenPod invariant violated: sharesInSM != withdrawableRestakedExecutionLayerGwei" + ); } - function _proveWithdrawalForPod(IEigenPod newPod) internal returns(IEigenPod) { + function _proveWithdrawalForPod(IEigenPod newPod) internal returns (IEigenPod) { BeaconChainOracleMock(address(beaconChainOracle)).setOracleBlockRootAtTimestamp(getLatestBlockRoot()); uint64 restakedExecutionLayerGweiBefore = newPod.withdrawableRestakedExecutionLayerGwei(); - + withdrawalFields = getWithdrawalFields(); - uint64 withdrawalAmountGwei = Endian.fromLittleEndianUint64(withdrawalFields[BeaconChainProofs.WITHDRAWAL_VALIDATOR_AMOUNT_INDEX]); + uint64 withdrawalAmountGwei = Endian.fromLittleEndianUint64( + withdrawalFields[BeaconChainProofs.WITHDRAWAL_VALIDATOR_AMOUNT_INDEX] + ); - uint64 leftOverBalanceWEI = uint64(withdrawalAmountGwei - newPod.MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR()) * uint64(GWEI_TO_WEI); + uint64 leftOverBalanceWEI = uint64(withdrawalAmountGwei - newPod.MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR()) * + uint64(GWEI_TO_WEI); cheats.deal(address(newPod), leftOverBalanceWEI); emit log_named_uint("leftOverBalanceWEI", leftOverBalanceWEI); emit log_named_uint("address(newPod)", address(newPod).balance); emit log_named_uint("withdrawalAmountGwei", withdrawalAmountGwei); - + uint256 delayedWithdrawalRouterContractBalanceBefore = address(delayedWithdrawalRouter).balance; { - BeaconChainProofs.WithdrawalProof[] memory withdrawalProofsArray = new BeaconChainProofs.WithdrawalProof[](1); + BeaconChainProofs.WithdrawalProof[] memory withdrawalProofsArray = new BeaconChainProofs.WithdrawalProof[]( + 1 + ); withdrawalProofsArray[0] = _getWithdrawalProof(); bytes[] memory validatorFieldsProofArray = new bytes[](1); validatorFieldsProofArray[0] = abi.encodePacked(getValidatorProof()); @@ -1604,24 +1833,46 @@ contract EigenPodTests is ProofParsing, EigenPodPausingConstants { BeaconChainProofs.StateRootProof memory stateRootProofStruct = _getStateRootProof(); - newPod.verifyAndProcessWithdrawals(0, stateRootProofStruct, withdrawalProofsArray, validatorFieldsProofArray, validatorFieldsArray, withdrawalFieldsArray); + newPod.verifyAndProcessWithdrawals( + 0, + stateRootProofStruct, + withdrawalProofsArray, + validatorFieldsProofArray, + validatorFieldsArray, + withdrawalFieldsArray + ); } - require(newPod.withdrawableRestakedExecutionLayerGwei() - restakedExecutionLayerGweiBefore == newPod.MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR(), - "restakedExecutionLayerGwei has not been incremented correctly"); - require(address(delayedWithdrawalRouter).balance - delayedWithdrawalRouterContractBalanceBefore == leftOverBalanceWEI, - "pod delayed withdrawal balance hasn't been updated correctly"); - require(newPod.validatorPubkeyHashToInfo(getValidatorPubkeyHash()).restakedBalanceGwei == 0, "balance not reset correctly"); - + require( + newPod.withdrawableRestakedExecutionLayerGwei() - restakedExecutionLayerGweiBefore == + newPod.MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR(), + "restakedExecutionLayerGwei has not been incremented correctly" + ); + require( + address(delayedWithdrawalRouter).balance - delayedWithdrawalRouterContractBalanceBefore == + leftOverBalanceWEI, + "pod delayed withdrawal balance hasn't been updated correctly" + ); + require( + newPod.validatorPubkeyHashToInfo(getValidatorPubkeyHash()).restakedBalanceGwei == 0, + "balance not reset correctly" + ); + cheats.roll(block.number + WITHDRAWAL_DELAY_BLOCKS + 1); uint256 podOwnerBalanceBefore = address(podOwner).balance; delayedWithdrawalRouter.claimDelayedWithdrawals(podOwner, 1); - require(address(podOwner).balance - podOwnerBalanceBefore == leftOverBalanceWEI, "Pod owner balance hasn't been updated correctly"); + require( + address(podOwner).balance - podOwnerBalanceBefore == leftOverBalanceWEI, + "Pod owner balance hasn't been updated correctly" + ); return newPod; } // simply tries to register 'sender' as a delegate, setting their 'DelegationTerms' contract in DelegationManager to 'dt' // verifies that the storage of DelegationManager contract is updated appropriately - function _testRegisterAsOperator(address sender, IDelegationManager.OperatorDetails memory operatorDetails) internal { + function _testRegisterAsOperator( + address sender, + IDelegationManager.OperatorDetails memory operatorDetails + ) internal { cheats.startPrank(sender); string memory emptyStringForMetadataURI; delegation.registerAsOperator(operatorDetails, emptyStringForMetadataURI); @@ -1638,8 +1889,7 @@ contract EigenPodTests is ProofParsing, EigenPodPausingConstants { function _testDelegateToOperator(address sender, address operator) internal { //delegator-specific information - (IStrategy[] memory delegateStrategies, uint256[] memory delegateShares) = - strategyManager.getDeposits(sender); + (IStrategy[] memory delegateStrategies, uint256[] memory delegateShares) = strategyManager.getDeposits(sender); uint256 numStrats = delegateShares.length; assertTrue(numStrats > 0, "_testDelegateToOperator: delegating from address with no deposits"); @@ -1657,10 +1907,7 @@ contract EigenPodTests is ProofParsing, EigenPodPausingConstants { delegation.delegatedTo(sender) == operator, "_testDelegateToOperator: delegated address not set appropriately" ); - assertTrue( - delegation.isDelegated(sender), - "_testDelegateToOperator: delegated status not set appropriately" - ); + assertTrue(delegation.isDelegated(sender), "_testDelegateToOperator: delegated status not set appropriately"); for (uint256 i = 0; i < numStrats; ++i) { uint256 operatorSharesBefore = inititalSharesInStrats[i]; @@ -1671,9 +1918,8 @@ contract EigenPodTests is ProofParsing, EigenPodPausingConstants { ); } } - function _testDelegation(address operator, address staker) - internal - { + + function _testDelegation(address operator, address staker) internal { if (!delegation.isOperator(operator)) { IDelegationManager.OperatorDetails memory operatorDetails = IDelegationManager.OperatorDetails({ earningsReceiver: operator, @@ -1690,13 +1936,14 @@ contract EigenPodTests is ProofParsing, EigenPodPausingConstants { IStrategy[] memory updatedStrategies; uint256[] memory updatedShares; - (updatedStrategies, updatedShares) = - strategyManager.getDeposits(staker); + (updatedStrategies, updatedShares) = strategyManager.getDeposits(staker); } - function _testDeployAndVerifyNewEigenPod(address _podOwner, bytes memory _signature, bytes32 _depositDataRoot) - internal returns (IEigenPod) - { + function _testDeployAndVerifyNewEigenPod( + address _podOwner, + bytes memory _signature, + bytes32 _depositDataRoot + ) internal returns (IEigenPod) { // (beaconStateRoot, beaconStateMerkleProofForValidators, validatorContainerFields, validatorMerkleProof, validatorTreeRoot, validatorRoot) = // getInitialDepositProof(validatorIndex); @@ -1714,7 +1961,7 @@ contract EigenPodTests is ProofParsing, EigenPodPausingConstants { return _verifyWithdrawalCredentials(newPod, _podOwner); } - function _verifyWithdrawalCredentials(IEigenPod newPod, address _podOwner) internal returns(IEigenPod) { + function _verifyWithdrawalCredentials(IEigenPod newPod, address _podOwner) internal returns (IEigenPod) { uint64 timestamp = 0; // cheats.expectEmit(true, true, true, true, address(newPod)); // emit ValidatorRestaked(validatorIndex); @@ -1734,26 +1981,38 @@ contract EigenPodTests is ProofParsing, EigenPodPausingConstants { cheats.startPrank(_podOwner); cheats.warp(timestamp); - if(newPod.hasRestaked() == false){ + if (newPod.hasRestaked() == false) { newPod.activateRestaking(); } //set the oracle block root _setOracleBlockRoot(); - emit log_named_bytes32("restaking activated", BeaconChainOracleMock(address(beaconChainOracle)).mockBeaconChainStateRoot()); + emit log_named_bytes32( + "restaking activated", + BeaconChainOracleMock(address(beaconChainOracle)).mockBeaconChainStateRoot() + ); cheats.warp(timestamp += 1); - newPod.verifyWithdrawalCredentials(timestamp, stateRootProofStruct, validatorIndices, proofsArray, validatorFieldsArray); + newPod.verifyWithdrawalCredentials( + timestamp, + stateRootProofStruct, + validatorIndices, + proofsArray, + validatorFieldsArray + ); cheats.stopPrank(); int256 beaconChainETHSharesAfter = eigenPodManager.podOwnerShares(_podOwner); - uint256 effectiveBalance = uint256(_calculateRestakedBalanceGwei(uint64(stakeAmount/GWEI_TO_WEI))) * GWEI_TO_WEI; - require((beaconChainETHSharesAfter - beaconChainETHSharesBefore) == int256(effectiveBalance), - "eigenPodManager shares not updated correctly"); + uint256 effectiveBalance = uint256(_calculateRestakedBalanceGwei(uint64(stakeAmount / GWEI_TO_WEI))) * + GWEI_TO_WEI; + require( + (beaconChainETHSharesAfter - beaconChainETHSharesBefore) == int256(effectiveBalance), + "eigenPodManager shares not updated correctly" + ); return newPod; } -/* TODO: reimplement similar tests + /* TODO: reimplement similar tests function _testQueueWithdrawal( address _podOwner, uint256 amountWei @@ -1772,23 +2031,21 @@ contract EigenPodTests is ProofParsing, EigenPodPausingConstants { } */ function _getLatestDelayedWithdrawalAmount(address recipient) internal view returns (uint256) { - return delayedWithdrawalRouter.userDelayedWithdrawalByIndex(recipient, delayedWithdrawalRouter.userWithdrawalsLength(recipient) - 1).amount; + return + delayedWithdrawalRouter + .userDelayedWithdrawalByIndex(recipient, delayedWithdrawalRouter.userWithdrawalsLength(recipient) - 1) + .amount; } function _getStateRootProof() internal returns (BeaconChainProofs.StateRootProof memory) { - - return BeaconChainProofs.StateRootProof( - getBeaconStateRoot(), - abi.encodePacked(getStateRootProof()) - ); + return BeaconChainProofs.StateRootProof(getBeaconStateRoot(), abi.encodePacked(getStateRootProof())); } function _getBalanceUpdateProof() internal returns (BeaconChainProofs.BalanceUpdateProof memory) { - bytes32 balanceRoot = getBalanceRoot(); BeaconChainProofs.BalanceUpdateProof memory proofs = BeaconChainProofs.BalanceUpdateProof( abi.encodePacked(getValidatorBalanceProof()), - abi.encodePacked(getWithdrawalCredentialProof()), //technically this is to verify validator pubkey in the validator fields, but the WC proof is effectively the same so we use it here again. + abi.encodePacked(getWithdrawalCredentialProof()), //technically this is to verify validator pubkey in the validator fields, but the WC proof is effectively the same so we use it here again. balanceRoot ); @@ -1796,7 +2053,7 @@ contract EigenPodTests is ProofParsing, EigenPodPausingConstants { } /// @notice this function just generates a valid proof so that we can test other functionalities of the withdrawal flow - function _getWithdrawalProof() internal returns(BeaconChainProofs.WithdrawalProof memory) { + function _getWithdrawalProof() internal returns (BeaconChainProofs.WithdrawalProof memory) { IEigenPod newPod = eigenPodManager.getPod(podOwner); //make initial deposit @@ -1806,28 +2063,27 @@ contract EigenPodTests is ProofParsing, EigenPodPausingConstants { eigenPodManager.stake{value: stakeAmount}(pubkey, signature, depositDataRoot); cheats.stopPrank(); - { bytes32 blockRoot = getBlockRoot(); bytes32 slotRoot = getSlotRoot(); bytes32 timestampRoot = getTimestampRoot(); bytes32 executionPayloadRoot = getExecutionPayloadRoot(); - return BeaconChainProofs.WithdrawalProof( - abi.encodePacked(getWithdrawalProof()), - abi.encodePacked(getSlotProof()), - abi.encodePacked(getExecutionPayloadProof()), - abi.encodePacked(getTimestampProof()), - abi.encodePacked(getHistoricalSummaryProof()), - uint64(getBlockRootIndex()), - uint64(getHistoricalSummaryIndex()), - uint64(getWithdrawalIndex()), - blockRoot, - slotRoot, - timestampRoot, - executionPayloadRoot - ); - + return + BeaconChainProofs.WithdrawalProof( + abi.encodePacked(getWithdrawalProof()), + abi.encodePacked(getSlotProof()), + abi.encodePacked(getExecutionPayloadProof()), + abi.encodePacked(getTimestampProof()), + abi.encodePacked(getHistoricalSummaryProof()), + uint64(getBlockRootIndex()), + uint64(getHistoricalSummaryIndex()), + uint64(getWithdrawalIndex()), + blockRoot, + slotRoot, + timestampRoot, + executionPayloadRoot + ); } } @@ -1843,16 +2099,16 @@ contract EigenPodTests is ProofParsing, EigenPodPausingConstants { emit log_named_uint("effectiveBalance", effectiveBalance); } - function _calculateRestakedBalanceGwei(uint64 amountGwei) internal view returns (uint64){ + function _calculateRestakedBalanceGwei(uint64 amountGwei) internal view returns (uint64) { if (amountGwei <= RESTAKED_BALANCE_OFFSET_GWEI) { return 0; } /** - * calculates the "floor" of amountGwei - RESTAKED_BALANCE_OFFSET_GWEI. By using integer division - * (dividing by GWEI_TO_WEI = 1e9) and then multiplying by 1e9, we effectively "round down" amountGwei to - * the nearest ETH, effectively calculating the floor of amountGwei. - */ - uint64 effectiveBalanceGwei = uint64((amountGwei - RESTAKED_BALANCE_OFFSET_GWEI) / GWEI_TO_WEI * GWEI_TO_WEI); + * calculates the "floor" of amountGwei - RESTAKED_BALANCE_OFFSET_GWEI. By using integer division + * (dividing by GWEI_TO_WEI = 1e9) and then multiplying by 1e9, we effectively "round down" amountGwei to + * the nearest ETH, effectively calculating the floor of amountGwei. + */ + uint64 effectiveBalanceGwei = uint64(((amountGwei - RESTAKED_BALANCE_OFFSET_GWEI) / GWEI_TO_WEI) * GWEI_TO_WEI); return uint64(MathUpgradeable.min(MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR, effectiveBalanceGwei)); } @@ -1862,20 +2118,17 @@ contract EigenPodTests is ProofParsing, EigenPodPausingConstants { function _deployInternalFunctionTester() internal { podInternalFunctionTester = new EPInternalFunctions( - ethPOSDeposit, - delayedWithdrawalRouter, - IEigenPodManager(podManagerAddress), - MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR, - RESTAKED_BALANCE_OFFSET_GWEI, - GOERLI_GENESIS_TIME + ethPOSDeposit, + delayedWithdrawalRouter, + IEigenPodManager(podManagerAddress), + MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR, + RESTAKED_BALANCE_OFFSET_GWEI, + GOERLI_GENESIS_TIME ); } +} - - } - - - contract Relayer is Test { +contract Relayer is Test { function verifyWithdrawal( bytes32 beaconStateRoot, bytes32[] calldata withdrawalFields, @@ -1883,4 +2136,4 @@ contract EigenPodTests is ProofParsing, EigenPodPausingConstants { ) public view { BeaconChainProofs.verifyWithdrawal(beaconStateRoot, withdrawalFields, proofs); } - } \ No newline at end of file +} diff --git a/src/test/Withdrawals.t.sol b/src/test/Withdrawals.t.sol index 91d54e9a8..73f2dc106 100644 --- a/src/test/Withdrawals.t.sol +++ b/src/test/Withdrawals.t.sol @@ -6,10 +6,9 @@ import "../test/EigenLayerTestHelper.t.sol"; import "./mocks/MiddlewareRegistryMock.sol"; import "./mocks/ServiceManagerMock.sol"; -import "./harnesses/StakeRegistryHarness.sol"; +import "./mocks/StakeRegistryStub.sol"; contract WithdrawalTests is EigenLayerTestHelper { - // packed info used to help handle stack-too-deep errors struct DataForTestWithdrawal { IStrategy[] delegatorStrategies; @@ -20,8 +19,7 @@ contract WithdrawalTests is EigenLayerTestHelper { address public registryCoordinator = address(uint160(uint256(keccak256("registryCoordinator")))); ServiceManagerMock public serviceManager; - StakeRegistryHarness public stakeRegistry; - StakeRegistryHarness public stakeRegistryImplementation; + StakeRegistryStub public stakeRegistry; MiddlewareRegistryMock public generalReg1; ServiceManagerMock public generalServiceManager1; @@ -40,14 +38,7 @@ contract WithdrawalTests is EigenLayerTestHelper { function initializeMiddlewares() public { serviceManager = new ServiceManagerMock(slasher); - stakeRegistry = StakeRegistryHarness( - address(new TransparentUpgradeableProxy(address(emptyContract), address(eigenLayerProxyAdmin), "")) - ); - stakeRegistryImplementation = new StakeRegistryHarness( - IRegistryCoordinator(registryCoordinator), - strategyManager, - serviceManager - ); + stakeRegistry = new StakeRegistryStub(); { uint96 multiplier = 1e18; @@ -58,7 +49,7 @@ contract WithdrawalTests is EigenLayerTestHelper { // _quorumBips[1] = 4000; // IVoteWeigher.StrategyAndWeightingMultiplier[] memory ethStratsAndMultipliers = // new IVoteWeigher.StrategyAndWeightingMultiplier[](1); - // ethStratsAndMultipliers[0].strategy = wethStrat; + // ethStratsAndMultipliers[0].strategy = wethStrat; // ethStratsAndMultipliers[0].multiplier = multiplier; // IVoteWeigher.StrategyAndWeightingMultiplier[] memory eigenStratsAndMultipliers = // new IVoteWeigher.StrategyAndWeightingMultiplier[](1); @@ -70,12 +61,14 @@ contract WithdrawalTests is EigenLayerTestHelper { // setup the dummy minimum stake for quorum uint96[] memory minimumStakeForQuorum = new uint96[](_NUMBER_OF_QUORUMS); for (uint256 i = 0; i < minimumStakeForQuorum.length; i++) { - minimumStakeForQuorum[i] = uint96(i+1); + minimumStakeForQuorum[i] = uint96(i + 1); } // setup the dummy quorum strategies - IVoteWeigher.StrategyAndWeightingMultiplier[][] memory quorumStrategiesConsideredAndMultipliers = - new IVoteWeigher.StrategyAndWeightingMultiplier[][](2); + IVoteWeigher.StrategyAndWeightingMultiplier[][] + memory quorumStrategiesConsideredAndMultipliers = new IVoteWeigher.StrategyAndWeightingMultiplier[][]( + 2 + ); quorumStrategiesConsideredAndMultipliers[0] = new IVoteWeigher.StrategyAndWeightingMultiplier[](1); quorumStrategiesConsideredAndMultipliers[0][0] = IVoteWeigher.StrategyAndWeightingMultiplier( wethStrat, @@ -87,76 +80,54 @@ contract WithdrawalTests is EigenLayerTestHelper { multiplier ); - eigenLayerProxyAdmin.upgradeAndCall( - TransparentUpgradeableProxy(payable(address(stakeRegistry))), - address(stakeRegistryImplementation), - abi.encodeWithSelector(StakeRegistry.initialize.selector, minimumStakeForQuorum, quorumStrategiesConsideredAndMultipliers) - ); cheats.stopPrank(); - } } function initializeGeneralMiddlewares() public { generalServiceManager1 = new ServiceManagerMock(slasher); - generalReg1 = new MiddlewareRegistryMock( - generalServiceManager1, - strategyManager - ); - + generalReg1 = new MiddlewareRegistryMock(generalServiceManager1, strategyManager); + generalServiceManager2 = new ServiceManagerMock(slasher); - generalReg2 = new MiddlewareRegistryMock( - generalServiceManager2, - strategyManager - ); + generalReg2 = new MiddlewareRegistryMock(generalServiceManager2, strategyManager); } //This function helps with stack too deep issues with "testWithdrawal" test function testWithdrawalWrapper( - address operator, - address depositor, - address withdrawer, - uint96 ethAmount, - uint96 eigenAmount, - bool withdrawAsTokens, - bool RANDAO - ) - public - fuzzedAddress(operator) - fuzzedAddress(depositor) - fuzzedAddress(withdrawer) - { - cheats.assume(depositor != operator); - cheats.assume(ethAmount >= 1 && ethAmount <= 1e18); - cheats.assume(eigenAmount >= 1 && eigenAmount <= 1e18); + address operator, + address depositor, + address withdrawer, + uint96 ethAmount, + uint96 eigenAmount, + bool withdrawAsTokens, + bool RANDAO + ) public fuzzedAddress(operator) fuzzedAddress(depositor) fuzzedAddress(withdrawer) { + cheats.assume(depositor != operator); + cheats.assume(ethAmount >= 1 && ethAmount <= 1e18); + cheats.assume(eigenAmount >= 1 && eigenAmount <= 1e18); - initializeGeneralMiddlewares(); - - if(RANDAO) { - _testWithdrawalAndDeregistration(operator, depositor, withdrawer, ethAmount, eigenAmount, withdrawAsTokens); - } - else{ - _testWithdrawalWithStakeUpdate(operator, depositor, withdrawer, ethAmount, eigenAmount, withdrawAsTokens); - } + initializeGeneralMiddlewares(); + if (RANDAO) { + _testWithdrawalAndDeregistration(operator, depositor, withdrawer, ethAmount, eigenAmount, withdrawAsTokens); + } else { + _testWithdrawalWithStakeUpdate(operator, depositor, withdrawer, ethAmount, eigenAmount, withdrawAsTokens); } + } /// @notice test staker's ability to undelegate/withdraw from an operator. /// @param operator is the operator being delegated to. /// @param depositor is the staker delegating stake to the operator. function _testWithdrawalAndDeregistration( - address operator, - address depositor, - address withdrawer, - uint96 ethAmount, - uint96 eigenAmount, - bool withdrawAsTokens - ) - internal - { - + address operator, + address depositor, + address withdrawer, + uint96 ethAmount, + uint96 eigenAmount, + bool withdrawAsTokens + ) internal { _initiateDelegation(operator, depositor, ethAmount, eigenAmount); cheats.startPrank(operator); @@ -173,8 +144,9 @@ contract WithdrawalTests is EigenLayerTestHelper { // scoped block to deal with stack-too-deep issues { //delegator-specific information - (IStrategy[] memory delegatorStrategies, uint256[] memory delegatorShares) = - strategyManager.getDeposits(depositor); + (IStrategy[] memory delegatorStrategies, uint256[] memory delegatorShares) = strategyManager.getDeposits( + depositor + ); dataForTestWithdrawal.delegatorStrategies = delegatorStrategies; dataForTestWithdrawal.delegatorShares = delegatorShares; dataForTestWithdrawal.withdrawer = withdrawer; @@ -203,18 +175,18 @@ contract WithdrawalTests is EigenLayerTestHelper { withdrawer ); uint32 queuedWithdrawalBlock = uint32(block.number); - + //now withdrawal block time is before deregistration cheats.warp(uint32(block.timestamp) + 2 days); cheats.roll(uint32(block.timestamp) + 2 days); - + generalReg1.deregisterOperator(operator); { //warp past the serve until time, which is 3 days from the beginning. THis puts us at 4 days past that point cheats.warp(uint32(block.timestamp) + 4 days); cheats.roll(uint32(block.timestamp) + 4 days); - uint256 middlewareTimeIndex = 1; + uint256 middlewareTimeIndex = 1; if (withdrawAsTokens) { _testCompleteQueuedWithdrawalTokens( depositor, @@ -247,15 +219,13 @@ contract WithdrawalTests is EigenLayerTestHelper { /// @param operator is the operator being delegated to. /// @param depositor is the staker delegating stake to the operator. function _testWithdrawalWithStakeUpdate( - address operator, - address depositor, - address withdrawer, - uint96 ethAmount, - uint96 eigenAmount, - bool withdrawAsTokens - ) - public - { + address operator, + address depositor, + address withdrawer, + uint96 ethAmount, + uint96 eigenAmount, + bool withdrawAsTokens + ) public { _initiateDelegation(operator, depositor, ethAmount, eigenAmount); cheats.startPrank(operator); @@ -283,8 +253,9 @@ contract WithdrawalTests is EigenLayerTestHelper { // scoped block to deal with stack-too-deep issues { //delegator-specific information - (IStrategy[] memory delegatorStrategies, uint256[] memory delegatorShares) = - strategyManager.getDeposits(depositor); + (IStrategy[] memory delegatorStrategies, uint256[] memory delegatorShares) = strategyManager.getDeposits( + depositor + ); dataForTestWithdrawal.delegatorStrategies = delegatorStrategies; dataForTestWithdrawal.delegatorShares = delegatorShares; dataForTestWithdrawal.withdrawer = withdrawer; @@ -313,12 +284,11 @@ contract WithdrawalTests is EigenLayerTestHelper { dataForTestWithdrawal.withdrawer ); uint32 queuedWithdrawalBlock = uint32(block.number); - + //now withdrawal block time is before deregistration cheats.warp(uint32(block.timestamp) + 2 days); cheats.roll(uint32(block.number) + 2); - uint256 prevElement = uint256(uint160(address(generalServiceManager2))); generalReg1.propagateStakeUpdate(operator, uint32(block.number), prevElement); @@ -327,13 +297,13 @@ contract WithdrawalTests is EigenLayerTestHelper { prevElement = uint256(uint160(address(generalServiceManager1))); generalReg2.propagateStakeUpdate(operator, uint32(block.number), prevElement); - + { //warp past the serve until time, which is 3 days from the beginning. THis puts us at 4 days past that point cheats.warp(uint32(block.timestamp) + 4 days); cheats.roll(uint32(block.number) + 4); - uint256 middlewareTimeIndex = 3; + uint256 middlewareTimeIndex = 3; if (withdrawAsTokens) { _testCompleteQueuedWithdrawalTokens( depositor, @@ -366,18 +336,13 @@ contract WithdrawalTests is EigenLayerTestHelper { // @param operator is the operator being delegated to. // @param staker is the staker delegating stake to the operator. function testRedelegateAfterWithdrawal( - address operator, - address depositor, - address withdrawer, - uint96 ethAmount, - uint96 eigenAmount, - bool withdrawAsShares - ) - public - fuzzedAddress(operator) - fuzzedAddress(depositor) - fuzzedAddress(withdrawer) - { + address operator, + address depositor, + address withdrawer, + uint96 ethAmount, + uint96 eigenAmount, + bool withdrawAsShares + ) public fuzzedAddress(operator) fuzzedAddress(depositor) fuzzedAddress(withdrawer) { cheats.assume(depositor != operator); //this function performs delegation and subsequent withdrawal testWithdrawalWrapper(operator, depositor, withdrawer, ethAmount, eigenAmount, withdrawAsShares, true); @@ -405,7 +370,7 @@ contract WithdrawalTests is EigenLayerTestHelper { // testDelegation(operator, staker, ethAmount, eigenAmount); // { - // address slashingContract = slasher.owner(); + // address slashingContract = slasher.owner(); // cheats.startPrank(operator); // slasher.optIntoSlashing(address(slashingContract)); @@ -434,33 +399,25 @@ contract WithdrawalTests is EigenLayerTestHelper { // _testQueueWithdrawal(staker, strategyIndexes, updatedStrategies, updatedShares, staker); // } - // Helper function to begin a delegation - function _initiateDelegation(address operator, address staker, uint96 ethAmount, uint96 eigenAmount) - internal - fuzzedAddress(operator) - fuzzedAddress(staker) - fuzzedAmounts(ethAmount, eigenAmount) - { + function _initiateDelegation( + address operator, + address staker, + uint96 ethAmount, + uint96 eigenAmount + ) internal fuzzedAddress(operator) fuzzedAddress(staker) fuzzedAmounts(ethAmount, eigenAmount) { cheats.assume(staker != operator); // base strategy will revert if these amounts are too small on first deposit cheats.assume(ethAmount >= 1); cheats.assume(eigenAmount >= 2); - - // Set weights ahead of the helper function call - bytes memory quorumNumbers = new bytes(2); - quorumNumbers[0] = bytes1(uint8(0)); - quorumNumbers[0] = bytes1(uint8(1)); - stakeRegistry.setOperatorWeight(0, operator, ethAmount); - stakeRegistry.setOperatorWeight(1, operator, eigenAmount); - stakeRegistry.registerOperatorNonCoordinator(operator, defaultOperatorId, quorumNumbers); + _testDelegation(operator, staker, ethAmount, eigenAmount, stakeRegistry); } - modifier fuzzedAmounts(uint256 ethAmount, uint256 eigenAmount) { cheats.assume(ethAmount >= 0 && ethAmount <= 1e18); cheats.assume(eigenAmount >= 0 && eigenAmount <= 1e18); _; } -} \ No newline at end of file +} + diff --git a/src/test/ffi/BLSPubKeyCompendiumFFI.t.sol b/src/test/ffi/BLSPubKeyCompendiumFFI.t.sol deleted file mode 100644 index c44d7ffe9..000000000 --- a/src/test/ffi/BLSPubKeyCompendiumFFI.t.sol +++ /dev/null @@ -1,46 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity =0.8.12; - -import "../../contracts/middleware/BLSPublicKeyCompendium.sol"; -import "./util/G2Operations.sol"; - -contract BLSPublicKeyCompendiumFFITests is G2Operations { - using BN254 for BN254.G1Point; - using Strings for uint256; - - BLSPublicKeyCompendium compendium; - - uint256 privKey; - BN254.G1Point pubKeyG1; - BN254.G2Point pubKeyG2; - BN254.G1Point signedMessageHash; - - address alice = address(0x69); - - function setUp() public { - compendium = new BLSPublicKeyCompendium(); - } - - function testRegisterBLSPublicKey(uint256 _privKey) public { - _setKeys(_privKey); - - signedMessageHash = _signMessage(alice); - - vm.prank(alice); - compendium.registerBLSPublicKey(signedMessageHash, pubKeyG1, pubKeyG2); - - assertEq(compendium.operatorToPubkeyHash(alice), BN254.hashG1Point(pubKeyG1), "pubkey hash not stored correctly"); - assertEq(compendium.pubkeyHashToOperator(BN254.hashG1Point(pubKeyG1)), alice, "operator address not stored correctly"); - } - - function _setKeys(uint256 _privKey) internal { - privKey = _privKey; - pubKeyG1 = BN254.generatorG1().scalar_mul(_privKey); - pubKeyG2 = G2Operations.mul(_privKey); - } - - function _signMessage(address signer) internal view returns(BN254.G1Point memory) { - BN254.G1Point memory messageHash = compendium.getMessageHash(signer); - return BN254.scalar_mul(messageHash, privKey); - } -} \ No newline at end of file diff --git a/src/test/ffi/BLSSignatureCheckerFFI.t.sol b/src/test/ffi/BLSSignatureCheckerFFI.t.sol deleted file mode 100644 index 76d4f70b5..000000000 --- a/src/test/ffi/BLSSignatureCheckerFFI.t.sol +++ /dev/null @@ -1,183 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity =0.8.12; - -import "./util/G2Operations.sol"; -import "../utils/MockAVSDeployer.sol"; -import "../../contracts/middleware/BLSSignatureChecker.sol"; - - -contract BLSSignatureCheckerFFITests is MockAVSDeployer, G2Operations { - - using BN254 for BN254.G1Point; - - bytes32 msgHash = keccak256(abi.encodePacked("hello world")); - uint256 aggSignerPrivKey; - BN254.G2Point aggSignerApkG2; - BN254.G2Point oneHundredQuorumApkG2; - BN254.G1Point sigma; - - BLSSignatureChecker blsSignatureChecker; - - function setUp() virtual public { - _deployMockEigenLayerAndAVS(); - - blsSignatureChecker = new BLSSignatureChecker(registryCoordinator); - } - - // this test checks that a valid signature from maxOperatorsToRegister with a random number of nonsigners is checked - // correctly on the BLSSignatureChecker contract when all operators are only regsitered for a single quorum and - // the signature is only checked for stakes on that quorum - function testBLSSignatureChecker_SingleQuorum_Valid(uint256 pseudoRandomNumber) public { - uint256 numNonSigners = pseudoRandomNumber % (maxOperatorsToRegister - 1); - - uint256 quorumBitmap = 1; - bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); - - (uint32 referenceBlockNumber, BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature) = - _registerSignatoriesAndGetNonSignerStakeAndSignatureRandom(pseudoRandomNumber, numNonSigners, quorumBitmap); - - uint256 gasBefore = gasleft(); - ( - BLSSignatureChecker.QuorumStakeTotals memory quorumStakeTotals, - /* bytes32 signatoryRecordHash */ - ) = blsSignatureChecker.checkSignatures( - msgHash, - quorumNumbers, - referenceBlockNumber, - nonSignerStakesAndSignature - ); - uint256 gasAfter = gasleft(); - emit log_named_uint("gasUsed", gasBefore - gasAfter); - assertTrue(quorumStakeTotals.signedStakeForQuorum[0] > 0); - - // 0 nonSigners: 159908 - // 1 nonSigner: 178683 - // 2 nonSigners: 197410 - } - - // this test checks that a valid signature from maxOperatorsToRegister with a random number of nonsigners is checked - // correctly on the BLSSignatureChecker contract when all operators are registered for the first 100 quorums - // and the signature is only checked for stakes on those quorums - function testBLSSignatureChecker_100Quorums_Valid(uint256 pseudoRandomNumber) public { - uint256 numNonSigners = pseudoRandomNumber % (maxOperatorsToRegister - 1); - - // 100 set bits - uint256 quorumBitmap = (1 << 100) - 1; - bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); - - (uint32 referenceBlockNumber, BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature) = - _registerSignatoriesAndGetNonSignerStakeAndSignatureRandom(pseudoRandomNumber, numNonSigners, quorumBitmap); - - nonSignerStakesAndSignature.sigma = sigma.scalar_mul(quorumNumbers.length); - nonSignerStakesAndSignature.apkG2 = oneHundredQuorumApkG2; - - uint256 gasBefore = gasleft(); - blsSignatureChecker.checkSignatures( - msgHash, - quorumNumbers, - referenceBlockNumber, - nonSignerStakesAndSignature - ); - uint256 gasAfter = gasleft(); - emit log_named_uint("gasUsed", gasBefore - gasAfter); - } - - function _setAggregatePublicKeysAndSignature(uint256 pseudoRandomNumber) internal { - if(pseudoRandomNumber > type(uint256).max / 100) { - pseudoRandomNumber = type(uint256).max / 100; - } - aggSignerPrivKey = pseudoRandomNumber; - aggSignerApkG2 = G2Operations.mul(aggSignerPrivKey); - oneHundredQuorumApkG2 = G2Operations.mul(100 * aggSignerPrivKey); - sigma = BN254.hashToG1(msgHash).scalar_mul(aggSignerPrivKey); - } - - function _generateSignerAndNonSignerPrivateKeys(uint256 pseudoRandomNumber, uint256 numSigners, uint256 numNonSigners) internal returns (uint256[] memory, uint256[] memory) { - _setAggregatePublicKeysAndSignature(pseudoRandomNumber); - - uint256[] memory signerPrivateKeys = new uint256[](numSigners); - // generate numSigners numbers that add up to aggSignerPrivKey mod BN254.FR_MODULUS - uint256 sum = 0; - for (uint i = 0; i < numSigners - 1; i++) { - signerPrivateKeys[i] = uint256(keccak256(abi.encodePacked("signerPrivateKey", pseudoRandomNumber, i))) % BN254.FR_MODULUS; - sum = addmod(sum, signerPrivateKeys[i], BN254.FR_MODULUS); - } - // signer private keys need to add to aggSignerPrivKey - signerPrivateKeys[numSigners - 1] = addmod(aggSignerPrivKey, BN254.FR_MODULUS - sum % BN254.FR_MODULUS, BN254.FR_MODULUS); - - uint256[] memory nonSignerPrivateKeys = new uint256[](numNonSigners); - for (uint i = 0; i < numNonSigners; i++) { - nonSignerPrivateKeys[i] = uint256(keccak256(abi.encodePacked("nonSignerPrivateKey", pseudoRandomNumber, i))) % BN254.FR_MODULUS; - } - - return (signerPrivateKeys, nonSignerPrivateKeys); - } - - function _registerSignatoriesAndGetNonSignerStakeAndSignatureRandom(uint256 pseudoRandomNumber, uint256 numNonSigners, uint256 quorumBitmap) internal returns(uint32, BLSSignatureChecker.NonSignerStakesAndSignature memory) { - (uint256[] memory signerPrivateKeys, uint256[] memory nonSignerPrivateKeys) = _generateSignerAndNonSignerPrivateKeys(pseudoRandomNumber, maxOperatorsToRegister - numNonSigners, numNonSigners); - bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); - - // randomly combine signer and non-signer private keys - uint256[] memory privateKeys = new uint256[](maxOperatorsToRegister); - // generate addresses and public keys - address[] memory operators = new address[](maxOperatorsToRegister); - BN254.G1Point[] memory pubkeys = new BN254.G1Point[](maxOperatorsToRegister); - BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature; - nonSignerStakesAndSignature.quorumApks = new BN254.G1Point[](quorumNumbers.length); - nonSignerStakesAndSignature.nonSignerPubkeys = new BN254.G1Point[](numNonSigners); - bytes32[] memory nonSignerOperatorIds = new bytes32[](numNonSigners); - { - uint256 signerIndex = 0; - uint256 nonSignerIndex = 0; - for (uint i = 0; i < maxOperatorsToRegister; i++) { - uint256 randomSeed = uint256(keccak256(abi.encodePacked("privKeyCombination", i))); - if (randomSeed % 2 == 0 && signerIndex < signerPrivateKeys.length) { - privateKeys[i] = signerPrivateKeys[signerIndex]; - signerIndex++; - } else if (nonSignerIndex < nonSignerPrivateKeys.length) { - privateKeys[i] = nonSignerPrivateKeys[nonSignerIndex]; - nonSignerStakesAndSignature.nonSignerPubkeys[nonSignerIndex] = BN254.generatorG1().scalar_mul(privateKeys[i]); - nonSignerOperatorIds[nonSignerIndex] = nonSignerStakesAndSignature.nonSignerPubkeys[nonSignerIndex].hashG1Point(); - nonSignerIndex++; - } else { - privateKeys[i] = signerPrivateKeys[signerIndex]; - signerIndex++; - } - - operators[i] = _incrementAddress(defaultOperator, i); - pubkeys[i] = BN254.generatorG1().scalar_mul(privateKeys[i]); - - // add the public key to each quorum - for (uint j = 0; j < nonSignerStakesAndSignature.quorumApks.length; j++) { - nonSignerStakesAndSignature.quorumApks[j] = nonSignerStakesAndSignature.quorumApks[j].plus(pubkeys[i]); - } - } - } - - // register all operators for the first quorum - for (uint i = 0; i < maxOperatorsToRegister; i++) { - cheats.roll(registrationBlockNumber + blocksBetweenRegistrations * i); - _registerOperatorWithCoordinator(operators[i], quorumBitmap, pubkeys[i], defaultStake); - } - - uint32 referenceBlockNumber = registrationBlockNumber + blocksBetweenRegistrations * uint32(maxOperatorsToRegister) + 1; - cheats.roll(referenceBlockNumber + 100); - - BLSOperatorStateRetriever.CheckSignaturesIndices memory checkSignaturesIndices = operatorStateRetriever.getCheckSignaturesIndices( - registryCoordinator, - referenceBlockNumber, - quorumNumbers, - nonSignerOperatorIds - ); - - nonSignerStakesAndSignature.nonSignerQuorumBitmapIndices = checkSignaturesIndices.nonSignerQuorumBitmapIndices; - nonSignerStakesAndSignature.apkG2 = aggSignerApkG2; - nonSignerStakesAndSignature.sigma = sigma; - nonSignerStakesAndSignature.quorumApkIndices = checkSignaturesIndices.quorumApkIndices; - nonSignerStakesAndSignature.totalStakeIndices = checkSignaturesIndices.totalStakeIndices; - nonSignerStakesAndSignature.nonSignerStakeIndices = checkSignaturesIndices.nonSignerStakeIndices; - - return (referenceBlockNumber, nonSignerStakesAndSignature); - } - -} \ No newline at end of file diff --git a/src/test/ffi/go/g2mul.go b/src/test/ffi/go/g2mul.go deleted file mode 100644 index 311de248b..000000000 --- a/src/test/ffi/go/g2mul.go +++ /dev/null @@ -1,62 +0,0 @@ -package main - -import ( - "fmt" - "os" - "strings" - "math/big" - "github.com/consensys/gnark-crypto/ecc/bn254" -) - -func main() { - //parse args - arg1 := os.Args[1] - n := new(big.Int) - n, _ = n.SetString(arg1, 10) - - //g2 mul - pubkey := new(bn254.G2Affine).ScalarMultiplication(GetG2Generator(), n) - px := pubkey.X.String() - py := pubkey.Y.String() - - //parse out point coords to big ints - pxs := strings.Split(px, "+") - pxss := strings.Split(pxs[1], "*") - - pys := strings.Split(py, "+") - pyss := strings.Split(pys[1], "*") - - pxsInt := new(big.Int) - pxsInt, _ = pxsInt.SetString(pxs[0], 10) - - pxssInt := new(big.Int) - pxssInt, _ = pxssInt.SetString(pxss[0], 10) - - pysInt := new(big.Int) - pysInt, _ = pysInt.SetString(pys[0], 10) - - pyssInt := new(big.Int) - pyssInt, _ = pyssInt.SetString(pyss[0], 10) - - //swtich to print coord requested - switch os.Args[2] { - case "1": - fmt.Printf("0x%064X", pxsInt) - case "2": - fmt.Printf("0x%064X", pxssInt) - case "3": - fmt.Printf("0x%064X", pysInt) - case "4": - fmt.Printf("0x%064X", pyssInt) - } - -} - -func GetG2Generator() *bn254.G2Affine { - g2Gen := new(bn254.G2Affine) - g2Gen.X.SetString("10857046999023057135944570762232829481370756359578518086990519993285655852781", - "11559732032986387107991004021392285783925812861821192530917403151452391805634") - g2Gen.Y.SetString("8495653923123431417604973247489272438418190587263600148770280649306958101930", - "4082367875863433681332203403145435568316851327593401208105741076214120093531") - return g2Gen -} \ No newline at end of file diff --git a/src/test/ffi/util/G2Operations.sol b/src/test/ffi/util/G2Operations.sol deleted file mode 100644 index 4490d3aa9..000000000 --- a/src/test/ffi/util/G2Operations.sol +++ /dev/null @@ -1,35 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity =0.8.12; - -import "forge-std/Test.sol"; -import "openzeppelin-contracts/contracts/utils/Strings.sol"; -import "../../../contracts/libraries/BN254.sol"; - -contract G2Operations is Test { - using Strings for uint256; - - function mul(uint256 x) public returns (BN254.G2Point memory g2Point) { - string[] memory inputs = new string[](5); - inputs[0] = "go"; - inputs[1] = "run"; - inputs[2] = "src/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)); - } - -} \ No newline at end of file diff --git a/src/test/harnesses/BLSRegistryCoordinatorWithIndicesHarness.sol b/src/test/harnesses/BLSRegistryCoordinatorWithIndicesHarness.sol deleted file mode 100644 index a115247bd..000000000 --- a/src/test/harnesses/BLSRegistryCoordinatorWithIndicesHarness.sol +++ /dev/null @@ -1,33 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity =0.8.12; - -import "../../contracts/middleware/BLSRegistryCoordinatorWithIndices.sol"; - -// wrapper around the BLSRegistryCoordinatorWithIndices contract that exposes the internal functions for unit testing. -contract BLSRegistryCoordinatorWithIndicesHarness is BLSRegistryCoordinatorWithIndices { - constructor( - ISlasher _slasher, - IServiceManager _serviceManager, - IStakeRegistry _stakeRegistry, - IBLSPubkeyRegistry _blsPubkeyRegistry, - IIndexRegistry _indexRegistry - ) BLSRegistryCoordinatorWithIndices(_slasher, _serviceManager, _stakeRegistry, _blsPubkeyRegistry, _indexRegistry) { - } - - function setOperatorId(address operator, bytes32 operatorId) external { - _operators[operator].operatorId = operatorId; - } - - function recordOperatorQuorumBitmapUpdate(bytes32 operatorId, uint192 quorumBitmap) external { - uint256 operatorQuorumBitmapHistoryLength = _operatorIdToQuorumBitmapHistory[operatorId].length; - if (operatorQuorumBitmapHistoryLength != 0) { - _operatorIdToQuorumBitmapHistory[operatorId][operatorQuorumBitmapHistoryLength - 1].nextUpdateBlockNumber = uint32(block.number); - } - - _operatorIdToQuorumBitmapHistory[operatorId].push(QuorumBitmapUpdate({ - updateBlockNumber: uint32(block.number), - nextUpdateBlockNumber: 0, - quorumBitmap: quorumBitmap - })); - } -} \ No newline at end of file diff --git a/src/test/harnesses/StakeRegistryHarness.sol b/src/test/harnesses/StakeRegistryHarness.sol deleted file mode 100644 index 22307049b..000000000 --- a/src/test/harnesses/StakeRegistryHarness.sol +++ /dev/null @@ -1,43 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity =0.8.12; - -import "../../contracts/middleware/StakeRegistry.sol"; - -// wrapper around the StakeRegistry contract that exposes the internal functions for unit testing. -contract StakeRegistryHarness is StakeRegistry { - mapping(uint8 => mapping(address => uint96)) private _weightOfOperatorForQuorum; - - constructor( - IRegistryCoordinator _registryCoordinator, - IStrategyManager _strategyManager, - IServiceManager _serviceManager - ) StakeRegistry(_registryCoordinator, _strategyManager, _serviceManager) { - } - - function recordOperatorStakeUpdate(bytes32 operatorId, uint8 quorumNumber, OperatorStakeUpdate memory operatorStakeUpdate) external returns(uint96) { - return _recordOperatorStakeUpdate(operatorId, quorumNumber, operatorStakeUpdate); - } - - function updateOperatorStake(address operator, bytes32 operatorId, uint8 quorumNumber) external returns (uint96, uint96) { - return _updateOperatorStake(operator, operatorId, quorumNumber); - } - - function recordTotalStakeUpdate(uint8 quorumNumber, OperatorStakeUpdate memory totalStakeUpdate) external { - _recordTotalStakeUpdate(quorumNumber, totalStakeUpdate); - } - - // mocked function so we can set this arbitrarily without having to mock other elements - function weightOfOperatorForQuorum(uint8 quorumNumber, address operator) public override view returns(uint96) { - return _weightOfOperatorForQuorum[quorumNumber][operator]; - } - - // mocked function so we can set this arbitrarily without having to mock other elements - function setOperatorWeight(uint8 quorumNumber, address operator, uint96 weight) external { - _weightOfOperatorForQuorum[quorumNumber][operator] = weight; - } - - // mocked function to register an operator without having to mock other elements - function registerOperatorNonCoordinator(address operator, bytes32 operatorId, bytes calldata quorumNumbers) external { - _registerOperator(operator, operatorId, quorumNumbers); - } -} \ No newline at end of file diff --git a/src/test/mocks/BLSPublicKeyCompendiumMock.sol b/src/test/mocks/BLSPublicKeyCompendiumMock.sol deleted file mode 100644 index 982885ae4..000000000 --- a/src/test/mocks/BLSPublicKeyCompendiumMock.sol +++ /dev/null @@ -1,46 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity =0.8.12; - -import "../../contracts/interfaces/IBLSPublicKeyCompendium.sol"; -import "../../contracts/libraries/BN254.sol"; -import "forge-std/Test.sol"; - -/** - * @title A shared contract for EigenLayer operators to register their BLS public keys. - * @author Layr Labs, Inc. - * @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service - */ -contract BLSPublicKeyCompendiumMock is IBLSPublicKeyCompendium, DSTest { - - /// @notice mapping from operator address to pubkey hash - mapping(address => bytes32) public operatorToPubkeyHash; - /// @notice mapping from pubkey hash to operator address - mapping(bytes32 => address) public pubkeyHashToOperator; - - /** - * @notice Called by an operator to register themselves as the owner of a BLS public key and reveal their G1 and G2 public key. - * @param signedMessageHash is the registration message hash signed by the private key of the operator - * @param pubkeyG1 is the corresponding G1 public key of the operator - * @param pubkeyG2 is the corresponding G2 public key of the operator - */ - function registerBLSPublicKey(BN254.G1Point memory signedMessageHash, BN254.G1Point memory pubkeyG1, BN254.G2Point memory pubkeyG2) external { - } - - function registerPublicKey(BN254.G1Point memory pk) external { - - bytes32 pubkeyHash = BN254.hashG1Point(pk); - // store updates - operatorToPubkeyHash[msg.sender] = pubkeyHash; - pubkeyHashToOperator[pubkeyHash] = msg.sender; - } - - function setBLSPublicKey(address account, BN254.G1Point memory pk) external { - - bytes32 pubkeyHash = BN254.hashG1Point(pk); - // store updates - operatorToPubkeyHash[account] = pubkeyHash; - pubkeyHashToOperator[pubkeyHash] = account; - } - - function getMessageHash(address operator) external view returns (BN254.G1Point memory) {} -} \ No newline at end of file diff --git a/src/test/mocks/StakeRegistryStub.sol b/src/test/mocks/StakeRegistryStub.sol new file mode 100644 index 000000000..003adce8c --- /dev/null +++ b/src/test/mocks/StakeRegistryStub.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity =0.8.12; + +contract StakeRegistryStub { + function updateStakes(address[] memory) external {} +} diff --git a/src/test/unit/BLSOperatorStateRetrieverUnit.t.sol b/src/test/unit/BLSOperatorStateRetrieverUnit.t.sol deleted file mode 100644 index a7eeef683..000000000 --- a/src/test/unit/BLSOperatorStateRetrieverUnit.t.sol +++ /dev/null @@ -1,192 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity =0.8.12; - -import "../utils/MockAVSDeployer.sol"; - -contract BLSOperatorStateRetrieverUnitTests is MockAVSDeployer { - using BN254 for BN254.G1Point; - - function setUp() virtual public { - _deployMockEigenLayerAndAVS(); - } - - function testGetOperatorState_Valid(uint256 pseudoRandomNumber) public { - // register random operators and get the expected indices within the quorums and the metadata for the operators - ( - OperatorMetadata[] memory operatorMetadatas, - uint256[][] memory expectedOperatorOverallIndices - ) = _registerRandomOperators(pseudoRandomNumber); - - for (uint i = 0; i < operatorMetadatas.length; i++) { - uint32 blockNumber = uint32(registrationBlockNumber + blocksBetweenRegistrations * i); - - uint256 gasBefore = gasleft(); - // retrieve the ordered list of operators for each quorum along with their id and stake - (uint256 quorumBitmap, BLSOperatorStateRetriever.Operator[][] memory operators) = - operatorStateRetriever.getOperatorState(registryCoordinator, operatorMetadatas[i].operatorId, blockNumber); - uint256 gasAfter = gasleft(); - emit log_named_uint("gasUsed", gasBefore - gasAfter); - - assertEq(operatorMetadatas[i].quorumBitmap, quorumBitmap); - bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); - - // assert that the operators returned are the expected ones - _assertExpectedOperators( - quorumNumbers, - operators, - expectedOperatorOverallIndices, - operatorMetadatas - ); - } - - // choose a random operator to deregister - uint256 operatorIndexToDeregister = pseudoRandomNumber % maxOperatorsToRegister; - bytes memory quorumNumbersToDeregister = BitmapUtils.bitmapToBytesArray(operatorMetadatas[operatorIndexToDeregister].quorumBitmap); - // get the operatorIds of the last operators in each quorum to swap with the operator to deregister - bytes32[] memory operatorIdsToSwap = new bytes32[](quorumNumbersToDeregister.length); - for (uint i = 0; i < quorumNumbersToDeregister.length; i++) { - uint8 quorumNumber = uint8(quorumNumbersToDeregister[i]); - operatorIdsToSwap[i] = operatorMetadatas[expectedOperatorOverallIndices[quorumNumber][expectedOperatorOverallIndices[quorumNumber].length - 1]].operatorId; - } - - uint32 deregistrationBlockNumber = registrationBlockNumber + blocksBetweenRegistrations * (uint32(operatorMetadatas.length) + 1); - cheats.roll(deregistrationBlockNumber); - - cheats.prank(_incrementAddress(defaultOperator, operatorIndexToDeregister)); - registryCoordinator.deregisterOperatorWithCoordinator(quorumNumbersToDeregister, operatorMetadatas[operatorIndexToDeregister].pubkey, operatorIdsToSwap); - - // modify expectedOperatorOverallIndices by moving th operatorIdsToSwap to the index where the operatorIndexToDeregister was - for (uint i = 0; i < quorumNumbersToDeregister.length; i++) { - uint8 quorumNumber = uint8(quorumNumbersToDeregister[i]); - // loop through indices till we find operatorIndexToDeregister, then move that last operator into that index - for (uint j = 0; j < expectedOperatorOverallIndices[quorumNumber].length; j++) { - if (expectedOperatorOverallIndices[quorumNumber][j] == operatorIndexToDeregister) { - expectedOperatorOverallIndices[quorumNumber][j] = expectedOperatorOverallIndices[quorumNumber][expectedOperatorOverallIndices[quorumNumber].length - 1]; - break; - } - } - } - - // make sure the state retriever returns the expected state after deregistration - bytes memory allQuorumNumbers = new bytes(maxQuorumsToRegisterFor); - for (uint8 i = 0; i < allQuorumNumbers.length; i++) { - allQuorumNumbers[i] = bytes1(i); - } - - _assertExpectedOperators( - allQuorumNumbers, - operatorStateRetriever.getOperatorState(registryCoordinator, allQuorumNumbers, deregistrationBlockNumber), - expectedOperatorOverallIndices, - operatorMetadatas - ); - } - - function testCheckSignaturesIndices_NoNonSigners_Valid(uint256 pseudoRandomNumber) public { - ( - OperatorMetadata[] memory operatorMetadatas, - uint256[][] memory expectedOperatorOverallIndices - ) = _registerRandomOperators(pseudoRandomNumber); - - uint32 cumulativeBlockNumber = registrationBlockNumber + blocksBetweenRegistrations * uint32(operatorMetadatas.length); - - // get the quorum bitmap for which there is at least 1 operator - uint256 allInclusiveQuorumBitmap = 0; - for (uint8 i = 0; i < operatorMetadatas.length; i++) { - allInclusiveQuorumBitmap |= operatorMetadatas[i].quorumBitmap; - } - - bytes memory allInclusiveQuorumNumbers = BitmapUtils.bitmapToBytesArray(allInclusiveQuorumBitmap); - - bytes32[] memory nonSignerOperatorIds = new bytes32[](0); - - BLSOperatorStateRetriever.CheckSignaturesIndices memory checkSignaturesIndices = - operatorStateRetriever.getCheckSignaturesIndices( - registryCoordinator, - cumulativeBlockNumber, - allInclusiveQuorumNumbers, - nonSignerOperatorIds - ); - - assertEq(checkSignaturesIndices.nonSignerQuorumBitmapIndices.length, 0, "nonSignerQuorumBitmapIndices should be empty if no nonsigners"); - assertEq(checkSignaturesIndices.quorumApkIndices.length, allInclusiveQuorumNumbers.length, "quorumApkIndices should be the number of quorums queried for"); - assertEq(checkSignaturesIndices.totalStakeIndices.length, allInclusiveQuorumNumbers.length, "totalStakeIndices should be the number of quorums queried for"); - assertEq(checkSignaturesIndices.nonSignerStakeIndices.length, allInclusiveQuorumNumbers.length, "nonSignerStakeIndices should be the number of quorums queried for"); - - // assert the indices are the number of registered operators for the quorum minus 1 - for (uint8 i = 0; i < allInclusiveQuorumNumbers.length; i++) { - uint8 quorumNumber = uint8(allInclusiveQuorumNumbers[i]); - assertEq(checkSignaturesIndices.quorumApkIndices[i], expectedOperatorOverallIndices[quorumNumber].length - 1, "quorumApkIndex should be the number of registered operators for the quorum minus 1"); - assertEq(checkSignaturesIndices.totalStakeIndices[i], expectedOperatorOverallIndices[quorumNumber].length - 1, "totalStakeIndex should be the number of registered operators for the quorum minus 1"); - } - } - - function testCheckSignaturesIndices_FewNonSigners_Valid(uint256 pseudoRandomNumber) public { - ( - OperatorMetadata[] memory operatorMetadatas, - uint256[][] memory expectedOperatorOverallIndices - ) = _registerRandomOperators(pseudoRandomNumber); - - uint32 cumulativeBlockNumber = registrationBlockNumber + blocksBetweenRegistrations * uint32(operatorMetadatas.length); - - // get the quorum bitmap for which there is at least 1 operator - uint256 allInclusiveQuorumBitmap = 0; - for (uint8 i = 0; i < operatorMetadatas.length; i++) { - allInclusiveQuorumBitmap |= operatorMetadatas[i].quorumBitmap; - } - - bytes memory allInclusiveQuorumNumbers = BitmapUtils.bitmapToBytesArray(allInclusiveQuorumBitmap); - - bytes32[] memory nonSignerOperatorIds = new bytes32[](pseudoRandomNumber % (operatorMetadatas.length - 1) + 1); - uint256 randomIndex = uint256(keccak256(abi.encodePacked("nonSignerOperatorIds", pseudoRandomNumber))) % operatorMetadatas.length; - for (uint i = 0; i < nonSignerOperatorIds.length; i++) { - nonSignerOperatorIds[i] = operatorMetadatas[(randomIndex + i) % operatorMetadatas.length].operatorId; - } - - BLSOperatorStateRetriever.CheckSignaturesIndices memory checkSignaturesIndices = - operatorStateRetriever.getCheckSignaturesIndices( - registryCoordinator, - cumulativeBlockNumber, - allInclusiveQuorumNumbers, - nonSignerOperatorIds - ); - - assertEq(checkSignaturesIndices.nonSignerQuorumBitmapIndices.length, nonSignerOperatorIds.length, "nonSignerQuorumBitmapIndices should be the number of nonsigners"); - assertEq(checkSignaturesIndices.quorumApkIndices.length, allInclusiveQuorumNumbers.length, "quorumApkIndices should be the number of quorums queried for"); - assertEq(checkSignaturesIndices.totalStakeIndices.length, allInclusiveQuorumNumbers.length, "totalStakeIndices should be the number of quorums queried for"); - assertEq(checkSignaturesIndices.nonSignerStakeIndices.length, allInclusiveQuorumNumbers.length, "nonSignerStakeIndices should be the number of quorums queried for"); - - // assert the indices are the number of registered operators for the quorum minus 1 - for (uint8 i = 0; i < allInclusiveQuorumNumbers.length; i++) { - uint8 quorumNumber = uint8(allInclusiveQuorumNumbers[i]); - assertEq(checkSignaturesIndices.quorumApkIndices[i], expectedOperatorOverallIndices[quorumNumber].length - 1, "quorumApkIndex should be the number of registered operators for the quorum minus 1"); - assertEq(checkSignaturesIndices.totalStakeIndices[i], expectedOperatorOverallIndices[quorumNumber].length - 1, "totalStakeIndex should be the number of registered operators for the quorum minus 1"); - } - - // assert the quorum bitmap and stake indices are zero because there have been no kicks or stake updates - for (uint i = 0; i < nonSignerOperatorIds.length; i++) { - assertEq(checkSignaturesIndices.nonSignerQuorumBitmapIndices[i], 0, "nonSignerQuorumBitmapIndices should be zero because there have been no kicks"); - } - for (uint i = 0; i < checkSignaturesIndices.nonSignerStakeIndices.length; i++) { - for (uint j = 0; j < checkSignaturesIndices.nonSignerStakeIndices[i].length; j++) { - assertEq(checkSignaturesIndices.nonSignerStakeIndices[i][j], 0, "nonSignerStakeIndices should be zero because there have been no stake updates past the first one"); - } - } - } - - function _assertExpectedOperators( - bytes memory quorumNumbers, - BLSOperatorStateRetriever.Operator[][] memory operators, - uint256[][] memory expectedOperatorOverallIndices, - OperatorMetadata[] memory operatorMetadatas - ) internal { - // for each quorum - for (uint j = 0; j < quorumNumbers.length; j++) { - // make sure the each operator id and stake is correct - for (uint k = 0; k < operators[j].length; k++) { - uint8 quorumNumber = uint8(quorumNumbers[j]); - assertEq(operators[j][k].operatorId, operatorMetadatas[expectedOperatorOverallIndices[quorumNumber][k]].operatorId); - assertEq(operators[j][k].stake, operatorMetadatas[expectedOperatorOverallIndices[quorumNumber][k]].stakes[quorumNumber]); - } - } - } -} \ No newline at end of file diff --git a/src/test/unit/BLSPubkeyRegistryUnit.t.sol b/src/test/unit/BLSPubkeyRegistryUnit.t.sol deleted file mode 100644 index 912fa7ccc..000000000 --- a/src/test/unit/BLSPubkeyRegistryUnit.t.sol +++ /dev/null @@ -1,254 +0,0 @@ -//SPDX-License-Identifier: BUSL-1.1 -pragma solidity =0.8.12; - - -import "forge-std/Test.sol"; -import "../../contracts/middleware/BLSPubkeyRegistry.sol"; -import "../../contracts/interfaces/IRegistryCoordinator.sol"; -import "../mocks/BLSPublicKeyCompendiumMock.sol"; -import "../mocks/RegistryCoordinatorMock.sol"; - - -contract BLSPubkeyRegistryUnitTests is Test { - using BN254 for BN254.G1Point; - Vm cheats = Vm(HEVM_ADDRESS); - - address defaultOperator = address(4545); - address defaultOperator2 = address(4546); - - bytes32 internal constant ZERO_PK_HASH = hex"ad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5"; - - BLSPubkeyRegistry public blsPubkeyRegistry; - BLSPublicKeyCompendiumMock public pkCompendium; - RegistryCoordinatorMock public registryCoordinator; - - BN254.G1Point internal defaultPubKey = BN254.G1Point(18260007818883133054078754218619977578772505796600400998181738095793040006897,3432351341799135763167709827653955074218841517684851694584291831827675065899); - - uint8 internal defaultQuorumNumber = 0; - - function setUp() external { - registryCoordinator = new RegistryCoordinatorMock(); - pkCompendium = new BLSPublicKeyCompendiumMock(); - blsPubkeyRegistry = new BLSPubkeyRegistry(registryCoordinator, pkCompendium); - } - - function testConstructorArgs() public view { - require(blsPubkeyRegistry.registryCoordinator() == registryCoordinator, "registryCoordinator not set correctly"); - require(blsPubkeyRegistry.pubkeyCompendium() == pkCompendium, "pubkeyCompendium not set correctly"); - } - - function testCallRegisterOperatorFromNonCoordinatorAddress(address nonCoordinatorAddress) public { - cheats.assume(nonCoordinatorAddress != address(registryCoordinator)); - - cheats.startPrank(nonCoordinatorAddress); - cheats.expectRevert(bytes("BLSPubkeyRegistry.onlyRegistryCoordinator: caller is not the registry coordinator")); - blsPubkeyRegistry.registerOperator(nonCoordinatorAddress, new bytes(0), BN254.G1Point(0, 0)); - cheats.stopPrank(); - } - - function testCallDeregisterOperatorFromNonCoordinatorAddress(address nonCoordinatorAddress) public { - cheats.assume(nonCoordinatorAddress != address(registryCoordinator)); - - cheats.startPrank(nonCoordinatorAddress); - cheats.expectRevert(bytes("BLSPubkeyRegistry.onlyRegistryCoordinator: caller is not the registry coordinator")); - blsPubkeyRegistry.deregisterOperator(nonCoordinatorAddress, new bytes(0), BN254.G1Point(0, 0)); - cheats.stopPrank(); - } - - function testOperatorDoesNotOwnPubKeyRegister() public { - cheats.startPrank(address(registryCoordinator)); - cheats.expectRevert(bytes("BLSPubkeyRegistry.registerOperator: operator does not own pubkey")); - blsPubkeyRegistry.registerOperator(defaultOperator, new bytes(1), BN254.G1Point(1, 0)); - cheats.stopPrank(); - } - - function testOperatorRegisterZeroPubkey() public { - cheats.startPrank(address(registryCoordinator)); - cheats.expectRevert(bytes("BLSPubkeyRegistry.registerOperator: cannot register zero pubkey")); - blsPubkeyRegistry.registerOperator(defaultOperator, new bytes(1), BN254.G1Point(0, 0)); - cheats.stopPrank(); - } - - function testRegisterOperatorBLSPubkey(address operator, bytes32 x) public returns(bytes32){ - - BN254.G1Point memory pubkey = BN254.hashToG1(x); - bytes32 pkHash = BN254.hashG1Point(pubkey); - - cheats.startPrank(operator); - pkCompendium.registerPublicKey(pubkey); - cheats.stopPrank(); - - //register for one quorum - bytes memory quorumNumbers = new bytes(1); - quorumNumbers[0] = bytes1(defaultQuorumNumber); - - cheats.startPrank(address(registryCoordinator)); - bytes32 registeredpkHash = blsPubkeyRegistry.registerOperator(operator, quorumNumbers, pubkey); - cheats.stopPrank(); - - - require(registeredpkHash == pkHash, "registeredpkHash not set correctly"); - emit log("ehey"); - - return pkHash; - } - - function testQuorumApkUpdates(uint8 quorumNumber1, uint8 quorumNumber2) public { - cheats.assume(quorumNumber1 != quorumNumber2); - - bytes memory quorumNumbers = new bytes(2); - quorumNumbers[0] = bytes1(quorumNumber1); - quorumNumbers[1] = bytes1(quorumNumber2); - - BN254.G1Point[] memory quorumApksBefore = new BN254.G1Point[](quorumNumbers.length); - for(uint8 i = 0; i < quorumNumbers.length; i++){ - quorumApksBefore[i] = blsPubkeyRegistry.getApkForQuorum(uint8(quorumNumbers[i])); - } - - cheats.startPrank(defaultOperator); - pkCompendium.registerPublicKey(defaultPubKey); - cheats.stopPrank(); - - cheats.startPrank(address(registryCoordinator)); - blsPubkeyRegistry.registerOperator(defaultOperator, quorumNumbers, defaultPubKey); - cheats.stopPrank(); - - //check quorum apk updates - for(uint8 i = 0; i < quorumNumbers.length; i++){ - BN254.G1Point memory quorumApkAfter = blsPubkeyRegistry.getApkForQuorum(uint8(quorumNumbers[i])); - bytes32 temp = BN254.hashG1Point(BN254.plus(quorumApkAfter, BN254.negate(quorumApksBefore[i]))); - require(temp == BN254.hashG1Point(defaultPubKey), "quorum apk not updated correctly"); - } - } - - function testRegisterWithNegativeQuorumApk(address operator, bytes32 x) external { - testRegisterOperatorBLSPubkey(defaultOperator, x); - - BN254.G1Point memory quorumApk = blsPubkeyRegistry.getApkForQuorum(defaultQuorumNumber); - - BN254.G1Point memory negatedQuorumApk = BN254.negate(quorumApk); - - //register for one quorum - bytes memory quorumNumbers = new bytes(1); - quorumNumbers[0] = bytes1(defaultQuorumNumber); - - cheats.startPrank(operator); - pkCompendium.registerPublicKey(negatedQuorumApk); - cheats.stopPrank(); - - cheats.startPrank(address(registryCoordinator)); - blsPubkeyRegistry.registerOperator(operator, quorumNumbers, negatedQuorumApk); - cheats.stopPrank(); - - require(BN254.hashG1Point(blsPubkeyRegistry.getApkForQuorum(defaultQuorumNumber)) == ZERO_PK_HASH, "quorumApk not set correctly"); - } - - function testQuorumApkUpdatesDeregistration(uint8 quorumNumber1, uint8 quorumNumber2) external { - cheats.assume(quorumNumber1 != quorumNumber2); - bytes memory quorumNumbers = new bytes(2); - quorumNumbers[0] = bytes1(quorumNumber1); - quorumNumbers[1] = bytes1(quorumNumber2); - - testQuorumApkUpdates(quorumNumber1, quorumNumber2); - - BN254.G1Point[] memory quorumApksBefore = new BN254.G1Point[](2); - for(uint8 i = 0; i < quorumNumbers.length; i++){ - quorumApksBefore[i] = blsPubkeyRegistry.getApkForQuorum(uint8(quorumNumbers[i])); - } - - cheats.startPrank(address(registryCoordinator)); - blsPubkeyRegistry.deregisterOperator(defaultOperator, quorumNumbers, defaultPubKey); - cheats.stopPrank(); - - - BN254.G1Point memory quorumApkAfter; - for(uint8 i = 0; i < quorumNumbers.length; i++){ - quorumApkAfter = blsPubkeyRegistry.getApkForQuorum(uint8(quorumNumbers[i])); - require(BN254.hashG1Point(quorumApksBefore[i].plus(defaultPubKey.negate())) == BN254.hashG1Point(quorumApkAfter), "quorum apk not updated correctly"); - } - } - - function testDeregisterOperatorWithQuorumApk(bytes32 x1, bytes32 x2) external { - testRegisterOperatorBLSPubkey(defaultOperator, x1); - testRegisterOperatorBLSPubkey(defaultOperator2, x2); - - BN254.G1Point memory quorumApksBefore= blsPubkeyRegistry.getApkForQuorum(defaultQuorumNumber); - - bytes memory quorumNumbers = new bytes(1); - quorumNumbers[0] = bytes1(defaultQuorumNumber); - - cheats.startPrank(defaultOperator); - pkCompendium.registerPublicKey(quorumApksBefore); - cheats.stopPrank(); - - cheats.prank(address(registryCoordinator)); - blsPubkeyRegistry.deregisterOperator(defaultOperator, quorumNumbers, quorumApksBefore); - - BN254.G1Point memory pk = blsPubkeyRegistry.getApkForQuorum(defaultQuorumNumber); - require(pk.X == 0, "quorum apk not set to zero"); - require(pk.Y == 0, "quorum apk not set to zero"); - } - - function testQuorumApkUpdatesAtBlockNumber(uint256 numRegistrants, uint256 blockGap) external{ - cheats.assume(numRegistrants > 0 && numRegistrants < 100); - cheats.assume(blockGap < 100); - - BN254.G1Point memory quorumApk = BN254.G1Point(0,0); - bytes24 quorumApkHash; - for (uint256 i = 0; i < numRegistrants; i++) { - bytes32 pk = _getRandomPk(i); - testRegisterOperatorBLSPubkey(defaultOperator, pk); - quorumApk = quorumApk.plus(BN254.hashToG1(pk)); - quorumApkHash = bytes24(BN254.hashG1Point(quorumApk)); - require(quorumApkHash == blsPubkeyRegistry.getApkHashForQuorumAtBlockNumberFromIndex(defaultQuorumNumber, uint32(block.number + blockGap) , i), "incorrect quorum aok updates"); - cheats.roll(block.number + 100); - if(_generateRandomNumber(i) % 2 == 0){ - _deregisterOperator(pk); - quorumApk = quorumApk.plus(BN254.hashToG1(pk).negate()); - quorumApkHash = bytes24(BN254.hashG1Point(quorumApk)); - require(quorumApkHash == blsPubkeyRegistry.getApkHashForQuorumAtBlockNumberFromIndex(defaultQuorumNumber, uint32(block.number + blockGap) , i + 1), "incorrect quorum aok updates"); - cheats.roll(block.number + 100); - i++; - } - } - } - - function testIncorrectBlockNumberForQuorumApkUpdates(uint256 numRegistrants, uint32 indexToCheck, uint32 wrongBlockNumber) external { - cheats.assume(numRegistrants > 0 && numRegistrants < 100); - cheats.assume(indexToCheck < numRegistrants - 1); - - uint256 startingBlockNumber = block.number; - - for (uint256 i = 0; i < numRegistrants; i++) { - bytes32 pk = _getRandomPk(i); - testRegisterOperatorBLSPubkey(defaultOperator, pk); - cheats.roll(block.number + 100); - } - if(wrongBlockNumber < startingBlockNumber + indexToCheck*100){ - cheats.expectRevert(bytes("BLSPubkeyRegistry._validateApkHashForQuorumAtBlockNumber: index too recent")); - blsPubkeyRegistry.getApkHashForQuorumAtBlockNumberFromIndex(defaultQuorumNumber, wrongBlockNumber, indexToCheck); - } - if (wrongBlockNumber >= startingBlockNumber + (indexToCheck+1)*100){ - cheats.expectRevert(bytes("BLSPubkeyRegistry._validateApkHashForQuorumAtBlockNumber: not latest apk update")); - blsPubkeyRegistry.getApkHashForQuorumAtBlockNumberFromIndex(defaultQuorumNumber, wrongBlockNumber, indexToCheck); - } - } - - function _getRandomPk(uint256 seed) internal view returns (bytes32) { - return keccak256(abi.encodePacked(block.timestamp, seed)); - } - - function _generateRandomNumber(uint256 seed) internal view returns (uint256) { - uint256 randomNumber = uint256(keccak256(abi.encodePacked(block.timestamp, seed))); - return (randomNumber % 100) + 1; - } - - function _deregisterOperator(bytes32 pk) internal { - bytes memory quorumNumbers = new bytes(1); - quorumNumbers[0] = bytes1(defaultQuorumNumber); - cheats.startPrank(address(registryCoordinator)); - blsPubkeyRegistry.deregisterOperator(defaultOperator, quorumNumbers, BN254.hashToG1(pk)); - cheats.stopPrank(); - } - -} \ No newline at end of file diff --git a/src/test/unit/BLSPublicKeyCompendiumUnit.t.sol b/src/test/unit/BLSPublicKeyCompendiumUnit.t.sol deleted file mode 100644 index 33831c067..000000000 --- a/src/test/unit/BLSPublicKeyCompendiumUnit.t.sol +++ /dev/null @@ -1,80 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity =0.8.12; - -import "forge-std/Test.sol"; -import "../../contracts/middleware/BLSPublicKeyCompendium.sol"; - -contract BLSPublicKeyCompendiumUnitTests is Test { - using BN254 for BN254.G1Point; - - BLSPublicKeyCompendium compendium; - uint256 privKey = 69; - - BN254.G1Point pubKeyG1; - BN254.G2Point pubKeyG2; - BN254.G1Point signedMessageHash; - - address alice = address(1); - address bob = address(2); - - function setUp() public { - compendium = new BLSPublicKeyCompendium(); - - pubKeyG1 = BN254.generatorG1().scalar_mul(privKey); - - //privKey*G2 - pubKeyG2.X[1] = 19101821850089705274637533855249918363070101489527618151493230256975900223847; - pubKeyG2.X[0] = 5334410886741819556325359147377682006012228123419628681352847439302316235957; - pubKeyG2.Y[1] = 354176189041917478648604979334478067325821134838555150300539079146482658331; - pubKeyG2.Y[0] = 4185483097059047421902184823581361466320657066600218863748375739772335928910; - } - - function testRegisterBLSPublicKey() public { - signedMessageHash = _signMessage(alice); - vm.prank(alice); - compendium.registerBLSPublicKey(signedMessageHash, pubKeyG1, pubKeyG2); - - assertEq(compendium.operatorToPubkeyHash(alice), BN254.hashG1Point(pubKeyG1), "pubkey hash not stored correctly"); - assertEq(compendium.pubkeyHashToOperator(BN254.hashG1Point(pubKeyG1)), alice, "operator address not stored correctly"); - } - - function testRegisterBLSPublicKey_NoMatch_Reverts() public { - signedMessageHash = _signMessage(alice); - BN254.G1Point memory badPubKeyG1 = BN254.generatorG1().scalar_mul(420); // mismatch public keys - - vm.prank(alice); - vm.expectRevert(bytes("BLSPublicKeyCompendium.registerBLSPublicKey: G1 and G2 private key do not match")); - compendium.registerBLSPublicKey(signedMessageHash, badPubKeyG1, pubKeyG2); - } - - function testRegisterBLSPublicKey_BadSig_Reverts() public { - signedMessageHash = _signMessage(bob); // sign with wrong private key - - vm.prank(alice); - vm.expectRevert(bytes("BLSPublicKeyCompendium.registerBLSPublicKey: G1 and G2 private key do not match")); - compendium.registerBLSPublicKey(signedMessageHash, pubKeyG1, pubKeyG2); - } - - function testRegisterBLSPublicKey_OpRegistered_Reverts() public { - testRegisterBLSPublicKey(); // register alice - - vm.prank(alice); - vm.expectRevert(bytes("BLSPublicKeyCompendium.registerBLSPublicKey: operator already registered pubkey")); - compendium.registerBLSPublicKey(signedMessageHash, pubKeyG1, pubKeyG2); - } - - function testRegisterBLSPublicKey_PkRegistered_Reverts() public { - testRegisterBLSPublicKey(); - signedMessageHash = _signMessage(bob); // same private key different operator - - vm.prank(bob); - vm.expectRevert(bytes("BLSPublicKeyCompendium.registerBLSPublicKey: public key already registered")); - compendium.registerBLSPublicKey(signedMessageHash, pubKeyG1, pubKeyG2); - } - - function _signMessage(address signer) internal view returns(BN254.G1Point memory) { - BN254.G1Point memory messageHash = compendium.getMessageHash(signer); - return BN254.scalar_mul(messageHash, privKey); - } - -} \ No newline at end of file diff --git a/src/test/unit/BLSRegistryCoordinatorWithIndicesUnit.t.sol b/src/test/unit/BLSRegistryCoordinatorWithIndicesUnit.t.sol deleted file mode 100644 index 794b99604..000000000 --- a/src/test/unit/BLSRegistryCoordinatorWithIndicesUnit.t.sol +++ /dev/null @@ -1,935 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity =0.8.12; - -import "../utils/MockAVSDeployer.sol"; - -contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { - using BN254 for BN254.G1Point; - - uint8 internal constant PAUSED_REGISTER_OPERATOR = 0; - uint8 internal constant PAUSED_DEREGISTER_OPERATOR = 1; - - event OperatorSocketUpdate(bytes32 indexed operatorId, string socket); - - /// @notice emitted whenever the stake of `operator` is updated - event StakeUpdate( - bytes32 indexed operatorId, - uint8 quorumNumber, - uint96 stake - ); - - // Emitted when a new operator pubkey is registered for a set of quorums - event OperatorAddedToQuorums( - address operator, - bytes quorumNumbers - ); - - // Emitted when an operator pubkey is removed from a set of quorums - event OperatorRemovedFromQuorums( - address operator, - bytes quorumNumbers - ); - - // emitted when an operator's index in the orderd operator list for the quorum with number `quorumNumber` is updated - event QuorumIndexUpdate(bytes32 indexed operatorId, uint8 quorumNumber, uint32 newIndex); - - event OperatorSetParamsUpdated(uint8 indexed quorumNumber, IBLSRegistryCoordinatorWithIndices.OperatorSetParam operatorSetParams); - - event ChurnApproverUpdated(address prevChurnApprover, address newChurnApprover); - - event EjectorUpdated(address prevEjector, address newEjector); - - function setUp() virtual public { - _deployMockEigenLayerAndAVS(); - } - - function testCorrectConstruction() public { - assertEq(address(registryCoordinator.stakeRegistry()), address(stakeRegistry)); - assertEq(address(registryCoordinator.blsPubkeyRegistry()), address(blsPubkeyRegistry)); - assertEq(address(registryCoordinator.indexRegistry()), address(indexRegistry)); - assertEq(address(registryCoordinator.slasher()), address(slasher)); - - for (uint i = 0; i < numQuorums; i++) { - assertEq( - keccak256(abi.encode(registryCoordinator.getOperatorSetParams(uint8(i)))), - keccak256(abi.encode(operatorSetParams[i])) - ); - } - - // make sure the contract intializers are disabled - cheats.expectRevert(bytes("Initializable: contract is already initialized")); - registryCoordinator.initialize(churnApprover, ejector, operatorSetParams, pauserRegistry, 0/*initialPausedStatus*/); - } - - function testSetOperatorSetParams_NotServiceManagerOwner_Reverts() public { - cheats.expectRevert("BLSRegistryCoordinatorWithIndices.onlyServiceManagerOwner: caller is not the service manager owner"); - cheats.prank(defaultOperator); - registryCoordinator.setOperatorSetParams(0, operatorSetParams[0]); - } - - function testSetOperatorSetParams_Valid() public { - cheats.prank(serviceManagerOwner); - cheats.expectEmit(true, true, true, true, address(registryCoordinator)); - emit OperatorSetParamsUpdated(0, operatorSetParams[1]); - registryCoordinator.setOperatorSetParams(0, operatorSetParams[1]); - } - - function testSetChurnApprover_NotServiceManagerOwner_Reverts() public { - address newChurnApprover = address(uint160(uint256(keccak256("newChurnApprover")))); - cheats.expectRevert("BLSRegistryCoordinatorWithIndices.onlyServiceManagerOwner: caller is not the service manager owner"); - cheats.prank(defaultOperator); - registryCoordinator.setChurnApprover(newChurnApprover); - } - - function testSetChurnApprover_Valid() public { - address newChurnApprover = address(uint160(uint256(keccak256("newChurnApprover")))); - cheats.prank(serviceManagerOwner); - cheats.expectEmit(true, true, true, true, address(registryCoordinator)); - emit ChurnApproverUpdated(churnApprover, newChurnApprover); - registryCoordinator.setChurnApprover(newChurnApprover); - } - - function testSetEjector_NotServiceManagerOwner_Reverts() public { - address newEjector = address(uint160(uint256(keccak256("newEjector")))); - cheats.expectRevert("BLSRegistryCoordinatorWithIndices.onlyServiceManagerOwner: caller is not the service manager owner"); - cheats.prank(defaultOperator); - registryCoordinator.setEjector(newEjector); - } - - function testSetEjector_Valid() public { - address newEjector = address(uint160(uint256(keccak256("newEjector")))); - cheats.prank(serviceManagerOwner); - cheats.expectEmit(true, true, true, true, address(registryCoordinator)); - emit EjectorUpdated(ejector, newEjector); - registryCoordinator.setEjector(newEjector); - assertEq(registryCoordinator.ejector(), newEjector); - } - - function testRegisterOperatorWithCoordinator_WhenPaused_Reverts() public { - bytes memory emptyQuorumNumbers = new bytes(0); - // pause registerOperator - cheats.prank(pauser); - registryCoordinator.pause(2 ** PAUSED_REGISTER_OPERATOR); - - cheats.startPrank(defaultOperator); - cheats.expectRevert(bytes("Pausable: index is paused")); - registryCoordinator.registerOperatorWithCoordinator(emptyQuorumNumbers, defaultPubKey, defaultSocket); - } - - function testRegisterOperatorWithCoordinator_EmptyQuorumNumbers_Reverts() public { - bytes memory emptyQuorumNumbers = new bytes(0); - cheats.expectRevert("BLSRegistryCoordinatorWithIndices._registerOperatorWithCoordinator: quorumBitmap cannot be 0"); - cheats.prank(defaultOperator); - registryCoordinator.registerOperatorWithCoordinator(emptyQuorumNumbers, defaultPubKey, defaultSocket); - } - - function testRegisterOperatorWithCoordinator_QuorumNumbersTooLarge_Reverts() public { - bytes memory quorumNumbersTooLarge = new bytes(1); - quorumNumbersTooLarge[0] = 0xC0; - cheats.expectRevert("BLSRegistryCoordinatorWithIndices._registerOperatorWithCoordinator: quorumBitmap exceeds of max bitmap size"); - registryCoordinator.registerOperatorWithCoordinator(quorumNumbersTooLarge, defaultPubKey, defaultSocket); - } - - function testRegisterOperatorWithCoordinator_QuorumNotCreated_Reverts() public { - _deployMockEigenLayerAndAVS(10); - bytes memory quorumNumbersNotCreated = new bytes(1); - quorumNumbersNotCreated[0] = 0x0B; - cheats.prank(defaultOperator); - cheats.expectRevert("StakeRegistry._registerOperator: greatest quorumNumber must be less than quorumCount"); - registryCoordinator.registerOperatorWithCoordinator(quorumNumbersNotCreated, defaultPubKey, defaultSocket); - } - - function testRegisterOperatorWithCoordinatorForSingleQuorum_Valid() public { - bytes memory quorumNumbers = new bytes(1); - quorumNumbers[0] = bytes1(defaultQuorumNumber); - - stakeRegistry.setOperatorWeight(uint8(quorumNumbers[0]), defaultOperator, defaultStake); - - cheats.prank(defaultOperator); - cheats.expectEmit(true, true, true, true, address(blsPubkeyRegistry)); - emit OperatorAddedToQuorums(defaultOperator, quorumNumbers); - cheats.expectEmit(true, true, true, true, address(stakeRegistry)); - emit StakeUpdate(defaultOperatorId, defaultQuorumNumber, defaultStake); - cheats.expectEmit(true, true, true, true, address(indexRegistry)); - emit QuorumIndexUpdate(defaultOperatorId, defaultQuorumNumber, 0); - cheats.expectEmit(true, true, true, true, address(registryCoordinator)); - emit OperatorSocketUpdate(defaultOperatorId, defaultSocket); - - uint256 gasBefore = gasleft(); - registryCoordinator.registerOperatorWithCoordinator(quorumNumbers, defaultPubKey, defaultSocket); - uint256 gasAfter = gasleft(); - emit log_named_uint("gasUsed", gasBefore - gasAfter); - - uint256 quorumBitmap = BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers); - - assertEq(registryCoordinator.getOperatorId(defaultOperator), defaultOperatorId); - assertEq( - keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), - keccak256(abi.encode(IRegistryCoordinator.Operator({ - operatorId: defaultOperatorId, - status: IRegistryCoordinator.OperatorStatus.REGISTERED - }))) - ); - assertEq(registryCoordinator.getCurrentQuorumBitmapByOperatorId(defaultOperatorId), quorumBitmap); - assertEq( - keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByOperatorIdByIndex(defaultOperatorId, 0))), - keccak256(abi.encode(IRegistryCoordinator.QuorumBitmapUpdate({ - quorumBitmap: uint192(quorumBitmap), - updateBlockNumber: uint32(block.number), - nextUpdateBlockNumber: 0 - }))) - ); - } - - function testRegisterOperatorWithCoordinatorForFuzzedQuorums_Valid(uint256 quorumBitmap) public { - quorumBitmap = quorumBitmap & MAX_QUORUM_BITMAP; - cheats.assume(quorumBitmap != 0); - bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); - - for (uint i = 0; i < quorumNumbers.length; i++) { - stakeRegistry.setOperatorWeight(uint8(quorumNumbers[i]), defaultOperator, defaultStake); - } - - cheats.prank(defaultOperator); - cheats.expectEmit(true, true, true, true, address(blsPubkeyRegistry)); - emit OperatorAddedToQuorums(defaultOperator, quorumNumbers); - - for (uint i = 0; i < quorumNumbers.length; i++) { - cheats.expectEmit(true, true, true, true, address(stakeRegistry)); - emit StakeUpdate(defaultOperatorId, uint8(quorumNumbers[i]), defaultStake); - } - - for (uint i = 0; i < quorumNumbers.length; i++) { - cheats.expectEmit(true, true, true, true, address(indexRegistry)); - emit QuorumIndexUpdate(defaultOperatorId, uint8(quorumNumbers[i]), 0); - } - cheats.expectEmit(true, true, true, true, address(registryCoordinator)); - emit OperatorSocketUpdate(defaultOperatorId, defaultSocket); - - uint256 gasBefore = gasleft(); - registryCoordinator.registerOperatorWithCoordinator(quorumNumbers, defaultPubKey, defaultSocket); - uint256 gasAfter = gasleft(); - emit log_named_uint("gasUsed", gasBefore - gasAfter); - emit log_named_uint("numQuorums", quorumNumbers.length); - - assertEq(registryCoordinator.getOperatorId(defaultOperator), defaultOperatorId); - assertEq( - keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), - keccak256(abi.encode(IRegistryCoordinator.Operator({ - operatorId: defaultOperatorId, - status: IRegistryCoordinator.OperatorStatus.REGISTERED - }))) - ); - assertEq(registryCoordinator.getCurrentQuorumBitmapByOperatorId(defaultOperatorId), quorumBitmap); - assertEq( - keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByOperatorIdByIndex(defaultOperatorId, 0))), - keccak256(abi.encode(IRegistryCoordinator.QuorumBitmapUpdate({ - quorumBitmap: uint192(quorumBitmap), - updateBlockNumber: uint32(block.number), - nextUpdateBlockNumber: 0 - }))) - ); - } - - function testRegisterOperatorWithCoordinator_RegisteredOperatorForNewQuorums_Valid() public { - uint256 registrationBlockNumber = block.number + 100; - uint256 nextRegistrationBlockNumber = registrationBlockNumber + 100; - - bytes memory quorumNumbers = new bytes(1); - quorumNumbers[0] = bytes1(defaultQuorumNumber); - - stakeRegistry.setOperatorWeight(uint8(quorumNumbers[0]), defaultOperator, defaultStake); - cheats.prank(defaultOperator); - cheats.roll(registrationBlockNumber); - registryCoordinator.registerOperatorWithCoordinator(quorumNumbers, defaultPubKey, defaultSocket); - - bytes memory newQuorumNumbers = new bytes(1); - newQuorumNumbers[0] = bytes1(defaultQuorumNumber+1); - - stakeRegistry.setOperatorWeight(uint8(newQuorumNumbers[0]), defaultOperator, defaultStake); - cheats.prank(defaultOperator); - cheats.expectEmit(true, true, true, true, address(blsPubkeyRegistry)); - emit OperatorAddedToQuorums(defaultOperator, newQuorumNumbers); - cheats.expectEmit(true, true, true, true, address(stakeRegistry)); - emit StakeUpdate(defaultOperatorId, uint8(newQuorumNumbers[0]), defaultStake); - cheats.expectEmit(true, true, true, true, address(indexRegistry)); - emit QuorumIndexUpdate(defaultOperatorId, uint8(newQuorumNumbers[0]), 0); - cheats.expectEmit(true, true, true, true, address(registryCoordinator)); - emit OperatorSocketUpdate(defaultOperatorId, defaultSocket); - cheats.roll(nextRegistrationBlockNumber); - registryCoordinator.registerOperatorWithCoordinator(newQuorumNumbers, defaultPubKey, defaultSocket); - - uint256 quorumBitmap = BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers) | BitmapUtils.orderedBytesArrayToBitmap(newQuorumNumbers); - - assertEq(registryCoordinator.getOperatorId(defaultOperator), defaultOperatorId); - assertEq( - keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), - keccak256(abi.encode(IRegistryCoordinator.Operator({ - operatorId: defaultOperatorId, - status: IRegistryCoordinator.OperatorStatus.REGISTERED - }))) - ); - assertEq(registryCoordinator.getCurrentQuorumBitmapByOperatorId(defaultOperatorId), quorumBitmap); - assertEq( - keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByOperatorIdByIndex(defaultOperatorId, 0))), - keccak256(abi.encode(IRegistryCoordinator.QuorumBitmapUpdate({ - quorumBitmap: uint192(BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers)), - updateBlockNumber: uint32(registrationBlockNumber), - nextUpdateBlockNumber: uint32(nextRegistrationBlockNumber) - }))) - ); - assertEq( - keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByOperatorIdByIndex(defaultOperatorId, 1))), - keccak256(abi.encode(IRegistryCoordinator.QuorumBitmapUpdate({ - quorumBitmap: uint192(quorumBitmap), - updateBlockNumber: uint32(nextRegistrationBlockNumber), - nextUpdateBlockNumber: 0 - }))) - ); - } - - function testRegisterOperatorWithCoordinator_OverFilledQuorum_Reverts(uint256 pseudoRandomNumber) public { - uint32 numOperators = defaultMaxOperatorCount; - uint32 registrationBlockNumber = 200; - - bytes memory quorumNumbers = new bytes(1); - quorumNumbers[0] = bytes1(defaultQuorumNumber); - - uint256 quorumBitmap = BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers); - - cheats.roll(registrationBlockNumber); - - for (uint i = 0; i < numOperators; i++) { - BN254.G1Point memory pubKey = BN254.hashToG1(keccak256(abi.encodePacked(pseudoRandomNumber, i))); - address operator = _incrementAddress(defaultOperator, i); - - _registerOperatorWithCoordinator(operator, quorumBitmap, pubKey); - } - - address operatorToRegister = _incrementAddress(defaultOperator, numOperators); - BN254.G1Point memory operatorToRegisterPubKey = BN254.hashToG1(keccak256(abi.encodePacked(pseudoRandomNumber, numOperators))); - - pubkeyCompendium.setBLSPublicKey(operatorToRegister, operatorToRegisterPubKey); - - stakeRegistry.setOperatorWeight(defaultQuorumNumber, operatorToRegister, defaultStake); - - cheats.prank(operatorToRegister); - cheats.expectRevert("BLSRegistryCoordinatorWithIndices._registerOperatorWithCoordinatorAndNoOverfilledQuorums: quorum is overfilled"); - registryCoordinator.registerOperatorWithCoordinator(quorumNumbers, operatorToRegisterPubKey, defaultSocket); - } - - function testRegisterOperatorWithCoordinator_RegisteredOperatorForSameQuorums_Reverts() public { - uint256 registrationBlockNumber = block.number + 100; - uint256 nextRegistrationBlockNumber = registrationBlockNumber + 100; - - bytes memory quorumNumbers = new bytes(1); - quorumNumbers[0] = bytes1(defaultQuorumNumber); - - stakeRegistry.setOperatorWeight(uint8(quorumNumbers[0]), defaultOperator, defaultStake); - cheats.prank(defaultOperator); - cheats.roll(registrationBlockNumber); - registryCoordinator.registerOperatorWithCoordinator(quorumNumbers, defaultPubKey, defaultSocket); - - cheats.prank(defaultOperator); - cheats.roll(nextRegistrationBlockNumber); - cheats.expectRevert("BLSRegistryCoordinatorWithIndices._registerOperatorWithCoordinator: operator already registered for some quorums being registered for"); - registryCoordinator.registerOperatorWithCoordinator(quorumNumbers, defaultPubKey, defaultSocket); - } - - function testDeregisterOperatorWithCoordinator_WhenPaused_Reverts() public { - bytes memory quorumNumbers = new bytes(1); - quorumNumbers[0] = bytes1(defaultQuorumNumber); - uint256 quorumBitmap = BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers); - - _registerOperatorWithCoordinator(defaultOperator, quorumBitmap, defaultPubKey); - - // pause deregisterOperator - cheats.prank(pauser); - registryCoordinator.pause(2 ** PAUSED_DEREGISTER_OPERATOR); - - cheats.expectRevert(bytes("Pausable: index is paused")); - cheats.prank(defaultOperator); - registryCoordinator.deregisterOperatorWithCoordinator(quorumNumbers, defaultPubKey, new bytes32[](0)); - } - - function testDeregisterOperatorWithCoordinator_NotRegistered_Reverts() public { - bytes memory quorumNumbers = new bytes(1); - quorumNumbers[0] = bytes1(defaultQuorumNumber); - - cheats.expectRevert("BLSRegistryCoordinatorWithIndices._deregisterOperatorWithCoordinator: operator is not registered"); - cheats.prank(defaultOperator); - registryCoordinator.deregisterOperatorWithCoordinator(quorumNumbers, defaultPubKey, new bytes32[](0)); - } - - function testDeregisterOperatorWithCoordinator_IncorrectPubkey_Reverts() public { - bytes memory quorumNumbers = new bytes(1); - quorumNumbers[0] = bytes1(defaultQuorumNumber); - uint256 quorumBitmap = BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers); - - _registerOperatorWithCoordinator(defaultOperator, quorumBitmap, defaultPubKey); - - BN254.G1Point memory incorrectPubKey = BN254.hashToG1(bytes32(uint256(123))); - - cheats.expectRevert("BLSRegistryCoordinatorWithIndices._deregisterOperatorWithCoordinator: operatorId does not match pubkey hash"); - cheats.prank(defaultOperator); - registryCoordinator.deregisterOperatorWithCoordinator(quorumNumbers, incorrectPubKey, new bytes32[](0)); - } - - function testDeregisterOperatorWithCoordinator_IncorrectQuorums_Reverts() public { - bytes memory quorumNumbers = new bytes(1); - quorumNumbers[0] = bytes1(defaultQuorumNumber); - uint256 quorumBitmap = BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers); - - _registerOperatorWithCoordinator(defaultOperator, quorumBitmap, defaultPubKey); - - quorumNumbers = new bytes(2); - quorumNumbers[0] = bytes1(defaultQuorumNumber + 1); - quorumNumbers[1] = bytes1(defaultQuorumNumber + 2); - - cheats.expectRevert("BLSRegistryCoordinatorWithIndices._deregisterOperatorWithCoordinator: operator is not registered for any of the provided quorums"); - cheats.prank(defaultOperator); - registryCoordinator.deregisterOperatorWithCoordinator(quorumNumbers, defaultPubKey, new bytes32[](0)); - } - - function testDeregisterOperatorWithCoordinatorForSingleQuorumAndSingleOperator_Valid() public { - uint32 registrationBlockNumber = 100; - uint32 deregistrationBlockNumber = 200; - - bytes memory quorumNumbers = new bytes(1); - quorumNumbers[0] = bytes1(defaultQuorumNumber); - - stakeRegistry.setOperatorWeight(uint8(quorumNumbers[0]), defaultOperator, defaultStake); - - cheats.startPrank(defaultOperator); - - cheats.roll(registrationBlockNumber); - - registryCoordinator.registerOperatorWithCoordinator(quorumNumbers, defaultPubKey, defaultSocket); - - uint256 quorumBitmap = BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers); - - bytes32[] memory operatorIdsToSwap = new bytes32[](1); - operatorIdsToSwap[0] = defaultOperatorId; - - cheats.expectEmit(true, true, true, true, address(blsPubkeyRegistry)); - emit OperatorRemovedFromQuorums(defaultOperator, quorumNumbers); - cheats.expectEmit(true, true, true, true, address(stakeRegistry)); - emit StakeUpdate(defaultOperatorId, defaultQuorumNumber, 0); - - cheats.roll(deregistrationBlockNumber); - - uint256 gasBefore = gasleft(); - registryCoordinator.deregisterOperatorWithCoordinator(quorumNumbers, defaultPubKey, operatorIdsToSwap); - uint256 gasAfter = gasleft(); - emit log_named_uint("gasUsed", gasBefore - gasAfter); - - assertEq( - keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), - keccak256(abi.encode(IRegistryCoordinator.Operator({ - operatorId: defaultOperatorId, - status: IRegistryCoordinator.OperatorStatus.DEREGISTERED - }))) - ); - assertEq(registryCoordinator.getCurrentQuorumBitmapByOperatorId(defaultOperatorId), 0); - assertEq( - keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByOperatorIdByIndex(defaultOperatorId, 0))), - keccak256(abi.encode(IRegistryCoordinator.QuorumBitmapUpdate({ - quorumBitmap: uint192(quorumBitmap), - updateBlockNumber: registrationBlockNumber, - nextUpdateBlockNumber: deregistrationBlockNumber - }))) - ); - } - - function testDeregisterOperatorWithCoordinatorForFuzzedQuorumAndSingleOperator_Valid(uint256 quorumBitmap) public { - uint32 registrationBlockNumber = 100; - uint32 deregistrationBlockNumber = 200; - - quorumBitmap = quorumBitmap & MAX_QUORUM_BITMAP; - cheats.assume(quorumBitmap != 0); - bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); - - for (uint i = 0; i < quorumNumbers.length; i++) { - stakeRegistry.setOperatorWeight(uint8(quorumNumbers[i]), defaultOperator, defaultStake); - } - - cheats.startPrank(defaultOperator); - - cheats.roll(registrationBlockNumber); - - registryCoordinator.registerOperatorWithCoordinator(quorumNumbers, defaultPubKey, defaultSocket); - - bytes32[] memory operatorIdsToSwap = new bytes32[](quorumNumbers.length); - for (uint i = 0; i < operatorIdsToSwap.length; i++) { - operatorIdsToSwap[i] = defaultOperatorId; - } - - cheats.expectEmit(true, true, true, true, address(blsPubkeyRegistry)); - emit OperatorRemovedFromQuorums(defaultOperator, quorumNumbers); - for (uint i = 0; i < quorumNumbers.length; i++) { - cheats.expectEmit(true, true, true, true, address(stakeRegistry)); - emit StakeUpdate(defaultOperatorId, uint8(quorumNumbers[i]), 0); - } - - cheats.roll(deregistrationBlockNumber); - - uint256 gasBefore = gasleft(); - registryCoordinator.deregisterOperatorWithCoordinator(quorumNumbers, defaultPubKey, operatorIdsToSwap); - uint256 gasAfter = gasleft(); - emit log_named_uint("gasUsed", gasBefore - gasAfter); - emit log_named_uint("numQuorums", quorumNumbers.length); - - assertEq( - keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), - keccak256(abi.encode(IRegistryCoordinator.Operator({ - operatorId: defaultOperatorId, - status: IRegistryCoordinator.OperatorStatus.DEREGISTERED - }))) - ); - assertEq(registryCoordinator.getCurrentQuorumBitmapByOperatorId(defaultOperatorId), 0); - assertEq( - keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByOperatorIdByIndex(defaultOperatorId, 0))), - keccak256(abi.encode(IRegistryCoordinator.QuorumBitmapUpdate({ - quorumBitmap: uint192(quorumBitmap), - updateBlockNumber: registrationBlockNumber, - nextUpdateBlockNumber: deregistrationBlockNumber - }))) - ); - } - - function testDeregisterOperatorWithCoordinatorForFuzzedQuorumAndManyOperators_Valid(uint256 pseudoRandomNumber) public { - uint32 numOperators = defaultMaxOperatorCount; - - uint32 registrationBlockNumber = 100; - uint32 deregistrationBlockNumber = 200; - - // pad quorumBitmap with 1 until it has numOperators elements - uint256[] memory quorumBitmaps = new uint256[](numOperators); - for (uint i = 0; i < numOperators; i++) { - // limit to maxQuorumsToRegisterFor quorums via mask so we don't run out of gas, make them all register for quorum 0 as well - quorumBitmaps[i] = uint256(keccak256(abi.encodePacked("quorumBitmap", pseudoRandomNumber, i))) & (1 << maxQuorumsToRegisterFor - 1) | 1; - } - - cheats.roll(registrationBlockNumber); - - bytes32[] memory lastOperatorInQuorum = new bytes32[](numQuorums); - for (uint i = 0; i < numOperators; i++) { - emit log_named_uint("i", i); - BN254.G1Point memory pubKey = BN254.hashToG1(keccak256(abi.encodePacked(pseudoRandomNumber, i))); - bytes32 operatorId = pubKey.hashG1Point(); - address operator = _incrementAddress(defaultOperator, i); - - _registerOperatorWithCoordinator(operator, quorumBitmaps[i], pubKey); - - // for each quorum the operator is in, save the operatorId - bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmaps[i]); - for (uint j = 0; j < quorumNumbers.length; j++) { - lastOperatorInQuorum[uint8(quorumNumbers[j])] = operatorId; - } - } - - uint256 indexOfOperatorToDerigister = pseudoRandomNumber % numOperators; - address operatorToDerigister = _incrementAddress(defaultOperator, indexOfOperatorToDerigister); - BN254.G1Point memory operatorToDeregisterPubKey = BN254.hashToG1(keccak256(abi.encodePacked(pseudoRandomNumber, indexOfOperatorToDerigister))); - bytes32 operatorToDerigisterId = operatorToDeregisterPubKey.hashG1Point(); - uint256 operatorToDeregisterQuorumBitmap = quorumBitmaps[indexOfOperatorToDerigister]; - bytes memory operatorToDeregisterQuorumNumbers = BitmapUtils.bitmapToBytesArray(operatorToDeregisterQuorumBitmap); - - bytes32[] memory operatorIdsToSwap = new bytes32[](operatorToDeregisterQuorumNumbers.length); - for (uint i = 0; i < operatorToDeregisterQuorumNumbers.length; i++) { - operatorIdsToSwap[i] = lastOperatorInQuorum[uint8(operatorToDeregisterQuorumNumbers[i])]; - } - - cheats.expectEmit(true, true, true, true, address(blsPubkeyRegistry)); - emit OperatorRemovedFromQuorums(operatorToDerigister, operatorToDeregisterQuorumNumbers); - - for (uint i = 0; i < operatorToDeregisterQuorumNumbers.length; i++) { - cheats.expectEmit(true, true, true, true, address(stakeRegistry)); - emit StakeUpdate(operatorToDerigisterId, uint8(operatorToDeregisterQuorumNumbers[i]), 0); - } - - // expect events from the index registry - for (uint i = 0; i < operatorToDeregisterQuorumNumbers.length; i++) { - if(operatorIdsToSwap[i] != operatorToDerigisterId) { - cheats.expectEmit(true, true, false, false, address(indexRegistry)); - emit QuorumIndexUpdate(operatorIdsToSwap[i], uint8(operatorToDeregisterQuorumNumbers[i]), 0); - } - } - - cheats.roll(deregistrationBlockNumber); - - cheats.prank(operatorToDerigister); - registryCoordinator.deregisterOperatorWithCoordinator(operatorToDeregisterQuorumNumbers, operatorToDeregisterPubKey, operatorIdsToSwap); - - assertEq( - keccak256(abi.encode(registryCoordinator.getOperator(operatorToDerigister))), - keccak256(abi.encode(IRegistryCoordinator.Operator({ - operatorId: operatorToDerigisterId, - status: IRegistryCoordinator.OperatorStatus.DEREGISTERED - }))) - ); - assertEq(registryCoordinator.getCurrentQuorumBitmapByOperatorId(defaultOperatorId), 0); - assertEq( - keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByOperatorIdByIndex(operatorToDerigisterId, 0))), - keccak256(abi.encode(IRegistryCoordinator.QuorumBitmapUpdate({ - quorumBitmap: uint192(operatorToDeregisterQuorumBitmap), - updateBlockNumber: registrationBlockNumber, - nextUpdateBlockNumber: deregistrationBlockNumber - }))) - ); - } - - - function testRegisterOperatorWithCoordinatorWithKicks_Valid(uint256 pseudoRandomNumber) public { - uint32 numOperators = defaultMaxOperatorCount; - uint32 kickRegistrationBlockNumber = 100; - uint32 registrationBlockNumber = 200; - - bytes memory quorumNumbers = new bytes(1); - quorumNumbers[0] = bytes1(defaultQuorumNumber); - - uint256 quorumBitmap = BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers); - - cheats.roll(kickRegistrationBlockNumber); - - for (uint i = 0; i < numOperators - 1; i++) { - BN254.G1Point memory pubKey = BN254.hashToG1(keccak256(abi.encodePacked(pseudoRandomNumber, i))); - address operator = _incrementAddress(defaultOperator, i); - - _registerOperatorWithCoordinator(operator, quorumBitmap, pubKey); - } - - address operatorToRegister = _incrementAddress(defaultOperator, numOperators); - BN254.G1Point memory operatorToRegisterPubKey = BN254.hashToG1(keccak256(abi.encodePacked(pseudoRandomNumber, numOperators))); - bytes32 operatorToRegisterId = operatorToRegisterPubKey.hashG1Point(); - bytes32 operatorToKickId; - address operatorToKick; - - // register last operator before kick - IBLSRegistryCoordinatorWithIndices.OperatorKickParam[] memory operatorKickParams = new IBLSRegistryCoordinatorWithIndices.OperatorKickParam[](1); - { - BN254.G1Point memory pubKey = BN254.hashToG1(keccak256(abi.encodePacked(pseudoRandomNumber, numOperators - 1))); - operatorToKickId = pubKey.hashG1Point(); - operatorToKick = _incrementAddress(defaultOperator, numOperators - 1); - - _registerOperatorWithCoordinator(operatorToKick, quorumBitmap, pubKey); - - bytes32[] memory operatorIdsToSwap = new bytes32[](1); - // operatorIdsToSwap[0] = operatorToRegisterId - operatorIdsToSwap[0] = operatorToRegisterId; - - operatorKickParams[0] = IBLSRegistryCoordinatorWithIndices.OperatorKickParam({ - quorumNumber: defaultQuorumNumber, - operator: operatorToKick, - pubkey: pubKey - }); - } - - pubkeyCompendium.setBLSPublicKey(operatorToRegister, operatorToRegisterPubKey); - - uint96 registeringStake = defaultKickBIPsOfOperatorStake * defaultStake; - stakeRegistry.setOperatorWeight(defaultQuorumNumber, operatorToRegister, registeringStake); - - cheats.roll(registrationBlockNumber); - cheats.expectEmit(true, true, true, true, address(blsPubkeyRegistry)); - emit OperatorAddedToQuorums(operatorToRegister, quorumNumbers); - cheats.expectEmit(true, true, true, true, address(stakeRegistry)); - emit StakeUpdate(operatorToRegisterId, defaultQuorumNumber, registeringStake); - cheats.expectEmit(true, true, true, true, address(indexRegistry)); - emit QuorumIndexUpdate(operatorToRegisterId, defaultQuorumNumber, numOperators); - - cheats.expectEmit(true, true, true, true, address(blsPubkeyRegistry)); - emit OperatorRemovedFromQuorums(operatorKickParams[0].operator, quorumNumbers); - cheats.expectEmit(true, true, true, true, address(stakeRegistry)); - emit StakeUpdate(operatorToKickId, defaultQuorumNumber, 0); - cheats.expectEmit(true, true, true, true, address(indexRegistry)); - emit QuorumIndexUpdate(operatorToRegisterId, defaultQuorumNumber, numOperators - 1); - - { - ISignatureUtils.SignatureWithSaltAndExpiry memory signatureWithExpiry = _signOperatorChurnApproval(operatorToRegisterId, operatorKickParams, defaultSalt, block.timestamp + 10); - cheats.prank(operatorToRegister); - uint256 gasBefore = gasleft(); - registryCoordinator.registerOperatorWithCoordinator( - quorumNumbers, - operatorToRegisterPubKey, - defaultSocket, - operatorKickParams, - signatureWithExpiry - ); - uint256 gasAfter = gasleft(); - emit log_named_uint("gasUsed", gasBefore - gasAfter); - } - - assertEq( - keccak256(abi.encode(registryCoordinator.getOperator(operatorToRegister))), - keccak256(abi.encode(IRegistryCoordinator.Operator({ - operatorId: operatorToRegisterId, - status: IRegistryCoordinator.OperatorStatus.REGISTERED - }))) - ); - assertEq( - keccak256(abi.encode(registryCoordinator.getOperator(operatorToKick))), - keccak256(abi.encode(IRegistryCoordinator.Operator({ - operatorId: operatorToKickId, - status: IRegistryCoordinator.OperatorStatus.DEREGISTERED - }))) - ); - assertEq( - keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByOperatorIdByIndex(operatorToKickId, 0))), - keccak256(abi.encode(IRegistryCoordinator.QuorumBitmapUpdate({ - quorumBitmap: uint192(quorumBitmap), - updateBlockNumber: kickRegistrationBlockNumber, - nextUpdateBlockNumber: registrationBlockNumber - }))) - ); - } - - function testRegisterOperatorWithCoordinatorWithKicks_LessThanKickBIPsOfOperatorStake_Reverts(uint256 pseudoRandomNumber) public { - bytes memory quorumNumbers = new bytes(1); - quorumNumbers[0] = bytes1(defaultQuorumNumber); - - ( - address operatorToRegister, - BN254.G1Point memory operatorToRegisterPubKey, - IBLSRegistryCoordinatorWithIndices.OperatorKickParam[] memory operatorKickParams - ) = _testRegisterOperatorWithKicks_SetUp(pseudoRandomNumber, quorumNumbers, defaultStake); - bytes32 operatorToRegisterId = operatorToRegisterPubKey.hashG1Point(); - - stakeRegistry.setOperatorWeight(defaultQuorumNumber, operatorToRegister, defaultStake); - - cheats.roll(registrationBlockNumber); - ISignatureUtils.SignatureWithSaltAndExpiry memory signatureWithExpiry = _signOperatorChurnApproval(operatorToRegisterId, operatorKickParams, defaultSalt, block.timestamp + 10); - cheats.prank(operatorToRegister); - cheats.expectRevert("BLSRegistryCoordinatorWithIndices.registerOperatorWithCoordinator: registering operator has less than kickBIPsOfOperatorStake"); - registryCoordinator.registerOperatorWithCoordinator(quorumNumbers, operatorToRegisterPubKey, defaultSocket, operatorKickParams, signatureWithExpiry); - } - - function testRegisterOperatorWithCoordinatorWithKicks_LessThanKickBIPsOfTotalStake_Reverts(uint256 pseudoRandomNumber) public { - bytes memory quorumNumbers = new bytes(1); - quorumNumbers[0] = bytes1(defaultQuorumNumber); - - uint96 operatorToKickStake = defaultMaxOperatorCount * defaultStake; - ( - address operatorToRegister, - BN254.G1Point memory operatorToRegisterPubKey, - IBLSRegistryCoordinatorWithIndices.OperatorKickParam[] memory operatorKickParams - ) = _testRegisterOperatorWithKicks_SetUp(pseudoRandomNumber, quorumNumbers, operatorToKickStake); - bytes32 operatorToRegisterId = operatorToRegisterPubKey.hashG1Point(); - - - // set the stake of the operator to register to the defaultKickBIPsOfOperatorStake multiple of the operatorToKickStake - stakeRegistry.setOperatorWeight(defaultQuorumNumber, operatorToRegister, operatorToKickStake * defaultKickBIPsOfOperatorStake / 10000 + 1); - - cheats.roll(registrationBlockNumber); - ISignatureUtils.SignatureWithSaltAndExpiry memory signatureWithExpiry = _signOperatorChurnApproval(operatorToRegisterId, operatorKickParams, defaultSalt, block.timestamp + 10); - cheats.prank(operatorToRegister); - cheats.expectRevert("BLSRegistryCoordinatorWithIndices.registerOperatorWithCoordinator: operator to kick has more than kickBIPSOfTotalStake"); - registryCoordinator.registerOperatorWithCoordinator(quorumNumbers, operatorToRegisterPubKey, defaultSocket, operatorKickParams, signatureWithExpiry); - } - - function testRegisterOperatorWithCoordinatorWithKicks_InvalidSignatures_Reverts(uint256 pseudoRandomNumber) public { - bytes memory quorumNumbers = new bytes(1); - quorumNumbers[0] = bytes1(defaultQuorumNumber); - - ( - address operatorToRegister, - BN254.G1Point memory operatorToRegisterPubKey, - IBLSRegistryCoordinatorWithIndices.OperatorKickParam[] memory operatorKickParams - ) = _testRegisterOperatorWithKicks_SetUp(pseudoRandomNumber, quorumNumbers, defaultStake); - - uint96 registeringStake = defaultKickBIPsOfOperatorStake * defaultStake; - stakeRegistry.setOperatorWeight(defaultQuorumNumber, operatorToRegister, registeringStake); - - cheats.roll(registrationBlockNumber); - ISignatureUtils.SignatureWithSaltAndExpiry memory signatureWithSaltAndExpiry; - signatureWithSaltAndExpiry.expiry = block.timestamp + 10; - signatureWithSaltAndExpiry.signature = hex"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001B"; - signatureWithSaltAndExpiry.salt = defaultSalt; - cheats.prank(operatorToRegister); - cheats.expectRevert("ECDSA: invalid signature"); - registryCoordinator.registerOperatorWithCoordinator(quorumNumbers, operatorToRegisterPubKey, defaultSocket, operatorKickParams, signatureWithSaltAndExpiry); - } - - function testRegisterOperatorWithCoordinatorWithKicks_ExpiredSignatures_Reverts(uint256 pseudoRandomNumber) public { - bytes memory quorumNumbers = new bytes(1); - quorumNumbers[0] = bytes1(defaultQuorumNumber); - - ( - address operatorToRegister, - BN254.G1Point memory operatorToRegisterPubKey, - IBLSRegistryCoordinatorWithIndices.OperatorKickParam[] memory operatorKickParams - ) = _testRegisterOperatorWithKicks_SetUp(pseudoRandomNumber, quorumNumbers, defaultStake); - bytes32 operatorToRegisterId = operatorToRegisterPubKey.hashG1Point(); - - uint96 registeringStake = defaultKickBIPsOfOperatorStake * defaultStake; - stakeRegistry.setOperatorWeight(defaultQuorumNumber, operatorToRegister, registeringStake); - - cheats.roll(registrationBlockNumber); - ISignatureUtils.SignatureWithSaltAndExpiry memory signatureWithSaltAndExpiry = _signOperatorChurnApproval(operatorToRegisterId, operatorKickParams, defaultSalt, block.timestamp - 1); - cheats.prank(operatorToRegister); - cheats.expectRevert("BLSRegistryCoordinatorWithIndices._verifyChurnApproverSignatureOnOperatorChurnApproval: churnApprover signature expired"); - registryCoordinator.registerOperatorWithCoordinator(quorumNumbers, operatorToRegisterPubKey, defaultSocket, operatorKickParams, signatureWithSaltAndExpiry); - } - - function testEjectOperatorFromCoordinator_AllQuorums_Valid() public { - // register operator with default stake with default quorum number - bytes memory quorumNumbers = new bytes(1); - quorumNumbers[0] = bytes1(defaultQuorumNumber); - - stakeRegistry.setOperatorWeight(uint8(quorumNumbers[0]), defaultOperator, defaultStake); - - cheats.prank(defaultOperator); - registryCoordinator.registerOperatorWithCoordinator(quorumNumbers, defaultPubKey, defaultSocket); - - // operator is the only one registered so they are the one with the largest index - bytes32[] memory operatorIdsToSwap = new bytes32[](1); - operatorIdsToSwap[0] = defaultOperatorId; - - cheats.expectEmit(true, true, true, true, address(blsPubkeyRegistry)); - emit OperatorRemovedFromQuorums(defaultOperator, quorumNumbers); - - cheats.expectEmit(true, true, true, true, address(stakeRegistry)); - emit StakeUpdate(defaultOperatorId, uint8(quorumNumbers[0]), 0); - - // eject - cheats.prank(ejector); - registryCoordinator.ejectOperatorFromCoordinator(defaultOperator, quorumNumbers, defaultPubKey, operatorIdsToSwap); - - // make sure the operator is deregistered - assertEq( - keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), - keccak256(abi.encode(IRegistryCoordinator.Operator({ - operatorId: defaultOperatorId, - status: IRegistryCoordinator.OperatorStatus.DEREGISTERED - }))) - ); - // make sure the operator is not in any quorums - assertEq(registryCoordinator.getCurrentQuorumBitmapByOperatorId(defaultOperatorId), 0); - } - - function testEjectOperatorFromCoordinator_SubsetOfQuorums_Valid() public { - // register operator with default stake with 2 quorums - bytes memory quorumNumbers = new bytes(2); - quorumNumbers[0] = bytes1(defaultQuorumNumber); - quorumNumbers[1] = bytes1(defaultQuorumNumber + 1); - - for (uint i = 0; i < quorumNumbers.length; i++) { - stakeRegistry.setOperatorWeight(uint8(quorumNumbers[i]), defaultOperator, defaultStake); - } - - cheats.prank(defaultOperator); - registryCoordinator.registerOperatorWithCoordinator(quorumNumbers, defaultPubKey, defaultSocket); - - // eject from only first quorum - bytes memory quorumNumbersToEject = new bytes(1); - quorumNumbersToEject[0] = quorumNumbers[0]; - - // operator is the only one registered so they are the one with the largest index - bytes32[] memory operatorIdsToSwap = new bytes32[](1); - operatorIdsToSwap[0] = defaultOperatorId; - - cheats.expectEmit(true, true, true, true, address(blsPubkeyRegistry)); - emit OperatorRemovedFromQuorums(defaultOperator, quorumNumbersToEject); - - cheats.expectEmit(true, true, true, true, address(stakeRegistry)); - emit StakeUpdate(defaultOperatorId, uint8(quorumNumbersToEject[0]), 0); - - cheats.prank(ejector); - registryCoordinator.ejectOperatorFromCoordinator(defaultOperator, quorumNumbersToEject, defaultPubKey, operatorIdsToSwap); - - // make sure the operator is registered - assertEq( - keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), - keccak256(abi.encode(IRegistryCoordinator.Operator({ - operatorId: defaultOperatorId, - status: IRegistryCoordinator.OperatorStatus.REGISTERED - }))) - ); - // make sure the operator is not in any quorums - assertEq( - registryCoordinator.getCurrentQuorumBitmapByOperatorId(defaultOperatorId), - BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers) & ~BitmapUtils.orderedBytesArrayToBitmap(quorumNumbersToEject) // quorumsRegisteredFor & ~quorumsEjectedFrom - ); - } - - function testEjectOperatorFromCoordinator_NotEjector_Reverts() public { - bytes memory quorumNumbers = new bytes(1); - quorumNumbers[0] = bytes1(defaultQuorumNumber); - - stakeRegistry.setOperatorWeight(uint8(quorumNumbers[0]), defaultOperator, defaultStake); - - cheats.prank(defaultOperator); - registryCoordinator.registerOperatorWithCoordinator(quorumNumbers, defaultPubKey, defaultSocket); - - bytes32[] memory operatorIdsToSwap = new bytes32[](1); - operatorIdsToSwap[0] = defaultOperatorId; - - cheats.expectRevert("BLSRegistryCoordinatorWithIndices.onlyEjector: caller is not the ejector"); - cheats.prank(defaultOperator); - registryCoordinator.ejectOperatorFromCoordinator(defaultOperator, quorumNumbers, defaultPubKey, operatorIdsToSwap); - } - - function testUpdateSocket() public { - bytes memory quorumNumbers = new bytes(1); - quorumNumbers[0] = bytes1(defaultQuorumNumber); - - uint256 quorumBitmap = BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers); - _registerOperatorWithCoordinator(defaultOperator, quorumBitmap, defaultPubKey); - - cheats.prank(defaultOperator); - cheats.expectEmit(true, true, true, true, address(registryCoordinator)); - emit OperatorSocketUpdate(defaultOperatorId, "localhost:32004"); - registryCoordinator.updateSocket("localhost:32004"); - - } - - function testUpdateSocket_NotRegistered_Reverts() public { - cheats.prank(defaultOperator); - cheats.expectRevert("BLSRegistryCoordinatorWithIndicies.updateSocket: operator is not registered"); - registryCoordinator.updateSocket("localhost:32004"); - } - - function _testRegisterOperatorWithKicks_SetUp(uint256 pseudoRandomNumber, bytes memory quorumNumbers, uint96 operatorToKickStake) internal returns(address operatorToRegister, BN254.G1Point memory operatorToRegisterPubKey, IBLSRegistryCoordinatorWithIndices.OperatorKickParam[] memory operatorKickParams) { - uint32 kickRegistrationBlockNumber = 100; - - uint256 quorumBitmap = BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers); - - cheats.roll(kickRegistrationBlockNumber); - - for (uint i = 0; i < defaultMaxOperatorCount - 1; i++) { - BN254.G1Point memory pubKey = BN254.hashToG1(keccak256(abi.encodePacked(pseudoRandomNumber, i))); - address operator = _incrementAddress(defaultOperator, i); - - _registerOperatorWithCoordinator(operator, quorumBitmap, pubKey); - } - - operatorToRegister = _incrementAddress(defaultOperator, defaultMaxOperatorCount); - operatorToRegisterPubKey = BN254.hashToG1(keccak256(abi.encodePacked(pseudoRandomNumber, defaultMaxOperatorCount))); - bytes32 operatorToRegisterId = operatorToRegisterPubKey.hashG1Point(); - bytes32 operatorToKickId; - address operatorToKick; - - // register last operator before kick - operatorKickParams = new IBLSRegistryCoordinatorWithIndices.OperatorKickParam[](1); - { - BN254.G1Point memory pubKey = BN254.hashToG1(keccak256(abi.encodePacked(pseudoRandomNumber, defaultMaxOperatorCount - 1))); - operatorToKickId = pubKey.hashG1Point(); - operatorToKick = _incrementAddress(defaultOperator, defaultMaxOperatorCount - 1); - - // register last operator with much more than the kickBIPsOfTotalStake stake - _registerOperatorWithCoordinator(operatorToKick, quorumBitmap, pubKey, operatorToKickStake); - - bytes32[] memory operatorIdsToSwap = new bytes32[](1); - // operatorIdsToSwap[0] = operatorToRegisterId - operatorIdsToSwap[0] = operatorToRegisterId; - - operatorKickParams[0] = IBLSRegistryCoordinatorWithIndices.OperatorKickParam({ - quorumNumber: uint8(quorumNumbers[0]), - operator: operatorToKick, - pubkey: pubKey - }); - } - - pubkeyCompendium.setBLSPublicKey(operatorToRegister, operatorToRegisterPubKey); - } -} \ No newline at end of file diff --git a/src/test/unit/BLSSignatureCheckerUnit.t.sol b/src/test/unit/BLSSignatureCheckerUnit.t.sol deleted file mode 100644 index f2283e5dc..000000000 --- a/src/test/unit/BLSSignatureCheckerUnit.t.sol +++ /dev/null @@ -1,240 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity =0.8.12; - -import "../../contracts/middleware/BLSSignatureChecker.sol"; -import "../utils/BLSMockAVSDeployer.sol"; - -contract BLSSignatureCheckerUnitTests is BLSMockAVSDeployer { - using BN254 for BN254.G1Point; - - BLSSignatureChecker blsSignatureChecker; - - function setUp() virtual public { - _setUpBLSMockAVSDeployer(); - - blsSignatureChecker = new BLSSignatureChecker(registryCoordinator); - } - - // this test checks that a valid signature from maxOperatorsToRegister with a random number of nonsigners is checked - // correctly on the BLSSignatureChecker contract when all operators are only regsitered for a single quorum and - // the signature is only checked for stakes on that quorum - function testBLSSignatureChecker_SingleQuorum_Valid(uint256 pseudoRandomNumber) public { - uint256 numNonSigners = pseudoRandomNumber % (maxOperatorsToRegister - 1); - - uint256 quorumBitmap = 1; - bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); - - (uint32 referenceBlockNumber, BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature) = - _registerSignatoriesAndGetNonSignerStakeAndSignatureRandom(pseudoRandomNumber, numNonSigners, quorumBitmap); - - uint256 gasBefore = gasleft(); - ( - BLSSignatureChecker.QuorumStakeTotals memory quorumStakeTotals, - /* bytes32 signatoryRecordHash */ - ) = blsSignatureChecker.checkSignatures( - msgHash, - quorumNumbers, - referenceBlockNumber, - nonSignerStakesAndSignature - ); - uint256 gasAfter = gasleft(); - emit log_named_uint("gasUsed", gasBefore - gasAfter); - assertTrue(quorumStakeTotals.signedStakeForQuorum[0] > 0); - - // 0 nonSigners: 159908 - // 1 nonSigner: 178683 - // 2 nonSigners: 197410 - } - - // this test checks that a valid signature from maxOperatorsToRegister with a random number of nonsigners is checked - // correctly on the BLSSignatureChecker contract when all operators are registered for the first 100 quorums - // and the signature is only checked for stakes on those quorums - function testBLSSignatureChecker_100Quorums_Valid(uint256 pseudoRandomNumber) public { - uint256 numNonSigners = pseudoRandomNumber % (maxOperatorsToRegister - 1); - - // 100 set bits - uint256 quorumBitmap = (1 << 100) - 1; - bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); - - (uint32 referenceBlockNumber, BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature) = - _registerSignatoriesAndGetNonSignerStakeAndSignatureRandom(pseudoRandomNumber, numNonSigners, quorumBitmap); - - nonSignerStakesAndSignature.sigma = sigma.scalar_mul(quorumNumbers.length); - nonSignerStakesAndSignature.apkG2 = oneHundredQuorumApkG2; - - uint256 gasBefore = gasleft(); - blsSignatureChecker.checkSignatures( - msgHash, - quorumNumbers, - referenceBlockNumber, - nonSignerStakesAndSignature - ); - uint256 gasAfter = gasleft(); - emit log_named_uint("gasUsed", gasBefore - gasAfter); - } - - function testBLSSignatureChecker_IncorrectQuorumBitmapIndex_Reverts(uint256 pseudoRandomNumber) public { - uint256 numNonSigners = pseudoRandomNumber % (maxOperatorsToRegister - 2) + 1; - - uint256 quorumBitmap = 1; - bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); - - (uint32 referenceBlockNumber, BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature) = - _registerSignatoriesAndGetNonSignerStakeAndSignatureRandom(pseudoRandomNumber, numNonSigners, quorumBitmap); - - // record a quorumBitmap update - registryCoordinator.recordOperatorQuorumBitmapUpdate(nonSignerStakesAndSignature.nonSignerPubkeys[0].hashG1Point(), uint192(quorumBitmap | 2)); - - // set the nonSignerQuorumBitmapIndices to a different value - nonSignerStakesAndSignature.nonSignerQuorumBitmapIndices[0] = 1; - - cheats.expectRevert("BLSRegistryCoordinatorWithIndices.getQuorumBitmapByOperatorIdAtBlockNumberByIndex: quorumBitmapUpdate is from after blockNumber"); - blsSignatureChecker.checkSignatures( - msgHash, - quorumNumbers, - referenceBlockNumber, - nonSignerStakesAndSignature - ); - } - - function testBLSSignatureChecker_IncorrectTotalStakeIndex_Reverts(uint256 pseudoRandomNumber) public { - uint256 numNonSigners = pseudoRandomNumber % (maxOperatorsToRegister - 2) + 1; - - uint256 quorumBitmap = 1; - bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); - - (uint32 referenceBlockNumber, BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature) = - _registerSignatoriesAndGetNonSignerStakeAndSignatureRandom(pseudoRandomNumber, numNonSigners, quorumBitmap); - - // set the totalStakeIndices to a different value - nonSignerStakesAndSignature.totalStakeIndices[0] = 0; - - cheats.expectRevert("StakeRegistry._validateOperatorStakeAtBlockNumber: there is a newer operatorStakeUpdate available before blockNumber"); - blsSignatureChecker.checkSignatures( - msgHash, - quorumNumbers, - referenceBlockNumber, - nonSignerStakesAndSignature - ); - } - - function testBLSSignatureChecker_IncorrectNonSignerStakeIndex_Reverts(uint256 pseudoRandomNumber) public { - uint256 numNonSigners = pseudoRandomNumber % (maxOperatorsToRegister - 2) + 1; - - uint256 quorumBitmap = 1; - bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); - - (uint32 referenceBlockNumber, BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature) = - _registerSignatoriesAndGetNonSignerStakeAndSignatureRandom(pseudoRandomNumber, numNonSigners, quorumBitmap); - - bytes32 nonSignerOperatorId = nonSignerStakesAndSignature.nonSignerPubkeys[0].hashG1Point(); - - // record a stake update - stakeRegistry.recordOperatorStakeUpdate( - nonSignerOperatorId, - uint8(quorumNumbers[0]), - IStakeRegistry.OperatorStakeUpdate({ - updateBlockNumber: uint32(block.number), - nextUpdateBlockNumber: 0, - stake: 1234 - }) - ); - - // set the nonSignerStakeIndices to a different value - nonSignerStakesAndSignature.nonSignerStakeIndices[0][0] = 1; - - cheats.expectRevert("StakeRegistry._validateOperatorStakeAtBlockNumber: operatorStakeUpdate is from after blockNumber"); - blsSignatureChecker.checkSignatures( - msgHash, - quorumNumbers, - referenceBlockNumber, - nonSignerStakesAndSignature - ); - - } - - function testBLSSignatureChecker_IncorrectQuorumAPKIndex_Reverts(uint256 pseudoRandomNumber) public { - uint256 numNonSigners = pseudoRandomNumber % (maxOperatorsToRegister - 2) + 1; - - uint256 quorumBitmap = 1; - bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); - - (uint32 referenceBlockNumber, BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature) = - _registerSignatoriesAndGetNonSignerStakeAndSignatureRandom(pseudoRandomNumber, numNonSigners, quorumBitmap); - - // set the quorumApkIndices to a different value - nonSignerStakesAndSignature.quorumApkIndices[0] = 0; - - cheats.expectRevert("BLSPubkeyRegistry._validateApkHashForQuorumAtBlockNumber: not latest apk update"); - blsSignatureChecker.checkSignatures( - msgHash, - quorumNumbers, - referenceBlockNumber, - nonSignerStakesAndSignature - ); - } - - function testBLSSignatureChecker_IncorrectQuorumAPK_Reverts(uint256 pseudoRandomNumber) public { - uint256 numNonSigners = pseudoRandomNumber % (maxOperatorsToRegister - 2) + 1; - - uint256 quorumBitmap = 1; - bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); - - (uint32 referenceBlockNumber, BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature) = - _registerSignatoriesAndGetNonSignerStakeAndSignatureRandom(pseudoRandomNumber, numNonSigners, quorumBitmap); - - // set the quorumApk to a different value - nonSignerStakesAndSignature.quorumApks[0] = nonSignerStakesAndSignature.quorumApks[0].negate(); - - cheats.expectRevert("BLSSignatureChecker.checkSignatures: quorumApk hash in storage does not match provided quorum apk"); - blsSignatureChecker.checkSignatures( - msgHash, - quorumNumbers, - referenceBlockNumber, - nonSignerStakesAndSignature - ); - } - - function testBLSSignatureChecker_IncorrectSignature_Reverts(uint256 pseudoRandomNumber) public { - uint256 numNonSigners = pseudoRandomNumber % (maxOperatorsToRegister - 2) + 1; - - uint256 quorumBitmap = 1; - bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); - - (uint32 referenceBlockNumber, BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature) = - _registerSignatoriesAndGetNonSignerStakeAndSignatureRandom(pseudoRandomNumber, numNonSigners, quorumBitmap); - - // set the sigma to a different value - nonSignerStakesAndSignature.sigma = nonSignerStakesAndSignature.sigma.negate(); - - cheats.expectRevert("BLSSignatureChecker.checkSignatures: signature is invalid"); - blsSignatureChecker.checkSignatures( - msgHash, - quorumNumbers, - referenceBlockNumber, - nonSignerStakesAndSignature - ); - } - - function testBLSSignatureChecker_InvalidSignature_Reverts(uint256 pseudoRandomNumber) public { - uint256 numNonSigners = pseudoRandomNumber % (maxOperatorsToRegister - 2) + 1; - - uint256 quorumBitmap = 1; - bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); - - (uint32 referenceBlockNumber, BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature) = - _registerSignatoriesAndGetNonSignerStakeAndSignatureRandom(pseudoRandomNumber, numNonSigners, quorumBitmap); - - // set the sigma to a different value - nonSignerStakesAndSignature.sigma.X++; - - // expect a non-specific low-level revert, since this call will ultimately fail as part of the precompile call - cheats.expectRevert(); - blsSignatureChecker.checkSignatures( - msgHash, - quorumNumbers, - referenceBlockNumber, - nonSignerStakesAndSignature - ); - } -} \ No newline at end of file diff --git a/src/test/unit/IndexRegistryUnit.t.sol b/src/test/unit/IndexRegistryUnit.t.sol deleted file mode 100644 index d6567a4ad..000000000 --- a/src/test/unit/IndexRegistryUnit.t.sol +++ /dev/null @@ -1,807 +0,0 @@ -//SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; - -import "../../contracts/interfaces/IIndexRegistry.sol"; -import "../../contracts/middleware/IndexRegistry.sol"; -import "../mocks/RegistryCoordinatorMock.sol"; -import "../harnesses/BitmapUtilsWrapper.sol"; - -import "forge-std/Test.sol"; - -contract IndexRegistryUnitTests is Test { - Vm cheats = Vm(HEVM_ADDRESS); - - IndexRegistry indexRegistry; - RegistryCoordinatorMock registryCoordinatorMock; - BitmapUtilsWrapper bitmapUtilsWrapper; - - uint8 defaultQuorumNumber = 1; - bytes32 operatorId1 = bytes32(uint256(34)); - bytes32 operatorId2 = bytes32(uint256(35)); - bytes32 operatorId3 = bytes32(uint256(36)); - - // Test 0 length operators in operators to remove - function setUp() public { - // deploy the contract - registryCoordinatorMock = new RegistryCoordinatorMock(); - indexRegistry = new IndexRegistry(registryCoordinatorMock); - bitmapUtilsWrapper = new BitmapUtilsWrapper(); - } - - function testConstructor() public { - // check that the registry coordinator is set correctly - assertEq(address(indexRegistry.registryCoordinator()), address(registryCoordinatorMock)); - } - - /******************************************************************************* - UNIT TESTS - REGISTRATION - *******************************************************************************/ - - /** - * Preconditions for registration -> checks in BLSRegistryCoordinator - * 1. quorumNumbers has no duplicates - * 2. quorumNumbers ordered in ascending order - * 3. quorumBitmap is <= uint192.max - * 4. quorumNumbers.length != 0 - * 5. operator is not already registerd for any quorums being registered for - */ - function testRegisterOperator() public { - // register an operator - bytes memory quorumNumbers = new bytes(1); - quorumNumbers[0] = bytes1(defaultQuorumNumber); - - cheats.prank(address(registryCoordinatorMock)); - uint32[] memory numOperatorsPerQuorum = indexRegistry.registerOperator(operatorId1, quorumNumbers); - - // Check return value - require( - numOperatorsPerQuorum.length == 1, - "IndexRegistry.registerOperator: numOperatorsPerQuorum length not 1" - ); - require(numOperatorsPerQuorum[0] == 1, "IndexRegistry.registerOperator: numOperatorsPerQuorum[0] not 1"); - - // Check globalOperatorList updates - require( - indexRegistry.globalOperatorList(0) == operatorId1, - "IndexRegistry.registerOperator: operator not appened to globalOperatorList" - ); - require( - indexRegistry.getGlobalOperatorListLength() == 1, - "IndexRegistry.registerOperator: globalOperatorList length incorrect" - ); - - // Check _operatorIdToIndexHistory updates - IIndexRegistry.OperatorIndexUpdate memory indexUpdate = indexRegistry - .getOperatorIndexUpdateOfOperatorIdForQuorumAtIndex(operatorId1, 1, 0); - require(indexUpdate.index == 0, "IndexRegistry.registerOperator: index not 0"); - require( - indexUpdate.fromBlockNumber == block.number, - "IndexRegistry.registerOperator: fromBlockNumber not correct" - ); - - // Check _totalOperatorsHistory updates - IIndexRegistry.OperatorIndexUpdate memory totalOperatorUpdate = indexRegistry - .getTotalOperatorsUpdateForQuorumAtIndex(1, 0); - require( - totalOperatorUpdate.index == 1, - "IndexRegistry.registerOperator: totalOperatorsHistory index (num operators) not 1" - ); - require( - totalOperatorUpdate.fromBlockNumber == block.number, - "IndexRegistry.registerOperator: totalOperatorsHistory fromBlockNumber not correct" - ); - require( - indexRegistry.totalOperatorsForQuorum(1) == 1, - "IndexRegistry.registerOperator: total operators for quorum not updated correctly" - ); - } - - function testRegisterOperatorMultipleQuorums() public { - // Register operator for 1st quorum - testRegisterOperator(); - - // Register operator for 2nd quorum - bytes memory quorumNumbers = new bytes(1); - quorumNumbers[0] = bytes1(defaultQuorumNumber + 1); - - cheats.prank(address(registryCoordinatorMock)); - uint32[] memory numOperatorsPerQuorum = indexRegistry.registerOperator(operatorId1, quorumNumbers); - - ///@notice The only value that should be different from before are what quorum we index into and the globalOperatorList - // Check return value - require( - numOperatorsPerQuorum.length == 1, - "IndexRegistry.registerOperator: numOperatorsPerQuorum length not 2" - ); - require(numOperatorsPerQuorum[0] == 1, "IndexRegistry.registerOperator: numOperatorsPerQuorum[1] not 1"); - - // Check globalOperatorList updates - require( - indexRegistry.globalOperatorList(1) == operatorId1, - "IndexRegistry.registerOperator: operator not appened to globalOperatorList" - ); - require( - indexRegistry.getGlobalOperatorListLength() == 2, - "IndexRegistry.registerOperator: globalOperatorList length incorrect" - ); - - // Check _operatorIdToIndexHistory updates - IIndexRegistry.OperatorIndexUpdate memory indexUpdate = indexRegistry - .getOperatorIndexUpdateOfOperatorIdForQuorumAtIndex(operatorId1, 2, 0); - require(indexUpdate.index == 0, "IndexRegistry.registerOperator: index not 0"); - require( - indexUpdate.fromBlockNumber == block.number, - "IndexRegistry.registerOperator: fromBlockNumber not correct" - ); - - // Check _totalOperatorsHistory updates - IIndexRegistry.OperatorIndexUpdate memory totalOperatorUpdate = indexRegistry - .getTotalOperatorsUpdateForQuorumAtIndex(2, 0); - require( - totalOperatorUpdate.index == 1, - "IndexRegistry.registerOperator: totalOperatorsHistory index (num operators) not 1" - ); - require( - totalOperatorUpdate.fromBlockNumber == block.number, - "IndexRegistry.registerOperator: totalOperatorsHistory fromBlockNumber not correct" - ); - require( - indexRegistry.totalOperatorsForQuorum(2) == 1, - "IndexRegistry.registerOperator: total operators for quorum not updated correctly" - ); - } - - function testRegisterOperatorMultipleQuorumsSingleCall() public { - // Register operator for 1st and 2nd quorum - bytes memory quorumNumbers = new bytes(2); - quorumNumbers[0] = bytes1(defaultQuorumNumber); - quorumNumbers[1] = bytes1(defaultQuorumNumber + 1); - - cheats.prank(address(registryCoordinatorMock)); - uint32[] memory numOperatorsPerQuorum = indexRegistry.registerOperator(operatorId1, quorumNumbers); - - // Check return value - require( - numOperatorsPerQuorum.length == 2, - "IndexRegistry.registerOperator: numOperatorsPerQuorum length not 2" - ); - require(numOperatorsPerQuorum[0] == 1, "IndexRegistry.registerOperator: numOperatorsPerQuorum[0] not 1"); - require(numOperatorsPerQuorum[1] == 1, "IndexRegistry.registerOperator: numOperatorsPerQuorum[1] not 1"); - - // Check globalOperatorList updates - require( - indexRegistry.globalOperatorList(0) == operatorId1, - "IndexRegistry.registerOperator: operator not appened to globalOperatorList" - ); - require( - indexRegistry.getGlobalOperatorListLength() == 1, - "IndexRegistry.registerOperator: globalOperatorList length incorrect" - ); - - // Check _operatorIdToIndexHistory updates for quorum 1 - IIndexRegistry.OperatorIndexUpdate memory indexUpdate = indexRegistry - .getOperatorIndexUpdateOfOperatorIdForQuorumAtIndex(operatorId1, 1, 0); - require(indexUpdate.index == 0, "IndexRegistry.registerOperator: index not 0"); - require( - indexUpdate.fromBlockNumber == block.number, - "IndexRegistry.registerOperator: fromBlockNumber not correct" - ); - - // Check _totalOperatorsHistory updates for quorum 1 - IIndexRegistry.OperatorIndexUpdate memory totalOperatorUpdate = indexRegistry - .getTotalOperatorsUpdateForQuorumAtIndex(1, 0); - require( - totalOperatorUpdate.index == 1, - "IndexRegistry.registerOperator: totalOperatorsHistory index (num operators) not 1" - ); - require( - totalOperatorUpdate.fromBlockNumber == block.number, - "IndexRegistry.registerOperator: totalOperatorsHistory fromBlockNumber not correct" - ); - require( - indexRegistry.totalOperatorsForQuorum(1) == 1, - "IndexRegistry.registerOperator: total operators for quorum not updated correctly" - ); - - // Check _operatorIdToIndexHistory updates for quorum 2 - indexUpdate = indexRegistry.getOperatorIndexUpdateOfOperatorIdForQuorumAtIndex(operatorId1, 2, 0); - require(indexUpdate.index == 0, "IndexRegistry.registerOperator: index not 0"); - require( - indexUpdate.fromBlockNumber == block.number, - "IndexRegistry.registerOperator: fromBlockNumber not correct" - ); - - // Check _totalOperatorsHistory updates for quorum 2 - totalOperatorUpdate = indexRegistry.getTotalOperatorsUpdateForQuorumAtIndex(2, 0); - require( - totalOperatorUpdate.index == 1, - "IndexRegistry.registerOperator: totalOperatorsHistory index (num operators) not 1" - ); - require( - totalOperatorUpdate.fromBlockNumber == block.number, - "IndexRegistry.registerOperator: totalOperatorsHistory fromBlockNumber not correct" - ); - require( - indexRegistry.totalOperatorsForQuorum(2) == 1, - "IndexRegistry.registerOperator: total operators for quorum not updated correctly" - ); - } - - function testRegisterMultipleOperatorsSingleQuorum() public { - // Register operator for first quorum - testRegisterOperator(); - - // Register another operator - bytes memory quorumNumbers = new bytes(1); - quorumNumbers[0] = bytes1(defaultQuorumNumber); - - cheats.prank(address(registryCoordinatorMock)); - uint32[] memory numOperatorsPerQuorum = indexRegistry.registerOperator(operatorId2, quorumNumbers); - - // Check return value - require( - numOperatorsPerQuorum.length == 1, - "IndexRegistry.registerOperator: numOperatorsPerQuorum length not 1" - ); - require(numOperatorsPerQuorum[0] == 2, "IndexRegistry.registerOperator: numOperatorsPerQuorum[0] not 2"); - - // Check globalOperatorList updates - require( - indexRegistry.globalOperatorList(1) == operatorId2, - "IndexRegistry.registerOperator: operator not appened to globalOperatorList" - ); - require( - indexRegistry.getGlobalOperatorListLength() == 2, - "IndexRegistry.registerOperator: globalOperatorList length incorrect" - ); - - // Check _operatorIdToIndexHistory updates - IIndexRegistry.OperatorIndexUpdate memory indexUpdate = indexRegistry - .getOperatorIndexUpdateOfOperatorIdForQuorumAtIndex(operatorId2, 1, 0); - require(indexUpdate.index == 1, "IndexRegistry.registerOperator: index not 1"); - require( - indexUpdate.fromBlockNumber == block.number, - "IndexRegistry.registerOperator: fromBlockNumber not correct" - ); - - // Check _totalOperatorsHistory updates - IIndexRegistry.OperatorIndexUpdate memory totalOperatorUpdate = indexRegistry - .getTotalOperatorsUpdateForQuorumAtIndex(1, 1); - require( - totalOperatorUpdate.index == 2, - "IndexRegistry.registerOperator: totalOperatorsHistory index (num operators) not 2" - ); - require( - totalOperatorUpdate.fromBlockNumber == block.number, - "IndexRegistry.registerOperator: totalOperatorsHistory fromBlockNumber not correct" - ); - require( - indexRegistry.totalOperatorsForQuorum(1) == 2, - "IndexRegistry.registerOperator: total operators for quorum not updated correctly" - ); - } - - /******************************************************************************* - UNIT TESTS - DEREGISTRATION - *******************************************************************************/ - - function testDeregisterOperatorRevertMismatchInputLengths() public { - bytes memory quorumNumbers = new bytes(1); - bytes32[] memory operatorIdsToSwap = new bytes32[](2); - - //deregister the operatorId1, removing it from both quorum 1 and 2. - cheats.prank(address(registryCoordinatorMock)); - cheats.expectRevert( - bytes("IndexRegistry.deregisterOperator: quorumNumbers and operatorIdsToSwap must be the same length") - ); - indexRegistry.deregisterOperator(operatorId1, quorumNumbers, operatorIdsToSwap); - } - - function testDeregisterOperatorSingleOperator() public { - // Register operator - bytes memory quorumNumbers = new bytes(1); - quorumNumbers[0] = bytes1(defaultQuorumNumber); - _registerOperator(operatorId1, quorumNumbers); - - // Deregister operator - bytes32[] memory operatorIdsToSwap = new bytes32[](1); - operatorIdsToSwap[0] = operatorId1; - cheats.prank(address(registryCoordinatorMock)); - indexRegistry.deregisterOperator(operatorId1, quorumNumbers, operatorIdsToSwap); - - // Check operator's index - IIndexRegistry.OperatorIndexUpdate memory indexUpdate = indexRegistry - .getOperatorIndexUpdateOfOperatorIdForQuorumAtIndex(operatorId1, defaultQuorumNumber, 1); - require(indexUpdate.fromBlockNumber == block.number, "fromBlockNumber not set correctly"); - require(indexUpdate.index == type(uint32).max, "incorrect index"); - - // Check total operators - IIndexRegistry.OperatorIndexUpdate memory totalOperatorUpdate = indexRegistry - .getTotalOperatorsUpdateForQuorumAtIndex(defaultQuorumNumber, 1); - require(totalOperatorUpdate.fromBlockNumber == block.number, "fromBlockNumber not set correctly"); - require(totalOperatorUpdate.index == 0, "incorrect total number of operators"); - require(indexRegistry.totalOperatorsForQuorum(1) == 0, "operator not deregistered correctly"); - } - - function testDeregisterOperatorRevertIncorrectOperatorToSwap() public { - // Register 3 operators - bytes memory quorumNumbers = new bytes(1); - quorumNumbers[0] = bytes1(defaultQuorumNumber); - _registerOperator(operatorId1, quorumNumbers); - _registerOperator(operatorId2, quorumNumbers); - _registerOperator(operatorId3, quorumNumbers); - - bytes32[] memory operatorIdsToSwap = new bytes32[](1); - operatorIdsToSwap[0] = operatorId2; - - //deregister the operatorId1, removing it from both quorum 1 and 2. - cheats.prank(address(registryCoordinatorMock)); - cheats.expectRevert( - bytes("IndexRegistry._processOperatorRemoval: operatorIdToSwap is not the last operator in the quorum") - ); - indexRegistry.deregisterOperator(operatorId1, quorumNumbers, operatorIdsToSwap); - } - - function testDeregisterOperatorMultipleQuorums() public { - // Register 3 operators to two quorums - bytes memory quorumNumbers = new bytes(3); - quorumNumbers[0] = bytes1(defaultQuorumNumber); - quorumNumbers[1] = bytes1(defaultQuorumNumber + 1); - quorumNumbers[2] = bytes1(defaultQuorumNumber + 2); - _registerOperator(operatorId1, quorumNumbers); - _registerOperator(operatorId2, quorumNumbers); - _registerOperator(operatorId3, quorumNumbers); - - // Deregister operator from quorums 1 and 2 - bytes memory quorumsToRemove = new bytes(2); - quorumsToRemove[0] = bytes1(defaultQuorumNumber); - quorumsToRemove[1] = bytes1(defaultQuorumNumber + 1); - bytes32[] memory operatorIdsToSwap = new bytes32[](2); - operatorIdsToSwap[0] = operatorId3; - operatorIdsToSwap[1] = operatorId3; - - cheats.prank(address(registryCoordinatorMock)); - indexRegistry.deregisterOperator(operatorId1, quorumsToRemove, operatorIdsToSwap); - - // Check operator's index for removed quorums - for (uint256 i = 0; i < quorumsToRemove.length; i++) { - IIndexRegistry.OperatorIndexUpdate memory indexUpdate = indexRegistry - .getOperatorIndexUpdateOfOperatorIdForQuorumAtIndex(operatorId1, uint8(quorumsToRemove[i]), 1); // 2 indexes -> 1 update and 1 remove - require(indexUpdate.fromBlockNumber == block.number, "fromBlockNumber not set correctly"); - require(indexUpdate.index == type(uint32).max, "incorrect index"); - } - - // Check total operators for removed quorums - for (uint256 i = 0; i < quorumsToRemove.length; i++) { - IIndexRegistry.OperatorIndexUpdate memory totalOperatorUpdate = indexRegistry - .getTotalOperatorsUpdateForQuorumAtIndex(uint8(quorumsToRemove[i]), 3); // 4 updates total - require(totalOperatorUpdate.fromBlockNumber == block.number, "fromBlockNumber not set correctly"); - require(totalOperatorUpdate.index == 2, "incorrect total number of operators"); - require( - indexRegistry.totalOperatorsForQuorum(uint8(quorumsToRemove[i])) == 2, - "operator not deregistered correctly" - ); - } - - // Check swapped operator's index for removed quorums - for (uint256 i = 0; i < quorumsToRemove.length; i++) { - IIndexRegistry.OperatorIndexUpdate memory indexUpdate = indexRegistry - .getOperatorIndexUpdateOfOperatorIdForQuorumAtIndex(operatorId3, uint8(quorumsToRemove[i]), 1); // 2 indexes -> 1 update and 1 swap - require(indexUpdate.fromBlockNumber == block.number, "fromBlockNumber not set correctly"); - require(indexUpdate.index == 0, "incorrect index"); - } - } - - /******************************************************************************* - UNIT TESTS - GETTERS - *******************************************************************************/ - - function testGetOperatorIndexForQuorumAtBlockNumberByIndex_revert_earlyBlockNumber() public { - bytes memory quorumNumbers = new bytes(1); - quorumNumbers[0] = bytes1(defaultQuorumNumber); - _registerOperator(operatorId1, quorumNumbers); - cheats.expectRevert( - bytes( - "IndexRegistry.getOperatorIndexForQuorumAtBlockNumberByIndex: provided index is too far in the past for provided block number" - ) - ); - indexRegistry.getOperatorIndexForQuorumAtBlockNumberByIndex( - operatorId1, - defaultQuorumNumber, - uint32(block.number - 1), - 0 - ); - } - - function testGetOperatorIndexForQuorumAtBlockNumberByIndex_revert_indexBlockMismatch() public { - // Register operator for first quorum - bytes memory quorumNumbers = new bytes(1); - quorumNumbers[0] = bytes1(defaultQuorumNumber); - _registerOperator(operatorId1, quorumNumbers); - - // Deregister operator from first quorum - vm.roll(block.number + 10); - bytes32[] memory operatorIdsToSwap = new bytes32[](1); - operatorIdsToSwap[0] = operatorId1; - cheats.prank(address(registryCoordinatorMock)); - indexRegistry.deregisterOperator(operatorId1, quorumNumbers, operatorIdsToSwap); - - // Fails due to block number corresponding to a later index (from the deregistration) - cheats.expectRevert( - bytes( - "IndexRegistry.getOperatorIndexForQuorumAtBlockNumberByIndex: provided index is too far in the future for provided block number" - ) - ); - indexRegistry.getOperatorIndexForQuorumAtBlockNumberByIndex( - operatorId1, - defaultQuorumNumber, - uint32(block.number), - 0 - ); - } - - function testGetOperatorIndexForQuorumAtBlockNumberByIndex() public { - // Register operator for first quorum - bytes memory quorumNumbers = new bytes(1); - quorumNumbers[0] = bytes1(defaultQuorumNumber); - _registerOperator(operatorId1, quorumNumbers); - - // Deregister operator from first quorum - vm.roll(block.number + 10); - bytes32[] memory operatorIdsToSwap = new bytes32[](1); - operatorIdsToSwap[0] = operatorId1; - cheats.prank(address(registryCoordinatorMock)); - indexRegistry.deregisterOperator(operatorId1, quorumNumbers, operatorIdsToSwap); - - // Check that the first index is correct - uint32 firstIndex = indexRegistry.getOperatorIndexForQuorumAtBlockNumberByIndex( - operatorId1, - defaultQuorumNumber, - uint32(block.number - 10), - 0 - ); - require(firstIndex == 0, "IndexRegistry.getOperatorIndexForQuorumAtBlockNumberByIndex: first index not 0"); - - // Check that the index is correct - uint32 index = indexRegistry.getOperatorIndexForQuorumAtBlockNumberByIndex( - operatorId1, - defaultQuorumNumber, - uint32(block.number), - 1 - ); - require( - index == type(uint32).max, - "IndexRegistry.getOperatorIndexForQuorumAtBlockNumberByIndex: second index not deregistered" - ); - } - - function testGetTotalOperatorsForQuorumAtBlockNumberByIndex_revert_indexTooEarly() public { - // Add operator - bytes memory quorumNumbers = new bytes(1); - quorumNumbers[0] = bytes1(defaultQuorumNumber); - _registerOperator(operatorId1, quorumNumbers); - - cheats.expectRevert( - "IndexRegistry.getTotalOperatorsForQuorumAtBlockNumberByIndex: provided index is too far in the past for provided block number" - ); - indexRegistry.getTotalOperatorsForQuorumAtBlockNumberByIndex(defaultQuorumNumber, uint32(block.number - 1), 0); - } - - function testGetTotalOperatorsForQuorumAtBlockNumberByIndex_revert_indexBlockMismatch() public { - // Add two operators - bytes memory quorumNumbers = new bytes(1); - quorumNumbers[0] = bytes1(defaultQuorumNumber); - _registerOperator(operatorId1, quorumNumbers); - vm.roll(block.number + 10); - _registerOperator(operatorId2, quorumNumbers); - - cheats.expectRevert( - "IndexRegistry.getTotalOperatorsForQuorumAtBlockNumberByIndex: provided index is too far in the future for provided block number" - ); - indexRegistry.getTotalOperatorsForQuorumAtBlockNumberByIndex(defaultQuorumNumber, uint32(block.number), 0); - } - - function testGetTotalOperatorsForQuorumAtBlockNumberByIndex() public { - // Add two operators - bytes memory quorumNumbers = new bytes(1); - quorumNumbers[0] = bytes1(defaultQuorumNumber); - _registerOperator(operatorId1, quorumNumbers); - vm.roll(block.number + 10); - _registerOperator(operatorId2, quorumNumbers); - - // Check that the first total is correct - uint32 prevTotal = indexRegistry.getTotalOperatorsForQuorumAtBlockNumberByIndex( - defaultQuorumNumber, - uint32(block.number - 10), - 0 - ); - require(prevTotal == 1, "IndexRegistry.getTotalOperatorsForQuorumAtBlockNumberByIndex: prev total not 1"); - - // Check that the total is correct - uint32 currentTotal = indexRegistry.getTotalOperatorsForQuorumAtBlockNumberByIndex( - defaultQuorumNumber, - uint32(block.number), - 1 - ); - require(currentTotal == 2, "IndexRegistry.getTotalOperatorsForQuorumAtBlockNumberByIndex: current total not 2"); - } - - function testGetOperatorListForQuorumAtBlockNumber() public { - // Register two operators - bytes memory quorumNumbers = new bytes(1); - quorumNumbers[0] = bytes1(defaultQuorumNumber); - _registerOperator(operatorId1, quorumNumbers); - vm.roll(block.number + 10); - _registerOperator(operatorId2, quorumNumbers); - - // Deregister first operator - vm.roll(block.number + 10); - bytes32[] memory operatorIdsToSwap = new bytes32[](1); - operatorIdsToSwap[0] = operatorId2; - cheats.prank(address(registryCoordinatorMock)); - indexRegistry.deregisterOperator(operatorId1, quorumNumbers, operatorIdsToSwap); - - // Check the operator list after first registration - bytes32[] memory operatorList = indexRegistry.getOperatorListForQuorumAtBlockNumber( - defaultQuorumNumber, - uint32(block.number - 20) - ); - require( - operatorList.length == 1, - "IndexRegistry.getOperatorListForQuorumAtBlockNumber: operator list length not 1" - ); - require( - operatorList[0] == operatorId1, - "IndexRegistry.getOperatorListForQuorumAtBlockNumber: operator list incorrect" - ); - - // Check the operator list after second registration - operatorList = indexRegistry.getOperatorListForQuorumAtBlockNumber( - defaultQuorumNumber, - uint32(block.number - 10) - ); - require( - operatorList.length == 2, - "IndexRegistry.getOperatorListForQuorumAtBlockNumber: operator list length not 2" - ); - require( - operatorList[0] == operatorId1, - "IndexRegistry.getOperatorListForQuorumAtBlockNumber: operator list incorrect" - ); - require( - operatorList[1] == operatorId2, - "IndexRegistry.getOperatorListForQuorumAtBlockNumber: operator list incorrect" - ); - - // Check the operator list after deregistration - operatorList = indexRegistry.getOperatorListForQuorumAtBlockNumber(defaultQuorumNumber, uint32(block.number)); - require( - operatorList.length == 1, - "IndexRegistry.getOperatorListForQuorumAtBlockNumber: operator list length not 1" - ); - require( - operatorList[0] == operatorId2, - "IndexRegistry.getOperatorListForQuorumAtBlockNumber: operator list incorrect" - ); - } - - /******************************************************************************* - FUZZ TESTS - *******************************************************************************/ - - function testFuzzRegisterOperatorRevertFromNonRegisterCoordinator(address nonRegistryCoordinator) public { - cheats.assume(address(registryCoordinatorMock) != nonRegistryCoordinator); - bytes memory quorumNumbers = new bytes(defaultQuorumNumber); - - cheats.prank(nonRegistryCoordinator); - cheats.expectRevert(bytes("IndexRegistry.onlyRegistryCoordinator: caller is not the registry coordinator")); - indexRegistry.registerOperator(bytes32(0), quorumNumbers); - } - - function testFuzzTotalOperatorUpdatesForOneQuorum(uint8 numOperators) public { - bytes memory quorumNumbers = new bytes(1); - quorumNumbers[0] = bytes1(defaultQuorumNumber); - - uint256 lengthBefore = 0; - for (uint256 i = 0; i < numOperators; i++) { - _registerOperator(bytes32(i), quorumNumbers); - require(indexRegistry.totalOperatorsForQuorum(1) - lengthBefore == 1, "incorrect update"); - lengthBefore++; - } - } - - /** - * Preconditions for registration -> checks in BLSRegistryCoordinator - * 1. quorumNumbers has no duplicates - * 2. quorumNumbers ordered in ascending order - * 3. quorumBitmap is <= uint192.max - * 4. quorumNumbers.length != 0 - * 5. operator is not already registerd for any quorums being registered for - */ - function testFuzzRegisterOperatorMultipleQuorums(bytes memory quorumNumbers) public { - // Validate quorumNumbers - cheats.assume(quorumNumbers.length > 0); - cheats.assume(bitmapUtilsWrapper.isArrayStrictlyAscendingOrdered(quorumNumbers)); - uint256 bitmap = bitmapUtilsWrapper.orderedBytesArrayToBitmap(quorumNumbers); - cheats.assume(bitmap <= type(uint192).max); - - // Register operator - cheats.prank(address(registryCoordinatorMock)); - uint32[] memory numOperatorsPerQuorum = indexRegistry.registerOperator(operatorId1, quorumNumbers); - - // Check return value - require( - numOperatorsPerQuorum.length == quorumNumbers.length, - "IndexRegistry.registerOperator: numOperatorsPerQuorum length not correct" - ); - for (uint256 i = 0; i < quorumNumbers.length; i++) { - require(numOperatorsPerQuorum[i] == 1, "IndexRegistry.registerOperator: numOperatorsPerQuorum not 1"); - } - - // Check globalOperatorList updates - require( - indexRegistry.globalOperatorList(0) == operatorId1, - "IndexRegistry.registerOperator: operator not appened to globalOperatorList" - ); - require( - indexRegistry.getGlobalOperatorListLength() == 1, - "IndexRegistry.registerOperator: globalOperatorList length incorrect" - ); - - // Check _operatorIdToIndexHistory updates - IIndexRegistry.OperatorIndexUpdate memory indexUpdate; - for (uint256 i = 0; i < quorumNumbers.length; i++) { - indexUpdate = indexRegistry.getOperatorIndexUpdateOfOperatorIdForQuorumAtIndex( - operatorId1, - uint8(quorumNumbers[i]), - 0 - ); - require(indexUpdate.index == 0, "IndexRegistry.registerOperator: index not 0"); - require( - indexUpdate.fromBlockNumber == block.number, - "IndexRegistry.registerOperator: fromBlockNumber not correct" - ); - } - - // Check _totalOperatorsHistory updates - IIndexRegistry.OperatorIndexUpdate memory totalOperatorUpdate; - for (uint256 i = 0; i < quorumNumbers.length; i++) { - totalOperatorUpdate = indexRegistry.getTotalOperatorsUpdateForQuorumAtIndex(uint8(quorumNumbers[i]), 0); - require( - totalOperatorUpdate.index == 1, - "IndexRegistry.registerOperator: totalOperatorsHistory index (num operators) not 1" - ); - require( - totalOperatorUpdate.fromBlockNumber == block.number, - "IndexRegistry.registerOperator: totalOperatorsHistory fromBlockNumber not correct" - ); - require( - indexRegistry.totalOperatorsForQuorum(uint8(quorumNumbers[i])) == 1, - "IndexRegistry.registerOperator: total operators for quorum not updated correctly" - ); - } - } - - function testFuzzRegsiterMultipleOperatorsMultipleQuorums(bytes memory quorumNumbers) public { - // Validate quorumNumbers - cheats.assume(quorumNumbers.length > 0); - cheats.assume(bitmapUtilsWrapper.isArrayStrictlyAscendingOrdered(quorumNumbers)); - uint256 bitmap = bitmapUtilsWrapper.orderedBytesArrayToBitmap(quorumNumbers); - cheats.assume(bitmap <= type(uint192).max); - - // Register operators 1,2,3 - _registerOperator(operatorId1, quorumNumbers); - vm.roll(block.number + 10); - _registerOperator(operatorId2, quorumNumbers); - vm.roll(block.number + 10); - _registerOperator(operatorId3, quorumNumbers); - - // Check globalOperatorList updates - require( - indexRegistry.getGlobalOperatorListLength() == 3, - "IndexRegistry.registerOperator: globalOperatorList length incorrect" - ); - - // Check history of _totalOperatorsHistory updates at each blockNumber - IIndexRegistry.OperatorIndexUpdate memory totalOperatorUpdate; - uint256 numOperators = 1; - for (uint256 blockNumber = block.number - 20; blockNumber <= block.number; blockNumber += 10) { - for (uint256 i = 0; i < quorumNumbers.length; i++) { - totalOperatorUpdate = indexRegistry.getTotalOperatorsUpdateForQuorumAtIndex( - uint8(quorumNumbers[i]), - uint32(numOperators - 1) - ); - require( - totalOperatorUpdate.index == numOperators, - "IndexRegistry.registerOperator: totalOperatorsHistory index (num operators) not correct" - ); - require( - totalOperatorUpdate.fromBlockNumber == blockNumber, - "IndexRegistry.registerOperator: totalOperatorsHistory fromBlockNumber not correct" - ); - } - numOperators++; - } - } - - function testFuzzDeregisterOperatorRevertFromNonRegisterCoordinator(address nonRegistryCoordinator) public { - cheats.assume(address(registryCoordinatorMock) != nonRegistryCoordinator); - // de-register an operator - bytes memory quorumNumbers = new bytes(defaultQuorumNumber); - bytes32[] memory operatorIdsToSwap = new bytes32[](1); - - cheats.prank(nonRegistryCoordinator); - cheats.expectRevert(bytes("IndexRegistry.onlyRegistryCoordinator: caller is not the registry coordinator")); - indexRegistry.deregisterOperator(bytes32(0), quorumNumbers, operatorIdsToSwap); - } - - function testFuzzDeregisterOperator(bytes memory quorumsToAdd, uint256 bitsToFlip) public { - // Validate quorumsToAdd - cheats.assume(quorumsToAdd.length > 0); - cheats.assume(bitmapUtilsWrapper.isArrayStrictlyAscendingOrdered(quorumsToAdd)); - uint256 bitmap = bitmapUtilsWrapper.orderedBytesArrayToBitmap(quorumsToAdd); - cheats.assume(bitmap <= type(uint192).max); - - // Format quorumsToRemove - bitsToFlip = bound(bitsToFlip, 1, quorumsToAdd.length); - uint256 bitsFlipped = 0; - uint256 bitPosition = 0; - uint256 bitmapQuorumsToRemove = bitmap; - while (bitsFlipped < bitsToFlip && bitPosition < 192) { - uint256 bitMask = 1 << bitPosition; - if (bitmapQuorumsToRemove & bitMask != 0) { - bitmapQuorumsToRemove ^= bitMask; - bitsFlipped++; - } - bitPosition++; - } - bytes memory quorumsToRemove = bitmapUtilsWrapper.bitmapToBytesArray(bitmapQuorumsToRemove); - // Sanity check quorumsToRemove - cheats.assume(bitmapUtilsWrapper.isArrayStrictlyAscendingOrdered(quorumsToRemove)); - - // Register operators - _registerOperator(operatorId1, quorumsToAdd); - _registerOperator(operatorId2, quorumsToAdd); - - // Deregister operator - bytes32[] memory operatorIdsToSwap = new bytes32[](quorumsToRemove.length); - for (uint256 i = 0; i < quorumsToRemove.length; i++) { - operatorIdsToSwap[i] = operatorId2; - } - cheats.prank(address(registryCoordinatorMock)); - indexRegistry.deregisterOperator(operatorId1, quorumsToRemove, operatorIdsToSwap); - - // Check operator's index for removed quorums - for (uint256 i = 0; i < quorumsToRemove.length; i++) { - IIndexRegistry.OperatorIndexUpdate memory indexUpdate = indexRegistry - .getOperatorIndexUpdateOfOperatorIdForQuorumAtIndex(operatorId1, uint8(quorumsToRemove[i]), 1); // 2 indexes -> 1 update and 1 remove - require(indexUpdate.fromBlockNumber == block.number, "fromBlockNumber not set correctly"); - require(indexUpdate.index == type(uint32).max, "incorrect index"); - } - - // Check total operators for removed quorums - for (uint256 i = 0; i < quorumsToRemove.length; i++) { - IIndexRegistry.OperatorIndexUpdate memory totalOperatorUpdate = indexRegistry - .getTotalOperatorsUpdateForQuorumAtIndex(uint8(quorumsToRemove[i]), 2); // 3 updates total - require(totalOperatorUpdate.fromBlockNumber == block.number, "fromBlockNumber not set correctly"); - require(totalOperatorUpdate.index == 1, "incorrect total number of operators"); - require( - indexRegistry.totalOperatorsForQuorum(uint8(quorumsToRemove[i])) == 1, - "operator not deregistered correctly" - ); - } - - // Check swapped operator's index for removed quorums - for (uint256 i = 0; i < quorumsToRemove.length; i++) { - IIndexRegistry.OperatorIndexUpdate memory indexUpdate = indexRegistry - .getOperatorIndexUpdateOfOperatorIdForQuorumAtIndex(operatorId2, uint8(quorumsToRemove[i]), 1); // 2 indexes -> 1 update and 1 swap - require(indexUpdate.fromBlockNumber == block.number, "fromBlockNumber not set correctly"); - require(indexUpdate.index == 0, "incorrect index"); - } - } - - function _registerOperator(bytes32 operatorId, bytes memory quorumNumbers) internal { - cheats.prank(address(registryCoordinatorMock)); - indexRegistry.registerOperator(operatorId, quorumNumbers); - } -} diff --git a/src/test/unit/StakeRegistryUnit.t.sol b/src/test/unit/StakeRegistryUnit.t.sol deleted file mode 100644 index f7b8a3185..000000000 --- a/src/test/unit/StakeRegistryUnit.t.sol +++ /dev/null @@ -1,611 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity =0.8.12; - -import "@openzeppelin/contracts/token/ERC20/presets/ERC20PresetFixedSupply.sol"; -import "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; -import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; - -import "../../contracts/core/Slasher.sol"; -import "../../contracts/permissions/PauserRegistry.sol"; -import "../../contracts/interfaces/IStrategyManager.sol"; -import "../../contracts/interfaces/IStakeRegistry.sol"; -import "../../contracts/interfaces/IServiceManager.sol"; -import "../../contracts/interfaces/IVoteWeigher.sol"; -import "../../contracts/interfaces/IIndexRegistry.sol"; - -import "../../contracts/libraries/BitmapUtils.sol"; - -import "../mocks/StrategyManagerMock.sol"; -import "../mocks/EigenPodManagerMock.sol"; -import "../mocks/ServiceManagerMock.sol"; -import "../mocks/OwnableMock.sol"; -import "../mocks/DelegationManagerMock.sol"; -import "../mocks/SlasherMock.sol"; - -import "../harnesses/StakeRegistryHarness.sol"; -import "../harnesses/BLSRegistryCoordinatorWithIndicesHarness.sol"; - -import "forge-std/Test.sol"; - -contract StakeRegistryUnitTests is Test { - Vm cheats = Vm(HEVM_ADDRESS); - - ProxyAdmin public proxyAdmin; - PauserRegistry public pauserRegistry; - - ISlasher public slasher = ISlasher(address(uint160(uint256(keccak256("slasher"))))); - - Slasher public slasherImplementation; - StakeRegistryHarness public stakeRegistryImplementation; - StakeRegistryHarness public stakeRegistry; - BLSRegistryCoordinatorWithIndicesHarness public registryCoordinator; - - ServiceManagerMock public serviceManagerMock; - StrategyManagerMock public strategyManagerMock; - DelegationManagerMock public delegationMock; - EigenPodManagerMock public eigenPodManagerMock; - - address public serviceManagerOwner = address(uint160(uint256(keccak256("serviceManagerOwner")))); - address public pauser = address(uint160(uint256(keccak256("pauser")))); - address public unpauser = address(uint160(uint256(keccak256("unpauser")))); - address public pubkeyRegistry = address(uint160(uint256(keccak256("pubkeyRegistry")))); - address public indexRegistry = address(uint160(uint256(keccak256("indexRegistry")))); - - address defaultOperator = address(uint160(uint256(keccak256("defaultOperator")))); - bytes32 defaultOperatorId = keccak256("defaultOperatorId"); - uint8 defaultQuorumNumber = 0; - uint8 numQuorums = 192; - uint8 maxQuorumsToRegisterFor = 4; - - uint256 gasUsed; - - /// @notice emitted whenever the stake of `operator` is updated - event StakeUpdate( - bytes32 indexed operatorId, - uint8 quorumNumber, - uint96 stake - ); - - function setUp() virtual public { - proxyAdmin = new ProxyAdmin(); - - address[] memory pausers = new address[](1); - pausers[0] = pauser; - pauserRegistry = new PauserRegistry(pausers, unpauser); - - delegationMock = new DelegationManagerMock(); - eigenPodManagerMock = new EigenPodManagerMock(); - strategyManagerMock = new StrategyManagerMock(); - slasherImplementation = new Slasher(strategyManagerMock, delegationMock); - slasher = Slasher( - address( - new TransparentUpgradeableProxy( - address(slasherImplementation), - address(proxyAdmin), - abi.encodeWithSelector(Slasher.initialize.selector, msg.sender, pauserRegistry, 0/*initialPausedStatus*/) - ) - ) - ); - - strategyManagerMock.setAddresses( - delegationMock, - eigenPodManagerMock, - slasher - ); - - registryCoordinator = new BLSRegistryCoordinatorWithIndicesHarness( - slasher, - serviceManagerMock, - stakeRegistry, - IBLSPubkeyRegistry(pubkeyRegistry), - IIndexRegistry(indexRegistry) - ); - - cheats.startPrank(serviceManagerOwner); - // make the serviceManagerOwner the owner of the serviceManager contract - serviceManagerMock = new ServiceManagerMock(slasher); - stakeRegistryImplementation = new StakeRegistryHarness( - IRegistryCoordinator(address(registryCoordinator)), - strategyManagerMock, - IServiceManager(address(serviceManagerMock)) - ); - - - // setup the dummy minimum stake for quorum - uint96[] memory minimumStakeForQuorum = new uint96[](maxQuorumsToRegisterFor); - for (uint256 i = 0; i < minimumStakeForQuorum.length; i++) { - minimumStakeForQuorum[i] = uint96(i+1); - } - - // setup the dummy quorum strategies - IVoteWeigher.StrategyAndWeightingMultiplier[][] memory quorumStrategiesConsideredAndMultipliers = - new IVoteWeigher.StrategyAndWeightingMultiplier[][](maxQuorumsToRegisterFor); - for (uint256 i = 0; i < quorumStrategiesConsideredAndMultipliers.length; i++) { - quorumStrategiesConsideredAndMultipliers[i] = new IVoteWeigher.StrategyAndWeightingMultiplier[](1); - quorumStrategiesConsideredAndMultipliers[i][0] = IVoteWeigher.StrategyAndWeightingMultiplier( - IStrategy(address(uint160(i))), - uint96(i+1) - ); - } - - stakeRegistry = StakeRegistryHarness( - address( - new TransparentUpgradeableProxy( - address(stakeRegistryImplementation), - address(proxyAdmin), - abi.encodeWithSelector( - StakeRegistry.initialize.selector, - minimumStakeForQuorum, - quorumStrategiesConsideredAndMultipliers - ) - ) - ) - ); - - cheats.stopPrank(); - } - - function testCorrectConstruction() public { - // make sure the contract intializers are disabled - cheats.expectRevert(bytes("Initializable: contract is already initialized")); - stakeRegistryImplementation.initialize(new uint96[](0), new IVoteWeigher.StrategyAndWeightingMultiplier[][](0)); - } - - function testSetMinimumStakeForQuorum_NotFromServiceManager_Reverts() public { - cheats.expectRevert("VoteWeigherBase.onlyServiceManagerOwner: caller is not the owner of the serviceManager"); - stakeRegistry.setMinimumStakeForQuorum(defaultQuorumNumber, 0); - } - - function testSetMinimumStakeForQuorum_Valid(uint8 quorumNumber, uint96 minimumStakeForQuorum) public { - // set the minimum stake for quorum - cheats.prank(serviceManagerOwner); - stakeRegistry.setMinimumStakeForQuorum(quorumNumber, minimumStakeForQuorum); - - // make sure the minimum stake for quorum is as expected - assertEq(stakeRegistry.minimumStakeForQuorum(quorumNumber), minimumStakeForQuorum); - } - - function testRegisterOperator_NotFromRegistryCoordinator_Reverts() public { - bytes memory quorumNumbers = new bytes(1); - quorumNumbers[0] = bytes1(defaultQuorumNumber); - cheats.expectRevert("StakeRegistry.onlyRegistryCoordinator: caller is not the RegistryCoordinator"); - stakeRegistry.registerOperator(defaultOperator, defaultOperatorId, quorumNumbers); - } - - function testRegisterOperator_MoreQuorumsThanQuorumCount_Reverts() public { - bytes memory quorumNumbers = new bytes(maxQuorumsToRegisterFor+1); - for (uint i = 0; i < quorumNumbers.length; i++) { - quorumNumbers[i] = bytes1(uint8(i)); - } - - // expect that it reverts when you register - cheats.expectRevert("StakeRegistry._registerOperator: greatest quorumNumber must be less than quorumCount"); - cheats.prank(address(registryCoordinator)); - stakeRegistry.registerOperator(defaultOperator, defaultOperatorId, quorumNumbers); - } - - function testRegisterOperator_LessThanMinimumStakeForQuorum_Reverts( - uint96[] memory stakesForQuorum - ) public { - cheats.assume(stakesForQuorum.length > 0); - - // set the weights of the operator - // stakeRegistry.setOperatorWeight() - - bytes memory quorumNumbers = new bytes(stakesForQuorum.length > maxQuorumsToRegisterFor ? maxQuorumsToRegisterFor : stakesForQuorum.length); - for (uint i = 0; i < quorumNumbers.length; i++) { - quorumNumbers[i] = bytes1(uint8(i)); - } - - stakesForQuorum[stakesForQuorum.length - 1] = stakeRegistry.minimumStakeForQuorum(uint8(quorumNumbers.length - 1)) - 1; - - // expect that it reverts when you register - cheats.expectRevert("StakeRegistry._registerOperator: Operator does not meet minimum stake requirement for quorum"); - cheats.prank(address(registryCoordinator)); - stakeRegistry.registerOperator(defaultOperator, defaultOperatorId, quorumNumbers); - } - - function testRegisterFirstOperator_Valid( - uint256 quorumBitmap, - uint80[] memory stakesForQuorum - ) public { - // limit to maxQuorumsToRegisterFor quorums and register for quorum 0 - quorumBitmap = quorumBitmap & (1 << maxQuorumsToRegisterFor - 1) | 1; - uint96[] memory paddedStakesForQuorum = _registerOperatorValid(defaultOperator, defaultOperatorId, quorumBitmap, stakesForQuorum); - - uint8 quorumNumberIndex = 0; - for (uint8 i = 0; i < maxQuorumsToRegisterFor; i++) { - if (quorumBitmap >> i & 1 == 1) { - // check that the operator has 1 stake update in the quorum numbers they registered for - assertEq(stakeRegistry.getLengthOfOperatorIdStakeHistoryForQuorum(defaultOperatorId, i), 1); - // make sure that the stake update is as expected - IStakeRegistry.OperatorStakeUpdate memory stakeUpdate = - stakeRegistry.getStakeUpdateForQuorumFromOperatorIdAndIndex(i, defaultOperatorId, 0); - emit log_named_uint("length of paddedStakesForQuorum", paddedStakesForQuorum.length); - assertEq(stakeUpdate.stake, paddedStakesForQuorum[quorumNumberIndex]); - assertEq(stakeUpdate.updateBlockNumber, uint32(block.number)); - assertEq(stakeUpdate.nextUpdateBlockNumber, 0); - - // make the analogous check for total stake history - assertEq(stakeRegistry.getLengthOfTotalStakeHistoryForQuorum(i), 1); - // make sure that the stake update is as expected - stakeUpdate = stakeRegistry.getTotalStakeUpdateForQuorumFromIndex(i, 0); - assertEq(stakeUpdate.stake, paddedStakesForQuorum[quorumNumberIndex]); - assertEq(stakeUpdate.updateBlockNumber, uint32(block.number)); - assertEq(stakeUpdate.nextUpdateBlockNumber, 0); - - quorumNumberIndex++; - } else { - // check that the operator has 0 stake updates in the quorum numbers they did not register for - assertEq(stakeRegistry.getLengthOfOperatorIdStakeHistoryForQuorum(defaultOperatorId, i), 0); - // make the analogous check for total stake history - assertEq(stakeRegistry.getLengthOfTotalStakeHistoryForQuorum(i), 0); - } - } - } - - function testRegisterManyOperators_Valid( - uint256 pseudoRandomNumber, - uint8 numOperators, - uint24[] memory blocksPassed - ) public { - cheats.assume(numOperators > 0 && numOperators <= 15); - // modulo so no overflow - pseudoRandomNumber = pseudoRandomNumber % type(uint128).max; - - uint256[] memory quorumBitmaps = new uint256[](numOperators); - - // append to blocksPassed as needed - uint24[] memory appendedBlocksPassed = new uint24[](quorumBitmaps.length); - for (uint256 i = blocksPassed.length; i < quorumBitmaps.length; i++) { - appendedBlocksPassed[i] = 0; - } - blocksPassed = appendedBlocksPassed; - - uint32 initialBlockNumber = 100; - cheats.roll(initialBlockNumber); - uint32 cumulativeBlockNumber = initialBlockNumber; - - uint96[][] memory paddedStakesForQuorums = new uint96[][](quorumBitmaps.length); - for (uint256 i = 0; i < quorumBitmaps.length; i++) { - (quorumBitmaps[i], paddedStakesForQuorums[i]) = _registerOperatorRandomValid(_incrementAddress(defaultOperator, i), _incrementBytes32(defaultOperatorId, i), pseudoRandomNumber + i); - - cumulativeBlockNumber += blocksPassed[i]; - cheats.roll(cumulativeBlockNumber); - } - - // for each bit in each quorumBitmap, increment the number of operators in that quorum - uint32[] memory numOperatorsInQuorum = new uint32[](maxQuorumsToRegisterFor); - for (uint256 i = 0; i < quorumBitmaps.length; i++) { - for (uint256 j = 0; j < maxQuorumsToRegisterFor; j++) { - if (quorumBitmaps[i] >> j & 1 == 1) { - numOperatorsInQuorum[j]++; - } - } - } - - // operatorQuorumIndices is an array of iindices within the quorum numbers that each operator registered for - // used for accounting in the next loops - uint32[] memory operatorQuorumIndices = new uint32[](quorumBitmaps.length); - - // for each quorum - for (uint8 i = 0; i < maxQuorumsToRegisterFor; i++) { - uint32 operatorCount = 0; - // reset the cumulative block number - cumulativeBlockNumber = initialBlockNumber; - - // make sure the number of total stake updates is as expected - assertEq(stakeRegistry.getLengthOfTotalStakeHistoryForQuorum(i), numOperatorsInQuorum[i]); - - uint96 cumulativeStake = 0; - // for each operator - for (uint256 j = 0; j < quorumBitmaps.length; j++) { - // if the operator is in the quorum - if (quorumBitmaps[j] >> i & 1 == 1) { - cumulativeStake += paddedStakesForQuorums[j][operatorQuorumIndices[j]]; - // make sure the number of stake updates is as expected - assertEq(stakeRegistry.getLengthOfOperatorIdStakeHistoryForQuorum(_incrementBytes32(defaultOperatorId, j), i), 1); - - // make sure that the stake update is as expected - IStakeRegistry.OperatorStakeUpdate memory totalStakeUpdate = - stakeRegistry.getTotalStakeUpdateForQuorumFromIndex(i, operatorCount); - - assertEq(totalStakeUpdate.stake, cumulativeStake); - assertEq(totalStakeUpdate.updateBlockNumber, cumulativeBlockNumber); - // make sure that the next update block number of the previous stake update is as expected - if (operatorCount != 0) { - IStakeRegistry.OperatorStakeUpdate memory prevTotalStakeUpdate = - stakeRegistry.getTotalStakeUpdateForQuorumFromIndex(i, operatorCount - 1); - assertEq(prevTotalStakeUpdate.nextUpdateBlockNumber, cumulativeBlockNumber); - } - - operatorQuorumIndices[j]++; - operatorCount++; - } else { - // make sure the number of stake updates is as expected - assertEq(stakeRegistry.getLengthOfOperatorIdStakeHistoryForQuorum(_incrementBytes32(defaultOperatorId, j), i), 0); - } - cumulativeBlockNumber += blocksPassed[j]; - } - } - } - - function testDeregisterOperator_Valid( - uint256 pseudoRandomNumber, - uint256 quorumBitmap, - uint256 deregistrationQuorumsFlag, - uint80[] memory stakesForQuorum - ) public { - // modulo so no overflow - pseudoRandomNumber = pseudoRandomNumber % type(uint128).max; - // limit to maxQuorumsToRegisterFor quorums and register for quorum 0 - quorumBitmap = quorumBitmap & (1 << maxQuorumsToRegisterFor - 1) | 1; - // register a bunch of operators - cheats.roll(100); - uint32 cumulativeBlockNumber = 100; - - uint8 numOperatorsRegisterBefore = 5; - uint256 numOperators = 1 + 2*numOperatorsRegisterBefore; - uint256[] memory quorumBitmaps = new uint256[](numOperators); - - // register - for (uint i = 0; i < numOperatorsRegisterBefore; i++) { - (quorumBitmaps[i],) = _registerOperatorRandomValid(_incrementAddress(defaultOperator, i), _incrementBytes32(defaultOperatorId, i), pseudoRandomNumber + i); - - cumulativeBlockNumber += 1; - cheats.roll(cumulativeBlockNumber); - } - - // register the operator to be deregistered - quorumBitmaps[numOperatorsRegisterBefore] = quorumBitmap; - bytes32 operatorIdToDeregister = _incrementBytes32(defaultOperatorId, numOperatorsRegisterBefore); - uint96[] memory paddedStakesForQuorum; - { - address operatorToDeregister = _incrementAddress(defaultOperator, numOperatorsRegisterBefore); - paddedStakesForQuorum = _registerOperatorValid(operatorToDeregister, operatorIdToDeregister, quorumBitmap, stakesForQuorum); - } - // register the rest of the operators - for (uint i = numOperatorsRegisterBefore + 1; i < 2*numOperatorsRegisterBefore; i++) { - cumulativeBlockNumber += 1; - cheats.roll(cumulativeBlockNumber); - - (quorumBitmaps[i],) = _registerOperatorRandomValid(_incrementAddress(defaultOperator, i), _incrementBytes32(defaultOperatorId, i), pseudoRandomNumber + i); - } - - { - bool shouldPassBlockBeforeDeregistration = uint256(keccak256(abi.encodePacked(pseudoRandomNumber, "shouldPassBlockBeforeDeregistration"))) & 1 == 1; - if (shouldPassBlockBeforeDeregistration) { - cumulativeBlockNumber += 1; - cheats.roll(cumulativeBlockNumber); - } - } - - // deregister the operator from a subset of the quorums - uint256 deregistrationQuroumBitmap = quorumBitmap & deregistrationQuorumsFlag; - _deregisterOperatorValid(operatorIdToDeregister, deregistrationQuroumBitmap); - - // for each bit in each quorumBitmap, increment the number of operators in that quorum - uint32[] memory numOperatorsInQuorum = new uint32[](maxQuorumsToRegisterFor); - for (uint256 i = 0; i < quorumBitmaps.length; i++) { - for (uint256 j = 0; j < maxQuorumsToRegisterFor; j++) { - if (quorumBitmaps[i] >> j & 1 == 1) { - numOperatorsInQuorum[j]++; - } - } - } - - uint8 quorumNumberIndex = 0; - for (uint8 i = 0; i < maxQuorumsToRegisterFor; i++) { - if (deregistrationQuroumBitmap >> i & 1 == 1) { - // check that the operator has 2 stake updates in the quorum numbers they registered for - assertEq(stakeRegistry.getLengthOfOperatorIdStakeHistoryForQuorum(operatorIdToDeregister, i), 2, "testDeregisterFirstOperator_Valid_0"); - // make sure that the last stake update is as expected - IStakeRegistry.OperatorStakeUpdate memory lastStakeUpdate = - stakeRegistry.getStakeUpdateForQuorumFromOperatorIdAndIndex(i, operatorIdToDeregister, 1); - assertEq(lastStakeUpdate.stake, 0, "testDeregisterFirstOperator_Valid_1"); - assertEq(lastStakeUpdate.updateBlockNumber, cumulativeBlockNumber, "testDeregisterFirstOperator_Valid_2"); - assertEq(lastStakeUpdate.nextUpdateBlockNumber, 0, "testDeregisterFirstOperator_Valid_3"); - - // make the analogous check for total stake history - assertEq(stakeRegistry.getLengthOfTotalStakeHistoryForQuorum(i), numOperatorsInQuorum[i] + 1, "testDeregisterFirstOperator_Valid_4"); - // make sure that the last stake update is as expected - IStakeRegistry.OperatorStakeUpdate memory lastTotalStakeUpdate - = stakeRegistry.getTotalStakeUpdateForQuorumFromIndex(i, numOperatorsInQuorum[i]); - assertEq(lastTotalStakeUpdate.stake, - stakeRegistry.getTotalStakeUpdateForQuorumFromIndex(i, numOperatorsInQuorum[i] - 1).stake // the previous total stake - - paddedStakesForQuorum[quorumNumberIndex], // minus the stake that was deregistered - "testDeregisterFirstOperator_Valid_5" - ); - assertEq(lastTotalStakeUpdate.updateBlockNumber, cumulativeBlockNumber, "testDeregisterFirstOperator_Valid_6"); - assertEq(lastTotalStakeUpdate.nextUpdateBlockNumber, 0, "testDeregisterFirstOperator_Valid_7"); - quorumNumberIndex++; - } else if (quorumBitmap >> i & 1 == 1) { - assertEq(stakeRegistry.getLengthOfOperatorIdStakeHistoryForQuorum(operatorIdToDeregister, i), 1, "testDeregisterFirstOperator_Valid_8"); - assertEq(stakeRegistry.getLengthOfTotalStakeHistoryForQuorum(i), numOperatorsInQuorum[i], "testDeregisterFirstOperator_Valid_9"); - quorumNumberIndex++; - } else { - // check that the operator has 0 stake updates in the quorum numbers they did not register for - assertEq(stakeRegistry.getLengthOfOperatorIdStakeHistoryForQuorum(operatorIdToDeregister, i), 0, "testDeregisterFirstOperator_Valid_10"); - } - } - } - - function testUpdateOperatorStake_Valid( - uint24[] memory blocksPassed, - uint96[] memory stakes - ) public { - cheats.assume(blocksPassed.length > 0); - cheats.assume(blocksPassed.length <= stakes.length); - // initialize at a non-zero block number - uint32 intialBlockNumber = 100; - cheats.roll(intialBlockNumber); - uint32 cumulativeBlockNumber = intialBlockNumber; - // loop through each one of the blocks passed, roll that many blocks, set the weight in the given quorum to the stake, and trigger a stake update - for (uint256 i = 0; i < blocksPassed.length; i++) { - stakeRegistry.setOperatorWeight(defaultQuorumNumber, defaultOperator, stakes[i]); - - cheats.expectEmit(true, true, true, true, address(stakeRegistry)); - emit StakeUpdate(defaultOperatorId, defaultQuorumNumber, stakes[i]); - stakeRegistry.updateOperatorStake(defaultOperator, defaultOperatorId, defaultQuorumNumber); - - cumulativeBlockNumber += blocksPassed[i]; - cheats.roll(cumulativeBlockNumber); - } - - // reset for checking indices - cumulativeBlockNumber = intialBlockNumber; - // make sure that the stake updates are as expected - for (uint256 i = 0; i < blocksPassed.length - 1; i++) { - IStakeRegistry.OperatorStakeUpdate memory operatorStakeUpdate = stakeRegistry.getStakeUpdateForQuorumFromOperatorIdAndIndex(defaultQuorumNumber, defaultOperatorId, i); - - uint96 expectedStake = stakes[i]; - if (expectedStake < stakeRegistry.minimumStakeForQuorum(defaultQuorumNumber)) { - expectedStake = 0; - } - - assertEq(operatorStakeUpdate.stake, expectedStake); - assertEq(operatorStakeUpdate.updateBlockNumber, cumulativeBlockNumber); - cumulativeBlockNumber += blocksPassed[i]; - assertEq(operatorStakeUpdate.nextUpdateBlockNumber, cumulativeBlockNumber); - } - - // make sure that the last stake update is as expected - IStakeRegistry.OperatorStakeUpdate memory lastOperatorStakeUpdate = stakeRegistry.getStakeUpdateForQuorumFromOperatorIdAndIndex(defaultQuorumNumber, defaultOperatorId, blocksPassed.length - 1); - assertEq(lastOperatorStakeUpdate.stake, stakes[blocksPassed.length - 1]); - assertEq(lastOperatorStakeUpdate.updateBlockNumber, cumulativeBlockNumber); - assertEq(lastOperatorStakeUpdate.nextUpdateBlockNumber, uint32(0)); - } - - function testRecordTotalStakeUpdate_Valid( - uint24[] memory blocksPassed, - uint96[] memory stakes - ) public { - cheats.assume(blocksPassed.length > 0); - cheats.assume(blocksPassed.length <= stakes.length); - // initialize at a non-zero block number - uint32 intialBlockNumber = 100; - cheats.roll(intialBlockNumber); - uint32 cumulativeBlockNumber = intialBlockNumber; - // loop through each one of the blocks passed, roll that many blocks, create an Operator Stake Update for total stake, and trigger a total stake update - for (uint256 i = 0; i < blocksPassed.length; i++) { - IStakeRegistry.OperatorStakeUpdate memory totalStakeUpdate; - totalStakeUpdate.stake = stakes[i]; - - stakeRegistry.recordTotalStakeUpdate(defaultQuorumNumber, totalStakeUpdate); - - cumulativeBlockNumber += blocksPassed[i]; - cheats.roll(cumulativeBlockNumber); - } - - // reset for checking indices - cumulativeBlockNumber = intialBlockNumber; - // make sure that the total stake updates are as expected - for (uint256 i = 0; i < blocksPassed.length - 1; i++) { - IStakeRegistry.OperatorStakeUpdate memory totalStakeUpdate = stakeRegistry.getTotalStakeUpdateForQuorumFromIndex(defaultQuorumNumber, i); - - assertEq(totalStakeUpdate.stake, stakes[i]); - assertEq(totalStakeUpdate.updateBlockNumber, cumulativeBlockNumber); - cumulativeBlockNumber += blocksPassed[i]; - assertEq(totalStakeUpdate.nextUpdateBlockNumber, cumulativeBlockNumber); - } - } - - function testUpdateStakes_Valid( - uint256 pseudoRandomNumber - ) public { - (uint256 quorumBitmap, uint96[] memory stakesForQuorum) = _registerOperatorRandomValid(defaultOperator, defaultOperatorId, pseudoRandomNumber); - registryCoordinator.setOperatorId(defaultOperator, defaultOperatorId); - registryCoordinator.recordOperatorQuorumBitmapUpdate(defaultOperatorId, uint192(quorumBitmap)); - - uint32 intialBlockNumber = 100; - cheats.roll(intialBlockNumber); - - bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); - for(uint i = 0; i < stakesForQuorum.length; i++) { - stakeRegistry.setOperatorWeight(uint8(quorumNumbers[i]), defaultOperator, stakesForQuorum[i] + 1); - } - - address[] memory operators = new address[](1); - operators[0] = defaultOperator; - stakeRegistry.updateStakes(operators); - - for(uint i = 0; i < quorumNumbers.length; i++) { - StakeRegistry.OperatorStakeUpdate memory operatorStakeUpdate = stakeRegistry.getStakeUpdateForQuorumFromOperatorIdAndIndex(uint8(quorumNumbers[i]), defaultOperatorId, 1); - assertEq(operatorStakeUpdate.stake, stakesForQuorum[i] + 1); - } - } - - // utility function for registering an operator with a valid quorumBitmap and stakesForQuorum using provided randomness - function _registerOperatorRandomValid( - address operator, - bytes32 operatorId, - uint256 psuedoRandomNumber - ) internal returns(uint256, uint96[] memory){ - // generate uint256 quorumBitmap from psuedoRandomNumber and limit to maxQuorumsToRegisterFor quorums and register for quorum 0 - uint256 quorumBitmap = uint256(keccak256(abi.encodePacked(psuedoRandomNumber, "quorumBitmap"))) & (1 << maxQuorumsToRegisterFor - 1) | 1; - // generate uint80[] stakesForQuorum from psuedoRandomNumber - uint80[] memory stakesForQuorum = new uint80[](BitmapUtils.countNumOnes(quorumBitmap)); - for(uint i = 0; i < stakesForQuorum.length; i++) { - stakesForQuorum[i] = uint80(uint256(keccak256(abi.encodePacked(psuedoRandomNumber, i, "stakesForQuorum")))); - } - - return (quorumBitmap, _registerOperatorValid(operator, operatorId, quorumBitmap, stakesForQuorum)); - } - - // utility function for registering an operator - function _registerOperatorValid( - address operator, - bytes32 operatorId, - uint256 quorumBitmap, - uint80[] memory stakesForQuorum - ) internal returns(uint96[] memory){ - cheats.assume(quorumBitmap != 0); - - bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); - - // pad the stakesForQuorum array with the minimum stake for the quorums - uint96[] memory paddedStakesForQuorum = new uint96[](BitmapUtils.countNumOnes(quorumBitmap)); - for(uint i = 0; i < paddedStakesForQuorum.length; i++) { - uint96 minimumStakeForQuorum = stakeRegistry.minimumStakeForQuorum(uint8(quorumNumbers[i])); - // make sure the operator has at least the mininmum stake in each quorum it is registering for - if (i >= stakesForQuorum.length || stakesForQuorum[i] < minimumStakeForQuorum) { - paddedStakesForQuorum[i] = minimumStakeForQuorum; - } else { - paddedStakesForQuorum[i] = stakesForQuorum[i]; - } - } - - // set the weights of the operator - for(uint i = 0; i < paddedStakesForQuorum.length; i++) { - stakeRegistry.setOperatorWeight(uint8(quorumNumbers[i]), operator, paddedStakesForQuorum[i]); - } - - // register operator - uint256 gasleftBefore = gasleft(); - cheats.prank(address(registryCoordinator)); - stakeRegistry.registerOperator(operator, operatorId, quorumNumbers); - gasUsed = gasleftBefore - gasleft(); - - return paddedStakesForQuorum; - } - - // utility function for deregistering an operator - function _deregisterOperatorValid( - bytes32 operatorId, - uint256 quorumBitmap - ) internal { - bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); - - // deregister operator - cheats.prank(address(registryCoordinator)); - stakeRegistry.deregisterOperator(operatorId, quorumNumbers); - } - - function _incrementAddress(address start, uint256 inc) internal pure returns(address) { - return address(uint160(uint256(uint160(start) + inc))); - } - - function _incrementBytes32(bytes32 start, uint256 inc) internal pure returns(bytes32) { - return bytes32(uint256(start) + inc); - } -} \ No newline at end of file diff --git a/src/test/unit/VoteWeigherBaseUnit.t.sol b/src/test/unit/VoteWeigherBaseUnit.t.sol deleted file mode 100644 index 539e38f55..000000000 --- a/src/test/unit/VoteWeigherBaseUnit.t.sol +++ /dev/null @@ -1,598 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity =0.8.12; - -import "@openzeppelin/contracts/token/ERC20/presets/ERC20PresetFixedSupply.sol"; -import "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; -import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; - -import "../../contracts/permissions/PauserRegistry.sol"; -import "../../contracts/interfaces/IStrategyManager.sol"; -import "../../contracts/interfaces/IServiceManager.sol"; -import "../../contracts/interfaces/IVoteWeigher.sol"; -import "../../contracts/middleware/VoteWeigherBase.sol"; - -import "../mocks/StrategyManagerMock.sol"; -import "../mocks/OwnableMock.sol"; -import "../mocks/DelegationManagerMock.sol"; - -import "forge-std/Test.sol"; - -contract VoteWeigherBaseUnitTests is Test { - Vm cheats = Vm(HEVM_ADDRESS); - - ProxyAdmin public proxyAdmin; - PauserRegistry public pauserRegistry; - IStrategyManager public strategyManager; - - address serviceManagerOwner; - IServiceManager public serviceManager; - - DelegationManagerMock delegationMock; - - VoteWeigherBase public voteWeigher; - - VoteWeigherBase public voteWeigherImplementation; - - address public pauser = address(555); - address public unpauser = address(999); - - uint256 initialSupply = 1e36; - address initialOwner = address(this); - - /// @notice emitted when a new quorum is created - event QuorumCreated(uint8 indexed quorumNumber); - /// @notice emitted when `strategy` has been added to the array at `strategiesConsideredAndMultipliers[quorumNumber]` - event StrategyAddedToQuorum(uint8 indexed quorumNumber, IStrategy strategy); - /// @notice emitted when `strategy` has removed from the array at `strategiesConsideredAndMultipliers[quorumNumber]` - event StrategyRemovedFromQuorum(uint8 indexed quorumNumber, IStrategy strategy); - /// @notice emitted when `strategy` has its `multiplier` updated in the array at `strategiesConsideredAndMultipliers[quorumNumber]` - event StrategyMultiplierUpdated(uint8 indexed quorumNumber, IStrategy strategy, uint256 multiplier); - - // addresses excluded from fuzzing due to abnormal behavior. TODO: @Sidu28 define this better and give it a clearer name - mapping (address => bool) fuzzedAddressMapping; - // strategy => is in current array. used for detecting duplicates - mapping (IStrategy => bool) strategyInCurrentArray; - // uint256 => is in current array - mapping (uint256 => bool) uint256InCurrentArray; - - //ensures that a passed in address is not set to true in the fuzzedAddressMapping - modifier fuzzedAddress(address addr) virtual { - cheats.assume(fuzzedAddressMapping[addr] == false); - _; - } - - function setUp() virtual public { - proxyAdmin = new ProxyAdmin(); - - address[] memory pausers = new address[](1); - pausers[0] = pauser; - pauserRegistry = new PauserRegistry(pausers, unpauser); - - StrategyManagerMock strategyManagerMock = new StrategyManagerMock(); - delegationMock = new DelegationManagerMock(); - strategyManagerMock.setAddresses( - delegationMock, - IEigenPodManager(address(uint160(uint256(keccak256(abi.encodePacked("eigenPodManager")))))), - ISlasher(address(uint160(uint256(keccak256(abi.encodePacked("slasher")))))) - ); - - strategyManager = IStrategyManager(address(strategyManagerMock)); - - // make the serviceManagerOwner the owner of the serviceManager contract - cheats.prank(serviceManagerOwner); - serviceManager = IServiceManager(address(new OwnableMock())); - - voteWeigherImplementation = new VoteWeigherBase(strategyManager, serviceManager); - - voteWeigher = VoteWeigherBase(address(new TransparentUpgradeableProxy(address(voteWeigherImplementation), address(proxyAdmin), ""))); - - fuzzedAddressMapping[address(proxyAdmin)] = true; - } - - function testCorrectConstructionParameters() public { - assertEq(address(voteWeigherImplementation.strategyManager()), address(strategyManager)); - assertEq(address(voteWeigherImplementation.slasher()), address(strategyManager.slasher())); - assertEq(address(voteWeigherImplementation.delegation()), address(strategyManager.delegation())); - assertEq(address(voteWeigherImplementation.serviceManager()), address(serviceManager)); - } - - function testCreateQuorum_Valid(IVoteWeigher.StrategyAndWeightingMultiplier[] memory strategiesAndWeightingMultipliers) public { - strategiesAndWeightingMultipliers = _convertToValidStrategiesAndWeightingMultipliers(strategiesAndWeightingMultipliers); - // create a quorum from the serviceManagerOwner - // get the quorum count before the quorum is created - uint8 quorumCountBefore = uint8(voteWeigher.quorumCount()); - cheats.prank(serviceManagerOwner); - // expect each strategy to be added to the quorum - for (uint i = 0; i < strategiesAndWeightingMultipliers.length; i++) { - cheats.expectEmit(true, true, true, true, address(voteWeigher)); - emit StrategyAddedToQuorum(quorumCountBefore, strategiesAndWeightingMultipliers[i].strategy); - } - // created quorum will have quorum number of the count before it was created - cheats.expectEmit(true, true, true, true, address(voteWeigher)); - emit QuorumCreated(quorumCountBefore); - voteWeigher.createQuorum(strategiesAndWeightingMultipliers); - - assertEq(voteWeigher.quorumCount(), quorumCountBefore + 1); - // check that all of the weights are correct - for (uint i = 0; i < strategiesAndWeightingMultipliers.length; i++) { - IVoteWeigher.StrategyAndWeightingMultiplier memory strategyAndWeightingMultiplier = voteWeigher.strategyAndWeightingMultiplierForQuorumByIndex(quorumCountBefore, i); - assertEq(address(strategyAndWeightingMultiplier.strategy), address(strategiesAndWeightingMultipliers[i].strategy)); - assertEq(strategyAndWeightingMultiplier.multiplier, strategiesAndWeightingMultipliers[i].multiplier); - } - } - - function testCreateQuorum_FromNotServiceManagerOwner_Reverts( - address notServiceManagerOwner, - IVoteWeigher.StrategyAndWeightingMultiplier[] memory strategiesAndWeightingMultipliers - ) public fuzzedAddress(notServiceManagerOwner) { - cheats.assume(notServiceManagerOwner != serviceManagerOwner); - cheats.prank(notServiceManagerOwner); - cheats.expectRevert("VoteWeigherBase.onlyServiceManagerOwner: caller is not the owner of the serviceManager"); - voteWeigher.createQuorum(strategiesAndWeightingMultipliers); - } - - function testCreateQuorum_StrategiesAndWeightingMultipliers_LengthGreaterThanMaxAllowed_Reverts( - IVoteWeigher.StrategyAndWeightingMultiplier[] memory strategiesAndWeightingMultipliers - ) public { - strategiesAndWeightingMultipliers = _removeDuplicates(strategiesAndWeightingMultipliers); - strategiesAndWeightingMultipliers = _replaceZeroWeights(strategiesAndWeightingMultipliers); - - cheats.assume(strategiesAndWeightingMultipliers.length > voteWeigher.MAX_WEIGHING_FUNCTION_LENGTH()); - cheats.prank(serviceManagerOwner); - cheats.expectRevert("VoteWeigherBase._addStrategiesConsideredAndMultipliers: exceed MAX_WEIGHING_FUNCTION_LENGTH"); - voteWeigher.createQuorum(strategiesAndWeightingMultipliers); - } - - function testCreateQuorum_StrategiesAndWeightingMultipliers_WithDuplicateStrategies_Reverts( - IVoteWeigher.StrategyAndWeightingMultiplier[] memory strategiesAndWeightingMultipliers, - uint256 indexFromDuplicate, - uint256 indexToDuplicate - ) public { - cheats.assume(strategiesAndWeightingMultipliers.length <= voteWeigher.MAX_WEIGHING_FUNCTION_LENGTH()); - cheats.assume(strategiesAndWeightingMultipliers.length > 1); - strategiesAndWeightingMultipliers = _replaceZeroWeights(strategiesAndWeightingMultipliers); - - // plant a duplicate strategy - indexToDuplicate = indexToDuplicate % strategiesAndWeightingMultipliers.length; - indexFromDuplicate = indexFromDuplicate % strategiesAndWeightingMultipliers.length; - cheats.assume(indexToDuplicate != indexFromDuplicate); - strategiesAndWeightingMultipliers[indexToDuplicate].strategy = strategiesAndWeightingMultipliers[indexFromDuplicate].strategy; - - cheats.prank(serviceManagerOwner); - cheats.expectRevert("VoteWeigherBase._addStrategiesConsideredAndMultipliers: cannot add same strategy 2x"); - voteWeigher.createQuorum(strategiesAndWeightingMultipliers); - } - - function testCreateQuorum_EmptyStrategiesAndWeightingMultipliers_Reverts() public { - IVoteWeigher.StrategyAndWeightingMultiplier[] memory strategiesAndWeightingMultipliers; - cheats.prank(serviceManagerOwner); - cheats.expectRevert("VoteWeigherBase._addStrategiesConsideredAndMultipliers: no strategies provided"); - voteWeigher.createQuorum(strategiesAndWeightingMultipliers); - } - - function testCreateQuorum_StrategiesAndWeightingMultipliers_WithZeroWeight( - IVoteWeigher.StrategyAndWeightingMultiplier[] memory strategiesAndWeightingMultipliers, - uint256 indexForZeroMultiplier - ) public { - strategiesAndWeightingMultipliers = _removeDuplicates(strategiesAndWeightingMultipliers); - cheats.assume(strategiesAndWeightingMultipliers.length <= voteWeigher.MAX_WEIGHING_FUNCTION_LENGTH()); - cheats.assume(strategiesAndWeightingMultipliers.length > 0); - //plant a zero weight - strategiesAndWeightingMultipliers[indexForZeroMultiplier % strategiesAndWeightingMultipliers.length].multiplier = 0; - - cheats.prank(serviceManagerOwner); - cheats.expectRevert("VoteWeigherBase._addStrategiesConsideredAndMultipliers: cannot add strategy with zero weight"); - voteWeigher.createQuorum(strategiesAndWeightingMultipliers); - } - - function testCreateQuorum_MoreThanMaxQuorums_Reverts() public { - IVoteWeigher.StrategyAndWeightingMultiplier[] memory strategiesAndWeightingMultipliers = _defaultStrategiesAndWeightingMultipliers(); - uint256 maxQuorums = voteWeigher.MAX_QUORUM_COUNT(); - - cheats.startPrank(serviceManagerOwner); - for (uint i = 0; i < maxQuorums; i++) { - voteWeigher.createQuorum(strategiesAndWeightingMultipliers); - } - assertEq(voteWeigher.quorumCount(), maxQuorums); - - cheats.expectRevert("VoteWeigherBase._createQuorum: number of quorums cannot exceed MAX_QUORUM_COUNT"); - voteWeigher.createQuorum(strategiesAndWeightingMultipliers); - } - - function testAddStrategiesConsideredAndMultipliers_Valid( - uint256 randomSplit, - IVoteWeigher.StrategyAndWeightingMultiplier[] memory strategiesAndWeightingMultipliers - ) public { - strategiesAndWeightingMultipliers = _convertToValidStrategiesAndWeightingMultipliers(strategiesAndWeightingMultipliers); - // make sure there is at least 2 strategies - cheats.assume(strategiesAndWeightingMultipliers.length > 1); - // we need at least 1 strategy in each side of the split - randomSplit = randomSplit % (strategiesAndWeightingMultipliers.length - 1) + 1; - // create 2 arrays, 1 with the first randomSplit elements and 1 with the rest - IVoteWeigher.StrategyAndWeightingMultiplier[] memory strategiesAndWeightingMultipliers1 = new IVoteWeigher.StrategyAndWeightingMultiplier[](randomSplit); - IVoteWeigher.StrategyAndWeightingMultiplier[] memory strategiesAndWeightingMultipliers2 = new IVoteWeigher.StrategyAndWeightingMultiplier[](strategiesAndWeightingMultipliers.length - randomSplit); - for (uint256 i = 0; i < strategiesAndWeightingMultipliers.length; i++) { - if (i < randomSplit) { - strategiesAndWeightingMultipliers1[i] = strategiesAndWeightingMultipliers[i]; - } else { - strategiesAndWeightingMultipliers2[i - randomSplit] = strategiesAndWeightingMultipliers[i]; - } - } - uint8 quorumNumber = uint8(voteWeigher.quorumCount()); - // create quorum with the first randomSplit elements - cheats.startPrank(serviceManagerOwner); - voteWeigher.createQuorum(strategiesAndWeightingMultipliers1); - - // add the rest of the strategies - for (uint i = 0; i < strategiesAndWeightingMultipliers2.length; i++) { - cheats.expectEmit(true, true, true, true, address(voteWeigher)); - emit StrategyAddedToQuorum(quorumNumber, strategiesAndWeightingMultipliers2[i].strategy); - } - voteWeigher.addStrategiesConsideredAndMultipliers(quorumNumber, strategiesAndWeightingMultipliers2); - - // check that the quorum was created and strategies were added correctly - for (uint i = 0; i < strategiesAndWeightingMultipliers.length; i++) { - IVoteWeigher.StrategyAndWeightingMultiplier memory strategyAndWeightingMultiplier = voteWeigher.strategyAndWeightingMultiplierForQuorumByIndex(quorumNumber, i); - assertEq(address(strategyAndWeightingMultiplier.strategy), address(strategiesAndWeightingMultipliers[i].strategy)); - assertEq(strategyAndWeightingMultiplier.multiplier, strategiesAndWeightingMultipliers[i].multiplier); - } - } - - function testAddStrategiesConsideredAndMultipliers_NotFromServiceManagerOwner_Reverts( - address notServiceManagerOwner - ) public fuzzedAddress(notServiceManagerOwner) { - cheats.assume(notServiceManagerOwner != serviceManagerOwner); - IVoteWeigher.StrategyAndWeightingMultiplier[] memory strategiesAndWeightingMultipliers = _defaultStrategiesAndWeightingMultipliers(); - - // create quorum with all but the last element - uint8 quorumNumber = uint8(voteWeigher.quorumCount()); - cheats.prank(serviceManagerOwner); - voteWeigher.createQuorum(strategiesAndWeightingMultipliers); - - // add the last element - cheats.prank(notServiceManagerOwner); - cheats.expectRevert("VoteWeigherBase.onlyServiceManagerOwner: caller is not the owner of the serviceManager"); - voteWeigher.addStrategiesConsideredAndMultipliers(quorumNumber, strategiesAndWeightingMultipliers); - } - - function testAddStrategiesConsideredAndMultipliers_ForNonexistentQuorum_Reverts() public { - IVoteWeigher.StrategyAndWeightingMultiplier[] memory strategiesAndWeightingMultipliers = _defaultStrategiesAndWeightingMultipliers(); - - // create quorum with all but the last element - uint8 quorumNumber = uint8(voteWeigher.quorumCount()); - cheats.startPrank(serviceManagerOwner); - voteWeigher.createQuorum(strategiesAndWeightingMultipliers); - - // add the last element - cheats.expectRevert("VoteWeigherBase.validQuorumNumber: quorumNumber is not valid"); - voteWeigher.addStrategiesConsideredAndMultipliers(quorumNumber+1, strategiesAndWeightingMultipliers); - } - - // this test generates a psudorandom descending order array of indices to remove - // removes them, and checks that the strategies were removed correctly by computing - // a local copy of the strategies when the removal algorithm is applied and comparing - function testRemoveStrategiesConsideredAndMultipliers_Valid( - IVoteWeigher.StrategyAndWeightingMultiplier[] memory strategiesAndWeightingMultipliers, - uint256 randomness - ) public { - strategiesAndWeightingMultipliers = _convertToValidStrategiesAndWeightingMultipliers(strategiesAndWeightingMultipliers); - // generate a bunch of random array of valid descending order indices - uint256[] memory indicesToRemove = _generateRandomUniqueIndices(randomness, strategiesAndWeightingMultipliers.length); - - // create the quorum - uint8 quorumNumber = uint8(voteWeigher.quorumCount()); - cheats.startPrank(serviceManagerOwner); - voteWeigher.createQuorum(strategiesAndWeightingMultipliers); - - // remove certain strategies - // make sure events are emmitted - for (uint i = 0; i < indicesToRemove.length; i++) { - cheats.expectEmit(true, true, true, true, address(voteWeigher)); - emit StrategyRemovedFromQuorum(quorumNumber, strategiesAndWeightingMultipliers[indicesToRemove[i]].strategy); - } - voteWeigher.removeStrategiesConsideredAndMultipliers(quorumNumber, indicesToRemove); - - // check that the strategies that were not removed are still there - // get all strategies and multipliers form the contracts - IVoteWeigher.StrategyAndWeightingMultiplier[] memory strategiesAndWeightingMultipliersFromContract = new IVoteWeigher.StrategyAndWeightingMultiplier[](voteWeigher.strategiesConsideredAndMultipliersLength(quorumNumber)); - for (uint256 i = 0; i < strategiesAndWeightingMultipliersFromContract.length; i++) { - strategiesAndWeightingMultipliersFromContract[i] = voteWeigher.strategyAndWeightingMultiplierForQuorumByIndex(quorumNumber, i); - } - - // remove indicesToRemove from local strategiesAndWeightingMultipliers - IVoteWeigher.StrategyAndWeightingMultiplier[] memory strategiesAndWeightingMultipliersLocal = new IVoteWeigher.StrategyAndWeightingMultiplier[](strategiesAndWeightingMultipliers.length - indicesToRemove.length); - - // run the removal algorithm locally - uint256 endIndex = strategiesAndWeightingMultipliers.length - 1; - for (uint256 i = 0; i < indicesToRemove.length; i++) { - strategiesAndWeightingMultipliers[indicesToRemove[i]] = strategiesAndWeightingMultipliers[endIndex]; - if (endIndex > 0) { - endIndex--; - } - } - for (uint256 i = 0; i < strategiesAndWeightingMultipliersLocal.length; i++) { - strategiesAndWeightingMultipliersLocal[i] = strategiesAndWeightingMultipliers[i]; - } - - // check that the arrays are the same - assertEq(strategiesAndWeightingMultipliersFromContract.length, strategiesAndWeightingMultipliersLocal.length); - for (uint256 i = 0; i < strategiesAndWeightingMultipliersFromContract.length; i++) { - assertEq(address(strategiesAndWeightingMultipliersFromContract[i].strategy), address(strategiesAndWeightingMultipliersLocal[i].strategy)); - assertEq(strategiesAndWeightingMultipliersFromContract[i].multiplier, strategiesAndWeightingMultipliersLocal[i].multiplier); - } - - } - - function testRemoveStrategiesConsideredAndMultipliers_NotFromServiceManagerOwner_Reverts( - address notServiceManagerOwner - ) public fuzzedAddress(notServiceManagerOwner) { - cheats.assume(notServiceManagerOwner != serviceManagerOwner); - IVoteWeigher.StrategyAndWeightingMultiplier[] memory strategiesAndWeightingMultipliers = _defaultStrategiesAndWeightingMultipliers(); - - uint256[] memory indicesToRemove = new uint256[](1); - - // create a valid quorum - uint8 quorumNumber = uint8(voteWeigher.quorumCount()); - cheats.prank(serviceManagerOwner); - voteWeigher.createQuorum(strategiesAndWeightingMultipliers); - - // remove certain strategies - cheats.prank(notServiceManagerOwner); - cheats.expectRevert("VoteWeigherBase.onlyServiceManagerOwner: caller is not the owner of the serviceManager"); - voteWeigher.removeStrategiesConsideredAndMultipliers(quorumNumber, indicesToRemove); - } - - function testRemoveStrategiesConsideredAndMultipliers_ForNonexistentQuorum_Reverts() public { - IVoteWeigher.StrategyAndWeightingMultiplier[] memory strategiesAndWeightingMultipliers = _defaultStrategiesAndWeightingMultipliers(); - - uint256[] memory indicesToRemove = new uint256[](1); - - // create a valid quorum - uint8 quorumNumber = uint8(voteWeigher.quorumCount()); - cheats.startPrank(serviceManagerOwner); - voteWeigher.createQuorum(strategiesAndWeightingMultipliers); - - // remove strategies from a non-existent quorum - cheats.expectRevert("VoteWeigherBase.validQuorumNumber: quorumNumber is not valid"); - voteWeigher.removeStrategiesConsideredAndMultipliers(quorumNumber + 1, indicesToRemove); - } - - function testRemoveStrategiesConsideredAndMultipliers_EmptyIndicesToRemove_Reverts() public { - IVoteWeigher.StrategyAndWeightingMultiplier[] memory strategiesAndWeightingMultipliers = _defaultStrategiesAndWeightingMultipliers(); - - // create a valid quorum - uint8 quorumNumber = uint8(voteWeigher.quorumCount()); - cheats.startPrank(serviceManagerOwner); - voteWeigher.createQuorum(strategiesAndWeightingMultipliers); - - // remove no strategies - cheats.expectRevert("VoteWeigherBase.removeStrategiesConsideredAndMultipliers: no indices to remove provided"); - voteWeigher.removeStrategiesConsideredAndMultipliers(quorumNumber, new uint256[](0)); - } - - function testModifyStrategyWeights_NotFromServiceManagerOwner_Reverts( - address notServiceManagerOwner - ) public fuzzedAddress(notServiceManagerOwner) { - cheats.assume(notServiceManagerOwner != serviceManagerOwner); - IVoteWeigher.StrategyAndWeightingMultiplier[] memory strategiesAndWeightingMultipliers = _defaultStrategiesAndWeightingMultipliers(); - - uint256[] memory strategyIndices = new uint256[](1); - uint96[] memory newWeights = new uint96[](1); - - // create a valid quorum - uint8 quorumNumber = uint8(voteWeigher.quorumCount()); - cheats.prank(serviceManagerOwner); - voteWeigher.createQuorum(strategiesAndWeightingMultipliers); - - // modify certain strategies - cheats.prank(notServiceManagerOwner); - cheats.expectRevert("VoteWeigherBase.onlyServiceManagerOwner: caller is not the owner of the serviceManager"); - voteWeigher.modifyStrategyWeights(quorumNumber, strategyIndices, newWeights); - } - - function testModifyStrategyWeights_Valid( - IVoteWeigher.StrategyAndWeightingMultiplier[] memory strategiesAndWeightingMultipliers, - uint96[] memory newWeights, - uint256 randomness - ) public { - strategiesAndWeightingMultipliers = _convertToValidStrategiesAndWeightingMultipliers(strategiesAndWeightingMultipliers); - uint256[] memory strategyIndices = _generateRandomUniqueIndices(randomness, strategiesAndWeightingMultipliers.length); - - // trim the provided weights to the length of the strategyIndices and extend if it is shorter - uint96[] memory newWeightsTrim = new uint96[](strategyIndices.length); - for (uint256 i = 0; i < strategyIndices.length; i++) { - if(i < newWeights.length) { - newWeightsTrim[i] = newWeights[i]; - } else { - newWeightsTrim[i] = strategiesAndWeightingMultipliers[strategyIndices[i]].multiplier - 1; - } - } - newWeights = newWeightsTrim; - - // create a valid quorum - uint8 quorumNumber = uint8(voteWeigher.quorumCount()); - cheats.startPrank(serviceManagerOwner); - voteWeigher.createQuorum(strategiesAndWeightingMultipliers); - - // modify certain strategies - for (uint i = 0; i < strategyIndices.length; i++) { - cheats.expectEmit(true, true, true, true, address(voteWeigher)); - emit StrategyMultiplierUpdated(quorumNumber, strategiesAndWeightingMultipliers[strategyIndices[i]].strategy, newWeights[i]); - } - voteWeigher.modifyStrategyWeights(quorumNumber, strategyIndices, newWeights); - - // convert the strategies and weighting multipliers to the modified - for (uint i = 0; i < strategyIndices.length; i++) { - strategiesAndWeightingMultipliers[strategyIndices[i]].multiplier = newWeights[i]; - } - // make sure the quorum strategies and weights have changed - for (uint i = 0; i < strategiesAndWeightingMultipliers.length; i++) { - IVoteWeigher.StrategyAndWeightingMultiplier memory strategyAndWeightingMultiplier = voteWeigher.strategyAndWeightingMultiplierForQuorumByIndex(quorumNumber, i); - assertEq(address(strategyAndWeightingMultiplier.strategy), address(strategiesAndWeightingMultipliers[i].strategy)); - assertEq(strategyAndWeightingMultiplier.multiplier, strategiesAndWeightingMultipliers[i].multiplier); - } - } - - function testModifyStrategyWeights_ForNonexistentQuorum_Reverts() public { - IVoteWeigher.StrategyAndWeightingMultiplier[] memory strategiesAndWeightingMultipliers = _defaultStrategiesAndWeightingMultipliers(); - - uint256[] memory strategyIndices = new uint256[](1); - uint96[] memory newWeights = new uint96[](1); - - // create a valid quorum - uint8 quorumNumber = uint8(voteWeigher.quorumCount()); - cheats.startPrank(serviceManagerOwner); - voteWeigher.createQuorum(strategiesAndWeightingMultipliers); - - // modify certain strategies of a non-existent quorum - cheats.expectRevert("VoteWeigherBase.validQuorumNumber: quorumNumber is not valid"); - voteWeigher.modifyStrategyWeights(quorumNumber + 1, strategyIndices, newWeights); - } - - function testModifyStrategyWeights_InconsistentStrategyAndWeightArrayLengths_Reverts( - uint256[] memory strategyIndices, - uint96[] memory newWeights - ) public { - IVoteWeigher.StrategyAndWeightingMultiplier[] memory strategiesAndWeightingMultipliers = _defaultStrategiesAndWeightingMultipliers(); - - // make sure the arrays are of different lengths - cheats.assume(strategyIndices.length != newWeights.length); - cheats.assume(strategyIndices.length > 0); - - // create a valid quorum - uint8 quorumNumber = uint8(voteWeigher.quorumCount()); - cheats.startPrank(serviceManagerOwner); - voteWeigher.createQuorum(strategiesAndWeightingMultipliers); - - // modify certain strategies - cheats.expectRevert("VoteWeigherBase.modifyStrategyWeights: input length mismatch"); - voteWeigher.modifyStrategyWeights(quorumNumber, strategyIndices, newWeights); - } - - function testModifyStrategyWeights_EmptyStrategyIndicesAndWeights_Reverts() public { - IVoteWeigher.StrategyAndWeightingMultiplier[] memory strategiesAndWeightingMultipliers = _defaultStrategiesAndWeightingMultipliers(); - - // create a valid quorum - uint8 quorumNumber = uint8(voteWeigher.quorumCount()); - cheats.startPrank(serviceManagerOwner); - voteWeigher.createQuorum(strategiesAndWeightingMultipliers); - - // modify no strategies - cheats.expectRevert("VoteWeigherBase.modifyStrategyWeights: no strategy indices provided"); - voteWeigher.modifyStrategyWeights(quorumNumber, new uint256[](0), new uint96[](0)); - } - - function testWeightOfOperatorForQuorum( - address operator, - IVoteWeigher.StrategyAndWeightingMultiplier[] memory strategiesAndMultipliers, - uint96[] memory shares - ) public { - strategiesAndMultipliers = _convertToValidStrategiesAndWeightingMultipliers(strategiesAndMultipliers); - cheats.assume(shares.length >= strategiesAndMultipliers.length); - for (uint i = 0; i < strategiesAndMultipliers.length; i++) { - if(uint256(shares[i]) * uint256(strategiesAndMultipliers[i].multiplier) > type(uint96).max) { - strategiesAndMultipliers[i].multiplier = 1; - } - } - - // set the operator shares - for (uint i = 0; i < strategiesAndMultipliers.length; i++) { - delegationMock.setOperatorShares(operator, strategiesAndMultipliers[i].strategy, shares[i]); - } - - // create a valid quorum - uint8 quorumNumber = uint8(voteWeigher.quorumCount()); - cheats.startPrank(serviceManagerOwner); - voteWeigher.createQuorum(strategiesAndMultipliers); - - // make sure the weight of the operator is correct - uint256 expectedWeight = 0; - for (uint i = 0; i < strategiesAndMultipliers.length; i++) { - expectedWeight += shares[i] * strategiesAndMultipliers[i].multiplier / voteWeigher.WEIGHTING_DIVISOR(); - } - - assertEq(voteWeigher.weightOfOperatorForQuorum(quorumNumber, operator), expectedWeight); - } - - function _removeDuplicates(IVoteWeigher.StrategyAndWeightingMultiplier[] memory strategiesAndWeightingMultipliers) - internal - returns(IVoteWeigher.StrategyAndWeightingMultiplier[] memory) - { - IVoteWeigher.StrategyAndWeightingMultiplier[] memory deduplicatedStrategiesAndWeightingMultipliers = new IVoteWeigher.StrategyAndWeightingMultiplier[](strategiesAndWeightingMultipliers.length); - uint256 numUniqueStrategies = 0; - // check for duplicates - for (uint i = 0; i < strategiesAndWeightingMultipliers.length; i++) { - if(strategyInCurrentArray[strategiesAndWeightingMultipliers[i].strategy]) { - continue; - } - strategyInCurrentArray[strategiesAndWeightingMultipliers[i].strategy] = true; - deduplicatedStrategiesAndWeightingMultipliers[numUniqueStrategies] = strategiesAndWeightingMultipliers[i]; - numUniqueStrategies++; - } - - // undo storage changes - for (uint i = 0; i < strategiesAndWeightingMultipliers.length; i++) { - strategyInCurrentArray[strategiesAndWeightingMultipliers[i].strategy] = false; - } - - IVoteWeigher.StrategyAndWeightingMultiplier[] memory trimmedStrategiesAndWeightingMultipliers = new IVoteWeigher.StrategyAndWeightingMultiplier[](numUniqueStrategies); - for (uint i = 0; i < numUniqueStrategies; i++) { - trimmedStrategiesAndWeightingMultipliers[i] = deduplicatedStrategiesAndWeightingMultipliers[i]; - } - return trimmedStrategiesAndWeightingMultipliers; - } - - function _replaceZeroWeights(IVoteWeigher.StrategyAndWeightingMultiplier[] memory strategiesAndWeightingMultipliers) internal pure returns(IVoteWeigher.StrategyAndWeightingMultiplier[] memory) { - for (uint256 i = 0; i < strategiesAndWeightingMultipliers.length; i++) { - if (strategiesAndWeightingMultipliers[i].multiplier == 0) { - strategiesAndWeightingMultipliers[i].multiplier = 1; - } - } - return strategiesAndWeightingMultipliers; - } - - function _generateRandomUniqueIndices(uint256 randomness, uint256 length) internal pure returns(uint256[] memory) { - uint256[] memory indices = new uint256[](length); - for (uint256 i = 0; i < length; i++) { - indices[i] = length - i - 1; - } - - uint256[] memory randomIndices = new uint256[](length); - uint256 numRandomIndices = 0; - // take random indices in ascending order - for (uint256 i = 0; i < length; i++) { - if (uint256(keccak256(abi.encode(randomness, i))) % length < 10) { - randomIndices[numRandomIndices] = indices[i]; - numRandomIndices++; - } - } - - // trim the array - uint256[] memory trimmedRandomIndices = new uint256[](numRandomIndices); - for (uint256 i = 0; i < numRandomIndices; i++) { - trimmedRandomIndices[i] = randomIndices[i]; - } - - return trimmedRandomIndices; - } - - function _convertToValidStrategiesAndWeightingMultipliers(IVoteWeigher.StrategyAndWeightingMultiplier[] memory strategiesAndWeightingMultipliers) internal returns (IVoteWeigher.StrategyAndWeightingMultiplier[] memory) { - strategiesAndWeightingMultipliers = _removeDuplicates(strategiesAndWeightingMultipliers); - cheats.assume(strategiesAndWeightingMultipliers.length <= voteWeigher.MAX_WEIGHING_FUNCTION_LENGTH()); - cheats.assume(strategiesAndWeightingMultipliers.length > 0); - return _replaceZeroWeights(strategiesAndWeightingMultipliers); - } - - function _defaultStrategiesAndWeightingMultipliers() internal pure returns (IVoteWeigher.StrategyAndWeightingMultiplier[] memory) { - IVoteWeigher.StrategyAndWeightingMultiplier[] memory strategiesAndWeightingMultipliers = new IVoteWeigher.StrategyAndWeightingMultiplier[](2); - strategiesAndWeightingMultipliers[0] = IVoteWeigher.StrategyAndWeightingMultiplier({ - strategy: IStrategy(address(uint160(uint256(keccak256("strategy1"))))), - multiplier: 1.04 ether - }); - strategiesAndWeightingMultipliers[1] = IVoteWeigher.StrategyAndWeightingMultiplier({ - strategy: IStrategy(address(uint160(uint256(keccak256("strategy2"))))), - multiplier: 1.69 ether - }); - return strategiesAndWeightingMultipliers; - } -} \ No newline at end of file diff --git a/src/test/utils/BLSMockAVSDeployer.sol b/src/test/utils/BLSMockAVSDeployer.sol deleted file mode 100644 index 2b42eaa7f..000000000 --- a/src/test/utils/BLSMockAVSDeployer.sol +++ /dev/null @@ -1,130 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity =0.8.12; - -import "../../contracts/middleware/BLSSignatureChecker.sol"; -import "./MockAVSDeployer.sol"; - -contract BLSMockAVSDeployer is MockAVSDeployer { - using BN254 for BN254.G1Point; - - bytes32 msgHash = keccak256(abi.encodePacked("hello world")); - uint256 aggSignerPrivKey = 69; - BN254.G2Point aggSignerApkG2; - BN254.G2Point oneHundredQuorumApkG2; - BN254.G1Point sigma; - - function _setUpBLSMockAVSDeployer() virtual public { - _deployMockEigenLayerAndAVS(); - _setAggregatePublicKeysAndSignature(); - } - - function _setAggregatePublicKeysAndSignature() internal { - // aggSignerPrivKey*g2 - aggSignerApkG2.X[1] = 19101821850089705274637533855249918363070101489527618151493230256975900223847; - aggSignerApkG2.X[0] = 5334410886741819556325359147377682006012228123419628681352847439302316235957; - aggSignerApkG2.Y[1] = 354176189041917478648604979334478067325821134838555150300539079146482658331; - aggSignerApkG2.Y[0] = 4185483097059047421902184823581361466320657066600218863748375739772335928910; - - // 100*aggSignerPrivKey*g2 - oneHundredQuorumApkG2.X[1] = 6187649255575786743153792867265230878737103598736372524337965086852090105771; - oneHundredQuorumApkG2.X[0] = 5334877400925935887383922877430837542135722474116902175395820705628447222839; - oneHundredQuorumApkG2.Y[1] = 4668116328019846503695710811760363536142902258271850958815598072072236299223; - oneHundredQuorumApkG2.Y[0] = 21446056442597180561077194011672151329458819211586246807143487001691968661015; - - sigma = BN254.hashToG1(msgHash).scalar_mul(aggSignerPrivKey); - } - - - function _generateSignerAndNonSignerPrivateKeys(uint256 pseudoRandomNumber, uint256 numSigners, uint256 numNonSigners) internal view returns (uint256[] memory, uint256[] memory) { - uint256[] memory signerPrivateKeys = new uint256[](numSigners); - // generate numSigners numbers that add up to aggSignerPrivKey mod BN254.FR_MODULUS - uint256 sum = 0; - for (uint i = 0; i < numSigners - 1; i++) { - signerPrivateKeys[i] = uint256(keccak256(abi.encodePacked("signerPrivateKey", pseudoRandomNumber, i))) % BN254.FR_MODULUS; - sum = addmod(sum, signerPrivateKeys[i], BN254.FR_MODULUS); - } - // signer private keys need to add to aggSignerPrivKey - signerPrivateKeys[numSigners - 1] = addmod(aggSignerPrivKey, BN254.FR_MODULUS - sum % BN254.FR_MODULUS, BN254.FR_MODULUS); - - uint256[] memory nonSignerPrivateKeys = new uint256[](numNonSigners); - for (uint i = 0; i < numNonSigners; i++) { - nonSignerPrivateKeys[i] = uint256(keccak256(abi.encodePacked("nonSignerPrivateKey", pseudoRandomNumber, i))) % BN254.FR_MODULUS; - uint256 j = 0; - if(i != 0) { - while(BN254.generatorG1().scalar_mul(nonSignerPrivateKeys[i]).hashG1Point() <= BN254.generatorG1().scalar_mul(nonSignerPrivateKeys[i-1]).hashG1Point()){ - nonSignerPrivateKeys[i] = uint256(keccak256(abi.encodePacked("nonSignerPrivateKey", pseudoRandomNumber, j))) % BN254.FR_MODULUS; - j++; - } - } - } - - return (signerPrivateKeys, nonSignerPrivateKeys); - } - - function _registerSignatoriesAndGetNonSignerStakeAndSignatureRandom(uint256 pseudoRandomNumber, uint256 numNonSigners, uint256 quorumBitmap) internal returns(uint32, BLSSignatureChecker.NonSignerStakesAndSignature memory) { - (uint256[] memory signerPrivateKeys, uint256[] memory nonSignerPrivateKeys) = _generateSignerAndNonSignerPrivateKeys(pseudoRandomNumber, maxOperatorsToRegister - numNonSigners, numNonSigners); - bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); - - // randomly combine signer and non-signer private keys - uint256[] memory privateKeys = new uint256[](maxOperatorsToRegister); - // generate addresses and public keys - address[] memory operators = new address[](maxOperatorsToRegister); - BN254.G1Point[] memory pubkeys = new BN254.G1Point[](maxOperatorsToRegister); - BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature; - nonSignerStakesAndSignature.quorumApks = new BN254.G1Point[](quorumNumbers.length); - nonSignerStakesAndSignature.nonSignerPubkeys = new BN254.G1Point[](numNonSigners); - bytes32[] memory nonSignerOperatorIds = new bytes32[](numNonSigners); - { - uint256 signerIndex = 0; - uint256 nonSignerIndex = 0; - for (uint i = 0; i < maxOperatorsToRegister; i++) { - uint256 randomSeed = uint256(keccak256(abi.encodePacked("privKeyCombination", i))); - if (randomSeed % 2 == 0 && signerIndex < signerPrivateKeys.length) { - privateKeys[i] = signerPrivateKeys[signerIndex]; - signerIndex++; - } else if (nonSignerIndex < nonSignerPrivateKeys.length) { - privateKeys[i] = nonSignerPrivateKeys[nonSignerIndex]; - nonSignerStakesAndSignature.nonSignerPubkeys[nonSignerIndex] = BN254.generatorG1().scalar_mul(privateKeys[i]); - nonSignerOperatorIds[nonSignerIndex] = nonSignerStakesAndSignature.nonSignerPubkeys[nonSignerIndex].hashG1Point(); - nonSignerIndex++; - } else { - privateKeys[i] = signerPrivateKeys[signerIndex]; - signerIndex++; - } - - operators[i] = _incrementAddress(defaultOperator, i); - pubkeys[i] = BN254.generatorG1().scalar_mul(privateKeys[i]); - - // add the public key to each quorum - for (uint j = 0; j < nonSignerStakesAndSignature.quorumApks.length; j++) { - nonSignerStakesAndSignature.quorumApks[j] = nonSignerStakesAndSignature.quorumApks[j].plus(pubkeys[i]); - } - } - } - - // register all operators for the first quorum - for (uint i = 0; i < maxOperatorsToRegister; i++) { - cheats.roll(registrationBlockNumber + blocksBetweenRegistrations * i); - _registerOperatorWithCoordinator(operators[i], quorumBitmap, pubkeys[i], defaultStake); - } - - uint32 referenceBlockNumber = registrationBlockNumber + blocksBetweenRegistrations * uint32(maxOperatorsToRegister) + 1; - cheats.roll(referenceBlockNumber + 100); - - BLSOperatorStateRetriever.CheckSignaturesIndices memory checkSignaturesIndices = operatorStateRetriever.getCheckSignaturesIndices( - registryCoordinator, - referenceBlockNumber, - quorumNumbers, - nonSignerOperatorIds - ); - - nonSignerStakesAndSignature.nonSignerQuorumBitmapIndices = checkSignaturesIndices.nonSignerQuorumBitmapIndices; - nonSignerStakesAndSignature.apkG2 = aggSignerApkG2; - nonSignerStakesAndSignature.sigma = sigma; - nonSignerStakesAndSignature.quorumApkIndices = checkSignaturesIndices.quorumApkIndices; - nonSignerStakesAndSignature.totalStakeIndices = checkSignaturesIndices.totalStakeIndices; - nonSignerStakesAndSignature.nonSignerStakeIndices = checkSignaturesIndices.nonSignerStakeIndices; - - return (referenceBlockNumber, nonSignerStakesAndSignature); - } -} \ No newline at end of file diff --git a/src/test/utils/MockAVSDeployer.sol b/src/test/utils/MockAVSDeployer.sol deleted file mode 100644 index 8d06d551c..000000000 --- a/src/test/utils/MockAVSDeployer.sol +++ /dev/null @@ -1,390 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity =0.8.12; - -import "@openzeppelin/contracts/token/ERC20/presets/ERC20PresetFixedSupply.sol"; -import "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; -import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; - -import "../../contracts/core/Slasher.sol"; -import "../../contracts/permissions/PauserRegistry.sol"; -import "../../contracts/interfaces/IStrategyManager.sol"; -import "../../contracts/interfaces/IStakeRegistry.sol"; -import "../../contracts/interfaces/IServiceManager.sol"; -import "../../contracts/interfaces/IVoteWeigher.sol"; - -import "../../contracts/middleware/BLSPublicKeyCompendium.sol"; -import "../../contracts/middleware/BLSOperatorStateRetriever.sol"; -import "../../contracts/middleware/BLSRegistryCoordinatorWithIndices.sol"; -import "../../contracts/middleware/BLSPubkeyRegistry.sol"; -import "../../contracts/middleware/IndexRegistry.sol"; - -import "../../contracts/libraries/BitmapUtils.sol"; - -import "../mocks/StrategyManagerMock.sol"; -import "../mocks/EigenPodManagerMock.sol"; -import "../mocks/ServiceManagerMock.sol"; -import "../mocks/OwnableMock.sol"; -import "../mocks/DelegationManagerMock.sol"; -import "../mocks/SlasherMock.sol"; -import "../mocks/BLSPublicKeyCompendiumMock.sol"; -import "../mocks/EmptyContract.sol"; - -import "../harnesses/StakeRegistryHarness.sol"; -import "../harnesses/BLSRegistryCoordinatorWithIndicesHarness.sol"; - -import "forge-std/Test.sol"; - -contract MockAVSDeployer is Test { - using BN254 for BN254.G1Point; - - Vm cheats = Vm(HEVM_ADDRESS); - - ProxyAdmin public proxyAdmin; - PauserRegistry public pauserRegistry; - - ISlasher public slasher = ISlasher(address(uint160(uint256(keccak256("slasher"))))); - Slasher public slasherImplementation; - - EmptyContract public emptyContract; - BLSPublicKeyCompendiumMock public pubkeyCompendium; - - BLSRegistryCoordinatorWithIndicesHarness public registryCoordinatorImplementation; - StakeRegistryHarness public stakeRegistryImplementation; - IBLSPubkeyRegistry public blsPubkeyRegistryImplementation; - IIndexRegistry public indexRegistryImplementation; - - BLSOperatorStateRetriever public operatorStateRetriever; - BLSRegistryCoordinatorWithIndicesHarness public registryCoordinator; - StakeRegistryHarness public stakeRegistry; - IBLSPubkeyRegistry public blsPubkeyRegistry; - IIndexRegistry public indexRegistry; - - ServiceManagerMock public serviceManagerMock; - StrategyManagerMock public strategyManagerMock; - DelegationManagerMock public delegationMock; - EigenPodManagerMock public eigenPodManagerMock; - - address public proxyAdminOwner = address(uint160(uint256(keccak256("proxyAdminOwner")))); - address public serviceManagerOwner = address(uint160(uint256(keccak256("serviceManagerOwner")))); - address public pauser = address(uint160(uint256(keccak256("pauser")))); - address public unpauser = address(uint160(uint256(keccak256("unpauser")))); - - uint256 churnApproverPrivateKey = uint256(keccak256("churnApproverPrivateKey")); - address churnApprover = cheats.addr(churnApproverPrivateKey); - bytes32 defaultSalt = bytes32(uint256(keccak256("defaultSalt"))); - - address ejector = address(uint160(uint256(keccak256("ejector")))); - - address defaultOperator = address(uint160(uint256(keccak256("defaultOperator")))); - bytes32 defaultOperatorId; - BN254.G1Point internal defaultPubKey = BN254.G1Point(18260007818883133054078754218619977578772505796600400998181738095793040006897,3432351341799135763167709827653955074218841517684851694584291831827675065899); - string defaultSocket = "69.69.69.69:420"; - uint96 defaultStake = 1 ether; - uint8 defaultQuorumNumber = 0; - - uint32 defaultMaxOperatorCount = 10; - uint16 defaultKickBIPsOfOperatorStake = 15000; - uint16 defaultKickBIPsOfTotalStake = 150; - uint8 numQuorums = 192; - - IBLSRegistryCoordinatorWithIndices.OperatorSetParam[] operatorSetParams; - - uint8 maxQuorumsToRegisterFor = 4; - uint256 maxOperatorsToRegister = 4; - uint32 registrationBlockNumber = 100; - uint32 blocksBetweenRegistrations = 10; - - struct OperatorMetadata { - uint256 quorumBitmap; - address operator; - bytes32 operatorId; - BN254.G1Point pubkey; - uint96[] stakes; // in every quorum for simplicity - } - - uint256 MAX_QUORUM_BITMAP = type(uint192).max; - - function _deployMockEigenLayerAndAVS() internal { - _deployMockEigenLayerAndAVS(numQuorums); - } - - function _deployMockEigenLayerAndAVS(uint8 numQuorumsToAdd) internal { - emptyContract = new EmptyContract(); - - defaultOperatorId = defaultPubKey.hashG1Point(); - - cheats.startPrank(proxyAdminOwner); - proxyAdmin = new ProxyAdmin(); - - address[] memory pausers = new address[](1); - pausers[0] = pauser; - pauserRegistry = new PauserRegistry(pausers, unpauser); - - delegationMock = new DelegationManagerMock(); - eigenPodManagerMock = new EigenPodManagerMock(); - strategyManagerMock = new StrategyManagerMock(); - slasherImplementation = new Slasher(strategyManagerMock, delegationMock); - slasher = Slasher( - address( - new TransparentUpgradeableProxy( - address(slasherImplementation), - address(proxyAdmin), - abi.encodeWithSelector(Slasher.initialize.selector, msg.sender, pauserRegistry, 0/*initialPausedStatus*/) - ) - ) - ); - - strategyManagerMock.setAddresses( - delegationMock, - eigenPodManagerMock, - slasher - ); - - pubkeyCompendium = new BLSPublicKeyCompendiumMock(); - pubkeyCompendium.setBLSPublicKey(defaultOperator, defaultPubKey); - - cheats.stopPrank(); - - cheats.startPrank(serviceManagerOwner); - // make the serviceManagerOwner the owner of the serviceManager contract - serviceManagerMock = new ServiceManagerMock(slasher); - registryCoordinator = BLSRegistryCoordinatorWithIndicesHarness(address( - new TransparentUpgradeableProxy( - address(emptyContract), - address(proxyAdmin), - "" - ) - )); - - stakeRegistry = StakeRegistryHarness( - address( - new TransparentUpgradeableProxy( - address(emptyContract), - address(proxyAdmin), - "" - ) - ) - ); - - indexRegistry = IndexRegistry( - address( - new TransparentUpgradeableProxy( - address(emptyContract), - address(proxyAdmin), - "" - ) - ) - ); - - blsPubkeyRegistry = BLSPubkeyRegistry( - address( - new TransparentUpgradeableProxy( - address(emptyContract), - address(proxyAdmin), - "" - ) - ) - ); - - stakeRegistryImplementation = new StakeRegistryHarness( - IRegistryCoordinator(registryCoordinator), - strategyManagerMock, - IServiceManager(address(serviceManagerMock)) - ); - - cheats.stopPrank(); - cheats.startPrank(proxyAdminOwner); - - // setup the dummy minimum stake for quorum - uint96[] memory minimumStakeForQuorum = new uint96[](numQuorumsToAdd); - for (uint256 i = 0; i < minimumStakeForQuorum.length; i++) { - minimumStakeForQuorum[i] = uint96(i+1); - } - - // setup the dummy quorum strategies - IVoteWeigher.StrategyAndWeightingMultiplier[][] memory quorumStrategiesConsideredAndMultipliers = - new IVoteWeigher.StrategyAndWeightingMultiplier[][](numQuorumsToAdd); - for (uint256 i = 0; i < quorumStrategiesConsideredAndMultipliers.length; i++) { - quorumStrategiesConsideredAndMultipliers[i] = new IVoteWeigher.StrategyAndWeightingMultiplier[](1); - quorumStrategiesConsideredAndMultipliers[i][0] = IVoteWeigher.StrategyAndWeightingMultiplier( - IStrategy(address(uint160(i))), - uint96(i+1) - ); - } - - proxyAdmin.upgradeAndCall( - TransparentUpgradeableProxy(payable(address(stakeRegistry))), - address(stakeRegistryImplementation), - abi.encodeWithSelector( - StakeRegistry.initialize.selector, - minimumStakeForQuorum, - quorumStrategiesConsideredAndMultipliers - ) - ); - - registryCoordinatorImplementation = new BLSRegistryCoordinatorWithIndicesHarness( - slasher, - serviceManagerMock, - stakeRegistry, - blsPubkeyRegistry, - indexRegistry - ); - { - delete operatorSetParams; - for (uint i = 0; i < numQuorumsToAdd; i++) { - // hard code these for now - operatorSetParams.push(IBLSRegistryCoordinatorWithIndices.OperatorSetParam({ - maxOperatorCount: defaultMaxOperatorCount, - kickBIPsOfOperatorStake: defaultKickBIPsOfOperatorStake, - kickBIPsOfTotalStake: defaultKickBIPsOfTotalStake - })); - } - - proxyAdmin.upgradeAndCall( - TransparentUpgradeableProxy(payable(address(registryCoordinator))), - address(registryCoordinatorImplementation), - abi.encodeWithSelector( - BLSRegistryCoordinatorWithIndices.initialize.selector, - churnApprover, - ejector, - operatorSetParams, - pauserRegistry, - 0/*initialPausedStatus*/ - ) - ); - } - - blsPubkeyRegistryImplementation = new BLSPubkeyRegistry( - registryCoordinator, - BLSPublicKeyCompendium(address(pubkeyCompendium)) - ); - - proxyAdmin.upgrade( - TransparentUpgradeableProxy(payable(address(blsPubkeyRegistry))), - address(blsPubkeyRegistryImplementation) - ); - - indexRegistryImplementation = new IndexRegistry( - registryCoordinator - ); - - proxyAdmin.upgrade( - TransparentUpgradeableProxy(payable(address(indexRegistry))), - address(indexRegistryImplementation) - ); - - operatorStateRetriever = new BLSOperatorStateRetriever(); - - cheats.stopPrank(); - } - - /** - * @notice registers operator with coordinator - */ - function _registerOperatorWithCoordinator(address operator, uint256 quorumBitmap, BN254.G1Point memory pubKey) internal { - _registerOperatorWithCoordinator(operator, quorumBitmap, pubKey, defaultStake); - } - - /** - * @notice registers operator with coordinator - */ - function _registerOperatorWithCoordinator(address operator, uint256 quorumBitmap, BN254.G1Point memory pubKey, uint96 stake) internal { - // quorumBitmap can only have 192 least significant bits - quorumBitmap &= MAX_QUORUM_BITMAP; - - pubkeyCompendium.setBLSPublicKey(operator, pubKey); - - bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); - for (uint i = 0; i < quorumNumbers.length; i++) { - stakeRegistry.setOperatorWeight(uint8(quorumNumbers[i]), operator, stake); - } - - cheats.prank(operator); - registryCoordinator.registerOperatorWithCoordinator(quorumNumbers, pubKey, defaultSocket); - } - - /** - * @notice registers operator with coordinator - */ - function _registerOperatorWithCoordinator(address operator, uint256 quorumBitmap, BN254.G1Point memory pubKey, uint96[] memory stakes) internal { - // quorumBitmap can only have 192 least significant bits - quorumBitmap &= MAX_QUORUM_BITMAP; - - pubkeyCompendium.setBLSPublicKey(operator, pubKey); - - bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); - for (uint i = 0; i < quorumNumbers.length; i++) { - stakeRegistry.setOperatorWeight(uint8(quorumNumbers[i]), operator, stakes[uint8(quorumNumbers[i])]); - } - - cheats.prank(operator); - registryCoordinator.registerOperatorWithCoordinator(quorumNumbers, pubKey, defaultSocket); - } - - function _registerRandomOperators(uint256 pseudoRandomNumber) internal returns(OperatorMetadata[] memory, uint256[][] memory) { - OperatorMetadata[] memory operatorMetadatas = new OperatorMetadata[](maxOperatorsToRegister); - for (uint i = 0; i < operatorMetadatas.length; i++) { - // limit to 16 quorums so we don't run out of gas, make them all register for quorum 0 as well - operatorMetadatas[i].quorumBitmap = uint256(keccak256(abi.encodePacked("quorumBitmap", pseudoRandomNumber, i))) & (1 << maxQuorumsToRegisterFor - 1) | 1; - operatorMetadatas[i].operator = _incrementAddress(defaultOperator, i); - operatorMetadatas[i].pubkey = BN254.hashToG1(keccak256(abi.encodePacked("pubkey", pseudoRandomNumber, i))); - operatorMetadatas[i].operatorId = operatorMetadatas[i].pubkey.hashG1Point(); - operatorMetadatas[i].stakes = new uint96[](maxQuorumsToRegisterFor); - for (uint j = 0; j < maxQuorumsToRegisterFor; j++) { - operatorMetadatas[i].stakes[j] = uint96(uint64(uint256(keccak256(abi.encodePacked("stakes", pseudoRandomNumber, i, j))))); - } - } - - // get the index in quorumBitmaps of each operator in each quorum in the order they will register - uint256[][] memory expectedOperatorOverallIndices = new uint256[][](numQuorums); - for (uint i = 0; i < numQuorums; i++) { - uint32 numOperatorsInQuorum; - // for each quorumBitmap, check if the i'th bit is set - for (uint j = 0; j < operatorMetadatas.length; j++) { - if (operatorMetadatas[j].quorumBitmap >> i & 1 == 1) { - numOperatorsInQuorum++; - } - } - expectedOperatorOverallIndices[i] = new uint256[](numOperatorsInQuorum); - uint256 numOperatorCounter; - for (uint j = 0; j < operatorMetadatas.length; j++) { - if (operatorMetadatas[j].quorumBitmap >> i & 1 == 1) { - expectedOperatorOverallIndices[i][numOperatorCounter] = j; - numOperatorCounter++; - } - } - } - - // register operators - for (uint i = 0; i < operatorMetadatas.length; i++) { - cheats.roll(registrationBlockNumber + blocksBetweenRegistrations * i); - - _registerOperatorWithCoordinator(operatorMetadatas[i].operator, operatorMetadatas[i].quorumBitmap, operatorMetadatas[i].pubkey, operatorMetadatas[i].stakes); - } - - return (operatorMetadatas, expectedOperatorOverallIndices); - } - - function _incrementAddress(address start, uint256 inc) internal pure returns(address) { - return address(uint160(uint256(uint160(start) + inc))); - } - - function _incrementBytes32(bytes32 start, uint256 inc) internal pure returns(bytes32) { - return bytes32(uint256(start) + inc); - } - - function _signOperatorChurnApproval(bytes32 registeringOperatorId, IBLSRegistryCoordinatorWithIndices.OperatorKickParam[] memory operatorKickParams, bytes32 salt, uint256 expiry) internal view returns(ISignatureUtils.SignatureWithSaltAndExpiry memory) { - bytes32 digestHash = registryCoordinator.calculateOperatorChurnApprovalDigestHash( - registeringOperatorId, - operatorKickParams, - salt, - expiry - ); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(churnApproverPrivateKey, digestHash); - return ISignatureUtils.SignatureWithSaltAndExpiry({ - signature: abi.encodePacked(r, s, v), - expiry: expiry, - salt: salt - }); - } -} \ No newline at end of file