From 5edcc781d7511b064ae788e18552bd81d412dcdb Mon Sep 17 00:00:00 2001 From: steven Date: Wed, 8 Nov 2023 12:42:01 -0500 Subject: [PATCH 001/101] fix: remove husky command that only needs to be run once --- CONTRIBUTING.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e88e2c61..590c7f8c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -17,8 +17,6 @@ npm install npx husky install -npx husky add .husky/commit-msg 'npx --no -- commitlint --edit ${1}' - forge install ``` From cda67ff88ea61eaf9f7c868ee4c92a5c9d4234a9 Mon Sep 17 00:00:00 2001 From: gpsanant Date: Wed, 15 Nov 2023 16:53:54 -0800 Subject: [PATCH 002/101] update nonSignerForQuorumIndex outside of loop --- src/BLSSignatureChecker.sol | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/BLSSignatureChecker.sol b/src/BLSSignatureChecker.sol index 86d44680..ee80785e 100644 --- a/src/BLSSignatureChecker.sol +++ b/src/BLSSignatureChecker.sol @@ -132,11 +132,13 @@ contract BLSSignatureChecker is IBLSSignatureChecker { stakeRegistry.getTotalStakeAtBlockNumberFromIndex(quorumNumber, referenceBlockNumber, nonSignerStakesAndSignature.totalStakeIndices[quorumNumberIndex]); // copy total stake to signed stake quorumStakeTotals.signedStakeForQuorum[quorumNumberIndex] = quorumStakeTotals.totalStakeForQuorum[quorumNumberIndex]; + + // keep track of the nonSigners index in the quorum + uint32 nonSignerForQuorumIndex = 0; + // 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] -= From 119085df6afb2ebd9a28dc03c10f359a594e7602 Mon Sep 17 00:00:00 2001 From: ChaoticWalrus <93558947+ChaoticWalrus@users.noreply.github.com> Date: Thu, 16 Nov 2023 10:59:27 -0800 Subject: [PATCH 003/101] chore: add MacOS indexing file to gitignore --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index db471e42..e10dbe82 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,6 @@ node_modules/ # Dotenv file .env + + +*.DS_Store From 92aefd290bf19cfab7ab9202f39fb5a979cfc4c1 Mon Sep 17 00:00:00 2001 From: ChaoticWalrus <93558947+ChaoticWalrus@users.noreply.github.com> Date: Thu, 16 Nov 2023 11:09:30 -0800 Subject: [PATCH 004/101] chore: correct addresses for M2 Goerli deployment switch these addresses over from pre-prod environment to the addresses that EigenDA is using now --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index c8fa6bf0..2cb70571 100644 --- a/README.md +++ b/README.md @@ -90,8 +90,8 @@ The current implementation of this contract is the [BLSSignatureChecker](./docs/ | Name | Solidity | Contract | Notes | | ------------------------- | ----------------------------- | ------------------------------------------------------------------------------------------------- | ----- | -| BLSOperatorStateRetriever | BLSOperatorStateRetriever.sol | [`0xf60a...7251`](https://goerli.etherscan.io/address/0xf60a330F8c1A0C0ecf3Eab1D54b00F3a8FE37251) | | -| BLSPubkeyCompendium | BLSPubkeyCompendium.sol | [`0xdd53...B9Bb`](https://goerli.etherscan.io/address/0xdd53D44257d5F4EB0ca60F60d88827C1b433B9Bb) | | +| BLSOperatorStateRetriever | BLSOperatorStateRetriever.sol | [`0x737D...A3a3`](https://goerli.etherscan.io/address/0x737Dd62816a9392e84Fa21C531aF77C00816A3a3) | | +| BLSPubkeyCompendium | BLSPubkeyCompendium.sol | [`0xc81d...1b19`](https://goerli.etherscan.io/address/0xc81d3963087Fe09316cd1E032457989C7aC91b19) | | ## Further reading From 2eee7956acbb7e57267c00c96a6740880d1c33eb Mon Sep 17 00:00:00 2001 From: steven Date: Mon, 4 Dec 2023 12:03:30 -0500 Subject: [PATCH 005/101] chore: test out relative import --- src/VoteWeigherBase.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/VoteWeigherBase.sol b/src/VoteWeigherBase.sol index 0be3d60e..1ed48392 100644 --- a/src/VoteWeigherBase.sol +++ b/src/VoteWeigherBase.sol @@ -3,7 +3,7 @@ pragma solidity =0.8.12; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "eigenlayer-contracts/src/contracts/interfaces/IStrategyManager.sol"; -import "src/VoteWeigherBaseStorage.sol"; +import "./VoteWeigherBaseStorage.sol"; /** * @title A simple implementation of the `IVoteWeigher` interface. From e610c6a72eb62b037e98db3b2bcc2ff454b4d09a Mon Sep 17 00:00:00 2001 From: steven Date: Mon, 4 Dec 2023 12:32:30 -0500 Subject: [PATCH 006/101] chore: all relative imports --- remappings.txt | 6 ++- script/AVSContractsDeploy.s.sol | 2 +- src/BLSOperatorStateRetriever.sol | 10 ++--- src/BLSPubkeyRegistry.sol | 4 +- src/BLSPubkeyRegistryStorage.sol | 8 ++-- src/BLSPublicKeyCompendium.sol | 4 +- src/BLSRegistryCoordinatorWithIndices.sol | 22 +++++------ src/BLSSignatureChecker.sol | 14 +++---- src/IndexRegistry.sol | 2 +- src/IndexRegistryStorage.sol | 4 +- src/ServiceManagerBase.sol | 6 +-- src/StakeRegistry.sol | 12 +++--- src/StakeRegistryStorage.sol | 6 +-- src/VoteWeigherBaseStorage.sol | 4 +- src/interfaces/IBLSPubkeyRegistry.sol | 4 +- src/interfaces/IBLSPublicKeyCompendium.sol | 2 +- src/interfaces/IBLSRegistry.sol | 2 +- .../IBLSRegistryCoordinatorWithIndices.sol | 10 ++--- src/interfaces/IBLSSignatureChecker.sol | 10 ++--- test/ffi/BLSPubKeyCompendiumFFI.t.sol | 4 +- test/ffi/BLSSignatureCheckerFFI.t.sol | 8 ++-- test/ffi/util/G2Operations.sol | 2 +- ...SRegistryCoordinatorWithIndicesHarness.sol | 2 +- test/harnesses/BitmapUtilsWrapper.sol | 2 +- test/harnesses/StakeRegistryHarness.sol | 2 +- test/mocks/BLSPublicKeyCompendiumMock.sol | 4 +- test/mocks/MiddlewareRegistryMock.sol | 2 +- test/mocks/RegistryCoordinatorMock.sol | 2 +- test/mocks/ServiceManagerMock.sol | 7 +--- test/mocks/StakeRegistryMock.sol | 4 +- test/unit/BLSPubkeyRegistryUnit.t.sol | 4 +- test/unit/BLSPublicKeyCompendiumUnit.t.sol | 2 +- test/unit/BLSSignatureCheckerUnit.t.sol | 4 +- test/unit/IndexRegistryUnit.t.sol | 8 ++-- test/unit/StakeRegistryUnit.t.sol | 20 +++++----- test/unit/VoteWeigherBaseUnit.t.sol | 6 +-- test/utils/BLSMockAVSDeployer.sol | 10 ++--- test/utils/MockAVSDeployer.sol | 38 +++++++++---------- test/utils/Operators.sol | 2 +- test/utils/ProofParsing.sol | 2 +- 40 files changed, 132 insertions(+), 135 deletions(-) diff --git a/remappings.txt b/remappings.txt index 8c001fd0..a7cd70c1 100644 --- a/remappings.txt +++ b/remappings.txt @@ -1,5 +1,7 @@ -@openzeppelin-upgrades/=lib/openzeppelin-contracts-upgradeable/ -@openzeppelin/=lib/openzeppelin-contracts/ +@openzeppelin-upgrades/=lib/eigenlayer-contracts/lib/openzeppelin-contracts-upgradeable/ +@openzeppelin/=lib/eigenlayer-contracts/lib/openzeppelin-contracts/ ds-test/=lib/ds-test/src/ eigenlayer-contracts/=lib/eigenlayer-contracts/ forge-std/=lib/forge-std/src/ +openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/ +openzeppelin-contracts/=lib/openzeppelin-contracts/ diff --git a/script/AVSContractsDeploy.s.sol b/script/AVSContractsDeploy.s.sol index d3386dd3..0abe9c8b 100644 --- a/script/AVSContractsDeploy.s.sol +++ b/script/AVSContractsDeploy.s.sol @@ -19,7 +19,7 @@ import "eigenlayer-contracts/src/contracts/pods/EigenPodManager.sol"; import "eigenlayer-contracts/src/contracts/pods/DelayedWithdrawalRouter.sol"; import "eigenlayer-contracts/src/contracts/permissions/PauserRegistry.sol"; -import "src/BLSPublicKeyCompendium.sol"; +import "../src/BLSPublicKeyCompendium.sol"; import "eigenlayer-contracts/src/test/mocks/EmptyContract.sol"; import "eigenlayer-contracts/src/test/mocks/ETHDepositMock.sol"; diff --git a/src/BLSOperatorStateRetriever.sol b/src/BLSOperatorStateRetriever.sol index 7b891923..8051eba5 100644 --- a/src/BLSOperatorStateRetriever.sol +++ b/src/BLSOperatorStateRetriever.sol @@ -1,12 +1,12 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity =0.8.12; -import "src/interfaces/IStakeRegistry.sol"; -import "src/interfaces/IBLSPubkeyRegistry.sol"; -import "src/interfaces/IIndexRegistry.sol"; -import "src/interfaces/IBLSRegistryCoordinatorWithIndices.sol"; +import "./interfaces/IStakeRegistry.sol"; +import "./interfaces/IBLSPubkeyRegistry.sol"; +import "./interfaces/IIndexRegistry.sol"; +import "./interfaces/IBLSRegistryCoordinatorWithIndices.sol"; -import "src/libraries/BitmapUtils.sol"; +import "./libraries/BitmapUtils.sol"; /** * @title BLSOperatorStateRetriever with view functions that allow to retrieve the state of an AVSs registry system. diff --git a/src/BLSPubkeyRegistry.sol b/src/BLSPubkeyRegistry.sol index f98af12b..804a4c35 100644 --- a/src/BLSPubkeyRegistry.sol +++ b/src/BLSPubkeyRegistry.sol @@ -1,9 +1,9 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity =0.8.12; -import "src/libraries/BN254.sol"; +import "./libraries/BN254.sol"; -import "src/BLSPubkeyRegistryStorage.sol"; +import "./BLSPubkeyRegistryStorage.sol"; contract BLSPubkeyRegistry is BLSPubkeyRegistryStorage { using BN254 for BN254.G1Point; diff --git a/src/BLSPubkeyRegistryStorage.sol b/src/BLSPubkeyRegistryStorage.sol index 10e2fe51..21ecd3ca 100644 --- a/src/BLSPubkeyRegistryStorage.sol +++ b/src/BLSPubkeyRegistryStorage.sol @@ -1,11 +1,11 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity =0.8.12; -import "src/interfaces/IBLSPubkeyRegistry.sol"; -import "src/interfaces/IRegistryCoordinator.sol"; -import "src/interfaces/IBLSPublicKeyCompendium.sol"; +import "./interfaces/IBLSPubkeyRegistry.sol"; +import "./interfaces/IRegistryCoordinator.sol"; +import "./interfaces/IBLSPublicKeyCompendium.sol"; -import "src/libraries/BN254.sol"; +import "./libraries/BN254.sol"; import "@openzeppelin-upgrades/contracts/proxy/utils/Initializable.sol"; diff --git a/src/BLSPublicKeyCompendium.sol b/src/BLSPublicKeyCompendium.sol index 88d47163..bc7120d6 100644 --- a/src/BLSPublicKeyCompendium.sol +++ b/src/BLSPublicKeyCompendium.sol @@ -1,9 +1,9 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity =0.8.12; -import "src/interfaces/IBLSPublicKeyCompendium.sol"; +import "./interfaces/IBLSPublicKeyCompendium.sol"; -import "src/libraries/BN254.sol"; +import "./libraries/BN254.sol"; /** * @title A shared contract for EigenLayer operators to register their BLS public keys. diff --git a/src/BLSRegistryCoordinatorWithIndices.sol b/src/BLSRegistryCoordinatorWithIndices.sol index 7b49eadf..c8fa1497 100644 --- a/src/BLSRegistryCoordinatorWithIndices.sol +++ b/src/BLSRegistryCoordinatorWithIndices.sol @@ -9,17 +9,17 @@ import "eigenlayer-contracts/src/contracts/interfaces/ISlasher.sol"; import "eigenlayer-contracts/src/contracts/libraries/EIP1271SignatureUtils.sol"; import "eigenlayer-contracts/src/contracts/permissions/Pausable.sol"; -import "src/interfaces/IBLSRegistryCoordinatorWithIndices.sol"; -import "src/interfaces/ISocketUpdater.sol"; -import "src/interfaces/IServiceManager.sol"; -import "src/interfaces/IBLSPubkeyRegistry.sol"; -import "src/interfaces/IVoteWeigher.sol"; -import "src/interfaces/IStakeRegistry.sol"; -import "src/interfaces/IIndexRegistry.sol"; -import "src/interfaces/IRegistryCoordinator.sol"; - -import "src/libraries/BitmapUtils.sol"; -import "src/libraries/BN254.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/IRegistryCoordinator.sol"; + +import "./libraries/BitmapUtils.sol"; +import "./libraries/BN254.sol"; diff --git a/src/BLSSignatureChecker.sol b/src/BLSSignatureChecker.sol index ee80785e..511a7c5c 100644 --- a/src/BLSSignatureChecker.sol +++ b/src/BLSSignatureChecker.sol @@ -1,14 +1,14 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity =0.8.12; -import "src/interfaces/IRegistryCoordinator.sol"; -import "src/interfaces/IStakeRegistry.sol"; -import "src/interfaces/IBLSPubkeyRegistry.sol"; -import "src/interfaces/IBLSRegistryCoordinatorWithIndices.sol"; -import "src/interfaces/IBLSSignatureChecker.sol"; +import "./interfaces/IRegistryCoordinator.sol"; +import "./interfaces/IStakeRegistry.sol"; +import "./interfaces/IBLSPubkeyRegistry.sol"; +import "./interfaces/IBLSRegistryCoordinatorWithIndices.sol"; +import "./interfaces/IBLSSignatureChecker.sol"; -import "src/libraries/BitmapUtils.sol"; -import "src/libraries/BN254.sol"; +import "./libraries/BitmapUtils.sol"; +import "./libraries/BN254.sol"; /** * @title Used for checking BLS aggregate signatures from the operators of a `BLSRegistry`. diff --git a/src/IndexRegistry.sol b/src/IndexRegistry.sol index 3c814e98..58e824c6 100644 --- a/src/IndexRegistry.sol +++ b/src/IndexRegistry.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity =0.8.12; -import "src/IndexRegistryStorage.sol"; +import "./IndexRegistryStorage.sol"; /** * @title A `Registry` that keeps track of an ordered list of operators for each quorum diff --git a/src/IndexRegistryStorage.sol b/src/IndexRegistryStorage.sol index 250369f6..5d42b2df 100644 --- a/src/IndexRegistryStorage.sol +++ b/src/IndexRegistryStorage.sol @@ -1,8 +1,8 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity =0.8.12; -import "src/interfaces/IIndexRegistry.sol"; -import "src/interfaces/IRegistryCoordinator.sol"; +import "./interfaces/IIndexRegistry.sol"; +import "./interfaces/IRegistryCoordinator.sol"; import "@openzeppelin-upgrades/contracts/proxy/utils/Initializable.sol"; diff --git a/src/ServiceManagerBase.sol b/src/ServiceManagerBase.sol index ae300c43..325065d4 100644 --- a/src/ServiceManagerBase.sol +++ b/src/ServiceManagerBase.sol @@ -8,10 +8,10 @@ import "eigenlayer-contracts/src/contracts/interfaces/IPauserRegistry.sol"; import "eigenlayer-contracts/src/contracts/interfaces/ISlasher.sol"; import "eigenlayer-contracts/src/contracts/permissions/Pausable.sol"; -import "src/interfaces/IBLSRegistryCoordinatorWithIndices.sol"; -import "src/interfaces/IServiceManager.sol"; +import "./interfaces/IBLSRegistryCoordinatorWithIndices.sol"; +import "./interfaces/IServiceManager.sol"; -import "src/BLSSignatureChecker.sol"; +import "./BLSSignatureChecker.sol"; /** * @title Base implementation of `IServiceManager` interface, designed to be inherited from by more complex ServiceManagers. diff --git a/src/StakeRegistry.sol b/src/StakeRegistry.sol index 5d32852a..c1c2564f 100644 --- a/src/StakeRegistry.sol +++ b/src/StakeRegistry.sol @@ -1,14 +1,14 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity =0.8.12; -import "src/interfaces/IServiceManager.sol"; -import "src/interfaces/IStakeRegistry.sol"; -import "src/interfaces/IRegistryCoordinator.sol"; +import "./interfaces/IServiceManager.sol"; +import "./interfaces/IStakeRegistry.sol"; +import "./interfaces/IRegistryCoordinator.sol"; -import "src/libraries/BitmapUtils.sol"; +import "./libraries/BitmapUtils.sol"; -import "src/StakeRegistryStorage.sol"; -import {VoteWeigherBase} from "src/VoteWeigherBase.sol"; +import "./StakeRegistryStorage.sol"; +import {VoteWeigherBase} from "./VoteWeigherBase.sol"; /** * @title A `Registry` that keeps track of stakes of operators for up to 256 quorums. diff --git a/src/StakeRegistryStorage.sol b/src/StakeRegistryStorage.sol index 6459a659..c8a47adc 100644 --- a/src/StakeRegistryStorage.sol +++ b/src/StakeRegistryStorage.sol @@ -2,9 +2,9 @@ pragma solidity =0.8.12; import {IStrategyManager} from "eigenlayer-contracts/src/contracts/interfaces/IStrategyManager.sol"; -import {IServiceManager} from "src/interfaces/IServiceManager.sol"; -import {IStakeRegistry} from "src/interfaces/IStakeRegistry.sol"; -import {IRegistryCoordinator} from "src/interfaces/IRegistryCoordinator.sol"; +import {IServiceManager} from "./interfaces/IServiceManager.sol"; +import {IStakeRegistry} from "./interfaces/IStakeRegistry.sol"; +import {IRegistryCoordinator} from "./interfaces/IRegistryCoordinator.sol"; /** * @title Storage variables for the `StakeRegistry` contract. diff --git a/src/VoteWeigherBaseStorage.sol b/src/VoteWeigherBaseStorage.sol index e09dbb4a..81d4d243 100644 --- a/src/VoteWeigherBaseStorage.sol +++ b/src/VoteWeigherBaseStorage.sol @@ -3,8 +3,8 @@ pragma solidity =0.8.12; import "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; import "eigenlayer-contracts/src/contracts/interfaces/IStrategyManager.sol"; -import "src/interfaces/IServiceManager.sol"; -import "src/interfaces/IVoteWeigher.sol"; +import "./interfaces/IServiceManager.sol"; +import "./interfaces/IVoteWeigher.sol"; import "@openzeppelin-upgrades/contracts/proxy/utils/Initializable.sol"; diff --git a/src/interfaces/IBLSPubkeyRegistry.sol b/src/interfaces/IBLSPubkeyRegistry.sol index f0fa5ec2..e2842f8e 100644 --- a/src/interfaces/IBLSPubkeyRegistry.sol +++ b/src/interfaces/IBLSPubkeyRegistry.sol @@ -1,8 +1,8 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity =0.8.12; -import {IRegistry} from "src/interfaces/IRegistry.sol"; -import {BN254} from "src/libraries/BN254.sol"; +import {IRegistry} from "./IRegistry.sol"; +import {BN254} from "../libraries/BN254.sol"; /** * @title Minimal interface for a registry that keeps track of aggregate operator public keys for among many quorums. diff --git a/src/interfaces/IBLSPublicKeyCompendium.sol b/src/interfaces/IBLSPublicKeyCompendium.sol index 3a6d6ff6..7d4ba58a 100644 --- a/src/interfaces/IBLSPublicKeyCompendium.sol +++ b/src/interfaces/IBLSPublicKeyCompendium.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity >=0.5.0; -import {BN254} from "src/libraries/BN254.sol"; +import {BN254} from "../libraries/BN254.sol"; /** * @title Minimal interface for the `BLSPublicKeyCompendium` contract. diff --git a/src/interfaces/IBLSRegistry.sol b/src/interfaces/IBLSRegistry.sol index 8cecc073..d9122263 100644 --- a/src/interfaces/IBLSRegistry.sol +++ b/src/interfaces/IBLSRegistry.sol @@ -2,7 +2,7 @@ pragma solidity >=0.5.0; import {IQuorumRegistry} from "./IQuorumRegistry.sol"; -import {BN254} from "src/libraries/BN254.sol"; +import {BN254} from "../libraries/BN254.sol"; /** diff --git a/src/interfaces/IBLSRegistryCoordinatorWithIndices.sol b/src/interfaces/IBLSRegistryCoordinatorWithIndices.sol index f8ad8452..81d71ff8 100644 --- a/src/interfaces/IBLSRegistryCoordinatorWithIndices.sol +++ b/src/interfaces/IBLSRegistryCoordinatorWithIndices.sol @@ -2,11 +2,11 @@ pragma solidity =0.8.12; import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol"; -import {IRegistryCoordinator} from "src/interfaces/IRegistryCoordinator.sol"; -import {IStakeRegistry} from "src/interfaces/IStakeRegistry.sol"; -import {IBLSPubkeyRegistry} from "src/interfaces/IBLSPubkeyRegistry.sol"; -import {IIndexRegistry} from "src/interfaces/IIndexRegistry.sol"; -import {BN254} from "src/libraries/BN254.sol"; +import {IRegistryCoordinator} from "./IRegistryCoordinator.sol"; +import {IStakeRegistry} from "./IStakeRegistry.sol"; +import {IBLSPubkeyRegistry} from "./IBLSPubkeyRegistry.sol"; +import {IIndexRegistry} from "./IIndexRegistry.sol"; +import {BN254} from "../libraries/BN254.sol"; /** * @title Minimal interface for the `IBLSStakeRegistryCoordinator` contract. diff --git a/src/interfaces/IBLSSignatureChecker.sol b/src/interfaces/IBLSSignatureChecker.sol index bf098acc..e2759f95 100644 --- a/src/interfaces/IBLSSignatureChecker.sol +++ b/src/interfaces/IBLSSignatureChecker.sol @@ -1,11 +1,11 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity =0.8.12; -import {IBLSRegistryCoordinatorWithIndices} from "src/interfaces/IBLSRegistryCoordinatorWithIndices.sol"; -import {IBLSPubkeyRegistry} from "src/interfaces/IBLSPubkeyRegistry.sol"; -import {IRegistryCoordinator} from "src/interfaces/IRegistryCoordinator.sol"; -import {IStakeRegistry} from "src/interfaces/IStakeRegistry.sol"; -import {BN254} from "src/libraries/BN254.sol"; +import {IBLSRegistryCoordinatorWithIndices} from "./IBLSRegistryCoordinatorWithIndices.sol"; +import {IBLSPubkeyRegistry} from "./IBLSPubkeyRegistry.sol"; +import {IRegistryCoordinator} from "./IRegistryCoordinator.sol"; +import {IStakeRegistry} from "./IStakeRegistry.sol"; +import {BN254} from "../libraries/BN254.sol"; /** * @title Used for checking BLS aggregate signatures from the operators of a EigenLayer AVS with the RegistryCoordinator/BLSPubkeyRegistry/StakeRegistry architechture. diff --git a/test/ffi/BLSPubKeyCompendiumFFI.t.sol b/test/ffi/BLSPubKeyCompendiumFFI.t.sol index 64e14ed6..501ba3bf 100644 --- a/test/ffi/BLSPubKeyCompendiumFFI.t.sol +++ b/test/ffi/BLSPubKeyCompendiumFFI.t.sol @@ -1,8 +1,8 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity =0.8.12; -import "src/BLSPublicKeyCompendium.sol"; -import "test/ffi/util/G2Operations.sol"; +import "../../src/BLSPublicKeyCompendium.sol"; +import "./util/G2Operations.sol"; contract BLSPublicKeyCompendiumFFITests is G2Operations { using BN254 for BN254.G1Point; diff --git a/test/ffi/BLSSignatureCheckerFFI.t.sol b/test/ffi/BLSSignatureCheckerFFI.t.sol index dce7b281..9eb265d3 100644 --- a/test/ffi/BLSSignatureCheckerFFI.t.sol +++ b/test/ffi/BLSSignatureCheckerFFI.t.sol @@ -3,10 +3,10 @@ pragma solidity =0.8.12; import {G2Operations} from "test/ffi/util/G2Operations.sol"; import {MockAVSDeployer} from "test/utils/MockAVSDeployer.sol"; -import {BLSSignatureChecker} from "src/BLSSignatureChecker.sol"; -import {BLSOperatorStateRetriever} from "src/BLSOperatorStateRetriever.sol"; -import {BN254} from "src/libraries/BN254.sol"; -import {BitmapUtils} from "src/libraries/BitmapUtils.sol"; +import {BLSSignatureChecker} from "../../src/BLSSignatureChecker.sol"; +import {BLSOperatorStateRetriever} from "../../src/BLSOperatorStateRetriever.sol"; +import {BN254} from "../../src/libraries/BN254.sol"; +import {BitmapUtils} from "../../src/libraries/BitmapUtils.sol"; contract BLSSignatureCheckerFFITests is MockAVSDeployer, G2Operations { diff --git a/test/ffi/util/G2Operations.sol b/test/ffi/util/G2Operations.sol index 488c30b2..d94b61d9 100644 --- a/test/ffi/util/G2Operations.sol +++ b/test/ffi/util/G2Operations.sol @@ -3,7 +3,7 @@ pragma solidity =0.8.12; import "forge-std/Test.sol"; import "openzeppelin-contracts/contracts/utils/Strings.sol"; -import "src/libraries/BN254.sol"; +import "../../../src/libraries/BN254.sol"; contract G2Operations is Test { using Strings for uint256; diff --git a/test/harnesses/BLSRegistryCoordinatorWithIndicesHarness.sol b/test/harnesses/BLSRegistryCoordinatorWithIndicesHarness.sol index c4105dae..6045bf08 100644 --- a/test/harnesses/BLSRegistryCoordinatorWithIndicesHarness.sol +++ b/test/harnesses/BLSRegistryCoordinatorWithIndicesHarness.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity =0.8.12; -import "src/BLSRegistryCoordinatorWithIndices.sol"; +import "../../src/BLSRegistryCoordinatorWithIndices.sol"; // wrapper around the BLSRegistryCoordinatorWithIndices contract that exposes the internal functions for unit testing. contract BLSRegistryCoordinatorWithIndicesHarness is BLSRegistryCoordinatorWithIndices { diff --git a/test/harnesses/BitmapUtilsWrapper.sol b/test/harnesses/BitmapUtilsWrapper.sol index 3f4e6d8c..954d1e74 100644 --- a/test/harnesses/BitmapUtilsWrapper.sol +++ b/test/harnesses/BitmapUtilsWrapper.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity =0.8.12; -import "src/libraries/BitmapUtils.sol"; +import "../../../src/libraries/BitmapUtils.sol"; // wrapper around the BitmapUtils library that exposes the internal functions contract BitmapUtilsWrapper { diff --git a/test/harnesses/StakeRegistryHarness.sol b/test/harnesses/StakeRegistryHarness.sol index 4cc8be9d..64036d55 100644 --- a/test/harnesses/StakeRegistryHarness.sol +++ b/test/harnesses/StakeRegistryHarness.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity =0.8.12; -import "src/StakeRegistry.sol"; +import "../../../src/StakeRegistry.sol"; // wrapper around the StakeRegistry contract that exposes the internal functions for unit testing. contract StakeRegistryHarness is StakeRegistry { diff --git a/test/mocks/BLSPublicKeyCompendiumMock.sol b/test/mocks/BLSPublicKeyCompendiumMock.sol index ee79d3fe..63e3088d 100644 --- a/test/mocks/BLSPublicKeyCompendiumMock.sol +++ b/test/mocks/BLSPublicKeyCompendiumMock.sol @@ -1,8 +1,8 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity =0.8.12; -import "src/interfaces/IBLSPublicKeyCompendium.sol"; -import "src/libraries/BN254.sol"; +import "../../src/interfaces/IBLSPublicKeyCompendium.sol"; +import "../../src/libraries/BN254.sol"; /** * @title A shared contract for EigenLayer operators to register their BLS public keys. * @author Layr Labs, Inc. diff --git a/test/mocks/MiddlewareRegistryMock.sol b/test/mocks/MiddlewareRegistryMock.sol index be13d43a..d848c802 100644 --- a/test/mocks/MiddlewareRegistryMock.sol +++ b/test/mocks/MiddlewareRegistryMock.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity =0.8.12; -import "src/interfaces/IServiceManager.sol"; +import "../../src/interfaces/IServiceManager.sol"; import "eigenlayer-contracts/src/contracts/interfaces/IStrategyManager.sol"; import "eigenlayer-contracts/src/contracts/interfaces/ISlasher.sol"; diff --git a/test/mocks/RegistryCoordinatorMock.sol b/test/mocks/RegistryCoordinatorMock.sol index 92322c62..d959d7d1 100644 --- a/test/mocks/RegistryCoordinatorMock.sol +++ b/test/mocks/RegistryCoordinatorMock.sol @@ -2,7 +2,7 @@ pragma solidity =0.8.12; -import "src/interfaces/IRegistryCoordinator.sol"; +import "../../src/interfaces/IRegistryCoordinator.sol"; contract RegistryCoordinatorMock is IRegistryCoordinator { diff --git a/test/mocks/ServiceManagerMock.sol b/test/mocks/ServiceManagerMock.sol index 7d2b874a..55b1f160 100644 --- a/test/mocks/ServiceManagerMock.sol +++ b/test/mocks/ServiceManagerMock.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity =0.8.12; -import "src/interfaces/IServiceManager.sol"; +import "../../src/interfaces/IServiceManager.sol"; import "eigenlayer-contracts/src/contracts/interfaces/ISlasher.sol"; contract ServiceManagerMock is IServiceManager{ @@ -19,11 +19,6 @@ contract ServiceManagerMock is IServiceManager{ slasher.freezeOperator(operator); } - /// @notice Token used for placing guarantee on challenges & payment commits - function paymentChallengeToken() external pure returns (IERC20) { - return IERC20(address(0)); - } - /// @notice Returns the `latestServeUntilBlock` until which operators must serve. function latestServeUntilBlock() external pure returns (uint32) { return type(uint32).max; diff --git a/test/mocks/StakeRegistryMock.sol b/test/mocks/StakeRegistryMock.sol index 8b6e932f..0819228b 100644 --- a/test/mocks/StakeRegistryMock.sol +++ b/test/mocks/StakeRegistryMock.sol @@ -1,8 +1,8 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity =0.8.12; -import "src/interfaces/IStakeRegistry.sol"; -import "src/interfaces/IRegistryCoordinator.sol"; +import "../../src/interfaces/IStakeRegistry.sol"; +import "../../src/interfaces/IRegistryCoordinator.sol"; /** * @title Interface for a `Registry` that keeps track of stakes of operators for up to 256 quorums. diff --git a/test/unit/BLSPubkeyRegistryUnit.t.sol b/test/unit/BLSPubkeyRegistryUnit.t.sol index 99247971..4b1720a9 100644 --- a/test/unit/BLSPubkeyRegistryUnit.t.sol +++ b/test/unit/BLSPubkeyRegistryUnit.t.sol @@ -3,8 +3,8 @@ pragma solidity =0.8.12; import "forge-std/Test.sol"; -import "src/BLSPubkeyRegistry.sol"; -import "src/interfaces/IRegistryCoordinator.sol"; +import "../../src/BLSPubkeyRegistry.sol"; +import "../../src/interfaces/IRegistryCoordinator.sol"; import "test/mocks/BLSPublicKeyCompendiumMock.sol"; import "test/mocks/RegistryCoordinatorMock.sol"; diff --git a/test/unit/BLSPublicKeyCompendiumUnit.t.sol b/test/unit/BLSPublicKeyCompendiumUnit.t.sol index 31ba0a95..a9181132 100644 --- a/test/unit/BLSPublicKeyCompendiumUnit.t.sol +++ b/test/unit/BLSPublicKeyCompendiumUnit.t.sol @@ -2,7 +2,7 @@ pragma solidity =0.8.12; import "forge-std/Test.sol"; -import "src/BLSPublicKeyCompendium.sol"; +import "../../src/BLSPublicKeyCompendium.sol"; contract BLSPublicKeyCompendiumUnitTests is Test { using BN254 for BN254.G1Point; diff --git a/test/unit/BLSSignatureCheckerUnit.t.sol b/test/unit/BLSSignatureCheckerUnit.t.sol index ecbefbb5..d61f847f 100644 --- a/test/unit/BLSSignatureCheckerUnit.t.sol +++ b/test/unit/BLSSignatureCheckerUnit.t.sol @@ -1,8 +1,8 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity =0.8.12; -import "src/BLSSignatureChecker.sol"; -import "test/utils/BLSMockAVSDeployer.sol"; +import "../../src/BLSSignatureChecker.sol"; +import "../utils/BLSMockAVSDeployer.sol"; contract BLSSignatureCheckerUnitTests is BLSMockAVSDeployer { using BN254 for BN254.G1Point; diff --git a/test/unit/IndexRegistryUnit.t.sol b/test/unit/IndexRegistryUnit.t.sol index ba63a11e..4f718e9a 100644 --- a/test/unit/IndexRegistryUnit.t.sol +++ b/test/unit/IndexRegistryUnit.t.sol @@ -1,10 +1,10 @@ //SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.12; -import "src/interfaces/IIndexRegistry.sol"; -import "src/IndexRegistry.sol"; -import "test/mocks/RegistryCoordinatorMock.sol"; -import "test/harnesses/BitmapUtilsWrapper.sol"; +import "../../src/interfaces/IIndexRegistry.sol"; +import "../../src/IndexRegistry.sol"; +import "../mocks/RegistryCoordinatorMock.sol"; +import "../harnesses/BitmapUtilsWrapper.sol"; import "forge-std/Test.sol"; diff --git a/test/unit/StakeRegistryUnit.t.sol b/test/unit/StakeRegistryUnit.t.sol index 91e7e3fb..f0e46b3d 100644 --- a/test/unit/StakeRegistryUnit.t.sol +++ b/test/unit/StakeRegistryUnit.t.sol @@ -10,24 +10,24 @@ import {PauserRegistry} from "eigenlayer-contracts/src/contracts/permissions/Pau import {IStrategyManager} from "eigenlayer-contracts/src/contracts/interfaces/IStrategyManager.sol"; import {ISlasher} from "eigenlayer-contracts/src/contracts/interfaces/ISlasher.sol"; import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; -import {IStakeRegistry} from "src/interfaces/IStakeRegistry.sol"; -import {IServiceManager} from "src/interfaces/IServiceManager.sol"; -import {IVoteWeigher} from "src/interfaces/IVoteWeigher.sol"; -import {IIndexRegistry} from "src/interfaces/IIndexRegistry.sol"; -import {IRegistryCoordinator} from "src/interfaces/IRegistryCoordinator.sol"; -import {IBLSPubkeyRegistry} from "src/interfaces/IBLSPubkeyRegistry.sol"; +import {IStakeRegistry} from "../../src/interfaces/IStakeRegistry.sol"; +import {IServiceManager} from "../../src/interfaces/IServiceManager.sol"; +import {IVoteWeigher} from "../../src/interfaces/IVoteWeigher.sol"; +import {IIndexRegistry} from "../../src/interfaces/IIndexRegistry.sol"; +import {IRegistryCoordinator} from "../../src/interfaces/IRegistryCoordinator.sol"; +import {IBLSPubkeyRegistry} from "../../src/interfaces/IBLSPubkeyRegistry.sol"; -import {BitmapUtils} from "src/libraries/BitmapUtils.sol"; +import {BitmapUtils} from "../../src/libraries/BitmapUtils.sol"; import {StrategyManagerMock} from "eigenlayer-contracts/src/test/mocks/StrategyManagerMock.sol"; import {EigenPodManagerMock} from "eigenlayer-contracts/src/test/mocks/EigenPodManagerMock.sol"; -import {ServiceManagerMock} from "test/mocks/ServiceManagerMock.sol"; +import {ServiceManagerMock} from "../mocks/ServiceManagerMock.sol"; import {OwnableMock} from "eigenlayer-contracts/src/test/mocks/OwnableMock.sol"; import {DelegationManagerMock} from "eigenlayer-contracts/src/test/mocks/DelegationManagerMock.sol"; import {SlasherMock} from "eigenlayer-contracts/src/test/mocks/SlasherMock.sol"; -import {StakeRegistryHarness} from "test/harnesses/StakeRegistryHarness.sol"; -import {StakeRegistry} from "src/StakeRegistry.sol"; +import {StakeRegistryHarness} from "../harnesses/StakeRegistryHarness.sol"; +import {StakeRegistry} from "../../src/StakeRegistry.sol"; import {BLSRegistryCoordinatorWithIndicesHarness} from "test/harnesses/BLSRegistryCoordinatorWithIndicesHarness.sol"; import "forge-std/Test.sol"; diff --git a/test/unit/VoteWeigherBaseUnit.t.sol b/test/unit/VoteWeigherBaseUnit.t.sol index 0ea8cd3b..3894da78 100644 --- a/test/unit/VoteWeigherBaseUnit.t.sol +++ b/test/unit/VoteWeigherBaseUnit.t.sol @@ -9,9 +9,9 @@ import {IStrategyManager} from "eigenlayer-contracts/src/contracts/interfaces/IS import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; import {IEigenPodManager} from "eigenlayer-contracts/src/contracts/interfaces/IEigenPodManager.sol"; import {ISlasher} from "eigenlayer-contracts/src/contracts/interfaces/ISlasher.sol"; -import {IServiceManager} from "src/interfaces/IServiceManager.sol"; -import {IVoteWeigher} from "src/interfaces/IVoteWeigher.sol"; -import {VoteWeigherBase} from "src/VoteWeigherBase.sol"; +import {IServiceManager} from "../../src/interfaces/IServiceManager.sol"; +import {IVoteWeigher} from "../../src/interfaces/IVoteWeigher.sol"; +import {VoteWeigherBase} from "../../src/VoteWeigherBase.sol"; import {StrategyManagerMock} from "eigenlayer-contracts/src/test/mocks/StrategyManagerMock.sol"; import {OwnableMock} from "eigenlayer-contracts/src/test/mocks/OwnableMock.sol"; diff --git a/test/utils/BLSMockAVSDeployer.sol b/test/utils/BLSMockAVSDeployer.sol index 5c935814..0d1b9a02 100644 --- a/test/utils/BLSMockAVSDeployer.sol +++ b/test/utils/BLSMockAVSDeployer.sol @@ -1,11 +1,11 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity =0.8.12; -import {BLSSignatureChecker} from "src/BLSSignatureChecker.sol"; -import {MockAVSDeployer} from "test/utils/MockAVSDeployer.sol"; -import {BN254} from "src/libraries/BN254.sol"; -import {BLSOperatorStateRetriever} from "src/BLSOperatorStateRetriever.sol"; -import {BitmapUtils} from "src/libraries/BitmapUtils.sol"; +import {BLSSignatureChecker} from "../../src/BLSSignatureChecker.sol"; +import {MockAVSDeployer} from "./MockAVSDeployer.sol"; +import {BN254} from "../../src/libraries/BN254.sol"; +import {BLSOperatorStateRetriever} from "../../../src/BLSOperatorStateRetriever.sol"; +import {BitmapUtils} from "../../../src/libraries/BitmapUtils.sol"; contract BLSMockAVSDeployer is MockAVSDeployer { using BN254 for BN254.G1Point; diff --git a/test/utils/MockAVSDeployer.sol b/test/utils/MockAVSDeployer.sol index 7f3981d7..f8c8473b 100644 --- a/test/utils/MockAVSDeployer.sol +++ b/test/utils/MockAVSDeployer.sol @@ -10,36 +10,36 @@ import {PauserRegistry} from "eigenlayer-contracts/src/contracts/permissions/Pau import {IStrategyManager} from "eigenlayer-contracts/src/contracts/interfaces/IStrategyManager.sol"; import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol"; -import {BitmapUtils} from "src/libraries/BitmapUtils.sol"; -import {BN254} from "src/libraries/BN254.sol"; +import {BitmapUtils} from "../../../src/libraries/BitmapUtils.sol"; +import {BN254} from "../../../src/libraries/BN254.sol"; -import {BLSPublicKeyCompendium} from "src/BLSPublicKeyCompendium.sol"; -import {BLSOperatorStateRetriever} from "src/BLSOperatorStateRetriever.sol"; -import {BLSRegistryCoordinatorWithIndices} from "src/BLSRegistryCoordinatorWithIndices.sol"; +import {BLSPublicKeyCompendium} from "../../../src/BLSPublicKeyCompendium.sol"; +import {BLSOperatorStateRetriever} from "../../../src/BLSOperatorStateRetriever.sol"; +import {BLSRegistryCoordinatorWithIndices} from "../../../src/BLSRegistryCoordinatorWithIndices.sol"; import {BLSRegistryCoordinatorWithIndicesHarness} from "test/harnesses/BLSRegistryCoordinatorWithIndicesHarness.sol"; -import {BLSPubkeyRegistry} from "src/BLSPubkeyRegistry.sol"; -import {StakeRegistry} from "src/StakeRegistry.sol"; -import {IndexRegistry} from "src/IndexRegistry.sol"; -import {IServiceManager} from "src/interfaces/IServiceManager.sol"; -import {IBLSPubkeyRegistry} from "src/interfaces/IBLSPubkeyRegistry.sol"; -import {IRegistryCoordinator} from "src/interfaces/IRegistryCoordinator.sol"; -import {IVoteWeigher} from "src/interfaces/IVoteWeigher.sol"; -import {IStakeRegistry} from "src/interfaces/IStakeRegistry.sol"; -import {IIndexRegistry} from "src/interfaces/IIndexRegistry.sol"; -import {IBLSRegistryCoordinatorWithIndices} from "src/interfaces/IBLSRegistryCoordinatorWithIndices.sol"; +import {BLSPubkeyRegistry} from "../../../src/BLSPubkeyRegistry.sol"; +import {StakeRegistry} from "../../../src/StakeRegistry.sol"; +import {IndexRegistry} from "../../../src/IndexRegistry.sol"; +import {IServiceManager} from "../../../src/interfaces/IServiceManager.sol"; +import {IBLSPubkeyRegistry} from "../../../src/interfaces/IBLSPubkeyRegistry.sol"; +import {IRegistryCoordinator} from "../../../src/interfaces/IRegistryCoordinator.sol"; +import {IVoteWeigher} from "../../../src/interfaces/IVoteWeigher.sol"; +import {IStakeRegistry} from "../../../src/interfaces/IStakeRegistry.sol"; +import {IIndexRegistry} from "../../../src/interfaces/IIndexRegistry.sol"; +import {IBLSRegistryCoordinatorWithIndices} from "../../../src/interfaces/IBLSRegistryCoordinatorWithIndices.sol"; import {StrategyManagerMock} from "eigenlayer-contracts/src/test/mocks/StrategyManagerMock.sol"; import {EigenPodManagerMock} from "eigenlayer-contracts/src/test/mocks/EigenPodManagerMock.sol"; -import {ServiceManagerMock} from "test/mocks/ServiceManagerMock.sol"; +import {ServiceManagerMock} from "../mocks/ServiceManagerMock.sol"; import {OwnableMock} from "eigenlayer-contracts/src/test/mocks/OwnableMock.sol"; import {DelegationManagerMock} from "eigenlayer-contracts/src/test/mocks/DelegationManagerMock.sol"; import {SlasherMock} from "eigenlayer-contracts/src/test/mocks/SlasherMock.sol"; -import {BLSPublicKeyCompendiumMock} from "test/mocks/BLSPublicKeyCompendiumMock.sol"; +import {BLSPublicKeyCompendiumMock} from "../mocks/BLSPublicKeyCompendiumMock.sol"; import {EmptyContract} from "eigenlayer-contracts/src/test/mocks/EmptyContract.sol"; -import {StakeRegistryHarness} from "test/harnesses/StakeRegistryHarness.sol"; -import {BLSRegistryCoordinatorWithIndices} from "test/harnesses/BLSRegistryCoordinatorWithIndicesHarness.sol"; +import {StakeRegistryHarness} from "../harnesses/StakeRegistryHarness.sol"; +import {BLSRegistryCoordinatorWithIndices} from "../harnesses/BLSRegistryCoordinatorWithIndicesHarness.sol"; import "forge-std/Test.sol"; diff --git a/test/utils/Operators.sol b/test/utils/Operators.sol index 910165e7..2ed0e31e 100644 --- a/test/utils/Operators.sol +++ b/test/utils/Operators.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity =0.8.12; -import "src/libraries/BN254.sol"; +import "../../../src/libraries/BN254.sol"; import "forge-std/Test.sol"; import "forge-std/StdJson.sol"; diff --git a/test/utils/ProofParsing.sol b/test/utils/ProofParsing.sol index f73f74b8..6b249df0 100644 --- a/test/utils/ProofParsing.sol +++ b/test/utils/ProofParsing.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity =0.8.12; -import "src/libraries/BN254.sol"; +import "../../../src/libraries/BN254.sol"; import "forge-std/Test.sol"; import "forge-std/StdJson.sol"; From f8a0cf8f6d41a33ea7ebb931219da17f82ebdb6b Mon Sep 17 00:00:00 2001 From: steven Date: Mon, 4 Dec 2023 12:41:58 -0500 Subject: [PATCH 007/101] chore: fix remaining relative imports --- test/harnesses/BitmapUtilsWrapper.sol | 2 +- test/harnesses/StakeRegistryHarness.sol | 2 +- test/mocks/ServiceManagerMock.sol | 4 ++++ test/utils/BLSMockAVSDeployer.sol | 4 ++-- test/utils/MockAVSDeployer.sol | 30 ++++++++++++------------- test/utils/Operators.sol | 2 +- test/utils/ProofParsing.sol | 2 +- 7 files changed, 25 insertions(+), 21 deletions(-) diff --git a/test/harnesses/BitmapUtilsWrapper.sol b/test/harnesses/BitmapUtilsWrapper.sol index 954d1e74..68d2ab38 100644 --- a/test/harnesses/BitmapUtilsWrapper.sol +++ b/test/harnesses/BitmapUtilsWrapper.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity =0.8.12; -import "../../../src/libraries/BitmapUtils.sol"; +import "../../src/libraries/BitmapUtils.sol"; // wrapper around the BitmapUtils library that exposes the internal functions contract BitmapUtilsWrapper { diff --git a/test/harnesses/StakeRegistryHarness.sol b/test/harnesses/StakeRegistryHarness.sol index 64036d55..8bb8d07b 100644 --- a/test/harnesses/StakeRegistryHarness.sol +++ b/test/harnesses/StakeRegistryHarness.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity =0.8.12; -import "../../../src/StakeRegistry.sol"; +import "../../src/StakeRegistry.sol"; // wrapper around the StakeRegistry contract that exposes the internal functions for unit testing. contract StakeRegistryHarness is StakeRegistry { diff --git a/test/mocks/ServiceManagerMock.sol b/test/mocks/ServiceManagerMock.sol index 55b1f160..2f87e8f0 100644 --- a/test/mocks/ServiceManagerMock.sol +++ b/test/mocks/ServiceManagerMock.sol @@ -19,6 +19,10 @@ contract ServiceManagerMock is IServiceManager{ slasher.freezeOperator(operator); } + function paymentChallengeToken() external pure returns (IERC20) { + return IERC20(address(0)); + } + /// @notice Returns the `latestServeUntilBlock` until which operators must serve. function latestServeUntilBlock() external pure returns (uint32) { return type(uint32).max; diff --git a/test/utils/BLSMockAVSDeployer.sol b/test/utils/BLSMockAVSDeployer.sol index 0d1b9a02..0f4c5d27 100644 --- a/test/utils/BLSMockAVSDeployer.sol +++ b/test/utils/BLSMockAVSDeployer.sol @@ -4,8 +4,8 @@ pragma solidity =0.8.12; import {BLSSignatureChecker} from "../../src/BLSSignatureChecker.sol"; import {MockAVSDeployer} from "./MockAVSDeployer.sol"; import {BN254} from "../../src/libraries/BN254.sol"; -import {BLSOperatorStateRetriever} from "../../../src/BLSOperatorStateRetriever.sol"; -import {BitmapUtils} from "../../../src/libraries/BitmapUtils.sol"; +import {BLSOperatorStateRetriever} from "../../src/BLSOperatorStateRetriever.sol"; +import {BitmapUtils} from "../../src/libraries/BitmapUtils.sol"; contract BLSMockAVSDeployer is MockAVSDeployer { using BN254 for BN254.G1Point; diff --git a/test/utils/MockAVSDeployer.sol b/test/utils/MockAVSDeployer.sol index f8c8473b..f27a4f5a 100644 --- a/test/utils/MockAVSDeployer.sol +++ b/test/utils/MockAVSDeployer.sol @@ -10,23 +10,23 @@ import {PauserRegistry} from "eigenlayer-contracts/src/contracts/permissions/Pau import {IStrategyManager} from "eigenlayer-contracts/src/contracts/interfaces/IStrategyManager.sol"; import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol"; -import {BitmapUtils} from "../../../src/libraries/BitmapUtils.sol"; -import {BN254} from "../../../src/libraries/BN254.sol"; +import {BitmapUtils} from "../../src/libraries/BitmapUtils.sol"; +import {BN254} from "../../src/libraries/BN254.sol"; -import {BLSPublicKeyCompendium} from "../../../src/BLSPublicKeyCompendium.sol"; -import {BLSOperatorStateRetriever} from "../../../src/BLSOperatorStateRetriever.sol"; -import {BLSRegistryCoordinatorWithIndices} from "../../../src/BLSRegistryCoordinatorWithIndices.sol"; +import {BLSPublicKeyCompendium} from "../../src/BLSPublicKeyCompendium.sol"; +import {BLSOperatorStateRetriever} from "../../src/BLSOperatorStateRetriever.sol"; +import {BLSRegistryCoordinatorWithIndices} from "../../src/BLSRegistryCoordinatorWithIndices.sol"; import {BLSRegistryCoordinatorWithIndicesHarness} from "test/harnesses/BLSRegistryCoordinatorWithIndicesHarness.sol"; -import {BLSPubkeyRegistry} from "../../../src/BLSPubkeyRegistry.sol"; -import {StakeRegistry} from "../../../src/StakeRegistry.sol"; -import {IndexRegistry} from "../../../src/IndexRegistry.sol"; -import {IServiceManager} from "../../../src/interfaces/IServiceManager.sol"; -import {IBLSPubkeyRegistry} from "../../../src/interfaces/IBLSPubkeyRegistry.sol"; -import {IRegistryCoordinator} from "../../../src/interfaces/IRegistryCoordinator.sol"; -import {IVoteWeigher} from "../../../src/interfaces/IVoteWeigher.sol"; -import {IStakeRegistry} from "../../../src/interfaces/IStakeRegistry.sol"; -import {IIndexRegistry} from "../../../src/interfaces/IIndexRegistry.sol"; -import {IBLSRegistryCoordinatorWithIndices} from "../../../src/interfaces/IBLSRegistryCoordinatorWithIndices.sol"; +import {BLSPubkeyRegistry} from "../../src/BLSPubkeyRegistry.sol"; +import {StakeRegistry} from "../../src/StakeRegistry.sol"; +import {IndexRegistry} from "../../src/IndexRegistry.sol"; +import {IServiceManager} from "../../src/interfaces/IServiceManager.sol"; +import {IBLSPubkeyRegistry} from "../../src/interfaces/IBLSPubkeyRegistry.sol"; +import {IRegistryCoordinator} from "../../src/interfaces/IRegistryCoordinator.sol"; +import {IVoteWeigher} from "../../src/interfaces/IVoteWeigher.sol"; +import {IStakeRegistry} from "../../src/interfaces/IStakeRegistry.sol"; +import {IIndexRegistry} from "../../src/interfaces/IIndexRegistry.sol"; +import {IBLSRegistryCoordinatorWithIndices} from "../../src/interfaces/IBLSRegistryCoordinatorWithIndices.sol"; import {StrategyManagerMock} from "eigenlayer-contracts/src/test/mocks/StrategyManagerMock.sol"; diff --git a/test/utils/Operators.sol b/test/utils/Operators.sol index 2ed0e31e..7414cdf7 100644 --- a/test/utils/Operators.sol +++ b/test/utils/Operators.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity =0.8.12; -import "../../../src/libraries/BN254.sol"; +import "../../src/libraries/BN254.sol"; import "forge-std/Test.sol"; import "forge-std/StdJson.sol"; diff --git a/test/utils/ProofParsing.sol b/test/utils/ProofParsing.sol index 6b249df0..a61fb127 100644 --- a/test/utils/ProofParsing.sol +++ b/test/utils/ProofParsing.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity =0.8.12; -import "../../../src/libraries/BN254.sol"; +import "../../src/libraries/BN254.sol"; import "forge-std/Test.sol"; import "forge-std/StdJson.sol"; From 889bfad0893b3caa1fb4a26c7e5f752f26892024 Mon Sep 17 00:00:00 2001 From: steven Date: Mon, 4 Dec 2023 13:17:19 -0500 Subject: [PATCH 008/101] fix: update missed import --- test/unit/BLSPubkeyRegistryUnit.t.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/unit/BLSPubkeyRegistryUnit.t.sol b/test/unit/BLSPubkeyRegistryUnit.t.sol index 4b1720a9..b241aa40 100644 --- a/test/unit/BLSPubkeyRegistryUnit.t.sol +++ b/test/unit/BLSPubkeyRegistryUnit.t.sol @@ -5,8 +5,8 @@ pragma solidity =0.8.12; import "forge-std/Test.sol"; import "../../src/BLSPubkeyRegistry.sol"; import "../../src/interfaces/IRegistryCoordinator.sol"; -import "test/mocks/BLSPublicKeyCompendiumMock.sol"; -import "test/mocks/RegistryCoordinatorMock.sol"; +import "../mocks/BLSPublicKeyCompendiumMock.sol"; +import "../mocks/RegistryCoordinatorMock.sol"; contract BLSPubkeyRegistryUnitTests is Test { From af9ddeaabdbfdd587acad42b7ed0cbdf45c62cc3 Mon Sep 17 00:00:00 2001 From: steven Date: Mon, 4 Dec 2023 13:19:59 -0500 Subject: [PATCH 009/101] fix: additional missed imports --- test/ffi/BLSSignatureCheckerFFI.t.sol | 4 ++-- test/unit/BLSRegistryCoordinatorWithIndicesUnit.t.sol | 2 +- test/unit/StakeRegistryUnit.t.sol | 2 +- test/utils/MockAVSDeployer.sol | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/test/ffi/BLSSignatureCheckerFFI.t.sol b/test/ffi/BLSSignatureCheckerFFI.t.sol index 9eb265d3..dbe71f00 100644 --- a/test/ffi/BLSSignatureCheckerFFI.t.sol +++ b/test/ffi/BLSSignatureCheckerFFI.t.sol @@ -1,8 +1,8 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity =0.8.12; -import {G2Operations} from "test/ffi/util/G2Operations.sol"; -import {MockAVSDeployer} from "test/utils/MockAVSDeployer.sol"; +import {G2Operations} from "./util/G2Operations.sol"; +import {MockAVSDeployer} from "../utils/MockAVSDeployer.sol"; import {BLSSignatureChecker} from "../../src/BLSSignatureChecker.sol"; import {BLSOperatorStateRetriever} from "../../src/BLSOperatorStateRetriever.sol"; import {BN254} from "../../src/libraries/BN254.sol"; diff --git a/test/unit/BLSRegistryCoordinatorWithIndicesUnit.t.sol b/test/unit/BLSRegistryCoordinatorWithIndicesUnit.t.sol index 9244d5f6..827be37c 100644 --- a/test/unit/BLSRegistryCoordinatorWithIndicesUnit.t.sol +++ b/test/unit/BLSRegistryCoordinatorWithIndicesUnit.t.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity =0.8.12; -import "test/utils/MockAVSDeployer.sol"; +import "../utils/MockAVSDeployer.sol"; contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { using BN254 for BN254.G1Point; diff --git a/test/unit/StakeRegistryUnit.t.sol b/test/unit/StakeRegistryUnit.t.sol index f0e46b3d..e2fdd849 100644 --- a/test/unit/StakeRegistryUnit.t.sol +++ b/test/unit/StakeRegistryUnit.t.sol @@ -28,7 +28,7 @@ import {SlasherMock} from "eigenlayer-contracts/src/test/mocks/SlasherMock.sol"; import {StakeRegistryHarness} from "../harnesses/StakeRegistryHarness.sol"; import {StakeRegistry} from "../../src/StakeRegistry.sol"; -import {BLSRegistryCoordinatorWithIndicesHarness} from "test/harnesses/BLSRegistryCoordinatorWithIndicesHarness.sol"; +import {BLSRegistryCoordinatorWithIndicesHarness} from "../harnesses/BLSRegistryCoordinatorWithIndicesHarness.sol"; import "forge-std/Test.sol"; diff --git a/test/utils/MockAVSDeployer.sol b/test/utils/MockAVSDeployer.sol index f27a4f5a..0429589c 100644 --- a/test/utils/MockAVSDeployer.sol +++ b/test/utils/MockAVSDeployer.sol @@ -16,7 +16,7 @@ import {BN254} from "../../src/libraries/BN254.sol"; import {BLSPublicKeyCompendium} from "../../src/BLSPublicKeyCompendium.sol"; import {BLSOperatorStateRetriever} from "../../src/BLSOperatorStateRetriever.sol"; import {BLSRegistryCoordinatorWithIndices} from "../../src/BLSRegistryCoordinatorWithIndices.sol"; -import {BLSRegistryCoordinatorWithIndicesHarness} from "test/harnesses/BLSRegistryCoordinatorWithIndicesHarness.sol"; +import {BLSRegistryCoordinatorWithIndicesHarness} from "../harnesses/BLSRegistryCoordinatorWithIndicesHarness.sol"; import {BLSPubkeyRegistry} from "../../src/BLSPubkeyRegistry.sol"; import {StakeRegistry} from "../../src/StakeRegistry.sol"; import {IndexRegistry} from "../../src/IndexRegistry.sol"; From 0f1a70baab3cf9077af938c37c7414cb73443fb1 Mon Sep 17 00:00:00 2001 From: Bowen Li Date: Fri, 15 Dec 2023 10:36:29 -0800 Subject: [PATCH 010/101] Update README.md to avoid confusing phrasing (#75) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2cb70571..e7343bfd 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ There are two things that matter in terms of operators’ onchain interaction wi - What qualities of operators need to be kept track of onchain? (e.g. stake, BLS pubkeys, etc.) - Registration/Deregistration conditions -These two points have been addressed through the Registry Coordinator/Registry Architecture. +These two points have been addressed through the architecture of Registry Coordinator and Registry. ### Definitions From 08c29fb1af5e6b74709efb1bcbcd7919923f56c4 Mon Sep 17 00:00:00 2001 From: wadealexc Date: Tue, 24 Oct 2023 15:24:13 +0000 Subject: [PATCH 011/101] style: use for loop indices when the index is just an index --- src/BLSRegistryCoordinatorWithIndices.sol | 11 +++++---- src/StakeRegistry.sol | 30 +++++++++++------------ 2 files changed, 21 insertions(+), 20 deletions(-) diff --git a/src/BLSRegistryCoordinatorWithIndices.sol b/src/BLSRegistryCoordinatorWithIndices.sol index c8fa1497..7dbc7132 100644 --- a/src/BLSRegistryCoordinatorWithIndices.sol +++ b/src/BLSRegistryCoordinatorWithIndices.sol @@ -568,15 +568,16 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr ) 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++) { + uint length = _operatorIdToQuorumBitmapHistory[operatorIds[i]].length; + for (uint j = 0; j < length; j++) { if (_operatorIdToQuorumBitmapHistory[operatorIds[i]][length - j - 1].updateBlockNumber <= blockNumber) { + uint32 nextUpdateBlockNumber = + _operatorIdToQuorumBitmapHistory[operatorIds[i]][length - j - 1].nextUpdateBlockNumber; require( - _operatorIdToQuorumBitmapHistory[operatorIds[i]][length - j - 1].nextUpdateBlockNumber == 0 || - _operatorIdToQuorumBitmapHistory[operatorIds[i]][length - j - 1].nextUpdateBlockNumber > blockNumber, + nextUpdateBlockNumber == 0 || nextUpdateBlockNumber > blockNumber, "BLSRegistryCoordinatorWithIndices.getQuorumBitmapIndicesByOperatorIdsAtBlockNumber: operatorId has no quorumBitmaps at blockNumber" ); - indices[i] = length - j - 1; + indices[i] = uint32(length - j - 1); break; } } diff --git a/src/StakeRegistry.sol b/src/StakeRegistry.sol index c1c2564f..2a0b1b89 100644 --- a/src/StakeRegistry.sol +++ b/src/StakeRegistry.sol @@ -148,9 +148,9 @@ contract StakeRegistry is VoteWeigherBase, StakeRegistryStorage { // 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; ) { + for (uint i = 0; i < quorumNumbers.length; ) { // get the next quorumNumber - uint8 quorumNumber = uint8(quorumNumbers[quorumNumbersIndex]); + uint8 quorumNumber = uint8(quorumNumbers[i]); // 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({ @@ -176,7 +176,7 @@ contract StakeRegistry is VoteWeigherBase, StakeRegistryStorage { // update storage of total stake _recordTotalStakeUpdate(quorumNumber, _newTotalStakeUpdate); unchecked { - ++quorumNumbersIndex; + ++i; } } } @@ -204,8 +204,8 @@ contract StakeRegistry is VoteWeigherBase, StakeRegistryStorage { // 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]); + for (uint i = 0; i < quorumNumbers.length; ) { + uint8 quorumNumber = uint8(quorumNumbers[i]); // update the operator's stake uint96 stakeBeforeUpdate = _recordOperatorStakeUpdate({ operatorId: operatorId, @@ -227,7 +227,7 @@ contract StakeRegistry is VoteWeigherBase, StakeRegistryStorage { 0 ); unchecked { - ++quorumNumbersIndex; + ++i; } } } @@ -250,16 +250,16 @@ contract StakeRegistry is VoteWeigherBase, StakeRegistryStorage { uint8 quorumNumber, uint32 blockNumber ) internal view returns (uint32) { - uint32 length = uint32(operatorIdToStakeHistory[operatorId][quorumNumber].length); - for (uint32 i = 0; i < length; i++) { + uint length = operatorIdToStakeHistory[operatorId][quorumNumber].length; + for (uint i = 0; i < length; i++) { if (operatorIdToStakeHistory[operatorId][quorumNumber][length - i - 1].updateBlockNumber <= blockNumber) { + uint32 nextUpdateBlockNumber = + operatorIdToStakeHistory[operatorId][quorumNumber][length - i - 1].nextUpdateBlockNumber; require( - operatorIdToStakeHistory[operatorId][quorumNumber][length - i - 1].nextUpdateBlockNumber == 0 || - operatorIdToStakeHistory[operatorId][quorumNumber][length - i - 1].nextUpdateBlockNumber > - blockNumber, + nextUpdateBlockNumber == 0 || nextUpdateBlockNumber > blockNumber, "StakeRegistry._getStakeUpdateIndexForOperatorIdForQuorumAtBlockNumber: operatorId has no stake update at blockNumber" ); - return length - i - 1; + return uint32(length - i - 1); } } revert( @@ -424,10 +424,10 @@ contract StakeRegistry is VoteWeigherBase, StakeRegistryStorage { _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++) { + uint length = _totalStakeHistory[quorumNumber].length; + for (uint j = 0; j < length; j++) { if (_totalStakeHistory[quorumNumber][length - j - 1].updateBlockNumber <= blockNumber) { - indices[i] = length - j - 1; + indices[i] = uint32(length - j - 1); break; } } From 40b343ab4a19607a71262802c97e9524e9eb409c Mon Sep 17 00:00:00 2001 From: wadealexc Date: Tue, 24 Oct 2023 18:40:38 +0000 Subject: [PATCH 012/101] refactor: Use a signed delta value in StakeRegistry to remove tons of unneeded code --- lib/eigenlayer-contracts | 2 +- src/StakeRegistry.sol | 225 +++++++++++++----------- test/harnesses/StakeRegistryHarness.sol | 54 +++--- test/unit/BLSSignatureCheckerUnit.t.sol | 6 +- test/unit/StakeRegistryUnit.t.sol | 18 +- 5 files changed, 164 insertions(+), 141 deletions(-) diff --git a/lib/eigenlayer-contracts b/lib/eigenlayer-contracts index bccae530..a7bb3d8c 160000 --- a/lib/eigenlayer-contracts +++ b/lib/eigenlayer-contracts @@ -1 +1 @@ -Subproject commit bccae530de8bcbf17cfd3e0a04c7b8b13679d34d +Subproject commit a7bb3d8ca4374c5cbb95324916cb44727e0ecadc diff --git a/src/StakeRegistry.sol b/src/StakeRegistry.sol index 2a0b1b89..8b41f070 100644 --- a/src/StakeRegistry.sol +++ b/src/StakeRegistry.sol @@ -80,38 +80,38 @@ contract StakeRegistry is VoteWeigherBase, StakeRegistryStorage { // 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 + int256 totalStakeDelta; + for (uint i = 0; i < operators.length; ) { bytes32 operatorId = registryCoordinator.getOperatorId(operators[i]); uint192 quorumBitmap = registryCoordinator.getCurrentQuorumBitmapByOperatorId(operatorId); - // if the operator is a part of the quorum + /** + * If the operator is a part of the quorum, update their current stake + * and apply the delta to the total + */ 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({ + (int256 stakeDelta, ) = _updateOperatorStake({ operator: operators[i], operatorId: operatorId, quorumNumber: quorumNumber }); - // calculate the new total stake for the quorum - totalStakeUpdate.stake = totalStakeUpdate.stake - stakeBeforeUpdate + stakeAfterUpdate; + + totalStakeDelta += stakeDelta; } 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); + + // If we have a change in total stake for this quorum, update state + // TODO - do we want to record the update, even if the delta is zero? + // maybe this makes sense in the case that the quorum doesn't have + // an update for this block? + if (totalStakeDelta != 0) { + _recordTotalStakeUpdate(quorumNumber, totalStakeDelta); } + unchecked { ++quorumNumber; } @@ -144,37 +144,29 @@ contract StakeRegistry is VoteWeigherBase, StakeRegistryStorage { 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 (uint i = 0; i < quorumNumbers.length; ) { - // get the next quorumNumber + + for (uint i = 0; i < quorumNumbers.length; ) { + /** + * Update the operator's stake for the quorum and retrieve their current stake + * as well as the change in stake. + * If this method returns `stake == 0`, the operator has not met the minimum requirement + * + * TODO - we only use the `stake` return here. It's probably better to use a bool instead + * of relying on the method returning "0" in only this one case. + */ uint8 quorumNumber = uint8(quorumNumbers[i]); - // 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({ + (int256 stakeDelta, uint96 stake) = _updateOperatorStake({ operator: operator, operatorId: operatorId, quorumNumber: 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); + + // Update this quorum's total stake + _recordTotalStakeUpdate(quorumNumber, stakeDelta); unchecked { ++i; } @@ -197,35 +189,22 @@ contract StakeRegistry is VoteWeigherBase, StakeRegistryStorage { bytes32 operatorId, bytes calldata quorumNumbers ) public virtual onlyRegistryCoordinator { - 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 each quorum, remove the operator's stake for the quorum and update + * the quorum's total stake to account for the removal + */ for (uint i = 0; i < quorumNumbers.length; ) { + // Update the operator's stake for the quorum and retrieve the shares removed uint8 quorumNumber = uint8(quorumNumbers[i]); - // update the operator's stake - uint96 stakeBeforeUpdate = _recordOperatorStakeUpdate({ + int256 stakeDelta = _recordOperatorStakeUpdate({ operatorId: operatorId, quorumNumber: quorumNumber, - operatorStakeUpdate: _operatorStakeUpdate + newStake: 0 }); - // 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 - ); + + // Apply the operator's stake delta to the total stake for this quorum + _recordTotalStakeUpdate(quorumNumber, stakeDelta); + unchecked { ++i; } @@ -273,71 +252,111 @@ contract StakeRegistry is VoteWeigherBase, StakeRegistryStorage { } /** - * @notice Finds the updated stake for `operator` for `quorumNumber`, stores it, records the update, - * and returns both the previous stake then updated stake. + * @notice Finds the updated stake for `operator` for `quorumNumber`, stores it and records the update * @dev **DOES NOT UPDATE `totalStake` IN ANY WAY** -- `totalStake` updates must be done elsewhere. + * @return `int256` The change in the operator's stake as a signed int256 + * @return `uint96` The operator's new stake after the update */ 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); + ) internal returns (int256, uint96) { + /** + * Get the operator's current stake for the quorum. If their stake + * is below the quorum's threshold, set their stake to 0 + */ + uint96 currentStake = weightOfOperatorForQuorum(quorumNumber, operator); + if (currentStake < minimumStakeForQuorum[quorumNumber]) { + currentStake = uint96(0); } - // get stakeBeforeUpdate and update with new stake - uint96 stakeBeforeUpdate = _recordOperatorStakeUpdate({ + + // Update the operator's stake and retrieve the delta + int256 delta = _recordOperatorStakeUpdate({ operatorId: operatorId, quorumNumber: quorumNumber, - operatorStakeUpdate: operatorStakeUpdate + newStake: currentStake }); - emit StakeUpdate(operatorId, quorumNumber, operatorStakeUpdate.stake); - - return (stakeBeforeUpdate, operatorStakeUpdate.stake); + return (delta, currentStake); } /** * @notice Records that `operatorId`'s current stake for `quorumNumber` is now param @operatorStakeUpdate - * and returns the previous stake. + * @return The change in the operator's stake as a signed int256 */ 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] + uint96 newStake + ) internal returns (int256) { + /** + * If the operator has previous stake history, update the previous entry + * and fetch their previous stake + */ + uint96 prevStake; + uint256 historyLength = operatorIdToStakeHistory[operatorId][quorumNumber].length; + if (historyLength != 0) { + operatorIdToStakeHistory[operatorId][quorumNumber][historyLength - 1] .nextUpdateBlockNumber = uint32(block.number); - // load stake before update into memory if it exists - stakeBeforeUpdate = operatorIdToStakeHistory[operatorId][quorumNumber][operatorStakeHistoryLength - 1] - .stake; + + prevStake = + operatorIdToStakeHistory[operatorId][quorumNumber][historyLength - 1].stake; } - // push new stake to storage - operatorIdToStakeHistory[operatorId][quorumNumber].push(operatorStakeUpdate); - return stakeBeforeUpdate; + + // Create a new stake update and push it to storage + // TODO - update the entry instead of pushing, if the last update block is this block + operatorIdToStakeHistory[operatorId][quorumNumber].push(OperatorStakeUpdate({ + updateBlockNumber: uint32(block.number), + nextUpdateBlockNumber: 0, + stake: newStake + })); + + emit StakeUpdate(operatorId, quorumNumber, newStake); + + // Return the change in stake + return _calculateDelta({ prev: prevStake, cur: newStake }); } /// @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); + function _recordTotalStakeUpdate(uint8 quorumNumber, int256 stakeDelta) internal { + /** + * If this quorum has previous stake history, update the previous entry + * and fetch the previous total stake + */ + uint96 prevStake; + uint256 historyLength = _totalStakeHistory[quorumNumber].length; + if (historyLength != 0) { + _totalStakeHistory[quorumNumber][historyLength - 1].nextUpdateBlockNumber = uint32(block.number); + + prevStake = _totalStakeHistory[quorumNumber][historyLength - 1].stake; + } + + // Apply the stake delta to the previous stake, and push an update to the + // quorum's stake history + _totalStakeHistory[quorumNumber].push(OperatorStakeUpdate({ + updateBlockNumber: uint32(block.number), + nextUpdateBlockNumber: 0, + stake: _applyDelta(prevStake, stakeDelta) + })); + } + + /// @notice Returns the change between a previous and current value as a signed int + function _calculateDelta(uint96 prev, uint96 cur) internal pure returns (int256) { + if (cur >= prev) { + return int256(uint256(cur - prev)); + } else { + return -int256(uint256(prev - cur)); + } + } + + /// @notice Adds or subtracts delta from value, according to its sign + function _applyDelta(uint96 value, int256 delta) internal pure returns (uint96) { + if (delta < 0) { + return value - uint96(uint256(-delta)); + } else { + return value + uint96(uint256(delta)); } - _totalStake.updateBlockNumber = uint32(block.number); - _totalStakeHistory[quorumNumber].push(_totalStake); } /// @notice Validates that the `operatorStake` was accurate at the given `blockNumber` diff --git a/test/harnesses/StakeRegistryHarness.sol b/test/harnesses/StakeRegistryHarness.sol index 8bb8d07b..9cdb2890 100644 --- a/test/harnesses/StakeRegistryHarness.sol +++ b/test/harnesses/StakeRegistryHarness.sol @@ -14,16 +14,16 @@ contract StakeRegistryHarness is StakeRegistry { ) StakeRegistry(_registryCoordinator, _strategyManager, _serviceManager) { } - function recordOperatorStakeUpdate(bytes32 operatorId, uint8 quorumNumber, OperatorStakeUpdate memory operatorStakeUpdate) external returns(uint96) { - return _recordOperatorStakeUpdate(operatorId, quorumNumber, operatorStakeUpdate); + function recordOperatorStakeUpdate(bytes32 operatorId, uint8 quorumNumber, uint96 newStake) external returns(int256) { + return _recordOperatorStakeUpdate(operatorId, quorumNumber, newStake); } - function updateOperatorStake(address operator, bytes32 operatorId, uint8 quorumNumber) external returns (uint96, uint96) { + function updateOperatorStake(address operator, bytes32 operatorId, uint8 quorumNumber) external returns (int256, uint96) { return _updateOperatorStake(operator, operatorId, quorumNumber); } - function recordTotalStakeUpdate(uint8 quorumNumber, OperatorStakeUpdate memory totalStakeUpdate) external { - _recordTotalStakeUpdate(quorumNumber, totalStakeUpdate); + function recordTotalStakeUpdate(uint8 quorumNumber, int256 stakeDelta) external { + _recordTotalStakeUpdate(quorumNumber, stakeDelta); } // mocked function so we can set this arbitrarily without having to mock other elements @@ -44,35 +44,31 @@ contract StakeRegistryHarness is StakeRegistry { 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 + + for (uint i = 0; i < quorumNumbers.length; ) { + /** + * Update the operator's stake for the quorum and retrieve their current stake + * as well as the change in stake. + * If this method returns `stake == 0`, the operator has not met the minimum requirement + * + * TODO - we only use the `stake` return here. It's probably better to use a bool instead + * of relying on the method returning "0" in only this one case. + */ + uint8 quorumNumber = uint8(quorumNumbers[i]); + (int256 stakeDelta, uint96 stake) = _updateOperatorStake({ + operator: operator, + operatorId: operatorId, + quorumNumber: quorumNumber + }); 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); + + // Update this quorum's total stake + _recordTotalStakeUpdate(quorumNumber, stakeDelta); unchecked { - ++quorumNumbersIndex; + ++i; } } } diff --git a/test/unit/BLSSignatureCheckerUnit.t.sol b/test/unit/BLSSignatureCheckerUnit.t.sol index d61f847f..0b9150cf 100644 --- a/test/unit/BLSSignatureCheckerUnit.t.sol +++ b/test/unit/BLSSignatureCheckerUnit.t.sol @@ -133,11 +133,7 @@ contract BLSSignatureCheckerUnitTests is BLSMockAVSDeployer { stakeRegistry.recordOperatorStakeUpdate( nonSignerOperatorId, uint8(quorumNumbers[0]), - IStakeRegistry.OperatorStakeUpdate({ - updateBlockNumber: uint32(block.number), - nextUpdateBlockNumber: 0, - stake: 1234 - }) + 1234 ); // set the nonSignerStakeIndices to a different value diff --git a/test/unit/StakeRegistryUnit.t.sol b/test/unit/StakeRegistryUnit.t.sol index e2fdd849..78307407 100644 --- a/test/unit/StakeRegistryUnit.t.sol +++ b/test/unit/StakeRegistryUnit.t.sol @@ -493,10 +493,14 @@ contract StakeRegistryUnitTests is Test { 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]; + int256 stakeDelta; + if (i == 0) { + stakeDelta = _calculateDelta({prev: 0, cur: stakes[i]}); + } else { + stakeDelta = _calculateDelta({prev: stakes[i-1], cur: stakes[i]}); + } - stakeRegistry.recordTotalStakeUpdate(defaultQuorumNumber, totalStakeUpdate); + stakeRegistry.recordTotalStakeUpdate(defaultQuorumNumber, stakeDelta); cumulativeBlockNumber += blocksPassed[i]; cheats.roll(cumulativeBlockNumber); @@ -613,4 +617,12 @@ contract StakeRegistryUnitTests is Test { function _incrementBytes32(bytes32 start, uint256 inc) internal pure returns(bytes32) { return bytes32(uint256(start) + inc); } + + function _calculateDelta(uint96 prev, uint96 cur) internal pure returns (int256) { + if (cur >= prev) { + return int256(uint256(cur - prev)); + } else { + return -int256(uint256(prev - cur)); + } + } } From 66e2ddcf3d50147baff1d16da2d5eed79a4b9c3a Mon Sep 17 00:00:00 2001 From: wadealexc Date: Tue, 24 Oct 2023 19:16:20 +0000 Subject: [PATCH 013/101] style: use over , and simplify --- src/StakeRegistry.sol | 20 ++++++++------------ test/unit/StakeRegistryUnit.t.sol | 6 +----- 2 files changed, 9 insertions(+), 17 deletions(-) diff --git a/src/StakeRegistry.sol b/src/StakeRegistry.sol index 8b41f070..c5fd4eed 100644 --- a/src/StakeRegistry.sol +++ b/src/StakeRegistry.sol @@ -82,7 +82,7 @@ contract StakeRegistry is VoteWeigherBase, StakeRegistryStorage { for (uint8 quorumNumber = 0; quorumNumber < quorumCount; ) { int256 totalStakeDelta; - for (uint i = 0; i < operators.length; ) { + for (uint256 i = 0; i < operators.length; ) { bytes32 operatorId = registryCoordinator.getOperatorId(operators[i]); uint192 quorumBitmap = registryCoordinator.getCurrentQuorumBitmapByOperatorId(operatorId); @@ -145,7 +145,7 @@ contract StakeRegistry is VoteWeigherBase, StakeRegistryStorage { "StakeRegistry._registerOperator: greatest quorumNumber must be less than quorumCount" ); - for (uint i = 0; i < quorumNumbers.length; ) { + for (uint256 i = 0; i < quorumNumbers.length; ) { /** * Update the operator's stake for the quorum and retrieve their current stake * as well as the change in stake. @@ -193,7 +193,7 @@ contract StakeRegistry is VoteWeigherBase, StakeRegistryStorage { * For each quorum, remove the operator's stake for the quorum and update * the quorum's total stake to account for the removal */ - for (uint i = 0; i < quorumNumbers.length; ) { + for (uint256 i = 0; i < quorumNumbers.length; ) { // Update the operator's stake for the quorum and retrieve the shares removed uint8 quorumNumber = uint8(quorumNumbers[i]); int256 stakeDelta = _recordOperatorStakeUpdate({ @@ -229,8 +229,8 @@ contract StakeRegistry is VoteWeigherBase, StakeRegistryStorage { uint8 quorumNumber, uint32 blockNumber ) internal view returns (uint32) { - uint length = operatorIdToStakeHistory[operatorId][quorumNumber].length; - for (uint i = 0; i < length; i++) { + uint256 length = operatorIdToStakeHistory[operatorId][quorumNumber].length; + for (uint256 i = 0; i < length; i++) { if (operatorIdToStakeHistory[operatorId][quorumNumber][length - i - 1].updateBlockNumber <= blockNumber) { uint32 nextUpdateBlockNumber = operatorIdToStakeHistory[operatorId][quorumNumber][length - i - 1].nextUpdateBlockNumber; @@ -343,11 +343,7 @@ contract StakeRegistry is VoteWeigherBase, StakeRegistryStorage { /// @notice Returns the change between a previous and current value as a signed int function _calculateDelta(uint96 prev, uint96 cur) internal pure returns (int256) { - if (cur >= prev) { - return int256(uint256(cur - prev)); - } else { - return -int256(uint256(prev - cur)); - } + return int256(uint256(cur)) - int256(uint256(prev)); } /// @notice Adds or subtracts delta from value, according to its sign @@ -443,8 +439,8 @@ contract StakeRegistry is VoteWeigherBase, StakeRegistryStorage { _totalStakeHistory[quorumNumber][0].updateBlockNumber <= blockNumber, "StakeRegistry.getTotalStakeIndicesByQuorumNumbersAtBlockNumber: quorum has no stake history at blockNumber" ); - uint length = _totalStakeHistory[quorumNumber].length; - for (uint j = 0; j < length; j++) { + uint256 length = _totalStakeHistory[quorumNumber].length; + for (uint256 j = 0; j < length; j++) { if (_totalStakeHistory[quorumNumber][length - j - 1].updateBlockNumber <= blockNumber) { indices[i] = uint32(length - j - 1); break; diff --git a/test/unit/StakeRegistryUnit.t.sol b/test/unit/StakeRegistryUnit.t.sol index 78307407..f8aaeaa0 100644 --- a/test/unit/StakeRegistryUnit.t.sol +++ b/test/unit/StakeRegistryUnit.t.sol @@ -619,10 +619,6 @@ contract StakeRegistryUnitTests is Test { } function _calculateDelta(uint96 prev, uint96 cur) internal pure returns (int256) { - if (cur >= prev) { - return int256(uint256(cur - prev)); - } else { - return -int256(uint256(prev - cur)); - } + return int256(uint256(cur)) - int256(uint256(prev)); } } From 057da677fd229009e0faecfd215cd3f2729ee73d Mon Sep 17 00:00:00 2001 From: wadealexc Date: Tue, 24 Oct 2023 21:51:02 +0000 Subject: [PATCH 014/101] feat: only update total history for nonzero delta, and dont push update if last update was in current block --- src/StakeRegistry.sol | 125 ++++++++++++++---------- test/harnesses/StakeRegistryHarness.sol | 14 ++- test/unit/StakeRegistryUnit.t.sol | 66 +++++++------ 3 files changed, 116 insertions(+), 89 deletions(-) diff --git a/src/StakeRegistry.sol b/src/StakeRegistry.sol index c5fd4eed..61cf5ee9 100644 --- a/src/StakeRegistry.sol +++ b/src/StakeRegistry.sol @@ -104,13 +104,8 @@ contract StakeRegistry is VoteWeigherBase, StakeRegistryStorage { } } - // If we have a change in total stake for this quorum, update state - // TODO - do we want to record the update, even if the delta is zero? - // maybe this makes sense in the case that the quorum doesn't have - // an update for this block? - if (totalStakeDelta != 0) { - _recordTotalStakeUpdate(quorumNumber, totalStakeDelta); - } + // Record the update for the quorum's total stake + _recordTotalStakeUpdate(quorumNumber, totalStakeDelta); unchecked { ++quorumNumber; @@ -149,19 +144,17 @@ contract StakeRegistry is VoteWeigherBase, StakeRegistryStorage { /** * Update the operator's stake for the quorum and retrieve their current stake * as well as the change in stake. - * If this method returns `stake == 0`, the operator has not met the minimum requirement - * - * TODO - we only use the `stake` return here. It's probably better to use a bool instead - * of relying on the method returning "0" in only this one case. + * - If this method returns `hasMinimumStake == false`, the operator has not met + * the minimum stake requirement for this quorum */ uint8 quorumNumber = uint8(quorumNumbers[i]); - (int256 stakeDelta, uint96 stake) = _updateOperatorStake({ + (int256 stakeDelta, bool hasMinimumStake) = _updateOperatorStake({ operator: operator, operatorId: operatorId, quorumNumber: quorumNumber }); require( - stake != 0, + hasMinimumStake, "StakeRegistry._registerOperator: Operator does not meet minimum stake requirement for quorum" ); @@ -254,14 +247,14 @@ contract StakeRegistry is VoteWeigherBase, StakeRegistryStorage { /** * @notice Finds the updated stake for `operator` for `quorumNumber`, stores it and records the update * @dev **DOES NOT UPDATE `totalStake` IN ANY WAY** -- `totalStake` updates must be done elsewhere. - * @return `int256` The change in the operator's stake as a signed int256 - * @return `uint96` The operator's new stake after the update + * @return delta The change in the operator's stake as a signed int256 + * @return hasMinimumStake Whether the operator meets the minimum stake requirement for the quorum */ function _updateOperatorStake( address operator, bytes32 operatorId, uint8 quorumNumber - ) internal returns (int256, uint96) { + ) internal returns (int256 delta, bool hasMinimumStake) { /** * Get the operator's current stake for the quorum. If their stake * is below the quorum's threshold, set their stake to 0 @@ -269,20 +262,22 @@ contract StakeRegistry is VoteWeigherBase, StakeRegistryStorage { uint96 currentStake = weightOfOperatorForQuorum(quorumNumber, operator); if (currentStake < minimumStakeForQuorum[quorumNumber]) { currentStake = uint96(0); + } else { + hasMinimumStake = true; } // Update the operator's stake and retrieve the delta - int256 delta = _recordOperatorStakeUpdate({ + delta = _recordOperatorStakeUpdate({ operatorId: operatorId, quorumNumber: quorumNumber, newStake: currentStake }); - return (delta, currentStake); + return (delta, hasMinimumStake); } /** - * @notice Records that `operatorId`'s current stake for `quorumNumber` is now param @operatorStakeUpdate + * @notice Records that `operatorId`'s current stake for `quorumNumber` is now `newStake` * @return The change in the operator's stake as a signed int256 */ function _recordOperatorStakeUpdate( @@ -290,55 +285,79 @@ contract StakeRegistry is VoteWeigherBase, StakeRegistryStorage { uint8 quorumNumber, uint96 newStake ) internal returns (int256) { - /** - * If the operator has previous stake history, update the previous entry - * and fetch their previous stake - */ + uint96 prevStake; uint256 historyLength = operatorIdToStakeHistory[operatorId][quorumNumber].length; - if (historyLength != 0) { - operatorIdToStakeHistory[operatorId][quorumNumber][historyLength - 1] - .nextUpdateBlockNumber = uint32(block.number); - prevStake = - operatorIdToStakeHistory[operatorId][quorumNumber][historyLength - 1].stake; + if (historyLength == 0) { + // No prior stake history - push our first entry + operatorIdToStakeHistory[operatorId][quorumNumber].push(OperatorStakeUpdate({ + updateBlockNumber: uint32(block.number), + nextUpdateBlockNumber: 0, + stake: newStake + })); + } else { + // We have prior stake history - fetch our last-recorded stake + prevStake = operatorIdToStakeHistory[operatorId][quorumNumber][historyLength-1].stake; + + /** + * If our last stake entry was made in the current block, update the entry + * Otherwise, push a new entry and update the previous entry's "next" field + */ + if (operatorIdToStakeHistory[operatorId][quorumNumber][historyLength-1].updateBlockNumber == uint32(block.number)) { + operatorIdToStakeHistory[operatorId][quorumNumber][historyLength-1].stake = newStake; + } else { + operatorIdToStakeHistory[operatorId][quorumNumber][historyLength-1].nextUpdateBlockNumber = uint32(block.number); + operatorIdToStakeHistory[operatorId][quorumNumber].push(OperatorStakeUpdate({ + updateBlockNumber: uint32(block.number), + nextUpdateBlockNumber: 0, + stake: newStake + })); + } } - - // Create a new stake update and push it to storage - // TODO - update the entry instead of pushing, if the last update block is this block - operatorIdToStakeHistory[operatorId][quorumNumber].push(OperatorStakeUpdate({ - updateBlockNumber: uint32(block.number), - nextUpdateBlockNumber: 0, - stake: newStake - })); + // Log update and return stake delta emit StakeUpdate(operatorId, quorumNumber, newStake); - - // Return the change in stake return _calculateDelta({ prev: prevStake, cur: newStake }); } - /// @notice Records that the `totalStake` for `quorumNumber` is now equal to the input param @_totalStake + /// @notice Applies a delta to the total stake recorded for `quorumNumber` function _recordTotalStakeUpdate(uint8 quorumNumber, int256 stakeDelta) internal { - /** - * If this quorum has previous stake history, update the previous entry - * and fetch the previous total stake - */ + // Return early if no update is needed + if (stakeDelta == 0) { + return; + } + uint96 prevStake; uint256 historyLength = _totalStakeHistory[quorumNumber].length; - if (historyLength != 0) { - _totalStakeHistory[quorumNumber][historyLength - 1].nextUpdateBlockNumber = uint32(block.number); + if (historyLength == 0) { + // No prior stake history - push our first entry + _totalStakeHistory[quorumNumber].push(OperatorStakeUpdate({ + updateBlockNumber: uint32(block.number), + nextUpdateBlockNumber: 0, + stake: _applyDelta(prevStake, stakeDelta) + })); + } else { + // We have prior stake history - calculate our new stake as a function of our last-recorded stake prevStake = _totalStakeHistory[quorumNumber][historyLength - 1].stake; + uint96 newStake = _applyDelta(prevStake, stakeDelta); + + /** + * If our last stake entry was made in the current block, update the entry + * Otherwise, push a new entry and update the previous entry's "next" field + */ + if (_totalStakeHistory[quorumNumber][historyLength-1].updateBlockNumber == uint32(block.number)) { + _totalStakeHistory[quorumNumber][historyLength-1].stake = newStake; + } else { + _totalStakeHistory[quorumNumber][historyLength-1].nextUpdateBlockNumber = uint32(block.number); + _totalStakeHistory[quorumNumber].push(OperatorStakeUpdate({ + updateBlockNumber: uint32(block.number), + nextUpdateBlockNumber: 0, + stake: newStake + })); + } } - - // Apply the stake delta to the previous stake, and push an update to the - // quorum's stake history - _totalStakeHistory[quorumNumber].push(OperatorStakeUpdate({ - updateBlockNumber: uint32(block.number), - nextUpdateBlockNumber: 0, - stake: _applyDelta(prevStake, stakeDelta) - })); } /// @notice Returns the change between a previous and current value as a signed int diff --git a/test/harnesses/StakeRegistryHarness.sol b/test/harnesses/StakeRegistryHarness.sol index 9cdb2890..48b93214 100644 --- a/test/harnesses/StakeRegistryHarness.sol +++ b/test/harnesses/StakeRegistryHarness.sol @@ -18,7 +18,7 @@ contract StakeRegistryHarness is StakeRegistry { return _recordOperatorStakeUpdate(operatorId, quorumNumber, newStake); } - function updateOperatorStake(address operator, bytes32 operatorId, uint8 quorumNumber) external returns (int256, uint96) { + function updateOperatorStake(address operator, bytes32 operatorId, uint8 quorumNumber) external returns (int256, bool) { return _updateOperatorStake(operator, operatorId, quorumNumber); } @@ -45,23 +45,21 @@ contract StakeRegistryHarness is StakeRegistry { "StakeRegistry._registerOperator: greatest quorumNumber must be less than quorumCount" ); - for (uint i = 0; i < quorumNumbers.length; ) { + for (uint256 i = 0; i < quorumNumbers.length; ) { /** * Update the operator's stake for the quorum and retrieve their current stake * as well as the change in stake. - * If this method returns `stake == 0`, the operator has not met the minimum requirement - * - * TODO - we only use the `stake` return here. It's probably better to use a bool instead - * of relying on the method returning "0" in only this one case. + * - If this method returns `hasMinimumStake == false`, the operator has not met + * the minimum stake requirement for this quorum */ uint8 quorumNumber = uint8(quorumNumbers[i]); - (int256 stakeDelta, uint96 stake) = _updateOperatorStake({ + (int256 stakeDelta, bool hasMinimumStake) = _updateOperatorStake({ operator: operator, operatorId: operatorId, quorumNumber: quorumNumber }); require( - stake != 0, + hasMinimumStake, "StakeRegistry._registerOperator: Operator does not meet minimum stake requirement for quorum" ); diff --git a/test/unit/StakeRegistryUnit.t.sol b/test/unit/StakeRegistryUnit.t.sol index f8aaeaa0..ae9ef19f 100644 --- a/test/unit/StakeRegistryUnit.t.sol +++ b/test/unit/StakeRegistryUnit.t.sol @@ -377,13 +377,8 @@ contract StakeRegistryUnitTests is Test { (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); - } - } + cumulativeBlockNumber += 1; + cheats.roll(cumulativeBlockNumber); // deregister the operator from a subset of the quorums uint256 deregistrationQuroumBitmap = quorumBitmap & deregistrationQuorumsFlag; @@ -411,13 +406,13 @@ contract StakeRegistryUnitTests is Test { 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"); + // Get history length for quorum + uint historyLength = stakeRegistry.getLengthOfTotalStakeHistoryForQuorum(i); // make sure that the last stake update is as expected IStakeRegistry.OperatorStakeUpdate memory lastTotalStakeUpdate - = stakeRegistry.getTotalStakeUpdateForQuorumFromIndex(i, numOperatorsInQuorum[i]); + = stakeRegistry.getTotalStakeUpdateForQuorumFromIndex(i, historyLength-1); assertEq(lastTotalStakeUpdate.stake, - stakeRegistry.getTotalStakeUpdateForQuorumFromIndex(i, numOperatorsInQuorum[i] - 1).stake // the previous total stake + stakeRegistry.getTotalStakeUpdateForQuorumFromIndex(i, historyLength-2).stake // the previous total stake - paddedStakesForQuorum[quorumNumberIndex], // minus the stake that was deregistered "testDeregisterFirstOperator_Valid_5" ); @@ -482,17 +477,15 @@ contract StakeRegistryUnitTests is Test { } function testRecordTotalStakeUpdate_Valid( - uint24[] memory blocksPassed, + uint24 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++) { + for (uint256 i = 0; i < stakes.length; i++) { int256 stakeDelta; if (i == 0) { stakeDelta = _calculateDelta({prev: 0, cur: stakes[i]}); @@ -500,22 +493,39 @@ contract StakeRegistryUnitTests is Test { stakeDelta = _calculateDelta({prev: stakes[i-1], cur: stakes[i]}); } - stakeRegistry.recordTotalStakeUpdate(defaultQuorumNumber, stakeDelta); + // Get previous history length and total stake update + uint prevHistoryLength = stakeRegistry.getLengthOfTotalStakeHistoryForQuorum(defaultQuorumNumber); + IStakeRegistry.OperatorStakeUpdate memory prevStakeUpdate; + if (prevHistoryLength != 0) { + prevStakeUpdate = stakeRegistry.getTotalStakeUpdateForQuorumFromIndex(defaultQuorumNumber, prevHistoryLength-1); + } + - cumulativeBlockNumber += blocksPassed[i]; - cheats.roll(cumulativeBlockNumber); - } + // Perform the update + stakeRegistry.recordTotalStakeUpdate(defaultQuorumNumber, stakeDelta); - // 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); + // Get new history length and total stake update + uint newHistoryLength = stakeRegistry.getLengthOfTotalStakeHistoryForQuorum(defaultQuorumNumber); + IStakeRegistry.OperatorStakeUpdate memory newStakeUpdate; + if (newHistoryLength != 0) { + newStakeUpdate = stakeRegistry.getTotalStakeUpdateForQuorumFromIndex(defaultQuorumNumber, newHistoryLength-1); + } + + // Check that the most recent entry reflects the correct stake + assertEq(newStakeUpdate.stake, stakes[i]); + + if (stakeDelta == 0) { + // Check that no update occurred + assertEq(prevHistoryLength, newHistoryLength); + assertEq(prevStakeUpdate.stake, newStakeUpdate.stake); + assertEq(prevStakeUpdate.updateBlockNumber, newStakeUpdate.updateBlockNumber); + assertEq(prevStakeUpdate.nextUpdateBlockNumber, newStakeUpdate.nextUpdateBlockNumber); + } else { + assertEq(newStakeUpdate.updateBlockNumber, cumulativeBlockNumber); + } - assertEq(totalStakeUpdate.stake, stakes[i]); - assertEq(totalStakeUpdate.updateBlockNumber, cumulativeBlockNumber); - cumulativeBlockNumber += blocksPassed[i]; - assertEq(totalStakeUpdate.nextUpdateBlockNumber, cumulativeBlockNumber); + cumulativeBlockNumber += blocksPassed; + cheats.roll(cumulativeBlockNumber); } } From 3125c7996d7326bb909a42c0025efbd590ba8204 Mon Sep 17 00:00:00 2001 From: wadealexc Date: Wed, 25 Oct 2023 13:58:59 +0000 Subject: [PATCH 015/101] style: use uint256 in registry coordinator --- src/BLSRegistryCoordinatorWithIndices.sol | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/BLSRegistryCoordinatorWithIndices.sol b/src/BLSRegistryCoordinatorWithIndices.sol index 7dbc7132..b810518d 100644 --- a/src/BLSRegistryCoordinatorWithIndices.sol +++ b/src/BLSRegistryCoordinatorWithIndices.sol @@ -432,7 +432,7 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr socket: socket }); - for (uint i = 0; i < numOperatorsPerQuorum.length; i++) { + for (uint256 i = 0; i < numOperatorsPerQuorum.length; i++) { require( numOperatorsPerQuorum[i] <= _quorumOperatorSetParams[uint8(quorumNumbers[i])].maxOperatorCount, "BLSRegistryCoordinatorWithIndices._registerOperatorWithCoordinatorAndNoOverfilledQuorums: quorum is overfilled" @@ -568,8 +568,8 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr ) external view returns (uint32[] memory) { uint32[] memory indices = new uint32[](operatorIds.length); for (uint256 i = 0; i < operatorIds.length; i++) { - uint length = _operatorIdToQuorumBitmapHistory[operatorIds[i]].length; - for (uint j = 0; j < length; j++) { + uint256 length = _operatorIdToQuorumBitmapHistory[operatorIds[i]].length; + for (uint256 j = 0; j < length; j++) { if (_operatorIdToQuorumBitmapHistory[operatorIds[i]][length - j - 1].updateBlockNumber <= blockNumber) { uint32 nextUpdateBlockNumber = _operatorIdToQuorumBitmapHistory[operatorIds[i]][length - j - 1].nextUpdateBlockNumber; From c496d5e1185de78313a35af74637a9bd24c0d45a Mon Sep 17 00:00:00 2001 From: wadealexc Date: Wed, 25 Oct 2023 14:32:39 +0000 Subject: [PATCH 016/101] test: fix broken tests for StakeRegistry changes generally i just removed testing of specific stake histories in favor of testing net outcomes. we can revisit these tests in a few weeks. --- test/unit/StakeRegistryUnit.t.sol | 91 +++++++++---------------------- 1 file changed, 27 insertions(+), 64 deletions(-) diff --git a/test/unit/StakeRegistryUnit.t.sol b/test/unit/StakeRegistryUnit.t.sol index ae9ef19f..4bf5c5bc 100644 --- a/test/unit/StakeRegistryUnit.t.sol +++ b/test/unit/StakeRegistryUnit.t.sol @@ -299,39 +299,39 @@ contract StakeRegistryUnitTests is Test { // 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]; - } + } + + uint historyLength = stakeRegistry.getLengthOfTotalStakeHistoryForQuorum(i); + + // If we don't have stake history, it should be because there is no stake + if (historyLength == 0) { + assertEq(cumulativeStake, 0); + continue; + } + + // make sure that the stake update is as expected + IStakeRegistry.OperatorStakeUpdate memory totalStakeUpdate = + stakeRegistry.getTotalStakeUpdateForQuorumFromIndex(i, historyLength-1); + + 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 (historyLength >= 2) { + IStakeRegistry.OperatorStakeUpdate memory prevTotalStakeUpdate = + stakeRegistry.getTotalStakeUpdateForQuorumFromIndex(i, historyLength-2); + assertEq(prevTotalStakeUpdate.nextUpdateBlockNumber, cumulativeBlockNumber); + } } } @@ -452,27 +452,9 @@ contract StakeRegistryUnitTests is Test { 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); + IStakeRegistry.OperatorStakeUpdate memory lastOperatorStakeUpdate = stakeRegistry.getMostRecentStakeUpdateByOperatorId(defaultOperatorId, defaultQuorumNumber); assertEq(lastOperatorStakeUpdate.stake, stakes[blocksPassed.length - 1]); - assertEq(lastOperatorStakeUpdate.updateBlockNumber, cumulativeBlockNumber); assertEq(lastOperatorStakeUpdate.nextUpdateBlockNumber, uint32(0)); } @@ -492,38 +474,19 @@ contract StakeRegistryUnitTests is Test { } else { stakeDelta = _calculateDelta({prev: stakes[i-1], cur: stakes[i]}); } - - // Get previous history length and total stake update - uint prevHistoryLength = stakeRegistry.getLengthOfTotalStakeHistoryForQuorum(defaultQuorumNumber); - IStakeRegistry.OperatorStakeUpdate memory prevStakeUpdate; - if (prevHistoryLength != 0) { - prevStakeUpdate = stakeRegistry.getTotalStakeUpdateForQuorumFromIndex(defaultQuorumNumber, prevHistoryLength-1); - } // Perform the update stakeRegistry.recordTotalStakeUpdate(defaultQuorumNumber, stakeDelta); - - // Get new history length and total stake update - uint newHistoryLength = stakeRegistry.getLengthOfTotalStakeHistoryForQuorum(defaultQuorumNumber); + IStakeRegistry.OperatorStakeUpdate memory newStakeUpdate; - if (newHistoryLength != 0) { - newStakeUpdate = stakeRegistry.getTotalStakeUpdateForQuorumFromIndex(defaultQuorumNumber, newHistoryLength-1); + uint historyLength = stakeRegistry.getLengthOfTotalStakeHistoryForQuorum(defaultQuorumNumber); + if (historyLength != 0) { + newStakeUpdate = stakeRegistry.getTotalStakeUpdateForQuorumFromIndex(defaultQuorumNumber, historyLength-1); } - // Check that the most recent entry reflects the correct stake assertEq(newStakeUpdate.stake, stakes[i]); - if (stakeDelta == 0) { - // Check that no update occurred - assertEq(prevHistoryLength, newHistoryLength); - assertEq(prevStakeUpdate.stake, newStakeUpdate.stake); - assertEq(prevStakeUpdate.updateBlockNumber, newStakeUpdate.updateBlockNumber); - assertEq(prevStakeUpdate.nextUpdateBlockNumber, newStakeUpdate.nextUpdateBlockNumber); - } else { - assertEq(newStakeUpdate.updateBlockNumber, cumulativeBlockNumber); - } - cumulativeBlockNumber += blocksPassed; cheats.roll(cumulativeBlockNumber); } From a8d0e208130b0c30eff0022a8cc5aceb7f165654 Mon Sep 17 00:00:00 2001 From: wadealexc Date: Thu, 26 Oct 2023 16:18:20 +0000 Subject: [PATCH 017/101] refactor: wip refactor to move "createQuorum" to the registry coordinator --- src/BLSPubkeyRegistry.sol | 42 +++- src/BLSRegistryCoordinatorWithIndices.sol | 101 ++++++--- src/IndexRegistry.sol | 34 ++- src/StakeRegistry.sol | 259 +++++++++++++++++----- src/VoteWeigherBase.sol | 226 ------------------- src/VoteWeigherBaseStorage.sol | 17 +- test/harnesses/StakeRegistryHarness.sol | 19 +- test/unit/VoteWeigherBaseUnit.t.sol | 28 +-- 8 files changed, 355 insertions(+), 371 deletions(-) delete mode 100644 src/VoteWeigherBase.sol diff --git a/src/BLSPubkeyRegistry.sol b/src/BLSPubkeyRegistry.sol index 804a4c35..999a8183 100644 --- a/src/BLSPubkeyRegistry.sol +++ b/src/BLSPubkeyRegistry.sol @@ -94,6 +94,20 @@ contract BLSPubkeyRegistry is BLSPubkeyRegistryStorage { emit OperatorRemovedFromQuorums(operator, quorumNumbers); } + /** + * @notice Creates a new quorum by pushing its first apk update + * @param quorumNumber The number of the new quorum + */ + function createQuorum(uint8 quorumNumber) public virtual onlyRegistryCoordinator { + require(quorumApkUpdates[quorumNumber].length == 0, "BLSPubkeyRegistry.createQuorum: quorum already exists"); + + quorumApkUpdates[quorumNumber].push(ApkUpdate({ + apkHash: bytes24(0), + updateBlockNumber: uint32(block.number), + nextUpdateBlockNumber: 0 + })); + } + /******************************************************************************* INTERNAL FUNCTIONS *******************************************************************************/ @@ -104,21 +118,24 @@ contract BLSPubkeyRegistry is BLSPubkeyRegistryStorage { 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); - } + // Validate quorumNumber + uint256 historyLength = quorumApkUpdates[quorumNumber].length; + require(historyLength != 0, "BLSPubkeyRegistry._processQuorumApkUpdate: quorum does not exist"); - apkAfterUpdate = quorumApk[quorumNumber].plus(point); + // Update the last entry to point at the current block + // TODO - if the last entry was made in this block, update the entry instead + quorumApkUpdates[quorumNumber][historyLength - 1].nextUpdateBlockNumber = uint32(block.number); - //update aggregate public key for this quorum + // Update aggregate public key for this quorum + apkAfterUpdate = quorumApk[quorumNumber].plus(point); 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); + + // Push update to history + quorumApkUpdates[quorumNumber].push(ApkUpdate({ + apkHash: bytes24(BN254.hashG1Point(apkAfterUpdate)), + updateBlockNumber: uint32(block.number), + nextUpdateBlockNumber: 0 + })); unchecked { ++i; @@ -126,6 +143,7 @@ contract BLSPubkeyRegistry is BLSPubkeyRegistryStorage { } } + // TODO - should this fail if apkUpdate.apkHash == 0? This will be the case for the first entry in each quorum function _validateApkHashForQuorumAtBlockNumber(ApkUpdate memory apkUpdate, uint32 blockNumber) internal pure { require( blockNumber >= apkUpdate.updateBlockNumber, diff --git a/src/BLSRegistryCoordinatorWithIndices.sol b/src/BLSRegistryCoordinatorWithIndices.sol index b810518d..e1416c9e 100644 --- a/src/BLSRegistryCoordinatorWithIndices.sol +++ b/src/BLSRegistryCoordinatorWithIndices.sol @@ -12,17 +12,15 @@ import "eigenlayer-contracts/src/contracts/permissions/Pausable.sol"; import "./interfaces/IBLSRegistryCoordinatorWithIndices.sol"; import "./interfaces/ISocketUpdater.sol"; import "./interfaces/IServiceManager.sol"; -import "./interfaces/IBLSPubkeyRegistry.sol"; +import "./BLSPubkeyRegistry.sol"; import "./interfaces/IVoteWeigher.sol"; -import "./interfaces/IStakeRegistry.sol"; -import "./interfaces/IIndexRegistry.sol"; +import "./StakeRegistry.sol"; +import "./IndexRegistry.sol"; import "./interfaces/IRegistryCoordinator.sol"; import "./libraries/BitmapUtils.sol"; import "./libraries/BN254.sol"; - - /** * @title A `RegistryCoordinator` that has three registries: * 1) a `StakeRegistry` that keeps track of operators' stakes @@ -45,18 +43,22 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr uint8 internal constant PAUSED_REGISTER_OPERATOR = 0; /// @notice Index for flag that pauses operator deregistration uint8 internal constant PAUSED_DEREGISTER_OPERATOR = 1; + /// @notice The maximum number of quorums this contract supports + uint8 internal constant MAX_QUORUM_COUNT = 192; /// @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; + BLSPubkeyRegistry public immutable blsPubkeyRegistry; /// @notice the Stake Registry contract that will keep track of operators' stakes - IStakeRegistry public immutable stakeRegistry; + StakeRegistry public immutable stakeRegistry; /// @notice the Index Registry contract that will keep track of operators' indexes - IIndexRegistry public immutable indexRegistry; + IndexRegistry public immutable indexRegistry; + /// @notice the current number of quorums supported by the registry coordinator + uint8 public quorumCount; /// @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 @@ -83,6 +85,14 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr _; } + modifier quorumExists(uint8 quorumNumber) { + require( + quorumNumber < quorumCount, + "BLSRegistryCoordinatorWithIndices.quorumExists: quorum does not exist" + ); + _; + } + constructor( ISlasher _slasher, IServiceManager _serviceManager, @@ -100,25 +110,30 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr function initialize( address _churnApprover, address _ejector, - OperatorSetParam[] memory _operatorSetParams, IPauserRegistry _pauserRegistry, - uint256 _initialPausedStatus + uint256 _initialPausedStatus, + OperatorSetParam[] memory _operatorSetParams, + uint96[] memory _minimumStakes, + IVoteWeigher.StrategyAndWeightingMultiplier[][] memory _strategyParams ) external initializer { - // set initial paused status + require( + _operatorSetParams.length == _minimumStakes.length && _minimumStakes.length == _strategyParams.length, + "BLSRegistryCoordinatorWithIndices.initialize: input length mismatch" + ); + + // Initialize roles _initializePauser(_pauserRegistry, _initialPausedStatus); - // set the churnApprover _setChurnApprover(_churnApprover); - // set the ejector _setEjector(_ejector); - // add registry contracts to the registries array + + // 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]); + // Create quorums + for (uint256 i = 0; i < _operatorSetParams.length; i++) { + _createQuorum(_operatorSetParams[i], _minimumStakes[i], _strategyParams[i]); } } @@ -323,16 +338,27 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr *******************************************************************************/ /** - * @notice Sets parameters of the operator set for the given `quorumNumber` + * @notice Creates a quorum and initializes it in each registry contract + */ + function createQuorum( + OperatorSetParam memory operatorSetParams, + uint96 minimumStake, + IVoteWeigher.StrategyAndWeightingMultiplier[] memory strategyParams + ) external virtual onlyServiceManagerOwner { + _createQuorum(operatorSetParams, minimumStake, strategyParams); + } + + /** + * @notice Updates a quorum's OperatorSetParams * @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` + * @param operatorSetParams 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); + OperatorSetParam memory operatorSetParams + ) external onlyServiceManagerOwner quorumExists(quorumNumber) { + _setOperatorSetParams(quorumNumber, operatorSetParams); } /** @@ -517,9 +543,32 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr EIP1271SignatureUtils.checkSignature_EIP1271(churnApprover, calculateOperatorChurnApprovalDigestHash(registeringOperatorId, operatorKickParams, signatureWithSaltAndExpiry.salt, signatureWithSaltAndExpiry.expiry), signatureWithSaltAndExpiry.signature); } - function _setOperatorSetParams(uint8 quorumNumber, OperatorSetParam memory operatorSetParam) internal { - _quorumOperatorSetParams[quorumNumber] = operatorSetParam; - emit OperatorSetParamsUpdated(quorumNumber, operatorSetParam); + /** + * @notice Creates and initializes a quorum in each registry contract + */ + function _createQuorum( + OperatorSetParam memory operatorSetParams, + uint96 minimumStake, + IVoteWeigher.StrategyAndWeightingMultiplier[] memory strategyParams + ) internal { + // Increment the total quorum count. Fails if we're already at the max + uint8 prevQuorumCount = quorumCount; + require(prevQuorumCount < MAX_QUORUM_COUNT, "BLSRegistryCoordinatorWithIndicies.createQuorum: max quorums reached"); + quorumCount = prevQuorumCount + 1; + + // The previous count is the new quorum's number + uint8 quorumNumber = prevQuorumCount; + + // Initialize the quorum here and in each registry + _setOperatorSetParams(quorumNumber, operatorSetParams); + stakeRegistry.createQuorum(quorumNumber, minimumStake, strategyParams); + indexRegistry.createQuorum(quorumNumber); + blsPubkeyRegistry.createQuorum(quorumNumber); + } + + function _setOperatorSetParams(uint8 quorumNumber, OperatorSetParam memory operatorSetParams) internal { + _quorumOperatorSetParams[quorumNumber] = operatorSetParams; + emit OperatorSetParamsUpdated(quorumNumber, operatorSetParams); } function _setChurnApprover(address newChurnApprover) internal { diff --git a/src/IndexRegistry.sol b/src/IndexRegistry.sol index 58e824c6..122a8cde 100644 --- a/src/IndexRegistry.sol +++ b/src/IndexRegistry.sol @@ -45,9 +45,12 @@ contract IndexRegistry is IndexRegistryStorage { 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].numOperators : 0; + // Check that the quorum exists + uint256 historyLength = _totalOperatorsHistory[quorumNumber].length; + require(historyLength != 0, "IndexRegistry.registerOperator: quorum does not exist"); + + // Get the number of operators currently in the quorum, which will be operator's new index + uint32 numOperators = historyLength > 0 ? _totalOperatorsHistory[quorumNumber][historyLength - 1].numOperators : 0; _updateOperatorIdToIndexHistory({ operatorId: operatorId, quorumNumber: quorumNumber, @@ -89,11 +92,24 @@ contract IndexRegistry is IndexRegistryStorage { }); _updateTotalOperatorHistory({ quorumNumber: quorumNumber, - numOperators: _totalOperatorsHistory[quorumNumber][_totalOperatorsHistory[quorumNumber].length - 1].numOperators - 1 + numOperators: _totalOperatorsHistory[quorumNumber][historyLength - 1].numOperators - 1 }); } } + /** + * @notice Creates a new quorum by pushing its first quorum update + * @param quorumNumber The number of the new quorum + */ + function createQuorum(uint8 quorumNumber) public virtual onlyRegistryCoordinator { + require(_totalOperatorsHistory[quorumNumber].length == 0, "IndexRegistry.createQuorum: quorum already exists"); + + _totalOperatorsHistory[quorumNumber].push(QuorumUpdate({ + numOperators: 0, + fromBlockNumber: uint32(block.number) + })); + } + /******************************************************************************* INTERNAL FUNCTIONS *******************************************************************************/ @@ -104,12 +120,10 @@ contract IndexRegistry is IndexRegistryStorage { * @param numOperators is the number of operators in the quorum */ function _updateTotalOperatorHistory(uint8 quorumNumber, uint32 numOperators) internal { - QuorumUpdate memory quorumUpdate; - // In the case of totalOperatorsHistory, the index parameter is the number of operators in the quorum - quorumUpdate.numOperators = numOperators; - quorumUpdate.fromBlockNumber = uint32(block.number); - - _totalOperatorsHistory[quorumNumber].push(quorumUpdate); + _totalOperatorsHistory[quorumNumber].push(QuorumUpdate({ + numOperators: numOperators, + fromBlockNumber: uint32(block.number) + })); } /** diff --git a/src/StakeRegistry.sol b/src/StakeRegistry.sol index 61cf5ee9..51d28bf0 100644 --- a/src/StakeRegistry.sol +++ b/src/StakeRegistry.sol @@ -1,14 +1,13 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity =0.8.12; +import "@openzeppelin/contracts/utils/math/Math.sol"; +import "./libraries/BitmapUtils.sol"; import "./interfaces/IServiceManager.sol"; import "./interfaces/IStakeRegistry.sol"; import "./interfaces/IRegistryCoordinator.sol"; - -import "./libraries/BitmapUtils.sol"; - import "./StakeRegistryStorage.sol"; -import {VoteWeigherBase} from "./VoteWeigherBase.sol"; +import "./VoteWeigherBaseStorage.sol"; /** * @title A `Registry` that keeps track of stakes of operators for up to 256 quorums. @@ -19,8 +18,8 @@ import {VoteWeigherBase} from "./VoteWeigherBase.sol"; * 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 VoteWeigherBase, StakeRegistryStorage { - /// @notice requires that the caller is the RegistryCoordinator +contract StakeRegistry is VoteWeigherBaseStorage, StakeRegistryStorage { + modifier onlyRegistryCoordinator() { require( msg.sender == address(registryCoordinator), @@ -29,42 +28,21 @@ contract StakeRegistry is VoteWeigherBase, StakeRegistryStorage { _; } + modifier onlyServiceManagerOwner() { + require(msg.sender == serviceManager.owner(), "StakeRegistry.onlyServiceManagerOwner: caller is not the owner of the serviceManager"); + _; + } + + modifier quorumExists(uint8 quorumNumber) { + require(_totalStakeHistory[quorumNumber].length != 0, "StakeRegistry.quorumExists: quorum does not exist"); + _; + } + constructor( IRegistryCoordinator _registryCoordinator, - IStrategyManager _strategyManager, + IDelegationManager _delegationManager, IServiceManager _serviceManager - ) VoteWeigherBase(_strategyManager, _serviceManager) StakeRegistryStorage(_registryCoordinator) {} - - /** - * @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; - } - } - } + ) VoteWeigherBaseStorage(_delegationManager, _serviceManager) StakeRegistryStorage(_registryCoordinator) {} /******************************************************************************* EXTERNAL FUNCTIONS @@ -79,9 +57,14 @@ contract StakeRegistry is VoteWeigherBase, StakeRegistryStorage { // 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 + uint8 quorumCount = registryCoordinator.quorumCount(); for (uint8 quorumNumber = 0; quorumNumber < quorumCount; ) { int256 totalStakeDelta; + // TODO - not a huge fan of this dependency on the reg coord, but i do prefer this + // over the stakereg also keeping its own count. + require(_totalStakeHistory[quorumNumber].length != 0, "StakeRegistry.updateStakes: quorum does not exist"); + for (uint256 i = 0; i < operators.length; ) { bytes32 operatorId = registryCoordinator.getOperatorId(operators[i]); uint192 quorumBitmap = registryCoordinator.getCurrentQuorumBitmapByOperatorId(operatorId); @@ -134,20 +117,18 @@ contract StakeRegistry is VoteWeigherBase, StakeRegistryStorage { bytes32 operatorId, bytes calldata quorumNumbers ) public virtual onlyRegistryCoordinator { - // 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" - ); for (uint256 i = 0; i < quorumNumbers.length; ) { + + uint8 quorumNumber = uint8(quorumNumbers[i]); + require(_totalStakeHistory[quorumNumber].length != 0, "StakeRegistry.registerOperator: quorum does not exist"); + /** * Update the operator's stake for the quorum and retrieve their current stake * as well as the change in stake. * - If this method returns `hasMinimumStake == false`, the operator has not met * the minimum stake requirement for this quorum */ - uint8 quorumNumber = uint8(quorumNumbers[i]); (int256 stakeDelta, bool hasMinimumStake) = _updateOperatorStake({ operator: operator, operatorId: operatorId, @@ -155,7 +136,7 @@ contract StakeRegistry is VoteWeigherBase, StakeRegistryStorage { }); require( hasMinimumStake, - "StakeRegistry._registerOperator: Operator does not meet minimum stake requirement for quorum" + "StakeRegistry.registerOperator: Operator does not meet minimum stake requirement for quorum" ); // Update this quorum's total stake @@ -187,8 +168,10 @@ contract StakeRegistry is VoteWeigherBase, StakeRegistryStorage { * the quorum's total stake to account for the removal */ for (uint256 i = 0; i < quorumNumbers.length; ) { - // Update the operator's stake for the quorum and retrieve the shares removed uint8 quorumNumber = uint8(quorumNumbers[i]); + require(_totalStakeHistory[quorumNumber].length != 0, "StakeRegistry.deregisterOperator: quorum does not exist"); + + // Update the operator's stake for the quorum and retrieve the shares removed int256 stakeDelta = _recordOperatorStakeUpdate({ operatorId: operatorId, quorumNumber: quorumNumber, @@ -204,15 +187,85 @@ contract StakeRegistry is VoteWeigherBase, StakeRegistryStorage { } } - /******************************************************************************* - EXTERNAL FUNCTIONS - SERVICE MANAGER OWNER - *******************************************************************************/ + /// @notice Create a new quorum and add the strategies and their associated weights to the quorum. + function createQuorum( + uint8 quorumNumber, + uint96 minimumStake, + StrategyAndWeightingMultiplier[] memory strategyParams + ) public virtual onlyRegistryCoordinator { + require(_totalStakeHistory[quorumNumber].length == 0, "StakeRegistry.createQuorum: quorum already exists"); + _addStrategyParams(quorumNumber, strategyParams); + _setMinimumStakeForQuorum(quorumNumber, minimumStake); + } - /// @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 { + function setMinimumStakeForQuorum( + uint8 quorumNumber, + uint96 minimumStake + ) public virtual onlyServiceManagerOwner quorumExists(quorumNumber) { _setMinimumStakeForQuorum(quorumNumber, minimumStake); } + /** + * @notice Adds strategies and weights to the 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 addStrategyParams( + uint8 quorumNumber, + StrategyAndWeightingMultiplier[] memory strategyParams + ) public virtual onlyServiceManagerOwner quorumExists(quorumNumber) { + _addStrategyParams(quorumNumber, strategyParams); + } + + /** + * @notice Remove strategies and their associated weights from the quorum's considered strategies + * @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 strategies to remove + */ + function removeStrategyParams( + uint8 quorumNumber, + uint256[] memory indicesToRemove + ) public virtual onlyServiceManagerOwner quorumExists(quorumNumber) { + uint256 toRemoveLength = indicesToRemove.length; + require(toRemoveLength > 0, "StakeRegistry.removeStrategyParams: no indices to remove provided"); + + StrategyAndWeightingMultiplier[] storage strategyParams = strategiesConsideredAndMultipliers[quorumNumber]; + + for (uint256 i = 0; i < toRemoveLength; i++) { + emit StrategyRemovedFromQuorum(quorumNumber, strategyParams[indicesToRemove[i]].strategy); + emit StrategyMultiplierUpdated(quorumNumber, strategyParams[indicesToRemove[i]].strategy, 0); + + // Replace index to remove with the last item in the list, then pop the last item + strategyParams[indicesToRemove[i]] = strategyParams[strategyParams.length - 1]; + strategyParams.pop(); + } + } + + /** + * @notice Modifys the weights of existing strategies for a specific quorum + * @param quorumNumber is the quorum number to which the strategies belong + * @param strategyIndices are the indices of the strategies to change + * @param newMultipliers are the new multipliers for the strategies + */ + function modifyStrategyParams( + uint8 quorumNumber, + uint256[] calldata strategyIndices, + uint96[] calldata newMultipliers + ) public virtual onlyServiceManagerOwner quorumExists(quorumNumber) { + uint256 numStrats = strategyIndices.length; + require(numStrats > 0, "StakeRegistry.modifyStrategyParams: no strategy indices provided"); + require(newMultipliers.length == numStrats, "StakeRegistry.modifyStrategyParams: input length mismatch"); + + StrategyAndWeightingMultiplier[] storage strategyParams = strategiesConsideredAndMultipliers[quorumNumber]; + + for (uint256 i = 0; i < numStrats; i++) { + // Change the strategy's associated multiplier + strategyParams[strategyIndices[i]].multiplier = newMultipliers[i]; + emit StrategyMultiplierUpdated(quorumNumber, strategyParams[strategyIndices[i]].strategy, newMultipliers[i]); + } + } + /******************************************************************************* INTERNAL FUNCTIONS *******************************************************************************/ @@ -259,7 +312,7 @@ contract StakeRegistry is VoteWeigherBase, StakeRegistryStorage { * Get the operator's current stake for the quorum. If their stake * is below the quorum's threshold, set their stake to 0 */ - uint96 currentStake = weightOfOperatorForQuorum(quorumNumber, operator); + uint96 currentStake = _weightOfOperatorForQuorum(quorumNumber, operator); if (currentStake < minimumStakeForQuorum[quorumNumber]) { currentStake = uint96(0); } else { @@ -360,6 +413,52 @@ contract StakeRegistry is VoteWeigherBase, StakeRegistryStorage { } } + /** + * @notice Adds `strategyParams` 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 _addStrategyParams( + uint8 quorumNumber, + StrategyAndWeightingMultiplier[] memory strategyParams + ) internal { + require(strategyParams.length > 0, "StakeRegistry._addStrategyParams: no strategies provided"); + uint256 numStratsToAdd = strategyParams.length; + uint256 numStratsExisting = strategiesConsideredAndMultipliers[quorumNumber].length; + require( + numStratsExisting + numStratsToAdd <= MAX_WEIGHING_FUNCTION_LENGTH, + "StakeRegistry._addStrategyParams: 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 != + strategyParams[i].strategy, + "StakeRegistry._addStrategyParams: cannot add same strategy 2x" + ); + unchecked { + ++j; + } + } + require( + strategyParams[i].multiplier > 0, + "StakeRegistry._addStrategyParams: cannot add strategy with zero weight" + ); + strategiesConsideredAndMultipliers[quorumNumber].push(strategyParams[i]); + emit StrategyAddedToQuorum(quorumNumber, strategyParams[i].strategy); + emit StrategyMultiplierUpdated( + quorumNumber, + strategyParams[i].strategy, + strategyParams[i].multiplier + ); + unchecked { + ++i; + } + } + } + /// @notice Returns the change between a previous and current value as a signed int function _calculateDelta(uint96 prev, uint96 cur) internal pure returns (int256) { return int256(uint256(cur)) - int256(uint256(prev)); @@ -389,10 +488,64 @@ contract StakeRegistry is VoteWeigherBase, StakeRegistryStorage { ); } + /** + * @notice This function computes the total weight of the @param operator in the quorum @param quorumNumber. + * @dev this method DOES NOT check that the quorum exists + */ + function _weightOfOperatorForQuorum(uint8 quorumNumber, address operator) internal view 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; + } + /******************************************************************************* VIEW FUNCTIONS *******************************************************************************/ + /** + * @notice This function computes the total weight of the @param operator in the quorum @param quorumNumber. + * @dev reverts if the quorum does not exist + */ + function weightOfOperatorForQuorum( + uint8 quorumNumber, + address operator + ) public virtual view quorumExists(quorumNumber) returns (uint96) { + return _weightOfOperatorForQuorum(quorumNumber, operator); + } + + /// @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 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 Returns the entire `operatorIdToStakeHistory[operatorId][quorumNumber]` array. * @param operatorId The id of the operator of interest. diff --git a/src/VoteWeigherBase.sol b/src/VoteWeigherBase.sol deleted file mode 100644 index 1ed48392..00000000 --- a/src/VoteWeigherBase.sol +++ /dev/null @@ -1,226 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity =0.8.12; - -import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import "eigenlayer-contracts/src/contracts/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) {} - - /******************************************************************************* - EXTERNAL FUNCTIONS - SERVICE MANAGER OWNER - *******************************************************************************/ - - /// @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; - } - } - } - - /******************************************************************************* - INTERNAL FUNCTIONS - *******************************************************************************/ - - /** - * @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; - } - } - } - - /******************************************************************************* - VIEW FUNCTIONS - *******************************************************************************/ - - /// @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 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 weightOfOperatorForQuorum( - 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; - } -} diff --git a/src/VoteWeigherBaseStorage.sol b/src/VoteWeigherBaseStorage.sol index 81d4d243..2058e44a 100644 --- a/src/VoteWeigherBaseStorage.sol +++ b/src/VoteWeigherBaseStorage.sol @@ -21,24 +21,13 @@ abstract contract VoteWeigherBaseStorage is Initializable, IVoteWeigher { 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 @@ -46,12 +35,10 @@ abstract contract VoteWeigherBaseStorage is Initializable, IVoteWeigher { mapping(uint8 => StrategyAndWeightingMultiplier[]) public strategiesConsideredAndMultipliers; constructor( - IStrategyManager _strategyManager, + IDelegationManager _delegationManager, IServiceManager _serviceManager ) { - strategyManager = _strategyManager; - delegation = _strategyManager.delegation(); - slasher = _strategyManager.slasher(); + delegation = _delegationManager; serviceManager = _serviceManager; // disable initializers so that the implementation contract cannot be initialized _disableInitializers(); diff --git a/test/harnesses/StakeRegistryHarness.sol b/test/harnesses/StakeRegistryHarness.sol index 48b93214..d95d7bfc 100644 --- a/test/harnesses/StakeRegistryHarness.sol +++ b/test/harnesses/StakeRegistryHarness.sol @@ -5,7 +5,7 @@ import "../../src/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; + mapping(uint8 => mapping(address => uint96)) private __weightOfOperatorForQuorum; constructor( IRegistryCoordinator _registryCoordinator, @@ -28,31 +28,28 @@ contract StakeRegistryHarness is StakeRegistry { // 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]; + 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; + __weightOfOperatorForQuorum[quorumNumber][operator] = weight; } // mocked function to register an operator without having to mock other elements // This is just a copy/paste from `registerOperator`, since that no longer uses an internal method function registerOperatorNonCoordinator(address operator, bytes32 operatorId, bytes calldata quorumNumbers) external { - // 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" - ); - for (uint256 i = 0; i < quorumNumbers.length; ) { + + uint8 quorumNumber = uint8(quorumNumbers[i]); + require(_totalStakeHistory[quorumNumber].length != 0, "StakeRegistry.registerOperator: quorum does not exist"); + /** * Update the operator's stake for the quorum and retrieve their current stake * as well as the change in stake. * - If this method returns `hasMinimumStake == false`, the operator has not met * the minimum stake requirement for this quorum */ - uint8 quorumNumber = uint8(quorumNumbers[i]); (int256 stakeDelta, bool hasMinimumStake) = _updateOperatorStake({ operator: operator, operatorId: operatorId, @@ -60,7 +57,7 @@ contract StakeRegistryHarness is StakeRegistry { }); require( hasMinimumStake, - "StakeRegistry._registerOperator: Operator does not meet minimum stake requirement for quorum" + "StakeRegistry.registerOperator: Operator does not meet minimum stake requirement for quorum" ); // Update this quorum's total stake diff --git a/test/unit/VoteWeigherBaseUnit.t.sol b/test/unit/VoteWeigherBaseUnit.t.sol index 3894da78..7b393e87 100644 --- a/test/unit/VoteWeigherBaseUnit.t.sol +++ b/test/unit/VoteWeigherBaseUnit.t.sol @@ -11,9 +11,9 @@ import {IEigenPodManager} from "eigenlayer-contracts/src/contracts/interfaces/IE import {ISlasher} from "eigenlayer-contracts/src/contracts/interfaces/ISlasher.sol"; import {IServiceManager} from "../../src/interfaces/IServiceManager.sol"; import {IVoteWeigher} from "../../src/interfaces/IVoteWeigher.sol"; -import {VoteWeigherBase} from "../../src/VoteWeigherBase.sol"; +import {StakeRegistry} from "../../src/StakeRegistry.sol"; -import {StrategyManagerMock} from "eigenlayer-contracts/src/test/mocks/StrategyManagerMock.sol"; +import {RegistryCoordinatorMock} from "../mocks/RegistryCoordinatorMock.sol"; import {OwnableMock} from "eigenlayer-contracts/src/test/mocks/OwnableMock.sol"; import {DelegationManagerMock} from "eigenlayer-contracts/src/test/mocks/DelegationManagerMock.sol"; @@ -24,16 +24,16 @@ contract VoteWeigherBaseUnitTests is Test { ProxyAdmin public proxyAdmin; PauserRegistry public pauserRegistry; - IStrategyManager public strategyManager; address serviceManagerOwner; IServiceManager public serviceManager; DelegationManagerMock delegationMock; + RegistryCoordinatorMock registryCoordinatorMock; - VoteWeigherBase public voteWeigher; + StakeRegistry public voteWeigher; - VoteWeigherBase public voteWeigherImplementation; + StakeRegistry public voteWeigherImplementation; address public pauser = address(555); address public unpauser = address(999); @@ -70,31 +70,23 @@ contract VoteWeigherBaseUnitTests is Test { pausers[0] = pauser; pauserRegistry = new PauserRegistry(pausers, unpauser); - StrategyManagerMock strategyManagerMock = new StrategyManagerMock(); + registryCoordinatorMock = new RegistryCoordinatorMock(); 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); + voteWeigherImplementation = new StakeRegistry(registryCoordinatorMock, delegationMock, serviceManager); - voteWeigher = VoteWeigherBase(address(new TransparentUpgradeableProxy(address(voteWeigherImplementation), address(proxyAdmin), ""))); + voteWeigher = StakeRegistry(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.registryCoordinator()), address(registryCoordinatorMock)); + assertEq(address(voteWeigherImplementation.delegation()), address(delegationMock)); assertEq(address(voteWeigherImplementation.serviceManager()), address(serviceManager)); } From acdd56a797b96eecfdfad6f2f2c4668faff49e37 Mon Sep 17 00:00:00 2001 From: wadealexc Date: Tue, 24 Oct 2023 15:24:13 +0000 Subject: [PATCH 018/101] style: use for loop indices when the index is just an index --- src/BLSRegistryCoordinatorWithIndices.sol | 4 +- src/StakeRegistry.sol | 50 +++++++++++++++++------ 2 files changed, 40 insertions(+), 14 deletions(-) diff --git a/src/BLSRegistryCoordinatorWithIndices.sol b/src/BLSRegistryCoordinatorWithIndices.sol index e1416c9e..d4b2131a 100644 --- a/src/BLSRegistryCoordinatorWithIndices.sol +++ b/src/BLSRegistryCoordinatorWithIndices.sol @@ -617,8 +617,8 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr ) external view returns (uint32[] memory) { uint32[] memory indices = new uint32[](operatorIds.length); for (uint256 i = 0; i < operatorIds.length; i++) { - uint256 length = _operatorIdToQuorumBitmapHistory[operatorIds[i]].length; - for (uint256 j = 0; j < length; j++) { + uint length = _operatorIdToQuorumBitmapHistory[operatorIds[i]].length; + for (uint j = 0; j < length; j++) { if (_operatorIdToQuorumBitmapHistory[operatorIds[i]][length - j - 1].updateBlockNumber <= blockNumber) { uint32 nextUpdateBlockNumber = _operatorIdToQuorumBitmapHistory[operatorIds[i]][length - j - 1].nextUpdateBlockNumber; diff --git a/src/StakeRegistry.sol b/src/StakeRegistry.sol index 51d28bf0..dc8b77b5 100644 --- a/src/StakeRegistry.sol +++ b/src/StakeRegistry.sol @@ -117,19 +117,21 @@ contract StakeRegistry is VoteWeigherBaseStorage, StakeRegistryStorage { bytes32 operatorId, bytes calldata quorumNumbers ) public virtual onlyRegistryCoordinator { - - for (uint256 i = 0; i < quorumNumbers.length; ) { - + // 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 (uint i = 0; i < quorumNumbers.length; ) { + // get the next quorumNumber uint8 quorumNumber = uint8(quorumNumbers[i]); - require(_totalStakeHistory[quorumNumber].length != 0, "StakeRegistry.registerOperator: quorum does not exist"); - - /** - * Update the operator's stake for the quorum and retrieve their current stake - * as well as the change in stake. - * - If this method returns `hasMinimumStake == false`, the operator has not met - * the minimum stake requirement for this quorum - */ - (int256 stakeDelta, bool hasMinimumStake) = _updateOperatorStake({ + // 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: operator, operatorId: operatorId, quorumNumber: quorumNumber @@ -163,6 +165,7 @@ contract StakeRegistry is VoteWeigherBaseStorage, StakeRegistryStorage { bytes32 operatorId, bytes calldata quorumNumbers ) public virtual onlyRegistryCoordinator { +<<<<<<< HEAD /** * For each quorum, remove the operator's stake for the quorum and update * the quorum's total stake to account for the removal @@ -173,6 +176,19 @@ contract StakeRegistry is VoteWeigherBaseStorage, StakeRegistryStorage { // Update the operator's stake for the quorum and retrieve the shares removed int256 stakeDelta = _recordOperatorStakeUpdate({ +======= + 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 (uint i = 0; i < quorumNumbers.length; ) { + uint8 quorumNumber = uint8(quorumNumbers[i]); + // update the operator's stake + uint96 stakeBeforeUpdate = _recordOperatorStakeUpdate({ +>>>>>>> 095a082 (style: use for loop indices when the index is just an index) operatorId: operatorId, quorumNumber: quorumNumber, newStake: 0 @@ -275,8 +291,13 @@ contract StakeRegistry is VoteWeigherBaseStorage, StakeRegistryStorage { uint8 quorumNumber, uint32 blockNumber ) internal view returns (uint32) { +<<<<<<< HEAD uint256 length = operatorIdToStakeHistory[operatorId][quorumNumber].length; for (uint256 i = 0; i < length; i++) { +======= + uint length = operatorIdToStakeHistory[operatorId][quorumNumber].length; + for (uint i = 0; i < length; i++) { +>>>>>>> 095a082 (style: use for loop indices when the index is just an index) if (operatorIdToStakeHistory[operatorId][quorumNumber][length - i - 1].updateBlockNumber <= blockNumber) { uint32 nextUpdateBlockNumber = operatorIdToStakeHistory[operatorId][quorumNumber][length - i - 1].nextUpdateBlockNumber; @@ -611,8 +632,13 @@ contract StakeRegistry is VoteWeigherBaseStorage, StakeRegistryStorage { _totalStakeHistory[quorumNumber][0].updateBlockNumber <= blockNumber, "StakeRegistry.getTotalStakeIndicesByQuorumNumbersAtBlockNumber: quorum has no stake history at blockNumber" ); +<<<<<<< HEAD uint256 length = _totalStakeHistory[quorumNumber].length; for (uint256 j = 0; j < length; j++) { +======= + uint length = _totalStakeHistory[quorumNumber].length; + for (uint j = 0; j < length; j++) { +>>>>>>> 095a082 (style: use for loop indices when the index is just an index) if (_totalStakeHistory[quorumNumber][length - j - 1].updateBlockNumber <= blockNumber) { indices[i] = uint32(length - j - 1); break; From 32978eabaa76578aba5cff55e7e60484c0b48dde Mon Sep 17 00:00:00 2001 From: wadealexc Date: Tue, 24 Oct 2023 18:40:38 +0000 Subject: [PATCH 019/101] refactor: Use a signed delta value in StakeRegistry to remove tons of unneeded code --- src/StakeRegistry.sol | 425 +++++++----------------- test/harnesses/StakeRegistryHarness.sol | 40 ++- test/unit/StakeRegistryUnit.t.sol | 141 +++++--- 3 files changed, 226 insertions(+), 380 deletions(-) diff --git a/src/StakeRegistry.sol b/src/StakeRegistry.sol index dc8b77b5..7b661229 100644 --- a/src/StakeRegistry.sol +++ b/src/StakeRegistry.sol @@ -2,12 +2,12 @@ pragma solidity =0.8.12; import "@openzeppelin/contracts/utils/math/Math.sol"; -import "./libraries/BitmapUtils.sol"; -import "./interfaces/IServiceManager.sol"; -import "./interfaces/IStakeRegistry.sol"; -import "./interfaces/IRegistryCoordinator.sol"; -import "./StakeRegistryStorage.sol"; -import "./VoteWeigherBaseStorage.sol"; +import "eigenlayer-contracts/src/contracts/libraries/BitmapUtils.sol"; +import "src/interfaces/IServiceManager.sol"; +import "src/interfaces/IStakeRegistry.sol"; +import "src/interfaces/IRegistryCoordinator.sol"; +import "src/StakeRegistryStorage.sol"; +import {VoteWeigherBase} from "src/VoteWeigherBase.sol"; /** * @title A `Registry` that keeps track of stakes of operators for up to 256 quorums. @@ -18,8 +18,8 @@ import "./VoteWeigherBaseStorage.sol"; * 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 VoteWeigherBaseStorage, StakeRegistryStorage { - +contract StakeRegistry is VoteWeigherBase, StakeRegistryStorage { + /// @notice requires that the caller is the RegistryCoordinator modifier onlyRegistryCoordinator() { require( msg.sender == address(registryCoordinator), @@ -28,21 +28,42 @@ contract StakeRegistry is VoteWeigherBaseStorage, StakeRegistryStorage { _; } - modifier onlyServiceManagerOwner() { - require(msg.sender == serviceManager.owner(), "StakeRegistry.onlyServiceManagerOwner: caller is not the owner of the serviceManager"); - _; - } - - modifier quorumExists(uint8 quorumNumber) { - require(_totalStakeHistory[quorumNumber].length != 0, "StakeRegistry.quorumExists: quorum does not exist"); - _; - } - constructor( IRegistryCoordinator _registryCoordinator, - IDelegationManager _delegationManager, + IStrategyManager _strategyManager, IServiceManager _serviceManager - ) VoteWeigherBaseStorage(_delegationManager, _serviceManager) StakeRegistryStorage(_registryCoordinator) {} + ) VoteWeigherBase(_strategyManager, _serviceManager) StakeRegistryStorage(_registryCoordinator) {} + + /** + * @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; + } + } + } /******************************************************************************* EXTERNAL FUNCTIONS @@ -57,15 +78,10 @@ contract StakeRegistry is VoteWeigherBaseStorage, StakeRegistryStorage { // 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 - uint8 quorumCount = registryCoordinator.quorumCount(); for (uint8 quorumNumber = 0; quorumNumber < quorumCount; ) { int256 totalStakeDelta; - // TODO - not a huge fan of this dependency on the reg coord, but i do prefer this - // over the stakereg also keeping its own count. - require(_totalStakeHistory[quorumNumber].length != 0, "StakeRegistry.updateStakes: quorum does not exist"); - - for (uint256 i = 0; i < operators.length; ) { + for (uint i = 0; i < operators.length; ) { bytes32 operatorId = registryCoordinator.getOperatorId(operators[i]); uint192 quorumBitmap = registryCoordinator.getCurrentQuorumBitmapByOperatorId(operatorId); @@ -87,8 +103,13 @@ contract StakeRegistry is VoteWeigherBaseStorage, StakeRegistryStorage { } } - // Record the update for the quorum's total stake - _recordTotalStakeUpdate(quorumNumber, totalStakeDelta); + // If we have a change in total stake for this quorum, update state + // TODO - do we want to record the update, even if the delta is zero? + // maybe this makes sense in the case that the quorum doesn't have + // an update for this block? + if (totalStakeDelta != 0) { + _recordTotalStakeUpdate(quorumNumber, totalStakeDelta); + } unchecked { ++quorumNumber; @@ -122,23 +143,25 @@ contract StakeRegistry is VoteWeigherBaseStorage, StakeRegistryStorage { 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 (uint i = 0; i < quorumNumbers.length; ) { - // get the next quorumNumber + + for (uint i = 0; i < quorumNumbers.length; ) { + /** + * Update the operator's stake for the quorum and retrieve their current stake + * as well as the change in stake. + * If this method returns `stake == 0`, the operator has not met the minimum requirement + * + * TODO - we only use the `stake` return here. It's probably better to use a bool instead + * of relying on the method returning "0" in only this one case. + */ uint8 quorumNumber = uint8(quorumNumbers[i]); - // 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({ + (int256 stakeDelta, uint96 stake) = _updateOperatorStake({ operator: operator, operatorId: operatorId, quorumNumber: quorumNumber }); require( - hasMinimumStake, - "StakeRegistry.registerOperator: Operator does not meet minimum stake requirement for quorum" + stake != 0, + "StakeRegistry._registerOperator: Operator does not meet minimum stake requirement for quorum" ); // Update this quorum's total stake @@ -165,30 +188,14 @@ contract StakeRegistry is VoteWeigherBaseStorage, StakeRegistryStorage { bytes32 operatorId, bytes calldata quorumNumbers ) public virtual onlyRegistryCoordinator { -<<<<<<< HEAD /** * For each quorum, remove the operator's stake for the quorum and update * the quorum's total stake to account for the removal */ - for (uint256 i = 0; i < quorumNumbers.length; ) { - uint8 quorumNumber = uint8(quorumNumbers[i]); - require(_totalStakeHistory[quorumNumber].length != 0, "StakeRegistry.deregisterOperator: quorum does not exist"); - - // Update the operator's stake for the quorum and retrieve the shares removed - int256 stakeDelta = _recordOperatorStakeUpdate({ -======= - 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 (uint i = 0; i < quorumNumbers.length; ) { + // Update the operator's stake for the quorum and retrieve the shares removed uint8 quorumNumber = uint8(quorumNumbers[i]); - // update the operator's stake - uint96 stakeBeforeUpdate = _recordOperatorStakeUpdate({ ->>>>>>> 095a082 (style: use for loop indices when the index is just an index) + int256 stakeDelta = _recordOperatorStakeUpdate({ operatorId: operatorId, quorumNumber: quorumNumber, newStake: 0 @@ -203,85 +210,15 @@ contract StakeRegistry is VoteWeigherBaseStorage, StakeRegistryStorage { } } - /// @notice Create a new quorum and add the strategies and their associated weights to the quorum. - function createQuorum( - uint8 quorumNumber, - uint96 minimumStake, - StrategyAndWeightingMultiplier[] memory strategyParams - ) public virtual onlyRegistryCoordinator { - require(_totalStakeHistory[quorumNumber].length == 0, "StakeRegistry.createQuorum: quorum already exists"); - _addStrategyParams(quorumNumber, strategyParams); - _setMinimumStakeForQuorum(quorumNumber, minimumStake); - } + /******************************************************************************* + EXTERNAL FUNCTIONS - SERVICE MANAGER OWNER + *******************************************************************************/ - function setMinimumStakeForQuorum( - uint8 quorumNumber, - uint96 minimumStake - ) public virtual onlyServiceManagerOwner quorumExists(quorumNumber) { + /// @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 Adds strategies and weights to the 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 addStrategyParams( - uint8 quorumNumber, - StrategyAndWeightingMultiplier[] memory strategyParams - ) public virtual onlyServiceManagerOwner quorumExists(quorumNumber) { - _addStrategyParams(quorumNumber, strategyParams); - } - - /** - * @notice Remove strategies and their associated weights from the quorum's considered strategies - * @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 strategies to remove - */ - function removeStrategyParams( - uint8 quorumNumber, - uint256[] memory indicesToRemove - ) public virtual onlyServiceManagerOwner quorumExists(quorumNumber) { - uint256 toRemoveLength = indicesToRemove.length; - require(toRemoveLength > 0, "StakeRegistry.removeStrategyParams: no indices to remove provided"); - - StrategyAndWeightingMultiplier[] storage strategyParams = strategiesConsideredAndMultipliers[quorumNumber]; - - for (uint256 i = 0; i < toRemoveLength; i++) { - emit StrategyRemovedFromQuorum(quorumNumber, strategyParams[indicesToRemove[i]].strategy); - emit StrategyMultiplierUpdated(quorumNumber, strategyParams[indicesToRemove[i]].strategy, 0); - - // Replace index to remove with the last item in the list, then pop the last item - strategyParams[indicesToRemove[i]] = strategyParams[strategyParams.length - 1]; - strategyParams.pop(); - } - } - - /** - * @notice Modifys the weights of existing strategies for a specific quorum - * @param quorumNumber is the quorum number to which the strategies belong - * @param strategyIndices are the indices of the strategies to change - * @param newMultipliers are the new multipliers for the strategies - */ - function modifyStrategyParams( - uint8 quorumNumber, - uint256[] calldata strategyIndices, - uint96[] calldata newMultipliers - ) public virtual onlyServiceManagerOwner quorumExists(quorumNumber) { - uint256 numStrats = strategyIndices.length; - require(numStrats > 0, "StakeRegistry.modifyStrategyParams: no strategy indices provided"); - require(newMultipliers.length == numStrats, "StakeRegistry.modifyStrategyParams: input length mismatch"); - - StrategyAndWeightingMultiplier[] storage strategyParams = strategiesConsideredAndMultipliers[quorumNumber]; - - for (uint256 i = 0; i < numStrats; i++) { - // Change the strategy's associated multiplier - strategyParams[strategyIndices[i]].multiplier = newMultipliers[i]; - emit StrategyMultiplierUpdated(quorumNumber, strategyParams[strategyIndices[i]].strategy, newMultipliers[i]); - } - } - /******************************************************************************* INTERNAL FUNCTIONS *******************************************************************************/ @@ -291,13 +228,8 @@ contract StakeRegistry is VoteWeigherBaseStorage, StakeRegistryStorage { uint8 quorumNumber, uint32 blockNumber ) internal view returns (uint32) { -<<<<<<< HEAD - uint256 length = operatorIdToStakeHistory[operatorId][quorumNumber].length; - for (uint256 i = 0; i < length; i++) { -======= uint length = operatorIdToStakeHistory[operatorId][quorumNumber].length; for (uint i = 0; i < length; i++) { ->>>>>>> 095a082 (style: use for loop indices when the index is just an index) if (operatorIdToStakeHistory[operatorId][quorumNumber][length - i - 1].updateBlockNumber <= blockNumber) { uint32 nextUpdateBlockNumber = operatorIdToStakeHistory[operatorId][quorumNumber][length - i - 1].nextUpdateBlockNumber; @@ -321,37 +253,35 @@ contract StakeRegistry is VoteWeigherBaseStorage, StakeRegistryStorage { /** * @notice Finds the updated stake for `operator` for `quorumNumber`, stores it and records the update * @dev **DOES NOT UPDATE `totalStake` IN ANY WAY** -- `totalStake` updates must be done elsewhere. - * @return delta The change in the operator's stake as a signed int256 - * @return hasMinimumStake Whether the operator meets the minimum stake requirement for the quorum + * @return `int256` The change in the operator's stake as a signed int256 + * @return `uint96` The operator's new stake after the update */ function _updateOperatorStake( address operator, bytes32 operatorId, uint8 quorumNumber - ) internal returns (int256 delta, bool hasMinimumStake) { + ) internal returns (int256, uint96) { /** * Get the operator's current stake for the quorum. If their stake * is below the quorum's threshold, set their stake to 0 */ - uint96 currentStake = _weightOfOperatorForQuorum(quorumNumber, operator); + uint96 currentStake = weightOfOperatorForQuorum(quorumNumber, operator); if (currentStake < minimumStakeForQuorum[quorumNumber]) { currentStake = uint96(0); - } else { - hasMinimumStake = true; } // Update the operator's stake and retrieve the delta - delta = _recordOperatorStakeUpdate({ + int256 delta = _recordOperatorStakeUpdate({ operatorId: operatorId, quorumNumber: quorumNumber, newStake: currentStake }); - return (delta, hasMinimumStake); + return (delta, currentStake); } /** - * @notice Records that `operatorId`'s current stake for `quorumNumber` is now `newStake` + * @notice Records that `operatorId`'s current stake for `quorumNumber` is now param @operatorStakeUpdate * @return The change in the operator's stake as a signed int256 */ function _recordOperatorStakeUpdate( @@ -359,130 +289,64 @@ contract StakeRegistry is VoteWeigherBaseStorage, StakeRegistryStorage { uint8 quorumNumber, uint96 newStake ) internal returns (int256) { - + /** + * If the operator has previous stake history, update the previous entry + * and fetch their previous stake + */ uint96 prevStake; uint256 historyLength = operatorIdToStakeHistory[operatorId][quorumNumber].length; + if (historyLength != 0) { + operatorIdToStakeHistory[operatorId][quorumNumber][historyLength - 1] + .nextUpdateBlockNumber = uint32(block.number); - if (historyLength == 0) { - // No prior stake history - push our first entry - operatorIdToStakeHistory[operatorId][quorumNumber].push(OperatorStakeUpdate({ - updateBlockNumber: uint32(block.number), - nextUpdateBlockNumber: 0, - stake: newStake - })); - } else { - // We have prior stake history - fetch our last-recorded stake - prevStake = operatorIdToStakeHistory[operatorId][quorumNumber][historyLength-1].stake; - - /** - * If our last stake entry was made in the current block, update the entry - * Otherwise, push a new entry and update the previous entry's "next" field - */ - if (operatorIdToStakeHistory[operatorId][quorumNumber][historyLength-1].updateBlockNumber == uint32(block.number)) { - operatorIdToStakeHistory[operatorId][quorumNumber][historyLength-1].stake = newStake; - } else { - operatorIdToStakeHistory[operatorId][quorumNumber][historyLength-1].nextUpdateBlockNumber = uint32(block.number); - operatorIdToStakeHistory[operatorId][quorumNumber].push(OperatorStakeUpdate({ - updateBlockNumber: uint32(block.number), - nextUpdateBlockNumber: 0, - stake: newStake - })); - } + prevStake = + operatorIdToStakeHistory[operatorId][quorumNumber][historyLength - 1].stake; } + + // Create a new stake update and push it to storage + // TODO - update the entry instead of pushing, if the last update block is this block + operatorIdToStakeHistory[operatorId][quorumNumber].push(OperatorStakeUpdate({ + updateBlockNumber: uint32(block.number), + nextUpdateBlockNumber: 0, + stake: newStake + })); - // Log update and return stake delta emit StakeUpdate(operatorId, quorumNumber, newStake); + + // Return the change in stake return _calculateDelta({ prev: prevStake, cur: newStake }); } - /// @notice Applies a delta to the total stake recorded for `quorumNumber` + /// @notice Records that the `totalStake` for `quorumNumber` is now equal to the input param @_totalStake function _recordTotalStakeUpdate(uint8 quorumNumber, int256 stakeDelta) internal { - // Return early if no update is needed - if (stakeDelta == 0) { - return; - } - + /** + * If this quorum has previous stake history, update the previous entry + * and fetch the previous total stake + */ uint96 prevStake; uint256 historyLength = _totalStakeHistory[quorumNumber].length; + if (historyLength != 0) { + _totalStakeHistory[quorumNumber][historyLength - 1].nextUpdateBlockNumber = uint32(block.number); - if (historyLength == 0) { - // No prior stake history - push our first entry - _totalStakeHistory[quorumNumber].push(OperatorStakeUpdate({ - updateBlockNumber: uint32(block.number), - nextUpdateBlockNumber: 0, - stake: _applyDelta(prevStake, stakeDelta) - })); - } else { - // We have prior stake history - calculate our new stake as a function of our last-recorded stake prevStake = _totalStakeHistory[quorumNumber][historyLength - 1].stake; - uint96 newStake = _applyDelta(prevStake, stakeDelta); - - /** - * If our last stake entry was made in the current block, update the entry - * Otherwise, push a new entry and update the previous entry's "next" field - */ - if (_totalStakeHistory[quorumNumber][historyLength-1].updateBlockNumber == uint32(block.number)) { - _totalStakeHistory[quorumNumber][historyLength-1].stake = newStake; - } else { - _totalStakeHistory[quorumNumber][historyLength-1].nextUpdateBlockNumber = uint32(block.number); - _totalStakeHistory[quorumNumber].push(OperatorStakeUpdate({ - updateBlockNumber: uint32(block.number), - nextUpdateBlockNumber: 0, - stake: newStake - })); - } - } - } - - /** - * @notice Adds `strategyParams` 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 _addStrategyParams( - uint8 quorumNumber, - StrategyAndWeightingMultiplier[] memory strategyParams - ) internal { - require(strategyParams.length > 0, "StakeRegistry._addStrategyParams: no strategies provided"); - uint256 numStratsToAdd = strategyParams.length; - uint256 numStratsExisting = strategiesConsideredAndMultipliers[quorumNumber].length; - require( - numStratsExisting + numStratsToAdd <= MAX_WEIGHING_FUNCTION_LENGTH, - "StakeRegistry._addStrategyParams: 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 != - strategyParams[i].strategy, - "StakeRegistry._addStrategyParams: cannot add same strategy 2x" - ); - unchecked { - ++j; - } - } - require( - strategyParams[i].multiplier > 0, - "StakeRegistry._addStrategyParams: cannot add strategy with zero weight" - ); - strategiesConsideredAndMultipliers[quorumNumber].push(strategyParams[i]); - emit StrategyAddedToQuorum(quorumNumber, strategyParams[i].strategy); - emit StrategyMultiplierUpdated( - quorumNumber, - strategyParams[i].strategy, - strategyParams[i].multiplier - ); - unchecked { - ++i; - } } + + // Apply the stake delta to the previous stake, and push an update to the + // quorum's stake history + _totalStakeHistory[quorumNumber].push(OperatorStakeUpdate({ + updateBlockNumber: uint32(block.number), + nextUpdateBlockNumber: 0, + stake: _applyDelta(prevStake, stakeDelta) + })); } /// @notice Returns the change between a previous and current value as a signed int function _calculateDelta(uint96 prev, uint96 cur) internal pure returns (int256) { - return int256(uint256(cur)) - int256(uint256(prev)); + if (cur >= prev) { + return int256(uint256(cur - prev)); + } else { + return -int256(uint256(prev - cur)); + } } /// @notice Adds or subtracts delta from value, according to its sign @@ -509,64 +373,10 @@ contract StakeRegistry is VoteWeigherBaseStorage, StakeRegistryStorage { ); } - /** - * @notice This function computes the total weight of the @param operator in the quorum @param quorumNumber. - * @dev this method DOES NOT check that the quorum exists - */ - function _weightOfOperatorForQuorum(uint8 quorumNumber, address operator) internal view 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; - } - /******************************************************************************* VIEW FUNCTIONS *******************************************************************************/ - /** - * @notice This function computes the total weight of the @param operator in the quorum @param quorumNumber. - * @dev reverts if the quorum does not exist - */ - function weightOfOperatorForQuorum( - uint8 quorumNumber, - address operator - ) public virtual view quorumExists(quorumNumber) returns (uint96) { - return _weightOfOperatorForQuorum(quorumNumber, operator); - } - - /// @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 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 Returns the entire `operatorIdToStakeHistory[operatorId][quorumNumber]` array. * @param operatorId The id of the operator of interest. @@ -632,13 +442,8 @@ contract StakeRegistry is VoteWeigherBaseStorage, StakeRegistryStorage { _totalStakeHistory[quorumNumber][0].updateBlockNumber <= blockNumber, "StakeRegistry.getTotalStakeIndicesByQuorumNumbersAtBlockNumber: quorum has no stake history at blockNumber" ); -<<<<<<< HEAD - uint256 length = _totalStakeHistory[quorumNumber].length; - for (uint256 j = 0; j < length; j++) { -======= uint length = _totalStakeHistory[quorumNumber].length; for (uint j = 0; j < length; j++) { ->>>>>>> 095a082 (style: use for loop indices when the index is just an index) if (_totalStakeHistory[quorumNumber][length - j - 1].updateBlockNumber <= blockNumber) { indices[i] = uint32(length - j - 1); break; diff --git a/test/harnesses/StakeRegistryHarness.sol b/test/harnesses/StakeRegistryHarness.sol index d95d7bfc..26e67ab9 100644 --- a/test/harnesses/StakeRegistryHarness.sol +++ b/test/harnesses/StakeRegistryHarness.sol @@ -1,11 +1,11 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity =0.8.12; -import "../../src/StakeRegistry.sol"; +import "src/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; + mapping(uint8 => mapping(address => uint96)) private _weightOfOperatorForQuorum; constructor( IRegistryCoordinator _registryCoordinator, @@ -18,7 +18,7 @@ contract StakeRegistryHarness is StakeRegistry { return _recordOperatorStakeUpdate(operatorId, quorumNumber, newStake); } - function updateOperatorStake(address operator, bytes32 operatorId, uint8 quorumNumber) external returns (int256, bool) { + function updateOperatorStake(address operator, bytes32 operatorId, uint8 quorumNumber) external returns (int256, uint96) { return _updateOperatorStake(operator, operatorId, quorumNumber); } @@ -28,36 +28,46 @@ contract StakeRegistryHarness is StakeRegistry { // 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]; + return _weightOfOperatorForQuorum[quorumNumber][operator]; + } + + /// TODO remove when core gets updated + function weightOfOperatorForQuorumView(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; + _weightOfOperatorForQuorum[quorumNumber][operator] = weight; } // mocked function to register an operator without having to mock other elements // This is just a copy/paste from `registerOperator`, since that no longer uses an internal method function registerOperatorNonCoordinator(address operator, bytes32 operatorId, bytes calldata quorumNumbers) external { - for (uint256 i = 0; i < quorumNumbers.length; ) { - - uint8 quorumNumber = uint8(quorumNumbers[i]); - require(_totalStakeHistory[quorumNumber].length != 0, "StakeRegistry.registerOperator: quorum does not exist"); - + // 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" + ); + + for (uint i = 0; i < quorumNumbers.length; ) { /** * Update the operator's stake for the quorum and retrieve their current stake * as well as the change in stake. - * - If this method returns `hasMinimumStake == false`, the operator has not met - * the minimum stake requirement for this quorum + * If this method returns `stake == 0`, the operator has not met the minimum requirement + * + * TODO - we only use the `stake` return here. It's probably better to use a bool instead + * of relying on the method returning "0" in only this one case. */ - (int256 stakeDelta, bool hasMinimumStake) = _updateOperatorStake({ + uint8 quorumNumber = uint8(quorumNumbers[i]); + (int256 stakeDelta, uint96 stake) = _updateOperatorStake({ operator: operator, operatorId: operatorId, quorumNumber: quorumNumber }); require( - hasMinimumStake, - "StakeRegistry.registerOperator: Operator does not meet minimum stake requirement for quorum" + stake != 0, + "StakeRegistry._registerOperator: Operator does not meet minimum stake requirement for quorum" ); // Update this quorum's total stake diff --git a/test/unit/StakeRegistryUnit.t.sol b/test/unit/StakeRegistryUnit.t.sol index 4bf5c5bc..ec27ca6e 100644 --- a/test/unit/StakeRegistryUnit.t.sol +++ b/test/unit/StakeRegistryUnit.t.sol @@ -1,8 +1,8 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity =0.8.12; -import "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; import "@openzeppelin/contracts/token/ERC20/presets/ERC20PresetFixedSupply.sol"; +import "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; import {Slasher} from "eigenlayer-contracts/src/contracts/core/Slasher.sol"; @@ -10,25 +10,25 @@ import {PauserRegistry} from "eigenlayer-contracts/src/contracts/permissions/Pau import {IStrategyManager} from "eigenlayer-contracts/src/contracts/interfaces/IStrategyManager.sol"; import {ISlasher} from "eigenlayer-contracts/src/contracts/interfaces/ISlasher.sol"; import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; -import {IStakeRegistry} from "../../src/interfaces/IStakeRegistry.sol"; -import {IServiceManager} from "../../src/interfaces/IServiceManager.sol"; -import {IVoteWeigher} from "../../src/interfaces/IVoteWeigher.sol"; -import {IIndexRegistry} from "../../src/interfaces/IIndexRegistry.sol"; -import {IRegistryCoordinator} from "../../src/interfaces/IRegistryCoordinator.sol"; -import {IBLSPubkeyRegistry} from "../../src/interfaces/IBLSPubkeyRegistry.sol"; +import {IStakeRegistry} from "src/interfaces/IStakeRegistry.sol"; +import {IServiceManager} from "src/interfaces/IServiceManager.sol"; +import {IVoteWeigher} from "src/interfaces/IVoteWeigher.sol"; +import {IIndexRegistry} from "src/interfaces/IIndexRegistry.sol"; +import {IRegistryCoordinator} from "src/interfaces/IRegistryCoordinator.sol"; +import {IBLSPubkeyRegistry} from "src/interfaces/IBLSPubkeyRegistry.sol"; -import {BitmapUtils} from "../../src/libraries/BitmapUtils.sol"; +import {BitmapUtils} from "eigenlayer-contracts/src/contracts/libraries/BitmapUtils.sol"; import {StrategyManagerMock} from "eigenlayer-contracts/src/test/mocks/StrategyManagerMock.sol"; import {EigenPodManagerMock} from "eigenlayer-contracts/src/test/mocks/EigenPodManagerMock.sol"; -import {ServiceManagerMock} from "../mocks/ServiceManagerMock.sol"; +import {ServiceManagerMock} from "test/mocks/ServiceManagerMock.sol"; import {OwnableMock} from "eigenlayer-contracts/src/test/mocks/OwnableMock.sol"; import {DelegationManagerMock} from "eigenlayer-contracts/src/test/mocks/DelegationManagerMock.sol"; import {SlasherMock} from "eigenlayer-contracts/src/test/mocks/SlasherMock.sol"; -import {StakeRegistryHarness} from "../harnesses/StakeRegistryHarness.sol"; -import {StakeRegistry} from "../../src/StakeRegistry.sol"; -import {BLSRegistryCoordinatorWithIndicesHarness} from "../harnesses/BLSRegistryCoordinatorWithIndicesHarness.sol"; +import {StakeRegistryHarness} from "test/harnesses/StakeRegistryHarness.sol"; +import {StakeRegistry} from "src/StakeRegistry.sol"; +import {BLSRegistryCoordinatorWithIndicesHarness} from "test/harnesses/BLSRegistryCoordinatorWithIndicesHarness.sol"; import "forge-std/Test.sol"; @@ -299,39 +299,39 @@ contract StakeRegistryUnitTests is Test { // 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]; - } - - uint historyLength = stakeRegistry.getLengthOfTotalStakeHistoryForQuorum(i); - - // If we don't have stake history, it should be because there is no stake - if (historyLength == 0) { - assertEq(cumulativeStake, 0); - continue; - } - - // make sure that the stake update is as expected - IStakeRegistry.OperatorStakeUpdate memory totalStakeUpdate = - stakeRegistry.getTotalStakeUpdateForQuorumFromIndex(i, historyLength-1); - - 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 (historyLength >= 2) { - IStakeRegistry.OperatorStakeUpdate memory prevTotalStakeUpdate = - stakeRegistry.getTotalStakeUpdateForQuorumFromIndex(i, historyLength-2); - assertEq(prevTotalStakeUpdate.nextUpdateBlockNumber, cumulativeBlockNumber); - } + } } } @@ -377,8 +377,13 @@ contract StakeRegistryUnitTests is Test { (quorumBitmaps[i],) = _registerOperatorRandomValid(_incrementAddress(defaultOperator, i), _incrementBytes32(defaultOperatorId, i), pseudoRandomNumber + i); } - cumulativeBlockNumber += 1; - cheats.roll(cumulativeBlockNumber); + { + 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; @@ -406,13 +411,13 @@ contract StakeRegistryUnitTests is Test { assertEq(lastStakeUpdate.updateBlockNumber, cumulativeBlockNumber, "testDeregisterFirstOperator_Valid_2"); assertEq(lastStakeUpdate.nextUpdateBlockNumber, 0, "testDeregisterFirstOperator_Valid_3"); - // Get history length for quorum - uint historyLength = stakeRegistry.getLengthOfTotalStakeHistoryForQuorum(i); + // 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, historyLength-1); + = stakeRegistry.getTotalStakeUpdateForQuorumFromIndex(i, numOperatorsInQuorum[i]); assertEq(lastTotalStakeUpdate.stake, - stakeRegistry.getTotalStakeUpdateForQuorumFromIndex(i, historyLength-2).stake // the previous total stake + stakeRegistry.getTotalStakeUpdateForQuorumFromIndex(i, numOperatorsInQuorum[i] - 1).stake // the previous total stake - paddedStakesForQuorum[quorumNumberIndex], // minus the stake that was deregistered "testDeregisterFirstOperator_Valid_5" ); @@ -452,44 +457,66 @@ contract StakeRegistryUnitTests is Test { 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.getMostRecentStakeUpdateByOperatorId(defaultOperatorId, defaultQuorumNumber); + 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 blocksPassed, + 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 < stakes.length; i++) { + for (uint256 i = 0; i < blocksPassed.length; i++) { int256 stakeDelta; if (i == 0) { stakeDelta = _calculateDelta({prev: 0, cur: stakes[i]}); } else { stakeDelta = _calculateDelta({prev: stakes[i-1], cur: stakes[i]}); } - - // Perform the update stakeRegistry.recordTotalStakeUpdate(defaultQuorumNumber, stakeDelta); - - IStakeRegistry.OperatorStakeUpdate memory newStakeUpdate; - uint historyLength = stakeRegistry.getLengthOfTotalStakeHistoryForQuorum(defaultQuorumNumber); - if (historyLength != 0) { - newStakeUpdate = stakeRegistry.getTotalStakeUpdateForQuorumFromIndex(defaultQuorumNumber, historyLength-1); - } - // Check that the most recent entry reflects the correct stake - assertEq(newStakeUpdate.stake, stakes[i]); - cumulativeBlockNumber += blocksPassed; + 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( @@ -592,6 +619,10 @@ contract StakeRegistryUnitTests is Test { } function _calculateDelta(uint96 prev, uint96 cur) internal pure returns (int256) { - return int256(uint256(cur)) - int256(uint256(prev)); + if (cur >= prev) { + return int256(uint256(cur - prev)); + } else { + return -int256(uint256(prev - cur)); + } } } From 7bbdf37dc458706172518cb7a4fd4d0e564f5a7c Mon Sep 17 00:00:00 2001 From: wadealexc Date: Tue, 24 Oct 2023 19:16:20 +0000 Subject: [PATCH 020/101] style: use over , and simplify --- src/StakeRegistry.sol | 20 ++++++++------------ test/unit/StakeRegistryUnit.t.sol | 6 +----- 2 files changed, 9 insertions(+), 17 deletions(-) diff --git a/src/StakeRegistry.sol b/src/StakeRegistry.sol index 7b661229..5f39eab9 100644 --- a/src/StakeRegistry.sol +++ b/src/StakeRegistry.sol @@ -81,7 +81,7 @@ contract StakeRegistry is VoteWeigherBase, StakeRegistryStorage { for (uint8 quorumNumber = 0; quorumNumber < quorumCount; ) { int256 totalStakeDelta; - for (uint i = 0; i < operators.length; ) { + for (uint256 i = 0; i < operators.length; ) { bytes32 operatorId = registryCoordinator.getOperatorId(operators[i]); uint192 quorumBitmap = registryCoordinator.getCurrentQuorumBitmapByOperatorId(operatorId); @@ -144,7 +144,7 @@ contract StakeRegistry is VoteWeigherBase, StakeRegistryStorage { "StakeRegistry._registerOperator: greatest quorumNumber must be less than quorumCount" ); - for (uint i = 0; i < quorumNumbers.length; ) { + for (uint256 i = 0; i < quorumNumbers.length; ) { /** * Update the operator's stake for the quorum and retrieve their current stake * as well as the change in stake. @@ -192,7 +192,7 @@ contract StakeRegistry is VoteWeigherBase, StakeRegistryStorage { * For each quorum, remove the operator's stake for the quorum and update * the quorum's total stake to account for the removal */ - for (uint i = 0; i < quorumNumbers.length; ) { + for (uint256 i = 0; i < quorumNumbers.length; ) { // Update the operator's stake for the quorum and retrieve the shares removed uint8 quorumNumber = uint8(quorumNumbers[i]); int256 stakeDelta = _recordOperatorStakeUpdate({ @@ -228,8 +228,8 @@ contract StakeRegistry is VoteWeigherBase, StakeRegistryStorage { uint8 quorumNumber, uint32 blockNumber ) internal view returns (uint32) { - uint length = operatorIdToStakeHistory[operatorId][quorumNumber].length; - for (uint i = 0; i < length; i++) { + uint256 length = operatorIdToStakeHistory[operatorId][quorumNumber].length; + for (uint256 i = 0; i < length; i++) { if (operatorIdToStakeHistory[operatorId][quorumNumber][length - i - 1].updateBlockNumber <= blockNumber) { uint32 nextUpdateBlockNumber = operatorIdToStakeHistory[operatorId][quorumNumber][length - i - 1].nextUpdateBlockNumber; @@ -342,11 +342,7 @@ contract StakeRegistry is VoteWeigherBase, StakeRegistryStorage { /// @notice Returns the change between a previous and current value as a signed int function _calculateDelta(uint96 prev, uint96 cur) internal pure returns (int256) { - if (cur >= prev) { - return int256(uint256(cur - prev)); - } else { - return -int256(uint256(prev - cur)); - } + return int256(uint256(cur)) - int256(uint256(prev)); } /// @notice Adds or subtracts delta from value, according to its sign @@ -442,8 +438,8 @@ contract StakeRegistry is VoteWeigherBase, StakeRegistryStorage { _totalStakeHistory[quorumNumber][0].updateBlockNumber <= blockNumber, "StakeRegistry.getTotalStakeIndicesByQuorumNumbersAtBlockNumber: quorum has no stake history at blockNumber" ); - uint length = _totalStakeHistory[quorumNumber].length; - for (uint j = 0; j < length; j++) { + uint256 length = _totalStakeHistory[quorumNumber].length; + for (uint256 j = 0; j < length; j++) { if (_totalStakeHistory[quorumNumber][length - j - 1].updateBlockNumber <= blockNumber) { indices[i] = uint32(length - j - 1); break; diff --git a/test/unit/StakeRegistryUnit.t.sol b/test/unit/StakeRegistryUnit.t.sol index ec27ca6e..1c32f5e1 100644 --- a/test/unit/StakeRegistryUnit.t.sol +++ b/test/unit/StakeRegistryUnit.t.sol @@ -619,10 +619,6 @@ contract StakeRegistryUnitTests is Test { } function _calculateDelta(uint96 prev, uint96 cur) internal pure returns (int256) { - if (cur >= prev) { - return int256(uint256(cur - prev)); - } else { - return -int256(uint256(prev - cur)); - } + return int256(uint256(cur)) - int256(uint256(prev)); } } From 42420294baf2890a15a89c3feb85f30a66f42cbf Mon Sep 17 00:00:00 2001 From: wadealexc Date: Tue, 24 Oct 2023 21:51:02 +0000 Subject: [PATCH 021/101] feat: only update total history for nonzero delta, and dont push update if last update was in current block --- src/StakeRegistry.sol | 125 ++++++++++++++---------- test/harnesses/StakeRegistryHarness.sol | 14 ++- test/unit/StakeRegistryUnit.t.sol | 66 +++++++------ 3 files changed, 116 insertions(+), 89 deletions(-) diff --git a/src/StakeRegistry.sol b/src/StakeRegistry.sol index 5f39eab9..10bf2c1c 100644 --- a/src/StakeRegistry.sol +++ b/src/StakeRegistry.sol @@ -103,13 +103,8 @@ contract StakeRegistry is VoteWeigherBase, StakeRegistryStorage { } } - // If we have a change in total stake for this quorum, update state - // TODO - do we want to record the update, even if the delta is zero? - // maybe this makes sense in the case that the quorum doesn't have - // an update for this block? - if (totalStakeDelta != 0) { - _recordTotalStakeUpdate(quorumNumber, totalStakeDelta); - } + // Record the update for the quorum's total stake + _recordTotalStakeUpdate(quorumNumber, totalStakeDelta); unchecked { ++quorumNumber; @@ -148,19 +143,17 @@ contract StakeRegistry is VoteWeigherBase, StakeRegistryStorage { /** * Update the operator's stake for the quorum and retrieve their current stake * as well as the change in stake. - * If this method returns `stake == 0`, the operator has not met the minimum requirement - * - * TODO - we only use the `stake` return here. It's probably better to use a bool instead - * of relying on the method returning "0" in only this one case. + * - If this method returns `hasMinimumStake == false`, the operator has not met + * the minimum stake requirement for this quorum */ uint8 quorumNumber = uint8(quorumNumbers[i]); - (int256 stakeDelta, uint96 stake) = _updateOperatorStake({ + (int256 stakeDelta, bool hasMinimumStake) = _updateOperatorStake({ operator: operator, operatorId: operatorId, quorumNumber: quorumNumber }); require( - stake != 0, + hasMinimumStake, "StakeRegistry._registerOperator: Operator does not meet minimum stake requirement for quorum" ); @@ -253,14 +246,14 @@ contract StakeRegistry is VoteWeigherBase, StakeRegistryStorage { /** * @notice Finds the updated stake for `operator` for `quorumNumber`, stores it and records the update * @dev **DOES NOT UPDATE `totalStake` IN ANY WAY** -- `totalStake` updates must be done elsewhere. - * @return `int256` The change in the operator's stake as a signed int256 - * @return `uint96` The operator's new stake after the update + * @return delta The change in the operator's stake as a signed int256 + * @return hasMinimumStake Whether the operator meets the minimum stake requirement for the quorum */ function _updateOperatorStake( address operator, bytes32 operatorId, uint8 quorumNumber - ) internal returns (int256, uint96) { + ) internal returns (int256 delta, bool hasMinimumStake) { /** * Get the operator's current stake for the quorum. If their stake * is below the quorum's threshold, set their stake to 0 @@ -268,20 +261,22 @@ contract StakeRegistry is VoteWeigherBase, StakeRegistryStorage { uint96 currentStake = weightOfOperatorForQuorum(quorumNumber, operator); if (currentStake < minimumStakeForQuorum[quorumNumber]) { currentStake = uint96(0); + } else { + hasMinimumStake = true; } // Update the operator's stake and retrieve the delta - int256 delta = _recordOperatorStakeUpdate({ + delta = _recordOperatorStakeUpdate({ operatorId: operatorId, quorumNumber: quorumNumber, newStake: currentStake }); - return (delta, currentStake); + return (delta, hasMinimumStake); } /** - * @notice Records that `operatorId`'s current stake for `quorumNumber` is now param @operatorStakeUpdate + * @notice Records that `operatorId`'s current stake for `quorumNumber` is now `newStake` * @return The change in the operator's stake as a signed int256 */ function _recordOperatorStakeUpdate( @@ -289,55 +284,79 @@ contract StakeRegistry is VoteWeigherBase, StakeRegistryStorage { uint8 quorumNumber, uint96 newStake ) internal returns (int256) { - /** - * If the operator has previous stake history, update the previous entry - * and fetch their previous stake - */ + uint96 prevStake; uint256 historyLength = operatorIdToStakeHistory[operatorId][quorumNumber].length; - if (historyLength != 0) { - operatorIdToStakeHistory[operatorId][quorumNumber][historyLength - 1] - .nextUpdateBlockNumber = uint32(block.number); - prevStake = - operatorIdToStakeHistory[operatorId][quorumNumber][historyLength - 1].stake; + if (historyLength == 0) { + // No prior stake history - push our first entry + operatorIdToStakeHistory[operatorId][quorumNumber].push(OperatorStakeUpdate({ + updateBlockNumber: uint32(block.number), + nextUpdateBlockNumber: 0, + stake: newStake + })); + } else { + // We have prior stake history - fetch our last-recorded stake + prevStake = operatorIdToStakeHistory[operatorId][quorumNumber][historyLength-1].stake; + + /** + * If our last stake entry was made in the current block, update the entry + * Otherwise, push a new entry and update the previous entry's "next" field + */ + if (operatorIdToStakeHistory[operatorId][quorumNumber][historyLength-1].updateBlockNumber == uint32(block.number)) { + operatorIdToStakeHistory[operatorId][quorumNumber][historyLength-1].stake = newStake; + } else { + operatorIdToStakeHistory[operatorId][quorumNumber][historyLength-1].nextUpdateBlockNumber = uint32(block.number); + operatorIdToStakeHistory[operatorId][quorumNumber].push(OperatorStakeUpdate({ + updateBlockNumber: uint32(block.number), + nextUpdateBlockNumber: 0, + stake: newStake + })); + } } - - // Create a new stake update and push it to storage - // TODO - update the entry instead of pushing, if the last update block is this block - operatorIdToStakeHistory[operatorId][quorumNumber].push(OperatorStakeUpdate({ - updateBlockNumber: uint32(block.number), - nextUpdateBlockNumber: 0, - stake: newStake - })); + // Log update and return stake delta emit StakeUpdate(operatorId, quorumNumber, newStake); - - // Return the change in stake return _calculateDelta({ prev: prevStake, cur: newStake }); } - /// @notice Records that the `totalStake` for `quorumNumber` is now equal to the input param @_totalStake + /// @notice Applies a delta to the total stake recorded for `quorumNumber` function _recordTotalStakeUpdate(uint8 quorumNumber, int256 stakeDelta) internal { - /** - * If this quorum has previous stake history, update the previous entry - * and fetch the previous total stake - */ + // Return early if no update is needed + if (stakeDelta == 0) { + return; + } + uint96 prevStake; uint256 historyLength = _totalStakeHistory[quorumNumber].length; - if (historyLength != 0) { - _totalStakeHistory[quorumNumber][historyLength - 1].nextUpdateBlockNumber = uint32(block.number); + if (historyLength == 0) { + // No prior stake history - push our first entry + _totalStakeHistory[quorumNumber].push(OperatorStakeUpdate({ + updateBlockNumber: uint32(block.number), + nextUpdateBlockNumber: 0, + stake: _applyDelta(prevStake, stakeDelta) + })); + } else { + // We have prior stake history - calculate our new stake as a function of our last-recorded stake prevStake = _totalStakeHistory[quorumNumber][historyLength - 1].stake; + uint96 newStake = _applyDelta(prevStake, stakeDelta); + + /** + * If our last stake entry was made in the current block, update the entry + * Otherwise, push a new entry and update the previous entry's "next" field + */ + if (_totalStakeHistory[quorumNumber][historyLength-1].updateBlockNumber == uint32(block.number)) { + _totalStakeHistory[quorumNumber][historyLength-1].stake = newStake; + } else { + _totalStakeHistory[quorumNumber][historyLength-1].nextUpdateBlockNumber = uint32(block.number); + _totalStakeHistory[quorumNumber].push(OperatorStakeUpdate({ + updateBlockNumber: uint32(block.number), + nextUpdateBlockNumber: 0, + stake: newStake + })); + } } - - // Apply the stake delta to the previous stake, and push an update to the - // quorum's stake history - _totalStakeHistory[quorumNumber].push(OperatorStakeUpdate({ - updateBlockNumber: uint32(block.number), - nextUpdateBlockNumber: 0, - stake: _applyDelta(prevStake, stakeDelta) - })); } /// @notice Returns the change between a previous and current value as a signed int diff --git a/test/harnesses/StakeRegistryHarness.sol b/test/harnesses/StakeRegistryHarness.sol index 26e67ab9..96fc9c94 100644 --- a/test/harnesses/StakeRegistryHarness.sol +++ b/test/harnesses/StakeRegistryHarness.sol @@ -18,7 +18,7 @@ contract StakeRegistryHarness is StakeRegistry { return _recordOperatorStakeUpdate(operatorId, quorumNumber, newStake); } - function updateOperatorStake(address operator, bytes32 operatorId, uint8 quorumNumber) external returns (int256, uint96) { + function updateOperatorStake(address operator, bytes32 operatorId, uint8 quorumNumber) external returns (int256, bool) { return _updateOperatorStake(operator, operatorId, quorumNumber); } @@ -50,23 +50,21 @@ contract StakeRegistryHarness is StakeRegistry { "StakeRegistry._registerOperator: greatest quorumNumber must be less than quorumCount" ); - for (uint i = 0; i < quorumNumbers.length; ) { + for (uint256 i = 0; i < quorumNumbers.length; ) { /** * Update the operator's stake for the quorum and retrieve their current stake * as well as the change in stake. - * If this method returns `stake == 0`, the operator has not met the minimum requirement - * - * TODO - we only use the `stake` return here. It's probably better to use a bool instead - * of relying on the method returning "0" in only this one case. + * - If this method returns `hasMinimumStake == false`, the operator has not met + * the minimum stake requirement for this quorum */ uint8 quorumNumber = uint8(quorumNumbers[i]); - (int256 stakeDelta, uint96 stake) = _updateOperatorStake({ + (int256 stakeDelta, bool hasMinimumStake) = _updateOperatorStake({ operator: operator, operatorId: operatorId, quorumNumber: quorumNumber }); require( - stake != 0, + hasMinimumStake, "StakeRegistry._registerOperator: Operator does not meet minimum stake requirement for quorum" ); diff --git a/test/unit/StakeRegistryUnit.t.sol b/test/unit/StakeRegistryUnit.t.sol index 1c32f5e1..fb9202ba 100644 --- a/test/unit/StakeRegistryUnit.t.sol +++ b/test/unit/StakeRegistryUnit.t.sol @@ -377,13 +377,8 @@ contract StakeRegistryUnitTests is Test { (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); - } - } + cumulativeBlockNumber += 1; + cheats.roll(cumulativeBlockNumber); // deregister the operator from a subset of the quorums uint256 deregistrationQuroumBitmap = quorumBitmap & deregistrationQuorumsFlag; @@ -411,13 +406,13 @@ contract StakeRegistryUnitTests is Test { 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"); + // Get history length for quorum + uint historyLength = stakeRegistry.getLengthOfTotalStakeHistoryForQuorum(i); // make sure that the last stake update is as expected IStakeRegistry.OperatorStakeUpdate memory lastTotalStakeUpdate - = stakeRegistry.getTotalStakeUpdateForQuorumFromIndex(i, numOperatorsInQuorum[i]); + = stakeRegistry.getTotalStakeUpdateForQuorumFromIndex(i, historyLength-1); assertEq(lastTotalStakeUpdate.stake, - stakeRegistry.getTotalStakeUpdateForQuorumFromIndex(i, numOperatorsInQuorum[i] - 1).stake // the previous total stake + stakeRegistry.getTotalStakeUpdateForQuorumFromIndex(i, historyLength-2).stake // the previous total stake - paddedStakesForQuorum[quorumNumberIndex], // minus the stake that was deregistered "testDeregisterFirstOperator_Valid_5" ); @@ -482,17 +477,15 @@ contract StakeRegistryUnitTests is Test { } function testRecordTotalStakeUpdate_Valid( - uint24[] memory blocksPassed, + uint24 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++) { + for (uint256 i = 0; i < stakes.length; i++) { int256 stakeDelta; if (i == 0) { stakeDelta = _calculateDelta({prev: 0, cur: stakes[i]}); @@ -500,22 +493,39 @@ contract StakeRegistryUnitTests is Test { stakeDelta = _calculateDelta({prev: stakes[i-1], cur: stakes[i]}); } - stakeRegistry.recordTotalStakeUpdate(defaultQuorumNumber, stakeDelta); + // Get previous history length and total stake update + uint prevHistoryLength = stakeRegistry.getLengthOfTotalStakeHistoryForQuorum(defaultQuorumNumber); + IStakeRegistry.OperatorStakeUpdate memory prevStakeUpdate; + if (prevHistoryLength != 0) { + prevStakeUpdate = stakeRegistry.getTotalStakeUpdateForQuorumFromIndex(defaultQuorumNumber, prevHistoryLength-1); + } + - cumulativeBlockNumber += blocksPassed[i]; - cheats.roll(cumulativeBlockNumber); - } + // Perform the update + stakeRegistry.recordTotalStakeUpdate(defaultQuorumNumber, stakeDelta); - // 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); + // Get new history length and total stake update + uint newHistoryLength = stakeRegistry.getLengthOfTotalStakeHistoryForQuorum(defaultQuorumNumber); + IStakeRegistry.OperatorStakeUpdate memory newStakeUpdate; + if (newHistoryLength != 0) { + newStakeUpdate = stakeRegistry.getTotalStakeUpdateForQuorumFromIndex(defaultQuorumNumber, newHistoryLength-1); + } + + // Check that the most recent entry reflects the correct stake + assertEq(newStakeUpdate.stake, stakes[i]); + + if (stakeDelta == 0) { + // Check that no update occurred + assertEq(prevHistoryLength, newHistoryLength); + assertEq(prevStakeUpdate.stake, newStakeUpdate.stake); + assertEq(prevStakeUpdate.updateBlockNumber, newStakeUpdate.updateBlockNumber); + assertEq(prevStakeUpdate.nextUpdateBlockNumber, newStakeUpdate.nextUpdateBlockNumber); + } else { + assertEq(newStakeUpdate.updateBlockNumber, cumulativeBlockNumber); + } - assertEq(totalStakeUpdate.stake, stakes[i]); - assertEq(totalStakeUpdate.updateBlockNumber, cumulativeBlockNumber); - cumulativeBlockNumber += blocksPassed[i]; - assertEq(totalStakeUpdate.nextUpdateBlockNumber, cumulativeBlockNumber); + cumulativeBlockNumber += blocksPassed; + cheats.roll(cumulativeBlockNumber); } } From 7b691a10315df7ace7bbddb52e224c993c277c8e Mon Sep 17 00:00:00 2001 From: wadealexc Date: Wed, 25 Oct 2023 13:58:59 +0000 Subject: [PATCH 022/101] style: use uint256 in registry coordinator --- src/BLSRegistryCoordinatorWithIndices.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/BLSRegistryCoordinatorWithIndices.sol b/src/BLSRegistryCoordinatorWithIndices.sol index d4b2131a..e1416c9e 100644 --- a/src/BLSRegistryCoordinatorWithIndices.sol +++ b/src/BLSRegistryCoordinatorWithIndices.sol @@ -617,8 +617,8 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr ) external view returns (uint32[] memory) { uint32[] memory indices = new uint32[](operatorIds.length); for (uint256 i = 0; i < operatorIds.length; i++) { - uint length = _operatorIdToQuorumBitmapHistory[operatorIds[i]].length; - for (uint j = 0; j < length; j++) { + uint256 length = _operatorIdToQuorumBitmapHistory[operatorIds[i]].length; + for (uint256 j = 0; j < length; j++) { if (_operatorIdToQuorumBitmapHistory[operatorIds[i]][length - j - 1].updateBlockNumber <= blockNumber) { uint32 nextUpdateBlockNumber = _operatorIdToQuorumBitmapHistory[operatorIds[i]][length - j - 1].nextUpdateBlockNumber; From c2d595973200905ce7e2db39e9ff9ecabe93d0e1 Mon Sep 17 00:00:00 2001 From: wadealexc Date: Wed, 25 Oct 2023 14:32:39 +0000 Subject: [PATCH 023/101] test: fix broken tests for StakeRegistry changes generally i just removed testing of specific stake histories in favor of testing net outcomes. we can revisit these tests in a few weeks. --- test/unit/StakeRegistryUnit.t.sol | 91 +++++++++---------------------- 1 file changed, 27 insertions(+), 64 deletions(-) diff --git a/test/unit/StakeRegistryUnit.t.sol b/test/unit/StakeRegistryUnit.t.sol index fb9202ba..bee91582 100644 --- a/test/unit/StakeRegistryUnit.t.sol +++ b/test/unit/StakeRegistryUnit.t.sol @@ -299,39 +299,39 @@ contract StakeRegistryUnitTests is Test { // 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]; - } + } + + uint historyLength = stakeRegistry.getLengthOfTotalStakeHistoryForQuorum(i); + + // If we don't have stake history, it should be because there is no stake + if (historyLength == 0) { + assertEq(cumulativeStake, 0); + continue; + } + + // make sure that the stake update is as expected + IStakeRegistry.OperatorStakeUpdate memory totalStakeUpdate = + stakeRegistry.getTotalStakeUpdateForQuorumFromIndex(i, historyLength-1); + + 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 (historyLength >= 2) { + IStakeRegistry.OperatorStakeUpdate memory prevTotalStakeUpdate = + stakeRegistry.getTotalStakeUpdateForQuorumFromIndex(i, historyLength-2); + assertEq(prevTotalStakeUpdate.nextUpdateBlockNumber, cumulativeBlockNumber); + } } } @@ -452,27 +452,9 @@ contract StakeRegistryUnitTests is Test { 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); + IStakeRegistry.OperatorStakeUpdate memory lastOperatorStakeUpdate = stakeRegistry.getMostRecentStakeUpdateByOperatorId(defaultOperatorId, defaultQuorumNumber); assertEq(lastOperatorStakeUpdate.stake, stakes[blocksPassed.length - 1]); - assertEq(lastOperatorStakeUpdate.updateBlockNumber, cumulativeBlockNumber); assertEq(lastOperatorStakeUpdate.nextUpdateBlockNumber, uint32(0)); } @@ -492,38 +474,19 @@ contract StakeRegistryUnitTests is Test { } else { stakeDelta = _calculateDelta({prev: stakes[i-1], cur: stakes[i]}); } - - // Get previous history length and total stake update - uint prevHistoryLength = stakeRegistry.getLengthOfTotalStakeHistoryForQuorum(defaultQuorumNumber); - IStakeRegistry.OperatorStakeUpdate memory prevStakeUpdate; - if (prevHistoryLength != 0) { - prevStakeUpdate = stakeRegistry.getTotalStakeUpdateForQuorumFromIndex(defaultQuorumNumber, prevHistoryLength-1); - } // Perform the update stakeRegistry.recordTotalStakeUpdate(defaultQuorumNumber, stakeDelta); - - // Get new history length and total stake update - uint newHistoryLength = stakeRegistry.getLengthOfTotalStakeHistoryForQuorum(defaultQuorumNumber); + IStakeRegistry.OperatorStakeUpdate memory newStakeUpdate; - if (newHistoryLength != 0) { - newStakeUpdate = stakeRegistry.getTotalStakeUpdateForQuorumFromIndex(defaultQuorumNumber, newHistoryLength-1); + uint historyLength = stakeRegistry.getLengthOfTotalStakeHistoryForQuorum(defaultQuorumNumber); + if (historyLength != 0) { + newStakeUpdate = stakeRegistry.getTotalStakeUpdateForQuorumFromIndex(defaultQuorumNumber, historyLength-1); } - // Check that the most recent entry reflects the correct stake assertEq(newStakeUpdate.stake, stakes[i]); - if (stakeDelta == 0) { - // Check that no update occurred - assertEq(prevHistoryLength, newHistoryLength); - assertEq(prevStakeUpdate.stake, newStakeUpdate.stake); - assertEq(prevStakeUpdate.updateBlockNumber, newStakeUpdate.updateBlockNumber); - assertEq(prevStakeUpdate.nextUpdateBlockNumber, newStakeUpdate.nextUpdateBlockNumber); - } else { - assertEq(newStakeUpdate.updateBlockNumber, cumulativeBlockNumber); - } - cumulativeBlockNumber += blocksPassed; cheats.roll(cumulativeBlockNumber); } From e6a18f10b47ef1d76d209c9f0ef358fda5c44fac Mon Sep 17 00:00:00 2001 From: wadealexc Date: Mon, 30 Oct 2023 20:37:34 +0000 Subject: [PATCH 024/101] fix: fix compilation issues and tests --- src/BLSPubkeyRegistry.sol | 4 +- src/BLSRegistryCoordinatorWithIndices.sol | 32 +- src/IndexRegistry.sol | 13 +- src/StakeRegistry.sol | 132 +++ src/StakeRegistryStorage.sol | 38 +- src/VoteWeigherBaseStorage.sol | 50 - src/interfaces/IBLSPubkeyRegistry.sol | 6 + src/interfaces/IIndexRegistry.sol | 6 + src/interfaces/IRegistryCoordinator.sol | 3 + src/interfaces/IStakeRegistry.sol | 92 +- src/interfaces/IVoteWeigher.sol | 97 -- test/EigenLayerDeployer.t.sol | 374 ++++++++ ...SRegistryCoordinatorWithIndicesHarness.sol | 4 + test/harnesses/StakeRegistryHarness.sol | 8 +- test/mocks/RegistryCoordinatorMock.sol | 1 + test/mocks/StakeRegistryMock.sol | 51 ++ test/unit/BLSOperatorStateRetrieverUnit.t.sol | 8 +- test/unit/BLSPubkeyRegistryUnit.t.sol | 45 +- ...LSRegistryCoordinatorWithIndicesUnit.t.sol | 12 +- test/unit/IndexRegistryUnit.t.sol | 84 +- test/unit/StakeRegistryUnit.t.sol | 104 ++- test/unit/VoteWeigherBaseUnit.t.sol | 855 +++++++++--------- test/utils/MockAVSDeployer.sol | 89 +- 23 files changed, 1377 insertions(+), 731 deletions(-) delete mode 100644 src/VoteWeigherBaseStorage.sol delete mode 100644 src/interfaces/IVoteWeigher.sol create mode 100644 test/EigenLayerDeployer.t.sol diff --git a/src/BLSPubkeyRegistry.sol b/src/BLSPubkeyRegistry.sol index 999a8183..33b3e898 100644 --- a/src/BLSPubkeyRegistry.sol +++ b/src/BLSPubkeyRegistry.sol @@ -95,10 +95,10 @@ contract BLSPubkeyRegistry is BLSPubkeyRegistryStorage { } /** - * @notice Creates a new quorum by pushing its first apk update + * @notice Initializes a new quorum by pushing its first apk update * @param quorumNumber The number of the new quorum */ - function createQuorum(uint8 quorumNumber) public virtual onlyRegistryCoordinator { + function initializeQuorum(uint8 quorumNumber) public virtual onlyRegistryCoordinator { require(quorumApkUpdates[quorumNumber].length == 0, "BLSPubkeyRegistry.createQuorum: quorum already exists"); quorumApkUpdates[quorumNumber].push(ApkUpdate({ diff --git a/src/BLSRegistryCoordinatorWithIndices.sol b/src/BLSRegistryCoordinatorWithIndices.sol index e1416c9e..b1338553 100644 --- a/src/BLSRegistryCoordinatorWithIndices.sol +++ b/src/BLSRegistryCoordinatorWithIndices.sol @@ -9,6 +9,7 @@ import "eigenlayer-contracts/src/contracts/interfaces/ISlasher.sol"; import "eigenlayer-contracts/src/contracts/libraries/EIP1271SignatureUtils.sol"; import "eigenlayer-contracts/src/contracts/permissions/Pausable.sol"; +<<<<<<< HEAD import "./interfaces/IBLSRegistryCoordinatorWithIndices.sol"; import "./interfaces/ISocketUpdater.sol"; import "./interfaces/IServiceManager.sol"; @@ -20,6 +21,15 @@ import "./interfaces/IRegistryCoordinator.sol"; import "./libraries/BitmapUtils.sol"; import "./libraries/BN254.sol"; +======= +import "src/interfaces/IBLSRegistryCoordinatorWithIndices.sol"; +import "src/interfaces/ISocketUpdater.sol"; +import "src/interfaces/IServiceManager.sol"; +import "src/interfaces/IBLSPubkeyRegistry.sol"; +import "src/interfaces/IStakeRegistry.sol"; +import "src/interfaces/IIndexRegistry.sol"; +import "src/interfaces/IRegistryCoordinator.sol"; +>>>>>>> 12b09de (fix: fix compilation issues and tests) /** * @title A `RegistryCoordinator` that has three registries: @@ -51,11 +61,11 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr /// @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 - BLSPubkeyRegistry public immutable blsPubkeyRegistry; + IBLSPubkeyRegistry public immutable blsPubkeyRegistry; /// @notice the Stake Registry contract that will keep track of operators' stakes - StakeRegistry public immutable stakeRegistry; + IStakeRegistry public immutable stakeRegistry; /// @notice the Index Registry contract that will keep track of operators' indexes - IndexRegistry public immutable indexRegistry; + IIndexRegistry public immutable indexRegistry; /// @notice the current number of quorums supported by the registry coordinator uint8 public quorumCount; @@ -114,7 +124,7 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr uint256 _initialPausedStatus, OperatorSetParam[] memory _operatorSetParams, uint96[] memory _minimumStakes, - IVoteWeigher.StrategyAndWeightingMultiplier[][] memory _strategyParams + IStakeRegistry.StrategyAndWeightingMultiplier[][] memory _strategyParams ) external initializer { require( _operatorSetParams.length == _minimumStakes.length && _minimumStakes.length == _strategyParams.length, @@ -343,7 +353,7 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr function createQuorum( OperatorSetParam memory operatorSetParams, uint96 minimumStake, - IVoteWeigher.StrategyAndWeightingMultiplier[] memory strategyParams + IStakeRegistry.StrategyAndWeightingMultiplier[] memory strategyParams ) external virtual onlyServiceManagerOwner { _createQuorum(operatorSetParams, minimumStake, strategyParams); } @@ -389,7 +399,11 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr bytes calldata quorumNumbers, BN254.G1Point memory pubkey, string memory socket +<<<<<<< HEAD ) internal virtual returns(uint32[] memory) { +======= + ) internal virtual returns(uint32[] memory) { +>>>>>>> 12b09de (fix: fix compilation issues and tests) // 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"); @@ -549,7 +563,7 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr function _createQuorum( OperatorSetParam memory operatorSetParams, uint96 minimumStake, - IVoteWeigher.StrategyAndWeightingMultiplier[] memory strategyParams + IStakeRegistry.StrategyAndWeightingMultiplier[] memory strategyParams ) internal { // Increment the total quorum count. Fails if we're already at the max uint8 prevQuorumCount = quorumCount; @@ -561,9 +575,9 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr // Initialize the quorum here and in each registry _setOperatorSetParams(quorumNumber, operatorSetParams); - stakeRegistry.createQuorum(quorumNumber, minimumStake, strategyParams); - indexRegistry.createQuorum(quorumNumber); - blsPubkeyRegistry.createQuorum(quorumNumber); + stakeRegistry.initializeQuorum(quorumNumber, minimumStake, strategyParams); + indexRegistry.initializeQuorum(quorumNumber); + blsPubkeyRegistry.initializeQuorum(quorumNumber); } function _setOperatorSetParams(uint8 quorumNumber, OperatorSetParam memory operatorSetParams) internal { diff --git a/src/IndexRegistry.sol b/src/IndexRegistry.sol index 122a8cde..14db2a2c 100644 --- a/src/IndexRegistry.sol +++ b/src/IndexRegistry.sol @@ -85,6 +85,7 @@ contract IndexRegistry is IndexRegistryStorage { for (uint256 i = 0; i < quorumNumbers.length; i++) { uint8 quorumNumber = uint8(quorumNumbers[i]); uint32 indexOfOperatorToRemove = operatorIdToIndex[quorumNumber][operatorId]; + uint256 historyLength = _totalOperatorsHistory[quorumNumber].length; _processOperatorRemoval({ operatorId: operatorId, quorumNumber: quorumNumber, @@ -98,10 +99,10 @@ contract IndexRegistry is IndexRegistryStorage { } /** - * @notice Creates a new quorum by pushing its first quorum update + * @notice Initialize a quorum by pushing its first quorum update * @param quorumNumber The number of the new quorum */ - function createQuorum(uint8 quorumNumber) public virtual onlyRegistryCoordinator { + function initializeQuorum(uint8 quorumNumber) public virtual onlyRegistryCoordinator { require(_totalOperatorsHistory[quorumNumber].length == 0, "IndexRegistry.createQuorum: quorum already exists"); _totalOperatorsHistory[quorumNumber].push(QuorumUpdate({ @@ -132,10 +133,10 @@ contract IndexRegistry is IndexRegistryStorage { * @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 { - OperatorUpdate memory latestOperatorUpdate; - latestOperatorUpdate.operatorId = operatorId; - latestOperatorUpdate.fromBlockNumber = uint32(block.number); - _indexToOperatorIdHistory[quorumNumber][index].push(latestOperatorUpdate); + _indexToOperatorIdHistory[quorumNumber][index].push(OperatorUpdate({ + operatorId: operatorId, + fromBlockNumber: uint32(block.number) + })); operatorIdToIndex[quorumNumber][operatorId] = index; diff --git a/src/StakeRegistry.sol b/src/StakeRegistry.sol index 10bf2c1c..670df5cd 100644 --- a/src/StakeRegistry.sol +++ b/src/StakeRegistry.sol @@ -7,7 +7,10 @@ import "src/interfaces/IServiceManager.sol"; import "src/interfaces/IStakeRegistry.sol"; import "src/interfaces/IRegistryCoordinator.sol"; import "src/StakeRegistryStorage.sol"; +<<<<<<< HEAD import {VoteWeigherBase} from "src/VoteWeigherBase.sol"; +======= +>>>>>>> 12b09de (fix: fix compilation issues and tests) /** * @title A `Registry` that keeps track of stakes of operators for up to 256 quorums. @@ -18,8 +21,13 @@ import {VoteWeigherBase} from "src/VoteWeigherBase.sol"; * It allows an additional functionality (in addition to registering and deregistering) to update the stake of an operator. * @author Layr Labs, Inc. */ +<<<<<<< HEAD contract StakeRegistry is VoteWeigherBase, StakeRegistryStorage { /// @notice requires that the caller is the RegistryCoordinator +======= +contract StakeRegistry is StakeRegistryStorage { + +>>>>>>> 12b09de (fix: fix compilation issues and tests) modifier onlyRegistryCoordinator() { require( msg.sender == address(registryCoordinator), @@ -32,6 +40,7 @@ contract StakeRegistry is VoteWeigherBase, StakeRegistryStorage { IRegistryCoordinator _registryCoordinator, IStrategyManager _strategyManager, IServiceManager _serviceManager +<<<<<<< HEAD ) VoteWeigherBase(_strategyManager, _serviceManager) StakeRegistryStorage(_registryCoordinator) {} /** @@ -64,6 +73,9 @@ contract StakeRegistry is VoteWeigherBase, StakeRegistryStorage { } } } +======= + ) StakeRegistryStorage(_registryCoordinator, _delegationManager, _serviceManager) {} +>>>>>>> 12b09de (fix: fix compilation issues and tests) /******************************************************************************* EXTERNAL FUNCTIONS @@ -203,15 +215,103 @@ contract StakeRegistry is VoteWeigherBase, StakeRegistryStorage { } } +<<<<<<< HEAD /******************************************************************************* EXTERNAL FUNCTIONS - SERVICE MANAGER OWNER *******************************************************************************/ /// @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 { +======= + /// @notice Initialize a new quorum and push its first history update + function initializeQuorum( + uint8 quorumNumber, + uint96 minimumStake, + StrategyAndWeightingMultiplier[] memory strategyParams + ) public virtual onlyRegistryCoordinator { + require(_totalStakeHistory[quorumNumber].length == 0, "StakeRegistry.initializeQuorum: quorum already exists"); + _addStrategyParams(quorumNumber, strategyParams); +>>>>>>> 12b09de (fix: fix compilation issues and tests) + _setMinimumStakeForQuorum(quorumNumber, minimumStake); + + _totalStakeHistory[quorumNumber].push(OperatorStakeUpdate({ + updateBlockNumber: uint32(block.number), + nextUpdateBlockNumber: 0, + stake: 0 + })); + } + +<<<<<<< HEAD +======= + function setMinimumStakeForQuorum( + uint8 quorumNumber, + uint96 minimumStake + ) public virtual onlyServiceManagerOwner quorumExists(quorumNumber) { _setMinimumStakeForQuorum(quorumNumber, minimumStake); } + /** + * @notice Adds strategies and weights to the 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 addStrategies( + uint8 quorumNumber, + StrategyAndWeightingMultiplier[] memory strategyParams + ) public virtual onlyServiceManagerOwner quorumExists(quorumNumber) { + _addStrategyParams(quorumNumber, strategyParams); + } + + /** + * @notice Remove strategies and their associated weights from the quorum's considered strategies + * @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 strategies to remove + */ + function removeStrategies( + uint8 quorumNumber, + uint256[] memory indicesToRemove + ) public virtual onlyServiceManagerOwner quorumExists(quorumNumber) { + uint256 toRemoveLength = indicesToRemove.length; + require(toRemoveLength > 0, "StakeRegistry.removeStrategyParams: no indices to remove provided"); + + StrategyAndWeightingMultiplier[] storage strategyParams = strategiesConsideredAndMultipliers[quorumNumber]; + + for (uint256 i = 0; i < toRemoveLength; i++) { + emit StrategyRemovedFromQuorum(quorumNumber, strategyParams[indicesToRemove[i]].strategy); + emit StrategyMultiplierUpdated(quorumNumber, strategyParams[indicesToRemove[i]].strategy, 0); + + // Replace index to remove with the last item in the list, then pop the last item + strategyParams[indicesToRemove[i]] = strategyParams[strategyParams.length - 1]; + strategyParams.pop(); + } + } + + /** + * @notice Modifys the weights of existing strategies for a specific quorum + * @param quorumNumber is the quorum number to which the strategies belong + * @param strategyIndices are the indices of the strategies to change + * @param newMultipliers are the new multipliers for the strategies + */ + function modifyStrategyParams( + uint8 quorumNumber, + uint256[] calldata strategyIndices, + uint96[] calldata newMultipliers + ) public virtual onlyServiceManagerOwner quorumExists(quorumNumber) { + uint256 numStrats = strategyIndices.length; + require(numStrats > 0, "StakeRegistry.modifyStrategyParams: no strategy indices provided"); + require(newMultipliers.length == numStrats, "StakeRegistry.modifyStrategyParams: input length mismatch"); + + StrategyAndWeightingMultiplier[] storage strategyParams = strategiesConsideredAndMultipliers[quorumNumber]; + + for (uint256 i = 0; i < numStrats; i++) { + // Change the strategy's associated multiplier + strategyParams[strategyIndices[i]].multiplier = newMultipliers[i]; + emit StrategyMultiplierUpdated(quorumNumber, strategyParams[strategyIndices[i]].strategy, newMultipliers[i]); + } + } + +>>>>>>> 12b09de (fix: fix compilation issues and tests) /******************************************************************************* INTERNAL FUNCTIONS *******************************************************************************/ @@ -388,6 +488,38 @@ contract StakeRegistry is VoteWeigherBase, StakeRegistryStorage { ); } +<<<<<<< HEAD +======= + /** + * @notice This function computes the total weight of the @param operator in the quorum @param quorumNumber. + * @dev this method DOES NOT check that the quorum exists + */ + function _weightOfOperatorForQuorum(uint8 quorumNumber, address operator) internal virtual view 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; + } + +>>>>>>> 12b09de (fix: fix compilation issues and tests) /******************************************************************************* VIEW FUNCTIONS *******************************************************************************/ diff --git a/src/StakeRegistryStorage.sol b/src/StakeRegistryStorage.sol index c8a47adc..7b5e192f 100644 --- a/src/StakeRegistryStorage.sol +++ b/src/StakeRegistryStorage.sol @@ -2,9 +2,17 @@ pragma solidity =0.8.12; import {IStrategyManager} from "eigenlayer-contracts/src/contracts/interfaces/IStrategyManager.sol"; +<<<<<<< HEAD import {IServiceManager} from "./interfaces/IServiceManager.sol"; import {IStakeRegistry} from "./interfaces/IStakeRegistry.sol"; import {IRegistryCoordinator} from "./interfaces/IRegistryCoordinator.sol"; +======= +import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; + +import {IServiceManager} from "src/interfaces/IServiceManager.sol"; +import {IStakeRegistry} from "src/interfaces/IStakeRegistry.sol"; +import {IRegistryCoordinator} from "src/interfaces/IRegistryCoordinator.sol"; +>>>>>>> 12b09de (fix: fix compilation issues and tests) /** * @title Storage variables for the `StakeRegistry` contract. @@ -12,6 +20,20 @@ import {IRegistryCoordinator} from "./interfaces/IRegistryCoordinator.sol"; * @notice This storage contract is separate from the logic to simplify the upgrade process. */ abstract contract StakeRegistryStorage is IStakeRegistry { + + /// @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 address of the Delegation contract for EigenLayer. + IDelegationManager public immutable delegation; + + /// @notice The ServiceManager contract for this middleware, where tasks are created / initiated. + IServiceManager public immutable serviceManager; + /// @notice the coordinator contract that this registry is associated with IRegistryCoordinator public immutable registryCoordinator; @@ -25,11 +47,23 @@ abstract contract StakeRegistryStorage is IStakeRegistry { /// @notice mapping from operator's operatorId to the history of their stake updates mapping(bytes32 => mapping(uint8 => OperatorStakeUpdate[])) internal operatorIdToStakeHistory; - constructor(IRegistryCoordinator _registryCoordinator) { + /** + * @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( + IRegistryCoordinator _registryCoordinator, + IDelegationManager _delegationManager, + IServiceManager _serviceManager + ) { registryCoordinator = _registryCoordinator; + delegation = _delegationManager; + serviceManager = _serviceManager; } // storage gap for upgradeability // slither-disable-next-line shadowing-state - uint256[65] private __GAP; + uint256[64] private __GAP; } diff --git a/src/VoteWeigherBaseStorage.sol b/src/VoteWeigherBaseStorage.sol deleted file mode 100644 index 2058e44a..00000000 --- a/src/VoteWeigherBaseStorage.sol +++ /dev/null @@ -1,50 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity =0.8.12; - -import "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; -import "eigenlayer-contracts/src/contracts/interfaces/IStrategyManager.sol"; -import "./interfaces/IServiceManager.sol"; -import "./interfaces/IVoteWeigher.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 address of the Delegation contract for EigenLayer. - IDelegationManager public immutable delegation; - - /// @notice The ServiceManager contract for this middleware, where tasks are created / initiated. - IServiceManager public immutable serviceManager; - - /** - * @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( - IDelegationManager _delegationManager, - IServiceManager _serviceManager - ) { - delegation = _delegationManager; - 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/interfaces/IBLSPubkeyRegistry.sol b/src/interfaces/IBLSPubkeyRegistry.sol index e2842f8e..08d1f380 100644 --- a/src/interfaces/IBLSPubkeyRegistry.sol +++ b/src/interfaces/IBLSPubkeyRegistry.sol @@ -62,6 +62,12 @@ interface IBLSPubkeyRegistry is IRegistry { */ function deregisterOperator(address operator, bytes calldata quorumNumbers, BN254.G1Point memory pubkey) external; + /** + * @notice Initializes a new quorum by pushing its first apk update + * @param quorumNumber The number of the new quorum + */ + function initializeQuorum(uint8 quorumNumber) external; + /// @notice Returns the current APK for the provided `quorumNumber ` function getApkForQuorum(uint8 quorumNumber) external view returns (BN254.G1Point memory); diff --git a/src/interfaces/IIndexRegistry.sol b/src/interfaces/IIndexRegistry.sol index 52b7fc2f..e7eaaf00 100644 --- a/src/interfaces/IIndexRegistry.sol +++ b/src/interfaces/IIndexRegistry.sol @@ -60,6 +60,12 @@ interface IIndexRegistry is IRegistry { */ function deregisterOperator(bytes32 operatorId, bytes calldata quorumNumbers) external; + /** + * @notice Initialize a quorum by pushing its first quorum update + * @param quorumNumber The number of the new quorum + */ + function initializeQuorum(uint8 quorumNumber) external; + /// @notice Returns the _indexToOperatorIdHistory entry for the specified `operatorIndex` and `quorumNumber` at the specified `index` function getOperatorIndexUpdateOfIndexForQuorumAtIndex(uint32 operatorIndex, uint8 quorumNumber, uint32 index) external view returns (OperatorUpdate memory); diff --git a/src/interfaces/IRegistryCoordinator.sol b/src/interfaces/IRegistryCoordinator.sol index 3ee5b1a9..21320b2d 100644 --- a/src/interfaces/IRegistryCoordinator.sol +++ b/src/interfaces/IRegistryCoordinator.sol @@ -45,6 +45,9 @@ interface IRegistryCoordinator { uint192 quorumBitmap; } + /// @notice Returns the number of quorums the registry coordinator has created + function quorumCount() external view returns (uint8); + /// @notice Returns the operator struct for the given `operator` function getOperator(address operator) external view returns (Operator memory); diff --git a/src/interfaces/IStakeRegistry.sol b/src/interfaces/IStakeRegistry.sol index e24bf6dd..f7998fac 100644 --- a/src/interfaces/IStakeRegistry.sol +++ b/src/interfaces/IStakeRegistry.sol @@ -1,20 +1,17 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity =0.8.12; +import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; +import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; + import {IRegistry} from "./IRegistry.sol"; +import {IServiceManager} from "./IServiceManager.sol"; /** * @title Interface for a `Registry` that keeps track of stakes of operators for up to 256 quorums. * @author Layr Labs, Inc. */ interface IStakeRegistry is IRegistry { - // EVENTS - /// @notice emitted whenever the stake of `operator` is updated - event StakeUpdate( - bytes32 indexed operatorId, - uint8 quorumNumber, - uint96 stake - ); // DATA STRUCTURES @@ -29,8 +26,33 @@ interface IStakeRegistry is IRegistry { uint96 stake; } + /** + * @notice In weighing a particular strategy, the amount of underlying asset for that strategy is + * multiplied by its multiplier, then divided by WEIGHTING_DIVISOR + */ + struct StrategyAndWeightingMultiplier { + IStrategy strategy; + uint96 multiplier; + } + // EVENTS + + /// @notice emitted whenever the stake of `operator` is updated + event StakeUpdate( + bytes32 indexed operatorId, + uint8 quorumNumber, + uint96 stake + ); + /// @notice emitted when the minimum stake for a quorum is updated event MinimumStakeForQuorumUpdated(uint8 indexed quorumNumber, uint96 minimumStake); + /// @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); /** * @notice Registers the `operator` with `operatorId` for the specified `quorumNumbers`. @@ -60,9 +82,65 @@ interface IStakeRegistry is IRegistry { */ function deregisterOperator(bytes32 operatorId, bytes memory quorumNumbers) external; + /** + * @notice Initialize a new quorum created by the registry coordinator by setting strategies, weights, and minimum stake + */ + function initializeQuorum(uint8 quorumNumber, uint96 minimumStake, StrategyAndWeightingMultiplier[] memory strategyParams) external; + + /// @notice Adds new strategies and the associated multipliers to the @param quorumNumber. + function addStrategies( + uint8 quorumNumber, + StrategyAndWeightingMultiplier[] memory strategyParams + ) external; + + /** + * @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 removeStrategies(uint8 quorumNumber, uint256[] calldata indicesToRemove) external; + + /** + * @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 modifyStrategyParams( + uint8 quorumNumber, + uint256[] calldata strategyIndices, + uint96[] calldata newMultipliers + ) external; + + /// @notice Constant used as a divisor in calculating weights. + function WEIGHTING_DIVISOR() external pure returns (uint256); + + /// @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); + /// @notice In order to register for a quorum i, an operator must have at least `minimumStakeForQuorum[i]` function minimumStakeForQuorum(uint256 quorumNumber) external view returns (uint96); + /// @notice Returns the length of the dynamic array stored in `strategiesConsideredAndMultipliers[quorumNumber]`. + function strategiesConsideredAndMultipliersLength(uint8 quorumNumber) external view returns (uint256); + + /// @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); + + /** + * @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 weightOfOperatorForQuorum(uint8 quorumNumber, address operator) external view returns (uint96); + /** * @notice Returns the entire `operatorIdToStakeHistory[operatorId][quorumNumber]` array. * @param operatorId The id of the operator of interest. diff --git a/src/interfaces/IVoteWeigher.sol b/src/interfaces/IVoteWeigher.sol deleted file mode 100644 index 15af7ca3..00000000 --- a/src/interfaces/IVoteWeigher.sol +++ /dev/null @@ -1,97 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity >=0.5.0; - -import {IServiceManager} from "./IServiceManager.sol"; -import {ISlasher} from "eigenlayer-contracts/src/contracts/interfaces/ISlasher.sol"; -import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; -import {IStrategyManager} from "eigenlayer-contracts/src/contracts/interfaces/IStrategyManager.sol"; -import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; - -/** - * @title Interface for a `VoteWeigher`-type contract. - * @author Layr Labs, Inc. - * @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service - * @notice Note that `NUMBER_OF_QUORUMS` is expected to remain constant, as suggested by its uppercase formatting. - */ -interface IVoteWeigher { - /// @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); - - /** - * @notice In weighing a particular strategy, the amount of underlying asset for that strategy is - * multiplied by its multiplier, then divided by WEIGHTING_DIVISOR - */ - struct StrategyAndWeightingMultiplier { - IStrategy strategy; - uint96 multiplier; - } - - /// @notice Constant used as a divisor in calculating weights. - function WEIGHTING_DIVISOR() external pure returns (uint256); - - /// @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); - - /** - * @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 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); - - /// @notice Create a new quorum and add the strategies and their associated weights to the quorum. - function createQuorum(StrategyAndWeightingMultiplier[] memory _strategiesConsideredAndMultipliers) external; - - /// @notice Adds new strategies and the associated multipliers to the @param quorumNumber. - function addStrategiesConsideredAndMultipliers( - uint8 quorumNumber, - StrategyAndWeightingMultiplier[] memory _newStrategiesConsideredAndMultipliers - ) external; - - /** - * @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; - - /** - * @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; - - /// @notice Returns the length of the dynamic array stored in `strategiesConsideredAndMultipliers[quorumNumber]`. - function strategiesConsideredAndMultipliersLength(uint8 quorumNumber) external view returns (uint256); -} diff --git a/test/EigenLayerDeployer.t.sol b/test/EigenLayerDeployer.t.sol new file mode 100644 index 00000000..d33f523a --- /dev/null +++ b/test/EigenLayerDeployer.t.sol @@ -0,0 +1,374 @@ +// 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/IBeacon.sol"; +import "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol"; + +import "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; +import "eigenlayer-contracts/src/contracts/core/DelegationManager.sol"; +import {StakeRegistry} from "src/StakeRegistry.sol"; + +import {IETHPOSDeposit} from "eigenlayer-contracts/src/contracts/interfaces/IETHPOSDeposit.sol"; +import {IBeaconChainOracle} from "eigenlayer-contracts/src/contracts/interfaces/IBeaconChainOracle.sol"; + +import {StrategyManager} from "eigenlayer-contracts/src/contracts/core/StrategyManager.sol"; +import {StrategyBase} from "eigenlayer-contracts/src/contracts/strategies/StrategyBase.sol"; +import {Slasher} from "eigenlayer-contracts/src/contracts/core/Slasher.sol"; + +import {EigenPod} from "eigenlayer-contracts/src/contracts/pods/EigenPod.sol"; +import {EigenPodManager} from "eigenlayer-contracts/src/contracts/pods/EigenPodManager.sol"; +import {DelayedWithdrawalRouter} from "eigenlayer-contracts/src/contracts/pods/DelayedWithdrawalRouter.sol"; + +import {PauserRegistry} from "eigenlayer-contracts/src/contracts/permissions/PauserRegistry.sol"; + + +import {Operators} from "test/utils/Operators.sol"; + +import {WETH} from "eigenlayer-contracts/src/test/mocks/LiquidStakingToken.sol"; +import {IDelayedWithdrawalRouter} from "eigenlayer-contracts/src/contracts/interfaces/IDelayedWithdrawalRouter.sol"; +import {EmptyContract} from "eigenlayer-contracts/src/test/mocks/EmptyContract.sol"; +import {ETHPOSDepositMock } from "eigenlayer-contracts/src/test/mocks/ETHDepositMock.sol"; +import {BeaconChainOracleMock} from "eigenlayer-contracts/src/test/mocks/BeaconChainOracleMock.sol"; + +import "forge-std/Test.sol"; + +contract EigenLayerDeployer is Operators { + + Vm cheats = Vm(HEVM_ADDRESS); + + // EigenLayer contracts + ProxyAdmin public eigenLayerProxyAdmin; + PauserRegistry public eigenLayerPauserReg; + + Slasher public slasher; + DelegationManager public delegation; + StrategyManager public strategyManager; + EigenPodManager public eigenPodManager; + IEigenPod public pod; + IDelayedWithdrawalRouter public delayedWithdrawalRouter; + IETHPOSDeposit public ethPOSDeposit; + IBeacon public eigenPodBeacon; + + // testing/mock contracts + IERC20 public eigenToken; + IERC20 public weth; + StrategyBase public wethStrat; + StrategyBase public eigenStrat; + StrategyBase public baseStrategyImplementation; + EmptyContract public emptyContract; + + mapping(uint256 => IStrategy) public strategies; + + //from testing seed phrase + bytes32 priv_key_0 = 0x1234567812345678123456781234567812345678123456781234567812345678; + bytes32 priv_key_1 = 0x1234567812345678123456781234567812345698123456781234567812348976; + + //strategy indexes for undelegation (see commitUndelegation function) + uint256[] public strategyIndexes; + address[2] public stakers; + address sample_registrant = cheats.addr(436364636); + + address[] public slashingContracts; + + uint256 wethInitialSupply = 10e50; + uint256 public constant eigenTotalSupply = 1000e18; + uint256 nonce = 69; + uint256 public gasLimit = 750000; + uint32 PARTIAL_WITHDRAWAL_FRAUD_PROOF_PERIOD_BLOCKS = 7 days / 12 seconds; + uint256 REQUIRED_BALANCE_WEI = 31 ether; + uint64 MAX_PARTIAL_WTIHDRAWAL_AMOUNT_GWEI = 1 ether / 1e9; + uint64 MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR = 32e9; + uint64 GOERLI_GENESIS_TIME = 1616508000; + + address pauser; + address unpauser; + address theMultiSig = address(420); + address operator = address(0x4206904396bF2f8b173350ADdEc5007A52664293); //sk: e88d9d864d5d731226020c5d2f02b62a4ce2a4534a39c225d32d3db795f83319 + address acct_0 = cheats.addr(uint256(priv_key_0)); + address acct_1 = cheats.addr(uint256(priv_key_1)); + address _challenger = address(0x6966904396bF2f8b173350bCcec5007A52669873); + address public eigenLayerReputedMultisig = address(this); + + address eigenLayerProxyAdminAddress; + address eigenLayerPauserRegAddress; + address slasherAddress; + address delegationAddress; + address strategyManagerAddress; + address eigenPodManagerAddress; + address podAddress; + address delayedWithdrawalRouterAddress; + address eigenPodBeaconAddress; + address beaconChainOracleAddress; + address emptyContractAddress; + 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; + + + //ensures that a passed in address is not set to true in the fuzzedAddressMapping + modifier fuzzedAddress(address addr) virtual { + cheats.assume(fuzzedAddressMapping[addr] == false); + _; + } + + modifier cannotReinit() { + cheats.expectRevert(bytes("Initializable: contract is already initialized")); + _; + } + + //performs basic deployment before each test + // for fork tests run: forge test -vv --fork-url https://eth-goerli.g.alchemy.com/v2/demo -vv + function setUp() public virtual { + try vm.envUint("CHAIN_ID") returns (uint256 chainId) { + if (chainId == 31337) { + _deployEigenLayerContractsLocal(); + } else if (chainId == 5) { + _deployEigenLayerContractsGoerli(); + } + // If CHAIN_ID ENV is not set, assume local deployment on 31337 + } catch { + _deployEigenLayerContractsLocal(); + } + + fuzzedAddressMapping[address(0)] = true; + fuzzedAddressMapping[address(eigenLayerProxyAdmin)] = true; + fuzzedAddressMapping[address(strategyManager)] = true; + fuzzedAddressMapping[address(eigenPodManager)] = true; + fuzzedAddressMapping[address(delegation)] = true; + fuzzedAddressMapping[address(slasher)] = true; + } + + function _deployEigenLayerContractsGoerli() internal { + _setAddresses(goerliDeploymentConfig); + pauser = operationsMultisig; + unpauser = executorMultisig; + // deploy proxy admin for ability to upgrade proxy contracts + eigenLayerProxyAdmin = ProxyAdmin(eigenLayerProxyAdminAddress); + + emptyContract = new EmptyContract(); + + //deploy pauser registry + eigenLayerPauserReg = PauserRegistry(eigenLayerPauserRegAddress); + + delegation = DelegationManager(delegationAddress); + strategyManager = StrategyManager(strategyManagerAddress); + slasher = Slasher(slasherAddress); + eigenPodManager = EigenPodManager(eigenPodManagerAddress); + delayedWithdrawalRouter = DelayedWithdrawalRouter(delayedWithdrawalRouterAddress); + + beaconChainOracleAddress = address(new BeaconChainOracleMock()); + + ethPOSDeposit = new ETHPOSDepositMock(); + pod = new EigenPod(ethPOSDeposit, delayedWithdrawalRouter, eigenPodManager, MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR, 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) + ); + + // deploy StrategyBase contract implementation, then create upgradeable proxy that points to implementation and initialize it + baseStrategyImplementation = new StrategyBase(strategyManager); + wethStrat = StrategyBase( + address( + new TransparentUpgradeableProxy( + address(baseStrategyImplementation), + address(eigenLayerProxyAdmin), + abi.encodeWithSelector(StrategyBase.initialize.selector, weth, eigenLayerPauserReg) + ) + ) + ); + + eigenToken = new ERC20PresetFixedSupply( + "eigen", + "EIGEN", + wethInitialSupply, + address(this) + ); + + // deploy upgradeable proxy that points to StrategyBase implementation and initialize it + eigenStrat = StrategyBase( + address( + new TransparentUpgradeableProxy( + address(baseStrategyImplementation), + address(eigenLayerProxyAdmin), + abi.encodeWithSelector(StrategyBase.initialize.selector, eigenToken, eigenLayerPauserReg) + ) + ) + ); + + stakers = [acct_0, acct_1]; + } + + function _deployEigenLayerContractsLocal() internal { + pauser = address(69); + unpauser = address(489); + // deploy proxy admin for ability to upgrade proxy contracts + eigenLayerProxyAdmin = new ProxyAdmin(); + + //deploy pauser registry + address[] memory pausers = new address[](1); + pausers[0] = pauser; + eigenLayerPauserReg = new PauserRegistry(pausers, unpauser); + + /** + * 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), "")) + ); + + ethPOSDeposit = new ETHPOSDepositMock(); + pod = new EigenPod(ethPOSDeposit, delayedWithdrawalRouter, eigenPodManager, MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR, GOERLI_GENESIS_TIME); + + eigenPodBeacon = new UpgradeableBeacon(address(pod)); + + // Second, deploy the *implementation* contracts, using the *proxy contracts* as inputs + 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); + DelayedWithdrawalRouter 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, + eigenLayerReputedMultisig, + eigenLayerPauserReg, + 0/*initialPausedStatus*/ + ) + ); + eigenLayerProxyAdmin.upgradeAndCall( + TransparentUpgradeableProxy(payable(address(strategyManager))), + address(strategyManagerImplementation), + abi.encodeWithSelector( + StrategyManager.initialize.selector, + eigenLayerReputedMultisig, + eigenLayerReputedMultisig, + eigenLayerPauserReg, + 0/*initialPausedStatus*/ + ) + ); + eigenLayerProxyAdmin.upgradeAndCall( + TransparentUpgradeableProxy(payable(address(slasher))), + address(slasherImplementation), + abi.encodeWithSelector( + Slasher.initialize.selector, + eigenLayerReputedMultisig, + eigenLayerPauserReg, + 0/*initialPausedStatus*/ + ) + ); + eigenLayerProxyAdmin.upgradeAndCall( + TransparentUpgradeableProxy(payable(address(eigenPodManager))), + address(eigenPodManagerImplementation), + abi.encodeWithSelector( + EigenPodManager.initialize.selector, + type(uint256).max, // maxPods + beaconChainOracleAddress, + eigenLayerReputedMultisig, + eigenLayerPauserReg, + 0/*initialPausedStatus*/ + ) + ); + uint256 initPausedStatus = 0; + uint256 withdrawalDelayBlocks = PARTIAL_WITHDRAWAL_FRAUD_PROOF_PERIOD_BLOCKS; + eigenLayerProxyAdmin.upgradeAndCall( + TransparentUpgradeableProxy(payable(address(delayedWithdrawalRouter))), + address(delayedWithdrawalRouterImplementation), + 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) + ); + + // deploy StrategyBase contract implementation, then create upgradeable proxy that points to implementation and initialize it + baseStrategyImplementation = new StrategyBase(strategyManager); + wethStrat = StrategyBase( + address( + new TransparentUpgradeableProxy( + address(baseStrategyImplementation), + address(eigenLayerProxyAdmin), + abi.encodeWithSelector(StrategyBase.initialize.selector, weth, eigenLayerPauserReg) + ) + ) + ); + + eigenToken = new ERC20PresetFixedSupply( + "eigen", + "EIGEN", + wethInitialSupply, + address(this) + ); + + // deploy upgradeable proxy that points to StrategyBase implementation and initialize it + eigenStrat = StrategyBase( + address( + new TransparentUpgradeableProxy( + address(baseStrategyImplementation), + address(eigenLayerProxyAdmin), + abi.encodeWithSelector(StrategyBase.initialize.selector, eigenToken, eigenLayerPauserReg) + ) + ) + ); + + stakers = [acct_0, acct_1]; + } + + function _setAddresses(string memory config) internal { + 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"); + 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/test/harnesses/BLSRegistryCoordinatorWithIndicesHarness.sol b/test/harnesses/BLSRegistryCoordinatorWithIndicesHarness.sol index 6045bf08..fb4af560 100644 --- a/test/harnesses/BLSRegistryCoordinatorWithIndicesHarness.sol +++ b/test/harnesses/BLSRegistryCoordinatorWithIndicesHarness.sol @@ -14,6 +14,10 @@ contract BLSRegistryCoordinatorWithIndicesHarness is BLSRegistryCoordinatorWithI ) BLSRegistryCoordinatorWithIndices(_slasher, _serviceManager, _stakeRegistry, _blsPubkeyRegistry, _indexRegistry) { } + function setQuorumCount(uint8 count) external { + quorumCount = count; + } + function setOperatorId(address operator, bytes32 operatorId) external { _operators[operator].operatorId = operatorId; } diff --git a/test/harnesses/StakeRegistryHarness.sol b/test/harnesses/StakeRegistryHarness.sol index 96fc9c94..16610afb 100644 --- a/test/harnesses/StakeRegistryHarness.sol +++ b/test/harnesses/StakeRegistryHarness.sol @@ -9,9 +9,9 @@ contract StakeRegistryHarness is StakeRegistry { constructor( IRegistryCoordinator _registryCoordinator, - IStrategyManager _strategyManager, + IDelegationManager _delegationManager, IServiceManager _serviceManager - ) StakeRegistry(_registryCoordinator, _strategyManager, _serviceManager) { + ) StakeRegistry(_registryCoordinator, _delegationManager, _serviceManager) { } function recordOperatorStakeUpdate(bytes32 operatorId, uint8 quorumNumber, uint96 newStake) external returns(int256) { @@ -36,6 +36,10 @@ contract StakeRegistryHarness is StakeRegistry { return _weightOfOperatorForQuorum[quorumNumber][operator]; } + function _weightOfOperatorForQuorum(uint8 quorumNumber, address operator) internal 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; diff --git a/test/mocks/RegistryCoordinatorMock.sol b/test/mocks/RegistryCoordinatorMock.sol index d959d7d1..fee2ee96 100644 --- a/test/mocks/RegistryCoordinatorMock.sol +++ b/test/mocks/RegistryCoordinatorMock.sol @@ -6,6 +6,7 @@ import "../../src/interfaces/IRegistryCoordinator.sol"; contract RegistryCoordinatorMock is IRegistryCoordinator { + function quorumCount() external view returns (uint8) {} /// @notice Returns the bitmap of the quorums the operator is registered for. function operatorIdToQuorumBitmap(bytes32 pubkeyHash) external view returns (uint256){} diff --git a/test/mocks/StakeRegistryMock.sol b/test/mocks/StakeRegistryMock.sol index 0819228b..f6de93e7 100644 --- a/test/mocks/StakeRegistryMock.sol +++ b/test/mocks/StakeRegistryMock.sol @@ -40,9 +40,60 @@ contract StakeRegistryMock is IStakeRegistry { */ function deregisterOperator(bytes32 operatorId, bytes memory quorumNumbers) external {} + /** + * @notice Initialize a new quorum created by the registry coordinator by setting strategies, weights, and minimum stake + */ + function initializeQuorum(uint8 quorumNumber, uint96 minimumStake, StrategyAndWeightingMultiplier[] memory strategyParams) external {} + + /// @notice Adds new strategies and the associated multipliers to the @param quorumNumber. + function addStrategies( + uint8 quorumNumber, + StrategyAndWeightingMultiplier[] memory strategyParams + ) external {} + + /** + * @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 removeStrategies(uint8 quorumNumber, uint256[] calldata indicesToRemove) external {} + + /** + * @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 modifyStrategyParams( + uint8 quorumNumber, + uint256[] calldata strategyIndices, + uint96[] calldata newMultipliers + ) external {} + + function delegation() external view returns (IDelegationManager) {} + function serviceManager() external view returns (IServiceManager) {} + + function WEIGHTING_DIVISOR() external pure returns (uint256) {} + + function strategiesConsideredAndMultipliersLength(uint8 quorumNumber) external view returns (uint256) {} + /// @notice In order to register for a quorum i, an operator must have at least `minimumStakeForQuorum[i]` function minimumStakeForQuorum(uint256 quorumNumber) external view returns (uint96) {} + /// @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) {} + + /** + * @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 weightOfOperatorForQuorum(uint8 quorumNumber, address operator) external view returns (uint96) {} + /** * @notice Returns the entire `operatorIdToStakeHistory[operatorId][quorumNumber]` array. * @param operatorId The id of the operator of interest. diff --git a/test/unit/BLSOperatorStateRetrieverUnit.t.sol b/test/unit/BLSOperatorStateRetrieverUnit.t.sol index 791803a2..6842bf16 100644 --- a/test/unit/BLSOperatorStateRetrieverUnit.t.sol +++ b/test/unit/BLSOperatorStateRetrieverUnit.t.sol @@ -108,8 +108,8 @@ contract BLSOperatorStateRetrieverUnitTests is MockAVSDeployer { // 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"); + assertEq(checkSignaturesIndices.quorumApkIndices[i], expectedOperatorOverallIndices[quorumNumber].length, "quorumApkIndex should be the number of registered operators for the quorum"); + assertEq(checkSignaturesIndices.totalStakeIndices[i], expectedOperatorOverallIndices[quorumNumber].length, "totalStakeIndex should be the number of registered operators for the quorum"); } } @@ -151,8 +151,8 @@ contract BLSOperatorStateRetrieverUnitTests is MockAVSDeployer { // 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"); + assertEq(checkSignaturesIndices.quorumApkIndices[i], expectedOperatorOverallIndices[quorumNumber].length, "quorumApkIndex should be the number of registered operators for the quorum"); + assertEq(checkSignaturesIndices.totalStakeIndices[i], expectedOperatorOverallIndices[quorumNumber].length, "totalStakeIndex should be the number of registered operators for the quorum"); } // assert the quorum bitmap and stake indices are zero because there have been no kicks or stake updates diff --git a/test/unit/BLSPubkeyRegistryUnit.t.sol b/test/unit/BLSPubkeyRegistryUnit.t.sol index b241aa40..83063129 100644 --- a/test/unit/BLSPubkeyRegistryUnit.t.sol +++ b/test/unit/BLSPubkeyRegistryUnit.t.sol @@ -26,10 +26,16 @@ contract BLSPubkeyRegistryUnitTests is Test { uint8 internal defaultQuorumNumber = 0; + // Track initialized quorums so we can filter these out when fuzzing + mapping(uint8 => bool) initializedQuorums; + function setUp() external { registryCoordinator = new RegistryCoordinatorMock(); pkCompendium = new BLSPublicKeyCompendiumMock(); blsPubkeyRegistry = new BLSPubkeyRegistry(registryCoordinator, pkCompendium); + + // Initialize a quorum + _initializeQuorum(defaultQuorumNumber); } function testConstructorArgs() public view { @@ -99,19 +105,23 @@ contract BLSPubkeyRegistryUnitTests is Test { bytes memory quorumNumbers = new bytes(2); quorumNumbers[0] = bytes1(quorumNumber1); quorumNumbers[1] = bytes1(quorumNumber2); + if (!initializedQuorums[quorumNumber1]) { + _initializeFuzzedQuorum(quorumNumber1); + } + if (!initializedQuorums[quorumNumber2]) { + _initializeFuzzedQuorum(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); + cheats.prank(defaultOperator); pkCompendium.registerPublicKey(defaultPubKey); - cheats.stopPrank(); - cheats.startPrank(address(registryCoordinator)); + cheats.prank(address(registryCoordinator)); blsPubkeyRegistry.registerOperator(defaultOperator, quorumNumbers, defaultPubKey); - cheats.stopPrank(); //check quorum apk updates for(uint8 i = 0; i < quorumNumbers.length; i++){ @@ -148,6 +158,8 @@ contract BLSPubkeyRegistryUnitTests is Test { bytes memory quorumNumbers = new bytes(2); quorumNumbers[0] = bytes1(quorumNumber1); quorumNumbers[1] = bytes1(quorumNumber2); + _initializeFuzzedQuorum(quorumNumber1); + _initializeFuzzedQuorum(quorumNumber2); testQuorumApkUpdates(quorumNumber1, quorumNumber2); @@ -200,19 +212,20 @@ contract BLSPubkeyRegistryUnitTests is Test { 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"); + require(quorumApkHash == blsPubkeyRegistry.getApkHashForQuorumAtBlockNumberFromIndex(defaultQuorumNumber, uint32(block.number + blockGap) , i + 1), "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"); + require(quorumApkHash == blsPubkeyRegistry.getApkHashForQuorumAtBlockNumberFromIndex(defaultQuorumNumber, uint32(block.number + blockGap) , i + 2), "incorrect quorum aok updates"); cheats.roll(block.number + 100); i++; } } } + /// TODO - fix test function testIncorrectBlockNumberForQuorumApkUpdates(uint256 numRegistrants, uint32 indexToCheck, uint32 wrongBlockNumber) external { cheats.assume(numRegistrants > 0 && numRegistrants < 100); cheats.assume(indexToCheck < numRegistrants - 1); @@ -225,15 +238,33 @@ contract BLSPubkeyRegistryUnitTests is Test { cheats.roll(block.number + 100); } if(wrongBlockNumber < startingBlockNumber + indexToCheck*100){ + emit log_named_uint("index too recent: ", indexToCheck); cheats.expectRevert(bytes("BLSPubkeyRegistry._validateApkHashForQuorumAtBlockNumber: index too recent")); blsPubkeyRegistry.getApkHashForQuorumAtBlockNumberFromIndex(defaultQuorumNumber, wrongBlockNumber, indexToCheck); } - if (wrongBlockNumber >= startingBlockNumber + (indexToCheck+1)*100){ + if (wrongBlockNumber >= startingBlockNumber + (indexToCheck+1)*100){ + emit log_named_uint("index not latest: ", indexToCheck); cheats.expectRevert(bytes("BLSPubkeyRegistry._validateApkHashForQuorumAtBlockNumber: not latest apk update")); blsPubkeyRegistry.getApkHashForQuorumAtBlockNumberFromIndex(defaultQuorumNumber, wrongBlockNumber, indexToCheck); } } + function _initializeQuorum( + uint8 quorumNumber + ) internal { + cheats.prank(address(registryCoordinator)); + + blsPubkeyRegistry.initializeQuorum(quorumNumber); + initializedQuorums[quorumNumber] = true; + } + + function _initializeFuzzedQuorum( + uint8 quorumNumber + ) internal { + cheats.assume(!initializedQuorums[quorumNumber]); + _initializeQuorum(quorumNumber); + } + function _getRandomPk(uint256 seed) internal view returns (bytes32) { return keccak256(abi.encodePacked(block.timestamp, seed)); } diff --git a/test/unit/BLSRegistryCoordinatorWithIndicesUnit.t.sol b/test/unit/BLSRegistryCoordinatorWithIndicesUnit.t.sol index 827be37c..3bff2d3a 100644 --- a/test/unit/BLSRegistryCoordinatorWithIndicesUnit.t.sol +++ b/test/unit/BLSRegistryCoordinatorWithIndicesUnit.t.sol @@ -58,7 +58,15 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { // make sure the contract intializers are disabled cheats.expectRevert(bytes("Initializable: contract is already initialized")); - registryCoordinator.initialize(churnApprover, ejector, operatorSetParams, pauserRegistry, 0/*initialPausedStatus*/); + registryCoordinator.initialize( + churnApprover, + ejector, + pauserRegistry, + 0/*initialPausedStatus*/, + operatorSetParams, + new uint96[](0), + new IStakeRegistry.StrategyAndWeightingMultiplier[][](0) + ); } function testSetOperatorSetParams_NotServiceManagerOwner_Reverts() public { @@ -135,7 +143,7 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { bytes memory quorumNumbersNotCreated = new bytes(1); quorumNumbersNotCreated[0] = 0x0B; cheats.prank(defaultOperator); - cheats.expectRevert("StakeRegistry._registerOperator: greatest quorumNumber must be less than quorumCount"); + cheats.expectRevert("BLSPubkeyRegistry._processQuorumApkUpdate: quorum does not exist"); registryCoordinator.registerOperatorWithCoordinator(quorumNumbersNotCreated, defaultPubKey, defaultSocket); } diff --git a/test/unit/IndexRegistryUnit.t.sol b/test/unit/IndexRegistryUnit.t.sol index 4f718e9a..3026a0da 100644 --- a/test/unit/IndexRegistryUnit.t.sol +++ b/test/unit/IndexRegistryUnit.t.sol @@ -20,12 +20,20 @@ contract IndexRegistryUnitTests is Test { bytes32 operatorId2 = bytes32(uint256(35)); bytes32 operatorId3 = bytes32(uint256(36)); + // Track initialized quorums so we can filter these out when fuzzing + mapping(uint8 => bool) initializedQuorums; + // Test 0 length operators in operators to remove function setUp() public { // deploy the contract registryCoordinatorMock = new RegistryCoordinatorMock(); indexRegistry = new IndexRegistry(registryCoordinatorMock); bitmapUtilsWrapper = new BitmapUtilsWrapper(); + + // Initialize quorums and add to fuzz filter + _initializeQuorum(defaultQuorumNumber); + _initializeQuorum(defaultQuorumNumber + 1); + _initializeQuorum(defaultQuorumNumber + 2); } function testConstructor() public { @@ -72,7 +80,7 @@ contract IndexRegistryUnitTests is Test { // Check _totalOperatorsHistory updates IIndexRegistry.QuorumUpdate memory quorumUpdate = indexRegistry - .getQuorumUpdateAtIndex(1, 0); + .getQuorumUpdateAtIndex(1, 1); require( quorumUpdate.numOperators == 1, "IndexRegistry.registerOperator: totalOperatorsHistory num operators not 1" @@ -117,7 +125,7 @@ contract IndexRegistryUnitTests is Test { // Check _totalOperatorsHistory updates IIndexRegistry.QuorumUpdate memory quorumUpdate = indexRegistry - .getQuorumUpdateAtIndex(2, 0); + .getQuorumUpdateAtIndex(2, 1); require( quorumUpdate.numOperators == 1, "IndexRegistry.registerOperator: totalOperatorsHistory num operators not 1" @@ -160,7 +168,7 @@ contract IndexRegistryUnitTests is Test { // Check _totalOperatorsHistory updates for quorum 1 IIndexRegistry.QuorumUpdate memory quorumUpdate = indexRegistry - .getQuorumUpdateAtIndex(1, 0); + .getQuorumUpdateAtIndex(1, 1); require( quorumUpdate.numOperators == 1, "IndexRegistry.registerOperator: totalOperatorsHistory numOperators not 1" @@ -183,7 +191,7 @@ contract IndexRegistryUnitTests is Test { ); // Check _totalOperatorsHistory updates for quorum 2 - quorumUpdate = indexRegistry.getQuorumUpdateAtIndex(2, 0); + quorumUpdate = indexRegistry.getQuorumUpdateAtIndex(2, 1); require( quorumUpdate.numOperators == 1, "IndexRegistry.registerOperator: totalOperatorsHistory num operators not 1" @@ -227,7 +235,7 @@ contract IndexRegistryUnitTests is Test { // Check _totalOperatorsHistory updates IIndexRegistry.QuorumUpdate memory quorumUpdate = indexRegistry - .getQuorumUpdateAtIndex(1, 1); + .getQuorumUpdateAtIndex(1, 2); require( quorumUpdate.numOperators == 2, "IndexRegistry.registerOperator: totalOperatorsHistory num operators not 2" @@ -264,7 +272,7 @@ contract IndexRegistryUnitTests is Test { // Check total operators IIndexRegistry.QuorumUpdate memory quorumUpdate = indexRegistry - .getQuorumUpdateAtIndex(defaultQuorumNumber, 1); + .getQuorumUpdateAtIndex(defaultQuorumNumber, 2); require(quorumUpdate.fromBlockNumber == block.number, "fromBlockNumber not set correctly"); require(quorumUpdate.numOperators == 0, "incorrect total number of operators"); require(indexRegistry.totalOperatorsForQuorum(1) == 0, "operator not deregistered correctly"); @@ -299,7 +307,7 @@ contract IndexRegistryUnitTests is Test { // Check total operators for removed quorums for (uint256 i = 0; i < quorumsToRemove.length; i++) { IIndexRegistry.QuorumUpdate memory quorumUpdate = indexRegistry - .getQuorumUpdateAtIndex(uint8(quorumsToRemove[i]), 3); // 4 updates total + .getQuorumUpdateAtIndex(uint8(quorumsToRemove[i]), 4); // 5 updates total require(quorumUpdate.fromBlockNumber == block.number, "fromBlockNumber not set correctly"); require(quorumUpdate.numOperators == 2, "incorrect total number of operators"); require( @@ -359,7 +367,7 @@ contract IndexRegistryUnitTests is Test { uint32 prevTotal = indexRegistry.getTotalOperatorsForQuorumAtBlockNumberByIndex( defaultQuorumNumber, uint32(block.number - 10), - 0 + 1 ); require(prevTotal == 1, "IndexRegistry.getTotalOperatorsForQuorumAtBlockNumberByIndex: prev total not 1"); @@ -367,7 +375,7 @@ contract IndexRegistryUnitTests is Test { uint32 currentTotal = indexRegistry.getTotalOperatorsForQuorumAtBlockNumberByIndex( defaultQuorumNumber, uint32(block.number), - 1 + 2 ); require(currentTotal == 2, "IndexRegistry.getTotalOperatorsForQuorumAtBlockNumberByIndex: current total not 2"); } @@ -463,13 +471,10 @@ contract IndexRegistryUnitTests is Test { * 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); + // Initialize quorum numbers, skipping invalid tests + _initializeFuzzedQuorums(quorumNumbers); - // Register operator + // Register for quorums cheats.prank(address(registryCoordinatorMock)); uint32[] memory numOperatorsPerQuorum = indexRegistry.registerOperator(operatorId1, quorumNumbers); @@ -500,7 +505,7 @@ contract IndexRegistryUnitTests is Test { // Check _totalOperatorsHistory updates IIndexRegistry.QuorumUpdate memory quorumUpdate; for (uint256 i = 0; i < quorumNumbers.length; i++) { - quorumUpdate = indexRegistry.getQuorumUpdateAtIndex(uint8(quorumNumbers[i]), 0); + quorumUpdate = indexRegistry.getQuorumUpdateAtIndex(uint8(quorumNumbers[i]), 1); require( quorumUpdate.numOperators == 1, "IndexRegistry.registerOperator: totalOperatorsHistory num operators not 1" @@ -516,12 +521,9 @@ contract IndexRegistryUnitTests is Test { } } - 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); + function testFuzzRegisterMultipleOperatorsMultipleQuorums(bytes memory quorumNumbers) public { + // Initialize quorum numbers, skipping invalid tests + _initializeFuzzedQuorums(quorumNumbers); // Register operators 1,2,3 _registerOperator(operatorId1, quorumNumbers); @@ -537,7 +539,7 @@ contract IndexRegistryUnitTests is Test { for (uint256 i = 0; i < quorumNumbers.length; i++) { quorumUpdate = indexRegistry.getQuorumUpdateAtIndex( uint8(quorumNumbers[i]), - uint32(numOperators - 1) + uint32(numOperators) ); require( quorumUpdate.numOperators == numOperators, @@ -563,13 +565,11 @@ contract IndexRegistryUnitTests is Test { } 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 + // Initialize quorum numbers, skipping invalid tests + _initializeFuzzedQuorums(quorumsToAdd); + uint bitmap = bitmapUtilsWrapper.orderedBytesArrayToBitmap(quorumsToAdd); + + // Format bitsToFlip = bound(bitsToFlip, 1, quorumsToAdd.length); uint256 bitsFlipped = 0; uint256 bitPosition = 0; @@ -605,7 +605,7 @@ contract IndexRegistryUnitTests is Test { // Check total operators for removed quorums for (uint256 i = 0; i < quorumsToRemove.length; i++) { IIndexRegistry.QuorumUpdate memory quorumUpdate = indexRegistry - .getQuorumUpdateAtIndex(uint8(quorumsToRemove[i]), 2); // 3 updates total + .getQuorumUpdateAtIndex(uint8(quorumsToRemove[i]), 3); // 4 updates total require(quorumUpdate.fromBlockNumber == block.number, "fromBlockNumber not set correctly"); require(quorumUpdate.numOperators == 1, "incorrect total number of operators"); require( @@ -623,6 +623,28 @@ contract IndexRegistryUnitTests is Test { } } + function _initializeQuorum(uint8 quorumNumber) internal { + cheats.prank(address(registryCoordinatorMock)); + + // Initialize quorum and mark registered + indexRegistry.initializeQuorum(quorumNumber); + initializedQuorums[quorumNumber] = true; + } + + function _initializeFuzzedQuorums(bytes memory quorumNumbers) internal { + cheats.assume(quorumNumbers.length > 0); + cheats.assume(bitmapUtilsWrapper.isArrayStrictlyAscendingOrdered(quorumNumbers)); + uint256 bitmap = bitmapUtilsWrapper.orderedBytesArrayToBitmap(quorumNumbers); + cheats.assume(bitmap <= type(uint192).max); + + // Initialize quorums and add to fuzz filter + for (uint i = 0; i < quorumNumbers.length; i++) { + uint8 quorumNumber = uint8(quorumNumbers[i]); + cheats.assume(!initializedQuorums[quorumNumber]); + _initializeQuorum(quorumNumber); + } + } + function _registerOperator(bytes32 operatorId, bytes memory quorumNumbers) internal { cheats.prank(address(registryCoordinatorMock)); indexRegistry.registerOperator(operatorId, quorumNumbers); diff --git a/test/unit/StakeRegistryUnit.t.sol b/test/unit/StakeRegistryUnit.t.sol index bee91582..c1663087 100644 --- a/test/unit/StakeRegistryUnit.t.sol +++ b/test/unit/StakeRegistryUnit.t.sol @@ -12,10 +12,10 @@ import {ISlasher} from "eigenlayer-contracts/src/contracts/interfaces/ISlasher.s import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; import {IStakeRegistry} from "src/interfaces/IStakeRegistry.sol"; import {IServiceManager} from "src/interfaces/IServiceManager.sol"; -import {IVoteWeigher} from "src/interfaces/IVoteWeigher.sol"; import {IIndexRegistry} from "src/interfaces/IIndexRegistry.sol"; import {IRegistryCoordinator} from "src/interfaces/IRegistryCoordinator.sol"; import {IBLSPubkeyRegistry} from "src/interfaces/IBLSPubkeyRegistry.sol"; +import {IBLSRegistryCoordinatorWithIndices} from "src/interfaces/IBLSRegistryCoordinatorWithIndices.sol"; import {BitmapUtils} from "eigenlayer-contracts/src/contracts/libraries/BitmapUtils.sol"; @@ -56,12 +56,19 @@ contract StakeRegistryUnitTests is Test { address public pubkeyRegistry = address(uint160(uint256(keccak256("pubkeyRegistry")))); address public indexRegistry = address(uint160(uint256(keccak256("indexRegistry")))); + uint256 churnApproverPrivateKey = uint256(keccak256("churnApproverPrivateKey")); + address churnApprover = cheats.addr(churnApproverPrivateKey); + address ejector = address(uint160(uint256(keccak256("ejector")))); + address defaultOperator = address(uint160(uint256(keccak256("defaultOperator")))); bytes32 defaultOperatorId = keccak256("defaultOperatorId"); uint8 defaultQuorumNumber = 0; uint8 numQuorums = 192; uint8 maxQuorumsToRegisterFor = 4; + // Track initialized quorums so we can filter these out when fuzzing + mapping(uint8 => bool) initializedQuorums; + uint256 gasUsed; /// @notice emitted whenever the stake of `operator` is updated @@ -111,57 +118,47 @@ contract StakeRegistryUnitTests is Test { serviceManagerMock = new ServiceManagerMock(slasher); stakeRegistryImplementation = new StakeRegistryHarness( IRegistryCoordinator(address(registryCoordinator)), - strategyManagerMock, + delegationMock, 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)); + // Initialize quorums with dummy minimum stake and strategies + for (uint i = 0; i < maxQuorumsToRegisterFor; i++) { + uint96 minimumStake = uint96(i + 1); + IStakeRegistry.StrategyAndWeightingMultiplier[] memory strategyParams = + new IStakeRegistry.StrategyAndWeightingMultiplier[](1); + strategyParams[0] = IStakeRegistry.StrategyAndWeightingMultiplier( + IStrategy(address(uint160(i))), + uint96(i+1) + ); + + _initializeQuorum(uint8(defaultQuorumNumber + i), minimumStake, strategyParams); + } + + // Update the reg coord quorum count so updateStakes works + registryCoordinator.setQuorumCount(maxQuorumsToRegisterFor); } function testSetMinimumStakeForQuorum_NotFromServiceManager_Reverts() public { - cheats.expectRevert("VoteWeigherBase.onlyServiceManagerOwner: caller is not the owner of the serviceManager"); + cheats.expectRevert("StakeRegistry.onlyServiceManagerOwner: caller is not the owner of the serviceManager"); stakeRegistry.setMinimumStakeForQuorum(defaultQuorumNumber, 0); } function testSetMinimumStakeForQuorum_Valid(uint8 quorumNumber, uint96 minimumStakeForQuorum) public { + // filter out non-initialized quorums + cheats.assume(initializedQuorums[quorumNumber]); + // set the minimum stake for quorum cheats.prank(serviceManagerOwner); stakeRegistry.setMinimumStakeForQuorum(quorumNumber, minimumStakeForQuorum); @@ -177,18 +174,6 @@ contract StakeRegistryUnitTests is Test { 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 { @@ -205,7 +190,7 @@ contract StakeRegistryUnitTests is Test { 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.expectRevert("StakeRegistry.registerOperator: Operator does not meet minimum stake requirement for quorum"); cheats.prank(address(registryCoordinator)); stakeRegistry.registerOperator(defaultOperator, defaultOperatorId, quorumNumbers); } @@ -244,7 +229,7 @@ contract StakeRegistryUnitTests is Test { // 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); + assertEq(stakeRegistry.getLengthOfTotalStakeHistoryForQuorum(i), 1); } } } @@ -322,10 +307,9 @@ contract StakeRegistryUnitTests is Test { // make sure that the stake update is as expected IStakeRegistry.OperatorStakeUpdate memory totalStakeUpdate = - stakeRegistry.getTotalStakeUpdateForQuorumFromIndex(i, historyLength-1); + stakeRegistry.getTotalStakeUpdateForQuorumFromIndex(i, historyLength-1); 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 (historyLength >= 2) { IStakeRegistry.OperatorStakeUpdate memory prevTotalStakeUpdate = @@ -421,7 +405,7 @@ contract StakeRegistryUnitTests is Test { 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"); + assertEq(stakeRegistry.getLengthOfTotalStakeHistoryForQuorum(i), numOperatorsInQuorum[i] + 1, "testDeregisterFirstOperator_Valid_9"); quorumNumberIndex++; } else { // check that the operator has 0 stake updates in the quorum numbers they did not register for @@ -503,8 +487,15 @@ contract StakeRegistryUnitTests is Test { cheats.roll(intialBlockNumber); bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); + for (uint i = 0; i < stakesForQuorum.length; i++) { + emit log_named_uint("quorum", uint8(quorumNumbers[i])); + emit log_named_uint("stake", uint96(stakesForQuorum[i])); + } + for(uint i = 0; i < stakesForQuorum.length; i++) { stakeRegistry.setOperatorWeight(uint8(quorumNumbers[i]), defaultOperator, stakesForQuorum[i] + 1); + emit log_named_uint("updating quorum", uint8(quorumNumbers[i])); + emit log_named_uint("to stake", uint96(stakesForQuorum[i] + 1)); } address[] memory operators = new address[](1); @@ -512,11 +503,24 @@ contract StakeRegistryUnitTests is Test { stakeRegistry.updateStakes(operators); for(uint i = 0; i < quorumNumbers.length; i++) { - StakeRegistry.OperatorStakeUpdate memory operatorStakeUpdate = stakeRegistry.getStakeUpdateForQuorumFromOperatorIdAndIndex(uint8(quorumNumbers[i]), defaultOperatorId, 1); + StakeRegistry.OperatorStakeUpdate memory operatorStakeUpdate = stakeRegistry.getMostRecentStakeUpdateByOperatorId(defaultOperatorId, uint8(quorumNumbers[i])); + emit log_named_uint("quorum", uint8(quorumNumbers[i])); + emit log_named_uint("most recent stake", operatorStakeUpdate.stake); assertEq(operatorStakeUpdate.stake, stakesForQuorum[i] + 1); } } + function _initializeQuorum( + uint8 quorumNumber, + uint96 minimumStake, + IStakeRegistry.StrategyAndWeightingMultiplier[] memory strategyParams + ) internal { + cheats.prank(address(registryCoordinator)); + + stakeRegistry.initializeQuorum(quorumNumber, minimumStake, strategyParams); + initializedQuorums[quorumNumber] = true; + } + // utility function for registering an operator with a valid quorumBitmap and stakesForQuorum using provided randomness function _registerOperatorRandomValid( address operator, diff --git a/test/unit/VoteWeigherBaseUnit.t.sol b/test/unit/VoteWeigherBaseUnit.t.sol index 7b393e87..0539575d 100644 --- a/test/unit/VoteWeigherBaseUnit.t.sol +++ b/test/unit/VoteWeigherBaseUnit.t.sol @@ -9,9 +9,15 @@ import {IStrategyManager} from "eigenlayer-contracts/src/contracts/interfaces/IS import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; import {IEigenPodManager} from "eigenlayer-contracts/src/contracts/interfaces/IEigenPodManager.sol"; import {ISlasher} from "eigenlayer-contracts/src/contracts/interfaces/ISlasher.sol"; +<<<<<<< HEAD import {IServiceManager} from "../../src/interfaces/IServiceManager.sol"; import {IVoteWeigher} from "../../src/interfaces/IVoteWeigher.sol"; import {StakeRegistry} from "../../src/StakeRegistry.sol"; +======= +import {IServiceManager} from "src/interfaces/IServiceManager.sol"; +import {IStakeRegistry} from "src/interfaces/IStakeRegistry.sol"; +import {StakeRegistry} from "src/StakeRegistry.sol"; +>>>>>>> 12b09de (fix: fix compilation issues and tests) import {RegistryCoordinatorMock} from "../mocks/RegistryCoordinatorMock.sol"; import {OwnableMock} from "eigenlayer-contracts/src/test/mocks/OwnableMock.sol"; @@ -90,429 +96,430 @@ contract VoteWeigherBaseUnitTests is Test { 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); + /// TODO - migrate tests to registry coordinator + // function testCreateQuorum_Valid(IStakeRegistry.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(); + // assertEq(voteWeigher.quorumCount(), quorumCountBefore + 1); + // // check that all of the weights are correct + // for (uint i = 0; i < strategiesAndWeightingMultipliers.length; i++) { + // IStakeRegistry.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, + // IStakeRegistry.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( + // IStakeRegistry.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( + // IStakeRegistry.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 { + // IStakeRegistry.StrategyAndWeightingMultiplier[] memory strategiesAndWeightingMultipliers; + // cheats.prank(serviceManagerOwner); + // cheats.expectRevert("VoteWeigherBase._addStrategiesConsideredAndMultipliers: no strategies provided"); + // voteWeigher.createQuorum(strategiesAndWeightingMultipliers); + // } + + // function testCreateQuorum_StrategiesAndWeightingMultipliers_WithZeroWeight( + // IStakeRegistry.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 { + // IStakeRegistry.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); + // 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, + // IStakeRegistry.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 + // IStakeRegistry.StrategyAndWeightingMultiplier[] memory strategiesAndWeightingMultipliers1 = new IStakeRegistry.StrategyAndWeightingMultiplier[](randomSplit); + // IStakeRegistry.StrategyAndWeightingMultiplier[] memory strategiesAndWeightingMultipliers2 = new IStakeRegistry.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++) { + // IStakeRegistry.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); + // IStakeRegistry.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 { + // IStakeRegistry.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( + // IStakeRegistry.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); + // // check that the strategies that were not removed are still there + // // get all strategies and multipliers form the contracts + // IStakeRegistry.StrategyAndWeightingMultiplier[] memory strategiesAndWeightingMultipliersFromContract = new IStakeRegistry.StrategyAndWeightingMultiplier[](voteWeigher.strategiesConsideredAndMultipliersLength(quorumNumber)); + // for (uint256 i = 0; i < strategiesAndWeightingMultipliersFromContract.length; i++) { + // strategiesAndWeightingMultipliersFromContract[i] = voteWeigher.strategyAndWeightingMultiplierForQuorumByIndex(quorumNumber, i); + // } + + // // remove indicesToRemove from local strategiesAndWeightingMultipliers + // IStakeRegistry.StrategyAndWeightingMultiplier[] memory strategiesAndWeightingMultipliersLocal = new IStakeRegistry.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) + // // 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); + // IStakeRegistry.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 { + // IStakeRegistry.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 { + // IStakeRegistry.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); + // IStakeRegistry.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( + // IStakeRegistry.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++) { + // IStakeRegistry.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 { + // IStakeRegistry.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 { + // IStakeRegistry.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 { + // IStakeRegistry.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, + // IStakeRegistry.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(IStakeRegistry.StrategyAndWeightingMultiplier[] memory strategiesAndWeightingMultipliers) internal - returns(IVoteWeigher.StrategyAndWeightingMultiplier[] memory) + returns(IStakeRegistry.StrategyAndWeightingMultiplier[] memory) { - IVoteWeigher.StrategyAndWeightingMultiplier[] memory deduplicatedStrategiesAndWeightingMultipliers = new IVoteWeigher.StrategyAndWeightingMultiplier[](strategiesAndWeightingMultipliers.length); + IStakeRegistry.StrategyAndWeightingMultiplier[] memory deduplicatedStrategiesAndWeightingMultipliers = new IStakeRegistry.StrategyAndWeightingMultiplier[](strategiesAndWeightingMultipliers.length); uint256 numUniqueStrategies = 0; // check for duplicates for (uint i = 0; i < strategiesAndWeightingMultipliers.length; i++) { @@ -529,14 +536,14 @@ contract VoteWeigherBaseUnitTests is Test { strategyInCurrentArray[strategiesAndWeightingMultipliers[i].strategy] = false; } - IVoteWeigher.StrategyAndWeightingMultiplier[] memory trimmedStrategiesAndWeightingMultipliers = new IVoteWeigher.StrategyAndWeightingMultiplier[](numUniqueStrategies); + IStakeRegistry.StrategyAndWeightingMultiplier[] memory trimmedStrategiesAndWeightingMultipliers = new IStakeRegistry.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) { + function _replaceZeroWeights(IStakeRegistry.StrategyAndWeightingMultiplier[] memory strategiesAndWeightingMultipliers) internal pure returns(IStakeRegistry.StrategyAndWeightingMultiplier[] memory) { for (uint256 i = 0; i < strategiesAndWeightingMultipliers.length; i++) { if (strategiesAndWeightingMultipliers[i].multiplier == 0) { strategiesAndWeightingMultipliers[i].multiplier = 1; @@ -570,20 +577,20 @@ contract VoteWeigherBaseUnitTests is Test { return trimmedRandomIndices; } - function _convertToValidStrategiesAndWeightingMultipliers(IVoteWeigher.StrategyAndWeightingMultiplier[] memory strategiesAndWeightingMultipliers) internal returns (IVoteWeigher.StrategyAndWeightingMultiplier[] memory) { + function _convertToValidStrategiesAndWeightingMultipliers(IStakeRegistry.StrategyAndWeightingMultiplier[] memory strategiesAndWeightingMultipliers) internal returns (IStakeRegistry.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({ + function _defaultStrategiesAndWeightingMultipliers() internal pure returns (IStakeRegistry.StrategyAndWeightingMultiplier[] memory) { + IStakeRegistry.StrategyAndWeightingMultiplier[] memory strategiesAndWeightingMultipliers = new IStakeRegistry.StrategyAndWeightingMultiplier[](2); + strategiesAndWeightingMultipliers[0] = IStakeRegistry.StrategyAndWeightingMultiplier({ strategy: IStrategy(address(uint160(uint256(keccak256("strategy1"))))), multiplier: 1.04 ether }); - strategiesAndWeightingMultipliers[1] = IVoteWeigher.StrategyAndWeightingMultiplier({ + strategiesAndWeightingMultipliers[1] = IStakeRegistry.StrategyAndWeightingMultiplier({ strategy: IStrategy(address(uint160(uint256(keccak256("strategy2"))))), multiplier: 1.69 ether }); diff --git a/test/utils/MockAVSDeployer.sol b/test/utils/MockAVSDeployer.sol index 0429589c..b3f78152 100644 --- a/test/utils/MockAVSDeployer.sol +++ b/test/utils/MockAVSDeployer.sol @@ -13,6 +13,7 @@ import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISi import {BitmapUtils} from "../../src/libraries/BitmapUtils.sol"; import {BN254} from "../../src/libraries/BN254.sol"; +<<<<<<< HEAD import {BLSPublicKeyCompendium} from "../../src/BLSPublicKeyCompendium.sol"; import {BLSOperatorStateRetriever} from "../../src/BLSOperatorStateRetriever.sol"; import {BLSRegistryCoordinatorWithIndices} from "../../src/BLSRegistryCoordinatorWithIndices.sol"; @@ -27,6 +28,21 @@ import {IVoteWeigher} from "../../src/interfaces/IVoteWeigher.sol"; import {IStakeRegistry} from "../../src/interfaces/IStakeRegistry.sol"; import {IIndexRegistry} from "../../src/interfaces/IIndexRegistry.sol"; import {IBLSRegistryCoordinatorWithIndices} from "../../src/interfaces/IBLSRegistryCoordinatorWithIndices.sol"; +======= +import {BLSPublicKeyCompendium} from "src/BLSPublicKeyCompendium.sol"; +import {BLSOperatorStateRetriever} from "src/BLSOperatorStateRetriever.sol"; +import {BLSRegistryCoordinatorWithIndices} from "src/BLSRegistryCoordinatorWithIndices.sol"; +import {BLSRegistryCoordinatorWithIndicesHarness} from "test/harnesses/BLSRegistryCoordinatorWithIndicesHarness.sol"; +import {BLSPubkeyRegistry} from "src/BLSPubkeyRegistry.sol"; +import {StakeRegistry} from "src/StakeRegistry.sol"; +import {IndexRegistry} from "src/IndexRegistry.sol"; +import {IServiceManager} from "src/interfaces/IServiceManager.sol"; +import {IBLSPubkeyRegistry} from "src/interfaces/IBLSPubkeyRegistry.sol"; +import {IRegistryCoordinator} from "src/interfaces/IRegistryCoordinator.sol"; +import {IStakeRegistry} from "src/interfaces/IStakeRegistry.sol"; +import {IIndexRegistry} from "src/interfaces/IIndexRegistry.sol"; +import {IBLSRegistryCoordinatorWithIndices} from "src/interfaces/IBLSRegistryCoordinatorWithIndices.sol"; +>>>>>>> 12b09de (fix: fix compilation issues and tests) import {StrategyManagerMock} from "eigenlayer-contracts/src/test/mocks/StrategyManagerMock.sol"; @@ -195,14 +211,38 @@ contract MockAVSDeployer is Test { ) ); + cheats.stopPrank(); + cheats.startPrank(proxyAdminOwner); + stakeRegistryImplementation = new StakeRegistryHarness( IRegistryCoordinator(registryCoordinator), - strategyManagerMock, + delegationMock, IServiceManager(address(serviceManagerMock)) ); - cheats.stopPrank(); - cheats.startPrank(proxyAdminOwner); + proxyAdmin.upgrade( + TransparentUpgradeableProxy(payable(address(stakeRegistry))), + address(stakeRegistryImplementation) + ); + + 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) + ); // setup the dummy minimum stake for quorum uint96[] memory minimumStakeForQuorum = new uint96[](numQuorumsToAdd); @@ -211,26 +251,16 @@ contract MockAVSDeployer is Test { } // setup the dummy quorum strategies - IVoteWeigher.StrategyAndWeightingMultiplier[][] memory quorumStrategiesConsideredAndMultipliers = - new IVoteWeigher.StrategyAndWeightingMultiplier[][](numQuorumsToAdd); + IStakeRegistry.StrategyAndWeightingMultiplier[][] memory quorumStrategiesConsideredAndMultipliers = + new IStakeRegistry.StrategyAndWeightingMultiplier[][](numQuorumsToAdd); for (uint256 i = 0; i < quorumStrategiesConsideredAndMultipliers.length; i++) { - quorumStrategiesConsideredAndMultipliers[i] = new IVoteWeigher.StrategyAndWeightingMultiplier[](1); - quorumStrategiesConsideredAndMultipliers[i][0] = IVoteWeigher.StrategyAndWeightingMultiplier( + quorumStrategiesConsideredAndMultipliers[i] = new IStakeRegistry.StrategyAndWeightingMultiplier[](1); + quorumStrategiesConsideredAndMultipliers[i][0] = IStakeRegistry.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, @@ -256,32 +286,15 @@ contract MockAVSDeployer is Test { BLSRegistryCoordinatorWithIndices.initialize.selector, churnApprover, ejector, - operatorSetParams, pauserRegistry, - 0/*initialPausedStatus*/ + 0/*initialPausedStatus*/, + operatorSetParams, + minimumStakeForQuorum, + quorumStrategiesConsideredAndMultipliers ) ); } - 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(); From 732747a80aa7ffb9d8ee4ebcc3bf4c6a838d72b5 Mon Sep 17 00:00:00 2001 From: wadealexc Date: Tue, 31 Oct 2023 20:58:48 +0000 Subject: [PATCH 025/101] refactor: wip refactor for index and blspubkey registries to simplify naming and logic --- src/BLSOperatorStateRetriever.sol | 2 +- src/BLSPubkeyRegistry.sol | 39 ++-- src/IndexRegistry.sol | 293 +++++++++++++++----------- src/IndexRegistryStorage.sol | 15 +- src/StakeRegistry.sol | 37 ++-- src/interfaces/IIndexRegistry.sol | 12 +- test/unit/BLSPubkeyRegistryUnit.t.sol | 12 +- test/unit/IndexRegistryUnit.t.sol | 58 ++--- 8 files changed, 251 insertions(+), 217 deletions(-) diff --git a/src/BLSOperatorStateRetriever.sol b/src/BLSOperatorStateRetriever.sol index 8051eba5..142fb960 100644 --- a/src/BLSOperatorStateRetriever.sol +++ b/src/BLSOperatorStateRetriever.sol @@ -71,7 +71,7 @@ contract BLSOperatorStateRetriever { 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); + bytes32[] memory operatorIds = indexRegistry.getOperatorListAtBlockNumber(quorumNumber, blockNumber); operators[i] = new Operator[](operatorIds.length); for (uint256 j = 0; j < operatorIds.length; j++) { bytes32 operatorId = bytes32(operatorIds[j]); diff --git a/src/BLSPubkeyRegistry.sol b/src/BLSPubkeyRegistry.sol index 33b3e898..cde6e522 100644 --- a/src/BLSPubkeyRegistry.sol +++ b/src/BLSPubkeyRegistry.sol @@ -113,32 +113,31 @@ contract BLSPubkeyRegistry is BLSPubkeyRegistryStorage { *******************************************************************************/ function _processQuorumApkUpdate(bytes memory quorumNumbers, BN254.G1Point memory point) internal { - BN254.G1Point memory apkAfterUpdate; + BN254.G1Point memory newApk; - for (uint i = 0; i < quorumNumbers.length; ) { + for (uint256 i = 0; i < quorumNumbers.length; i++) { + // Validate quorum exists and get history length uint8 quorumNumber = uint8(quorumNumbers[i]); - - // Validate quorumNumber uint256 historyLength = quorumApkUpdates[quorumNumber].length; require(historyLength != 0, "BLSPubkeyRegistry._processQuorumApkUpdate: quorum does not exist"); - // Update the last entry to point at the current block - // TODO - if the last entry was made in this block, update the entry instead - quorumApkUpdates[quorumNumber][historyLength - 1].nextUpdateBlockNumber = uint32(block.number); - // Update aggregate public key for this quorum - apkAfterUpdate = quorumApk[quorumNumber].plus(point); - quorumApk[quorumNumber] = apkAfterUpdate; - - // Push update to history - quorumApkUpdates[quorumNumber].push(ApkUpdate({ - apkHash: bytes24(BN254.hashG1Point(apkAfterUpdate)), - updateBlockNumber: uint32(block.number), - nextUpdateBlockNumber: 0 - })); - - unchecked { - ++i; + newApk = quorumApk[quorumNumber].plus(point); + quorumApk[quorumNumber] = newApk; + bytes24 newApkHash = bytes24(BN254.hashG1Point(newApk)); + + // Update apk history. If the last update was made in this block, update the entry + // Otherwise, push a new historical entry and update the prev->next pointer + ApkUpdate storage lastUpdate = quorumApkUpdates[quorumNumber][historyLength - 1]; + if (lastUpdate.updateBlockNumber == uint32(block.number)) { + lastUpdate.apkHash = newApkHash; + } else { + lastUpdate.nextUpdateBlockNumber = uint32(block.number); + quorumApkUpdates[quorumNumber].push(ApkUpdate({ + apkHash: newApkHash, + updateBlockNumber: uint32(block.number), + nextUpdateBlockNumber: 0 + })); } } } diff --git a/src/IndexRegistry.sol b/src/IndexRegistry.sol index 14db2a2c..37e94243 100644 --- a/src/IndexRegistry.sol +++ b/src/IndexRegistry.sol @@ -43,24 +43,24 @@ contract IndexRegistry is IndexRegistryStorage { uint32[] memory numOperatorsPerQuorum = new uint32[](quorumNumbers.length); for (uint256 i = 0; i < quorumNumbers.length; i++) { + // Validate quorum exists and get current operator count uint8 quorumNumber = uint8(quorumNumbers[i]); - - // Check that the quorum exists - uint256 historyLength = _totalOperatorsHistory[quorumNumber].length; + uint256 historyLength = _operatorCountHistory[quorumNumber].length; require(historyLength != 0, "IndexRegistry.registerOperator: quorum does not exist"); - // Get the number of operators currently in the quorum, which will be operator's new index - uint32 numOperators = historyLength > 0 ? _totalOperatorsHistory[quorumNumber][historyLength - 1].numOperators : 0; - _updateOperatorIdToIndexHistory({ - operatorId: operatorId, - quorumNumber: quorumNumber, - index: numOperators - }); - _updateTotalOperatorHistory({ - quorumNumber: quorumNumber, - numOperators: numOperators + 1 + /** + * Increase the number of operators currently active for this quorum, + * and assign the operator to the last index available + */ + uint32 newOperatorCount = _increaseOperatorCount(quorumNumber); + _assignOperatorToIndex({ + operatorId: operatorId, + quorumNumber: quorumNumber, + index: newOperatorCount - 1 }); - numOperatorsPerQuorum[i] = numOperators + 1; + + // Record the current operator count for each quorum + numOperatorsPerQuorum[i] = newOperatorCount; } return numOperatorsPerQuorum; @@ -83,18 +83,27 @@ contract IndexRegistry is IndexRegistryStorage { bytes calldata quorumNumbers ) public virtual onlyRegistryCoordinator { for (uint256 i = 0; i < quorumNumbers.length; i++) { + // Validate quorum exists and get the index of the operator being deregistered uint8 quorumNumber = uint8(quorumNumbers[i]); - uint32 indexOfOperatorToRemove = operatorIdToIndex[quorumNumber][operatorId]; - uint256 historyLength = _totalOperatorsHistory[quorumNumber].length; - _processOperatorRemoval({ - operatorId: operatorId, - quorumNumber: quorumNumber, - indexOfOperatorToRemove: indexOfOperatorToRemove - }); - _updateTotalOperatorHistory({ - quorumNumber: quorumNumber, - numOperators: _totalOperatorsHistory[quorumNumber][historyLength - 1].numOperators - 1 - }); + uint256 historyLength = _operatorCountHistory[quorumNumber].length; + require(historyLength != 0, "IndexRegistry.registerOperator: quorum does not exist"); + uint32 indexOfOperatorToRemove = currentOperatorIndex[quorumNumber][operatorId]; + + /** + * "Pop" the operator from the registry: + * 1. Decrease the operator count for the quorum + * 2. Remove the last operator associated with the count + * 3. Place the last operator in the deregistered operator's old position + */ + uint32 newOperatorCount = _decreaseOperatorCount(quorumNumber); + bytes32 lastOperatorId = _popLastOperator(quorumNumber, newOperatorCount); + if (operatorId != lastOperatorId) { + _assignOperatorToIndex({ + operatorId: lastOperatorId, + quorumNumber: quorumNumber, + index: indexOfOperatorToRemove + }); + } } } @@ -103,9 +112,9 @@ contract IndexRegistry is IndexRegistryStorage { * @param quorumNumber The number of the new quorum */ function initializeQuorum(uint8 quorumNumber) public virtual onlyRegistryCoordinator { - require(_totalOperatorsHistory[quorumNumber].length == 0, "IndexRegistry.createQuorum: quorum already exists"); + require(_operatorCountHistory[quorumNumber].length == 0, "IndexRegistry.createQuorum: quorum already exists"); - _totalOperatorsHistory[quorumNumber].push(QuorumUpdate({ + _operatorCountHistory[quorumNumber].push(QuorumUpdate({ numOperators: 0, fromBlockNumber: uint32(block.number) })); @@ -116,121 +125,155 @@ contract IndexRegistry is IndexRegistryStorage { *******************************************************************************/ /** - * @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 + * @notice Increases the historical operator count by 1 and returns the new count */ - function _updateTotalOperatorHistory(uint8 quorumNumber, uint32 numOperators) internal { - _totalOperatorsHistory[quorumNumber].push(QuorumUpdate({ - numOperators: numOperators, - fromBlockNumber: uint32(block.number) - })); + function _increaseOperatorCount(uint8 quorumNumber) internal returns (uint32) { + QuorumUpdate storage lastUpdate = _latestQuorumUpdate(quorumNumber); + uint32 newOperatorCount = lastUpdate.numOperators + 1; + + // If the last update was made in this block, update the entry + // Otherwise, push a new historical entry. + if (lastUpdate.fromBlockNumber == uint32(block.number)) { + lastUpdate.numOperators = newOperatorCount; + } else { + _operatorCountHistory[quorumNumber].push(QuorumUpdate({ + numOperators: newOperatorCount, + fromBlockNumber: uint32(block.number) + })); + } + + return newOperatorCount; } /** + * @notice Decreases the historical operator count by 1 and returns the new count + */ + function _decreaseOperatorCount(uint8 quorumNumber) internal returns (uint32) { + QuorumUpdate storage lastUpdate = _latestQuorumUpdate(quorumNumber); + uint32 newOperatorCount = lastUpdate.numOperators - 1; + + // If the last update was made in this block, update the entry + // Otherwise, push a new historical entry. + if (lastUpdate.fromBlockNumber == uint32(block.number)) { + lastUpdate.numOperators = newOperatorCount; + } else { + _operatorCountHistory[quorumNumber].push(QuorumUpdate({ + numOperators: newOperatorCount, + fromBlockNumber: uint32(block.number) + })); + } + + return newOperatorCount; + } + + /** + * @notice For a given quorum and index, pop and return the last operatorId in the history + * @dev The last entry's operatorId is updated to OPERATOR_DOES_NOT_EXIST_ID + * @return The removed operatorId + */ + function _popLastOperator(uint8 quorumNumber, uint32 index) internal returns (bytes32) { + OperatorUpdate storage lastUpdate = _latestIndexUpdate(quorumNumber, index); + bytes32 removedOperatorId = lastUpdate.operatorId; + + // If the last update was made in this block, update the entry + // Otherwise, push a new historical entry for the index + if (lastUpdate.fromBlockNumber == uint32(block.number)) { + lastUpdate.operatorId = OPERATOR_DOES_NOT_EXIST_ID; + } else { + _indexHistory[quorumNumber][index].push(OperatorUpdate({ + operatorId: OPERATOR_DOES_NOT_EXIST_ID, + fromBlockNumber: uint32(block.number) + })); + } + + return removedOperatorId; + } + + /** + * @notice Assign an operator to an index and update the index history * @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 { - _indexToOperatorIdHistory[quorumNumber][index].push(OperatorUpdate({ - operatorId: operatorId, - fromBlockNumber: uint32(block.number) - })); + function _assignOperatorToIndex(bytes32 operatorId, uint8 quorumNumber, uint32 index) internal { + OperatorUpdate storage lastUpdate = _latestIndexUpdate(quorumNumber, index); - operatorIdToIndex[quorumNumber][operatorId] = index; + // If the last update was made in this block, update the entry + // Otherwise, push a new historical entry for the index + if (lastUpdate.fromBlockNumber == uint32(block.number)) { + lastUpdate.operatorId = operatorId; + } else { + _indexHistory[quorumNumber][index].push(OperatorUpdate({ + operatorId: operatorId, + fromBlockNumber: uint32(block.number) + })); + } + // Assign the operator to their new current index + currentOperatorIndex[quorumNumber][operatorId] = index; emit QuorumIndexUpdate(operatorId, quorumNumber, index); } - /**v - * @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 - ) internal { - uint32 currentNumOperators = _totalOperatorsForQuorum(quorumNumber); - bytes32 operatorIdToSwap = _indexToOperatorIdHistory[quorumNumber][currentNumOperators - 1][_indexToOperatorIdHistory[quorumNumber][currentNumOperators - 1].length - 1].operatorId; - // 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({ - operatorId: operatorIdToSwap, - quorumNumber: quorumNumber, - index: indexOfOperatorToRemove - }); - } - // marking the last index with OPERATOR_DOES_NOT_EXIST_ID, to signal that the index it not at use at the block number - _updateOperatorIdToIndexHistory({ - operatorId: OPERATOR_DOES_NOT_EXIST_ID, - quorumNumber: quorumNumber, - index: currentNumOperators - 1 - }); + /// @notice Returns the most recent operator count update for a quorum + /// @dev Reverts if the quorum does not exist (history length == 0) + function _latestQuorumUpdate(uint8 quorumNumber) internal view returns (QuorumUpdate storage) { + uint256 historyLength = _operatorCountHistory[quorumNumber].length; + return _operatorCountHistory[quorumNumber][historyLength - 1]; + } + + /// @notice Returns the most recent operator id update for an index + /// @dev Reverts if the index has never been used (history length == 0) + function _latestIndexUpdate(uint8 quorumNumber, uint32 index) internal view returns (OperatorUpdate storage) { + uint256 historyLength = _indexHistory[quorumNumber][index].length; + return _indexHistory[quorumNumber][index][historyLength - 1]; } /** * @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. + * @dev Reverts if the quorum does not exist, or if the blockNumber is from before the quorum existed */ - function _getTotalOperatorsForQuorumAtBlockNumber( + function _operatorCountAtBlockNumber( 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; - } + uint256 historyLength = _operatorCountHistory[quorumNumber].length; + require(historyLength != 0, "IndexRegistry._operatorCountAtBlockNumber: quorum does not exist"); + require( + blockNumber >= _operatorCountHistory[quorumNumber][0].fromBlockNumber, + "IndexRegistry._operatorCountAtBlockNumber: quorum did not exist at given block number" + ); - // 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; - QuorumUpdate memory quorumUpdate = _totalOperatorsHistory[quorumNumber][listIndex]; - // look for the first update that began before or at `blockNumber` + // Loop backwards through the total operator history + for (uint256 i = 0; i < historyLength; i++) { + uint256 listIndex = (historyLength - 1) - i; + QuorumUpdate memory quorumUpdate = _operatorCountHistory[quorumNumber][listIndex]; + // Look for the first update that began before or at `blockNumber` if (quorumUpdate.fromBlockNumber <= blockNumber) { - return _totalOperatorsHistory[quorumNumber][listIndex].numOperators; + return quorumUpdate.numOperators; } - } - return _totalOperatorsHistory[quorumNumber][0].numOperators; - } - - /// @notice Returns the total number of operators for a given `quorumNumber` - function _totalOperatorsForQuorum(uint8 quorumNumber) internal view returns (uint32){ - uint256 totalOperatorsHistoryLength = _totalOperatorsHistory[quorumNumber].length; - if (totalOperatorsHistoryLength == 0) { - return 0; } - return _totalOperatorsHistory[quorumNumber][totalOperatorsHistoryLength - 1].numOperators; + + // Shouldn't be able to reach this point + revert("IndexRegistry._operatorCountAtBlockNumber: quorum did not exist at given block number"); } /** * @return operatorId at the given `index` at the given `blockNumber` for the given `quorumNumber` * Precondition: requires that the index was used active at the given block number for quorum */ - function _getOperatorIdAtIndexForQuorumAtBlockNumber( + function _operatorIdForIndexAtBlockNumber( uint32 index, uint8 quorumNumber, uint32 blockNumber ) internal view returns(bytes32) { - uint256 indexOperatorHistoryLength = _indexToOperatorIdHistory[quorumNumber][index].length; - // loop backward through index history to find the index of the operator at the given block number - for (uint256 i = 0; i < indexOperatorHistoryLength; i++) { - uint256 listIndex = (indexOperatorHistoryLength - 1) - i; - OperatorUpdate memory operatorIndexUpdate = _indexToOperatorIdHistory[quorumNumber][index][listIndex]; + uint256 historyLength = _indexHistory[quorumNumber][index].length; + // Loop backward through index history + for (uint256 i = 0; i < historyLength; i++) { + uint256 listIndex = (historyLength - 1) - i; + OperatorUpdate memory operatorIndexUpdate = _indexHistory[quorumNumber][index][listIndex]; + // Look for the first update that began before or at `blockNumber` if (operatorIndexUpdate.fromBlockNumber <= blockNumber) { - // one special case is that this will be OPERATOR_DOES_NOT_EXIST_ID if this index was not used at the block number + // Special case: this will be OPERATOR_DOES_NOT_EXIST_ID if this index was not used at the block number return operatorIndexUpdate.operatorId; } } @@ -243,29 +286,29 @@ contract IndexRegistry is IndexRegistryStorage { VIEW FUNCTIONS *******************************************************************************/ - /// @notice Returns the _indexToOperatorIdHistory entry for the specified `operatorIndex` and `quorumNumber` at the specified `index` - function getOperatorIndexUpdateOfIndexForQuorumAtIndex(uint32 operatorIndex, uint8 quorumNumber, uint32 index) external view returns (OperatorUpdate memory) { - return _indexToOperatorIdHistory[quorumNumber][operatorIndex][index]; + /// @notice Returns the _indexHistory entry for the specified `operatorIndex` and `quorumNumber` at the specified `index` + function getOperatorUpdateAtIndex(uint32 operatorIndex, uint8 quorumNumber, uint32 index) external view returns (OperatorUpdate memory) { + return _indexHistory[quorumNumber][operatorIndex][index]; } - /// @notice Returns the _totalOperatorsHistory entry for the specified `quorumNumber` at the specified `index` + /// @notice Returns the _operatorCountHistory entry for the specified `quorumNumber` at the specified `index` function getQuorumUpdateAtIndex(uint8 quorumNumber, uint32 index) external view returns (QuorumUpdate memory) { - return _totalOperatorsHistory[quorumNumber][index]; + return _operatorCountHistory[quorumNumber][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 + * @param index is the index of the entry in the dynamic array `_operatorCountHistory[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( + function getTotalOperatorsForIndexAtBlockNumber( uint8 quorumNumber, uint32 blockNumber, uint32 index ) external view returns (uint32){ - QuorumUpdate memory quorumUpdate = _totalOperatorsHistory[quorumNumber][index]; + QuorumUpdate memory quorumUpdate = _operatorCountHistory[quorumNumber][index]; // blocknumber must be at or after the "index'th" entry's fromBlockNumber require( @@ -274,8 +317,8 @@ contract IndexRegistry is IndexRegistryStorage { ); // 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){ - QuorumUpdate memory nextQuorumUpdate = _totalOperatorsHistory[quorumNumber][index + 1]; + if (index != _operatorCountHistory[quorumNumber].length - 1){ + QuorumUpdate memory nextQuorumUpdate = _operatorCountHistory[quorumNumber][index + 1]; require( blockNumber < nextQuorumUpdate.fromBlockNumber, "IndexRegistry.getTotalOperatorsForQuorumAtBlockNumberByIndex: provided index is too far in the future for provided block number" @@ -285,23 +328,25 @@ contract IndexRegistry is IndexRegistryStorage { } /// @notice Returns an ordered list of operators of the services for the given `quorumNumber` at the given `blockNumber` - function getOperatorListForQuorumAtBlockNumber( + function getOperatorListAtBlockNumber( uint8 quorumNumber, uint32 blockNumber ) external view returns (bytes32[] memory){ - bytes32[] memory quorumOperatorList = new bytes32[](_getTotalOperatorsForQuorumAtBlockNumber(quorumNumber, blockNumber)); - for (uint256 i = 0; i < quorumOperatorList.length; i++) { - quorumOperatorList[i] = _getOperatorIdAtIndexForQuorumAtBlockNumber(uint32(i), quorumNumber, blockNumber); + uint32 operatorCount = _operatorCountAtBlockNumber(quorumNumber, blockNumber); + bytes32[] memory operatorList = new bytes32[](operatorCount); + for (uint256 i = 0; i < operatorCount; i++) { + operatorList[i] = _operatorIdForIndexAtBlockNumber(uint32(i), quorumNumber, blockNumber); require( - quorumOperatorList[i] != OPERATOR_DOES_NOT_EXIST_ID, - "IndexRegistry.getOperatorListForQuorumAtBlockNumber: operator does not exist at the given block number" + operatorList[i] != OPERATOR_DOES_NOT_EXIST_ID, + "IndexRegistry.getOperatorListAtBlockNumber: operator does not exist at the given block number" ); } - return quorumOperatorList; + return operatorList; } /// @notice Returns the total number of operators for a given `quorumNumber` + /// @dev This will revert if the quorum does not exist function totalOperatorsForQuorum(uint8 quorumNumber) external view returns (uint32){ - return _totalOperatorsForQuorum(quorumNumber); + return _latestQuorumUpdate(quorumNumber).numOperators; } } diff --git a/src/IndexRegistryStorage.sol b/src/IndexRegistryStorage.sol index 5d42b2df..d69c436c 100644 --- a/src/IndexRegistryStorage.sol +++ b/src/IndexRegistryStorage.sol @@ -19,15 +19,12 @@ abstract contract IndexRegistryStorage is Initializable, IIndexRegistry { /// @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 quorumNumber => operator id => current index - mapping(uint8 => mapping(bytes32 => uint32)) public operatorIdToIndex; - /// @notice mapping of quorumNumber => index => operator id history for that index - mapping(uint8 => mapping(uint32 => OperatorUpdate[])) internal _indexToOperatorIdHistory; - /// @notice mapping of quorumNumber => history of numbers of unique registered operators - mapping(uint8 => QuorumUpdate[]) internal _totalOperatorsHistory; + /// @notice maps quorumNumber => operator id => current index + mapping(uint8 => mapping(bytes32 => uint32)) public currentOperatorIndex; + /// @notice maps quorumNumber => index => historical operator ids at that index + mapping(uint8 => mapping(uint32 => OperatorUpdate[])) internal _indexHistory; + /// @notice maps quorumNumber => historical number of unique registered operators + mapping(uint8 => QuorumUpdate[]) internal _operatorCountHistory; constructor( IRegistryCoordinator _registryCoordinator diff --git a/src/StakeRegistry.sol b/src/StakeRegistry.sol index 670df5cd..86ae8c06 100644 --- a/src/StakeRegistry.sol +++ b/src/StakeRegistry.sol @@ -427,35 +427,26 @@ contract StakeRegistry is StakeRegistryStorage { return; } - uint96 prevStake; + // Get our last-recorded stake update uint256 historyLength = _totalStakeHistory[quorumNumber].length; + OperatorStakeUpdate storage lastStakeUpdate = _totalStakeHistory[quorumNumber][historyLength - 1]; + + // Calculate the new total stake by applying the delta to our previous stake + uint96 newStake = _applyDelta(lastStakeUpdate.stake, stakeDelta); - if (historyLength == 0) { - // No prior stake history - push our first entry + /** + * If our last stake entry was made in the current block, update the entry + * Otherwise, push a new entry and update the previous entry's "next" field + */ + if (lastStakeUpdate.updateBlockNumber == uint32(block.number)) { + lastStakeUpdate.stake = newStake; + } else { + lastStakeUpdate.nextUpdateBlockNumber = uint32(block.number); _totalStakeHistory[quorumNumber].push(OperatorStakeUpdate({ updateBlockNumber: uint32(block.number), nextUpdateBlockNumber: 0, - stake: _applyDelta(prevStake, stakeDelta) + stake: newStake })); - } else { - // We have prior stake history - calculate our new stake as a function of our last-recorded stake - prevStake = _totalStakeHistory[quorumNumber][historyLength - 1].stake; - uint96 newStake = _applyDelta(prevStake, stakeDelta); - - /** - * If our last stake entry was made in the current block, update the entry - * Otherwise, push a new entry and update the previous entry's "next" field - */ - if (_totalStakeHistory[quorumNumber][historyLength-1].updateBlockNumber == uint32(block.number)) { - _totalStakeHistory[quorumNumber][historyLength-1].stake = newStake; - } else { - _totalStakeHistory[quorumNumber][historyLength-1].nextUpdateBlockNumber = uint32(block.number); - _totalStakeHistory[quorumNumber].push(OperatorStakeUpdate({ - updateBlockNumber: uint32(block.number), - nextUpdateBlockNumber: 0, - stake: newStake - })); - } } } diff --git a/src/interfaces/IIndexRegistry.sol b/src/interfaces/IIndexRegistry.sol index e7eaaf00..9875e50f 100644 --- a/src/interfaces/IIndexRegistry.sol +++ b/src/interfaces/IIndexRegistry.sol @@ -66,23 +66,23 @@ interface IIndexRegistry is IRegistry { */ function initializeQuorum(uint8 quorumNumber) external; - /// @notice Returns the _indexToOperatorIdHistory entry for the specified `operatorIndex` and `quorumNumber` at the specified `index` - function getOperatorIndexUpdateOfIndexForQuorumAtIndex(uint32 operatorIndex, uint8 quorumNumber, uint32 index) external view returns (OperatorUpdate memory); + /// @notice Returns the _indexHistory entry for the specified `operatorIndex` and `quorumNumber` at the specified `index` + function getOperatorUpdateAtIndex(uint32 operatorIndex, uint8 quorumNumber, uint32 index) external view returns (OperatorUpdate memory); - /// @notice Returns the _totalOperatorsHistory entry for the specified `quorumNumber` at the specified `index` + /// @notice Returns the _operatorCountHistory entry for the specified `quorumNumber` at the specified `index` function getQuorumUpdateAtIndex(uint8 quorumNumber, uint32 index) external view returns (QuorumUpdate memory); /** * @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 + * @param index is the index of the entry in the dynamic array `_operatorCountHistory[quorumNumber]` to read data from */ - function getTotalOperatorsForQuorumAtBlockNumberByIndex(uint8 quorumNumber, uint32 blockNumber, uint32 index) external view returns (uint32); + function getTotalOperatorsForIndexAtBlockNumber(uint8 quorumNumber, uint32 blockNumber, uint32 index) external view returns (uint32); /// @notice Returns the current number of operators of this service for `quorumNumber`. function totalOperatorsForQuorum(uint8 quorumNumber) external view returns (uint32); /// @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); + function getOperatorListAtBlockNumber(uint8 quorumNumber, uint32 blockNumber) external view returns (bytes32[] memory); } \ No newline at end of file diff --git a/test/unit/BLSPubkeyRegistryUnit.t.sol b/test/unit/BLSPubkeyRegistryUnit.t.sol index 83063129..f803c73d 100644 --- a/test/unit/BLSPubkeyRegistryUnit.t.sol +++ b/test/unit/BLSPubkeyRegistryUnit.t.sol @@ -212,13 +212,15 @@ contract BLSPubkeyRegistryUnitTests is Test { testRegisterOperatorBLSPubkey(defaultOperator, pk); quorumApk = quorumApk.plus(BN254.hashToG1(pk)); quorumApkHash = bytes24(BN254.hashG1Point(quorumApk)); - require(quorumApkHash == blsPubkeyRegistry.getApkHashForQuorumAtBlockNumberFromIndex(defaultQuorumNumber, uint32(block.number + blockGap) , i + 1), "incorrect quorum aok updates"); + uint historyLength = blsPubkeyRegistry.getQuorumApkHistoryLength(defaultQuorumNumber); + assertEq(quorumApkHash, blsPubkeyRegistry.getApkHashForQuorumAtBlockNumberFromIndex(defaultQuorumNumber, uint32(block.number + blockGap), historyLength-1), "incorrect quorum apk update"); 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 + 2), "incorrect quorum aok updates"); + _deregisterOperator(pk); + quorumApk = quorumApk.plus(BN254.hashToG1(pk).negate()); + quorumApkHash = bytes24(BN254.hashG1Point(quorumApk)); + historyLength = blsPubkeyRegistry.getQuorumApkHistoryLength(defaultQuorumNumber); + assertEq(quorumApkHash, blsPubkeyRegistry.getApkHashForQuorumAtBlockNumberFromIndex(defaultQuorumNumber, uint32(block.number + blockGap), historyLength-1), "incorrect quorum apk update"); cheats.roll(block.number + 100); i++; } diff --git a/test/unit/IndexRegistryUnit.t.sol b/test/unit/IndexRegistryUnit.t.sol index 3026a0da..9d4489be 100644 --- a/test/unit/IndexRegistryUnit.t.sol +++ b/test/unit/IndexRegistryUnit.t.sol @@ -71,7 +71,7 @@ contract IndexRegistryUnitTests is Test { // Check _operatorIdToIndexHistory updates IIndexRegistry.OperatorUpdate memory operatorUpdate = indexRegistry - .getOperatorIndexUpdateOfIndexForQuorumAtIndex({operatorIndex: 0, quorumNumber: 1, index: 0}); + .getOperatorUpdateAtIndex({operatorIndex: 0, quorumNumber: 1, index: 0}); require(operatorUpdate.operatorId == operatorId1, "IndexRegistry.registerOperator: operatorId not operatorId1"); require( operatorUpdate.fromBlockNumber == block.number, @@ -116,7 +116,7 @@ contract IndexRegistryUnitTests is Test { // Check _operatorIdToIndexHistory updates IIndexRegistry.OperatorUpdate memory operatorUpdate = indexRegistry - .getOperatorIndexUpdateOfIndexForQuorumAtIndex({operatorIndex: 0, quorumNumber: 2, index: 0}); + .getOperatorUpdateAtIndex({operatorIndex: 0, quorumNumber: 2, index: 0}); require(operatorUpdate.operatorId == operatorId1, "IndexRegistry.registerOperator: operatorId not operatorId1"); require( operatorUpdate.fromBlockNumber == block.number, @@ -159,7 +159,7 @@ contract IndexRegistryUnitTests is Test { // Check _operatorIdToIndexHistory updates for quorum 1 IIndexRegistry.OperatorUpdate memory operatorUpdate = indexRegistry - .getOperatorIndexUpdateOfIndexForQuorumAtIndex({operatorIndex: 0, quorumNumber: 1, index: 0}); + .getOperatorUpdateAtIndex({operatorIndex: 0, quorumNumber: 1, index: 0}); require(operatorUpdate.operatorId == operatorId1, "IndexRegistry.registerOperator: operatorId not 1operatorId1"); require( operatorUpdate.fromBlockNumber == block.number, @@ -183,7 +183,7 @@ contract IndexRegistryUnitTests is Test { ); // Check _operatorIdToIndexHistory updates for quorum 2 - operatorUpdate = indexRegistry.getOperatorIndexUpdateOfIndexForQuorumAtIndex({operatorIndex: 0 , quorumNumber: 2, index: 0}); + operatorUpdate = indexRegistry.getOperatorUpdateAtIndex({operatorIndex: 0 , quorumNumber: 2, index: 0}); require(operatorUpdate.operatorId == operatorId1, "IndexRegistry.registerOperator: operatorId not operatorId1"); require( operatorUpdate.fromBlockNumber == block.number, @@ -226,7 +226,7 @@ contract IndexRegistryUnitTests is Test { // Check _operatorIdToIndexHistory updates IIndexRegistry.OperatorUpdate memory operatorUpdate = indexRegistry - .getOperatorIndexUpdateOfIndexForQuorumAtIndex({operatorIndex: 1, quorumNumber: 1, index: 0}); + .getOperatorUpdateAtIndex({operatorIndex: 1, quorumNumber: 1, index: 0}); require(operatorUpdate.operatorId == operatorId2, "IndexRegistry.registerOperator: operatorId not operatorId2"); require( operatorUpdate.fromBlockNumber == block.number, @@ -266,7 +266,7 @@ contract IndexRegistryUnitTests is Test { // Check operator's index IIndexRegistry.OperatorUpdate memory operatorUpdate = indexRegistry - .getOperatorIndexUpdateOfIndexForQuorumAtIndex({operatorIndex: 0, quorumNumber: defaultQuorumNumber, index: 1}); + .getOperatorUpdateAtIndex({operatorIndex: 0, quorumNumber: defaultQuorumNumber, index: 1}); require(operatorUpdate.fromBlockNumber == block.number, "fromBlockNumber not set correctly"); require(operatorUpdate.operatorId == bytes32(0), "incorrect operatorId"); @@ -299,7 +299,7 @@ contract IndexRegistryUnitTests is Test { // Check operator's index for removed quorums for (uint256 i = 0; i < quorumsToRemove.length; i++) { IIndexRegistry.OperatorUpdate memory operatorUpdate = indexRegistry - .getOperatorIndexUpdateOfIndexForQuorumAtIndex({operatorIndex: 2, quorumNumber: uint8(quorumsToRemove[i]), index: 1}); // 2 indexes -> 1 update and 1 remove + .getOperatorUpdateAtIndex({operatorIndex: 2, quorumNumber: uint8(quorumsToRemove[i]), index: 1}); // 2 indexes -> 1 update and 1 remove require(operatorUpdate.fromBlockNumber == block.number, "fromBlockNumber not set correctly"); require(operatorUpdate.operatorId == bytes32(0), "incorrect operatorId"); } @@ -319,7 +319,7 @@ contract IndexRegistryUnitTests is Test { // Check swapped operator's index for removed quorums for (uint256 i = 0; i < quorumsToRemove.length; i++) { IIndexRegistry.OperatorUpdate memory operatorUpdate = indexRegistry - .getOperatorIndexUpdateOfIndexForQuorumAtIndex({operatorIndex: 0, quorumNumber: uint8(quorumsToRemove[i]), index: 1}); // 2 indexes -> 1 update and 1 swap + .getOperatorUpdateAtIndex({operatorIndex: 0, quorumNumber: uint8(quorumsToRemove[i]), index: 1}); // 2 indexes -> 1 update and 1 swap require(operatorUpdate.fromBlockNumber == block.number, "fromBlockNumber not set correctly"); require(operatorUpdate.operatorId == operatorId3, "incorrect operatorId"); } @@ -336,9 +336,9 @@ contract IndexRegistryUnitTests is Test { _registerOperator(operatorId1, quorumNumbers); cheats.expectRevert( - "IndexRegistry.getTotalOperatorsForQuorumAtBlockNumberByIndex: provided index is too far in the past for provided block number" + "IndexRegistry.getTotalOperatorsForIndexAtBlockNumber: provided index is too far in the past for provided block number" ); - indexRegistry.getTotalOperatorsForQuorumAtBlockNumberByIndex(defaultQuorumNumber, uint32(block.number - 1), 0); + indexRegistry.getTotalOperatorsForIndexAtBlockNumber(defaultQuorumNumber, uint32(block.number - 1), 0); } function testGetTotalOperatorsForQuorumAtBlockNumberByIndex_revert_indexBlockMismatch() public { @@ -350,9 +350,9 @@ contract IndexRegistryUnitTests is Test { _registerOperator(operatorId2, quorumNumbers); cheats.expectRevert( - "IndexRegistry.getTotalOperatorsForQuorumAtBlockNumberByIndex: provided index is too far in the future for provided block number" + "IndexRegistry.getTotalOperatorsForIndexAtBlockNumber: provided index is too far in the future for provided block number" ); - indexRegistry.getTotalOperatorsForQuorumAtBlockNumberByIndex(defaultQuorumNumber, uint32(block.number), 0); + indexRegistry.getTotalOperatorsForIndexAtBlockNumber(defaultQuorumNumber, uint32(block.number), 0); } function testGetTotalOperatorsForQuorumAtBlockNumberByIndex() public { @@ -364,20 +364,20 @@ contract IndexRegistryUnitTests is Test { _registerOperator(operatorId2, quorumNumbers); // Check that the first total is correct - uint32 prevTotal = indexRegistry.getTotalOperatorsForQuorumAtBlockNumberByIndex( + uint32 prevTotal = indexRegistry.getTotalOperatorsForIndexAtBlockNumber( defaultQuorumNumber, uint32(block.number - 10), 1 ); - require(prevTotal == 1, "IndexRegistry.getTotalOperatorsForQuorumAtBlockNumberByIndex: prev total not 1"); + require(prevTotal == 1, "IndexRegistry.getTotalOperatorsForIndexAtBlockNumber: prev total not 1"); // Check that the total is correct - uint32 currentTotal = indexRegistry.getTotalOperatorsForQuorumAtBlockNumberByIndex( + uint32 currentTotal = indexRegistry.getTotalOperatorsForIndexAtBlockNumber( defaultQuorumNumber, uint32(block.number), 2 ); - require(currentTotal == 2, "IndexRegistry.getTotalOperatorsForQuorumAtBlockNumberByIndex: current total not 2"); + require(currentTotal == 2, "IndexRegistry.getTotalOperatorsForIndexAtBlockNumber: current total not 2"); } function testGetOperatorListForQuorumAtBlockNumber() public { @@ -394,46 +394,46 @@ contract IndexRegistryUnitTests is Test { indexRegistry.deregisterOperator(operatorId1, quorumNumbers); // Check the operator list after first registration - bytes32[] memory operatorList = indexRegistry.getOperatorListForQuorumAtBlockNumber( + bytes32[] memory operatorList = indexRegistry.getOperatorListAtBlockNumber( defaultQuorumNumber, uint32(block.number - 20) ); require( operatorList.length == 1, - "IndexRegistry.getOperatorListForQuorumAtBlockNumber: operator list length not 1" + "IndexRegistry.getOperatorListAtBlockNumber: operator list length not 1" ); require( operatorList[0] == operatorId1, - "IndexRegistry.getOperatorListForQuorumAtBlockNumber: operator list incorrect" + "IndexRegistry.getOperatorListAtBlockNumber: operator list incorrect" ); // Check the operator list after second registration - operatorList = indexRegistry.getOperatorListForQuorumAtBlockNumber( + operatorList = indexRegistry.getOperatorListAtBlockNumber( defaultQuorumNumber, uint32(block.number - 10) ); require( operatorList.length == 2, - "IndexRegistry.getOperatorListForQuorumAtBlockNumber: operator list length not 2" + "IndexRegistry.getOperatorListAtBlockNumber: operator list length not 2" ); require( operatorList[0] == operatorId1, - "IndexRegistry.getOperatorListForQuorumAtBlockNumber: operator list incorrect" + "IndexRegistry.getOperatorListAtBlockNumber: operator list incorrect" ); require( operatorList[1] == operatorId2, - "IndexRegistry.getOperatorListForQuorumAtBlockNumber: operator list incorrect" + "IndexRegistry.getOperatorListAtBlockNumber: operator list incorrect" ); // Check the operator list after deregistration - operatorList = indexRegistry.getOperatorListForQuorumAtBlockNumber(defaultQuorumNumber, uint32(block.number)); + operatorList = indexRegistry.getOperatorListAtBlockNumber(defaultQuorumNumber, uint32(block.number)); require( operatorList.length == 1, - "IndexRegistry.getOperatorListForQuorumAtBlockNumber: operator list length not 1" + "IndexRegistry.getOperatorListAtBlockNumber: operator list length not 1" ); require( operatorList[0] == operatorId2, - "IndexRegistry.getOperatorListForQuorumAtBlockNumber: operator list incorrect" + "IndexRegistry.getOperatorListAtBlockNumber: operator list incorrect" ); } @@ -490,7 +490,7 @@ contract IndexRegistryUnitTests is Test { // Check _operatorIdToIndexHistory updates IIndexRegistry.OperatorUpdate memory operatorUpdate; for (uint256 i = 0; i < quorumNumbers.length; i++) { - operatorUpdate = indexRegistry.getOperatorIndexUpdateOfIndexForQuorumAtIndex({ + operatorUpdate = indexRegistry.getOperatorUpdateAtIndex({ operatorIndex: 0, quorumNumber: uint8(quorumNumbers[i]), index: 0 @@ -597,7 +597,7 @@ contract IndexRegistryUnitTests is Test { // Check operator's index for removed quorums for (uint256 i = 0; i < quorumsToRemove.length; i++) { IIndexRegistry.OperatorUpdate memory operatorUpdate = indexRegistry - .getOperatorIndexUpdateOfIndexForQuorumAtIndex({operatorIndex: 1, quorumNumber: uint8(quorumsToRemove[i]), index: 1}); // 2 indexes -> 1 update and 1 remove + .getOperatorUpdateAtIndex({operatorIndex: 1, quorumNumber: uint8(quorumsToRemove[i]), index: 1}); // 2 indexes -> 1 update and 1 remove require(operatorUpdate.fromBlockNumber == block.number, "fromBlockNumber not set correctly"); require(operatorUpdate.operatorId == bytes32(0), "incorrect operatorId"); } @@ -617,7 +617,7 @@ contract IndexRegistryUnitTests is Test { // Check swapped operator's index for removed quorums for (uint256 i = 0; i < quorumsToRemove.length; i++) { IIndexRegistry.OperatorUpdate memory operatorUpdate = indexRegistry - .getOperatorIndexUpdateOfIndexForQuorumAtIndex({operatorIndex: 0, quorumNumber: uint8(quorumsToRemove[i]), index: 1}); // 2 indexes -> 1 update and 1 swap + .getOperatorUpdateAtIndex({operatorIndex: 0, quorumNumber: uint8(quorumsToRemove[i]), index: 1}); // 2 indexes -> 1 update and 1 swap require(operatorUpdate.fromBlockNumber == block.number, "fromBlockNumber not set correctly"); require(operatorUpdate.operatorId == operatorId2, "incorrect operatorId"); } From b92242121609e8c283a845a53c38ef3bb05f7ebe Mon Sep 17 00:00:00 2001 From: wadealexc Date: Wed, 1 Nov 2023 14:44:16 +0000 Subject: [PATCH 026/101] style: pull out common logic to a helper method --- src/IndexRegistry.sol | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/src/IndexRegistry.sol b/src/IndexRegistry.sol index 37e94243..04e2f80e 100644 --- a/src/IndexRegistry.sol +++ b/src/IndexRegistry.sol @@ -131,16 +131,7 @@ contract IndexRegistry is IndexRegistryStorage { QuorumUpdate storage lastUpdate = _latestQuorumUpdate(quorumNumber); uint32 newOperatorCount = lastUpdate.numOperators + 1; - // If the last update was made in this block, update the entry - // Otherwise, push a new historical entry. - if (lastUpdate.fromBlockNumber == uint32(block.number)) { - lastUpdate.numOperators = newOperatorCount; - } else { - _operatorCountHistory[quorumNumber].push(QuorumUpdate({ - numOperators: newOperatorCount, - fromBlockNumber: uint32(block.number) - })); - } + _updateOperatorCountHistory(quorumNumber, lastUpdate, newOperatorCount); return newOperatorCount; } @@ -152,8 +143,21 @@ contract IndexRegistry is IndexRegistryStorage { QuorumUpdate storage lastUpdate = _latestQuorumUpdate(quorumNumber); uint32 newOperatorCount = lastUpdate.numOperators - 1; - // If the last update was made in this block, update the entry - // Otherwise, push a new historical entry. + _updateOperatorCountHistory(quorumNumber, lastUpdate, newOperatorCount); + + return newOperatorCount; + } + + /** + * @notice Update `_operatorCountHistory` with a new operator count + * @dev If the lastUpdate was made in the this block, update the entry. + * Otherwise, push a new historical entry. + */ + function _updateOperatorCountHistory( + uint8 quorumNumber, + QuorumUpdate storage lastUpdate, + uint32 newOperatorCount + ) internal { if (lastUpdate.fromBlockNumber == uint32(block.number)) { lastUpdate.numOperators = newOperatorCount; } else { @@ -162,8 +166,6 @@ contract IndexRegistry is IndexRegistryStorage { fromBlockNumber: uint32(block.number) })); } - - return newOperatorCount; } /** From ea9aa1a1eb777b6535cb633589f16b71f7f88c11 Mon Sep 17 00:00:00 2001 From: wadealexc Date: Wed, 1 Nov 2023 14:55:55 +0000 Subject: [PATCH 027/101] style: pulled additional logic out into a helper method --- src/IndexRegistry.sol | 42 +++++++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/src/IndexRegistry.sol b/src/IndexRegistry.sol index 04e2f80e..d981b029 100644 --- a/src/IndexRegistry.sol +++ b/src/IndexRegistry.sol @@ -176,17 +176,9 @@ contract IndexRegistry is IndexRegistryStorage { function _popLastOperator(uint8 quorumNumber, uint32 index) internal returns (bytes32) { OperatorUpdate storage lastUpdate = _latestIndexUpdate(quorumNumber, index); bytes32 removedOperatorId = lastUpdate.operatorId; - - // If the last update was made in this block, update the entry - // Otherwise, push a new historical entry for the index - if (lastUpdate.fromBlockNumber == uint32(block.number)) { - lastUpdate.operatorId = OPERATOR_DOES_NOT_EXIST_ID; - } else { - _indexHistory[quorumNumber][index].push(OperatorUpdate({ - operatorId: OPERATOR_DOES_NOT_EXIST_ID, - fromBlockNumber: uint32(block.number) - })); - } + + // Set the current operator id for this quorum/index to 0 + _updateIndexHistory(quorumNumber, index, lastUpdate, OPERATOR_DOES_NOT_EXIST_ID); return removedOperatorId; } @@ -200,20 +192,32 @@ contract IndexRegistry is IndexRegistryStorage { function _assignOperatorToIndex(bytes32 operatorId, uint8 quorumNumber, uint32 index) internal { OperatorUpdate storage lastUpdate = _latestIndexUpdate(quorumNumber, index); - // If the last update was made in this block, update the entry - // Otherwise, push a new historical entry for the index + _updateIndexHistory(quorumNumber, index, lastUpdate, operatorId); + + // Assign the operator to their new current index + currentOperatorIndex[quorumNumber][operatorId] = index; + emit QuorumIndexUpdate(operatorId, quorumNumber, index); + } + + /** + * @notice Update `_indexHistory` with a new operator id for the current block + * @dev If the lastUpdate was made in the this block, update the entry. + * Otherwise, push a new historical entry. + */ + function _updateIndexHistory( + uint8 quorumNumber, + uint32 index, + OperatorUpdate storage lastUpdate, + bytes32 newOperatorId + ) internal { if (lastUpdate.fromBlockNumber == uint32(block.number)) { - lastUpdate.operatorId = operatorId; + lastUpdate.operatorId = newOperatorId; } else { _indexHistory[quorumNumber][index].push(OperatorUpdate({ - operatorId: operatorId, + operatorId: newOperatorId, fromBlockNumber: uint32(block.number) })); } - - // Assign the operator to their new current index - currentOperatorIndex[quorumNumber][operatorId] = index; - emit QuorumIndexUpdate(operatorId, quorumNumber, index); } /// @notice Returns the most recent operator count update for a quorum From b33bfc3411d0b3a265a82ca7c378ae873e2791b2 Mon Sep 17 00:00:00 2001 From: wadealexc Date: Wed, 1 Nov 2023 15:52:54 +0000 Subject: [PATCH 028/101] refactor: simplify registry coord state variable names and clean logic and comments for deregistration --- src/BLSRegistryCoordinatorWithIndices.sol | 226 +++++++----------- src/interfaces/IRegistryCoordinator.sol | 14 -- ...SRegistryCoordinatorWithIndicesHarness.sol | 8 +- 3 files changed, 92 insertions(+), 156 deletions(-) diff --git a/src/BLSRegistryCoordinatorWithIndices.sol b/src/BLSRegistryCoordinatorWithIndices.sol index b1338553..ae7f9df8 100644 --- a/src/BLSRegistryCoordinatorWithIndices.sol +++ b/src/BLSRegistryCoordinatorWithIndices.sol @@ -6,22 +6,11 @@ import "@openzeppelin-upgrades/contracts/proxy/utils/Initializable.sol"; import "eigenlayer-contracts/src/contracts/interfaces/IPauserRegistry.sol"; import "eigenlayer-contracts/src/contracts/interfaces/ISlasher.sol"; +import "src/contracts/libraries/BN254.sol"; import "eigenlayer-contracts/src/contracts/libraries/EIP1271SignatureUtils.sol"; +import "src/libraries/BitmapUtils.sol"; import "eigenlayer-contracts/src/contracts/permissions/Pausable.sol"; -<<<<<<< HEAD -import "./interfaces/IBLSRegistryCoordinatorWithIndices.sol"; -import "./interfaces/ISocketUpdater.sol"; -import "./interfaces/IServiceManager.sol"; -import "./BLSPubkeyRegistry.sol"; -import "./interfaces/IVoteWeigher.sol"; -import "./StakeRegistry.sol"; -import "./IndexRegistry.sol"; -import "./interfaces/IRegistryCoordinator.sol"; - -import "./libraries/BitmapUtils.sol"; -import "./libraries/BN254.sol"; -======= import "src/interfaces/IBLSRegistryCoordinatorWithIndices.sol"; import "src/interfaces/ISocketUpdater.sol"; import "src/interfaces/IServiceManager.sol"; @@ -29,7 +18,6 @@ import "src/interfaces/IBLSPubkeyRegistry.sol"; import "src/interfaces/IStakeRegistry.sol"; import "src/interfaces/IIndexRegistry.sol"; import "src/interfaces/IRegistryCoordinator.sol"; ->>>>>>> 12b09de (fix: fix compilation issues and tests) /** * @title A `RegistryCoordinator` that has three registries: @@ -69,12 +57,12 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr /// @notice the current number of quorums supported by the registry coordinator uint8 public quorumCount; - /// @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 maps quorum number => operator cap and kick params + mapping(uint8 => OperatorSetParam) internal _quorumParams; + /// @notice maps operator id => historical quorums they registered for + mapping(bytes32 => QuorumBitmapUpdate[]) internal _operatorBitmapHistory; + /// @notice maps operator address => operator id and status + mapping(address => Operator) internal _operatorInfo; /// @notice whether the salt has been used for an operator churn approval mapping(bytes32 => bool) public isChurnApproverSaltUsed; @@ -151,27 +139,6 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr EXTERNAL FUNCTIONS *******************************************************************************/ - /** - * @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({ - operator: msg.sender, - quorumNumbers: quorumNumbers, - pubkey: pubkey, - socket: 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 @@ -199,14 +166,14 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr * @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 + * @param churnApproverSignature is the signature of the churnApprover on the operator kick params */ function registerOperatorWithCoordinator( bytes calldata quorumNumbers, BN254.G1Point memory pubkey, string calldata socket, OperatorKickParam[] calldata operatorKickParams, - SignatureWithSaltAndExpiry memory signatureWithSaltAndExpiry + SignatureWithSaltAndExpiry memory churnApproverSignature ) external onlyWhenNotPaused(PAUSED_REGISTER_OPERATOR) { // register the operator uint32[] memory numOperatorsPerQuorum = _registerOperatorWithCoordinator({ @@ -221,10 +188,10 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr operatorIdsToSwap[0] = pubkey.hashG1Point(); // verify the churnApprover's signature - _verifyChurnApproverSignatureOnOperatorChurnApproval({ + _verifyChurnApproverSignature({ registeringOperatorId: operatorIdsToSwap[0], operatorKickParams: operatorKickParams, - signatureWithSaltAndExpiry: signatureWithSaltAndExpiry + churnApproverSignature: churnApproverSignature }); uint256 operatorToKickParamsIndex = 0; @@ -233,7 +200,7 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr // check that the quorum has reached the max operator count { uint8 quorumNumber = uint8(quorumNumbers[i]); - OperatorSetParam memory operatorSetParam = _quorumOperatorSetParams[quorumNumber]; + OperatorSetParam memory operatorSetParam = _quorumParams[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) { @@ -247,7 +214,7 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr // get the total stake for the quorum uint96 totalStakeForQuorum = stakeRegistry.getCurrentTotalStakeForQuorum(quorumNumber); - bytes32 operatorToKickId = _operators[operatorKickParams[operatorToKickParamsIndex].operator].operatorId; + bytes32 operatorToKickId = _operatorInfo[operatorKickParams[i].operator].operatorId; uint96 operatorToKickStake = stakeRegistry.getCurrentOperatorStakeForQuorum(operatorToKickId, quorumNumber); uint96 registeringOperatorStake = stakeRegistry.getCurrentOperatorStakeForQuorum(operatorIdsToSwap[0], quorumNumber); @@ -276,26 +243,6 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr } } - /** - * @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) = abi.decode(deregistrationData, (BN254.G1Point)); - // call internal function to deregister the operator - _deregisterOperatorWithCoordinator({ - operator: msg.sender, - quorumNumbers: quorumNumbers, - pubkey: pubkey - }); - } - /** * @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 @@ -317,8 +264,8 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr * @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); + require(_operatorInfo[msg.sender].status == OperatorStatus.REGISTERED, "BLSRegistryCoordinatorWithIndicies.updateSocket: operator is not registered"); + emit OperatorSocketUpdate(_operatorInfo[msg.sender].operatorId, socket); } /******************************************************************************* @@ -399,15 +346,11 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr bytes calldata quorumNumbers, BN254.G1Point memory pubkey, string memory socket -<<<<<<< HEAD - ) internal virtual returns(uint32[] memory) { -======= ) internal virtual returns(uint32[] memory) { ->>>>>>> 12b09de (fix: fix compilation issues and tests) - // get the quorum bitmap from the quorum numbers + // Create and validate bitmap from quorumNumbers 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"); + require(quorumBitmap <= MAX_QUORUM_BITMAP, "BLSRegistryCoordinatorWithIndices._registerOperatorWithCoordinator: bitmap exceeds max bitmap size"); + require(quorumBitmap != 0, "BLSRegistryCoordinatorWithIndices._registerOperatorWithCoordinator: bitmap cannot be 0"); /** * Register the operator with the BLSPubkeyRegistry, StakeRegistry, and IndexRegistry. Retrieves: @@ -424,26 +367,26 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr * Skip this step if the `nextUpdateBlockNumber` is already set for the last entry in the operator's bitmap history, * as this indicates that the operator previously completely deregistered, and thus is no longer registered for any quorums. */ - uint256 historyLength = _operatorIdToQuorumBitmapHistory[operatorId].length; - if (historyLength != 0 && _operatorIdToQuorumBitmapHistory[operatorId][historyLength - 1].nextUpdateBlockNumber == 0) { - uint256 prevQuorumBitmap = _operatorIdToQuorumBitmapHistory[operatorId][historyLength - 1].quorumBitmap; + uint256 historyLength = _operatorBitmapHistory[operatorId].length; + if (historyLength != 0 && _operatorBitmapHistory[operatorId][historyLength - 1].nextUpdateBlockNumber == 0) { + uint256 prevQuorumBitmap = _operatorBitmapHistory[operatorId][historyLength - 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; - _operatorIdToQuorumBitmapHistory[operatorId][historyLength - 1].nextUpdateBlockNumber = uint32(block.number); + _operatorBitmapHistory[operatorId][historyLength - 1].nextUpdateBlockNumber = uint32(block.number); } // set the operatorId to quorum bitmap history - _operatorIdToQuorumBitmapHistory[operatorId].push(QuorumBitmapUpdate({ + _operatorBitmapHistory[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({ + if (_operatorInfo[operator].status != OperatorStatus.REGISTERED) { + _operatorInfo[operator] = Operator({ operatorId: operatorId, status: OperatorStatus.REGISTERED }); @@ -474,7 +417,7 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr for (uint256 i = 0; i < numOperatorsPerQuorum.length; i++) { require( - numOperatorsPerQuorum[i] <= _quorumOperatorSetParams[uint8(quorumNumbers[i])].maxOperatorCount, + numOperatorsPerQuorum[i] <= _quorumParams[uint8(quorumNumbers[i])].maxOperatorCount, "BLSRegistryCoordinatorWithIndices._registerOperatorWithCoordinatorAndNoOverfilledQuorums: quorum is overfilled" ); } @@ -485,76 +428,74 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr bytes calldata quorumNumbers, BN254.G1Point memory pubkey ) internal virtual { - require(_operators[operator].status == OperatorStatus.REGISTERED, "BLSRegistryCoordinatorWithIndices._deregisterOperatorWithCoordinator: operator is not registered"); - - // get the operatorId of the operator - bytes32 operatorId = _operators[operator].operatorId; + /** + * Fetch the operator's id and status. Check that: + * - the operator is currently registered + * - the operatorId matches the provided pubkey hash + */ + Operator storage operatorInfo = _operatorInfo[operator]; + bytes32 operatorId = operatorInfo.operatorId; + require(operatorInfo.status == OperatorStatus.REGISTERED, "BLSRegistryCoordinatorWithIndices._deregisterOperatorWithCoordinator: operator is not registered"); require(operatorId == pubkey.hashG1Point(), "BLSRegistryCoordinatorWithIndices._deregisterOperatorWithCoordinator: operatorId does not match pubkey hash"); - - // get the bitmap of quorums to remove the operator from + + // Create and validate bitmap of quorums to remove uint256 quorumsToRemoveBitmap = BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers); + require(quorumsToRemoveBitmap <= MAX_QUORUM_BITMAP, "BLSRegistryCoordinatorWithIndices._deregisterOperatorWithCoordinator: bitmap exceeds max bitmap size"); + require(quorumsToRemoveBitmap != 0, "BLSRegistryCoordinatorWithIndices._deregisterOperatorWithCoordinator: bitmap cannot be 0"); - // get the quorum bitmap before the update - uint256 operatorQuorumBitmapHistoryLengthMinusOne = _operatorIdToQuorumBitmapHistory[operatorId].length - 1; - uint192 quorumBitmapBeforeUpdate = _operatorIdToQuorumBitmapHistory[operatorId][operatorQuorumBitmapHistoryLengthMinusOne].quorumBitmap; + // Get the operator's last quorum bitmap and update its "next" pointer to the current block + // TODO - change to use new history update pattern + QuorumBitmapUpdate storage lastUpdate = _latestBitmapUpdate(operatorId); + lastUpdate.nextUpdateBlockNumber = uint32(block.number); + uint192 previousBitmap = lastUpdate.quorumBitmap; - // and out quorums that the operator is not a part of - quorumsToRemoveBitmap = quorumBitmapBeforeUpdate & quorumsToRemoveBitmap; + // Remove quorums the operator isn't registered for and check that the result isn't empty + quorumsToRemoveBitmap = previousBitmap & 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; + // Check if the operator is completely deregistering + bool completeDeregistration = previousBitmap == quorumsToRemoveBitmap; - // deregister the operator from the BLSPubkeyRegistry + // Deregister operator with each of the registry contracts: 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); - - // 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 the operator still has active quorums, push a bitmap update. + // Otherwise, set them to deregistered + // TODO - change this to update history regardless if (!completeDeregistration) { - _operatorIdToQuorumBitmapHistory[operatorId].push(QuorumBitmapUpdate({ + _operatorBitmapHistory[operatorId].push(QuorumBitmapUpdate({ updateBlockNumber: uint32(block.number), nextUpdateBlockNumber: 0, - quorumBitmap: quorumBitmapBeforeUpdate & ~uint192(quorumsToRemoveBitmap) // this removes the quorumsToRemoveBitmap from the quorumBitmapBeforeUpdate + quorumBitmap: previousBitmap & ~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; - + operatorInfo.status = OperatorStatus.DEREGISTERED; emit OperatorDeregistered(operator, operatorId); } } /// @notice verifies churnApprover's signature on operator churn approval and increments the churnApprover nonce - function _verifyChurnApproverSignatureOnOperatorChurnApproval( + function _verifyChurnApproverSignature( bytes32 registeringOperatorId, OperatorKickParam[] memory operatorKickParams, - SignatureWithSaltAndExpiry memory signatureWithSaltAndExpiry + SignatureWithSaltAndExpiry memory churnApproverSignature ) 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"); + require(!isChurnApproverSaltUsed[churnApproverSignature.salt], "BLSRegistryCoordinatorWithIndices._verifyChurnApproverSignature: churnApprover salt already used"); + require(churnApproverSignature.expiry >= block.timestamp, "BLSRegistryCoordinatorWithIndices._verifyChurnApproverSignature: churnApprover signature expired"); // set salt used to true - isChurnApproverSaltUsed[signatureWithSaltAndExpiry.salt] = true; + isChurnApproverSaltUsed[churnApproverSignature.salt] = true; // check the churnApprover's signature - EIP1271SignatureUtils.checkSignature_EIP1271(churnApprover, calculateOperatorChurnApprovalDigestHash(registeringOperatorId, operatorKickParams, signatureWithSaltAndExpiry.salt, signatureWithSaltAndExpiry.expiry), signatureWithSaltAndExpiry.signature); + EIP1271SignatureUtils.checkSignature_EIP1271( + churnApprover, + calculateOperatorChurnApprovalDigestHash(registeringOperatorId, operatorKickParams, churnApproverSignature.salt, churnApproverSignature.expiry), + churnApproverSignature.signature + ); } /** @@ -581,7 +522,7 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr } function _setOperatorSetParams(uint8 quorumNumber, OperatorSetParam memory operatorSetParams) internal { - _quorumOperatorSetParams[quorumNumber] = operatorSetParams; + _quorumParams[quorumNumber] = operatorSetParams; emit OperatorSetParamsUpdated(quorumNumber, operatorSetParams); } @@ -595,23 +536,32 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr ejector = newEjector; } + /** + * @notice Fetch the most recent bitmap update for an operatorId + * @dev This method reverts (underflow) if the operator does not have any bitmap updates + */ + function _latestBitmapUpdate(bytes32 operatorId) internal view returns (QuorumBitmapUpdate storage) { + uint256 historyLength = _operatorBitmapHistory[operatorId].length; + return _operatorBitmapHistory[operatorId][historyLength - 1]; + } + /******************************************************************************* VIEW FUNCTIONS *******************************************************************************/ /// @notice Returns the operator set params for the given `quorumNumber` function getOperatorSetParams(uint8 quorumNumber) external view returns (OperatorSetParam memory) { - return _quorumOperatorSetParams[quorumNumber]; + return _quorumParams[quorumNumber]; } /// @notice Returns the operator struct for the given `operator` function getOperator(address operator) external view returns (Operator memory) { - return _operators[operator]; + return _operatorInfo[operator]; } /// @notice Returns the operatorId for the given `operator` function getOperatorId(address operator) external view returns (bytes32) { - return _operators[operator].operatorId; + return _operatorInfo[operator].operatorId; } /// @notice Returns the operator address for the given `operatorId` @@ -621,7 +571,7 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr /// @notice Returns the status for the given `operator` function getOperatorStatus(address operator) external view returns (IRegistryCoordinator.OperatorStatus) { - return _operators[operator].status; + return _operatorInfo[operator].status; } /// @notice Returns the indices of the quorumBitmaps for the provided `operatorIds` at the given `blockNumber` @@ -631,11 +581,11 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr ) external view returns (uint32[] memory) { uint32[] memory indices = new uint32[](operatorIds.length); for (uint256 i = 0; i < operatorIds.length; i++) { - uint256 length = _operatorIdToQuorumBitmapHistory[operatorIds[i]].length; + uint256 length = _operatorBitmapHistory[operatorIds[i]].length; for (uint256 j = 0; j < length; j++) { - if (_operatorIdToQuorumBitmapHistory[operatorIds[i]][length - j - 1].updateBlockNumber <= blockNumber) { + if (_operatorBitmapHistory[operatorIds[i]][length - j - 1].updateBlockNumber <= blockNumber) { uint32 nextUpdateBlockNumber = - _operatorIdToQuorumBitmapHistory[operatorIds[i]][length - j - 1].nextUpdateBlockNumber; + _operatorBitmapHistory[operatorIds[i]][length - j - 1].nextUpdateBlockNumber; require( nextUpdateBlockNumber == 0 || nextUpdateBlockNumber > blockNumber, "BLSRegistryCoordinatorWithIndices.getQuorumBitmapIndicesByOperatorIdsAtBlockNumber: operatorId has no quorumBitmaps at blockNumber" @@ -657,7 +607,7 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr uint32 blockNumber, uint256 index ) external view returns (uint192) { - QuorumBitmapUpdate memory quorumBitmapUpdate = _operatorIdToQuorumBitmapHistory[operatorId][index]; + QuorumBitmapUpdate memory quorumBitmapUpdate = _operatorBitmapHistory[operatorId][index]; require( quorumBitmapUpdate.updateBlockNumber <= blockNumber, "BLSRegistryCoordinatorWithIndices.getQuorumBitmapByOperatorIdAtBlockNumberByIndex: quorumBitmapUpdate is from after blockNumber" @@ -676,23 +626,23 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr bytes32 operatorId, uint256 index ) external view returns (QuorumBitmapUpdate memory) { - return _operatorIdToQuorumBitmapHistory[operatorId][index]; + return _operatorBitmapHistory[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; + uint256 quorumBitmapHistoryLength = _operatorBitmapHistory[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) { + if (quorumBitmapHistoryLength == 0 || _operatorBitmapHistory[operatorId][quorumBitmapHistoryLength - 1].nextUpdateBlockNumber != 0) { return 0; } - return _operatorIdToQuorumBitmapHistory[operatorId][quorumBitmapHistoryLength - 1].quorumBitmap; + return _operatorBitmapHistory[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; + return _operatorBitmapHistory[operatorId].length; } /// @notice Returns the number of registries diff --git a/src/interfaces/IRegistryCoordinator.sol b/src/interfaces/IRegistryCoordinator.sol index 21320b2d..c693ba0d 100644 --- a/src/interfaces/IRegistryCoordinator.sol +++ b/src/interfaces/IRegistryCoordinator.sol @@ -83,18 +83,4 @@ interface IRegistryCoordinator { /// @notice Returns the number of registries function numRegistries() external view returns (uint256); - - /** - * @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 - */ - function registerOperatorWithCoordinator(bytes memory quorumNumbers, bytes calldata registrationData) external; - - /** - * @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 - */ - function deregisterOperatorWithCoordinator(bytes calldata quorumNumbers, bytes calldata deregistrationData) external; } \ No newline at end of file diff --git a/test/harnesses/BLSRegistryCoordinatorWithIndicesHarness.sol b/test/harnesses/BLSRegistryCoordinatorWithIndicesHarness.sol index fb4af560..25b97b59 100644 --- a/test/harnesses/BLSRegistryCoordinatorWithIndicesHarness.sol +++ b/test/harnesses/BLSRegistryCoordinatorWithIndicesHarness.sol @@ -19,16 +19,16 @@ contract BLSRegistryCoordinatorWithIndicesHarness is BLSRegistryCoordinatorWithI } function setOperatorId(address operator, bytes32 operatorId) external { - _operators[operator].operatorId = operatorId; + _operatorInfo[operator].operatorId = operatorId; } function recordOperatorQuorumBitmapUpdate(bytes32 operatorId, uint192 quorumBitmap) external { - uint256 operatorQuorumBitmapHistoryLength = _operatorIdToQuorumBitmapHistory[operatorId].length; + uint256 operatorQuorumBitmapHistoryLength = _operatorBitmapHistory[operatorId].length; if (operatorQuorumBitmapHistoryLength != 0) { - _operatorIdToQuorumBitmapHistory[operatorId][operatorQuorumBitmapHistoryLength - 1].nextUpdateBlockNumber = uint32(block.number); + _operatorBitmapHistory[operatorId][operatorQuorumBitmapHistoryLength - 1].nextUpdateBlockNumber = uint32(block.number); } - _operatorIdToQuorumBitmapHistory[operatorId].push(QuorumBitmapUpdate({ + _operatorBitmapHistory[operatorId].push(QuorumBitmapUpdate({ updateBlockNumber: uint32(block.number), nextUpdateBlockNumber: 0, quorumBitmap: quorumBitmap From c86303ea90ec61c21a8bc1e991a64a2dacd35166 Mon Sep 17 00:00:00 2001 From: wadealexc Date: Wed, 1 Nov 2023 21:28:17 +0000 Subject: [PATCH 029/101] fix: enforce invariant that existing indice have nonzero length history. also fix tests --- src/IndexRegistry.sol | 25 +++++- ...LSRegistryCoordinatorWithIndicesUnit.t.sol | 6 +- test/unit/IndexRegistryUnit.t.sol | 85 +++++++++---------- 3 files changed, 64 insertions(+), 52 deletions(-) diff --git a/src/IndexRegistry.sol b/src/IndexRegistry.sol index d981b029..a55173d1 100644 --- a/src/IndexRegistry.sol +++ b/src/IndexRegistry.sol @@ -133,6 +133,15 @@ contract IndexRegistry is IndexRegistryStorage { _updateOperatorCountHistory(quorumNumber, lastUpdate, newOperatorCount); + // If this is the first time we're using this index, push its first update + // This maintains an invariant: existing indices have nonzero history + if (_indexHistory[quorumNumber][newOperatorCount - 1].length == 0) { + _indexHistory[quorumNumber][newOperatorCount - 1].push(OperatorUpdate({ + operatorId: OPERATOR_DOES_NOT_EXIST_ID, + fromBlockNumber: uint32(block.number) + })); + } + return newOperatorCount; } @@ -302,6 +311,18 @@ contract IndexRegistry is IndexRegistryStorage { return _operatorCountHistory[quorumNumber][index]; } + /// @notice Returns the most recent _operatorCountHistory entry for the specified quorumNumber + /// @dev Reverts if the quorum does not exist + function getLatestQuorumUpdate(uint8 quorumNumber) external view returns (QuorumUpdate memory) { + return _latestQuorumUpdate(quorumNumber); + } + + /// @notice Returns the most recent _operatorCountHistory entry for the specified quorumNumber + /// @dev Reverts if there is no update for the given index + function getLatestOperatorUpdate(uint8 quorumNumber, uint32 index) external view returns (OperatorUpdate memory) { + return _latestIndexUpdate(quorumNumber, 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 @@ -319,7 +340,7 @@ contract IndexRegistry is IndexRegistryStorage { // blocknumber must be at or after the "index'th" entry's fromBlockNumber require( blockNumber >= quorumUpdate.fromBlockNumber, - "IndexRegistry.getTotalOperatorsForQuorumAtBlockNumberByIndex: provided index is too far in the past for provided block number" + "IndexRegistry.getTotalOperatorsForIndexAtBlockNumber: 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 @@ -327,7 +348,7 @@ contract IndexRegistry is IndexRegistryStorage { QuorumUpdate memory nextQuorumUpdate = _operatorCountHistory[quorumNumber][index + 1]; require( blockNumber < nextQuorumUpdate.fromBlockNumber, - "IndexRegistry.getTotalOperatorsForQuorumAtBlockNumberByIndex: provided index is too far in the future for provided block number" + "IndexRegistry.getTotalOperatorsForIndexAtBlockNumber: provided index is too far in the future for provided block number" ); } return quorumUpdate.numOperators; diff --git a/test/unit/BLSRegistryCoordinatorWithIndicesUnit.t.sol b/test/unit/BLSRegistryCoordinatorWithIndicesUnit.t.sol index 3bff2d3a..644c112c 100644 --- a/test/unit/BLSRegistryCoordinatorWithIndicesUnit.t.sol +++ b/test/unit/BLSRegistryCoordinatorWithIndicesUnit.t.sol @@ -126,7 +126,7 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { function testRegisterOperatorWithCoordinator_EmptyQuorumNumbers_Reverts() public { bytes memory emptyQuorumNumbers = new bytes(0); - cheats.expectRevert("BLSRegistryCoordinatorWithIndices._registerOperatorWithCoordinator: quorumBitmap cannot be 0"); + cheats.expectRevert("BLSRegistryCoordinatorWithIndices._registerOperatorWithCoordinator: bitmap cannot be 0"); cheats.prank(defaultOperator); registryCoordinator.registerOperatorWithCoordinator(emptyQuorumNumbers, defaultPubKey, defaultSocket); } @@ -134,7 +134,7 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { function testRegisterOperatorWithCoordinator_QuorumNumbersTooLarge_Reverts() public { bytes memory quorumNumbersTooLarge = new bytes(1); quorumNumbersTooLarge[0] = 0xC0; - cheats.expectRevert("BLSRegistryCoordinatorWithIndices._registerOperatorWithCoordinator: quorumBitmap exceeds of max bitmap size"); + cheats.expectRevert("BLSRegistryCoordinatorWithIndices._registerOperatorWithCoordinator: bitmap exceeds max bitmap size"); registryCoordinator.registerOperatorWithCoordinator(quorumNumbersTooLarge, defaultPubKey, defaultSocket); } @@ -805,7 +805,7 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { cheats.roll(registrationBlockNumber); ISignatureUtils.SignatureWithSaltAndExpiry memory signatureWithSaltAndExpiry = _signOperatorChurnApproval(operatorToRegisterId, operatorKickParams, defaultSalt, block.timestamp - 1); cheats.prank(operatorToRegister); - cheats.expectRevert("BLSRegistryCoordinatorWithIndices._verifyChurnApproverSignatureOnOperatorChurnApproval: churnApprover signature expired"); + cheats.expectRevert("BLSRegistryCoordinatorWithIndices._verifyChurnApproverSignature: churnApprover signature expired"); registryCoordinator.registerOperatorWithCoordinator(quorumNumbers, operatorToRegisterPubKey, defaultSocket, operatorKickParams, signatureWithSaltAndExpiry); } diff --git a/test/unit/IndexRegistryUnit.t.sol b/test/unit/IndexRegistryUnit.t.sol index 9d4489be..3a4f10db 100644 --- a/test/unit/IndexRegistryUnit.t.sol +++ b/test/unit/IndexRegistryUnit.t.sol @@ -61,6 +61,8 @@ contract IndexRegistryUnitTests is Test { cheats.prank(address(registryCoordinatorMock)); uint32[] memory numOperatorsPerQuorum = indexRegistry.registerOperator(operatorId1, quorumNumbers); + emit log_named_uint("hi: ", 1); + // Check return value require( numOperatorsPerQuorum.length == 1, @@ -68,10 +70,11 @@ contract IndexRegistryUnitTests is Test { ); require(numOperatorsPerQuorum[0] == 1, "IndexRegistry.registerOperator: numOperatorsPerQuorum[0] not 1"); + emit log_named_uint("hi: ", 2); // Check _operatorIdToIndexHistory updates IIndexRegistry.OperatorUpdate memory operatorUpdate = indexRegistry - .getOperatorUpdateAtIndex({operatorIndex: 0, quorumNumber: 1, index: 0}); + .getOperatorUpdateAtIndex({operatorIndex: 0, quorumNumber: defaultQuorumNumber, index: 0}); require(operatorUpdate.operatorId == operatorId1, "IndexRegistry.registerOperator: operatorId not operatorId1"); require( operatorUpdate.fromBlockNumber == block.number, @@ -80,7 +83,8 @@ contract IndexRegistryUnitTests is Test { // Check _totalOperatorsHistory updates IIndexRegistry.QuorumUpdate memory quorumUpdate = indexRegistry - .getQuorumUpdateAtIndex(1, 1); + .getLatestQuorumUpdate(defaultQuorumNumber); + require( quorumUpdate.numOperators == 1, "IndexRegistry.registerOperator: totalOperatorsHistory num operators not 1" @@ -90,7 +94,7 @@ contract IndexRegistryUnitTests is Test { "IndexRegistry.registerOperator: totalOperatorsHistory fromBlockNumber not correct" ); require( - indexRegistry.totalOperatorsForQuorum(1) == 1, + indexRegistry.totalOperatorsForQuorum(defaultQuorumNumber) == 1, "IndexRegistry.registerOperator: total operators for quorum not updated correctly" ); } @@ -116,7 +120,7 @@ contract IndexRegistryUnitTests is Test { // Check _operatorIdToIndexHistory updates IIndexRegistry.OperatorUpdate memory operatorUpdate = indexRegistry - .getOperatorUpdateAtIndex({operatorIndex: 0, quorumNumber: 2, index: 0}); + .getOperatorUpdateAtIndex({operatorIndex: 0, quorumNumber: defaultQuorumNumber + 1, index: 0}); require(operatorUpdate.operatorId == operatorId1, "IndexRegistry.registerOperator: operatorId not operatorId1"); require( operatorUpdate.fromBlockNumber == block.number, @@ -125,7 +129,7 @@ contract IndexRegistryUnitTests is Test { // Check _totalOperatorsHistory updates IIndexRegistry.QuorumUpdate memory quorumUpdate = indexRegistry - .getQuorumUpdateAtIndex(2, 1); + .getLatestQuorumUpdate(defaultQuorumNumber + 1); require( quorumUpdate.numOperators == 1, "IndexRegistry.registerOperator: totalOperatorsHistory num operators not 1" @@ -135,7 +139,7 @@ contract IndexRegistryUnitTests is Test { "IndexRegistry.registerOperator: totalOperatorsHistory fromBlockNumber not correct" ); require( - indexRegistry.totalOperatorsForQuorum(2) == 1, + indexRegistry.totalOperatorsForQuorum(defaultQuorumNumber + 1) == 1, "IndexRegistry.registerOperator: total operators for quorum not updated correctly" ); } @@ -159,7 +163,7 @@ contract IndexRegistryUnitTests is Test { // Check _operatorIdToIndexHistory updates for quorum 1 IIndexRegistry.OperatorUpdate memory operatorUpdate = indexRegistry - .getOperatorUpdateAtIndex({operatorIndex: 0, quorumNumber: 1, index: 0}); + .getOperatorUpdateAtIndex({operatorIndex: 0, quorumNumber: defaultQuorumNumber, index: 0}); require(operatorUpdate.operatorId == operatorId1, "IndexRegistry.registerOperator: operatorId not 1operatorId1"); require( operatorUpdate.fromBlockNumber == block.number, @@ -168,7 +172,7 @@ contract IndexRegistryUnitTests is Test { // Check _totalOperatorsHistory updates for quorum 1 IIndexRegistry.QuorumUpdate memory quorumUpdate = indexRegistry - .getQuorumUpdateAtIndex(1, 1); + .getLatestQuorumUpdate(defaultQuorumNumber); require( quorumUpdate.numOperators == 1, "IndexRegistry.registerOperator: totalOperatorsHistory numOperators not 1" @@ -178,12 +182,12 @@ contract IndexRegistryUnitTests is Test { "IndexRegistry.registerOperator: totalOperatorsHistory fromBlockNumber not correct" ); require( - indexRegistry.totalOperatorsForQuorum(1) == 1, + indexRegistry.totalOperatorsForQuorum(defaultQuorumNumber) == 1, "IndexRegistry.registerOperator: total operators for quorum not updated correctly" ); // Check _operatorIdToIndexHistory updates for quorum 2 - operatorUpdate = indexRegistry.getOperatorUpdateAtIndex({operatorIndex: 0 , quorumNumber: 2, index: 0}); + operatorUpdate = indexRegistry.getOperatorUpdateAtIndex({operatorIndex: 0 , quorumNumber: defaultQuorumNumber + 1, index: 0}); require(operatorUpdate.operatorId == operatorId1, "IndexRegistry.registerOperator: operatorId not operatorId1"); require( operatorUpdate.fromBlockNumber == block.number, @@ -191,7 +195,7 @@ contract IndexRegistryUnitTests is Test { ); // Check _totalOperatorsHistory updates for quorum 2 - quorumUpdate = indexRegistry.getQuorumUpdateAtIndex(2, 1); + quorumUpdate = indexRegistry.getLatestQuorumUpdate(defaultQuorumNumber + 1); require( quorumUpdate.numOperators == 1, "IndexRegistry.registerOperator: totalOperatorsHistory num operators not 1" @@ -201,7 +205,7 @@ contract IndexRegistryUnitTests is Test { "IndexRegistry.registerOperator: totalOperatorsHistory fromBlockNumber not correct" ); require( - indexRegistry.totalOperatorsForQuorum(2) == 1, + indexRegistry.totalOperatorsForQuorum(defaultQuorumNumber + 1) == 1, "IndexRegistry.registerOperator: total operators for quorum not updated correctly" ); } @@ -226,7 +230,7 @@ contract IndexRegistryUnitTests is Test { // Check _operatorIdToIndexHistory updates IIndexRegistry.OperatorUpdate memory operatorUpdate = indexRegistry - .getOperatorUpdateAtIndex({operatorIndex: 1, quorumNumber: 1, index: 0}); + .getLatestOperatorUpdate({quorumNumber: defaultQuorumNumber, index: 1}); require(operatorUpdate.operatorId == operatorId2, "IndexRegistry.registerOperator: operatorId not operatorId2"); require( operatorUpdate.fromBlockNumber == block.number, @@ -235,7 +239,7 @@ contract IndexRegistryUnitTests is Test { // Check _totalOperatorsHistory updates IIndexRegistry.QuorumUpdate memory quorumUpdate = indexRegistry - .getQuorumUpdateAtIndex(1, 2); + .getLatestQuorumUpdate(defaultQuorumNumber); require( quorumUpdate.numOperators == 2, "IndexRegistry.registerOperator: totalOperatorsHistory num operators not 2" @@ -245,7 +249,7 @@ contract IndexRegistryUnitTests is Test { "IndexRegistry.registerOperator: totalOperatorsHistory fromBlockNumber not correct" ); require( - indexRegistry.totalOperatorsForQuorum(1) == 2, + indexRegistry.totalOperatorsForQuorum(defaultQuorumNumber) == 2, "IndexRegistry.registerOperator: total operators for quorum not updated correctly" ); } @@ -266,16 +270,16 @@ contract IndexRegistryUnitTests is Test { // Check operator's index IIndexRegistry.OperatorUpdate memory operatorUpdate = indexRegistry - .getOperatorUpdateAtIndex({operatorIndex: 0, quorumNumber: defaultQuorumNumber, index: 1}); + .getLatestOperatorUpdate({quorumNumber: defaultQuorumNumber, index: 0}); require(operatorUpdate.fromBlockNumber == block.number, "fromBlockNumber not set correctly"); require(operatorUpdate.operatorId == bytes32(0), "incorrect operatorId"); // Check total operators IIndexRegistry.QuorumUpdate memory quorumUpdate = indexRegistry - .getQuorumUpdateAtIndex(defaultQuorumNumber, 2); + .getLatestQuorumUpdate(defaultQuorumNumber); require(quorumUpdate.fromBlockNumber == block.number, "fromBlockNumber not set correctly"); require(quorumUpdate.numOperators == 0, "incorrect total number of operators"); - require(indexRegistry.totalOperatorsForQuorum(1) == 0, "operator not deregistered correctly"); + require(indexRegistry.totalOperatorsForQuorum(defaultQuorumNumber) == 0, "operator not deregistered correctly"); } function testDeregisterOperatorMultipleQuorums() public { @@ -299,7 +303,7 @@ contract IndexRegistryUnitTests is Test { // Check operator's index for removed quorums for (uint256 i = 0; i < quorumsToRemove.length; i++) { IIndexRegistry.OperatorUpdate memory operatorUpdate = indexRegistry - .getOperatorUpdateAtIndex({operatorIndex: 2, quorumNumber: uint8(quorumsToRemove[i]), index: 1}); // 2 indexes -> 1 update and 1 remove + .getLatestOperatorUpdate({quorumNumber: uint8(quorumsToRemove[i]), index: 2}); require(operatorUpdate.fromBlockNumber == block.number, "fromBlockNumber not set correctly"); require(operatorUpdate.operatorId == bytes32(0), "incorrect operatorId"); } @@ -307,7 +311,7 @@ contract IndexRegistryUnitTests is Test { // Check total operators for removed quorums for (uint256 i = 0; i < quorumsToRemove.length; i++) { IIndexRegistry.QuorumUpdate memory quorumUpdate = indexRegistry - .getQuorumUpdateAtIndex(uint8(quorumsToRemove[i]), 4); // 5 updates total + .getLatestQuorumUpdate(uint8(quorumsToRemove[i])); // 5 updates total require(quorumUpdate.fromBlockNumber == block.number, "fromBlockNumber not set correctly"); require(quorumUpdate.numOperators == 2, "incorrect total number of operators"); require( @@ -319,7 +323,7 @@ contract IndexRegistryUnitTests is Test { // Check swapped operator's index for removed quorums for (uint256 i = 0; i < quorumsToRemove.length; i++) { IIndexRegistry.OperatorUpdate memory operatorUpdate = indexRegistry - .getOperatorUpdateAtIndex({operatorIndex: 0, quorumNumber: uint8(quorumsToRemove[i]), index: 1}); // 2 indexes -> 1 update and 1 swap + .getLatestOperatorUpdate({quorumNumber: uint8(quorumsToRemove[i]), index: 0}); require(operatorUpdate.fromBlockNumber == block.number, "fromBlockNumber not set correctly"); require(operatorUpdate.operatorId == operatorId3, "incorrect operatorId"); } @@ -355,7 +359,7 @@ contract IndexRegistryUnitTests is Test { indexRegistry.getTotalOperatorsForIndexAtBlockNumber(defaultQuorumNumber, uint32(block.number), 0); } - function testGetTotalOperatorsForQuorumAtBlockNumberByIndex() public { + function testGetTotalOperatorsForIndexAtBlockNumber() public { // Add two operators bytes memory quorumNumbers = new bytes(1); quorumNumbers[0] = bytes1(defaultQuorumNumber); @@ -367,7 +371,7 @@ contract IndexRegistryUnitTests is Test { uint32 prevTotal = indexRegistry.getTotalOperatorsForIndexAtBlockNumber( defaultQuorumNumber, uint32(block.number - 10), - 1 + 0 ); require(prevTotal == 1, "IndexRegistry.getTotalOperatorsForIndexAtBlockNumber: prev total not 1"); @@ -375,7 +379,7 @@ contract IndexRegistryUnitTests is Test { uint32 currentTotal = indexRegistry.getTotalOperatorsForIndexAtBlockNumber( defaultQuorumNumber, uint32(block.number), - 2 + 1 ); require(currentTotal == 2, "IndexRegistry.getTotalOperatorsForIndexAtBlockNumber: current total not 2"); } @@ -490,8 +494,7 @@ contract IndexRegistryUnitTests is Test { // Check _operatorIdToIndexHistory updates IIndexRegistry.OperatorUpdate memory operatorUpdate; for (uint256 i = 0; i < quorumNumbers.length; i++) { - operatorUpdate = indexRegistry.getOperatorUpdateAtIndex({ - operatorIndex: 0, + operatorUpdate = indexRegistry.getLatestOperatorUpdate({ quorumNumber: uint8(quorumNumbers[i]), index: 0 }); @@ -505,7 +508,7 @@ contract IndexRegistryUnitTests is Test { // Check _totalOperatorsHistory updates IIndexRegistry.QuorumUpdate memory quorumUpdate; for (uint256 i = 0; i < quorumNumbers.length; i++) { - quorumUpdate = indexRegistry.getQuorumUpdateAtIndex(uint8(quorumNumbers[i]), 1); + quorumUpdate = indexRegistry.getLatestQuorumUpdate(uint8(quorumNumbers[i])); require( quorumUpdate.numOperators == 1, "IndexRegistry.registerOperator: totalOperatorsHistory num operators not 1" @@ -534,23 +537,11 @@ contract IndexRegistryUnitTests is Test { // Check history of _totalOperatorsHistory updates at each blockNumber IIndexRegistry.QuorumUpdate memory quorumUpdate; - uint256 numOperators = 1; - for (uint256 blockNumber = block.number - 20; blockNumber <= block.number; blockNumber += 10) { - for (uint256 i = 0; i < quorumNumbers.length; i++) { - quorumUpdate = indexRegistry.getQuorumUpdateAtIndex( - uint8(quorumNumbers[i]), - uint32(numOperators) - ); - require( - quorumUpdate.numOperators == numOperators, - "IndexRegistry.registerOperator: totalOperatorsHistory num operators not correct" - ); - require( - quorumUpdate.fromBlockNumber == blockNumber, - "IndexRegistry.registerOperator: totalOperatorsHistory fromBlockNumber not correct" - ); - } - numOperators++; + uint256 numOperators = 3; + for (uint256 i = 0; i < quorumNumbers.length; i++) { + quorumUpdate = indexRegistry.getLatestQuorumUpdate(uint8(quorumNumbers[i])); + assertEq(quorumUpdate.numOperators, numOperators, "num operators not correct"); + assertEq(quorumUpdate.fromBlockNumber, block.number, "latest update should be from current block number"); } } @@ -597,7 +588,7 @@ contract IndexRegistryUnitTests is Test { // Check operator's index for removed quorums for (uint256 i = 0; i < quorumsToRemove.length; i++) { IIndexRegistry.OperatorUpdate memory operatorUpdate = indexRegistry - .getOperatorUpdateAtIndex({operatorIndex: 1, quorumNumber: uint8(quorumsToRemove[i]), index: 1}); // 2 indexes -> 1 update and 1 remove + .getLatestOperatorUpdate({quorumNumber: uint8(quorumsToRemove[i]), index: 1}); require(operatorUpdate.fromBlockNumber == block.number, "fromBlockNumber not set correctly"); require(operatorUpdate.operatorId == bytes32(0), "incorrect operatorId"); } @@ -605,7 +596,7 @@ contract IndexRegistryUnitTests is Test { // Check total operators for removed quorums for (uint256 i = 0; i < quorumsToRemove.length; i++) { IIndexRegistry.QuorumUpdate memory quorumUpdate = indexRegistry - .getQuorumUpdateAtIndex(uint8(quorumsToRemove[i]), 3); // 4 updates total + .getLatestQuorumUpdate(uint8(quorumsToRemove[i])); require(quorumUpdate.fromBlockNumber == block.number, "fromBlockNumber not set correctly"); require(quorumUpdate.numOperators == 1, "incorrect total number of operators"); require( @@ -617,7 +608,7 @@ contract IndexRegistryUnitTests is Test { // Check swapped operator's index for removed quorums for (uint256 i = 0; i < quorumsToRemove.length; i++) { IIndexRegistry.OperatorUpdate memory operatorUpdate = indexRegistry - .getOperatorUpdateAtIndex({operatorIndex: 0, quorumNumber: uint8(quorumsToRemove[i]), index: 1}); // 2 indexes -> 1 update and 1 swap + .getLatestOperatorUpdate({quorumNumber: uint8(quorumsToRemove[i]), index: 0}); require(operatorUpdate.fromBlockNumber == block.number, "fromBlockNumber not set correctly"); require(operatorUpdate.operatorId == operatorId2, "incorrect operatorId"); } From 41742953f65711b81aaaab31c0a3c73a6a459b93 Mon Sep 17 00:00:00 2001 From: wadealexc Date: Thu, 2 Nov 2023 14:47:45 +0000 Subject: [PATCH 030/101] style: shorten state variable and function naming in registry coordinator --- src/BLSOperatorStateRetriever.sol | 8 +- src/BLSRegistryCoordinatorWithIndices.sol | 92 ++++++------- src/BLSSignatureChecker.sol | 2 +- src/StakeRegistry.sol | 2 +- .../IBLSRegistryCoordinatorWithIndices.sol | 2 +- src/interfaces/IRegistryCoordinator.sol | 10 +- test/mocks/RegistryCoordinatorMock.sol | 14 +- test/unit/BLSOperatorStateRetrieverUnit.t.sol | 2 +- ...LSRegistryCoordinatorWithIndicesUnit.t.sol | 124 +++++++++--------- test/unit/BLSSignatureCheckerUnit.t.sol | 2 +- test/utils/MockAVSDeployer.sol | 4 +- 11 files changed, 124 insertions(+), 138 deletions(-) diff --git a/src/BLSOperatorStateRetriever.sol b/src/BLSOperatorStateRetriever.sol index 142fb960..5b88272e 100644 --- a/src/BLSOperatorStateRetriever.sol +++ b/src/BLSOperatorStateRetriever.sol @@ -43,9 +43,9 @@ contract BLSOperatorStateRetriever { ) external view returns (uint256, Operator[][] memory) { bytes32[] memory operatorIds = new bytes32[](1); operatorIds[0] = operatorId; - uint256 index = registryCoordinator.getQuorumBitmapIndicesByOperatorIdsAtBlockNumber(blockNumber, operatorIds)[0]; + uint256 index = registryCoordinator.getQuorumBitmapIndicesAtBlockNumber(blockNumber, operatorIds)[0]; - uint256 quorumBitmap = registryCoordinator.getQuorumBitmapByOperatorIdAtBlockNumberByIndex(operatorId, blockNumber, index); + uint256 quorumBitmap = registryCoordinator.getQuorumBitmapAtBlockNumberByIndex(operatorId, blockNumber, index); bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); @@ -109,7 +109,7 @@ contract BLSOperatorStateRetriever { 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); + checkSignaturesIndices.nonSignerQuorumBitmapIndices = registryCoordinator.getQuorumBitmapIndicesAtBlockNumber(referenceBlockNumber, nonSignerOperatorIds); // get the indices of the totalStake updates for each of the quorums in the quorumNumbers array checkSignaturesIndices.totalStakeIndices = stakeRegistry.getTotalStakeIndicesByQuorumNumbersAtBlockNumber(referenceBlockNumber, quorumNumbers); @@ -122,7 +122,7 @@ contract BLSOperatorStateRetriever { for (uint i = 0; i < nonSignerOperatorIds.length; i++) { // get the quorumBitmap for the operator at the given blocknumber and index uint192 nonSignerQuorumBitmap = - registryCoordinator.getQuorumBitmapByOperatorIdAtBlockNumberByIndex( + registryCoordinator.getQuorumBitmapAtBlockNumberByIndex( nonSignerOperatorIds[i], referenceBlockNumber, checkSignaturesIndices.nonSignerQuorumBitmapIndices[i] diff --git a/src/BLSRegistryCoordinatorWithIndices.sol b/src/BLSRegistryCoordinatorWithIndices.sol index ae7f9df8..7118ea13 100644 --- a/src/BLSRegistryCoordinatorWithIndices.sol +++ b/src/BLSRegistryCoordinatorWithIndices.sol @@ -145,17 +145,24 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr * @param pubkey is the BLS public key of the operator * @param socket is the socket of the operator */ - function registerOperatorWithCoordinator( + function registerOperator( bytes calldata quorumNumbers, BN254.G1Point memory pubkey, string calldata socket ) external onlyWhenNotPaused(PAUSED_REGISTER_OPERATOR) { - _registerOperatorWithCoordinatorAndNoOverfilledQuorums({ + uint32[] memory numOperatorsPerQuorum = _registerOperator({ operator: msg.sender, quorumNumbers: quorumNumbers, pubkey: pubkey, socket: socket }); + + for (uint256 i = 0; i < numOperatorsPerQuorum.length; i++) { + require( + numOperatorsPerQuorum[i] <= _quorumParams[uint8(quorumNumbers[i])].maxOperatorCount, + "BLSRegistryCoordinatorWithIndices.registerOperator: quorum is overfilled" + ); + } } /** @@ -168,7 +175,7 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr * and ids of the operators to swap with the kicked operator. * @param churnApproverSignature is the signature of the churnApprover on the operator kick params */ - function registerOperatorWithCoordinator( + function registerOperatorWithChurn( bytes calldata quorumNumbers, BN254.G1Point memory pubkey, string calldata socket, @@ -176,7 +183,7 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr SignatureWithSaltAndExpiry memory churnApproverSignature ) external onlyWhenNotPaused(PAUSED_REGISTER_OPERATOR) { // register the operator - uint32[] memory numOperatorsPerQuorum = _registerOperatorWithCoordinator({ + uint32[] memory numOperatorsPerQuorum = _registerOperator({ operator: msg.sender, quorumNumbers: quorumNumbers, pubkey: pubkey, @@ -209,7 +216,7 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr require( operatorKickParams[operatorToKickParamsIndex].quorumNumber == quorumNumber, - "BLSRegistryCoordinatorWithIndices.registerOperatorWithCoordinator: quorumNumber not the same as signed" + "BLSRegistryCoordinatorWithIndices.registerOperatorWithChurn: quorumNumber not the same as signed" ); // get the total stake for the quorum @@ -221,13 +228,13 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr // 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" + "BLSRegistryCoordinatorWithIndices.registerOperatorWithChurn: 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" + "BLSRegistryCoordinatorWithIndices.registerOperatorWithChurn: operator to kick has more than kickBIPSOfTotalStake" ); // increment the operatorToKickParamsIndex @@ -235,7 +242,7 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr } // kick the operator - _deregisterOperatorWithCoordinator({ + _deregisterOperator({ operator: operatorKickParams[i].operator, quorumNumbers: quorumNumbers[i:i+1], pubkey: operatorKickParams[i].pubkey @@ -248,11 +255,11 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr * @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 */ - function deregisterOperatorWithCoordinator( + function deregisterOperator( bytes calldata quorumNumbers, BN254.G1Point memory pubkey ) external onlyWhenNotPaused(PAUSED_DEREGISTER_OPERATOR) { - _deregisterOperatorWithCoordinator({ + _deregisterOperator({ operator: msg.sender, quorumNumbers: quorumNumbers, pubkey: pubkey @@ -264,7 +271,7 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr * @param socket is the new socket of the operator */ function updateSocket(string memory socket) external { - require(_operatorInfo[msg.sender].status == OperatorStatus.REGISTERED, "BLSRegistryCoordinatorWithIndicies.updateSocket: operator is not registered"); + require(_operatorInfo[msg.sender].status == OperatorStatus.REGISTERED, "BLSRegistryCoordinatorWithIndices.updateSocket: operator is not registered"); emit OperatorSocketUpdate(_operatorInfo[msg.sender].operatorId, socket); } @@ -278,12 +285,12 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr * @param quorumNumbers are the quorum numbers to eject the operator from * @param pubkey is the BLS public key of the operator */ - function ejectOperatorFromCoordinator( + function ejectOperator( address operator, bytes calldata quorumNumbers, BN254.G1Point memory pubkey ) external onlyEjector { - _deregisterOperatorWithCoordinator({ + _deregisterOperator({ operator: operator, quorumNumbers: quorumNumbers, pubkey: pubkey @@ -341,7 +348,7 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr *******************************************************************************/ /// @return numOperatorsPerQuorum is the list of number of operators per quorum in quorumNumberss - function _registerOperatorWithCoordinator( + function _registerOperator( address operator, bytes calldata quorumNumbers, BN254.G1Point memory pubkey, @@ -349,8 +356,8 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr ) internal virtual returns(uint32[] memory) { // Create and validate bitmap from quorumNumbers uint256 quorumBitmap = BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers); - require(quorumBitmap <= MAX_QUORUM_BITMAP, "BLSRegistryCoordinatorWithIndices._registerOperatorWithCoordinator: bitmap exceeds max bitmap size"); - require(quorumBitmap != 0, "BLSRegistryCoordinatorWithIndices._registerOperatorWithCoordinator: bitmap cannot be 0"); + require(quorumBitmap <= MAX_QUORUM_BITMAP, "BLSRegistryCoordinatorWithIndices._registerOperator: bitmap exceeds max bitmap size"); + require(quorumBitmap != 0, "BLSRegistryCoordinatorWithIndices._registerOperator: bitmap cannot be 0"); /** * Register the operator with the BLSPubkeyRegistry, StakeRegistry, and IndexRegistry. Retrieves: @@ -370,7 +377,7 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr uint256 historyLength = _operatorBitmapHistory[operatorId].length; if (historyLength != 0 && _operatorBitmapHistory[operatorId][historyLength - 1].nextUpdateBlockNumber == 0) { uint256 prevQuorumBitmap = _operatorBitmapHistory[operatorId][historyLength - 1].quorumBitmap; - require(prevQuorumBitmap & quorumBitmap == 0, "BLSRegistryCoordinatorWithIndices._registerOperatorWithCoordinator: operator already registered for some quorums being registered for"); + require(prevQuorumBitmap & quorumBitmap == 0, "BLSRegistryCoordinatorWithIndices._registerOperator: 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; @@ -402,28 +409,7 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr return numOperatorsPerQuorum; } - function _registerOperatorWithCoordinatorAndNoOverfilledQuorums( - address operator, - bytes calldata quorumNumbers, - BN254.G1Point memory pubkey, - string memory socket - ) internal { - uint32[] memory numOperatorsPerQuorum = _registerOperatorWithCoordinator({ - operator: operator, - quorumNumbers: quorumNumbers, - pubkey: pubkey, - socket: socket - }); - - for (uint256 i = 0; i < numOperatorsPerQuorum.length; i++) { - require( - numOperatorsPerQuorum[i] <= _quorumParams[uint8(quorumNumbers[i])].maxOperatorCount, - "BLSRegistryCoordinatorWithIndices._registerOperatorWithCoordinatorAndNoOverfilledQuorums: quorum is overfilled" - ); - } - } - - function _deregisterOperatorWithCoordinator( + function _deregisterOperator( address operator, bytes calldata quorumNumbers, BN254.G1Point memory pubkey @@ -435,13 +421,13 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr */ Operator storage operatorInfo = _operatorInfo[operator]; bytes32 operatorId = operatorInfo.operatorId; - require(operatorInfo.status == OperatorStatus.REGISTERED, "BLSRegistryCoordinatorWithIndices._deregisterOperatorWithCoordinator: operator is not registered"); - require(operatorId == pubkey.hashG1Point(), "BLSRegistryCoordinatorWithIndices._deregisterOperatorWithCoordinator: operatorId does not match pubkey hash"); + require(operatorInfo.status == OperatorStatus.REGISTERED, "BLSRegistryCoordinatorWithIndices._deregisterOperator: operator is not registered"); + require(operatorId == pubkey.hashG1Point(), "BLSRegistryCoordinatorWithIndices._deregisterOperator: operatorId does not match pubkey hash"); // Create and validate bitmap of quorums to remove uint256 quorumsToRemoveBitmap = BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers); - require(quorumsToRemoveBitmap <= MAX_QUORUM_BITMAP, "BLSRegistryCoordinatorWithIndices._deregisterOperatorWithCoordinator: bitmap exceeds max bitmap size"); - require(quorumsToRemoveBitmap != 0, "BLSRegistryCoordinatorWithIndices._deregisterOperatorWithCoordinator: bitmap cannot be 0"); + require(quorumsToRemoveBitmap <= MAX_QUORUM_BITMAP, "BLSRegistryCoordinatorWithIndices._deregisterOperator: bitmap exceeds max bitmap size"); + require(quorumsToRemoveBitmap != 0, "BLSRegistryCoordinatorWithIndices._deregisterOperator: bitmap cannot be 0"); // Get the operator's last quorum bitmap and update its "next" pointer to the current block // TODO - change to use new history update pattern @@ -452,7 +438,7 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr // Remove quorums the operator isn't registered for and check that the result isn't empty quorumsToRemoveBitmap = previousBitmap & quorumsToRemoveBitmap; bytes memory quorumNumbersToRemove = BitmapUtils.bitmapToBytesArray(quorumsToRemoveBitmap); - require(quorumNumbersToRemove.length != 0, "BLSRegistryCoordinatorWithIndices._deregisterOperatorWithCoordinator: operator is not registered for any of the provided quorums"); + require(quorumNumbersToRemove.length != 0, "BLSRegistryCoordinatorWithIndices._deregisterOperator: operator is not registered for any of the provided quorums"); // Check if the operator is completely deregistering bool completeDeregistration = previousBitmap == quorumsToRemoveBitmap; @@ -508,7 +494,7 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr ) internal { // Increment the total quorum count. Fails if we're already at the max uint8 prevQuorumCount = quorumCount; - require(prevQuorumCount < MAX_QUORUM_COUNT, "BLSRegistryCoordinatorWithIndicies.createQuorum: max quorums reached"); + require(prevQuorumCount < MAX_QUORUM_COUNT, "BLSRegistryCoordinatorWithIndices.createQuorum: max quorums reached"); quorumCount = prevQuorumCount + 1; // The previous count is the new quorum's number @@ -575,7 +561,7 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr } /// @notice Returns the indices of the quorumBitmaps for the provided `operatorIds` at the given `blockNumber` - function getQuorumBitmapIndicesByOperatorIdsAtBlockNumber( + function getQuorumBitmapIndicesAtBlockNumber( uint32 blockNumber, bytes32[] memory operatorIds ) external view returns (uint32[] memory) { @@ -588,7 +574,7 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr _operatorBitmapHistory[operatorIds[i]][length - j - 1].nextUpdateBlockNumber; require( nextUpdateBlockNumber == 0 || nextUpdateBlockNumber > blockNumber, - "BLSRegistryCoordinatorWithIndices.getQuorumBitmapIndicesByOperatorIdsAtBlockNumber: operatorId has no quorumBitmaps at blockNumber" + "BLSRegistryCoordinatorWithIndices.getQuorumBitmapIndicesAtBlockNumber: operatorId has no quorumBitmaps at blockNumber" ); indices[i] = uint32(length - j - 1); break; @@ -602,7 +588,7 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr * @notice Returns the quorum bitmap for the given `operatorId` at the given `blockNumber` via the `index` * @dev reverts if `index` is incorrect */ - function getQuorumBitmapByOperatorIdAtBlockNumberByIndex( + function getQuorumBitmapAtBlockNumberByIndex( bytes32 operatorId, uint32 blockNumber, uint256 index @@ -610,19 +596,19 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr QuorumBitmapUpdate memory quorumBitmapUpdate = _operatorBitmapHistory[operatorId][index]; require( quorumBitmapUpdate.updateBlockNumber <= blockNumber, - "BLSRegistryCoordinatorWithIndices.getQuorumBitmapByOperatorIdAtBlockNumberByIndex: quorumBitmapUpdate is from after blockNumber" + "BLSRegistryCoordinatorWithIndices.getQuorumBitmapAtBlockNumberByIndex: 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" + "BLSRegistryCoordinatorWithIndices.getQuorumBitmapAtBlockNumberByIndex: quorumBitmapUpdate is from before blockNumber" ); return quorumBitmapUpdate.quorumBitmap; } /// @notice Returns the `index`th entry in the operator with `operatorId`'s bitmap history - function getQuorumBitmapUpdateByOperatorIdByIndex( + function getQuorumBitmapUpdateByIndex( bytes32 operatorId, uint256 index ) external view returns (QuorumBitmapUpdate memory) { @@ -630,7 +616,7 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr } /// @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) { + function getCurrentQuorumBitmap(bytes32 operatorId) external view returns (uint192) { uint256 quorumBitmapHistoryLength = _operatorBitmapHistory[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 @@ -641,7 +627,7 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr } /// @notice Returns the length of the quorum bitmap history for the given `operatorId` - function getQuorumBitmapUpdateByOperatorIdLength(bytes32 operatorId) external view returns (uint256) { + function getQuorumBitmapHistoryLength(bytes32 operatorId) external view returns (uint256) { return _operatorBitmapHistory[operatorId].length; } diff --git a/src/BLSSignatureChecker.sol b/src/BLSSignatureChecker.sol index 511a7c5c..454e61b4 100644 --- a/src/BLSSignatureChecker.sol +++ b/src/BLSSignatureChecker.sol @@ -107,7 +107,7 @@ contract BLSSignatureChecker is IBLSSignatureChecker { } nonSignerQuorumBitmaps[i] = - registryCoordinator.getQuorumBitmapByOperatorIdAtBlockNumberByIndex( + registryCoordinator.getQuorumBitmapAtBlockNumberByIndex( nonSignerPubkeyHashes[i], referenceBlockNumber, nonSignerStakesAndSignature.nonSignerQuorumBitmapIndices[i] diff --git a/src/StakeRegistry.sol b/src/StakeRegistry.sol index 86ae8c06..bf4a9b68 100644 --- a/src/StakeRegistry.sol +++ b/src/StakeRegistry.sol @@ -95,7 +95,7 @@ contract StakeRegistry is StakeRegistryStorage { for (uint256 i = 0; i < operators.length; ) { bytes32 operatorId = registryCoordinator.getOperatorId(operators[i]); - uint192 quorumBitmap = registryCoordinator.getCurrentQuorumBitmapByOperatorId(operatorId); + uint192 quorumBitmap = registryCoordinator.getCurrentQuorumBitmap(operatorId); /** * If the operator is a part of the quorum, update their current stake diff --git a/src/interfaces/IBLSRegistryCoordinatorWithIndices.sol b/src/interfaces/IBLSRegistryCoordinatorWithIndices.sol index 81d71ff8..b91d91bb 100644 --- a/src/interfaces/IBLSRegistryCoordinatorWithIndices.sol +++ b/src/interfaces/IBLSRegistryCoordinatorWithIndices.sol @@ -60,7 +60,7 @@ interface IBLSRegistryCoordinatorWithIndices is ISignatureUtils, IRegistryCoordi * @param quorumNumbers are the quorum numbers to eject the operator from * @param pubkey is the BLS public key of the operator */ - function ejectOperatorFromCoordinator( + function ejectOperator( address operator, bytes calldata quorumNumbers, BN254.G1Point memory pubkey diff --git a/src/interfaces/IRegistryCoordinator.sol b/src/interfaces/IRegistryCoordinator.sol index c693ba0d..badf5890 100644 --- a/src/interfaces/IRegistryCoordinator.sol +++ b/src/interfaces/IRegistryCoordinator.sol @@ -61,22 +61,22 @@ interface IRegistryCoordinator { function getOperatorStatus(address operator) external view returns (IRegistryCoordinator.OperatorStatus); /// @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); + function getQuorumBitmapIndicesAtBlockNumber(uint32 blockNumber, bytes32[] memory operatorIds) external view returns (uint32[] memory); /** * @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); + function getQuorumBitmapAtBlockNumberByIndex(bytes32 operatorId, uint32 blockNumber, uint256 index) external view returns (uint192); /// @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); + function getQuorumBitmapUpdateByIndex(bytes32 operatorId, uint256 index) external view returns (QuorumBitmapUpdate memory); /// @notice Returns the current quorum bitmap for the given `operatorId` - function getCurrentQuorumBitmapByOperatorId(bytes32 operatorId) external view returns (uint192); + function getCurrentQuorumBitmap(bytes32 operatorId) external view returns (uint192); /// @notice Returns the length of the quorum bitmap history for the given `operatorId` - function getQuorumBitmapUpdateByOperatorIdLength(bytes32 operatorId) external view returns (uint256); + function getQuorumBitmapHistoryLength(bytes32 operatorId) external view returns (uint256); /// @notice Returns the registry at the desired index function registries(uint256) external view returns (address); diff --git a/test/mocks/RegistryCoordinatorMock.sol b/test/mocks/RegistryCoordinatorMock.sol index fee2ee96..9d0e7c4a 100644 --- a/test/mocks/RegistryCoordinatorMock.sol +++ b/test/mocks/RegistryCoordinatorMock.sol @@ -24,25 +24,25 @@ contract RegistryCoordinatorMock is IRegistryCoordinator { /// @notice Returns task number from when `operator` has been registered. function getFromTaskNumberForOperator(address operator) external view returns (uint32){} - function getQuorumBitmapIndicesByOperatorIdsAtBlockNumber(uint32 blockNumber, bytes32[] memory operatorIds) external view returns (uint32[] memory){} + function getQuorumBitmapIndicesAtBlockNumber(uint32 blockNumber, bytes32[] memory operatorIds) external view returns (uint32[] memory){} /// @notice Returns the quorum bitmap for the given `operatorId` at the given `blockNumber` via the `index` - function getQuorumBitmapByOperatorIdAtBlockNumberByIndex(bytes32 operatorId, uint32 blockNumber, uint256 index) external view returns (uint192) {} + function getQuorumBitmapAtBlockNumberByIndex(bytes32 operatorId, uint32 blockNumber, uint256 index) external view returns (uint192) {} /// @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) {} + function getQuorumBitmapUpdateByIndex(bytes32 operatorId, uint256 index) external view returns (QuorumBitmapUpdate memory) {} /// @notice Returns the current quorum bitmap for the given `operatorId` - function getCurrentQuorumBitmapByOperatorId(bytes32 operatorId) external view returns (uint192) {} + function getCurrentQuorumBitmap(bytes32 operatorId) external view returns (uint192) {} /// @notice Returns the length of the quorum bitmap history for the given `operatorId` - function getQuorumBitmapUpdateByOperatorIdLength(bytes32 operatorId) external view returns (uint256) {} + function getQuorumBitmapHistoryLength(bytes32 operatorId) external view returns (uint256) {} function numRegistries() external view returns (uint256){} function registries(uint256) external view returns (address){} - function registerOperatorWithCoordinator(bytes memory quorumNumbers, bytes calldata) external {} + function registerOperator(bytes memory quorumNumbers, bytes calldata) external {} - function deregisterOperatorWithCoordinator(bytes calldata quorumNumbers, bytes calldata) external {} + function deregisterOperator(bytes calldata quorumNumbers, bytes calldata) external {} } diff --git a/test/unit/BLSOperatorStateRetrieverUnit.t.sol b/test/unit/BLSOperatorStateRetrieverUnit.t.sol index 6842bf16..b2cc0cc7 100644 --- a/test/unit/BLSOperatorStateRetrieverUnit.t.sol +++ b/test/unit/BLSOperatorStateRetrieverUnit.t.sol @@ -47,7 +47,7 @@ contract BLSOperatorStateRetrieverUnitTests is MockAVSDeployer { cheats.roll(deregistrationBlockNumber); cheats.prank(_incrementAddress(defaultOperator, operatorIndexToDeregister)); - registryCoordinator.deregisterOperatorWithCoordinator(quorumNumbersToDeregister, operatorMetadatas[operatorIndexToDeregister].pubkey); + registryCoordinator.deregisterOperator(quorumNumbersToDeregister, operatorMetadatas[operatorIndexToDeregister].pubkey); // 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]); diff --git a/test/unit/BLSRegistryCoordinatorWithIndicesUnit.t.sol b/test/unit/BLSRegistryCoordinatorWithIndicesUnit.t.sol index 644c112c..c2f22c63 100644 --- a/test/unit/BLSRegistryCoordinatorWithIndicesUnit.t.sol +++ b/test/unit/BLSRegistryCoordinatorWithIndicesUnit.t.sol @@ -121,21 +121,21 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { cheats.startPrank(defaultOperator); cheats.expectRevert(bytes("Pausable: index is paused")); - registryCoordinator.registerOperatorWithCoordinator(emptyQuorumNumbers, defaultPubKey, defaultSocket); + registryCoordinator.registerOperator(emptyQuorumNumbers, defaultPubKey, defaultSocket); } function testRegisterOperatorWithCoordinator_EmptyQuorumNumbers_Reverts() public { bytes memory emptyQuorumNumbers = new bytes(0); - cheats.expectRevert("BLSRegistryCoordinatorWithIndices._registerOperatorWithCoordinator: bitmap cannot be 0"); + cheats.expectRevert("BLSRegistryCoordinatorWithIndices._registerOperator: bitmap cannot be 0"); cheats.prank(defaultOperator); - registryCoordinator.registerOperatorWithCoordinator(emptyQuorumNumbers, defaultPubKey, defaultSocket); + registryCoordinator.registerOperator(emptyQuorumNumbers, defaultPubKey, defaultSocket); } function testRegisterOperatorWithCoordinator_QuorumNumbersTooLarge_Reverts() public { bytes memory quorumNumbersTooLarge = new bytes(1); quorumNumbersTooLarge[0] = 0xC0; - cheats.expectRevert("BLSRegistryCoordinatorWithIndices._registerOperatorWithCoordinator: bitmap exceeds max bitmap size"); - registryCoordinator.registerOperatorWithCoordinator(quorumNumbersTooLarge, defaultPubKey, defaultSocket); + cheats.expectRevert("BLSRegistryCoordinatorWithIndices._registerOperator: bitmap exceeds max bitmap size"); + registryCoordinator.registerOperator(quorumNumbersTooLarge, defaultPubKey, defaultSocket); } function testRegisterOperatorWithCoordinator_QuorumNotCreated_Reverts() public { @@ -144,7 +144,7 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { quorumNumbersNotCreated[0] = 0x0B; cheats.prank(defaultOperator); cheats.expectRevert("BLSPubkeyRegistry._processQuorumApkUpdate: quorum does not exist"); - registryCoordinator.registerOperatorWithCoordinator(quorumNumbersNotCreated, defaultPubKey, defaultSocket); + registryCoordinator.registerOperator(quorumNumbersNotCreated, defaultPubKey, defaultSocket); } function testRegisterOperatorWithCoordinatorForSingleQuorum_Valid() public { @@ -164,7 +164,7 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { emit OperatorSocketUpdate(defaultOperatorId, defaultSocket); uint256 gasBefore = gasleft(); - registryCoordinator.registerOperatorWithCoordinator(quorumNumbers, defaultPubKey, defaultSocket); + registryCoordinator.registerOperator(quorumNumbers, defaultPubKey, defaultSocket); uint256 gasAfter = gasleft(); emit log_named_uint("gasUsed", gasBefore - gasAfter); @@ -178,9 +178,9 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { status: IRegistryCoordinator.OperatorStatus.REGISTERED }))) ); - assertEq(registryCoordinator.getCurrentQuorumBitmapByOperatorId(defaultOperatorId), quorumBitmap); + assertEq(registryCoordinator.getCurrentQuorumBitmap(defaultOperatorId), quorumBitmap); assertEq( - keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByOperatorIdByIndex(defaultOperatorId, 0))), + keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0))), keccak256(abi.encode(IRegistryCoordinator.QuorumBitmapUpdate({ quorumBitmap: uint192(quorumBitmap), updateBlockNumber: uint32(block.number), @@ -215,7 +215,7 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { emit OperatorSocketUpdate(defaultOperatorId, defaultSocket); uint256 gasBefore = gasleft(); - registryCoordinator.registerOperatorWithCoordinator(quorumNumbers, defaultPubKey, defaultSocket); + registryCoordinator.registerOperator(quorumNumbers, defaultPubKey, defaultSocket); uint256 gasAfter = gasleft(); emit log_named_uint("gasUsed", gasBefore - gasAfter); emit log_named_uint("numQuorums", quorumNumbers.length); @@ -228,9 +228,9 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { status: IRegistryCoordinator.OperatorStatus.REGISTERED }))) ); - assertEq(registryCoordinator.getCurrentQuorumBitmapByOperatorId(defaultOperatorId), quorumBitmap); + assertEq(registryCoordinator.getCurrentQuorumBitmap(defaultOperatorId), quorumBitmap); assertEq( - keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByOperatorIdByIndex(defaultOperatorId, 0))), + keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0))), keccak256(abi.encode(IRegistryCoordinator.QuorumBitmapUpdate({ quorumBitmap: uint192(quorumBitmap), updateBlockNumber: uint32(block.number), @@ -249,7 +249,7 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { stakeRegistry.setOperatorWeight(uint8(quorumNumbers[0]), defaultOperator, defaultStake); cheats.prank(defaultOperator); cheats.roll(registrationBlockNumber); - registryCoordinator.registerOperatorWithCoordinator(quorumNumbers, defaultPubKey, defaultSocket); + registryCoordinator.registerOperator(quorumNumbers, defaultPubKey, defaultSocket); bytes memory newQuorumNumbers = new bytes(1); newQuorumNumbers[0] = bytes1(defaultQuorumNumber+1); @@ -265,7 +265,7 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { cheats.expectEmit(true, true, true, true, address(registryCoordinator)); emit OperatorSocketUpdate(defaultOperatorId, defaultSocket); cheats.roll(nextRegistrationBlockNumber); - registryCoordinator.registerOperatorWithCoordinator(newQuorumNumbers, defaultPubKey, defaultSocket); + registryCoordinator.registerOperator(newQuorumNumbers, defaultPubKey, defaultSocket); uint256 quorumBitmap = BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers) | BitmapUtils.orderedBytesArrayToBitmap(newQuorumNumbers); @@ -277,9 +277,9 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { status: IRegistryCoordinator.OperatorStatus.REGISTERED }))) ); - assertEq(registryCoordinator.getCurrentQuorumBitmapByOperatorId(defaultOperatorId), quorumBitmap); + assertEq(registryCoordinator.getCurrentQuorumBitmap(defaultOperatorId), quorumBitmap); assertEq( - keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByOperatorIdByIndex(defaultOperatorId, 0))), + keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0))), keccak256(abi.encode(IRegistryCoordinator.QuorumBitmapUpdate({ quorumBitmap: uint192(BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers)), updateBlockNumber: uint32(registrationBlockNumber), @@ -287,7 +287,7 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { }))) ); assertEq( - keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByOperatorIdByIndex(defaultOperatorId, 1))), + keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 1))), keccak256(abi.encode(IRegistryCoordinator.QuorumBitmapUpdate({ quorumBitmap: uint192(quorumBitmap), updateBlockNumber: uint32(nextRegistrationBlockNumber), @@ -322,8 +322,8 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { stakeRegistry.setOperatorWeight(defaultQuorumNumber, operatorToRegister, defaultStake); cheats.prank(operatorToRegister); - cheats.expectRevert("BLSRegistryCoordinatorWithIndices._registerOperatorWithCoordinatorAndNoOverfilledQuorums: quorum is overfilled"); - registryCoordinator.registerOperatorWithCoordinator(quorumNumbers, operatorToRegisterPubKey, defaultSocket); + cheats.expectRevert("BLSRegistryCoordinatorWithIndices.registerOperator: quorum is overfilled"); + registryCoordinator.registerOperator(quorumNumbers, operatorToRegisterPubKey, defaultSocket); } function testRegisterOperatorWithCoordinator_RegisteredOperatorForSameQuorums_Reverts() public { @@ -336,12 +336,12 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { stakeRegistry.setOperatorWeight(uint8(quorumNumbers[0]), defaultOperator, defaultStake); cheats.prank(defaultOperator); cheats.roll(registrationBlockNumber); - registryCoordinator.registerOperatorWithCoordinator(quorumNumbers, defaultPubKey, defaultSocket); + registryCoordinator.registerOperator(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); + cheats.expectRevert("BLSRegistryCoordinatorWithIndices._registerOperator: operator already registered for some quorums being registered for"); + registryCoordinator.registerOperator(quorumNumbers, defaultPubKey, defaultSocket); } function testDeregisterOperatorWithCoordinator_WhenPaused_Reverts() public { @@ -357,16 +357,16 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { cheats.expectRevert(bytes("Pausable: index is paused")); cheats.prank(defaultOperator); - registryCoordinator.deregisterOperatorWithCoordinator(quorumNumbers, defaultPubKey); + registryCoordinator.deregisterOperator(quorumNumbers, defaultPubKey); } function testDeregisterOperatorWithCoordinator_NotRegistered_Reverts() public { bytes memory quorumNumbers = new bytes(1); quorumNumbers[0] = bytes1(defaultQuorumNumber); - cheats.expectRevert("BLSRegistryCoordinatorWithIndices._deregisterOperatorWithCoordinator: operator is not registered"); + cheats.expectRevert("BLSRegistryCoordinatorWithIndices._deregisterOperator: operator is not registered"); cheats.prank(defaultOperator); - registryCoordinator.deregisterOperatorWithCoordinator(quorumNumbers, defaultPubKey); + registryCoordinator.deregisterOperator(quorumNumbers, defaultPubKey); } function testDeregisterOperatorWithCoordinator_IncorrectPubkey_Reverts() public { @@ -378,9 +378,9 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { BN254.G1Point memory incorrectPubKey = BN254.hashToG1(bytes32(uint256(123))); - cheats.expectRevert("BLSRegistryCoordinatorWithIndices._deregisterOperatorWithCoordinator: operatorId does not match pubkey hash"); + cheats.expectRevert("BLSRegistryCoordinatorWithIndices._deregisterOperator: operatorId does not match pubkey hash"); cheats.prank(defaultOperator); - registryCoordinator.deregisterOperatorWithCoordinator(quorumNumbers, incorrectPubKey); + registryCoordinator.deregisterOperator(quorumNumbers, incorrectPubKey); } function testDeregisterOperatorWithCoordinator_IncorrectQuorums_Reverts() public { @@ -394,9 +394,9 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { 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.expectRevert("BLSRegistryCoordinatorWithIndices._deregisterOperator: operator is not registered for any of the provided quorums"); cheats.prank(defaultOperator); - registryCoordinator.deregisterOperatorWithCoordinator(quorumNumbers, defaultPubKey); + registryCoordinator.deregisterOperator(quorumNumbers, defaultPubKey); } function testDeregisterOperatorWithCoordinatorForSingleQuorumAndSingleOperator_Valid() public { @@ -412,7 +412,7 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { cheats.roll(registrationBlockNumber); - registryCoordinator.registerOperatorWithCoordinator(quorumNumbers, defaultPubKey, defaultSocket); + registryCoordinator.registerOperator(quorumNumbers, defaultPubKey, defaultSocket); uint256 quorumBitmap = BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers); @@ -424,7 +424,7 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { cheats.roll(deregistrationBlockNumber); uint256 gasBefore = gasleft(); - registryCoordinator.deregisterOperatorWithCoordinator(quorumNumbers, defaultPubKey); + registryCoordinator.deregisterOperator(quorumNumbers, defaultPubKey); uint256 gasAfter = gasleft(); emit log_named_uint("gasUsed", gasBefore - gasAfter); @@ -435,9 +435,9 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { status: IRegistryCoordinator.OperatorStatus.DEREGISTERED }))) ); - assertEq(registryCoordinator.getCurrentQuorumBitmapByOperatorId(defaultOperatorId), 0); + assertEq(registryCoordinator.getCurrentQuorumBitmap(defaultOperatorId), 0); assertEq( - keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByOperatorIdByIndex(defaultOperatorId, 0))), + keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0))), keccak256(abi.encode(IRegistryCoordinator.QuorumBitmapUpdate({ quorumBitmap: uint192(quorumBitmap), updateBlockNumber: registrationBlockNumber, @@ -462,7 +462,7 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { cheats.roll(registrationBlockNumber); - registryCoordinator.registerOperatorWithCoordinator(quorumNumbers, defaultPubKey, defaultSocket); + registryCoordinator.registerOperator(quorumNumbers, defaultPubKey, defaultSocket); cheats.expectEmit(true, true, true, true, address(blsPubkeyRegistry)); emit OperatorRemovedFromQuorums(defaultOperator, quorumNumbers); @@ -474,7 +474,7 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { cheats.roll(deregistrationBlockNumber); uint256 gasBefore = gasleft(); - registryCoordinator.deregisterOperatorWithCoordinator(quorumNumbers, defaultPubKey); + registryCoordinator.deregisterOperator(quorumNumbers, defaultPubKey); uint256 gasAfter = gasleft(); emit log_named_uint("gasUsed", gasBefore - gasAfter); emit log_named_uint("numQuorums", quorumNumbers.length); @@ -486,9 +486,9 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { status: IRegistryCoordinator.OperatorStatus.DEREGISTERED }))) ); - assertEq(registryCoordinator.getCurrentQuorumBitmapByOperatorId(defaultOperatorId), 0); + assertEq(registryCoordinator.getCurrentQuorumBitmap(defaultOperatorId), 0); assertEq( - keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByOperatorIdByIndex(defaultOperatorId, 0))), + keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0))), keccak256(abi.encode(IRegistryCoordinator.QuorumBitmapUpdate({ quorumBitmap: uint192(quorumBitmap), updateBlockNumber: registrationBlockNumber, @@ -551,7 +551,7 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { cheats.roll(deregistrationBlockNumber); cheats.prank(operatorToDerigister); - registryCoordinator.deregisterOperatorWithCoordinator(operatorToDeregisterQuorumNumbers, operatorToDeregisterPubKey); + registryCoordinator.deregisterOperator(operatorToDeregisterQuorumNumbers, operatorToDeregisterPubKey); assertEq( keccak256(abi.encode(registryCoordinator.getOperator(operatorToDerigister))), @@ -560,9 +560,9 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { status: IRegistryCoordinator.OperatorStatus.DEREGISTERED }))) ); - assertEq(registryCoordinator.getCurrentQuorumBitmapByOperatorId(defaultOperatorId), 0); + assertEq(registryCoordinator.getCurrentQuorumBitmap(defaultOperatorId), 0); assertEq( - keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByOperatorIdByIndex(operatorToDerigisterId, 0))), + keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(operatorToDerigisterId, 0))), keccak256(abi.encode(IRegistryCoordinator.QuorumBitmapUpdate({ quorumBitmap: uint192(operatorToDeregisterQuorumBitmap), updateBlockNumber: registrationBlockNumber, @@ -586,10 +586,10 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { // store data before registering, to check against later IRegistryCoordinator.QuorumBitmapUpdate memory previousQuorumBitmapUpdate = - registryCoordinator.getQuorumBitmapUpdateByOperatorIdByIndex(defaultOperatorId, 0); + registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0); // re-register the operator - registryCoordinator.registerOperatorWithCoordinator(quorumNumbers, defaultPubKey, defaultSocket); + registryCoordinator.registerOperator(quorumNumbers, defaultPubKey, defaultSocket); // check success of registration uint256 quorumBitmap = BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers); @@ -601,15 +601,15 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { status: IRegistryCoordinator.OperatorStatus.REGISTERED }))) ); - assertEq(registryCoordinator.getCurrentQuorumBitmapByOperatorId(defaultOperatorId), quorumBitmap); + assertEq(registryCoordinator.getCurrentQuorumBitmap(defaultOperatorId), quorumBitmap); // check that previous entry in bitmap history was not changed assertEq( - keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByOperatorIdByIndex(defaultOperatorId, 0))), + keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0))), keccak256(abi.encode(previousQuorumBitmapUpdate)) ); // check that new entry in bitmap history is as expected assertEq( - keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByOperatorIdByIndex(defaultOperatorId, 1))), + keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 1))), keccak256(abi.encode(IRegistryCoordinator.QuorumBitmapUpdate({ quorumBitmap: uint192(quorumBitmap), updateBlockNumber: uint32(reregistrationBlockNumber), @@ -687,7 +687,7 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { ISignatureUtils.SignatureWithSaltAndExpiry memory signatureWithExpiry = _signOperatorChurnApproval(operatorToRegisterId, operatorKickParams, defaultSalt, block.timestamp + 10); cheats.prank(operatorToRegister); uint256 gasBefore = gasleft(); - registryCoordinator.registerOperatorWithCoordinator( + registryCoordinator.registerOperatorWithChurn( quorumNumbers, operatorToRegisterPubKey, defaultSocket, @@ -713,7 +713,7 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { }))) ); assertEq( - keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByOperatorIdByIndex(operatorToKickId, 0))), + keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(operatorToKickId, 0))), keccak256(abi.encode(IRegistryCoordinator.QuorumBitmapUpdate({ quorumBitmap: uint192(quorumBitmap), updateBlockNumber: kickRegistrationBlockNumber, @@ -738,8 +738,8 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { 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); + cheats.expectRevert("BLSRegistryCoordinatorWithIndices.registerOperatorWithChurn: registering operator has less than kickBIPsOfOperatorStake"); + registryCoordinator.registerOperatorWithChurn(quorumNumbers, operatorToRegisterPubKey, defaultSocket, operatorKickParams, signatureWithExpiry); } function testRegisterOperatorWithCoordinatorWithKicks_LessThanKickBIPsOfTotalStake_Reverts(uint256 pseudoRandomNumber) public { @@ -761,8 +761,8 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { 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); + cheats.expectRevert("BLSRegistryCoordinatorWithIndices.registerOperatorWithChurn: operator to kick has more than kickBIPSOfTotalStake"); + registryCoordinator.registerOperatorWithChurn(quorumNumbers, operatorToRegisterPubKey, defaultSocket, operatorKickParams, signatureWithExpiry); } function testRegisterOperatorWithCoordinatorWithKicks_InvalidSignatures_Reverts(uint256 pseudoRandomNumber) public { @@ -785,7 +785,7 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { signatureWithSaltAndExpiry.salt = defaultSalt; cheats.prank(operatorToRegister); cheats.expectRevert("ECDSA: invalid signature"); - registryCoordinator.registerOperatorWithCoordinator(quorumNumbers, operatorToRegisterPubKey, defaultSocket, operatorKickParams, signatureWithSaltAndExpiry); + registryCoordinator.registerOperatorWithChurn(quorumNumbers, operatorToRegisterPubKey, defaultSocket, operatorKickParams, signatureWithSaltAndExpiry); } function testRegisterOperatorWithCoordinatorWithKicks_ExpiredSignatures_Reverts(uint256 pseudoRandomNumber) public { @@ -806,7 +806,7 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { ISignatureUtils.SignatureWithSaltAndExpiry memory signatureWithSaltAndExpiry = _signOperatorChurnApproval(operatorToRegisterId, operatorKickParams, defaultSalt, block.timestamp - 1); cheats.prank(operatorToRegister); cheats.expectRevert("BLSRegistryCoordinatorWithIndices._verifyChurnApproverSignature: churnApprover signature expired"); - registryCoordinator.registerOperatorWithCoordinator(quorumNumbers, operatorToRegisterPubKey, defaultSocket, operatorKickParams, signatureWithSaltAndExpiry); + registryCoordinator.registerOperatorWithChurn(quorumNumbers, operatorToRegisterPubKey, defaultSocket, operatorKickParams, signatureWithSaltAndExpiry); } function testEjectOperatorFromCoordinator_AllQuorums_Valid() public { @@ -817,7 +817,7 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { stakeRegistry.setOperatorWeight(uint8(quorumNumbers[0]), defaultOperator, defaultStake); cheats.prank(defaultOperator); - registryCoordinator.registerOperatorWithCoordinator(quorumNumbers, defaultPubKey, defaultSocket); + registryCoordinator.registerOperator(quorumNumbers, defaultPubKey, defaultSocket); cheats.expectEmit(true, true, true, true, address(blsPubkeyRegistry)); emit OperatorRemovedFromQuorums(defaultOperator, quorumNumbers); @@ -827,7 +827,7 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { // eject cheats.prank(ejector); - registryCoordinator.ejectOperatorFromCoordinator(defaultOperator, quorumNumbers, defaultPubKey); + registryCoordinator.ejectOperator(defaultOperator, quorumNumbers, defaultPubKey); // make sure the operator is deregistered assertEq( @@ -838,7 +838,7 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { }))) ); // make sure the operator is not in any quorums - assertEq(registryCoordinator.getCurrentQuorumBitmapByOperatorId(defaultOperatorId), 0); + assertEq(registryCoordinator.getCurrentQuorumBitmap(defaultOperatorId), 0); } function testEjectOperatorFromCoordinator_SubsetOfQuorums_Valid() public { @@ -852,7 +852,7 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { } cheats.prank(defaultOperator); - registryCoordinator.registerOperatorWithCoordinator(quorumNumbers, defaultPubKey, defaultSocket); + registryCoordinator.registerOperator(quorumNumbers, defaultPubKey, defaultSocket); // eject from only first quorum bytes memory quorumNumbersToEject = new bytes(1); @@ -865,7 +865,7 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { emit StakeUpdate(defaultOperatorId, uint8(quorumNumbersToEject[0]), 0); cheats.prank(ejector); - registryCoordinator.ejectOperatorFromCoordinator(defaultOperator, quorumNumbersToEject, defaultPubKey); + registryCoordinator.ejectOperator(defaultOperator, quorumNumbersToEject, defaultPubKey); // make sure the operator is registered assertEq( @@ -877,7 +877,7 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { ); // make sure the operator is not in any quorums assertEq( - registryCoordinator.getCurrentQuorumBitmapByOperatorId(defaultOperatorId), + registryCoordinator.getCurrentQuorumBitmap(defaultOperatorId), BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers) & ~BitmapUtils.orderedBytesArrayToBitmap(quorumNumbersToEject) // quorumsRegisteredFor & ~quorumsEjectedFrom ); } @@ -889,11 +889,11 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { stakeRegistry.setOperatorWeight(uint8(quorumNumbers[0]), defaultOperator, defaultStake); cheats.prank(defaultOperator); - registryCoordinator.registerOperatorWithCoordinator(quorumNumbers, defaultPubKey, defaultSocket); + registryCoordinator.registerOperator(quorumNumbers, defaultPubKey, defaultSocket); cheats.expectRevert("BLSRegistryCoordinatorWithIndices.onlyEjector: caller is not the ejector"); cheats.prank(defaultOperator); - registryCoordinator.ejectOperatorFromCoordinator(defaultOperator, quorumNumbers, defaultPubKey); + registryCoordinator.ejectOperator(defaultOperator, quorumNumbers, defaultPubKey); } function testUpdateSocket() public { @@ -912,7 +912,7 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { function testUpdateSocket_NotRegistered_Reverts() public { cheats.prank(defaultOperator); - cheats.expectRevert("BLSRegistryCoordinatorWithIndicies.updateSocket: operator is not registered"); + cheats.expectRevert("BLSRegistryCoordinatorWithIndices.updateSocket: operator is not registered"); registryCoordinator.updateSocket("localhost:32004"); } diff --git a/test/unit/BLSSignatureCheckerUnit.t.sol b/test/unit/BLSSignatureCheckerUnit.t.sol index 0b9150cf..f6177979 100644 --- a/test/unit/BLSSignatureCheckerUnit.t.sol +++ b/test/unit/BLSSignatureCheckerUnit.t.sol @@ -88,7 +88,7 @@ contract BLSSignatureCheckerUnitTests is BLSMockAVSDeployer { // set the nonSignerQuorumBitmapIndices to a different value nonSignerStakesAndSignature.nonSignerQuorumBitmapIndices[0] = 1; - cheats.expectRevert("BLSRegistryCoordinatorWithIndices.getQuorumBitmapByOperatorIdAtBlockNumberByIndex: quorumBitmapUpdate is from after blockNumber"); + cheats.expectRevert("BLSRegistryCoordinatorWithIndices.getQuorumBitmapAtBlockNumberByIndex: quorumBitmapUpdate is from after blockNumber"); blsSignatureChecker.checkSignatures( msgHash, quorumNumbers, diff --git a/test/utils/MockAVSDeployer.sol b/test/utils/MockAVSDeployer.sol index b3f78152..265cbe6c 100644 --- a/test/utils/MockAVSDeployer.sol +++ b/test/utils/MockAVSDeployer.sol @@ -322,7 +322,7 @@ contract MockAVSDeployer is Test { } cheats.prank(operator); - registryCoordinator.registerOperatorWithCoordinator(quorumNumbers, pubKey, defaultSocket); + registryCoordinator.registerOperator(quorumNumbers, pubKey, defaultSocket); } /** @@ -340,7 +340,7 @@ contract MockAVSDeployer is Test { } cheats.prank(operator); - registryCoordinator.registerOperatorWithCoordinator(quorumNumbers, pubKey, defaultSocket); + registryCoordinator.registerOperator(quorumNumbers, pubKey, defaultSocket); } function _registerRandomOperators(uint256 pseudoRandomNumber) internal returns(OperatorMetadata[] memory, uint256[][] memory) { From be8f4447afb882de9b1af110173cc4caf56c51d1 Mon Sep 17 00:00:00 2001 From: wadealexc Date: Thu, 2 Nov 2023 15:31:18 +0000 Subject: [PATCH 031/101] style: shorten state variable and function naming in stake registry --- src/BLSOperatorStateRetriever.sol | 6 +- src/BLSRegistryCoordinatorWithIndices.sol | 6 +- src/BLSSignatureChecker.sol | 2 +- src/StakeRegistry.sol | 256 ++++++++++-------- src/StakeRegistryStorage.sol | 4 +- src/interfaces/IStakeRegistry.sol | 40 +-- test/mocks/StakeRegistryMock.sol | 26 +- ...LSRegistryCoordinatorWithIndicesUnit.t.sol | 2 +- test/unit/StakeRegistryUnit.t.sol | 46 ++-- test/unit/VoteWeigherBaseUnit.t.sol | 74 ++--- test/utils/MockAVSDeployer.sol | 8 +- 11 files changed, 248 insertions(+), 222 deletions(-) diff --git a/src/BLSOperatorStateRetriever.sol b/src/BLSOperatorStateRetriever.sol index 5b88272e..b1c9f194 100644 --- a/src/BLSOperatorStateRetriever.sol +++ b/src/BLSOperatorStateRetriever.sol @@ -77,7 +77,7 @@ contract BLSOperatorStateRetriever { bytes32 operatorId = bytes32(operatorIds[j]); operators[i][j] = Operator({ operatorId: operatorId, - stake: stakeRegistry.getStakeForOperatorIdForQuorumAtBlockNumber(operatorId, quorumNumber, blockNumber) + stake: stakeRegistry.getOperatorStakeAtBlockNumber(operatorId, quorumNumber, blockNumber) }); } } @@ -111,7 +111,7 @@ contract BLSOperatorStateRetriever { // get the indices of the quorumBitmap updates for each of the operators in the nonSignerOperatorIds array checkSignaturesIndices.nonSignerQuorumBitmapIndices = registryCoordinator.getQuorumBitmapIndicesAtBlockNumber(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.totalStakeIndices = stakeRegistry.getTotalStakeIndicesAtBlockNumber(referenceBlockNumber, quorumNumbers); checkSignaturesIndices.nonSignerStakeIndices = new uint32[][](quorumNumbers.length); for (uint8 quorumNumberIndex = 0; quorumNumberIndex < quorumNumbers.length; quorumNumberIndex++) { @@ -131,7 +131,7 @@ contract BLSOperatorStateRetriever { // 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( + checkSignaturesIndices.nonSignerStakeIndices[quorumNumberIndex][numNonSignersForQuorum] = stakeRegistry.getStakeUpdateIndexForOperatorAtBlockNumber( nonSignerOperatorIds[i], uint8(quorumNumbers[quorumNumberIndex]), referenceBlockNumber diff --git a/src/BLSRegistryCoordinatorWithIndices.sol b/src/BLSRegistryCoordinatorWithIndices.sol index 7118ea13..1497eea9 100644 --- a/src/BLSRegistryCoordinatorWithIndices.sol +++ b/src/BLSRegistryCoordinatorWithIndices.sol @@ -112,7 +112,7 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr uint256 _initialPausedStatus, OperatorSetParam[] memory _operatorSetParams, uint96[] memory _minimumStakes, - IStakeRegistry.StrategyAndWeightingMultiplier[][] memory _strategyParams + IStakeRegistry.StrategyParams[][] memory _strategyParams ) external initializer { require( _operatorSetParams.length == _minimumStakes.length && _minimumStakes.length == _strategyParams.length, @@ -307,7 +307,7 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr function createQuorum( OperatorSetParam memory operatorSetParams, uint96 minimumStake, - IStakeRegistry.StrategyAndWeightingMultiplier[] memory strategyParams + IStakeRegistry.StrategyParams[] memory strategyParams ) external virtual onlyServiceManagerOwner { _createQuorum(operatorSetParams, minimumStake, strategyParams); } @@ -490,7 +490,7 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr function _createQuorum( OperatorSetParam memory operatorSetParams, uint96 minimumStake, - IStakeRegistry.StrategyAndWeightingMultiplier[] memory strategyParams + IStakeRegistry.StrategyParams[] memory strategyParams ) internal { // Increment the total quorum count. Fails if we're already at the max uint8 prevQuorumCount = quorumCount; diff --git a/src/BLSSignatureChecker.sol b/src/BLSSignatureChecker.sol index 454e61b4..f57b0f47 100644 --- a/src/BLSSignatureChecker.sol +++ b/src/BLSSignatureChecker.sol @@ -142,7 +142,7 @@ contract BLSSignatureChecker is IBLSSignatureChecker { // 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( + stakeRegistry.getOperatorStakeAtBlockNumberAndIndex( quorumNumber, referenceBlockNumber, nonSignerPubkeyHashes[i], diff --git a/src/StakeRegistry.sol b/src/StakeRegistry.sol index bf4a9b68..397bf6a1 100644 --- a/src/StakeRegistry.sol +++ b/src/StakeRegistry.sol @@ -7,10 +7,6 @@ import "src/interfaces/IServiceManager.sol"; import "src/interfaces/IStakeRegistry.sol"; import "src/interfaces/IRegistryCoordinator.sol"; import "src/StakeRegistryStorage.sol"; -<<<<<<< HEAD -import {VoteWeigherBase} from "src/VoteWeigherBase.sol"; -======= ->>>>>>> 12b09de (fix: fix compilation issues and tests) /** * @title A `Registry` that keeps track of stakes of operators for up to 256 quorums. @@ -21,13 +17,8 @@ import {VoteWeigherBase} from "src/VoteWeigherBase.sol"; * It allows an additional functionality (in addition to registering and deregistering) to update the stake of an operator. * @author Layr Labs, Inc. */ -<<<<<<< HEAD -contract StakeRegistry is VoteWeigherBase, StakeRegistryStorage { - /// @notice requires that the caller is the RegistryCoordinator -======= contract StakeRegistry is StakeRegistryStorage { ->>>>>>> 12b09de (fix: fix compilation issues and tests) modifier onlyRegistryCoordinator() { require( msg.sender == address(registryCoordinator), @@ -36,46 +27,21 @@ contract StakeRegistry is StakeRegistryStorage { _; } + modifier onlyServiceManagerOwner() { + require(msg.sender == serviceManager.owner(), "StakeRegistry.onlyServiceManagerOwner: caller is not the owner of the serviceManager"); + _; + } + + modifier quorumExists(uint8 quorumNumber) { + require(_totalStakeHistory[quorumNumber].length != 0, "StakeRegistry.quorumExists: quorum does not exist"); + _; + } + constructor( IRegistryCoordinator _registryCoordinator, - IStrategyManager _strategyManager, + IDelegationManager _delegationManager, IServiceManager _serviceManager -<<<<<<< HEAD - ) VoteWeigherBase(_strategyManager, _serviceManager) StakeRegistryStorage(_registryCoordinator) {} - - /** - * @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; - } - } - } -======= ) StakeRegistryStorage(_registryCoordinator, _delegationManager, _serviceManager) {} ->>>>>>> 12b09de (fix: fix compilation issues and tests) /******************************************************************************* EXTERNAL FUNCTIONS @@ -90,9 +56,14 @@ contract StakeRegistry is StakeRegistryStorage { // 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 + uint8 quorumCount = registryCoordinator.quorumCount(); for (uint8 quorumNumber = 0; quorumNumber < quorumCount; ) { int256 totalStakeDelta; + // TODO - not a huge fan of this dependency on the reg coord, but i do prefer this + // over the stakereg also keeping its own count. + require(_totalStakeHistory[quorumNumber].length != 0, "StakeRegistry.updateStakes: quorum does not exist"); + for (uint256 i = 0; i < operators.length; ) { bytes32 operatorId = registryCoordinator.getOperatorId(operators[i]); uint192 quorumBitmap = registryCoordinator.getCurrentQuorumBitmap(operatorId); @@ -145,20 +116,18 @@ contract StakeRegistry is StakeRegistryStorage { bytes32 operatorId, bytes calldata quorumNumbers ) public virtual onlyRegistryCoordinator { - // 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" - ); for (uint256 i = 0; i < quorumNumbers.length; ) { + + uint8 quorumNumber = uint8(quorumNumbers[i]); + require(_totalStakeHistory[quorumNumber].length != 0, "StakeRegistry.registerOperator: quorum does not exist"); + /** * Update the operator's stake for the quorum and retrieve their current stake * as well as the change in stake. * - If this method returns `hasMinimumStake == false`, the operator has not met * the minimum stake requirement for this quorum */ - uint8 quorumNumber = uint8(quorumNumbers[i]); (int256 stakeDelta, bool hasMinimumStake) = _updateOperatorStake({ operator: operator, operatorId: operatorId, @@ -166,7 +135,7 @@ contract StakeRegistry is StakeRegistryStorage { }); require( hasMinimumStake, - "StakeRegistry._registerOperator: Operator does not meet minimum stake requirement for quorum" + "StakeRegistry.registerOperator: Operator does not meet minimum stake requirement for quorum" ); // Update this quorum's total stake @@ -198,8 +167,10 @@ contract StakeRegistry is StakeRegistryStorage { * the quorum's total stake to account for the removal */ for (uint256 i = 0; i < quorumNumbers.length; ) { - // Update the operator's stake for the quorum and retrieve the shares removed uint8 quorumNumber = uint8(quorumNumbers[i]); + require(_totalStakeHistory[quorumNumber].length != 0, "StakeRegistry.deregisterOperator: quorum does not exist"); + + // Update the operator's stake for the quorum and retrieve the shares removed int256 stakeDelta = _recordOperatorStakeUpdate({ operatorId: operatorId, quorumNumber: quorumNumber, @@ -215,23 +186,14 @@ contract StakeRegistry is StakeRegistryStorage { } } -<<<<<<< HEAD - /******************************************************************************* - EXTERNAL FUNCTIONS - SERVICE MANAGER OWNER - *******************************************************************************/ - - /// @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 { -======= /// @notice Initialize a new quorum and push its first history update function initializeQuorum( uint8 quorumNumber, uint96 minimumStake, - StrategyAndWeightingMultiplier[] memory strategyParams + StrategyParams[] memory strategyParams ) public virtual onlyRegistryCoordinator { require(_totalStakeHistory[quorumNumber].length == 0, "StakeRegistry.initializeQuorum: quorum already exists"); _addStrategyParams(quorumNumber, strategyParams); ->>>>>>> 12b09de (fix: fix compilation issues and tests) _setMinimumStakeForQuorum(quorumNumber, minimumStake); _totalStakeHistory[quorumNumber].push(OperatorStakeUpdate({ @@ -241,8 +203,6 @@ contract StakeRegistry is StakeRegistryStorage { })); } -<<<<<<< HEAD -======= function setMinimumStakeForQuorum( uint8 quorumNumber, uint96 minimumStake @@ -258,7 +218,7 @@ contract StakeRegistry is StakeRegistryStorage { */ function addStrategies( uint8 quorumNumber, - StrategyAndWeightingMultiplier[] memory strategyParams + StrategyParams[] memory strategyParams ) public virtual onlyServiceManagerOwner quorumExists(quorumNumber) { _addStrategyParams(quorumNumber, strategyParams); } @@ -273,9 +233,9 @@ contract StakeRegistry is StakeRegistryStorage { uint256[] memory indicesToRemove ) public virtual onlyServiceManagerOwner quorumExists(quorumNumber) { uint256 toRemoveLength = indicesToRemove.length; - require(toRemoveLength > 0, "StakeRegistry.removeStrategyParams: no indices to remove provided"); + require(toRemoveLength > 0, "StakeRegistry.removeStrategies: no indices to remove provided"); - StrategyAndWeightingMultiplier[] storage strategyParams = strategiesConsideredAndMultipliers[quorumNumber]; + StrategyParams[] storage strategyParams = strategyParams[quorumNumber]; for (uint256 i = 0; i < toRemoveLength; i++) { emit StrategyRemovedFromQuorum(quorumNumber, strategyParams[indicesToRemove[i]].strategy); @@ -302,7 +262,7 @@ contract StakeRegistry is StakeRegistryStorage { require(numStrats > 0, "StakeRegistry.modifyStrategyParams: no strategy indices provided"); require(newMultipliers.length == numStrats, "StakeRegistry.modifyStrategyParams: input length mismatch"); - StrategyAndWeightingMultiplier[] storage strategyParams = strategiesConsideredAndMultipliers[quorumNumber]; + StrategyParams[] storage strategyParams = strategyParams[quorumNumber]; for (uint256 i = 0; i < numStrats; i++) { // Change the strategy's associated multiplier @@ -311,30 +271,29 @@ contract StakeRegistry is StakeRegistryStorage { } } ->>>>>>> 12b09de (fix: fix compilation issues and tests) /******************************************************************************* INTERNAL FUNCTIONS *******************************************************************************/ - function _getStakeUpdateIndexForOperatorIdForQuorumAtBlockNumber( + function _getStakeUpdateIndexForOperatorAtBlockNumber( bytes32 operatorId, uint8 quorumNumber, uint32 blockNumber ) internal view returns (uint32) { - uint256 length = operatorIdToStakeHistory[operatorId][quorumNumber].length; + uint256 length = operatorStakeHistory[operatorId][quorumNumber].length; for (uint256 i = 0; i < length; i++) { - if (operatorIdToStakeHistory[operatorId][quorumNumber][length - i - 1].updateBlockNumber <= blockNumber) { + if (operatorStakeHistory[operatorId][quorumNumber][length - i - 1].updateBlockNumber <= blockNumber) { uint32 nextUpdateBlockNumber = - operatorIdToStakeHistory[operatorId][quorumNumber][length - i - 1].nextUpdateBlockNumber; + operatorStakeHistory[operatorId][quorumNumber][length - i - 1].nextUpdateBlockNumber; require( nextUpdateBlockNumber == 0 || nextUpdateBlockNumber > blockNumber, - "StakeRegistry._getStakeUpdateIndexForOperatorIdForQuorumAtBlockNumber: operatorId has no stake update at blockNumber" + "StakeRegistry._getStakeUpdateIndexForOperatorAtBlockNumber: operatorId has no stake update at blockNumber" ); return uint32(length - i - 1); } } revert( - "StakeRegistry._getStakeUpdateIndexForOperatorIdForQuorumAtBlockNumber: no stake update found for operatorId and quorumNumber at block number" + "StakeRegistry._getStakeUpdateIndexForOperatorAtBlockNumber: no stake update found for operatorId and quorumNumber at block number" ); } @@ -358,7 +317,7 @@ contract StakeRegistry is StakeRegistryStorage { * Get the operator's current stake for the quorum. If their stake * is below the quorum's threshold, set their stake to 0 */ - uint96 currentStake = weightOfOperatorForQuorum(quorumNumber, operator); + uint96 currentStake = _weightOfOperatorForQuorum(quorumNumber, operator); if (currentStake < minimumStakeForQuorum[quorumNumber]) { currentStake = uint96(0); } else { @@ -386,28 +345,28 @@ contract StakeRegistry is StakeRegistryStorage { ) internal returns (int256) { uint96 prevStake; - uint256 historyLength = operatorIdToStakeHistory[operatorId][quorumNumber].length; + uint256 historyLength = operatorStakeHistory[operatorId][quorumNumber].length; if (historyLength == 0) { // No prior stake history - push our first entry - operatorIdToStakeHistory[operatorId][quorumNumber].push(OperatorStakeUpdate({ + operatorStakeHistory[operatorId][quorumNumber].push(OperatorStakeUpdate({ updateBlockNumber: uint32(block.number), nextUpdateBlockNumber: 0, stake: newStake })); } else { // We have prior stake history - fetch our last-recorded stake - prevStake = operatorIdToStakeHistory[operatorId][quorumNumber][historyLength-1].stake; + prevStake = operatorStakeHistory[operatorId][quorumNumber][historyLength-1].stake; /** * If our last stake entry was made in the current block, update the entry * Otherwise, push a new entry and update the previous entry's "next" field */ - if (operatorIdToStakeHistory[operatorId][quorumNumber][historyLength-1].updateBlockNumber == uint32(block.number)) { - operatorIdToStakeHistory[operatorId][quorumNumber][historyLength-1].stake = newStake; + if (operatorStakeHistory[operatorId][quorumNumber][historyLength-1].updateBlockNumber == uint32(block.number)) { + operatorStakeHistory[operatorId][quorumNumber][historyLength-1].stake = newStake; } else { - operatorIdToStakeHistory[operatorId][quorumNumber][historyLength-1].nextUpdateBlockNumber = uint32(block.number); - operatorIdToStakeHistory[operatorId][quorumNumber].push(OperatorStakeUpdate({ + operatorStakeHistory[operatorId][quorumNumber][historyLength-1].nextUpdateBlockNumber = uint32(block.number); + operatorStakeHistory[operatorId][quorumNumber].push(OperatorStakeUpdate({ updateBlockNumber: uint32(block.number), nextUpdateBlockNumber: 0, stake: newStake @@ -450,6 +409,51 @@ contract StakeRegistry is StakeRegistryStorage { } } + /** + * @notice Adds `strategyParams` 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 _addStrategyParams( + uint8 quorumNumber, + StrategyParams[] memory _strategyParams + ) internal { + require(_strategyParams.length > 0, "StakeRegistry._addStrategyParams: no strategies provided"); + uint256 numStratsToAdd = _strategyParams.length; + uint256 numStratsExisting = strategyParams[quorumNumber].length; + require( + numStratsExisting + numStratsToAdd <= MAX_WEIGHING_FUNCTION_LENGTH, + "StakeRegistry._addStrategyParams: 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( + strategyParams[quorumNumber][j].strategy != _strategyParams[i].strategy, + "StakeRegistry._addStrategyParams: cannot add same strategy 2x" + ); + unchecked { + ++j; + } + } + require( + _strategyParams[i].multiplier > 0, + "StakeRegistry._addStrategyParams: cannot add strategy with zero weight" + ); + strategyParams[quorumNumber].push(_strategyParams[i]); + emit StrategyAddedToQuorum(quorumNumber, _strategyParams[i].strategy); + emit StrategyMultiplierUpdated( + quorumNumber, + _strategyParams[i].strategy, + _strategyParams[i].multiplier + ); + unchecked { + ++i; + } + } + } + /// @notice Returns the change between a previous and current value as a signed int function _calculateDelta(uint96 prev, uint96 cur) internal pure returns (int256) { return int256(uint256(cur)) - int256(uint256(prev)); @@ -479,20 +483,18 @@ contract StakeRegistry is StakeRegistryStorage { ); } -<<<<<<< HEAD -======= /** * @notice This function computes the total weight of the @param operator in the quorum @param quorumNumber. * @dev this method DOES NOT check that the quorum exists */ function _weightOfOperatorForQuorum(uint8 quorumNumber, address operator) internal virtual view returns (uint96) { uint96 weight; - uint256 stratsLength = strategiesConsideredAndMultipliersLength(quorumNumber); - StrategyAndWeightingMultiplier memory strategyAndMultiplier; + uint256 stratsLength = strategyParamsLength(quorumNumber); + StrategyParams memory strategyAndMultiplier; for (uint256 i = 0; i < stratsLength;) { - // accessing i^th StrategyAndWeightingMultiplier struct for the quorumNumber - strategyAndMultiplier = strategiesConsideredAndMultipliers[quorumNumber][i]; + // accessing i^th StrategyParams struct for the quorumNumber + strategyAndMultiplier = strategyParams[quorumNumber][i]; // shares of the operator in the strategy uint256 sharesAmount = delegation.operatorShares(operator, strategyAndMultiplier.strategy); @@ -510,36 +512,60 @@ contract StakeRegistry is StakeRegistryStorage { return weight; } ->>>>>>> 12b09de (fix: fix compilation issues and tests) /******************************************************************************* VIEW FUNCTIONS *******************************************************************************/ /** - * @notice Returns the entire `operatorIdToStakeHistory[operatorId][quorumNumber]` array. + * @notice This function computes the total weight of the @param operator in the quorum @param quorumNumber. + * @dev reverts if the quorum does not exist + */ + function weightOfOperatorForQuorum( + uint8 quorumNumber, + address operator + ) public virtual view quorumExists(quorumNumber) returns (uint96) { + return _weightOfOperatorForQuorum(quorumNumber, operator); + } + + /// @notice Returns the length of the dynamic array stored in `strategyParams[quorumNumber]`. + function strategyParamsLength(uint8 quorumNumber) public view returns (uint256) { + return strategyParams[quorumNumber].length; + } + + /// @notice Returns the strategy and weight multiplier for the `index`'th strategy in the quorum `quorumNumber` + function strategyParamsByIndex( + uint8 quorumNumber, + uint256 index + ) public view returns (StrategyParams memory) + { + return strategyParams[quorumNumber][index]; + } + + /** + * @notice Returns the entire `operatorStakeHistory[operatorId][quorumNumber]` array. * @param operatorId The id of the operator of interest. * @param quorumNumber The quorum number to get the stake for. */ - function getOperatorIdToStakeHistory( + function getOperatorStakeHistory( bytes32 operatorId, uint8 quorumNumber ) external view returns (OperatorStakeUpdate[] memory) { - return operatorIdToStakeHistory[operatorId][quorumNumber]; + return operatorStakeHistory[operatorId][quorumNumber]; } /** - * @notice Returns the `index`-th entry in the `operatorIdToStakeHistory[operatorId][quorumNumber]` array. + * @notice Returns the `index`-th entry in the `operatorStakeHistory[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]`. + * @param index Array index for lookup, within the dynamic array `operatorStakeHistory[operatorId][quorumNumber]`. * @dev Function will revert if `index` is out-of-bounds. */ - function getStakeUpdateForQuorumFromOperatorIdAndIndex( + function getStakeUpdateForOperatorAtIndex( uint8 quorumNumber, bytes32 operatorId, uint256 index ) external view returns (OperatorStakeUpdate memory) { - return operatorIdToStakeHistory[operatorId][quorumNumber][index]; + return operatorStakeHistory[operatorId][quorumNumber][index]; } /** @@ -547,7 +573,7 @@ contract StakeRegistry is StakeRegistryStorage { * @param quorumNumber The quorum number to get the stake for. * @param index Array index for lookup, within the dynamic array `_totalStakeHistory[quorumNumber]`. */ - function getTotalStakeUpdateForQuorumFromIndex( + function getTotalStakeUpdateAtIndex( uint8 quorumNumber, uint256 index ) external view returns (OperatorStakeUpdate memory) { @@ -555,12 +581,12 @@ contract StakeRegistry is StakeRegistryStorage { } /// @notice Returns the indices of the operator stakes for the provided `quorumNumber` at the given `blockNumber` - function getStakeUpdateIndexForOperatorIdForQuorumAtBlockNumber( + function getStakeUpdateIndexForOperatorAtBlockNumber( bytes32 operatorId, uint8 quorumNumber, uint32 blockNumber ) external view returns (uint32) { - return _getStakeUpdateIndexForOperatorIdForQuorumAtBlockNumber(operatorId, quorumNumber, blockNumber); + return _getStakeUpdateIndexForOperatorAtBlockNumber(operatorId, quorumNumber, blockNumber); } /** @@ -569,7 +595,7 @@ contract StakeRegistry is StakeRegistryStorage { * @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( + function getTotalStakeIndicesAtBlockNumber( uint32 blockNumber, bytes calldata quorumNumbers ) external view returns (uint32[] memory) { @@ -578,7 +604,7 @@ contract StakeRegistry is StakeRegistryStorage { uint8 quorumNumber = uint8(quorumNumbers[i]); require( _totalStakeHistory[quorumNumber][0].updateBlockNumber <= blockNumber, - "StakeRegistry.getTotalStakeIndicesByQuorumNumbersAtBlockNumber: quorum has no stake history at blockNumber" + "StakeRegistry.getTotalStakeIndicesAtBlockNumber: quorum has no stake history at blockNumber" ); uint256 length = _totalStakeHistory[quorumNumber].length; for (uint256 j = 0; j < length; j++) { @@ -593,21 +619,21 @@ contract StakeRegistry is StakeRegistryStorage { /** * @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 + * `index`-th entry in the `operatorStakeHistory[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 index Array index for lookup, within the dynamic array `operatorStakeHistory[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( + function getOperatorStakeAtBlockNumberAndIndex( uint8 quorumNumber, uint32 blockNumber, bytes32 operatorId, uint256 index ) external view returns (uint96) { - OperatorStakeUpdate memory operatorStakeUpdate = operatorIdToStakeHistory[operatorId][quorumNumber][index]; + OperatorStakeUpdate memory operatorStakeUpdate = operatorStakeHistory[operatorId][quorumNumber][index]; _validateOperatorStakeUpdateAtBlockNumber(operatorStakeUpdate, blockNumber); return operatorStakeUpdate.stake; } @@ -638,12 +664,12 @@ contract StakeRegistry is StakeRegistryStorage { bytes32 operatorId, uint8 quorumNumber ) public view returns (OperatorStakeUpdate memory) { - uint256 historyLength = operatorIdToStakeHistory[operatorId][quorumNumber].length; + uint256 historyLength = operatorStakeHistory[operatorId][quorumNumber].length; OperatorStakeUpdate memory operatorStakeUpdate; if (historyLength == 0) { return operatorStakeUpdate; } else { - operatorStakeUpdate = operatorIdToStakeHistory[operatorId][quorumNumber][historyLength - 1]; + operatorStakeUpdate = operatorStakeHistory[operatorId][quorumNumber][historyLength - 1]; return operatorStakeUpdate; } } @@ -658,14 +684,14 @@ contract StakeRegistry is StakeRegistryStorage { } /// @notice Returns the stake of the operator for the provided `quorumNumber` at the given `blockNumber` - function getStakeForOperatorIdForQuorumAtBlockNumber( + function getOperatorStakeAtBlockNumber( bytes32 operatorId, uint8 quorumNumber, uint32 blockNumber ) external view returns (uint96) { return - operatorIdToStakeHistory[operatorId][quorumNumber][ - _getStakeUpdateIndexForOperatorIdForQuorumAtBlockNumber(operatorId, quorumNumber, blockNumber) + operatorStakeHistory[operatorId][quorumNumber][ + _getStakeUpdateIndexForOperatorAtBlockNumber(operatorId, quorumNumber, blockNumber) ].stake; } @@ -677,14 +703,14 @@ contract StakeRegistry is StakeRegistryStorage { return _totalStakeHistory[quorumNumber][_totalStakeHistory[quorumNumber].length - 1].stake; } - function getLengthOfOperatorIdStakeHistoryForQuorum( + function getOperatorStakeHistoryLength( bytes32 operatorId, uint8 quorumNumber ) external view returns (uint256) { - return operatorIdToStakeHistory[operatorId][quorumNumber].length; + return operatorStakeHistory[operatorId][quorumNumber].length; } - function getLengthOfTotalStakeHistoryForQuorum(uint8 quorumNumber) external view returns (uint256) { + function getTotalStakeHistoryLength(uint8 quorumNumber) external view returns (uint256) { return _totalStakeHistory[quorumNumber].length; } } diff --git a/src/StakeRegistryStorage.sol b/src/StakeRegistryStorage.sol index 7b5e192f..c5e2f305 100644 --- a/src/StakeRegistryStorage.sol +++ b/src/StakeRegistryStorage.sol @@ -45,13 +45,13 @@ abstract contract StakeRegistryStorage is IStakeRegistry { OperatorStakeUpdate[][256] internal _totalStakeHistory; /// @notice mapping from operator's operatorId to the history of their stake updates - mapping(bytes32 => mapping(uint8 => OperatorStakeUpdate[])) internal operatorIdToStakeHistory; + mapping(bytes32 => mapping(uint8 => OperatorStakeUpdate[])) internal operatorStakeHistory; /** * @notice mapping from quorum number to the list of strategies considered and their * corresponding multipliers for that specific quorum */ - mapping(uint8 => StrategyAndWeightingMultiplier[]) public strategiesConsideredAndMultipliers; + mapping(uint8 => StrategyParams[]) public strategyParams; constructor( IRegistryCoordinator _registryCoordinator, diff --git a/src/interfaces/IStakeRegistry.sol b/src/interfaces/IStakeRegistry.sol index f7998fac..3ae2fa17 100644 --- a/src/interfaces/IStakeRegistry.sol +++ b/src/interfaces/IStakeRegistry.sol @@ -30,7 +30,7 @@ interface IStakeRegistry is IRegistry { * @notice In weighing a particular strategy, the amount of underlying asset for that strategy is * multiplied by its multiplier, then divided by WEIGHTING_DIVISOR */ - struct StrategyAndWeightingMultiplier { + struct StrategyParams { IStrategy strategy; uint96 multiplier; } @@ -47,11 +47,11 @@ interface IStakeRegistry is IRegistry { event MinimumStakeForQuorumUpdated(uint8 indexed quorumNumber, uint96 minimumStake); /// @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]` + /// @notice emitted when `strategy` has been added to the array at `strategyParams[quorumNumber]` event StrategyAddedToQuorum(uint8 indexed quorumNumber, IStrategy strategy); - /// @notice emitted when `strategy` has removed from the array at `strategiesConsideredAndMultipliers[quorumNumber]` + /// @notice emitted when `strategy` has removed from the array at `strategyParams[quorumNumber]` event StrategyRemovedFromQuorum(uint8 indexed quorumNumber, IStrategy strategy); - /// @notice emitted when `strategy` has its `multiplier` updated in the array at `strategiesConsideredAndMultipliers[quorumNumber]` + /// @notice emitted when `strategy` has its `multiplier` updated in the array at `strategyParams[quorumNumber]` event StrategyMultiplierUpdated(uint8 indexed quorumNumber, IStrategy strategy, uint256 multiplier); /** @@ -85,17 +85,17 @@ interface IStakeRegistry is IRegistry { /** * @notice Initialize a new quorum created by the registry coordinator by setting strategies, weights, and minimum stake */ - function initializeQuorum(uint8 quorumNumber, uint96 minimumStake, StrategyAndWeightingMultiplier[] memory strategyParams) external; + function initializeQuorum(uint8 quorumNumber, uint96 minimumStake, StrategyParams[] memory strategyParams) external; /// @notice Adds new strategies and the associated multipliers to the @param quorumNumber. function addStrategies( uint8 quorumNumber, - StrategyAndWeightingMultiplier[] memory strategyParams + StrategyParams[] memory strategyParams ) external; /** * @notice This function is used for removing strategies and their associated weights from the - * mapping strategiesConsideredAndMultipliers for a specific @param quorumNumber. + * mapping strategyParams 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 */ @@ -103,7 +103,7 @@ interface IStakeRegistry is IRegistry { /** * @notice This function is used for modifying the weights of strategies that are already in the - * mapping strategiesConsideredAndMultipliers for a specific + * mapping strategyParams 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 @@ -126,14 +126,14 @@ interface IStakeRegistry is IRegistry { /// @notice In order to register for a quorum i, an operator must have at least `minimumStakeForQuorum[i]` function minimumStakeForQuorum(uint256 quorumNumber) external view returns (uint96); - /// @notice Returns the length of the dynamic array stored in `strategiesConsideredAndMultipliers[quorumNumber]`. - function strategiesConsideredAndMultipliersLength(uint8 quorumNumber) external view returns (uint256); + /// @notice Returns the length of the dynamic array stored in `strategyParams[quorumNumber]`. + function strategyParamsLength(uint8 quorumNumber) external view returns (uint256); /// @notice Returns the strategy and weight multiplier for the `index`'th strategy in the quorum `quorumNumber` - function strategyAndWeightingMultiplierForQuorumByIndex( + function strategyParamsByIndex( uint8 quorumNumber, uint256 index - ) external view returns (StrategyAndWeightingMultiplier memory); + ) external view returns (StrategyParams memory); /** * @notice This function computes the total weight of the @param operator in the quorum @param quorumNumber. @@ -146,25 +146,25 @@ interface IStakeRegistry is IRegistry { * @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); + function getOperatorStakeHistory(bytes32 operatorId, uint8 quorumNumber) external view returns (OperatorStakeUpdate[] memory); - function getLengthOfTotalStakeHistoryForQuorum(uint8 quorumNumber) external view returns (uint256); + function getTotalStakeHistoryLength(uint8 quorumNumber) external view returns (uint256); /** * @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); + function getTotalStakeUpdateAtIndex(uint8 quorumNumber, uint256 index) external view returns (OperatorStakeUpdate memory); /// @notice Returns the indices of the operator stakes for the provided `quorumNumber` at the given `blockNumber` - function getStakeUpdateIndexForOperatorIdForQuorumAtBlockNumber(bytes32 operatorId, uint8 quorumNumber, uint32 blockNumber) + function getStakeUpdateIndexForOperatorAtBlockNumber(bytes32 operatorId, uint8 quorumNumber, uint32 blockNumber) external view returns (uint32); /// @notice Returns the indices of the total stakes for the provided `quorumNumbers` at the given `blockNumber` - function getTotalStakeIndicesByQuorumNumbersAtBlockNumber(uint32 blockNumber, bytes calldata quorumNumbers) external view returns(uint32[] memory) ; + function getTotalStakeIndicesAtBlockNumber(uint32 blockNumber, bytes calldata quorumNumbers) external view returns(uint32[] memory) ; /** * @notice Returns the `index`-th entry in the `operatorIdToStakeHistory[operatorId][quorumNumber]` array. @@ -173,7 +173,7 @@ interface IStakeRegistry is IRegistry { * @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) + function getStakeUpdateForOperatorAtIndex(uint8 quorumNumber, bytes32 operatorId, uint256 index) external view returns (OperatorStakeUpdate memory); @@ -195,7 +195,7 @@ interface IStakeRegistry is IRegistry { * @dev Function will revert if `index` is out-of-bounds. * @dev used the BLSSignatureChecker to get past stakes of signing operators */ - function getStakeForQuorumAtBlockNumberFromOperatorIdAndIndex(uint8 quorumNumber, uint32 blockNumber, bytes32 operatorId, uint256 index) + function getOperatorStakeAtBlockNumberAndIndex(uint8 quorumNumber, uint32 blockNumber, bytes32 operatorId, uint256 index) external view returns (uint96); @@ -219,7 +219,7 @@ interface IStakeRegistry is IRegistry { function getCurrentOperatorStakeForQuorum(bytes32 operatorId, uint8 quorumNumber) external view returns (uint96); /// @notice Returns the stake of the operator for the provided `quorumNumber` at the given `blockNumber` - function getStakeForOperatorIdForQuorumAtBlockNumber(bytes32 operatorId, uint8 quorumNumber, uint32 blockNumber) + function getOperatorStakeAtBlockNumber(bytes32 operatorId, uint8 quorumNumber, uint32 blockNumber) external view returns (uint96); diff --git a/test/mocks/StakeRegistryMock.sol b/test/mocks/StakeRegistryMock.sol index f6de93e7..fd61e77e 100644 --- a/test/mocks/StakeRegistryMock.sol +++ b/test/mocks/StakeRegistryMock.sol @@ -43,12 +43,12 @@ contract StakeRegistryMock is IStakeRegistry { /** * @notice Initialize a new quorum created by the registry coordinator by setting strategies, weights, and minimum stake */ - function initializeQuorum(uint8 quorumNumber, uint96 minimumStake, StrategyAndWeightingMultiplier[] memory strategyParams) external {} + function initializeQuorum(uint8 quorumNumber, uint96 minimumStake, StrategyParams[] memory strategyParams) external {} /// @notice Adds new strategies and the associated multipliers to the @param quorumNumber. function addStrategies( uint8 quorumNumber, - StrategyAndWeightingMultiplier[] memory strategyParams + StrategyParams[] memory strategyParams ) external {} /** @@ -77,16 +77,16 @@ contract StakeRegistryMock is IStakeRegistry { function WEIGHTING_DIVISOR() external pure returns (uint256) {} - function strategiesConsideredAndMultipliersLength(uint8 quorumNumber) external view returns (uint256) {} + function strategyParamsLength(uint8 quorumNumber) external view returns (uint256) {} /// @notice In order to register for a quorum i, an operator must have at least `minimumStakeForQuorum[i]` function minimumStakeForQuorum(uint256 quorumNumber) external view returns (uint96) {} /// @notice Returns the strategy and weight multiplier for the `index`'th strategy in the quorum `quorumNumber` - function strategyAndWeightingMultiplierForQuorumByIndex( + function strategyParamsByIndex( uint8 quorumNumber, uint256 index - ) external view returns (StrategyAndWeightingMultiplier memory) {} + ) external view returns (StrategyParams memory) {} /** * @notice This function computes the total weight of the @param operator in the quorum @param quorumNumber. @@ -99,25 +99,25 @@ contract StakeRegistryMock is IStakeRegistry { * @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) {} + function getOperatorStakeHistory(bytes32 operatorId, uint8 quorumNumber) external view returns (OperatorStakeUpdate[] memory) {} - function getLengthOfTotalStakeHistoryForQuorum(uint8 quorumNumber) external view returns (uint256) {} + function getTotalStakeHistoryLength(uint8 quorumNumber) external view returns (uint256) {} /** * @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) {} + function getTotalStakeUpdateAtIndex(uint8 quorumNumber, uint256 index) external view returns (OperatorStakeUpdate memory) {} /// @notice Returns the indices of the operator stakes for the provided `quorumNumber` at the given `blockNumber` - function getStakeUpdateIndexForOperatorIdForQuorumAtBlockNumber(bytes32 operatorId, uint8 quorumNumber, uint32 blockNumber) + function getStakeUpdateIndexForOperatorAtBlockNumber(bytes32 operatorId, uint8 quorumNumber, uint32 blockNumber) external view returns (uint32) {} /// @notice Returns the indices of the total stakes for the provided `quorumNumbers` at the given `blockNumber` - function getTotalStakeIndicesByQuorumNumbersAtBlockNumber(uint32 blockNumber, bytes calldata quorumNumbers) external view returns(uint32[] memory) {} + function getTotalStakeIndicesAtBlockNumber(uint32 blockNumber, bytes calldata quorumNumbers) external view returns(uint32[] memory) {} /** * @notice Returns the `index`-th entry in the `operatorIdToStakeHistory[operatorId][quorumNumber]` array. @@ -126,7 +126,7 @@ contract StakeRegistryMock is IStakeRegistry { * @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) + function getStakeUpdateForOperatorAtIndex(uint8 quorumNumber, bytes32 operatorId, uint256 index) external view returns (OperatorStakeUpdate memory) {} @@ -148,7 +148,7 @@ contract StakeRegistryMock is IStakeRegistry { * @dev Function will revert if `index` is out-of-bounds. * @dev used the BLSSignatureChecker to get past stakes of signing operators */ - function getStakeForQuorumAtBlockNumberFromOperatorIdAndIndex(uint8 quorumNumber, uint32 blockNumber, bytes32 operatorId, uint256 index) + function getOperatorStakeAtBlockNumberAndIndex(uint8 quorumNumber, uint32 blockNumber, bytes32 operatorId, uint256 index) external view returns (uint96) {} @@ -172,7 +172,7 @@ contract StakeRegistryMock is IStakeRegistry { function getCurrentOperatorStakeForQuorum(bytes32 operatorId, uint8 quorumNumber) external view returns (uint96) {} /// @notice Returns the stake of the operator for the provided `quorumNumber` at the given `blockNumber` - function getStakeForOperatorIdForQuorumAtBlockNumber(bytes32 operatorId, uint8 quorumNumber, uint32 blockNumber) + function getOperatorStakeAtBlockNumber(bytes32 operatorId, uint8 quorumNumber, uint32 blockNumber) external view returns (uint96){} diff --git a/test/unit/BLSRegistryCoordinatorWithIndicesUnit.t.sol b/test/unit/BLSRegistryCoordinatorWithIndicesUnit.t.sol index c2f22c63..76d5d7e1 100644 --- a/test/unit/BLSRegistryCoordinatorWithIndicesUnit.t.sol +++ b/test/unit/BLSRegistryCoordinatorWithIndicesUnit.t.sol @@ -65,7 +65,7 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { 0/*initialPausedStatus*/, operatorSetParams, new uint96[](0), - new IStakeRegistry.StrategyAndWeightingMultiplier[][](0) + new IStakeRegistry.StrategyParams[][](0) ); } diff --git a/test/unit/StakeRegistryUnit.t.sol b/test/unit/StakeRegistryUnit.t.sol index c1663087..2a362c8f 100644 --- a/test/unit/StakeRegistryUnit.t.sol +++ b/test/unit/StakeRegistryUnit.t.sol @@ -136,9 +136,9 @@ contract StakeRegistryUnitTests is Test { // Initialize quorums with dummy minimum stake and strategies for (uint i = 0; i < maxQuorumsToRegisterFor; i++) { uint96 minimumStake = uint96(i + 1); - IStakeRegistry.StrategyAndWeightingMultiplier[] memory strategyParams = - new IStakeRegistry.StrategyAndWeightingMultiplier[](1); - strategyParams[0] = IStakeRegistry.StrategyAndWeightingMultiplier( + IStakeRegistry.StrategyParams[] memory strategyParams = + new IStakeRegistry.StrategyParams[](1); + strategyParams[0] = IStakeRegistry.StrategyParams( IStrategy(address(uint160(i))), uint96(i+1) ); @@ -207,19 +207,19 @@ contract StakeRegistryUnitTests is Test { 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); + assertEq(stakeRegistry.getOperatorStakeHistoryLength(defaultOperatorId, i), 1); // make sure that the stake update is as expected IStakeRegistry.OperatorStakeUpdate memory stakeUpdate = - stakeRegistry.getStakeUpdateForQuorumFromOperatorIdAndIndex(i, defaultOperatorId, 0); + stakeRegistry.getStakeUpdateForOperatorAtIndex(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); + assertEq(stakeRegistry.getTotalStakeHistoryLength(i), 1); // make sure that the stake update is as expected - stakeUpdate = stakeRegistry.getTotalStakeUpdateForQuorumFromIndex(i, 0); + stakeUpdate = stakeRegistry.getTotalStakeUpdateAtIndex(i, 0); assertEq(stakeUpdate.stake, paddedStakesForQuorum[quorumNumberIndex]); assertEq(stakeUpdate.updateBlockNumber, uint32(block.number)); assertEq(stakeUpdate.nextUpdateBlockNumber, 0); @@ -227,9 +227,9 @@ contract StakeRegistryUnitTests is Test { 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); + assertEq(stakeRegistry.getOperatorStakeHistoryLength(defaultOperatorId, i), 0); // make the analogous check for total stake history - assertEq(stakeRegistry.getLengthOfTotalStakeHistoryForQuorum(i), 1); + assertEq(stakeRegistry.getTotalStakeHistoryLength(i), 1); } } } @@ -297,7 +297,7 @@ contract StakeRegistryUnitTests is Test { cumulativeBlockNumber += blocksPassed[j]; } - uint historyLength = stakeRegistry.getLengthOfTotalStakeHistoryForQuorum(i); + uint historyLength = stakeRegistry.getTotalStakeHistoryLength(i); // If we don't have stake history, it should be because there is no stake if (historyLength == 0) { @@ -307,13 +307,13 @@ contract StakeRegistryUnitTests is Test { // make sure that the stake update is as expected IStakeRegistry.OperatorStakeUpdate memory totalStakeUpdate = - stakeRegistry.getTotalStakeUpdateForQuorumFromIndex(i, historyLength-1); + stakeRegistry.getTotalStakeUpdateAtIndex(i, historyLength-1); assertEq(totalStakeUpdate.stake, cumulativeStake); // make sure that the next update block number of the previous stake update is as expected if (historyLength >= 2) { IStakeRegistry.OperatorStakeUpdate memory prevTotalStakeUpdate = - stakeRegistry.getTotalStakeUpdateForQuorumFromIndex(i, historyLength-2); + stakeRegistry.getTotalStakeUpdateAtIndex(i, historyLength-2); assertEq(prevTotalStakeUpdate.nextUpdateBlockNumber, cumulativeBlockNumber); } } @@ -382,21 +382,21 @@ contract StakeRegistryUnitTests is Test { 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"); + assertEq(stakeRegistry.getOperatorStakeHistoryLength(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); + stakeRegistry.getStakeUpdateForOperatorAtIndex(i, operatorIdToDeregister, 1); assertEq(lastStakeUpdate.stake, 0, "testDeregisterFirstOperator_Valid_1"); assertEq(lastStakeUpdate.updateBlockNumber, cumulativeBlockNumber, "testDeregisterFirstOperator_Valid_2"); assertEq(lastStakeUpdate.nextUpdateBlockNumber, 0, "testDeregisterFirstOperator_Valid_3"); // Get history length for quorum - uint historyLength = stakeRegistry.getLengthOfTotalStakeHistoryForQuorum(i); + uint historyLength = stakeRegistry.getTotalStakeHistoryLength(i); // make sure that the last stake update is as expected IStakeRegistry.OperatorStakeUpdate memory lastTotalStakeUpdate - = stakeRegistry.getTotalStakeUpdateForQuorumFromIndex(i, historyLength-1); + = stakeRegistry.getTotalStakeUpdateAtIndex(i, historyLength-1); assertEq(lastTotalStakeUpdate.stake, - stakeRegistry.getTotalStakeUpdateForQuorumFromIndex(i, historyLength-2).stake // the previous total stake + stakeRegistry.getTotalStakeUpdateAtIndex(i, historyLength-2).stake // the previous total stake - paddedStakesForQuorum[quorumNumberIndex], // minus the stake that was deregistered "testDeregisterFirstOperator_Valid_5" ); @@ -404,12 +404,12 @@ contract StakeRegistryUnitTests is Test { 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] + 1, "testDeregisterFirstOperator_Valid_9"); + assertEq(stakeRegistry.getOperatorStakeHistoryLength(operatorIdToDeregister, i), 1, "testDeregisterFirstOperator_Valid_8"); + assertEq(stakeRegistry.getTotalStakeHistoryLength(i), numOperatorsInQuorum[i] + 1, "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"); + assertEq(stakeRegistry.getOperatorStakeHistoryLength(operatorIdToDeregister, i), 0, "testDeregisterFirstOperator_Valid_10"); } } } @@ -464,9 +464,9 @@ contract StakeRegistryUnitTests is Test { stakeRegistry.recordTotalStakeUpdate(defaultQuorumNumber, stakeDelta); IStakeRegistry.OperatorStakeUpdate memory newStakeUpdate; - uint historyLength = stakeRegistry.getLengthOfTotalStakeHistoryForQuorum(defaultQuorumNumber); + uint historyLength = stakeRegistry.getTotalStakeHistoryLength(defaultQuorumNumber); if (historyLength != 0) { - newStakeUpdate = stakeRegistry.getTotalStakeUpdateForQuorumFromIndex(defaultQuorumNumber, historyLength-1); + newStakeUpdate = stakeRegistry.getTotalStakeUpdateAtIndex(defaultQuorumNumber, historyLength-1); } // Check that the most recent entry reflects the correct stake assertEq(newStakeUpdate.stake, stakes[i]); @@ -513,7 +513,7 @@ contract StakeRegistryUnitTests is Test { function _initializeQuorum( uint8 quorumNumber, uint96 minimumStake, - IStakeRegistry.StrategyAndWeightingMultiplier[] memory strategyParams + IStakeRegistry.StrategyParams[] memory strategyParams ) internal { cheats.prank(address(registryCoordinator)); diff --git a/test/unit/VoteWeigherBaseUnit.t.sol b/test/unit/VoteWeigherBaseUnit.t.sol index 0539575d..23db3a5f 100644 --- a/test/unit/VoteWeigherBaseUnit.t.sol +++ b/test/unit/VoteWeigherBaseUnit.t.sol @@ -97,7 +97,7 @@ contract VoteWeigherBaseUnitTests is Test { } /// TODO - migrate tests to registry coordinator - // function testCreateQuorum_Valid(IStakeRegistry.StrategyAndWeightingMultiplier[] memory strategiesAndWeightingMultipliers) public { + // function testCreateQuorum_Valid(IStakeRegistry.StrategyParams[] memory strategiesAndWeightingMultipliers) public { // strategiesAndWeightingMultipliers = _convertToValidStrategiesAndWeightingMultipliers(strategiesAndWeightingMultipliers); // // create a quorum from the serviceManagerOwner // // get the quorum count before the quorum is created @@ -116,7 +116,7 @@ contract VoteWeigherBaseUnitTests is Test { // assertEq(voteWeigher.quorumCount(), quorumCountBefore + 1); // // check that all of the weights are correct // for (uint i = 0; i < strategiesAndWeightingMultipliers.length; i++) { - // IStakeRegistry.StrategyAndWeightingMultiplier memory strategyAndWeightingMultiplier = voteWeigher.strategyAndWeightingMultiplierForQuorumByIndex(quorumCountBefore, i); + // IStakeRegistry.StrategyParams memory strategyAndWeightingMultiplier = voteWeigher.strategyAndWeightingMultiplierForQuorumByIndex(quorumCountBefore, i); // assertEq(address(strategyAndWeightingMultiplier.strategy), address(strategiesAndWeightingMultipliers[i].strategy)); // assertEq(strategyAndWeightingMultiplier.multiplier, strategiesAndWeightingMultipliers[i].multiplier); // } @@ -124,7 +124,7 @@ contract VoteWeigherBaseUnitTests is Test { // function testCreateQuorum_FromNotServiceManagerOwner_Reverts( // address notServiceManagerOwner, - // IStakeRegistry.StrategyAndWeightingMultiplier[] memory strategiesAndWeightingMultipliers + // IStakeRegistry.StrategyParams[] memory strategiesAndWeightingMultipliers // ) public fuzzedAddress(notServiceManagerOwner) { // cheats.assume(notServiceManagerOwner != serviceManagerOwner); // cheats.prank(notServiceManagerOwner); @@ -133,7 +133,7 @@ contract VoteWeigherBaseUnitTests is Test { // } // function testCreateQuorum_StrategiesAndWeightingMultipliers_LengthGreaterThanMaxAllowed_Reverts( - // IStakeRegistry.StrategyAndWeightingMultiplier[] memory strategiesAndWeightingMultipliers + // IStakeRegistry.StrategyParams[] memory strategiesAndWeightingMultipliers // ) public { // strategiesAndWeightingMultipliers = _removeDuplicates(strategiesAndWeightingMultipliers); // strategiesAndWeightingMultipliers = _replaceZeroWeights(strategiesAndWeightingMultipliers); @@ -145,7 +145,7 @@ contract VoteWeigherBaseUnitTests is Test { // } // function testCreateQuorum_StrategiesAndWeightingMultipliers_WithDuplicateStrategies_Reverts( - // IStakeRegistry.StrategyAndWeightingMultiplier[] memory strategiesAndWeightingMultipliers, + // IStakeRegistry.StrategyParams[] memory strategiesAndWeightingMultipliers, // uint256 indexFromDuplicate, // uint256 indexToDuplicate // ) public { @@ -165,14 +165,14 @@ contract VoteWeigherBaseUnitTests is Test { // } // function testCreateQuorum_EmptyStrategiesAndWeightingMultipliers_Reverts() public { - // IStakeRegistry.StrategyAndWeightingMultiplier[] memory strategiesAndWeightingMultipliers; + // IStakeRegistry.StrategyParams[] memory strategiesAndWeightingMultipliers; // cheats.prank(serviceManagerOwner); // cheats.expectRevert("VoteWeigherBase._addStrategiesConsideredAndMultipliers: no strategies provided"); // voteWeigher.createQuorum(strategiesAndWeightingMultipliers); // } // function testCreateQuorum_StrategiesAndWeightingMultipliers_WithZeroWeight( - // IStakeRegistry.StrategyAndWeightingMultiplier[] memory strategiesAndWeightingMultipliers, + // IStakeRegistry.StrategyParams[] memory strategiesAndWeightingMultipliers, // uint256 indexForZeroMultiplier // ) public { // strategiesAndWeightingMultipliers = _removeDuplicates(strategiesAndWeightingMultipliers); @@ -187,7 +187,7 @@ contract VoteWeigherBaseUnitTests is Test { // } // function testCreateQuorum_MoreThanMaxQuorums_Reverts() public { - // IStakeRegistry.StrategyAndWeightingMultiplier[] memory strategiesAndWeightingMultipliers = _defaultStrategiesAndWeightingMultipliers(); + // IStakeRegistry.StrategyParams[] memory strategiesAndWeightingMultipliers = _defaultStrategiesAndWeightingMultipliers(); // uint256 maxQuorums = voteWeigher.MAX_QUORUM_COUNT(); // cheats.startPrank(serviceManagerOwner); @@ -202,7 +202,7 @@ contract VoteWeigherBaseUnitTests is Test { // function testAddStrategiesConsideredAndMultipliers_Valid( // uint256 randomSplit, - // IStakeRegistry.StrategyAndWeightingMultiplier[] memory strategiesAndWeightingMultipliers + // IStakeRegistry.StrategyParams[] memory strategiesAndWeightingMultipliers // ) public { // strategiesAndWeightingMultipliers = _convertToValidStrategiesAndWeightingMultipliers(strategiesAndWeightingMultipliers); // // make sure there is at least 2 strategies @@ -210,8 +210,8 @@ contract VoteWeigherBaseUnitTests is Test { // // 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 - // IStakeRegistry.StrategyAndWeightingMultiplier[] memory strategiesAndWeightingMultipliers1 = new IStakeRegistry.StrategyAndWeightingMultiplier[](randomSplit); - // IStakeRegistry.StrategyAndWeightingMultiplier[] memory strategiesAndWeightingMultipliers2 = new IStakeRegistry.StrategyAndWeightingMultiplier[](strategiesAndWeightingMultipliers.length - randomSplit); + // IStakeRegistry.StrategyParams[] memory strategiesAndWeightingMultipliers1 = new IStakeRegistry.StrategyParams[](randomSplit); + // IStakeRegistry.StrategyParams[] memory strategiesAndWeightingMultipliers2 = new IStakeRegistry.StrategyParams[](strategiesAndWeightingMultipliers.length - randomSplit); // for (uint256 i = 0; i < strategiesAndWeightingMultipliers.length; i++) { // if (i < randomSplit) { // strategiesAndWeightingMultipliers1[i] = strategiesAndWeightingMultipliers[i]; @@ -233,7 +233,7 @@ contract VoteWeigherBaseUnitTests is Test { // // check that the quorum was created and strategies were added correctly // for (uint i = 0; i < strategiesAndWeightingMultipliers.length; i++) { - // IStakeRegistry.StrategyAndWeightingMultiplier memory strategyAndWeightingMultiplier = voteWeigher.strategyAndWeightingMultiplierForQuorumByIndex(quorumNumber, i); + // IStakeRegistry.StrategyParams memory strategyAndWeightingMultiplier = voteWeigher.strategyAndWeightingMultiplierForQuorumByIndex(quorumNumber, i); // assertEq(address(strategyAndWeightingMultiplier.strategy), address(strategiesAndWeightingMultipliers[i].strategy)); // assertEq(strategyAndWeightingMultiplier.multiplier, strategiesAndWeightingMultipliers[i].multiplier); // } @@ -243,7 +243,7 @@ contract VoteWeigherBaseUnitTests is Test { // address notServiceManagerOwner // ) public fuzzedAddress(notServiceManagerOwner) { // cheats.assume(notServiceManagerOwner != serviceManagerOwner); - // IStakeRegistry.StrategyAndWeightingMultiplier[] memory strategiesAndWeightingMultipliers = _defaultStrategiesAndWeightingMultipliers(); + // IStakeRegistry.StrategyParams[] memory strategiesAndWeightingMultipliers = _defaultStrategiesAndWeightingMultipliers(); // // create quorum with all but the last element // uint8 quorumNumber = uint8(voteWeigher.quorumCount()); @@ -257,7 +257,7 @@ contract VoteWeigherBaseUnitTests is Test { // } // function testAddStrategiesConsideredAndMultipliers_ForNonexistentQuorum_Reverts() public { - // IStakeRegistry.StrategyAndWeightingMultiplier[] memory strategiesAndWeightingMultipliers = _defaultStrategiesAndWeightingMultipliers(); + // IStakeRegistry.StrategyParams[] memory strategiesAndWeightingMultipliers = _defaultStrategiesAndWeightingMultipliers(); // // create quorum with all but the last element // uint8 quorumNumber = uint8(voteWeigher.quorumCount()); @@ -273,7 +273,7 @@ contract VoteWeigherBaseUnitTests is Test { // // 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( - // IStakeRegistry.StrategyAndWeightingMultiplier[] memory strategiesAndWeightingMultipliers, + // IStakeRegistry.StrategyParams[] memory strategiesAndWeightingMultipliers, // uint256 randomness // ) public { // strategiesAndWeightingMultipliers = _convertToValidStrategiesAndWeightingMultipliers(strategiesAndWeightingMultipliers); @@ -295,13 +295,13 @@ contract VoteWeigherBaseUnitTests is Test { // // check that the strategies that were not removed are still there // // get all strategies and multipliers form the contracts - // IStakeRegistry.StrategyAndWeightingMultiplier[] memory strategiesAndWeightingMultipliersFromContract = new IStakeRegistry.StrategyAndWeightingMultiplier[](voteWeigher.strategiesConsideredAndMultipliersLength(quorumNumber)); + // IStakeRegistry.StrategyParams[] memory strategiesAndWeightingMultipliersFromContract = new IStakeRegistry.StrategyParams[](voteWeigher.strategiesConsideredAndMultipliersLength(quorumNumber)); // for (uint256 i = 0; i < strategiesAndWeightingMultipliersFromContract.length; i++) { // strategiesAndWeightingMultipliersFromContract[i] = voteWeigher.strategyAndWeightingMultiplierForQuorumByIndex(quorumNumber, i); // } // // remove indicesToRemove from local strategiesAndWeightingMultipliers - // IStakeRegistry.StrategyAndWeightingMultiplier[] memory strategiesAndWeightingMultipliersLocal = new IStakeRegistry.StrategyAndWeightingMultiplier[](strategiesAndWeightingMultipliers.length - indicesToRemove.length); + // IStakeRegistry.StrategyParams[] memory strategiesAndWeightingMultipliersLocal = new IStakeRegistry.StrategyParams[](strategiesAndWeightingMultipliers.length - indicesToRemove.length); // // run the removal algorithm locally // uint256 endIndex = strategiesAndWeightingMultipliers.length - 1; @@ -328,7 +328,7 @@ contract VoteWeigherBaseUnitTests is Test { // address notServiceManagerOwner // ) public fuzzedAddress(notServiceManagerOwner) { // cheats.assume(notServiceManagerOwner != serviceManagerOwner); - // IStakeRegistry.StrategyAndWeightingMultiplier[] memory strategiesAndWeightingMultipliers = _defaultStrategiesAndWeightingMultipliers(); + // IStakeRegistry.StrategyParams[] memory strategiesAndWeightingMultipliers = _defaultStrategiesAndWeightingMultipliers(); // uint256[] memory indicesToRemove = new uint256[](1); @@ -344,7 +344,7 @@ contract VoteWeigherBaseUnitTests is Test { // } // function testRemoveStrategiesConsideredAndMultipliers_ForNonexistentQuorum_Reverts() public { - // IStakeRegistry.StrategyAndWeightingMultiplier[] memory strategiesAndWeightingMultipliers = _defaultStrategiesAndWeightingMultipliers(); + // IStakeRegistry.StrategyParams[] memory strategiesAndWeightingMultipliers = _defaultStrategiesAndWeightingMultipliers(); // uint256[] memory indicesToRemove = new uint256[](1); @@ -359,7 +359,7 @@ contract VoteWeigherBaseUnitTests is Test { // } // function testRemoveStrategiesConsideredAndMultipliers_EmptyIndicesToRemove_Reverts() public { - // IStakeRegistry.StrategyAndWeightingMultiplier[] memory strategiesAndWeightingMultipliers = _defaultStrategiesAndWeightingMultipliers(); + // IStakeRegistry.StrategyParams[] memory strategiesAndWeightingMultipliers = _defaultStrategiesAndWeightingMultipliers(); // // create a valid quorum // uint8 quorumNumber = uint8(voteWeigher.quorumCount()); @@ -375,7 +375,7 @@ contract VoteWeigherBaseUnitTests is Test { // address notServiceManagerOwner // ) public fuzzedAddress(notServiceManagerOwner) { // cheats.assume(notServiceManagerOwner != serviceManagerOwner); - // IStakeRegistry.StrategyAndWeightingMultiplier[] memory strategiesAndWeightingMultipliers = _defaultStrategiesAndWeightingMultipliers(); + // IStakeRegistry.StrategyParams[] memory strategiesAndWeightingMultipliers = _defaultStrategiesAndWeightingMultipliers(); // uint256[] memory strategyIndices = new uint256[](1); // uint96[] memory newWeights = new uint96[](1); @@ -392,7 +392,7 @@ contract VoteWeigherBaseUnitTests is Test { // } // function testModifyStrategyWeights_Valid( - // IStakeRegistry.StrategyAndWeightingMultiplier[] memory strategiesAndWeightingMultipliers, + // IStakeRegistry.StrategyParams[] memory strategiesAndWeightingMultipliers, // uint96[] memory newWeights, // uint256 randomness // ) public { @@ -428,14 +428,14 @@ contract VoteWeigherBaseUnitTests is Test { // } // // make sure the quorum strategies and weights have changed // for (uint i = 0; i < strategiesAndWeightingMultipliers.length; i++) { - // IStakeRegistry.StrategyAndWeightingMultiplier memory strategyAndWeightingMultiplier = voteWeigher.strategyAndWeightingMultiplierForQuorumByIndex(quorumNumber, i); + // IStakeRegistry.StrategyParams 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 { - // IStakeRegistry.StrategyAndWeightingMultiplier[] memory strategiesAndWeightingMultipliers = _defaultStrategiesAndWeightingMultipliers(); + // IStakeRegistry.StrategyParams[] memory strategiesAndWeightingMultipliers = _defaultStrategiesAndWeightingMultipliers(); // uint256[] memory strategyIndices = new uint256[](1); // uint96[] memory newWeights = new uint96[](1); @@ -454,7 +454,7 @@ contract VoteWeigherBaseUnitTests is Test { // uint256[] memory strategyIndices, // uint96[] memory newWeights // ) public { - // IStakeRegistry.StrategyAndWeightingMultiplier[] memory strategiesAndWeightingMultipliers = _defaultStrategiesAndWeightingMultipliers(); + // IStakeRegistry.StrategyParams[] memory strategiesAndWeightingMultipliers = _defaultStrategiesAndWeightingMultipliers(); // // make sure the arrays are of different lengths // cheats.assume(strategyIndices.length != newWeights.length); @@ -471,7 +471,7 @@ contract VoteWeigherBaseUnitTests is Test { // } // function testModifyStrategyWeights_EmptyStrategyIndicesAndWeights_Reverts() public { - // IStakeRegistry.StrategyAndWeightingMultiplier[] memory strategiesAndWeightingMultipliers = _defaultStrategiesAndWeightingMultipliers(); + // IStakeRegistry.StrategyParams[] memory strategiesAndWeightingMultipliers = _defaultStrategiesAndWeightingMultipliers(); // // create a valid quorum // uint8 quorumNumber = uint8(voteWeigher.quorumCount()); @@ -485,7 +485,7 @@ contract VoteWeigherBaseUnitTests is Test { // function testWeightOfOperatorForQuorum( // address operator, - // IStakeRegistry.StrategyAndWeightingMultiplier[] memory strategiesAndMultipliers, + // IStakeRegistry.StrategyParams[] memory strategiesAndMultipliers, // uint96[] memory shares // ) public { // strategiesAndMultipliers = _convertToValidStrategiesAndWeightingMultipliers(strategiesAndMultipliers); @@ -515,11 +515,11 @@ contract VoteWeigherBaseUnitTests is Test { // assertEq(voteWeigher.weightOfOperatorForQuorum(quorumNumber, operator), expectedWeight); // } - function _removeDuplicates(IStakeRegistry.StrategyAndWeightingMultiplier[] memory strategiesAndWeightingMultipliers) + function _removeDuplicates(IStakeRegistry.StrategyParams[] memory strategiesAndWeightingMultipliers) internal - returns(IStakeRegistry.StrategyAndWeightingMultiplier[] memory) + returns(IStakeRegistry.StrategyParams[] memory) { - IStakeRegistry.StrategyAndWeightingMultiplier[] memory deduplicatedStrategiesAndWeightingMultipliers = new IStakeRegistry.StrategyAndWeightingMultiplier[](strategiesAndWeightingMultipliers.length); + IStakeRegistry.StrategyParams[] memory deduplicatedStrategiesAndWeightingMultipliers = new IStakeRegistry.StrategyParams[](strategiesAndWeightingMultipliers.length); uint256 numUniqueStrategies = 0; // check for duplicates for (uint i = 0; i < strategiesAndWeightingMultipliers.length; i++) { @@ -536,14 +536,14 @@ contract VoteWeigherBaseUnitTests is Test { strategyInCurrentArray[strategiesAndWeightingMultipliers[i].strategy] = false; } - IStakeRegistry.StrategyAndWeightingMultiplier[] memory trimmedStrategiesAndWeightingMultipliers = new IStakeRegistry.StrategyAndWeightingMultiplier[](numUniqueStrategies); + IStakeRegistry.StrategyParams[] memory trimmedStrategiesAndWeightingMultipliers = new IStakeRegistry.StrategyParams[](numUniqueStrategies); for (uint i = 0; i < numUniqueStrategies; i++) { trimmedStrategiesAndWeightingMultipliers[i] = deduplicatedStrategiesAndWeightingMultipliers[i]; } return trimmedStrategiesAndWeightingMultipliers; } - function _replaceZeroWeights(IStakeRegistry.StrategyAndWeightingMultiplier[] memory strategiesAndWeightingMultipliers) internal pure returns(IStakeRegistry.StrategyAndWeightingMultiplier[] memory) { + function _replaceZeroWeights(IStakeRegistry.StrategyParams[] memory strategiesAndWeightingMultipliers) internal pure returns(IStakeRegistry.StrategyParams[] memory) { for (uint256 i = 0; i < strategiesAndWeightingMultipliers.length; i++) { if (strategiesAndWeightingMultipliers[i].multiplier == 0) { strategiesAndWeightingMultipliers[i].multiplier = 1; @@ -577,20 +577,20 @@ contract VoteWeigherBaseUnitTests is Test { return trimmedRandomIndices; } - function _convertToValidStrategiesAndWeightingMultipliers(IStakeRegistry.StrategyAndWeightingMultiplier[] memory strategiesAndWeightingMultipliers) internal returns (IStakeRegistry.StrategyAndWeightingMultiplier[] memory) { + function _convertToValidStrategiesAndWeightingMultipliers(IStakeRegistry.StrategyParams[] memory strategiesAndWeightingMultipliers) internal returns (IStakeRegistry.StrategyParams[] 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 (IStakeRegistry.StrategyAndWeightingMultiplier[] memory) { - IStakeRegistry.StrategyAndWeightingMultiplier[] memory strategiesAndWeightingMultipliers = new IStakeRegistry.StrategyAndWeightingMultiplier[](2); - strategiesAndWeightingMultipliers[0] = IStakeRegistry.StrategyAndWeightingMultiplier({ + function _defaultStrategiesAndWeightingMultipliers() internal pure returns (IStakeRegistry.StrategyParams[] memory) { + IStakeRegistry.StrategyParams[] memory strategiesAndWeightingMultipliers = new IStakeRegistry.StrategyParams[](2); + strategiesAndWeightingMultipliers[0] = IStakeRegistry.StrategyParams({ strategy: IStrategy(address(uint160(uint256(keccak256("strategy1"))))), multiplier: 1.04 ether }); - strategiesAndWeightingMultipliers[1] = IStakeRegistry.StrategyAndWeightingMultiplier({ + strategiesAndWeightingMultipliers[1] = IStakeRegistry.StrategyParams({ strategy: IStrategy(address(uint160(uint256(keccak256("strategy2"))))), multiplier: 1.69 ether }); diff --git a/test/utils/MockAVSDeployer.sol b/test/utils/MockAVSDeployer.sol index 265cbe6c..c12e9a64 100644 --- a/test/utils/MockAVSDeployer.sol +++ b/test/utils/MockAVSDeployer.sol @@ -251,11 +251,11 @@ contract MockAVSDeployer is Test { } // setup the dummy quorum strategies - IStakeRegistry.StrategyAndWeightingMultiplier[][] memory quorumStrategiesConsideredAndMultipliers = - new IStakeRegistry.StrategyAndWeightingMultiplier[][](numQuorumsToAdd); + IStakeRegistry.StrategyParams[][] memory quorumStrategiesConsideredAndMultipliers = + new IStakeRegistry.StrategyParams[][](numQuorumsToAdd); for (uint256 i = 0; i < quorumStrategiesConsideredAndMultipliers.length; i++) { - quorumStrategiesConsideredAndMultipliers[i] = new IStakeRegistry.StrategyAndWeightingMultiplier[](1); - quorumStrategiesConsideredAndMultipliers[i][0] = IStakeRegistry.StrategyAndWeightingMultiplier( + quorumStrategiesConsideredAndMultipliers[i] = new IStakeRegistry.StrategyParams[](1); + quorumStrategiesConsideredAndMultipliers[i][0] = IStakeRegistry.StrategyParams( IStrategy(address(uint160(i))), uint96(i+1) ); From e1ef7338f33586c35de1383c92f7b5fd3bc94c8e Mon Sep 17 00:00:00 2001 From: wadealexc Date: Thu, 2 Nov 2023 15:41:00 +0000 Subject: [PATCH 032/101] style: remove unused index registry function --- src/IndexRegistry.sol | 31 ------------------- src/interfaces/IIndexRegistry.sol | 8 ----- test/unit/IndexRegistryUnit.t.sol | 51 ------------------------------- 3 files changed, 90 deletions(-) diff --git a/src/IndexRegistry.sol b/src/IndexRegistry.sol index a55173d1..7b75002d 100644 --- a/src/IndexRegistry.sol +++ b/src/IndexRegistry.sol @@ -323,37 +323,6 @@ contract IndexRegistry is IndexRegistryStorage { return _latestIndexUpdate(quorumNumber, 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 `_operatorCountHistory[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 getTotalOperatorsForIndexAtBlockNumber( - uint8 quorumNumber, - uint32 blockNumber, - uint32 index - ) external view returns (uint32){ - QuorumUpdate memory quorumUpdate = _operatorCountHistory[quorumNumber][index]; - - // blocknumber must be at or after the "index'th" entry's fromBlockNumber - require( - blockNumber >= quorumUpdate.fromBlockNumber, - "IndexRegistry.getTotalOperatorsForIndexAtBlockNumber: 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 != _operatorCountHistory[quorumNumber].length - 1){ - QuorumUpdate memory nextQuorumUpdate = _operatorCountHistory[quorumNumber][index + 1]; - require( - blockNumber < nextQuorumUpdate.fromBlockNumber, - "IndexRegistry.getTotalOperatorsForIndexAtBlockNumber: provided index is too far in the future for provided block number" - ); - } - return quorumUpdate.numOperators; - } - /// @notice Returns an ordered list of operators of the services for the given `quorumNumber` at the given `blockNumber` function getOperatorListAtBlockNumber( uint8 quorumNumber, diff --git a/src/interfaces/IIndexRegistry.sol b/src/interfaces/IIndexRegistry.sol index 9875e50f..83c6b872 100644 --- a/src/interfaces/IIndexRegistry.sol +++ b/src/interfaces/IIndexRegistry.sol @@ -72,14 +72,6 @@ interface IIndexRegistry is IRegistry { /// @notice Returns the _operatorCountHistory entry for the specified `quorumNumber` at the specified `index` function getQuorumUpdateAtIndex(uint8 quorumNumber, uint32 index) external view returns (QuorumUpdate memory); - /** - * @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 `_operatorCountHistory[quorumNumber]` to read data from - */ - function getTotalOperatorsForIndexAtBlockNumber(uint8 quorumNumber, uint32 blockNumber, uint32 index) external view returns (uint32); - /// @notice Returns the current number of operators of this service for `quorumNumber`. function totalOperatorsForQuorum(uint8 quorumNumber) external view returns (uint32); diff --git a/test/unit/IndexRegistryUnit.t.sol b/test/unit/IndexRegistryUnit.t.sol index 3a4f10db..85dd7f1a 100644 --- a/test/unit/IndexRegistryUnit.t.sol +++ b/test/unit/IndexRegistryUnit.t.sol @@ -333,57 +333,6 @@ contract IndexRegistryUnitTests is Test { UNIT TESTS - GETTERS *******************************************************************************/ - function testGetTotalOperatorsForQuorumAtBlockNumberByIndex_revert_indexTooEarly() public { - // Add operator - bytes memory quorumNumbers = new bytes(1); - quorumNumbers[0] = bytes1(defaultQuorumNumber); - _registerOperator(operatorId1, quorumNumbers); - - cheats.expectRevert( - "IndexRegistry.getTotalOperatorsForIndexAtBlockNumber: provided index is too far in the past for provided block number" - ); - indexRegistry.getTotalOperatorsForIndexAtBlockNumber(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.getTotalOperatorsForIndexAtBlockNumber: provided index is too far in the future for provided block number" - ); - indexRegistry.getTotalOperatorsForIndexAtBlockNumber(defaultQuorumNumber, uint32(block.number), 0); - } - - function testGetTotalOperatorsForIndexAtBlockNumber() 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.getTotalOperatorsForIndexAtBlockNumber( - defaultQuorumNumber, - uint32(block.number - 10), - 0 - ); - require(prevTotal == 1, "IndexRegistry.getTotalOperatorsForIndexAtBlockNumber: prev total not 1"); - - // Check that the total is correct - uint32 currentTotal = indexRegistry.getTotalOperatorsForIndexAtBlockNumber( - defaultQuorumNumber, - uint32(block.number), - 1 - ); - require(currentTotal == 2, "IndexRegistry.getTotalOperatorsForIndexAtBlockNumber: current total not 2"); - } - function testGetOperatorListForQuorumAtBlockNumber() public { // Register two operators bytes memory quorumNumbers = new bytes(1); From 2d4236054f25045b7318a03b7a416ac4e6216db4 Mon Sep 17 00:00:00 2001 From: wadealexc Date: Thu, 2 Nov 2023 18:53:02 +0000 Subject: [PATCH 033/101] refactor: pk compendium stores operator pubkeys and can look them up ... which means pubkey parameters for various functions/structs can be removed --- src/BLSPubkeyRegistry.sol | 36 +++----- src/BLSPubkeyRegistryStorage.sol | 2 - src/BLSPublicKeyCompendium.sol | 31 ++++++- src/BLSRegistryCoordinatorWithIndices.sol | 60 ++++---------- src/interfaces/IBLSPubkeyRegistry.sol | 7 +- src/interfaces/IBLSPublicKeyCompendium.sol | 6 ++ .../IBLSRegistryCoordinatorWithIndices.sol | 7 +- test/ffi/BLSPubKeyCompendiumFFI.t.sol | 3 + test/mocks/BLSPublicKeyCompendiumMock.sol | 25 +++++- test/unit/BLSOperatorStateRetrieverUnit.t.sol | 2 +- test/unit/BLSPubkeyRegistryUnit.t.sol | 31 +++---- ...LSRegistryCoordinatorWithIndicesUnit.t.sol | 83 ++++++++----------- test/utils/MockAVSDeployer.sol | 4 +- 13 files changed, 141 insertions(+), 156 deletions(-) diff --git a/src/BLSPubkeyRegistry.sol b/src/BLSPubkeyRegistry.sol index cde6e522..160bd1b1 100644 --- a/src/BLSPubkeyRegistry.sol +++ b/src/BLSPubkeyRegistry.sol @@ -31,7 +31,6 @@ contract BLSPubkeyRegistry is BLSPubkeyRegistryStorage { * @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): @@ -42,31 +41,23 @@ contract BLSPubkeyRegistry is BLSPubkeyRegistryStorage { */ function registerOperator( address operator, - bytes memory quorumNumbers, - BN254.G1Point memory pubkey + bytes memory quorumNumbers ) public virtual onlyRegistryCoordinator returns (bytes32) { - //calculate hash of the operator's pubkey - bytes32 pubkeyHash = BN254.hashG1Point(pubkey); + // Get the operator's pubkey from the compendium. Reverts if they have not registered a key + BN254.G1Point memory pubkey = pubkeyCompendium.getRegisteredPubkey(operator); - 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 + // Update each quorum's aggregate pubkey _processQuorumApkUpdate(quorumNumbers, pubkey); - // emit event so offchain actors can update their state + // Return pubkeyHash, which will become the operator's unique id emit OperatorAddedToQuorums(operator, quorumNumbers); - return pubkeyHash; + return BN254.hashG1Point(pubkey); } /** * @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 @@ -74,23 +65,16 @@ contract BLSPubkeyRegistry is BLSPubkeyRegistryStorage { * 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 + bytes memory quorumNumbers ) public virtual onlyRegistryCoordinator { - bytes32 pubkeyHash = BN254.hashG1Point(pubkey); - - require( - getOperatorFromPubkeyHash(pubkeyHash) == operator, - "BLSPubkeyRegistry.registerOperator: operator does not own pubkey" - ); + // Get the operator's pubkey from the compendium. Reverts if they have not registered a key + BN254.G1Point memory pubkey = pubkeyCompendium.getRegisteredPubkey(operator); - // update each quorum's aggregate pubkey + // Update each quorum's aggregate pubkey _processQuorumApkUpdate(quorumNumbers, pubkey.negate()); - emit OperatorRemovedFromQuorums(operator, quorumNumbers); } diff --git a/src/BLSPubkeyRegistryStorage.sol b/src/BLSPubkeyRegistryStorage.sol index 21ecd3ca..5e42202a 100644 --- a/src/BLSPubkeyRegistryStorage.sol +++ b/src/BLSPubkeyRegistryStorage.sol @@ -10,8 +10,6 @@ import "./libraries/BN254.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 diff --git a/src/BLSPublicKeyCompendium.sol b/src/BLSPublicKeyCompendium.sol index bc7120d6..fc97f7f5 100644 --- a/src/BLSPublicKeyCompendium.sol +++ b/src/BLSPublicKeyCompendium.sol @@ -13,10 +13,15 @@ import "./libraries/BN254.sol"; contract BLSPublicKeyCompendium is IBLSPublicKeyCompendium { using BN254 for BN254.G1Point; - /// @notice mapping from operator address to pubkey hash + /// @notice the hash of the zero pubkey aka BN254.G1Point(0,0) + bytes32 internal constant ZERO_PK_HASH = hex"ad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5"; + + /// @notice maps operator address to pubkey hash mapping(address => bytes32) public operatorToPubkeyHash; - /// @notice mapping from pubkey hash to operator address + /// @notice maps pubkey hash to operator address mapping(bytes32 => address) public pubkeyHashToOperator; + /// @notice maps operator address to pubkeyG1 + mapping(address => BN254.G1Point) public operatorToPubkey; /******************************************************************************* EXTERNAL FUNCTIONS @@ -34,6 +39,9 @@ contract BLSPublicKeyCompendium is IBLSPublicKeyCompendium { BN254.G2Point memory pubkeyG2 ) external { bytes32 pubkeyHash = BN254.hashG1Point(pubkeyG1); + require( + pubkeyHash != ZERO_PK_HASH, "BLSPublicKeyCompendium.registerBLSPublicKey: cannot register zero pubkey" + ); require( operatorToPubkeyHash[msg.sender] == bytes32(0), "BLSPublicKeyCompendium.registerBLSPublicKey: operator already registered pubkey" @@ -66,6 +74,7 @@ contract BLSPublicKeyCompendium is IBLSPublicKeyCompendium { pubkeyG2 ), "BLSPublicKeyCompendium.registerBLSPublicKey: either the G1 signature is wrong, or G1 and G2 private key do not match"); + operatorToPubkey[msg.sender] = pubkeyG1; operatorToPubkeyHash[msg.sender] = pubkeyHash; pubkeyHashToOperator[pubkeyHash] = msg.sender; @@ -76,6 +85,24 @@ contract BLSPublicKeyCompendium is IBLSPublicKeyCompendium { VIEW FUNCTIONS *******************************************************************************/ + /** + * @notice Returns the pubkey of an operator, verifying that the pubkey and its hash are valid + * @dev Reverts if the operator has not registered a valid pubkey + */ + function getRegisteredPubkey(address operator) public view returns (BN254.G1Point memory) { + BN254.G1Point memory pubkey = operatorToPubkey[operator]; + bytes32 pubkeyHash = operatorToPubkeyHash[operator]; + + require( + pubkeyHash != bytes32(0) && BN254.hashG1Point(pubkey) == pubkeyHash, + "BLSPublicKeyCompendium.getRegisteredPubkey: operator is not registered" + ); + + require(pubkeyHash != ZERO_PK_HASH, "BLSPublicKeyCompendium.getRegisteredPubkey: invalid pubkey"); + + return pubkey; + } + /** * @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 diff --git a/src/BLSRegistryCoordinatorWithIndices.sol b/src/BLSRegistryCoordinatorWithIndices.sol index 1497eea9..25e65f20 100644 --- a/src/BLSRegistryCoordinatorWithIndices.sol +++ b/src/BLSRegistryCoordinatorWithIndices.sol @@ -32,7 +32,7 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr /// @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)"); + keccak256("OperatorChurnApproval(bytes32 registeringOperatorId,OperatorKickParam[] operatorKickParams)OperatorKickParam(address operator,bytes32[] operatorIdsToSwap)"); /// @notice The maximum value of a quorum bitmap uint256 internal constant MAX_QUORUM_BITMAP = type(uint192).max; /// @notice The basis point denominator @@ -142,18 +142,15 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr /** * @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 registerOperator( bytes calldata quorumNumbers, - BN254.G1Point memory pubkey, string calldata socket ) external onlyWhenNotPaused(PAUSED_REGISTER_OPERATOR) { - uint32[] memory numOperatorsPerQuorum = _registerOperator({ + (uint32[] memory numOperatorsPerQuorum, ) = _registerOperator({ operator: msg.sender, quorumNumbers: quorumNumbers, - pubkey: pubkey, socket: socket }); @@ -169,30 +166,27 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr * @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, + * quorum that will be filled after the operator registers. These parameters should include an operator * and ids of the operators to swap with the kicked operator. * @param churnApproverSignature is the signature of the churnApprover on the operator kick params */ function registerOperatorWithChurn( bytes calldata quorumNumbers, - BN254.G1Point memory pubkey, string calldata socket, OperatorKickParam[] calldata operatorKickParams, SignatureWithSaltAndExpiry memory churnApproverSignature ) external onlyWhenNotPaused(PAUSED_REGISTER_OPERATOR) { // register the operator - uint32[] memory numOperatorsPerQuorum = _registerOperator({ + (uint32[] memory numOperatorsPerQuorum, bytes32 operatorId) = _registerOperator({ operator: msg.sender, quorumNumbers: quorumNumbers, - pubkey: pubkey, socket: 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(); + operatorIdsToSwap[0] = operatorId; // verify the churnApprover's signature _verifyChurnApproverSignature({ @@ -244,8 +238,7 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr // kick the operator _deregisterOperator({ operator: operatorKickParams[i].operator, - quorumNumbers: quorumNumbers[i:i+1], - pubkey: operatorKickParams[i].pubkey + quorumNumbers: quorumNumbers[i:i+1] }); } } @@ -253,16 +246,13 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr /** * @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 */ function deregisterOperator( - bytes calldata quorumNumbers, - BN254.G1Point memory pubkey + bytes calldata quorumNumbers ) external onlyWhenNotPaused(PAUSED_DEREGISTER_OPERATOR) { _deregisterOperator({ operator: msg.sender, - quorumNumbers: quorumNumbers, - pubkey: pubkey + quorumNumbers: quorumNumbers }); } @@ -283,17 +273,14 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr * @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 */ function ejectOperator( address operator, - bytes calldata quorumNumbers, - BN254.G1Point memory pubkey + bytes calldata quorumNumbers ) external onlyEjector { _deregisterOperator({ operator: operator, - quorumNumbers: quorumNumbers, - pubkey: pubkey + quorumNumbers: quorumNumbers }); } @@ -350,10 +337,9 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr /// @return numOperatorsPerQuorum is the list of number of operators per quorum in quorumNumberss function _registerOperator( address operator, - bytes calldata quorumNumbers, - BN254.G1Point memory pubkey, + bytes calldata quorumNumbers, string memory socket - ) internal virtual returns(uint32[] memory) { + ) internal virtual returns(uint32[] memory, bytes32) { // Create and validate bitmap from quorumNumbers uint256 quorumBitmap = BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers); require(quorumBitmap <= MAX_QUORUM_BITMAP, "BLSRegistryCoordinatorWithIndices._registerOperator: bitmap exceeds max bitmap size"); @@ -361,10 +347,10 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr /** * Register the operator with the BLSPubkeyRegistry, StakeRegistry, and IndexRegistry. Retrieves: - * - operatorId: hash of the operator's pubkey, unique to the operator + * - operatorId: hash of the operator's public key, unique to the operator * - numOperatorsPerQuorum: list of # operators for each quorum in `quorumNumbers` */ - bytes32 operatorId = blsPubkeyRegistry.registerOperator(operator, quorumNumbers, pubkey); + bytes32 operatorId = blsPubkeyRegistry.registerOperator(operator, quorumNumbers); stakeRegistry.registerOperator(operator, operatorId, quorumNumbers); uint32[] memory numOperatorsPerQuorum = indexRegistry.registerOperator(operatorId, quorumNumbers); @@ -401,28 +387,18 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr emit OperatorRegistered(operator, operatorId); } - // 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; + return (numOperatorsPerQuorum, operatorId); } function _deregisterOperator( address operator, - bytes calldata quorumNumbers, - BN254.G1Point memory pubkey + bytes calldata quorumNumbers ) internal virtual { - /** - * Fetch the operator's id and status. Check that: - * - the operator is currently registered - * - the operatorId matches the provided pubkey hash - */ + // Fetch the operator's info and ensure they are registered Operator storage operatorInfo = _operatorInfo[operator]; bytes32 operatorId = operatorInfo.operatorId; require(operatorInfo.status == OperatorStatus.REGISTERED, "BLSRegistryCoordinatorWithIndices._deregisterOperator: operator is not registered"); - require(operatorId == pubkey.hashG1Point(), "BLSRegistryCoordinatorWithIndices._deregisterOperator: operatorId does not match pubkey hash"); // Create and validate bitmap of quorums to remove uint256 quorumsToRemoveBitmap = BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers); @@ -444,7 +420,7 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr bool completeDeregistration = previousBitmap == quorumsToRemoveBitmap; // Deregister operator with each of the registry contracts: - blsPubkeyRegistry.deregisterOperator(operator, quorumNumbersToRemove, pubkey); + blsPubkeyRegistry.deregisterOperator(operator, quorumNumbersToRemove); stakeRegistry.deregisterOperator(operatorId, quorumNumbersToRemove); indexRegistry.deregisterOperator(operatorId, quorumNumbersToRemove); diff --git a/src/interfaces/IBLSPubkeyRegistry.sol b/src/interfaces/IBLSPubkeyRegistry.sol index 08d1f380..c3379cc3 100644 --- a/src/interfaces/IBLSPubkeyRegistry.sol +++ b/src/interfaces/IBLSPubkeyRegistry.sol @@ -36,7 +36,6 @@ interface IBLSPubkeyRegistry is IRegistry { * @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 @@ -44,13 +43,12 @@ interface IBLSPubkeyRegistry is IRegistry { * 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); + function registerOperator(address operator, bytes calldata quorumNumbers) 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 @@ -58,9 +56,8 @@ interface IBLSPubkeyRegistry is IRegistry { * 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; + function deregisterOperator(address operator, bytes calldata quorumNumbers) external; /** * @notice Initializes a new quorum by pushing its first apk update diff --git a/src/interfaces/IBLSPublicKeyCompendium.sol b/src/interfaces/IBLSPublicKeyCompendium.sol index 7d4ba58a..c6f18435 100644 --- a/src/interfaces/IBLSPublicKeyCompendium.sol +++ b/src/interfaces/IBLSPublicKeyCompendium.sol @@ -35,6 +35,12 @@ interface IBLSPublicKeyCompendium { */ function registerBLSPublicKey(BN254.G1Point memory signedMessageHash, BN254.G1Point memory pubkeyG1, BN254.G2Point memory pubkeyG2) external; + /** + * @notice Returns the pubkey of an operator, verifying that the pubkey and its hash are valid + * @dev Reverts if the operator has not registered a valid pubkey + */ + function getRegisteredPubkey(address operator) external view returns (BN254.G1Point memory); + /** * @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 diff --git a/src/interfaces/IBLSRegistryCoordinatorWithIndices.sol b/src/interfaces/IBLSRegistryCoordinatorWithIndices.sol index b91d91bb..6966af04 100644 --- a/src/interfaces/IBLSRegistryCoordinatorWithIndices.sol +++ b/src/interfaces/IBLSRegistryCoordinatorWithIndices.sol @@ -29,12 +29,11 @@ interface IBLSRegistryCoordinatorWithIndices is ISignatureUtils, IRegistryCoordi /** * @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, + * `operator` is the address of the operator to kick */ struct OperatorKickParam { uint8 quorumNumber; address operator; - BN254.G1Point pubkey; } // EVENTS @@ -58,11 +57,9 @@ interface IBLSRegistryCoordinatorWithIndices is ISignatureUtils, IRegistryCoordi * @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 */ function ejectOperator( address operator, - bytes calldata quorumNumbers, - BN254.G1Point memory pubkey + bytes calldata quorumNumbers ) external; } diff --git a/test/ffi/BLSPubKeyCompendiumFFI.t.sol b/test/ffi/BLSPubKeyCompendiumFFI.t.sol index 501ba3bf..0fd9b4d4 100644 --- a/test/ffi/BLSPubKeyCompendiumFFI.t.sol +++ b/test/ffi/BLSPubKeyCompendiumFFI.t.sol @@ -8,6 +8,8 @@ contract BLSPublicKeyCompendiumFFITests is G2Operations { using BN254 for BN254.G1Point; using Strings for uint256; + Vm cheats = Vm(HEVM_ADDRESS); + BLSPublicKeyCompendium compendium; uint256 privKey; @@ -22,6 +24,7 @@ contract BLSPublicKeyCompendiumFFITests is G2Operations { } function testRegisterBLSPublicKey(uint256 _privKey) public { + cheats.assume(_privKey != 0); _setKeys(_privKey); signedMessageHash = _signMessage(alice); diff --git a/test/mocks/BLSPublicKeyCompendiumMock.sol b/test/mocks/BLSPublicKeyCompendiumMock.sol index 63e3088d..f2dd9caa 100644 --- a/test/mocks/BLSPublicKeyCompendiumMock.sol +++ b/test/mocks/BLSPublicKeyCompendiumMock.sol @@ -10,10 +10,15 @@ import "../../src/libraries/BN254.sol"; */ contract BLSPublicKeyCompendiumMock is IBLSPublicKeyCompendium{ - /// @notice mapping from operator address to pubkey hash + /// @notice the hash of the zero pubkey aka BN254.G1Point(0,0) + bytes32 internal constant ZERO_PK_HASH = hex"ad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5"; + + /// @notice maps operator address to pubkey hash mapping(address => bytes32) public operatorToPubkeyHash; - /// @notice mapping from pubkey hash to operator address + /// @notice maps pubkey hash to operator address mapping(bytes32 => address) public pubkeyHashToOperator; + /// @notice maps operator address to pubkeyG1 + mapping(address => BN254.G1Point) public operatorToPubkey; /** * @notice Called by an operator to register themselves as the owner of a BLS public key and reveal their G1 and G2 public key. @@ -30,6 +35,7 @@ contract BLSPublicKeyCompendiumMock is IBLSPublicKeyCompendium{ // store updates operatorToPubkeyHash[msg.sender] = pubkeyHash; pubkeyHashToOperator[pubkeyHash] = msg.sender; + operatorToPubkey[msg.sender] = pk; } function setBLSPublicKey(address account, BN254.G1Point memory pk) external { @@ -38,6 +44,21 @@ contract BLSPublicKeyCompendiumMock is IBLSPublicKeyCompendium{ // store updates operatorToPubkeyHash[account] = pubkeyHash; pubkeyHashToOperator[pubkeyHash] = account; + operatorToPubkey[account] = pk; + } + + function getRegisteredPubkey(address operator) public view returns (BN254.G1Point memory) { + BN254.G1Point memory pubkey = operatorToPubkey[operator]; + bytes32 pubkeyHash = operatorToPubkeyHash[operator]; + + require( + pubkeyHash != bytes32(0) && BN254.hashG1Point(pubkey) == pubkeyHash, + "BLSPublicKeyCompendium.getRegisteredPubkey: operator is not registered" + ); + + require(pubkeyHash != ZERO_PK_HASH, "BLSPublicKeyCompendium.getRegisteredPubkey: invalid pubkey"); + + return pubkey; } function getMessageHash(address operator) external view returns (BN254.G1Point memory) {} diff --git a/test/unit/BLSOperatorStateRetrieverUnit.t.sol b/test/unit/BLSOperatorStateRetrieverUnit.t.sol index b2cc0cc7..9ae96b7e 100644 --- a/test/unit/BLSOperatorStateRetrieverUnit.t.sol +++ b/test/unit/BLSOperatorStateRetrieverUnit.t.sol @@ -47,7 +47,7 @@ contract BLSOperatorStateRetrieverUnitTests is MockAVSDeployer { cheats.roll(deregistrationBlockNumber); cheats.prank(_incrementAddress(defaultOperator, operatorIndexToDeregister)); - registryCoordinator.deregisterOperator(quorumNumbersToDeregister, operatorMetadatas[operatorIndexToDeregister].pubkey); + registryCoordinator.deregisterOperator(quorumNumbersToDeregister); // 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]); diff --git a/test/unit/BLSPubkeyRegistryUnit.t.sol b/test/unit/BLSPubkeyRegistryUnit.t.sol index f803c73d..69f1a1be 100644 --- a/test/unit/BLSPubkeyRegistryUnit.t.sol +++ b/test/unit/BLSPubkeyRegistryUnit.t.sol @@ -48,7 +48,7 @@ contract BLSPubkeyRegistryUnitTests is Test { cheats.startPrank(nonCoordinatorAddress); cheats.expectRevert(bytes("BLSPubkeyRegistry.onlyRegistryCoordinator: caller is not the registry coordinator")); - blsPubkeyRegistry.registerOperator(nonCoordinatorAddress, new bytes(0), BN254.G1Point(0, 0)); + blsPubkeyRegistry.registerOperator(nonCoordinatorAddress, new bytes(0)); cheats.stopPrank(); } @@ -57,21 +57,14 @@ contract BLSPubkeyRegistryUnitTests is Test { cheats.startPrank(nonCoordinatorAddress); cheats.expectRevert(bytes("BLSPubkeyRegistry.onlyRegistryCoordinator: caller is not the registry coordinator")); - blsPubkeyRegistry.deregisterOperator(nonCoordinatorAddress, new bytes(0), BN254.G1Point(0, 0)); + blsPubkeyRegistry.deregisterOperator(nonCoordinatorAddress, new bytes(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.expectRevert(bytes("BLSPublicKeyCompendium.getRegisteredPubkey: operator is not registered")); + blsPubkeyRegistry.registerOperator(defaultOperator, new bytes(1)); cheats.stopPrank(); } @@ -89,7 +82,7 @@ contract BLSPubkeyRegistryUnitTests is Test { quorumNumbers[0] = bytes1(defaultQuorumNumber); cheats.startPrank(address(registryCoordinator)); - bytes32 registeredpkHash = blsPubkeyRegistry.registerOperator(operator, quorumNumbers, pubkey); + bytes32 registeredpkHash = blsPubkeyRegistry.registerOperator(operator, quorumNumbers); cheats.stopPrank(); @@ -121,7 +114,7 @@ contract BLSPubkeyRegistryUnitTests is Test { pkCompendium.registerPublicKey(defaultPubKey); cheats.prank(address(registryCoordinator)); - blsPubkeyRegistry.registerOperator(defaultOperator, quorumNumbers, defaultPubKey); + blsPubkeyRegistry.registerOperator(defaultOperator, quorumNumbers); //check quorum apk updates for(uint8 i = 0; i < quorumNumbers.length; i++){ @@ -147,7 +140,7 @@ contract BLSPubkeyRegistryUnitTests is Test { cheats.stopPrank(); cheats.startPrank(address(registryCoordinator)); - blsPubkeyRegistry.registerOperator(operator, quorumNumbers, negatedQuorumApk); + blsPubkeyRegistry.registerOperator(operator, quorumNumbers); cheats.stopPrank(); require(BN254.hashG1Point(blsPubkeyRegistry.getApkForQuorum(defaultQuorumNumber)) == ZERO_PK_HASH, "quorumApk not set correctly"); @@ -169,7 +162,7 @@ contract BLSPubkeyRegistryUnitTests is Test { } cheats.startPrank(address(registryCoordinator)); - blsPubkeyRegistry.deregisterOperator(defaultOperator, quorumNumbers, defaultPubKey); + blsPubkeyRegistry.deregisterOperator(defaultOperator, quorumNumbers); cheats.stopPrank(); @@ -194,7 +187,7 @@ contract BLSPubkeyRegistryUnitTests is Test { cheats.stopPrank(); cheats.prank(address(registryCoordinator)); - blsPubkeyRegistry.deregisterOperator(defaultOperator, quorumNumbers, quorumApksBefore); + blsPubkeyRegistry.deregisterOperator(defaultOperator, quorumNumbers); BN254.G1Point memory pk = blsPubkeyRegistry.getApkForQuorum(defaultQuorumNumber); require(pk.X == 0, "quorum apk not set to zero"); @@ -216,7 +209,7 @@ contract BLSPubkeyRegistryUnitTests is Test { assertEq(quorumApkHash, blsPubkeyRegistry.getApkHashForQuorumAtBlockNumberFromIndex(defaultQuorumNumber, uint32(block.number + blockGap), historyLength-1), "incorrect quorum apk update"); cheats.roll(block.number + 100); if(_generateRandomNumber(i) % 2 == 0){ - _deregisterOperator(pk); + _deregisterOperator(); quorumApk = quorumApk.plus(BN254.hashToG1(pk).negate()); quorumApkHash = bytes24(BN254.hashG1Point(quorumApk)); historyLength = blsPubkeyRegistry.getQuorumApkHistoryLength(defaultQuorumNumber); @@ -276,11 +269,11 @@ contract BLSPubkeyRegistryUnitTests is Test { return (randomNumber % 100) + 1; } - function _deregisterOperator(bytes32 pk) internal { + function _deregisterOperator() internal { bytes memory quorumNumbers = new bytes(1); quorumNumbers[0] = bytes1(defaultQuorumNumber); cheats.startPrank(address(registryCoordinator)); - blsPubkeyRegistry.deregisterOperator(defaultOperator, quorumNumbers, BN254.hashToG1(pk)); + blsPubkeyRegistry.deregisterOperator(defaultOperator, quorumNumbers); cheats.stopPrank(); } diff --git a/test/unit/BLSRegistryCoordinatorWithIndicesUnit.t.sol b/test/unit/BLSRegistryCoordinatorWithIndicesUnit.t.sol index 76d5d7e1..c9710fdc 100644 --- a/test/unit/BLSRegistryCoordinatorWithIndicesUnit.t.sol +++ b/test/unit/BLSRegistryCoordinatorWithIndicesUnit.t.sol @@ -121,21 +121,21 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { cheats.startPrank(defaultOperator); cheats.expectRevert(bytes("Pausable: index is paused")); - registryCoordinator.registerOperator(emptyQuorumNumbers, defaultPubKey, defaultSocket); + registryCoordinator.registerOperator(emptyQuorumNumbers, defaultSocket); } function testRegisterOperatorWithCoordinator_EmptyQuorumNumbers_Reverts() public { bytes memory emptyQuorumNumbers = new bytes(0); cheats.expectRevert("BLSRegistryCoordinatorWithIndices._registerOperator: bitmap cannot be 0"); cheats.prank(defaultOperator); - registryCoordinator.registerOperator(emptyQuorumNumbers, defaultPubKey, defaultSocket); + registryCoordinator.registerOperator(emptyQuorumNumbers, defaultSocket); } function testRegisterOperatorWithCoordinator_QuorumNumbersTooLarge_Reverts() public { bytes memory quorumNumbersTooLarge = new bytes(1); quorumNumbersTooLarge[0] = 0xC0; cheats.expectRevert("BLSRegistryCoordinatorWithIndices._registerOperator: bitmap exceeds max bitmap size"); - registryCoordinator.registerOperator(quorumNumbersTooLarge, defaultPubKey, defaultSocket); + registryCoordinator.registerOperator(quorumNumbersTooLarge, defaultSocket); } function testRegisterOperatorWithCoordinator_QuorumNotCreated_Reverts() public { @@ -144,7 +144,7 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { quorumNumbersNotCreated[0] = 0x0B; cheats.prank(defaultOperator); cheats.expectRevert("BLSPubkeyRegistry._processQuorumApkUpdate: quorum does not exist"); - registryCoordinator.registerOperator(quorumNumbersNotCreated, defaultPubKey, defaultSocket); + registryCoordinator.registerOperator(quorumNumbersNotCreated, defaultSocket); } function testRegisterOperatorWithCoordinatorForSingleQuorum_Valid() public { @@ -164,7 +164,7 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { emit OperatorSocketUpdate(defaultOperatorId, defaultSocket); uint256 gasBefore = gasleft(); - registryCoordinator.registerOperator(quorumNumbers, defaultPubKey, defaultSocket); + registryCoordinator.registerOperator(quorumNumbers, defaultSocket); uint256 gasAfter = gasleft(); emit log_named_uint("gasUsed", gasBefore - gasAfter); @@ -215,7 +215,7 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { emit OperatorSocketUpdate(defaultOperatorId, defaultSocket); uint256 gasBefore = gasleft(); - registryCoordinator.registerOperator(quorumNumbers, defaultPubKey, defaultSocket); + registryCoordinator.registerOperator(quorumNumbers, defaultSocket); uint256 gasAfter = gasleft(); emit log_named_uint("gasUsed", gasBefore - gasAfter); emit log_named_uint("numQuorums", quorumNumbers.length); @@ -249,7 +249,7 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { stakeRegistry.setOperatorWeight(uint8(quorumNumbers[0]), defaultOperator, defaultStake); cheats.prank(defaultOperator); cheats.roll(registrationBlockNumber); - registryCoordinator.registerOperator(quorumNumbers, defaultPubKey, defaultSocket); + registryCoordinator.registerOperator(quorumNumbers, defaultSocket); bytes memory newQuorumNumbers = new bytes(1); newQuorumNumbers[0] = bytes1(defaultQuorumNumber+1); @@ -265,7 +265,7 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { cheats.expectEmit(true, true, true, true, address(registryCoordinator)); emit OperatorSocketUpdate(defaultOperatorId, defaultSocket); cheats.roll(nextRegistrationBlockNumber); - registryCoordinator.registerOperator(newQuorumNumbers, defaultPubKey, defaultSocket); + registryCoordinator.registerOperator(newQuorumNumbers, defaultSocket); uint256 quorumBitmap = BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers) | BitmapUtils.orderedBytesArrayToBitmap(newQuorumNumbers); @@ -323,7 +323,7 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { cheats.prank(operatorToRegister); cheats.expectRevert("BLSRegistryCoordinatorWithIndices.registerOperator: quorum is overfilled"); - registryCoordinator.registerOperator(quorumNumbers, operatorToRegisterPubKey, defaultSocket); + registryCoordinator.registerOperator(quorumNumbers, defaultSocket); } function testRegisterOperatorWithCoordinator_RegisteredOperatorForSameQuorums_Reverts() public { @@ -336,12 +336,12 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { stakeRegistry.setOperatorWeight(uint8(quorumNumbers[0]), defaultOperator, defaultStake); cheats.prank(defaultOperator); cheats.roll(registrationBlockNumber); - registryCoordinator.registerOperator(quorumNumbers, defaultPubKey, defaultSocket); + registryCoordinator.registerOperator(quorumNumbers, defaultSocket); cheats.prank(defaultOperator); cheats.roll(nextRegistrationBlockNumber); cheats.expectRevert("BLSRegistryCoordinatorWithIndices._registerOperator: operator already registered for some quorums being registered for"); - registryCoordinator.registerOperator(quorumNumbers, defaultPubKey, defaultSocket); + registryCoordinator.registerOperator(quorumNumbers, defaultSocket); } function testDeregisterOperatorWithCoordinator_WhenPaused_Reverts() public { @@ -357,7 +357,7 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { cheats.expectRevert(bytes("Pausable: index is paused")); cheats.prank(defaultOperator); - registryCoordinator.deregisterOperator(quorumNumbers, defaultPubKey); + registryCoordinator.deregisterOperator(quorumNumbers); } function testDeregisterOperatorWithCoordinator_NotRegistered_Reverts() public { @@ -366,21 +366,7 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { cheats.expectRevert("BLSRegistryCoordinatorWithIndices._deregisterOperator: operator is not registered"); cheats.prank(defaultOperator); - registryCoordinator.deregisterOperator(quorumNumbers, defaultPubKey); - } - - 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._deregisterOperator: operatorId does not match pubkey hash"); - cheats.prank(defaultOperator); - registryCoordinator.deregisterOperator(quorumNumbers, incorrectPubKey); + registryCoordinator.deregisterOperator(quorumNumbers); } function testDeregisterOperatorWithCoordinator_IncorrectQuorums_Reverts() public { @@ -396,7 +382,7 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { cheats.expectRevert("BLSRegistryCoordinatorWithIndices._deregisterOperator: operator is not registered for any of the provided quorums"); cheats.prank(defaultOperator); - registryCoordinator.deregisterOperator(quorumNumbers, defaultPubKey); + registryCoordinator.deregisterOperator(quorumNumbers); } function testDeregisterOperatorWithCoordinatorForSingleQuorumAndSingleOperator_Valid() public { @@ -412,7 +398,7 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { cheats.roll(registrationBlockNumber); - registryCoordinator.registerOperator(quorumNumbers, defaultPubKey, defaultSocket); + registryCoordinator.registerOperator(quorumNumbers, defaultSocket); uint256 quorumBitmap = BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers); @@ -424,7 +410,7 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { cheats.roll(deregistrationBlockNumber); uint256 gasBefore = gasleft(); - registryCoordinator.deregisterOperator(quorumNumbers, defaultPubKey); + registryCoordinator.deregisterOperator(quorumNumbers); uint256 gasAfter = gasleft(); emit log_named_uint("gasUsed", gasBefore - gasAfter); @@ -462,7 +448,7 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { cheats.roll(registrationBlockNumber); - registryCoordinator.registerOperator(quorumNumbers, defaultPubKey, defaultSocket); + registryCoordinator.registerOperator(quorumNumbers, defaultSocket); cheats.expectEmit(true, true, true, true, address(blsPubkeyRegistry)); emit OperatorRemovedFromQuorums(defaultOperator, quorumNumbers); @@ -474,7 +460,7 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { cheats.roll(deregistrationBlockNumber); uint256 gasBefore = gasleft(); - registryCoordinator.deregisterOperator(quorumNumbers, defaultPubKey); + registryCoordinator.deregisterOperator(quorumNumbers); uint256 gasAfter = gasleft(); emit log_named_uint("gasUsed", gasBefore - gasAfter); emit log_named_uint("numQuorums", quorumNumbers.length); @@ -551,7 +537,7 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { cheats.roll(deregistrationBlockNumber); cheats.prank(operatorToDerigister); - registryCoordinator.deregisterOperator(operatorToDeregisterQuorumNumbers, operatorToDeregisterPubKey); + registryCoordinator.deregisterOperator(operatorToDeregisterQuorumNumbers); assertEq( keccak256(abi.encode(registryCoordinator.getOperator(operatorToDerigister))), @@ -589,7 +575,7 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0); // re-register the operator - registryCoordinator.registerOperator(quorumNumbers, defaultPubKey, defaultSocket); + registryCoordinator.registerOperator(quorumNumbers, defaultSocket); // check success of registration uint256 quorumBitmap = BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers); @@ -658,8 +644,7 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { operatorKickParams[0] = IBLSRegistryCoordinatorWithIndices.OperatorKickParam({ quorumNumber: defaultQuorumNumber, - operator: operatorToKick, - pubkey: pubKey + operator: operatorToKick }); } @@ -689,7 +674,6 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { uint256 gasBefore = gasleft(); registryCoordinator.registerOperatorWithChurn( quorumNumbers, - operatorToRegisterPubKey, defaultSocket, operatorKickParams, signatureWithExpiry @@ -739,7 +723,7 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { ISignatureUtils.SignatureWithSaltAndExpiry memory signatureWithExpiry = _signOperatorChurnApproval(operatorToRegisterId, operatorKickParams, defaultSalt, block.timestamp + 10); cheats.prank(operatorToRegister); cheats.expectRevert("BLSRegistryCoordinatorWithIndices.registerOperatorWithChurn: registering operator has less than kickBIPsOfOperatorStake"); - registryCoordinator.registerOperatorWithChurn(quorumNumbers, operatorToRegisterPubKey, defaultSocket, operatorKickParams, signatureWithExpiry); + registryCoordinator.registerOperatorWithChurn(quorumNumbers, defaultSocket, operatorKickParams, signatureWithExpiry); } function testRegisterOperatorWithCoordinatorWithKicks_LessThanKickBIPsOfTotalStake_Reverts(uint256 pseudoRandomNumber) public { @@ -762,7 +746,7 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { ISignatureUtils.SignatureWithSaltAndExpiry memory signatureWithExpiry = _signOperatorChurnApproval(operatorToRegisterId, operatorKickParams, defaultSalt, block.timestamp + 10); cheats.prank(operatorToRegister); cheats.expectRevert("BLSRegistryCoordinatorWithIndices.registerOperatorWithChurn: operator to kick has more than kickBIPSOfTotalStake"); - registryCoordinator.registerOperatorWithChurn(quorumNumbers, operatorToRegisterPubKey, defaultSocket, operatorKickParams, signatureWithExpiry); + registryCoordinator.registerOperatorWithChurn(quorumNumbers, defaultSocket, operatorKickParams, signatureWithExpiry); } function testRegisterOperatorWithCoordinatorWithKicks_InvalidSignatures_Reverts(uint256 pseudoRandomNumber) public { @@ -771,7 +755,7 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { ( address operatorToRegister, - BN254.G1Point memory operatorToRegisterPubKey, + , IBLSRegistryCoordinatorWithIndices.OperatorKickParam[] memory operatorKickParams ) = _testRegisterOperatorWithKicks_SetUp(pseudoRandomNumber, quorumNumbers, defaultStake); @@ -785,7 +769,7 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { signatureWithSaltAndExpiry.salt = defaultSalt; cheats.prank(operatorToRegister); cheats.expectRevert("ECDSA: invalid signature"); - registryCoordinator.registerOperatorWithChurn(quorumNumbers, operatorToRegisterPubKey, defaultSocket, operatorKickParams, signatureWithSaltAndExpiry); + registryCoordinator.registerOperatorWithChurn(quorumNumbers, defaultSocket, operatorKickParams, signatureWithSaltAndExpiry); } function testRegisterOperatorWithCoordinatorWithKicks_ExpiredSignatures_Reverts(uint256 pseudoRandomNumber) public { @@ -806,7 +790,7 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { ISignatureUtils.SignatureWithSaltAndExpiry memory signatureWithSaltAndExpiry = _signOperatorChurnApproval(operatorToRegisterId, operatorKickParams, defaultSalt, block.timestamp - 1); cheats.prank(operatorToRegister); cheats.expectRevert("BLSRegistryCoordinatorWithIndices._verifyChurnApproverSignature: churnApprover signature expired"); - registryCoordinator.registerOperatorWithChurn(quorumNumbers, operatorToRegisterPubKey, defaultSocket, operatorKickParams, signatureWithSaltAndExpiry); + registryCoordinator.registerOperatorWithChurn(quorumNumbers, defaultSocket, operatorKickParams, signatureWithSaltAndExpiry); } function testEjectOperatorFromCoordinator_AllQuorums_Valid() public { @@ -817,7 +801,7 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { stakeRegistry.setOperatorWeight(uint8(quorumNumbers[0]), defaultOperator, defaultStake); cheats.prank(defaultOperator); - registryCoordinator.registerOperator(quorumNumbers, defaultPubKey, defaultSocket); + registryCoordinator.registerOperator(quorumNumbers, defaultSocket); cheats.expectEmit(true, true, true, true, address(blsPubkeyRegistry)); emit OperatorRemovedFromQuorums(defaultOperator, quorumNumbers); @@ -827,7 +811,7 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { // eject cheats.prank(ejector); - registryCoordinator.ejectOperator(defaultOperator, quorumNumbers, defaultPubKey); + registryCoordinator.ejectOperator(defaultOperator, quorumNumbers); // make sure the operator is deregistered assertEq( @@ -852,7 +836,7 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { } cheats.prank(defaultOperator); - registryCoordinator.registerOperator(quorumNumbers, defaultPubKey, defaultSocket); + registryCoordinator.registerOperator(quorumNumbers, defaultSocket); // eject from only first quorum bytes memory quorumNumbersToEject = new bytes(1); @@ -865,7 +849,7 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { emit StakeUpdate(defaultOperatorId, uint8(quorumNumbersToEject[0]), 0); cheats.prank(ejector); - registryCoordinator.ejectOperator(defaultOperator, quorumNumbersToEject, defaultPubKey); + registryCoordinator.ejectOperator(defaultOperator, quorumNumbersToEject); // make sure the operator is registered assertEq( @@ -889,11 +873,11 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { stakeRegistry.setOperatorWeight(uint8(quorumNumbers[0]), defaultOperator, defaultStake); cheats.prank(defaultOperator); - registryCoordinator.registerOperator(quorumNumbers, defaultPubKey, defaultSocket); + registryCoordinator.registerOperator(quorumNumbers, defaultSocket); cheats.expectRevert("BLSRegistryCoordinatorWithIndices.onlyEjector: caller is not the ejector"); cheats.prank(defaultOperator); - registryCoordinator.ejectOperator(defaultOperator, quorumNumbers, defaultPubKey); + registryCoordinator.ejectOperator(defaultOperator, quorumNumbers); } function testUpdateSocket() public { @@ -952,8 +936,7 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { operatorKickParams[0] = IBLSRegistryCoordinatorWithIndices.OperatorKickParam({ quorumNumber: uint8(quorumNumbers[0]), - operator: operatorToKick, - pubkey: pubKey + operator: operatorToKick }); } diff --git a/test/utils/MockAVSDeployer.sol b/test/utils/MockAVSDeployer.sol index c12e9a64..e0c8573f 100644 --- a/test/utils/MockAVSDeployer.sol +++ b/test/utils/MockAVSDeployer.sol @@ -322,7 +322,7 @@ contract MockAVSDeployer is Test { } cheats.prank(operator); - registryCoordinator.registerOperator(quorumNumbers, pubKey, defaultSocket); + registryCoordinator.registerOperator(quorumNumbers, defaultSocket); } /** @@ -340,7 +340,7 @@ contract MockAVSDeployer is Test { } cheats.prank(operator); - registryCoordinator.registerOperator(quorumNumbers, pubKey, defaultSocket); + registryCoordinator.registerOperator(quorumNumbers, defaultSocket); } function _registerRandomOperators(uint256 pseudoRandomNumber) internal returns(OperatorMetadata[] memory, uint256[][] memory) { From e2eb6d6dc14b6954d47fef71359cacfbc25cfe7d Mon Sep 17 00:00:00 2001 From: wadealexc Date: Thu, 2 Nov 2023 19:17:34 +0000 Subject: [PATCH 034/101] style: shorten state variable and function names to be more consistent with other registry contracts --- src/BLSOperatorStateRetriever.sol | 2 +- src/BLSPubkeyRegistry.sol | 48 ++++++++++++------------- src/BLSPubkeyRegistryStorage.sol | 8 ++--- src/BLSSignatureChecker.sol | 2 +- src/interfaces/IBLSPubkeyRegistry.sol | 8 ++--- test/unit/BLSPubkeyRegistryUnit.t.sol | 32 ++++++++--------- test/unit/BLSSignatureCheckerUnit.t.sol | 2 +- 7 files changed, 51 insertions(+), 51 deletions(-) diff --git a/src/BLSOperatorStateRetriever.sol b/src/BLSOperatorStateRetriever.sol index b1c9f194..362abc38 100644 --- a/src/BLSOperatorStateRetriever.sol +++ b/src/BLSOperatorStateRetriever.sol @@ -150,7 +150,7 @@ contract BLSOperatorStateRetriever { 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); + checkSignaturesIndices.quorumApkIndices = blsPubkeyRegistry.getApkIndicesAtBlockNumber(quorumNumbers, referenceBlockNumber); return checkSignaturesIndices; } diff --git a/src/BLSPubkeyRegistry.sol b/src/BLSPubkeyRegistry.sol index 160bd1b1..30cc3286 100644 --- a/src/BLSPubkeyRegistry.sol +++ b/src/BLSPubkeyRegistry.sol @@ -83,9 +83,9 @@ contract BLSPubkeyRegistry is BLSPubkeyRegistryStorage { * @param quorumNumber The number of the new quorum */ function initializeQuorum(uint8 quorumNumber) public virtual onlyRegistryCoordinator { - require(quorumApkUpdates[quorumNumber].length == 0, "BLSPubkeyRegistry.createQuorum: quorum already exists"); + require(apkHistory[quorumNumber].length == 0, "BLSPubkeyRegistry.initializeQuorum: quorum already exists"); - quorumApkUpdates[quorumNumber].push(ApkUpdate({ + apkHistory[quorumNumber].push(ApkUpdate({ apkHash: bytes24(0), updateBlockNumber: uint32(block.number), nextUpdateBlockNumber: 0 @@ -102,22 +102,22 @@ contract BLSPubkeyRegistry is BLSPubkeyRegistryStorage { for (uint256 i = 0; i < quorumNumbers.length; i++) { // Validate quorum exists and get history length uint8 quorumNumber = uint8(quorumNumbers[i]); - uint256 historyLength = quorumApkUpdates[quorumNumber].length; + uint256 historyLength = apkHistory[quorumNumber].length; require(historyLength != 0, "BLSPubkeyRegistry._processQuorumApkUpdate: quorum does not exist"); // Update aggregate public key for this quorum - newApk = quorumApk[quorumNumber].plus(point); - quorumApk[quorumNumber] = newApk; + newApk = currentApk[quorumNumber].plus(point); + currentApk[quorumNumber] = newApk; bytes24 newApkHash = bytes24(BN254.hashG1Point(newApk)); // Update apk history. If the last update was made in this block, update the entry // Otherwise, push a new historical entry and update the prev->next pointer - ApkUpdate storage lastUpdate = quorumApkUpdates[quorumNumber][historyLength - 1]; + ApkUpdate storage lastUpdate = apkHistory[quorumNumber][historyLength - 1]; if (lastUpdate.updateBlockNumber == uint32(block.number)) { lastUpdate.apkHash = newApkHash; } else { lastUpdate.nextUpdateBlockNumber = uint32(block.number); - quorumApkUpdates[quorumNumber].push(ApkUpdate({ + apkHistory[quorumNumber].push(ApkUpdate({ apkHash: newApkHash, updateBlockNumber: uint32(block.number), nextUpdateBlockNumber: 0 @@ -127,10 +127,10 @@ contract BLSPubkeyRegistry is BLSPubkeyRegistryStorage { } // TODO - should this fail if apkUpdate.apkHash == 0? This will be the case for the first entry in each quorum - function _validateApkHashForQuorumAtBlockNumber(ApkUpdate memory apkUpdate, uint32 blockNumber) internal pure { + function _validateApkHashAtBlockNumber(ApkUpdate memory apkUpdate, uint32 blockNumber) internal pure { require( blockNumber >= apkUpdate.updateBlockNumber, - "BLSPubkeyRegistry._validateApkHashForQuorumAtBlockNumber: index too recent" + "BLSPubkeyRegistry._validateApkHashAtBlockNumber: index too recent" ); /** * if there is a next update, check that the blockNumber is before the next update or if @@ -138,7 +138,7 @@ contract BLSPubkeyRegistry is BLSPubkeyRegistryStorage { */ require( apkUpdate.nextUpdateBlockNumber == 0 || blockNumber < apkUpdate.nextUpdateBlockNumber, - "BLSPubkeyRegistry._validateApkHashForQuorumAtBlockNumber: not latest apk update" + "BLSPubkeyRegistry._validateApkHashAtBlockNumber: not latest apk update" ); } @@ -150,23 +150,23 @@ contract BLSPubkeyRegistry is BLSPubkeyRegistryStorage { * @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( + function getApkIndicesAtBlockNumber( 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); + uint32 quorumApkUpdatesLength = uint32(apkHistory[quorumNumber].length); - if (quorumApkUpdatesLength == 0 || blockNumber < quorumApkUpdates[quorumNumber][0].updateBlockNumber) { + if (quorumApkUpdatesLength == 0 || blockNumber < apkHistory[quorumNumber][0].updateBlockNumber) { revert( - "BLSPubkeyRegistry.getApkIndicesForQuorumsAtBlockNumber: blockNumber is before the first update" + "BLSPubkeyRegistry.getApkIndicesAtBlockNumber: blockNumber is before the first update" ); } for (uint32 j = 0; j < quorumApkUpdatesLength; j++) { - if (quorumApkUpdates[quorumNumber][quorumApkUpdatesLength - j - 1].updateBlockNumber <= blockNumber) { + if (apkHistory[quorumNumber][quorumApkUpdatesLength - j - 1].updateBlockNumber <= blockNumber) { indices[i] = quorumApkUpdatesLength - j - 1; break; } @@ -176,13 +176,13 @@ contract BLSPubkeyRegistry is BLSPubkeyRegistryStorage { } /// @notice Returns the current APK for the provided `quorumNumber ` - function getApkForQuorum(uint8 quorumNumber) external view returns (BN254.G1Point memory) { - return quorumApk[quorumNumber]; + function getApk(uint8 quorumNumber) external view returns (BN254.G1Point memory) { + return currentApk[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]; + function getApkUpdateAtIndex(uint8 quorumNumber, uint256 index) external view returns (ApkUpdate memory) { + return apkHistory[quorumNumber][index]; } /** @@ -192,19 +192,19 @@ contract BLSPubkeyRegistry is BLSPubkeyRegistryStorage { * @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( + function getApkHashAtBlockNumberAndIndex( uint8 quorumNumber, uint32 blockNumber, uint256 index ) external view returns (bytes24) { - ApkUpdate memory quorumApkUpdate = quorumApkUpdates[quorumNumber][index]; - _validateApkHashForQuorumAtBlockNumber(quorumApkUpdate, blockNumber); + ApkUpdate memory quorumApkUpdate = apkHistory[quorumNumber][index]; + _validateApkHashAtBlockNumber(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); + function getApkHistoryLength(uint8 quorumNumber) external view returns (uint32) { + return uint32(apkHistory[quorumNumber].length); } /// @notice Returns the operator address for the given `pubkeyHash` diff --git a/src/BLSPubkeyRegistryStorage.sol b/src/BLSPubkeyRegistryStorage.sol index 5e42202a..42359387 100644 --- a/src/BLSPubkeyRegistryStorage.sol +++ b/src/BLSPubkeyRegistryStorage.sol @@ -15,10 +15,10 @@ abstract contract BLSPubkeyRegistryStorage is Initializable, IBLSPubkeyRegistry /// @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; + /// @notice maps quorumNumber => historical aggregate pubkey updates + mapping(uint8 => ApkUpdate[]) public apkHistory; + /// @notice maps quorumNumber => current aggregate pubkey of quorum + mapping(uint8 => BN254.G1Point) public currentApk; constructor(IRegistryCoordinator _registryCoordinator, IBLSPublicKeyCompendium _pubkeyCompendium) { registryCoordinator = _registryCoordinator; diff --git a/src/BLSSignatureChecker.sol b/src/BLSSignatureChecker.sol index f57b0f47..5c0b801c 100644 --- a/src/BLSSignatureChecker.sol +++ b/src/BLSSignatureChecker.sol @@ -75,7 +75,7 @@ contract BLSSignatureChecker is IBLSSignatureChecker { for (uint i = 0; i < quorumNumbers.length; i++) { require( bytes24(nonSignerStakesAndSignature.quorumApks[i].hashG1Point()) == - IBLSPubkeyRegistry(blsPubkeyRegistry).getApkHashForQuorumAtBlockNumberFromIndex( + IBLSPubkeyRegistry(blsPubkeyRegistry).getApkHashAtBlockNumberAndIndex( uint8(quorumNumbers[i]), referenceBlockNumber, nonSignerStakesAndSignature.quorumApkIndices[i] diff --git a/src/interfaces/IBLSPubkeyRegistry.sol b/src/interfaces/IBLSPubkeyRegistry.sol index c3379cc3..7ee95d1b 100644 --- a/src/interfaces/IBLSPubkeyRegistry.sol +++ b/src/interfaces/IBLSPubkeyRegistry.sol @@ -66,13 +66,13 @@ interface IBLSPubkeyRegistry is IRegistry { function initializeQuorum(uint8 quorumNumber) external; /// @notice Returns the current APK for the provided `quorumNumber ` - function getApkForQuorum(uint8 quorumNumber) external view returns (BN254.G1Point memory); + function getApk(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); + function getApkIndicesAtBlockNumber(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); + function getApkUpdateAtIndex(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); @@ -84,5 +84,5 @@ interface IBLSPubkeyRegistry is IRegistry { * @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); + function getApkHashAtBlockNumberAndIndex(uint8 quorumNumber, uint32 blockNumber, uint256 index) external view returns (bytes24); } diff --git a/test/unit/BLSPubkeyRegistryUnit.t.sol b/test/unit/BLSPubkeyRegistryUnit.t.sol index 69f1a1be..5c5c326f 100644 --- a/test/unit/BLSPubkeyRegistryUnit.t.sol +++ b/test/unit/BLSPubkeyRegistryUnit.t.sol @@ -107,7 +107,7 @@ contract BLSPubkeyRegistryUnitTests is Test { BN254.G1Point[] memory quorumApksBefore = new BN254.G1Point[](quorumNumbers.length); for(uint8 i = 0; i < quorumNumbers.length; i++){ - quorumApksBefore[i] = blsPubkeyRegistry.getApkForQuorum(uint8(quorumNumbers[i])); + quorumApksBefore[i] = blsPubkeyRegistry.getApk(uint8(quorumNumbers[i])); } cheats.prank(defaultOperator); @@ -118,7 +118,7 @@ contract BLSPubkeyRegistryUnitTests is Test { //check quorum apk updates for(uint8 i = 0; i < quorumNumbers.length; i++){ - BN254.G1Point memory quorumApkAfter = blsPubkeyRegistry.getApkForQuorum(uint8(quorumNumbers[i])); + BN254.G1Point memory quorumApkAfter = blsPubkeyRegistry.getApk(uint8(quorumNumbers[i])); bytes32 temp = BN254.hashG1Point(BN254.plus(quorumApkAfter, BN254.negate(quorumApksBefore[i]))); require(temp == BN254.hashG1Point(defaultPubKey), "quorum apk not updated correctly"); } @@ -127,7 +127,7 @@ contract BLSPubkeyRegistryUnitTests is Test { function testRegisterWithNegativeQuorumApk(address operator, bytes32 x) external { testRegisterOperatorBLSPubkey(defaultOperator, x); - BN254.G1Point memory quorumApk = blsPubkeyRegistry.getApkForQuorum(defaultQuorumNumber); + BN254.G1Point memory quorumApk = blsPubkeyRegistry.getApk(defaultQuorumNumber); BN254.G1Point memory negatedQuorumApk = BN254.negate(quorumApk); @@ -143,7 +143,7 @@ contract BLSPubkeyRegistryUnitTests is Test { blsPubkeyRegistry.registerOperator(operator, quorumNumbers); cheats.stopPrank(); - require(BN254.hashG1Point(blsPubkeyRegistry.getApkForQuorum(defaultQuorumNumber)) == ZERO_PK_HASH, "quorumApk not set correctly"); + require(BN254.hashG1Point(blsPubkeyRegistry.getApk(defaultQuorumNumber)) == ZERO_PK_HASH, "quorumApk not set correctly"); } function testQuorumApkUpdatesDeregistration(uint8 quorumNumber1, uint8 quorumNumber2) external { @@ -158,7 +158,7 @@ contract BLSPubkeyRegistryUnitTests is Test { BN254.G1Point[] memory quorumApksBefore = new BN254.G1Point[](2); for(uint8 i = 0; i < quorumNumbers.length; i++){ - quorumApksBefore[i] = blsPubkeyRegistry.getApkForQuorum(uint8(quorumNumbers[i])); + quorumApksBefore[i] = blsPubkeyRegistry.getApk(uint8(quorumNumbers[i])); } cheats.startPrank(address(registryCoordinator)); @@ -168,7 +168,7 @@ contract BLSPubkeyRegistryUnitTests is Test { BN254.G1Point memory quorumApkAfter; for(uint8 i = 0; i < quorumNumbers.length; i++){ - quorumApkAfter = blsPubkeyRegistry.getApkForQuorum(uint8(quorumNumbers[i])); + quorumApkAfter = blsPubkeyRegistry.getApk(uint8(quorumNumbers[i])); require(BN254.hashG1Point(quorumApksBefore[i].plus(defaultPubKey.negate())) == BN254.hashG1Point(quorumApkAfter), "quorum apk not updated correctly"); } } @@ -177,7 +177,7 @@ contract BLSPubkeyRegistryUnitTests is Test { testRegisterOperatorBLSPubkey(defaultOperator, x1); testRegisterOperatorBLSPubkey(defaultOperator2, x2); - BN254.G1Point memory quorumApksBefore= blsPubkeyRegistry.getApkForQuorum(defaultQuorumNumber); + BN254.G1Point memory quorumApksBefore= blsPubkeyRegistry.getApk(defaultQuorumNumber); bytes memory quorumNumbers = new bytes(1); quorumNumbers[0] = bytes1(defaultQuorumNumber); @@ -189,7 +189,7 @@ contract BLSPubkeyRegistryUnitTests is Test { cheats.prank(address(registryCoordinator)); blsPubkeyRegistry.deregisterOperator(defaultOperator, quorumNumbers); - BN254.G1Point memory pk = blsPubkeyRegistry.getApkForQuorum(defaultQuorumNumber); + BN254.G1Point memory pk = blsPubkeyRegistry.getApk(defaultQuorumNumber); require(pk.X == 0, "quorum apk not set to zero"); require(pk.Y == 0, "quorum apk not set to zero"); } @@ -205,15 +205,15 @@ contract BLSPubkeyRegistryUnitTests is Test { testRegisterOperatorBLSPubkey(defaultOperator, pk); quorumApk = quorumApk.plus(BN254.hashToG1(pk)); quorumApkHash = bytes24(BN254.hashG1Point(quorumApk)); - uint historyLength = blsPubkeyRegistry.getQuorumApkHistoryLength(defaultQuorumNumber); - assertEq(quorumApkHash, blsPubkeyRegistry.getApkHashForQuorumAtBlockNumberFromIndex(defaultQuorumNumber, uint32(block.number + blockGap), historyLength-1), "incorrect quorum apk update"); + uint historyLength = blsPubkeyRegistry.getApkHistoryLength(defaultQuorumNumber); + assertEq(quorumApkHash, blsPubkeyRegistry.getApkHashAtBlockNumberAndIndex(defaultQuorumNumber, uint32(block.number + blockGap), historyLength-1), "incorrect quorum apk update"); cheats.roll(block.number + 100); if(_generateRandomNumber(i) % 2 == 0){ _deregisterOperator(); quorumApk = quorumApk.plus(BN254.hashToG1(pk).negate()); quorumApkHash = bytes24(BN254.hashG1Point(quorumApk)); - historyLength = blsPubkeyRegistry.getQuorumApkHistoryLength(defaultQuorumNumber); - assertEq(quorumApkHash, blsPubkeyRegistry.getApkHashForQuorumAtBlockNumberFromIndex(defaultQuorumNumber, uint32(block.number + blockGap), historyLength-1), "incorrect quorum apk update"); + historyLength = blsPubkeyRegistry.getApkHistoryLength(defaultQuorumNumber); + assertEq(quorumApkHash, blsPubkeyRegistry.getApkHashAtBlockNumberAndIndex(defaultQuorumNumber, uint32(block.number + blockGap), historyLength-1), "incorrect quorum apk update"); cheats.roll(block.number + 100); i++; } @@ -234,13 +234,13 @@ contract BLSPubkeyRegistryUnitTests is Test { } if(wrongBlockNumber < startingBlockNumber + indexToCheck*100){ emit log_named_uint("index too recent: ", indexToCheck); - cheats.expectRevert(bytes("BLSPubkeyRegistry._validateApkHashForQuorumAtBlockNumber: index too recent")); - blsPubkeyRegistry.getApkHashForQuorumAtBlockNumberFromIndex(defaultQuorumNumber, wrongBlockNumber, indexToCheck); + cheats.expectRevert(bytes("BLSPubkeyRegistry._validateApkHashAtBlockNumber: index too recent")); + blsPubkeyRegistry.getApkHashAtBlockNumberAndIndex(defaultQuorumNumber, wrongBlockNumber, indexToCheck); } if (wrongBlockNumber >= startingBlockNumber + (indexToCheck+1)*100){ emit log_named_uint("index not latest: ", indexToCheck); - cheats.expectRevert(bytes("BLSPubkeyRegistry._validateApkHashForQuorumAtBlockNumber: not latest apk update")); - blsPubkeyRegistry.getApkHashForQuorumAtBlockNumberFromIndex(defaultQuorumNumber, wrongBlockNumber, indexToCheck); + cheats.expectRevert(bytes("BLSPubkeyRegistry._validateApkHashAtBlockNumber: not latest apk update")); + blsPubkeyRegistry.getApkHashAtBlockNumberAndIndex(defaultQuorumNumber, wrongBlockNumber, indexToCheck); } } diff --git a/test/unit/BLSSignatureCheckerUnit.t.sol b/test/unit/BLSSignatureCheckerUnit.t.sol index f6177979..aa8de5b7 100644 --- a/test/unit/BLSSignatureCheckerUnit.t.sol +++ b/test/unit/BLSSignatureCheckerUnit.t.sol @@ -161,7 +161,7 @@ contract BLSSignatureCheckerUnitTests is BLSMockAVSDeployer { // set the quorumApkIndices to a different value nonSignerStakesAndSignature.quorumApkIndices[0] = 0; - cheats.expectRevert("BLSPubkeyRegistry._validateApkHashForQuorumAtBlockNumber: not latest apk update"); + cheats.expectRevert("BLSPubkeyRegistry._validateApkHashAtBlockNumber: not latest apk update"); blsSignatureChecker.checkSignatures( msgHash, quorumNumbers, From 3f8458fb20ad6ecd9493e7c03ece9506c3d6de36 Mon Sep 17 00:00:00 2001 From: wadealexc Date: Mon, 6 Nov 2023 15:38:10 +0000 Subject: [PATCH 035/101] style: removed redundant check and swapped param order to be consistent with corresponding state lookup --- src/IndexRegistry.sol | 11 +++-------- src/interfaces/IIndexRegistry.sol | 2 +- test/unit/IndexRegistryUnit.t.sol | 8 ++++---- 3 files changed, 8 insertions(+), 13 deletions(-) diff --git a/src/IndexRegistry.sol b/src/IndexRegistry.sol index 7b75002d..571d8960 100644 --- a/src/IndexRegistry.sol +++ b/src/IndexRegistry.sol @@ -253,10 +253,6 @@ contract IndexRegistry is IndexRegistryStorage { ) internal view returns (uint32){ uint256 historyLength = _operatorCountHistory[quorumNumber].length; require(historyLength != 0, "IndexRegistry._operatorCountAtBlockNumber: quorum does not exist"); - require( - blockNumber >= _operatorCountHistory[quorumNumber][0].fromBlockNumber, - "IndexRegistry._operatorCountAtBlockNumber: quorum did not exist at given block number" - ); // Loop backwards through the total operator history for (uint256 i = 0; i < historyLength; i++) { @@ -268,7 +264,6 @@ contract IndexRegistry is IndexRegistryStorage { } } - // Shouldn't be able to reach this point revert("IndexRegistry._operatorCountAtBlockNumber: quorum did not exist at given block number"); } @@ -277,8 +272,8 @@ contract IndexRegistry is IndexRegistryStorage { * Precondition: requires that the index was used active at the given block number for quorum */ function _operatorIdForIndexAtBlockNumber( - uint32 index, uint8 quorumNumber, + uint32 index, uint32 blockNumber ) internal view returns(bytes32) { uint256 historyLength = _indexHistory[quorumNumber][index].length; @@ -302,7 +297,7 @@ contract IndexRegistry is IndexRegistryStorage { *******************************************************************************/ /// @notice Returns the _indexHistory entry for the specified `operatorIndex` and `quorumNumber` at the specified `index` - function getOperatorUpdateAtIndex(uint32 operatorIndex, uint8 quorumNumber, uint32 index) external view returns (OperatorUpdate memory) { + function getOperatorUpdateAtIndex(uint8 quorumNumber, uint32 operatorIndex, uint32 index) external view returns (OperatorUpdate memory) { return _indexHistory[quorumNumber][operatorIndex][index]; } @@ -331,7 +326,7 @@ contract IndexRegistry is IndexRegistryStorage { uint32 operatorCount = _operatorCountAtBlockNumber(quorumNumber, blockNumber); bytes32[] memory operatorList = new bytes32[](operatorCount); for (uint256 i = 0; i < operatorCount; i++) { - operatorList[i] = _operatorIdForIndexAtBlockNumber(uint32(i), quorumNumber, blockNumber); + operatorList[i] = _operatorIdForIndexAtBlockNumber(quorumNumber, uint32(i), blockNumber); require( operatorList[i] != OPERATOR_DOES_NOT_EXIST_ID, "IndexRegistry.getOperatorListAtBlockNumber: operator does not exist at the given block number" diff --git a/src/interfaces/IIndexRegistry.sol b/src/interfaces/IIndexRegistry.sol index 83c6b872..552c1a4d 100644 --- a/src/interfaces/IIndexRegistry.sol +++ b/src/interfaces/IIndexRegistry.sol @@ -67,7 +67,7 @@ interface IIndexRegistry is IRegistry { function initializeQuorum(uint8 quorumNumber) external; /// @notice Returns the _indexHistory entry for the specified `operatorIndex` and `quorumNumber` at the specified `index` - function getOperatorUpdateAtIndex(uint32 operatorIndex, uint8 quorumNumber, uint32 index) external view returns (OperatorUpdate memory); + function getOperatorUpdateAtIndex(uint8 quorumNumber, uint32 operatorIndex, uint32 index) external view returns (OperatorUpdate memory); /// @notice Returns the _operatorCountHistory entry for the specified `quorumNumber` at the specified `index` function getQuorumUpdateAtIndex(uint8 quorumNumber, uint32 index) external view returns (QuorumUpdate memory); diff --git a/test/unit/IndexRegistryUnit.t.sol b/test/unit/IndexRegistryUnit.t.sol index 85dd7f1a..6e9271fa 100644 --- a/test/unit/IndexRegistryUnit.t.sol +++ b/test/unit/IndexRegistryUnit.t.sol @@ -74,7 +74,7 @@ contract IndexRegistryUnitTests is Test { // Check _operatorIdToIndexHistory updates IIndexRegistry.OperatorUpdate memory operatorUpdate = indexRegistry - .getOperatorUpdateAtIndex({operatorIndex: 0, quorumNumber: defaultQuorumNumber, index: 0}); + .getOperatorUpdateAtIndex({quorumNumber: defaultQuorumNumber, operatorIndex: 0, index: 0}); require(operatorUpdate.operatorId == operatorId1, "IndexRegistry.registerOperator: operatorId not operatorId1"); require( operatorUpdate.fromBlockNumber == block.number, @@ -120,7 +120,7 @@ contract IndexRegistryUnitTests is Test { // Check _operatorIdToIndexHistory updates IIndexRegistry.OperatorUpdate memory operatorUpdate = indexRegistry - .getOperatorUpdateAtIndex({operatorIndex: 0, quorumNumber: defaultQuorumNumber + 1, index: 0}); + .getOperatorUpdateAtIndex({quorumNumber: defaultQuorumNumber + 1, operatorIndex: 0, index: 0}); require(operatorUpdate.operatorId == operatorId1, "IndexRegistry.registerOperator: operatorId not operatorId1"); require( operatorUpdate.fromBlockNumber == block.number, @@ -163,7 +163,7 @@ contract IndexRegistryUnitTests is Test { // Check _operatorIdToIndexHistory updates for quorum 1 IIndexRegistry.OperatorUpdate memory operatorUpdate = indexRegistry - .getOperatorUpdateAtIndex({operatorIndex: 0, quorumNumber: defaultQuorumNumber, index: 0}); + .getOperatorUpdateAtIndex({quorumNumber: defaultQuorumNumber, operatorIndex: 0, index: 0}); require(operatorUpdate.operatorId == operatorId1, "IndexRegistry.registerOperator: operatorId not 1operatorId1"); require( operatorUpdate.fromBlockNumber == block.number, @@ -187,7 +187,7 @@ contract IndexRegistryUnitTests is Test { ); // Check _operatorIdToIndexHistory updates for quorum 2 - operatorUpdate = indexRegistry.getOperatorUpdateAtIndex({operatorIndex: 0 , quorumNumber: defaultQuorumNumber + 1, index: 0}); + operatorUpdate = indexRegistry.getOperatorUpdateAtIndex({quorumNumber: defaultQuorumNumber + 1, operatorIndex: 0, index: 0}); require(operatorUpdate.operatorId == operatorId1, "IndexRegistry.registerOperator: operatorId not operatorId1"); require( operatorUpdate.fromBlockNumber == block.number, From 1d7b20170ce1796ed6521f7b5d600976b909f2ec Mon Sep 17 00:00:00 2001 From: wadealexc Date: Mon, 6 Nov 2023 15:50:45 +0000 Subject: [PATCH 036/101] style: remove redundant checks from pubkey compendium --- src/BLSPublicKeyCompendium.sol | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/BLSPublicKeyCompendium.sol b/src/BLSPublicKeyCompendium.sol index fc97f7f5..7c1d24ca 100644 --- a/src/BLSPublicKeyCompendium.sol +++ b/src/BLSPublicKeyCompendium.sol @@ -94,11 +94,9 @@ contract BLSPublicKeyCompendium is IBLSPublicKeyCompendium { bytes32 pubkeyHash = operatorToPubkeyHash[operator]; require( - pubkeyHash != bytes32(0) && BN254.hashG1Point(pubkey) == pubkeyHash, + pubkeyHash != bytes32(0), "BLSPublicKeyCompendium.getRegisteredPubkey: operator is not registered" ); - - require(pubkeyHash != ZERO_PK_HASH, "BLSPublicKeyCompendium.getRegisteredPubkey: invalid pubkey"); return pubkey; } From a54109e260d959a6dbc6be30b3feeda87aa6202c Mon Sep 17 00:00:00 2001 From: wadealexc Date: Tue, 7 Nov 2023 16:36:18 +0000 Subject: [PATCH 037/101] fix: replace borked state variable --- src/BLSRegistryCoordinatorWithIndices.sol | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/BLSRegistryCoordinatorWithIndices.sol b/src/BLSRegistryCoordinatorWithIndices.sol index 25e65f20..39c9ae6e 100644 --- a/src/BLSRegistryCoordinatorWithIndices.sol +++ b/src/BLSRegistryCoordinatorWithIndices.sol @@ -6,9 +6,7 @@ import "@openzeppelin-upgrades/contracts/proxy/utils/Initializable.sol"; import "eigenlayer-contracts/src/contracts/interfaces/IPauserRegistry.sol"; import "eigenlayer-contracts/src/contracts/interfaces/ISlasher.sol"; -import "src/contracts/libraries/BN254.sol"; import "eigenlayer-contracts/src/contracts/libraries/EIP1271SignatureUtils.sol"; -import "src/libraries/BitmapUtils.sol"; import "eigenlayer-contracts/src/contracts/permissions/Pausable.sol"; import "src/interfaces/IBLSRegistryCoordinatorWithIndices.sol"; @@ -19,6 +17,11 @@ import "src/interfaces/IStakeRegistry.sol"; import "src/interfaces/IIndexRegistry.sol"; import "src/interfaces/IRegistryCoordinator.sol"; +import "src/libraries/BitmapUtils.sol"; +import "src/libraries/BN254.sol"; + + + /** * @title A `RegistryCoordinator` that has three registries: * 1) a `StakeRegistry` that keeps track of operators' stakes @@ -215,7 +218,7 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr // get the total stake for the quorum uint96 totalStakeForQuorum = stakeRegistry.getCurrentTotalStakeForQuorum(quorumNumber); - bytes32 operatorToKickId = _operatorInfo[operatorKickParams[i].operator].operatorId; + bytes32 operatorToKickId = _operatorInfo[operatorKickParams[operatorToKickParamsIndex].operator].operatorId; uint96 operatorToKickStake = stakeRegistry.getCurrentOperatorStakeForQuorum(operatorToKickId, quorumNumber); uint96 registeringOperatorStake = stakeRegistry.getCurrentOperatorStakeForQuorum(operatorIdsToSwap[0], quorumNumber); From 78155a4c7f1694f314a35f2db4a0478d8f052ce6 Mon Sep 17 00:00:00 2001 From: wadealexc Date: Tue, 7 Nov 2023 20:32:40 +0000 Subject: [PATCH 038/101] refactor: move updateStakes to registry coordinator --- src/BLSPubkeyRegistry.sol | 5 + src/BLSPublicKeyCompendium.sol | 6 +- src/BLSRegistryCoordinatorWithIndices.sol | 409 +++++++++++++--------- src/StakeRegistry.sol | 233 ++++++------ src/libraries/BitmapUtils.sol | 56 ++- 5 files changed, 416 insertions(+), 293 deletions(-) diff --git a/src/BLSPubkeyRegistry.sol b/src/BLSPubkeyRegistry.sol index 30cc3286..3905b060 100644 --- a/src/BLSPubkeyRegistry.sol +++ b/src/BLSPubkeyRegistry.sol @@ -211,4 +211,9 @@ contract BLSPubkeyRegistry is BLSPubkeyRegistryStorage { function getOperatorFromPubkeyHash(bytes32 pubkeyHash) public view returns (address) { return pubkeyCompendium.pubkeyHashToOperator(pubkeyHash); } + + function getOperatorId(address operator) public view returns (bytes32) { + (, bytes32 pubkeyHash) = pubkeyCompendium.getRegisteredPubkey(operator); + return pubkeyHash; + } } diff --git a/src/BLSPublicKeyCompendium.sol b/src/BLSPublicKeyCompendium.sol index 7c1d24ca..28c49a97 100644 --- a/src/BLSPublicKeyCompendium.sol +++ b/src/BLSPublicKeyCompendium.sol @@ -86,10 +86,10 @@ contract BLSPublicKeyCompendium is IBLSPublicKeyCompendium { *******************************************************************************/ /** - * @notice Returns the pubkey of an operator, verifying that the pubkey and its hash are valid + * @notice Returns the pubkey and pubkey hash of an operator * @dev Reverts if the operator has not registered a valid pubkey */ - function getRegisteredPubkey(address operator) public view returns (BN254.G1Point memory) { + function getRegisteredPubkey(address operator) public view returns (BN254.G1Point memory, bytes32) { BN254.G1Point memory pubkey = operatorToPubkey[operator]; bytes32 pubkeyHash = operatorToPubkeyHash[operator]; @@ -98,7 +98,7 @@ contract BLSPublicKeyCompendium is IBLSPublicKeyCompendium { "BLSPublicKeyCompendium.getRegisteredPubkey: operator is not registered" ); - return pubkey; + return (pubkey, pubkeyHash); } /** diff --git a/src/BLSRegistryCoordinatorWithIndices.sol b/src/BLSRegistryCoordinatorWithIndices.sol index 39c9ae6e..0a4bec50 100644 --- a/src/BLSRegistryCoordinatorWithIndices.sol +++ b/src/BLSRegistryCoordinatorWithIndices.sol @@ -32,6 +32,7 @@ import "src/libraries/BN254.sol"; */ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistryCoordinatorWithIndices, ISocketUpdater, Pausable { using BN254 for BN254.G1Point; + using BitmapUtils for *; /// @notice The EIP-712 typehash for the `DelegationApproval` struct used by the contract bytes32 public constant OPERATOR_CHURN_APPROVAL_TYPEHASH = @@ -44,6 +45,8 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr uint8 internal constant PAUSED_REGISTER_OPERATOR = 0; /// @notice Index for flag that pauses operator deregistration uint8 internal constant PAUSED_DEREGISTER_OPERATOR = 1; + /// @notice Index for flag pausing operator stake updates + uint8 internal constant PAUSED_UPDATE_OPERATOR = 2; /// @notice The maximum number of quorums this contract supports uint8 internal constant MAX_QUORUM_COUNT = 192; @@ -65,6 +68,7 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr /// @notice maps operator id => historical quorums they registered for mapping(bytes32 => QuorumBitmapUpdate[]) internal _operatorBitmapHistory; /// @notice maps operator address => operator id and status + // TODO rename struct to OperatorInfo mapping(address => Operator) internal _operatorInfo; /// @notice whether the salt has been used for an operator churn approval mapping(bytes32 => bool) public isChurnApproverSaltUsed; @@ -143,35 +147,33 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr *******************************************************************************/ /** - * @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 socket is the socket of the operator + * @notice Registers msg.sender as an operator for one or more quorums. If any quorum reaches its maximum + * operator capacity, this method will fail. + * @param quorumNumbers is an ordered byte array containing the quorum numbers being registered for */ function registerOperator( bytes calldata quorumNumbers, string calldata socket ) external onlyWhenNotPaused(PAUSED_REGISTER_OPERATOR) { - (uint32[] memory numOperatorsPerQuorum, ) = _registerOperator({ - operator: msg.sender, + bytes32 operatorId = blsPubkeyRegistry.getOperatorId(msg.sender); + + // Register the operator, failing if churn is needed + _registerOperator({ + operatorToRegister: msg.sender, + idToRegister: operatorId, quorumNumbers: quorumNumbers, - socket: socket + socket: socket, + performChurn: false, + operatorKickParams: new OperatorKickParam[](0) }); - - for (uint256 i = 0; i < numOperatorsPerQuorum.length; i++) { - require( - numOperatorsPerQuorum[i] <= _quorumParams[uint8(quorumNumbers[i])].maxOperatorCount, - "BLSRegistryCoordinatorWithIndices.registerOperator: quorum is overfilled" - ); - } } /** - * @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 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 - * and ids of the operators to swap with the kicked operator. + * @notice Registers msg.sender as an operator for one or more quorums. If any quorum reaches its maximum operator + * capacity, `operatorKickParams` is used to replace an old operator with the new one. + * @param quorumNumbers is an ordered byte array containing the quorum numbers being registered for + * @param operatorKickParams are used to determine which operator is removed to maintain quorum capacity as the + * operator registers for quorums. * @param churnApproverSignature is the signature of the churnApprover on the operator kick params */ function registerOperatorWithChurn( @@ -180,75 +182,31 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr OperatorKickParam[] calldata operatorKickParams, SignatureWithSaltAndExpiry memory churnApproverSignature ) external onlyWhenNotPaused(PAUSED_REGISTER_OPERATOR) { - // register the operator - (uint32[] memory numOperatorsPerQuorum, bytes32 operatorId) = _registerOperator({ - operator: msg.sender, - quorumNumbers: quorumNumbers, - socket: socket - }); + require(operatorKickParams.length == quorumNumbers.length, "BLSRegistryCoordinatorWithIndices.registerOperatorWithChurn: input length mismatch"); - // 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] = operatorId; + bytes32 idToRegister = blsPubkeyRegistry.getOperatorId(msg.sender); - // verify the churnApprover's signature + // Verify the churn approver's signature for the registering operator and kick params _verifyChurnApproverSignature({ - registeringOperatorId: operatorIdsToSwap[0], + registeringOperatorId: idToRegister, operatorKickParams: operatorKickParams, churnApproverSignature: churnApproverSignature }); - 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 = _quorumParams[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.registerOperatorWithChurn: quorumNumber not the same as signed" - ); - - // get the total stake for the quorum - uint96 totalStakeForQuorum = stakeRegistry.getCurrentTotalStakeForQuorum(quorumNumber); - bytes32 operatorToKickId = _operatorInfo[operatorKickParams[operatorToKickParamsIndex].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.registerOperatorWithChurn: 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.registerOperatorWithChurn: operator to kick has more than kickBIPSOfTotalStake" - ); - - // increment the operatorToKickParamsIndex - operatorToKickParamsIndex++; - } - - // kick the operator - _deregisterOperator({ - operator: operatorKickParams[i].operator, - quorumNumbers: quorumNumbers[i:i+1] - }); - } + // Register the operator and perform churn if needed + _registerOperator({ + operatorToRegister: msg.sender, + idToRegister: idToRegister, + quorumNumbers: quorumNumbers, + socket: socket, + performChurn: true, + operatorKickParams: operatorKickParams + }); } /** - * @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 + * @notice Deregisters the caller from one or more quorums + * @param quorumNumbers is an ordered byte array containing the quorum numbers being deregistered from */ function deregisterOperator( bytes calldata quorumNumbers @@ -259,6 +217,43 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr }); } + /** + * @notice Updates the stakes of one or more operators in the StakeRegistry, for each quorum + * the operator is registered for. + * + * If any operator no longer meets the minimum stake required to remain in the quorum, + * they are deregistered. + */ + function updateOperators(address[] calldata operators) external onlyWhenNotPaused(PAUSED_UPDATE_OPERATOR) { + for (uint256 i = 0; i < operators.length; i++) { + + address operator = operators[i]; + Operator storage operatorInfo = _operatorInfo[operator]; + bytes32 operatorId = operatorInfo.operatorId; + + // Only update operators currently registered for at least one quorum + if (operatorInfo.status != OperatorStatus.REGISTERED) { + continue; + } + + uint192 currentBitmap = _currentOperatorBitmap(operatorId); + bytes memory currentQuorums = BitmapUtils.bitmapToBytesArray(currentBitmap); + + /** + * Update the operator's stake for their active quorums. The stakeRegistry returns a bitmap + * of quorums where the operator no longer meets the minimum stake, and should be deregistered. + */ + (uint192 quorumsToRemove) = stakeRegistry.updateOperatorStake(operator, operatorId, currentQuorums); + + if (!quorumsToRemove.isEmpty()) { + _deregisterOperator({ + operator: operator, + quorumNumbers: BitmapUtils.bitmapToBytesArray(quorumsToRemove) + }); + } + } + } + /** * @notice Updates the socket of the msg.sender given they are a registered operator * @param socket is the new socket of the operator @@ -337,63 +332,110 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr INTERNAL FUNCTIONS *******************************************************************************/ - /// @return numOperatorsPerQuorum is the list of number of operators per quorum in quorumNumberss + /** + * @notice Register the operator for one or more quorums. This method updates the + * operator's quorum bitmap, socket, and status, then registers them with each registry. + */ function _registerOperator( - address operator, + address operatorToRegister, + bytes32 idToRegister, bytes calldata quorumNumbers, - string memory socket - ) internal virtual returns(uint32[] memory, bytes32) { - // Create and validate bitmap from quorumNumbers - uint256 quorumBitmap = BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers); - require(quorumBitmap <= MAX_QUORUM_BITMAP, "BLSRegistryCoordinatorWithIndices._registerOperator: bitmap exceeds max bitmap size"); - require(quorumBitmap != 0, "BLSRegistryCoordinatorWithIndices._registerOperator: bitmap cannot be 0"); - + string memory socket, + bool performChurn, + OperatorKickParam[] memory operatorKickParams + ) internal virtual { /** - * Register the operator with the BLSPubkeyRegistry, StakeRegistry, and IndexRegistry. Retrieves: - * - operatorId: hash of the operator's public key, unique to the operator - * - numOperatorsPerQuorum: list of # operators for each quorum in `quorumNumbers` + * Get bitmap of quorums to register for and operator's current bitmap. Validate that: + * - we're trying to register for at least 1 quorum + * - the operator is not currently registered for any quorums we're registering for + * Then, calculate the operator's new bitmap after registration */ - bytes32 operatorId = blsPubkeyRegistry.registerOperator(operator, quorumNumbers); - stakeRegistry.registerOperator(operator, operatorId, quorumNumbers); - uint32[] memory numOperatorsPerQuorum = indexRegistry.registerOperator(operatorId, quorumNumbers); + uint192 quorumsToAdd = uint192(BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers, quorumCount)); + uint192 currentBitmap = _currentOperatorBitmap(idToRegister); + require(!quorumsToAdd.isEmpty(), "BLSRegistryCoordinatorWithIndices._registerOperator: bitmap cannot be 0"); + require(quorumsToAdd.noBitsInCommon(currentBitmap), "BLSRegistryCoordinatorWithIndices._registerOperator: operator already registered for some quorums being registered for"); + uint192 newBitmap = uint192(currentBitmap.plus(quorumsToAdd)); /** - * If the operator has an existing bitmap history, combine the last entry with `quorumBitmap` - * and set its `nextUpdateBlockNumber` to the current block. - * Skip this step if the `nextUpdateBlockNumber` is already set for the last entry in the operator's bitmap history, - * as this indicates that the operator previously completely deregistered, and thus is no longer registered for any quorums. + * Update operator's bitmap, socket, and status. Only update operatorInfo if needed: + * if we're `REGISTERED`, the operatorId and status are already correct. */ - uint256 historyLength = _operatorBitmapHistory[operatorId].length; - if (historyLength != 0 && _operatorBitmapHistory[operatorId][historyLength - 1].nextUpdateBlockNumber == 0) { - uint256 prevQuorumBitmap = _operatorBitmapHistory[operatorId][historyLength - 1].quorumBitmap; - require(prevQuorumBitmap & quorumBitmap == 0, "BLSRegistryCoordinatorWithIndices._registerOperator: 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; + _updateOperatorBitmap({ + operatorId: idToRegister, + newBitmap: newBitmap + }); - _operatorBitmapHistory[operatorId][historyLength - 1].nextUpdateBlockNumber = uint32(block.number); - } + emit OperatorSocketUpdate(idToRegister, socket); - // set the operatorId to quorum bitmap history - _operatorBitmapHistory[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 (_operatorInfo[operator].status != OperatorStatus.REGISTERED) { - _operatorInfo[operator] = Operator({ - operatorId: operatorId, + if (_operatorInfo[operatorToRegister].status != OperatorStatus.REGISTERED) { + _operatorInfo[operatorToRegister] = Operator({ + operatorId: idToRegister, status: OperatorStatus.REGISTERED }); - emit OperatorRegistered(operator, operatorId); + emit OperatorRegistered(operatorToRegister, idToRegister); } - emit OperatorSocketUpdate(operatorId, socket); - return (numOperatorsPerQuorum, operatorId); + /** + * Register the operator with the BLSPubkeyRegistry, StakeRegistry, and IndexRegistry + */ + bytes32 registeredId = blsPubkeyRegistry.registerOperator(operatorToRegister, quorumNumbers); + require(registeredId == idToRegister, "BLSRegistryCoordinatorWithIndices._registerOperator: operatorId mismatch"); + (uint96[] memory registeredStakes, uint96[] memory totalStakes) = + stakeRegistry.registerOperator(operatorToRegister, idToRegister, quorumNumbers); + uint32[] memory numOperatorsPerQuorum = indexRegistry.registerOperator(idToRegister, quorumNumbers); + + /** + * Now that we've registered the operator, validate that the new total operators + * in each quorum do not exceed the maximum. + * + * If they do and we're performing churn, we deregister the corresponding operator + * specified in `operatorKickParams` + */ + + uint256 kickIndex = 0; + for (uint256 i = 0; i < quorumNumbers.length; i++) { + uint8 quorumNumber = uint8(quorumNumbers[i]); + + OperatorSetParam memory operatorSetParams = _quorumParams[quorumNumber]; + bool exceedsOperatorCapacity = numOperatorsPerQuorum[i] > operatorSetParams.maxOperatorCount; + + /** + * If we exceed the max operator count for this quorum, we need to replace an existing operator + * with a new one. + */ + if (exceedsOperatorCapacity) { + require(performChurn, "BLSRegistryCoordinatorWithIndices._registerOperator: churn required for overfilled quorum"); + + // Validate that we specified the right operator to kick + address operatorToKick = operatorKickParams[kickIndex].operator; + bytes32 idToKick = _operatorInfo[operatorToKick].operatorId; + require(idToRegister != idToKick, "BLSRegistryCoordinatorWithIndices._registerOperator: cannot churn self"); + require(operatorKickParams[kickIndex].quorumNumber == quorumNumber, "BLSRegistryCoordinatorWithIndices._registerOperator: quorumNumber not the same as signed"); + + // Get the target operator's stake and check that it is below the kick thresholds + uint96 operatorToKickStake = stakeRegistry.getCurrentOperatorStakeForQuorum(idToKick, quorumNumber); + require( + registeredStakes[i] > _individualKickThreshold(operatorToKickStake, operatorSetParams), + "BLSRegistryCoordinatorWithIndices._registerOperator: incoming operator has insufficient stake for churn" + ); + require( + operatorToKickStake < _totalKickThreshold(totalStakes[i], operatorSetParams), + "BLSRegistryCoordinatorWithIndices._registerOperator: cannot kick operator with more than kickBIPsOfTotalStake" + ); + + // Deregister the operator + _deregisterOperator(operatorToKick, quorumNumbers[i:i+1]); + kickIndex++; + } + } } + /** + * @dev Deregister the operator from one or more quorums + * This method updates the operator's quorum bitmap and status, then deregisters + * the operator with the BLSPubkeyRegistry, IndexRegistry, and StakeRegistry + */ function _deregisterOperator( address operator, bytes calldata quorumNumbers @@ -403,43 +445,52 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr bytes32 operatorId = operatorInfo.operatorId; require(operatorInfo.status == OperatorStatus.REGISTERED, "BLSRegistryCoordinatorWithIndices._deregisterOperator: operator is not registered"); - // Create and validate bitmap of quorums to remove - uint256 quorumsToRemoveBitmap = BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers); - require(quorumsToRemoveBitmap <= MAX_QUORUM_BITMAP, "BLSRegistryCoordinatorWithIndices._deregisterOperator: bitmap exceeds max bitmap size"); - require(quorumsToRemoveBitmap != 0, "BLSRegistryCoordinatorWithIndices._deregisterOperator: bitmap cannot be 0"); - - // Get the operator's last quorum bitmap and update its "next" pointer to the current block - // TODO - change to use new history update pattern - QuorumBitmapUpdate storage lastUpdate = _latestBitmapUpdate(operatorId); - lastUpdate.nextUpdateBlockNumber = uint32(block.number); - uint192 previousBitmap = lastUpdate.quorumBitmap; - - // Remove quorums the operator isn't registered for and check that the result isn't empty - quorumsToRemoveBitmap = previousBitmap & quorumsToRemoveBitmap; - bytes memory quorumNumbersToRemove = BitmapUtils.bitmapToBytesArray(quorumsToRemoveBitmap); - require(quorumNumbersToRemove.length != 0, "BLSRegistryCoordinatorWithIndices._deregisterOperator: operator is not registered for any of the provided quorums"); + /** + * Get bitmap of quorums to deregister from and operator's current bitmap. Validate that: + * - we're trying to deregister from at least 1 quorum + * - the operator is currently registered for any quorums we're trying to deregister from + * Then, calculate the opreator's new bitmap after deregistration + */ + uint192 quorumsToRemove = uint192(BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers, quorumCount)); + uint192 currentBitmap = _currentOperatorBitmap(operatorId); + require(!quorumsToRemove.isEmpty(), "BLSRegistryCoordinatorWithIndices._deregisterOperator: bitmap cannot be 0"); + require(quorumsToRemove.isSubsetOf(currentBitmap), "BLSRegistryCoordinatorWithIndices._deregisterOperator: operator is not registered for specified quorums"); + uint192 newBitmap = uint192(currentBitmap.minus(quorumsToRemove)); - // Check if the operator is completely deregistering - bool completeDeregistration = previousBitmap == quorumsToRemoveBitmap; + /** + * Update operator's bitmap and status: + */ + _updateOperatorBitmap({ + operatorId: operatorId, + newBitmap: newBitmap + }); - // Deregister operator with each of the registry contracts: - blsPubkeyRegistry.deregisterOperator(operator, quorumNumbersToRemove); - stakeRegistry.deregisterOperator(operatorId, quorumNumbersToRemove); - indexRegistry.deregisterOperator(operatorId, quorumNumbersToRemove); - - // If the operator still has active quorums, push a bitmap update. - // Otherwise, set them to deregistered - // TODO - change this to update history regardless - if (!completeDeregistration) { - _operatorBitmapHistory[operatorId].push(QuorumBitmapUpdate({ - updateBlockNumber: uint32(block.number), - nextUpdateBlockNumber: 0, - quorumBitmap: previousBitmap & ~uint192(quorumsToRemoveBitmap) // this removes the quorumsToRemoveBitmap from the quorumBitmapBeforeUpdate - })); - } else { + // If the operator is no longer registered for any quorums, update their status + if (newBitmap.isEmpty()) { operatorInfo.status = OperatorStatus.DEREGISTERED; emit OperatorDeregistered(operator, operatorId); } + + // Deregister operator with each of the registry contracts: + blsPubkeyRegistry.deregisterOperator(operator, quorumNumbers); + stakeRegistry.deregisterOperator(operatorId, quorumNumbers); + indexRegistry.deregisterOperator(operatorId, quorumNumbers); + } + + /** + * @notice Returns the stake threshold required for an incoming operator to replace an existing operator + * The incoming operator must have more stake than the return value. + */ + function _individualKickThreshold(uint96 operatorStake, OperatorSetParam memory setParams) internal pure returns (uint96) { + return operatorStake * setParams.kickBIPsOfOperatorStake / BIPS_DENOMINATOR; + } + + /** + * @notice Returns the total stake threshold required for an operator to remain in a quorum. + * The operator must have at least the returned stake amount to keep their position. + */ + function _totalKickThreshold(uint96 totalStake, OperatorSetParam memory setParams) internal pure returns (uint96) { + return totalStake * setParams.kickBIPsOfTotalStake / BIPS_DENOMINATOR; } /// @notice verifies churnApprover's signature on operator churn approval and increments the churnApprover nonce @@ -486,6 +537,53 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr blsPubkeyRegistry.initializeQuorum(quorumNumber); } + /** + * @notice Record an update to an operator's quorum bitmap. + * @param newBitmap is the most up-to-date set of bitmaps the operator is registered for + */ + function _updateOperatorBitmap(bytes32 operatorId, uint192 newBitmap) internal { + + uint256 historyLength = _operatorBitmapHistory[operatorId].length; + + if (historyLength == 0) { + // No prior bitmap history - push our first entry + _operatorBitmapHistory[operatorId].push(QuorumBitmapUpdate({ + updateBlockNumber: uint32(block.number), + nextUpdateBlockNumber: 0, + quorumBitmap: newBitmap + })); + } else { + // We have prior history - fetch our last-recorded update + QuorumBitmapUpdate storage lastUpdate = _operatorBitmapHistory[operatorId][historyLength - 1]; + + /** + * If the last update was made in the current block, update the entry. + * Otherwise, push a new entry and update the previous entry's "next" field + */ + if (lastUpdate.updateBlockNumber == uint32(block.number)) { + lastUpdate.quorumBitmap = newBitmap; + } else { + lastUpdate.nextUpdateBlockNumber = uint32(block.number); + _operatorBitmapHistory[operatorId].push(QuorumBitmapUpdate({ + updateBlockNumber: uint32(block.number), + nextUpdateBlockNumber: 0, + quorumBitmap: newBitmap + })); + } + } + } + + /// @notice Get the most recent bitmap for the operator, returning an empty bitmap if + /// the operator is not registered. + function _currentOperatorBitmap(bytes32 operatorId) internal view returns (uint192) { + uint256 historyLength = _operatorBitmapHistory[operatorId].length; + if (historyLength == 0) { + return 0; + } else { + return _operatorBitmapHistory[operatorId][historyLength - 1].quorumBitmap; + } + } + function _setOperatorSetParams(uint8 quorumNumber, OperatorSetParam memory operatorSetParams) internal { _quorumParams[quorumNumber] = operatorSetParams; emit OperatorSetParamsUpdated(quorumNumber, operatorSetParams); @@ -501,15 +599,6 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr ejector = newEjector; } - /** - * @notice Fetch the most recent bitmap update for an operatorId - * @dev This method reverts (underflow) if the operator does not have any bitmap updates - */ - function _latestBitmapUpdate(bytes32 operatorId) internal view returns (QuorumBitmapUpdate storage) { - uint256 historyLength = _operatorBitmapHistory[operatorId].length; - return _operatorBitmapHistory[operatorId][historyLength - 1]; - } - /******************************************************************************* VIEW FUNCTIONS *******************************************************************************/ diff --git a/src/StakeRegistry.sol b/src/StakeRegistry.sol index 397bf6a1..d1aa0712 100644 --- a/src/StakeRegistry.sol +++ b/src/StakeRegistry.sol @@ -33,7 +33,7 @@ contract StakeRegistry is StakeRegistryStorage { } modifier quorumExists(uint8 quorumNumber) { - require(_totalStakeHistory[quorumNumber].length != 0, "StakeRegistry.quorumExists: quorum does not exist"); + require(_quorumExists(quorumNumber), "StakeRegistry.quorumExists: quorum does not exist"); _; } @@ -43,58 +43,6 @@ contract StakeRegistry is StakeRegistryStorage { IServiceManager _serviceManager ) StakeRegistryStorage(_registryCoordinator, _delegationManager, _serviceManager) {} - /******************************************************************************* - EXTERNAL FUNCTIONS - *******************************************************************************/ - - /** - * @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 - uint8 quorumCount = registryCoordinator.quorumCount(); - for (uint8 quorumNumber = 0; quorumNumber < quorumCount; ) { - int256 totalStakeDelta; - - // TODO - not a huge fan of this dependency on the reg coord, but i do prefer this - // over the stakereg also keeping its own count. - require(_totalStakeHistory[quorumNumber].length != 0, "StakeRegistry.updateStakes: quorum does not exist"); - - for (uint256 i = 0; i < operators.length; ) { - bytes32 operatorId = registryCoordinator.getOperatorId(operators[i]); - uint192 quorumBitmap = registryCoordinator.getCurrentQuorumBitmap(operatorId); - - /** - * If the operator is a part of the quorum, update their current stake - * and apply the delta to the total - */ - if (BitmapUtils.numberIsInBitmap(quorumBitmap, quorumNumber)) { - (int256 stakeDelta, ) = _updateOperatorStake({ - operator: operators[i], - operatorId: operatorId, - quorumNumber: quorumNumber - }); - - totalStakeDelta += stakeDelta; - } - unchecked { - ++i; - } - } - - // Record the update for the quorum's total stake - _recordTotalStakeUpdate(quorumNumber, totalStakeDelta); - - unchecked { - ++quorumNumber; - } - } - } - /******************************************************************************* EXTERNAL FUNCTIONS - REGISTRY COORDINATOR *******************************************************************************/ @@ -115,35 +63,35 @@ contract StakeRegistry is StakeRegistryStorage { address operator, bytes32 operatorId, bytes calldata quorumNumbers - ) public virtual onlyRegistryCoordinator { + ) public virtual onlyRegistryCoordinator returns (uint96[] memory) { - for (uint256 i = 0; i < quorumNumbers.length; ) { + uint96[] memory currentStakes = new uint96[](quorumNumbers.length); + for (uint256 i = 0; i < quorumNumbers.length; i++) { uint8 quorumNumber = uint8(quorumNumbers[i]); - require(_totalStakeHistory[quorumNumber].length != 0, "StakeRegistry.registerOperator: quorum does not exist"); - - /** - * Update the operator's stake for the quorum and retrieve their current stake - * as well as the change in stake. - * - If this method returns `hasMinimumStake == false`, the operator has not met - * the minimum stake requirement for this quorum - */ - (int256 stakeDelta, bool hasMinimumStake) = _updateOperatorStake({ - operator: operator, - operatorId: operatorId, - quorumNumber: quorumNumber - }); + require(_quorumExists(quorumNumber), "StakeRegistry.registerOperator: quorum does not exist"); + + // Retrieve the operator's current weighted stake for the quorum, reverting if they have not met + // the minimum. + (uint96 currentStake, bool hasMinimumStake) = _weightOfOperatorForQuorum(quorumNumber, operator); require( hasMinimumStake, "StakeRegistry.registerOperator: Operator does not meet minimum stake requirement for quorum" ); + currentStakes[i] = currentStake; + + // Update the operator's stake + int256 stakeDelta = _recordOperatorStakeUpdate({ + operatorId: operatorId, + quorumNumber: quorumNumber, + newStake: currentStake + }); - // Update this quorum's total stake + // Update this quorum's total stake by applying the operator's delta _recordTotalStakeUpdate(quorumNumber, stakeDelta); - unchecked { - ++i; - } } + + return currentStakes; } /** @@ -168,7 +116,7 @@ contract StakeRegistry is StakeRegistryStorage { */ for (uint256 i = 0; i < quorumNumbers.length; ) { uint8 quorumNumber = uint8(quorumNumbers[i]); - require(_totalStakeHistory[quorumNumber].length != 0, "StakeRegistry.deregisterOperator: quorum does not exist"); + require(_quorumExists(quorumNumber), "StakeRegistry.deregisterOperator: quorum does not exist"); // Update the operator's stake for the quorum and retrieve the shares removed int256 stakeDelta = _recordOperatorStakeUpdate({ @@ -186,14 +134,58 @@ contract StakeRegistry is StakeRegistryStorage { } } + function updateOperatorStake( + address operator, + bytes32 operatorId, + bytes calldata currentQuorums + ) external onlyRegistryCoordinator returns (uint192) { + uint192 deregisteredBitmap; + + /** + * For each quorum, update the operator's stake and record the delta + * in the quorum's total stake. + * + * If the operator no longer has the minimum stake required to be registered + * in the quorum, the quorum number is added to `deregisteredBitmap`, which + * is returned to the registry coordinator. + */ + for (uint256 i = 0; i < currentQuorums.length; i++) { + uint8 quorumNumber = currentQuorums[i]; + require(_quorumExists(quorumNumber), "StakeRegistry.updateOperatorStake: quorum does not exist"); + + // Fetch the operator's current stake, applying weighting parameters and checking + // against the minimum stake requirements for the quorum. + (uint96 stakeWeight, bool hasMinimumStake) = _weightOfOperatorForQuorum(quorumNumber, operator); + + // If the operator no longer meets the minimum stake, deregister them from the quorum + if (!hasMinimumStake) { + stakeWeight = 0; + deregisteredBitmap |= quorumNumber; + } + + // Update the operator's stake and retrieve the delta + // If we're deregistering them, their weight is set to 0 + int256 stakeDelta = _recordOperatorStakeUpdate({ + operatorId: operatorId, + quorumNumber: quorumNumber, + newStake: stakeWeight + }); + + // Apply the delta to the quorum's total stake + _recordTotalStakeUpdate(quorumNumber, stakeDelta); + } + + return deregisteredBitmap; + } + /// @notice Initialize a new quorum and push its first history update function initializeQuorum( uint8 quorumNumber, uint96 minimumStake, - StrategyParams[] memory strategyParams + StrategyParams[] memory _strategyParams ) public virtual onlyRegistryCoordinator { - require(_totalStakeHistory[quorumNumber].length == 0, "StakeRegistry.initializeQuorum: quorum already exists"); - _addStrategyParams(quorumNumber, strategyParams); + require(!_quorumExists(quorumNumber), "StakeRegistry.initializeQuorum: quorum already exists"); + _addStrategyParams(quorumNumber, _strategyParams); _setMinimumStakeForQuorum(quorumNumber, minimumStake); _totalStakeHistory[quorumNumber].push(OperatorStakeUpdate({ @@ -218,9 +210,9 @@ contract StakeRegistry is StakeRegistryStorage { */ function addStrategies( uint8 quorumNumber, - StrategyParams[] memory strategyParams + StrategyParams[] memory _strategyParams ) public virtual onlyServiceManagerOwner quorumExists(quorumNumber) { - _addStrategyParams(quorumNumber, strategyParams); + _addStrategyParams(quorumNumber, _strategyParams); } /** @@ -235,15 +227,15 @@ contract StakeRegistry is StakeRegistryStorage { uint256 toRemoveLength = indicesToRemove.length; require(toRemoveLength > 0, "StakeRegistry.removeStrategies: no indices to remove provided"); - StrategyParams[] storage strategyParams = strategyParams[quorumNumber]; + StrategyParams[] storage _strategyParams = strategyParams[quorumNumber]; for (uint256 i = 0; i < toRemoveLength; i++) { - emit StrategyRemovedFromQuorum(quorumNumber, strategyParams[indicesToRemove[i]].strategy); - emit StrategyMultiplierUpdated(quorumNumber, strategyParams[indicesToRemove[i]].strategy, 0); + emit StrategyRemovedFromQuorum(quorumNumber, _strategyParams[indicesToRemove[i]].strategy); + emit StrategyMultiplierUpdated(quorumNumber, _strategyParams[indicesToRemove[i]].strategy, 0); // Replace index to remove with the last item in the list, then pop the last item - strategyParams[indicesToRemove[i]] = strategyParams[strategyParams.length - 1]; - strategyParams.pop(); + _strategyParams[indicesToRemove[i]] = _strategyParams[_strategyParams.length - 1]; + _strategyParams.pop(); } } @@ -262,12 +254,12 @@ contract StakeRegistry is StakeRegistryStorage { require(numStrats > 0, "StakeRegistry.modifyStrategyParams: no strategy indices provided"); require(newMultipliers.length == numStrats, "StakeRegistry.modifyStrategyParams: input length mismatch"); - StrategyParams[] storage strategyParams = strategyParams[quorumNumber]; + StrategyParams[] storage _strategyParams = strategyParams[quorumNumber]; for (uint256 i = 0; i < numStrats; i++) { // Change the strategy's associated multiplier - strategyParams[strategyIndices[i]].multiplier = newMultipliers[i]; - emit StrategyMultiplierUpdated(quorumNumber, strategyParams[strategyIndices[i]].strategy, newMultipliers[i]); + _strategyParams[strategyIndices[i]].multiplier = newMultipliers[i]; + emit StrategyMultiplierUpdated(quorumNumber, _strategyParams[strategyIndices[i]].strategy, newMultipliers[i]); } } @@ -302,38 +294,6 @@ contract StakeRegistry is StakeRegistryStorage { emit MinimumStakeForQuorumUpdated(quorumNumber, minimumStake); } - /** - * @notice Finds the updated stake for `operator` for `quorumNumber`, stores it and records the update - * @dev **DOES NOT UPDATE `totalStake` IN ANY WAY** -- `totalStake` updates must be done elsewhere. - * @return delta The change in the operator's stake as a signed int256 - * @return hasMinimumStake Whether the operator meets the minimum stake requirement for the quorum - */ - function _updateOperatorStake( - address operator, - bytes32 operatorId, - uint8 quorumNumber - ) internal returns (int256 delta, bool hasMinimumStake) { - /** - * Get the operator's current stake for the quorum. If their stake - * is below the quorum's threshold, set their stake to 0 - */ - uint96 currentStake = _weightOfOperatorForQuorum(quorumNumber, operator); - if (currentStake < minimumStakeForQuorum[quorumNumber]) { - currentStake = uint96(0); - } else { - hasMinimumStake = true; - } - - // Update the operator's stake and retrieve the delta - delta = _recordOperatorStakeUpdate({ - operatorId: operatorId, - quorumNumber: quorumNumber, - newStake: currentStake - }); - - return (delta, hasMinimumStake); - } - /** * @notice Records that `operatorId`'s current stake for `quorumNumber` is now `newStake` * @return The change in the operator's stake as a signed int256 @@ -356,16 +316,22 @@ contract StakeRegistry is StakeRegistryStorage { })); } else { // We have prior stake history - fetch our last-recorded stake - prevStake = operatorStakeHistory[operatorId][quorumNumber][historyLength-1].stake; + OperatorStakeUpdate storage lastUpdate = operatorStakeHistory[operatorId][quorumNumber][historyLength-1]; + prevStake = lastUpdate.stake; + + // Short-circuit in case there's no change in stake + if (prevStake == newStake) { + return 0; + } /** * If our last stake entry was made in the current block, update the entry * Otherwise, push a new entry and update the previous entry's "next" field */ - if (operatorStakeHistory[operatorId][quorumNumber][historyLength-1].updateBlockNumber == uint32(block.number)) { - operatorStakeHistory[operatorId][quorumNumber][historyLength-1].stake = newStake; + if (lastUpdate.updateBlockNumber == uint32(block.number)) { + lastUpdate.stake = newStake; } else { - operatorStakeHistory[operatorId][quorumNumber][historyLength-1].nextUpdateBlockNumber = uint32(block.number); + lastUpdate.nextUpdateBlockNumber = uint32(block.number); operatorStakeHistory[operatorId][quorumNumber].push(OperatorStakeUpdate({ updateBlockNumber: uint32(block.number), nextUpdateBlockNumber: 0, @@ -486,13 +452,15 @@ contract StakeRegistry is StakeRegistryStorage { /** * @notice This function computes the total weight of the @param operator in the quorum @param quorumNumber. * @dev this method DOES NOT check that the quorum exists + * @return `uint96` The weighted sum of the operator's shares across each strategy considered by the quorum + * @return `bool` True if the operator meets the quorum's minimum stake */ - function _weightOfOperatorForQuorum(uint8 quorumNumber, address operator) internal virtual view returns (uint96) { + function _weightOfOperatorForQuorum(uint8 quorumNumber, address operator) internal virtual view returns (uint96, bool) { uint96 weight; uint256 stratsLength = strategyParamsLength(quorumNumber); StrategyParams memory strategyAndMultiplier; - for (uint256 i = 0; i < stratsLength;) { + for (uint256 i = 0; i < stratsLength; i++) { // accessing i^th StrategyParams struct for the quorumNumber strategyAndMultiplier = strategyParams[quorumNumber][i]; @@ -503,13 +471,19 @@ contract StakeRegistry is StakeRegistryStorage { if (sharesAmount > 0) { weight += uint96(sharesAmount * strategyAndMultiplier.multiplier / WEIGHTING_DIVISOR); } + } - unchecked { - ++i; - } + // Return the weight, and `true` if the operator meets the quorum's minimum stake + if (weight < minimumStakeForQuorum[quorumNumber]) { + return (weight, true); + } else { + return (weight, false); } + } - return weight; + /// @notice Returns `true` if the quorum has been initialized + function _quorumExists(uint8 quorumNumber) internal view returns (bool) { + return _totalStakeHistory[quorumNumber].length != 0; } /******************************************************************************* @@ -524,7 +498,8 @@ contract StakeRegistry is StakeRegistryStorage { uint8 quorumNumber, address operator ) public virtual view quorumExists(quorumNumber) returns (uint96) { - return _weightOfOperatorForQuorum(quorumNumber, operator); + (uint96 stake, ) = _weightOfOperatorForQuorum(quorumNumber, operator); + return stake; } /// @notice Returns the length of the dynamic array stored in `strategyParams[quorumNumber]`. diff --git a/src/libraries/BitmapUtils.sol b/src/libraries/BitmapUtils.sol index 1db53985..83cc7b13 100644 --- a/src/libraries/BitmapUtils.sol +++ b/src/libraries/BitmapUtils.sol @@ -91,6 +91,25 @@ library BitmapUtils { return bitmap; } + /** + * @notice Converts an ordered byte array to a bitmap, validating that all bits are less than `maxSetBit` + * @param orderedBytesArray The array to convert to a bitmap; must be in strictly ascending order + * @param maxSetBit The bitmap may not contain a bit greater than or equal to this value + * @dev Reverts if bitmap contains a bit greater than or equal to `maxSetBit` + */ + function orderedBytesArrayToBitmap(bytes memory orderedBytesArray, uint8 maxSetBit) internal pure returns (uint256) { + uint256 bitmap = orderedBytesArrayToBitmap(orderedBytesArray); + + if (bitmap != 0) { + require( + uint8(orderedBytesArray[orderedBytesArray.length - 1]) < maxSetBit, + "BitmapUtils.orderedBytesArrayToBitmap: bitmap exceeds max value" + ); + } + + return bitmap; + } + /** * @notice Converts an ordered array of bytes into a bitmap. Optimized, Yul-heavy version of `orderedBytesArrayToBitmap`. * @param orderedBytesArray The array of bytes to convert/compress into a bitmap. Must be in strictly ascending order. @@ -276,8 +295,43 @@ library BitmapUtils { return count; } - // @notice returns 'true' if `numberToCheckForInclusion` is in `bitmap` and 'false' otherwise. + /// @notice returns 'true' if `numberToCheckForInclusion` is in `bitmap` and 'false' otherwise. function numberIsInBitmap(uint256 bitmap, uint8 numberToCheckForInclusion) internal pure returns (bool) { return (((bitmap >> numberToCheckForInclusion) & 1) == 1); } + + /** + * @notice Returns true if `bitmap` has no set bits + */ + function isEmpty(uint256 bitmap) internal pure returns (bool) { + return bitmap == 0; + } + + /** + * @notice Returns true if `a` and `b` have no common set bits + */ + function noBitsInCommon(uint256 a, uint256 b) internal pure returns (bool) { + return a & b == 0; + } + + /** + * @notice Returns true if `a` is a subset of `b`: ALL of the bits in `a` are also in `b` + */ + function isSubsetOf(uint256 a, uint256 b) internal pure returns (bool) { + return a & b == a; + } + + /** + * @notice Adds `a` and `b` using bitwise-or + */ + function plus(uint256 a, uint256 b) internal pure returns (uint256) { + return a | b; + } + + /** + * @notice Subtracts `b` from `a` by negating `b` and using bitwise-and + */ + function minus(uint256 a, uint256 b) internal pure returns (uint256) { + return a & ~b; + } } \ No newline at end of file From a9ab3f9bd620123725033d51fe9edfc453c2d906 Mon Sep 17 00:00:00 2001 From: wadealexc Date: Tue, 7 Nov 2023 21:19:47 +0000 Subject: [PATCH 039/101] fix: fixes several compiler issues still running into "stack too deep" --- src/BLSPubkeyRegistry.sol | 6 +- src/BLSRegistryCoordinatorWithIndices.sol | 58 ++++++++++++------- src/StakeRegistry.sol | 48 ++++++++++------ src/interfaces/IBLSPubkeyRegistry.sol | 2 + src/interfaces/IBLSPublicKeyCompendium.sol | 4 +- src/interfaces/IStakeRegistry.sol | 20 +++++-- test/harnesses/StakeRegistryHarness.sol | 67 +++++++++------------- test/mocks/BLSPublicKeyCompendiumMock.sol | 8 +-- test/mocks/StakeRegistryMock.sol | 28 +++++---- 9 files changed, 138 insertions(+), 103 deletions(-) diff --git a/src/BLSPubkeyRegistry.sol b/src/BLSPubkeyRegistry.sol index 3905b060..1d8028d2 100644 --- a/src/BLSPubkeyRegistry.sol +++ b/src/BLSPubkeyRegistry.sol @@ -44,14 +44,14 @@ contract BLSPubkeyRegistry is BLSPubkeyRegistryStorage { bytes memory quorumNumbers ) public virtual onlyRegistryCoordinator returns (bytes32) { // Get the operator's pubkey from the compendium. Reverts if they have not registered a key - BN254.G1Point memory pubkey = pubkeyCompendium.getRegisteredPubkey(operator); + (BN254.G1Point memory pubkey, bytes32 pubkeyHash) = pubkeyCompendium.getRegisteredPubkey(operator); // Update each quorum's aggregate pubkey _processQuorumApkUpdate(quorumNumbers, pubkey); // Return pubkeyHash, which will become the operator's unique id emit OperatorAddedToQuorums(operator, quorumNumbers); - return BN254.hashG1Point(pubkey); + return pubkeyHash; } /** @@ -71,7 +71,7 @@ contract BLSPubkeyRegistry is BLSPubkeyRegistryStorage { bytes memory quorumNumbers ) public virtual onlyRegistryCoordinator { // Get the operator's pubkey from the compendium. Reverts if they have not registered a key - BN254.G1Point memory pubkey = pubkeyCompendium.getRegisteredPubkey(operator); + (BN254.G1Point memory pubkey, ) = pubkeyCompendium.getRegisteredPubkey(operator); // Update each quorum's aggregate pubkey _processQuorumApkUpdate(quorumNumbers, pubkey.negate()); diff --git a/src/BLSRegistryCoordinatorWithIndices.sol b/src/BLSRegistryCoordinatorWithIndices.sol index 0a4bec50..08dd271e 100644 --- a/src/BLSRegistryCoordinatorWithIndices.sol +++ b/src/BLSRegistryCoordinatorWithIndices.sol @@ -407,30 +407,48 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr if (exceedsOperatorCapacity) { require(performChurn, "BLSRegistryCoordinatorWithIndices._registerOperator: churn required for overfilled quorum"); - // Validate that we specified the right operator to kick - address operatorToKick = operatorKickParams[kickIndex].operator; - bytes32 idToKick = _operatorInfo[operatorToKick].operatorId; - require(idToRegister != idToKick, "BLSRegistryCoordinatorWithIndices._registerOperator: cannot churn self"); - require(operatorKickParams[kickIndex].quorumNumber == quorumNumber, "BLSRegistryCoordinatorWithIndices._registerOperator: quorumNumber not the same as signed"); - - // Get the target operator's stake and check that it is below the kick thresholds - uint96 operatorToKickStake = stakeRegistry.getCurrentOperatorStakeForQuorum(idToKick, quorumNumber); - require( - registeredStakes[i] > _individualKickThreshold(operatorToKickStake, operatorSetParams), - "BLSRegistryCoordinatorWithIndices._registerOperator: incoming operator has insufficient stake for churn" - ); - require( - operatorToKickStake < _totalKickThreshold(totalStakes[i], operatorSetParams), - "BLSRegistryCoordinatorWithIndices._registerOperator: cannot kick operator with more than kickBIPsOfTotalStake" - ); - - // Deregister the operator - _deregisterOperator(operatorToKick, quorumNumbers[i:i+1]); + // Validate that `operatorToRegister` can replace the operator specified in the kick params + _validateChurn({ + quorumNumber: quorumNumber, + totalQuorumStake: totalStakes[i], + newOperator: operatorToRegister, + newOperatorStake: registeredStakes[i], + kickParams: operatorKickParams[i], + setParams: operatorSetParams + }); + + _deregisterOperator(operatorKickParams[i].operator, quorumNumbers[i:i+1]); kickIndex++; } } } + function _validateChurn( + uint8 quorumNumber, + uint96 totalQuorumStake, + address newOperator, + uint96 newOperatorStake, + OperatorKickParam memory kickParams, + OperatorSetParam memory setParams + ) internal view { + address operatorToKick = kickParams.operator; + bytes32 idToKick = _operatorInfo[operatorToKick].operatorId; + require(_operatorInfo[operatorToKick].status == OperatorStatus.REGISTERED, "BLSRegistryCoordinatorWithIndices._validateChurn: cannot remove unregistered operator"); + require(newOperator != operatorToKick, "BLSRegistryCoordinatorWithIndices._validateChurn: cannot churn self"); + require(kickParams.quorumNumber == quorumNumber, "BLSRegistryCoordinatorWithIndices._validateChurn: quorumNumber not the same as signed"); + + // Get the target operator's stake and check that it is below the kick thresholds + uint96 operatorToKickStake = stakeRegistry.getCurrentOperatorStakeForQuorum(idToKick, quorumNumber); + require( + newOperatorStake > _individualKickThreshold(operatorToKickStake, setParams), + "BLSRegistryCoordinatorWithIndices._validateChurn: incoming operator has insufficient stake for churn" + ); + require( + operatorToKickStake < _totalKickThreshold(totalQuorumStake, setParams), + "BLSRegistryCoordinatorWithIndices._validateChurn: cannot kick operator with more than kickBIPsOfTotalStake" + ); + } + /** * @dev Deregister the operator from one or more quorums * This method updates the operator's quorum bitmap and status, then deregisters @@ -438,7 +456,7 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr */ function _deregisterOperator( address operator, - bytes calldata quorumNumbers + bytes memory quorumNumbers ) internal virtual { // Fetch the operator's info and ensure they are registered Operator storage operatorInfo = _operatorInfo[operator]; diff --git a/src/StakeRegistry.sol b/src/StakeRegistry.sol index d1aa0712..863191a2 100644 --- a/src/StakeRegistry.sol +++ b/src/StakeRegistry.sol @@ -52,6 +52,7 @@ contract StakeRegistry is StakeRegistryStorage { * @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. + * @return The operator's current stake for each quorum, and the total stake for each quorum * @dev access restricted to the RegistryCoordinator * @dev Preconditions (these are assumed, not validated in this contract): * 1) `quorumNumbers` has no duplicates @@ -63,9 +64,10 @@ contract StakeRegistry is StakeRegistryStorage { address operator, bytes32 operatorId, bytes calldata quorumNumbers - ) public virtual onlyRegistryCoordinator returns (uint96[] memory) { + ) public virtual onlyRegistryCoordinator returns (uint96[] memory, uint96[] memory) { uint96[] memory currentStakes = new uint96[](quorumNumbers.length); + uint96[] memory totalStakes = new uint96[](quorumNumbers.length); for (uint256 i = 0; i < quorumNumbers.length; i++) { uint8 quorumNumber = uint8(quorumNumbers[i]); @@ -78,7 +80,6 @@ contract StakeRegistry is StakeRegistryStorage { hasMinimumStake, "StakeRegistry.registerOperator: Operator does not meet minimum stake requirement for quorum" ); - currentStakes[i] = currentStake; // Update the operator's stake int256 stakeDelta = _recordOperatorStakeUpdate({ @@ -88,10 +89,11 @@ contract StakeRegistry is StakeRegistryStorage { }); // Update this quorum's total stake by applying the operator's delta - _recordTotalStakeUpdate(quorumNumber, stakeDelta); + currentStakes[i] = currentStake; + totalStakes[i] = _recordTotalStakeUpdate(quorumNumber, stakeDelta); } - return currentStakes; + return (currentStakes, totalStakes); } /** @@ -134,33 +136,40 @@ contract StakeRegistry is StakeRegistryStorage { } } + /** + * @notice Called by the registry coordinator to update an operator's stake for one + * or more quorums. + * + * If the operator no longer has the minimum stake required for a quorum, they are + * added to the + */ function updateOperatorStake( address operator, bytes32 operatorId, - bytes calldata currentQuorums + bytes calldata quorumNumbers ) external onlyRegistryCoordinator returns (uint192) { - uint192 deregisteredBitmap; + uint192 quorumsToRemove; /** * For each quorum, update the operator's stake and record the delta * in the quorum's total stake. * * If the operator no longer has the minimum stake required to be registered - * in the quorum, the quorum number is added to `deregisteredBitmap`, which + * in the quorum, the quorum number is added to `quorumsToRemove`, which * is returned to the registry coordinator. */ - for (uint256 i = 0; i < currentQuorums.length; i++) { - uint8 quorumNumber = currentQuorums[i]; + for (uint256 i = 0; i < quorumNumbers.length; i++) { + uint8 quorumNumber = uint8(quorumNumbers[i]); require(_quorumExists(quorumNumber), "StakeRegistry.updateOperatorStake: quorum does not exist"); // Fetch the operator's current stake, applying weighting parameters and checking // against the minimum stake requirements for the quorum. (uint96 stakeWeight, bool hasMinimumStake) = _weightOfOperatorForQuorum(quorumNumber, operator); - // If the operator no longer meets the minimum stake, deregister them from the quorum + // If the operator no longer meets the minimum stake, set their stake to zero and mark them for removal if (!hasMinimumStake) { stakeWeight = 0; - deregisteredBitmap |= quorumNumber; + quorumsToRemove |= quorumNumber; } // Update the operator's stake and retrieve the delta @@ -175,7 +184,7 @@ contract StakeRegistry is StakeRegistryStorage { _recordTotalStakeUpdate(quorumNumber, stakeDelta); } - return deregisteredBitmap; + return quorumsToRemove; } /// @notice Initialize a new quorum and push its first history update @@ -346,15 +355,16 @@ contract StakeRegistry is StakeRegistryStorage { } /// @notice Applies a delta to the total stake recorded for `quorumNumber` - function _recordTotalStakeUpdate(uint8 quorumNumber, int256 stakeDelta) internal { - // Return early if no update is needed - if (stakeDelta == 0) { - return; - } - + /// @return Returns the new total stake for the quorum + function _recordTotalStakeUpdate(uint8 quorumNumber, int256 stakeDelta) internal returns (uint96) { // Get our last-recorded stake update uint256 historyLength = _totalStakeHistory[quorumNumber].length; OperatorStakeUpdate storage lastStakeUpdate = _totalStakeHistory[quorumNumber][historyLength - 1]; + + // Return early if no update is needed + if (stakeDelta == 0) { + return lastStakeUpdate.stake; + } // Calculate the new total stake by applying the delta to our previous stake uint96 newStake = _applyDelta(lastStakeUpdate.stake, stakeDelta); @@ -373,6 +383,8 @@ contract StakeRegistry is StakeRegistryStorage { stake: newStake })); } + + return newStake; } /** diff --git a/src/interfaces/IBLSPubkeyRegistry.sol b/src/interfaces/IBLSPubkeyRegistry.sol index 7ee95d1b..86ba835f 100644 --- a/src/interfaces/IBLSPubkeyRegistry.sol +++ b/src/interfaces/IBLSPubkeyRegistry.sol @@ -85,4 +85,6 @@ interface IBLSPubkeyRegistry is IRegistry { * @param index is the index of the apkUpdate being retrieved from the list of quorum apkUpdates in storage */ function getApkHashAtBlockNumberAndIndex(uint8 quorumNumber, uint32 blockNumber, uint256 index) external view returns (bytes24); + + function getOperatorId(address operator) external view returns (bytes32); } diff --git a/src/interfaces/IBLSPublicKeyCompendium.sol b/src/interfaces/IBLSPublicKeyCompendium.sol index c6f18435..10342213 100644 --- a/src/interfaces/IBLSPublicKeyCompendium.sol +++ b/src/interfaces/IBLSPublicKeyCompendium.sol @@ -36,10 +36,10 @@ interface IBLSPublicKeyCompendium { function registerBLSPublicKey(BN254.G1Point memory signedMessageHash, BN254.G1Point memory pubkeyG1, BN254.G2Point memory pubkeyG2) external; /** - * @notice Returns the pubkey of an operator, verifying that the pubkey and its hash are valid + * @notice Returns the pubkey and pubkey hash of an operator * @dev Reverts if the operator has not registered a valid pubkey */ - function getRegisteredPubkey(address operator) external view returns (BN254.G1Point memory); + function getRegisteredPubkey(address operator) external view returns (BN254.G1Point memory, bytes32); /** * @notice Returns the message hash that an operator must sign to register their BLS public key. diff --git a/src/interfaces/IStakeRegistry.sol b/src/interfaces/IStakeRegistry.sol index 3ae2fa17..56e30a21 100644 --- a/src/interfaces/IStakeRegistry.sol +++ b/src/interfaces/IStakeRegistry.sol @@ -59,6 +59,7 @@ interface IStakeRegistry is IRegistry { * @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. + * @return The operator's current stake for each quorum, and the total stake for each quorum * @dev access restricted to the RegistryCoordinator * @dev Preconditions (these are assumed, not validated in this contract): * 1) `quorumNumbers` has no duplicates @@ -66,7 +67,11 @@ interface IStakeRegistry is IRegistry { * 3) `quorumNumbers` is ordered in ascending order * 4) the operator is not already registered */ - function registerOperator(address operator, bytes32 operatorId, bytes memory quorumNumbers) external; + function registerOperator( + address operator, + bytes32 operatorId, + bytes memory quorumNumbers + ) external returns (uint96[] memory, uint96[] memory); /** * @notice Deregisters the operator with `operatorId` for the specified `quorumNumbers`. @@ -231,8 +236,15 @@ interface IStakeRegistry is IRegistry { function getCurrentTotalStakeForQuorum(uint8 quorumNumber) external view returns (uint96); /** - * @notice Used for updating information on deposits of nodes. - * @param operators are the addresses of the operators whose stake information is getting updated + * @notice Called by the registry coordinator to update an operator's stake for one + * or more quorums. + * + * If the operator no longer has the minimum stake required for a quorum, they are + * added to the */ - function updateStakes(address[] memory operators) external; + function updateOperatorStake( + address operator, + bytes32 operatorId, + bytes calldata quorumNumbers + ) external returns (uint192); } \ No newline at end of file diff --git a/test/harnesses/StakeRegistryHarness.sol b/test/harnesses/StakeRegistryHarness.sol index 16610afb..824000ed 100644 --- a/test/harnesses/StakeRegistryHarness.sol +++ b/test/harnesses/StakeRegistryHarness.sol @@ -5,7 +5,7 @@ import "src/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; + mapping(uint8 => mapping(address => uint96)) private __weightOfOperatorForQuorum; constructor( IRegistryCoordinator _registryCoordinator, @@ -18,65 +18,54 @@ contract StakeRegistryHarness is StakeRegistry { return _recordOperatorStakeUpdate(operatorId, quorumNumber, newStake); } - function updateOperatorStake(address operator, bytes32 operatorId, uint8 quorumNumber) external returns (int256, bool) { - return _updateOperatorStake(operator, operatorId, quorumNumber); - } - function recordTotalStakeUpdate(uint8 quorumNumber, int256 stakeDelta) external { _recordTotalStakeUpdate(quorumNumber, stakeDelta); } // 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]; - } - - /// TODO remove when core gets updated - function weightOfOperatorForQuorumView(uint8 quorumNumber, address operator) public override view returns(uint96) { - return _weightOfOperatorForQuorum[quorumNumber][operator]; + return __weightOfOperatorForQuorum[quorumNumber][operator]; } - function _weightOfOperatorForQuorum(uint8 quorumNumber, address operator) internal override view returns(uint96) { - return __weightOfOperatorForQuorum[quorumNumber][operator]; + function _weightOfOperatorForQuorum(uint8 quorumNumber, address operator) internal override view returns(uint96, bool) { + return (__weightOfOperatorForQuorum[quorumNumber][operator], true); } // 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; + __weightOfOperatorForQuorum[quorumNumber][operator] = weight; } // mocked function to register an operator without having to mock other elements // This is just a copy/paste from `registerOperator`, since that no longer uses an internal method - function registerOperatorNonCoordinator(address operator, bytes32 operatorId, bytes calldata quorumNumbers) external { - // 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" - ); - - for (uint256 i = 0; i < quorumNumbers.length; ) { - /** - * Update the operator's stake for the quorum and retrieve their current stake - * as well as the change in stake. - * - If this method returns `hasMinimumStake == false`, the operator has not met - * the minimum stake requirement for this quorum - */ + function registerOperatorNonCoordinator(address operator, bytes32 operatorId, bytes calldata quorumNumbers) external returns (uint96[] memory, uint96[] memory) { + uint96[] memory currentStakes = new uint96[](quorumNumbers.length); + uint96[] memory totalStakes = new uint96[](quorumNumbers.length); + for (uint256 i = 0; i < quorumNumbers.length; i++) { + uint8 quorumNumber = uint8(quorumNumbers[i]); - (int256 stakeDelta, bool hasMinimumStake) = _updateOperatorStake({ - operator: operator, - operatorId: operatorId, - quorumNumber: quorumNumber - }); + require(_quorumExists(quorumNumber), "StakeRegistry.registerOperator: quorum does not exist"); + + // Retrieve the operator's current weighted stake for the quorum, reverting if they have not met + // the minimum. + (uint96 currentStake, bool hasMinimumStake) = _weightOfOperatorForQuorum(quorumNumber, operator); require( hasMinimumStake, - "StakeRegistry._registerOperator: Operator does not meet minimum stake requirement for quorum" + "StakeRegistry.registerOperator: Operator does not meet minimum stake requirement for quorum" ); - // Update this quorum's total stake - _recordTotalStakeUpdate(quorumNumber, stakeDelta); - unchecked { - ++i; - } + // Update the operator's stake + int256 stakeDelta = _recordOperatorStakeUpdate({ + operatorId: operatorId, + quorumNumber: quorumNumber, + newStake: currentStake + }); + + // Update this quorum's total stake by applying the operator's delta + currentStakes[i] = currentStake; + totalStakes[i] = _recordTotalStakeUpdate(quorumNumber, stakeDelta); } + + return (currentStakes, totalStakes); } } diff --git a/test/mocks/BLSPublicKeyCompendiumMock.sol b/test/mocks/BLSPublicKeyCompendiumMock.sol index f2dd9caa..9c5ba938 100644 --- a/test/mocks/BLSPublicKeyCompendiumMock.sol +++ b/test/mocks/BLSPublicKeyCompendiumMock.sol @@ -47,18 +47,16 @@ contract BLSPublicKeyCompendiumMock is IBLSPublicKeyCompendium{ operatorToPubkey[account] = pk; } - function getRegisteredPubkey(address operator) public view returns (BN254.G1Point memory) { + function getRegisteredPubkey(address operator) public view returns (BN254.G1Point memory, bytes32) { BN254.G1Point memory pubkey = operatorToPubkey[operator]; bytes32 pubkeyHash = operatorToPubkeyHash[operator]; require( - pubkeyHash != bytes32(0) && BN254.hashG1Point(pubkey) == pubkeyHash, + pubkeyHash != bytes32(0), "BLSPublicKeyCompendium.getRegisteredPubkey: operator is not registered" ); - - require(pubkeyHash != ZERO_PK_HASH, "BLSPublicKeyCompendium.getRegisteredPubkey: invalid pubkey"); - return pubkey; + return (pubkey, pubkeyHash); } function getMessageHash(address operator) external view returns (BN254.G1Point memory) {} diff --git a/test/mocks/StakeRegistryMock.sol b/test/mocks/StakeRegistryMock.sol index fd61e77e..dfe31cd5 100644 --- a/test/mocks/StakeRegistryMock.sol +++ b/test/mocks/StakeRegistryMock.sol @@ -17,6 +17,7 @@ contract StakeRegistryMock is IStakeRegistry { * @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. + * @return The operator's current stake for each quorum, and the total stake for each quorum * @dev access restricted to the RegistryCoordinator * @dev Preconditions (these are assumed, not validated in this contract): * 1) `quorumNumbers` has no duplicates @@ -24,7 +25,11 @@ contract StakeRegistryMock is IStakeRegistry { * 3) `quorumNumbers` is ordered in ascending order * 4) the operator is not already registered */ - function registerOperator(address operator, bytes32 operatorId, bytes memory quorumNumbers) external {} + function registerOperator( + address operator, + bytes32 operatorId, + bytes memory quorumNumbers + ) external returns (uint96[] memory, uint96[] memory) {} /** * @notice Deregisters the operator with `operatorId` for the specified `quorumNumbers`. @@ -184,18 +189,17 @@ contract StakeRegistryMock is IStakeRegistry { function getCurrentTotalStakeForQuorum(uint8 quorumNumber) external view returns (uint96) {} /** - * @notice Used for updating information on deposits of nodes. - * @param operators are the addresses of the operators whose stake information is getting updated + * @notice Called by the registry coordinator to update an operator's stake for one + * or more quorums. + * + * If the operator no longer has the minimum stake required for a quorum, they are + * added to the */ - function updateStakes(address[] memory operators) external { - for (uint256 i = 0; i < operators.length; i++) { - emit StakeUpdate( - bytes32(uint256(keccak256(abi.encodePacked(operators[i], "operatorId")))), - uint8(uint256(keccak256(abi.encodePacked(operators[i], i, "quorumNumber")))), - uint96(uint256(keccak256(abi.encodePacked(operators[i], i, "stake")))) - ); - } - } + function updateOperatorStake( + address operator, + bytes32 operatorId, + bytes calldata quorumNumbers + ) external returns (uint192) {} function getMockOperatorId(address operator) external returns(bytes32) { return bytes32(uint256(keccak256(abi.encodePacked(operator, "operatorId")))); From 2ae9bad4603439f0c6cef3b65cb5de0c8fbe3010 Mon Sep 17 00:00:00 2001 From: wadealexc Date: Wed, 8 Nov 2023 15:25:22 +0000 Subject: [PATCH 040/101] fix: stack too deep issue addressed also addressed review comments: - natspec comments in IStakeRegistry - incorrect boolean in StakeRegistry --- src/BLSRegistryCoordinatorWithIndices.sol | 107 ++++++++++++---------- src/StakeRegistry.sol | 7 +- src/interfaces/IStakeRegistry.sol | 2 + 3 files changed, 62 insertions(+), 54 deletions(-) diff --git a/src/BLSRegistryCoordinatorWithIndices.sol b/src/BLSRegistryCoordinatorWithIndices.sol index 08dd271e..38ea1613 100644 --- a/src/BLSRegistryCoordinatorWithIndices.sol +++ b/src/BLSRegistryCoordinatorWithIndices.sol @@ -158,14 +158,27 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr bytes32 operatorId = blsPubkeyRegistry.getOperatorId(msg.sender); // Register the operator, failing if churn is needed - _registerOperator({ + RegisterResults memory results = _registerOperator({ operatorToRegister: msg.sender, idToRegister: operatorId, quorumNumbers: quorumNumbers, - socket: socket, - performChurn: false, - operatorKickParams: new OperatorKickParam[](0) + socket: socket }); + + for (uint256 i = 0; i < quorumNumbers.length; i++) { + uint8 quorumNumber = uint8(quorumNumbers[i]); + + OperatorSetParam memory operatorSetParams = _quorumParams[quorumNumber]; + + /** + * The new operator count for each quorum may not exceed the configured maximum + * If it does, use `registerOperatorWithChurn` instead. + */ + require( + results.numOperatorsPerQuorum[i] <= operatorSetParams.maxOperatorCount, + "BLSRegistryCoordinatorWithIndices.registerOperator: operator count exceeds maximum" + ); + } } /** @@ -194,14 +207,37 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr }); // Register the operator and perform churn if needed - _registerOperator({ + RegisterResults memory results = _registerOperator({ operatorToRegister: msg.sender, idToRegister: idToRegister, quorumNumbers: quorumNumbers, - socket: socket, - performChurn: true, - operatorKickParams: operatorKickParams + socket: socket }); + + uint256 kickIndex = 0; + for (uint256 i = 0; i < quorumNumbers.length; i++) { + uint8 quorumNumber = uint8(quorumNumbers[i]); + + OperatorSetParam memory operatorSetParams = _quorumParams[quorumNumber]; + + /** + * If the new operator count for any quorum exceeds the maximum, validate + * that churn can be performed, then deregister the specified operator + */ + if (results.numOperatorsPerQuorum[i] > operatorSetParams.maxOperatorCount) { + _validateChurn({ + quorumNumber: quorumNumber, + totalQuorumStake: results.totalStakes[i], + newOperator: msg.sender, + newOperatorStake: results.operatorStakes[i], + kickParams: operatorKickParams[i], + setParams: operatorSetParams + }); + + _deregisterOperator(operatorKickParams[i].operator, quorumNumbers[i:i+1]); + kickIndex++; + } + } } /** @@ -332,6 +368,12 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr INTERNAL FUNCTIONS *******************************************************************************/ + struct RegisterResults { + uint32[] numOperatorsPerQuorum; + uint96[] operatorStakes; + uint96[] totalStakes; + } + /** * @notice Register the operator for one or more quorums. This method updates the * operator's quorum bitmap, socket, and status, then registers them with each registry. @@ -340,10 +382,8 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr address operatorToRegister, bytes32 idToRegister, bytes calldata quorumNumbers, - string memory socket, - bool performChurn, - OperatorKickParam[] memory operatorKickParams - ) internal virtual { + string memory socket + ) internal virtual returns (RegisterResults memory) { /** * Get bitmap of quorums to register for and operator's current bitmap. Validate that: * - we're trying to register for at least 1 quorum @@ -381,46 +421,15 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr */ bytes32 registeredId = blsPubkeyRegistry.registerOperator(operatorToRegister, quorumNumbers); require(registeredId == idToRegister, "BLSRegistryCoordinatorWithIndices._registerOperator: operatorId mismatch"); - (uint96[] memory registeredStakes, uint96[] memory totalStakes) = + (uint96[] memory operatorStakes, uint96[] memory totalStakes) = stakeRegistry.registerOperator(operatorToRegister, idToRegister, quorumNumbers); uint32[] memory numOperatorsPerQuorum = indexRegistry.registerOperator(idToRegister, quorumNumbers); - /** - * Now that we've registered the operator, validate that the new total operators - * in each quorum do not exceed the maximum. - * - * If they do and we're performing churn, we deregister the corresponding operator - * specified in `operatorKickParams` - */ - - uint256 kickIndex = 0; - for (uint256 i = 0; i < quorumNumbers.length; i++) { - uint8 quorumNumber = uint8(quorumNumbers[i]); - - OperatorSetParam memory operatorSetParams = _quorumParams[quorumNumber]; - bool exceedsOperatorCapacity = numOperatorsPerQuorum[i] > operatorSetParams.maxOperatorCount; - - /** - * If we exceed the max operator count for this quorum, we need to replace an existing operator - * with a new one. - */ - if (exceedsOperatorCapacity) { - require(performChurn, "BLSRegistryCoordinatorWithIndices._registerOperator: churn required for overfilled quorum"); - - // Validate that `operatorToRegister` can replace the operator specified in the kick params - _validateChurn({ - quorumNumber: quorumNumber, - totalQuorumStake: totalStakes[i], - newOperator: operatorToRegister, - newOperatorStake: registeredStakes[i], - kickParams: operatorKickParams[i], - setParams: operatorSetParams - }); - - _deregisterOperator(operatorKickParams[i].operator, quorumNumbers[i:i+1]); - kickIndex++; - } - } + return RegisterResults({ + numOperatorsPerQuorum: numOperatorsPerQuorum, + operatorStakes: operatorStakes, + totalStakes: totalStakes + }); } function _validateChurn( diff --git a/src/StakeRegistry.sol b/src/StakeRegistry.sol index 863191a2..bdc58c3a 100644 --- a/src/StakeRegistry.sol +++ b/src/StakeRegistry.sol @@ -486,11 +486,8 @@ contract StakeRegistry is StakeRegistryStorage { } // Return the weight, and `true` if the operator meets the quorum's minimum stake - if (weight < minimumStakeForQuorum[quorumNumber]) { - return (weight, true); - } else { - return (weight, false); - } + bool hasMinimumStake = weight > minimumStakeForQuorum[quorumNumber]; + return (weight, hasMinimumStake); } /// @notice Returns `true` if the quorum has been initialized diff --git a/src/interfaces/IStakeRegistry.sol b/src/interfaces/IStakeRegistry.sol index 56e30a21..a11caf3b 100644 --- a/src/interfaces/IStakeRegistry.sol +++ b/src/interfaces/IStakeRegistry.sol @@ -241,6 +241,8 @@ interface IStakeRegistry is IRegistry { * * If the operator no longer has the minimum stake required for a quorum, they are * added to the + * @return A bitmap of quorums where the operator no longer meets the minimum stake + * and should be deregistered. */ function updateOperatorStake( address operator, From 6fba84e7fd21aae72cba67243810fafdba125c19 Mon Sep 17 00:00:00 2001 From: wadealexc Date: Wed, 8 Nov 2023 15:27:51 +0000 Subject: [PATCH 041/101] fix: actually commit natspec changes --- src/StakeRegistry.sol | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/StakeRegistry.sol b/src/StakeRegistry.sol index bdc58c3a..3b848413 100644 --- a/src/StakeRegistry.sol +++ b/src/StakeRegistry.sol @@ -142,6 +142,8 @@ contract StakeRegistry is StakeRegistryStorage { * * If the operator no longer has the minimum stake required for a quorum, they are * added to the + * @return A bitmap of quorums where the operator no longer meets the minimum stake + * and should be deregistered. */ function updateOperatorStake( address operator, From 258b5178e1ead638765edc9514efbd52ba4898b3 Mon Sep 17 00:00:00 2001 From: wadealexc Date: Wed, 8 Nov 2023 16:00:07 +0000 Subject: [PATCH 042/101] style: stakeregistry naming is more consistent with other contracts --- src/BLSOperatorStateRetriever.sol | 4 +- src/BLSRegistryCoordinatorWithIndices.sol | 2 +- src/BLSSignatureChecker.sol | 2 +- src/StakeRegistry.sol | 204 ++++++++++++---------- src/interfaces/IStakeRegistry.sol | 16 +- test/mocks/StakeRegistryMock.sol | 16 +- 6 files changed, 129 insertions(+), 115 deletions(-) diff --git a/src/BLSOperatorStateRetriever.sol b/src/BLSOperatorStateRetriever.sol index 362abc38..0ce58b15 100644 --- a/src/BLSOperatorStateRetriever.sol +++ b/src/BLSOperatorStateRetriever.sol @@ -77,7 +77,7 @@ contract BLSOperatorStateRetriever { bytes32 operatorId = bytes32(operatorIds[j]); operators[i][j] = Operator({ operatorId: operatorId, - stake: stakeRegistry.getOperatorStakeAtBlockNumber(operatorId, quorumNumber, blockNumber) + stake: stakeRegistry.getStakeAtBlockNumber(operatorId, quorumNumber, blockNumber) }); } } @@ -131,7 +131,7 @@ contract BLSOperatorStateRetriever { // 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.getStakeUpdateIndexForOperatorAtBlockNumber( + checkSignaturesIndices.nonSignerStakeIndices[quorumNumberIndex][numNonSignersForQuorum] = stakeRegistry.getStakeUpdateIndexAtBlockNumber( nonSignerOperatorIds[i], uint8(quorumNumbers[quorumNumberIndex]), referenceBlockNumber diff --git a/src/BLSRegistryCoordinatorWithIndices.sol b/src/BLSRegistryCoordinatorWithIndices.sol index 38ea1613..caa49df6 100644 --- a/src/BLSRegistryCoordinatorWithIndices.sol +++ b/src/BLSRegistryCoordinatorWithIndices.sol @@ -447,7 +447,7 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr require(kickParams.quorumNumber == quorumNumber, "BLSRegistryCoordinatorWithIndices._validateChurn: quorumNumber not the same as signed"); // Get the target operator's stake and check that it is below the kick thresholds - uint96 operatorToKickStake = stakeRegistry.getCurrentOperatorStakeForQuorum(idToKick, quorumNumber); + uint96 operatorToKickStake = stakeRegistry.getCurrentStake(idToKick, quorumNumber); require( newOperatorStake > _individualKickThreshold(operatorToKickStake, setParams), "BLSRegistryCoordinatorWithIndices._validateChurn: incoming operator has insufficient stake for churn" diff --git a/src/BLSSignatureChecker.sol b/src/BLSSignatureChecker.sol index 5c0b801c..4d440e92 100644 --- a/src/BLSSignatureChecker.sol +++ b/src/BLSSignatureChecker.sol @@ -142,7 +142,7 @@ contract BLSSignatureChecker is IBLSSignatureChecker { // 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.getOperatorStakeAtBlockNumberAndIndex( + stakeRegistry.getStakeAtBlockNumberAndIndex( quorumNumber, referenceBlockNumber, nonSignerPubkeyHashes[i], diff --git a/src/StakeRegistry.sol b/src/StakeRegistry.sol index 3b848413..c3ec8b24 100644 --- a/src/StakeRegistry.sol +++ b/src/StakeRegistry.sol @@ -525,6 +525,20 @@ contract StakeRegistry is StakeRegistryStorage { ) public view returns (StrategyParams memory) { return strategyParams[quorumNumber][index]; + } + + /******************************************************************************* + VIEW FUNCTIONS - Operator Stake History + *******************************************************************************/ + + /** + * @notice Returns the length of an operator's stake history for the given quorum + */ + function getStakeHistoryLength( + bytes32 operatorId, + uint8 quorumNumber + ) external view returns (uint256) { + return operatorStakeHistory[operatorId][quorumNumber].length; } /** @@ -532,13 +546,40 @@ contract StakeRegistry is StakeRegistryStorage { * @param operatorId The id of the operator of interest. * @param quorumNumber The quorum number to get the stake for. */ - function getOperatorStakeHistory( + function getStakeHistory( bytes32 operatorId, uint8 quorumNumber ) external view returns (OperatorStakeUpdate[] memory) { return operatorStakeHistory[operatorId][quorumNumber]; } + /** + * @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 getCurrentStake(bytes32 operatorId, uint8 quorumNumber) external view returns (uint96) { + OperatorStakeUpdate memory operatorStakeUpdate = getLatestStakeUpdate(operatorId, quorumNumber); + return operatorStakeUpdate.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 getLatestStakeUpdate( + bytes32 operatorId, + uint8 quorumNumber + ) public view returns (OperatorStakeUpdate memory) { + uint256 historyLength = operatorStakeHistory[operatorId][quorumNumber].length; + OperatorStakeUpdate memory operatorStakeUpdate; + if (historyLength == 0) { + return operatorStakeUpdate; + } else { + operatorStakeUpdate = operatorStakeHistory[operatorId][quorumNumber][historyLength - 1]; + return operatorStakeUpdate; + } + } + /** * @notice Returns the `index`-th entry in the `operatorStakeHistory[operatorId][quorumNumber]` array. * @param quorumNumber The quorum number to get the stake for. @@ -546,7 +587,7 @@ contract StakeRegistry is StakeRegistryStorage { * @param index Array index for lookup, within the dynamic array `operatorStakeHistory[operatorId][quorumNumber]`. * @dev Function will revert if `index` is out-of-bounds. */ - function getStakeUpdateForOperatorAtIndex( + function getStakeUpdateAtIndex( uint8 quorumNumber, bytes32 operatorId, uint256 index @@ -554,20 +595,20 @@ contract StakeRegistry is StakeRegistryStorage { return operatorStakeHistory[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 getTotalStakeUpdateAtIndex( + /// @notice Returns the stake of the operator for the provided `quorumNumber` at the given `blockNumber` + function getStakeAtBlockNumber( + bytes32 operatorId, uint8 quorumNumber, - uint256 index - ) external view returns (OperatorStakeUpdate memory) { - return _totalStakeHistory[quorumNumber][index]; + uint32 blockNumber + ) external view returns (uint96) { + return + operatorStakeHistory[operatorId][quorumNumber][ + _getStakeUpdateIndexForOperatorAtBlockNumber(operatorId, quorumNumber, blockNumber) + ].stake; } /// @notice Returns the indices of the operator stakes for the provided `quorumNumber` at the given `blockNumber` - function getStakeUpdateIndexForOperatorAtBlockNumber( + function getStakeUpdateIndexAtBlockNumber( bytes32 operatorId, uint8 quorumNumber, uint32 blockNumber @@ -575,34 +616,6 @@ contract StakeRegistry is StakeRegistryStorage { return _getStakeUpdateIndexForOperatorAtBlockNumber(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 getTotalStakeIndicesAtBlockNumber( - 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.getTotalStakeIndicesAtBlockNumber: quorum has no stake history at blockNumber" - ); - uint256 length = _totalStakeHistory[quorumNumber].length; - for (uint256 j = 0; j < length; j++) { - if (_totalStakeHistory[quorumNumber][length - j - 1].updateBlockNumber <= blockNumber) { - indices[i] = uint32(length - j - 1); - break; - } - } - } - return indices; - } - /** * @notice Returns the stake weight corresponding to `operatorId` for quorum `quorumNumber`, at the * `index`-th entry in the `operatorStakeHistory[operatorId][quorumNumber]` array if it was the operator's @@ -613,7 +626,7 @@ contract StakeRegistry is StakeRegistryStorage { * @param blockNumber Block number to make sure the stake is from. * @dev Function will revert if `index` is out-of-bounds. */ - function getOperatorStakeAtBlockNumberAndIndex( + function getStakeAtBlockNumberAndIndex( uint8 quorumNumber, uint32 blockNumber, bytes32 operatorId, @@ -624,6 +637,37 @@ contract StakeRegistry is StakeRegistryStorage { return operatorStakeUpdate.stake; } + /******************************************************************************* + VIEW FUNCTIONS - Total Stake History + *******************************************************************************/ + + /** + * @notice Returns the length of the total stake history for the given quorum + */ + function getTotalStakeHistoryLength(uint8 quorumNumber) external view returns (uint256) { + return _totalStakeHistory[quorumNumber].length; + } + + /** + * @notice Returns the stake weight from the latest entry in `_totalStakeHistory` for quorum `quorumNumber`. + * @dev Will revert if `_totalStakeHistory[quorumNumber]` is empty. + */ + function getCurrentTotalStake(uint8 quorumNumber) external view returns (uint96) { + return _totalStakeHistory[quorumNumber][_totalStakeHistory[quorumNumber].length - 1].stake; + } + + /** + * @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 getTotalStakeUpdateAtIndex( + uint8 quorumNumber, + uint256 index + ) external view returns (OperatorStakeUpdate memory) { + return _totalStakeHistory[quorumNumber][index]; + } + /** * @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. @@ -632,7 +676,7 @@ contract StakeRegistry is StakeRegistryStorage { * @param blockNumber Block number to make sure the stake is from. * @dev Function will revert if `index` is out-of-bounds. */ - function getTotalStakeAtBlockNumberFromIndex( + function getTotalStakeAtBlockNumberFromIndex( uint8 quorumNumber, uint32 blockNumber, uint256 index @@ -643,60 +687,30 @@ contract StakeRegistry is StakeRegistryStorage { } /** - * @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 + * @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 getMostRecentStakeUpdateByOperatorId( - bytes32 operatorId, - uint8 quorumNumber - ) public view returns (OperatorStakeUpdate memory) { - uint256 historyLength = operatorStakeHistory[operatorId][quorumNumber].length; - OperatorStakeUpdate memory operatorStakeUpdate; - if (historyLength == 0) { - return operatorStakeUpdate; - } else { - operatorStakeUpdate = operatorStakeHistory[operatorId][quorumNumber][historyLength - 1]; - return operatorStakeUpdate; + function getTotalStakeIndicesAtBlockNumber( + 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.getTotalStakeIndicesAtBlockNumber: quorum has no stake history at blockNumber" + ); + uint256 length = _totalStakeHistory[quorumNumber].length; + for (uint256 j = 0; j < length; j++) { + if (_totalStakeHistory[quorumNumber][length - j - 1].updateBlockNumber <= blockNumber) { + indices[i] = uint32(length - j - 1); + break; + } + } } - } - - /** - * @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 getOperatorStakeAtBlockNumber( - bytes32 operatorId, - uint8 quorumNumber, - uint32 blockNumber - ) external view returns (uint96) { - return - operatorStakeHistory[operatorId][quorumNumber][ - _getStakeUpdateIndexForOperatorAtBlockNumber(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 getOperatorStakeHistoryLength( - bytes32 operatorId, - uint8 quorumNumber - ) external view returns (uint256) { - return operatorStakeHistory[operatorId][quorumNumber].length; - } - - function getTotalStakeHistoryLength(uint8 quorumNumber) external view returns (uint256) { - return _totalStakeHistory[quorumNumber].length; + return indices; } } diff --git a/src/interfaces/IStakeRegistry.sol b/src/interfaces/IStakeRegistry.sol index a11caf3b..5a7409b0 100644 --- a/src/interfaces/IStakeRegistry.sol +++ b/src/interfaces/IStakeRegistry.sol @@ -151,7 +151,7 @@ interface IStakeRegistry is IRegistry { * @param operatorId The id of the operator of interest. * @param quorumNumber The quorum number to get the stake for. */ - function getOperatorStakeHistory(bytes32 operatorId, uint8 quorumNumber) external view returns (OperatorStakeUpdate[] memory); + function getStakeHistory(bytes32 operatorId, uint8 quorumNumber) external view returns (OperatorStakeUpdate[] memory); function getTotalStakeHistoryLength(uint8 quorumNumber) external view returns (uint256); @@ -163,7 +163,7 @@ interface IStakeRegistry is IRegistry { function getTotalStakeUpdateAtIndex(uint8 quorumNumber, uint256 index) external view returns (OperatorStakeUpdate memory); /// @notice Returns the indices of the operator stakes for the provided `quorumNumber` at the given `blockNumber` - function getStakeUpdateIndexForOperatorAtBlockNumber(bytes32 operatorId, uint8 quorumNumber, uint32 blockNumber) + function getStakeUpdateIndexAtBlockNumber(bytes32 operatorId, uint8 quorumNumber, uint32 blockNumber) external view returns (uint32); @@ -178,7 +178,7 @@ interface IStakeRegistry is IRegistry { * @param index Array index for lookup, within the dynamic array `operatorIdToStakeHistory[operatorId][quorumNumber]`. * @dev Function will revert if `index` is out-of-bounds. */ - function getStakeUpdateForOperatorAtIndex(uint8 quorumNumber, bytes32 operatorId, uint256 index) + function getStakeUpdateAtIndex(uint8 quorumNumber, bytes32 operatorId, uint256 index) external view returns (OperatorStakeUpdate memory); @@ -187,7 +187,7 @@ interface IStakeRegistry is IRegistry { * @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) external view returns (OperatorStakeUpdate memory); + function getLatestStakeUpdate(bytes32 operatorId, uint8 quorumNumber) external view returns (OperatorStakeUpdate memory); /** * @notice Returns the stake weight corresponding to `operatorId` for quorum `quorumNumber`, at the @@ -200,7 +200,7 @@ interface IStakeRegistry is IRegistry { * @dev Function will revert if `index` is out-of-bounds. * @dev used the BLSSignatureChecker to get past stakes of signing operators */ - function getOperatorStakeAtBlockNumberAndIndex(uint8 quorumNumber, uint32 blockNumber, bytes32 operatorId, uint256 index) + function getStakeAtBlockNumberAndIndex(uint8 quorumNumber, uint32 blockNumber, bytes32 operatorId, uint256 index) external view returns (uint96); @@ -221,10 +221,10 @@ interface IStakeRegistry is IRegistry { * @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); + function getCurrentStake(bytes32 operatorId, uint8 quorumNumber) external view returns (uint96); /// @notice Returns the stake of the operator for the provided `quorumNumber` at the given `blockNumber` - function getOperatorStakeAtBlockNumber(bytes32 operatorId, uint8 quorumNumber, uint32 blockNumber) + function getStakeAtBlockNumber(bytes32 operatorId, uint8 quorumNumber, uint32 blockNumber) external view returns (uint96); @@ -233,7 +233,7 @@ interface IStakeRegistry is IRegistry { * @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); + function getCurrentTotalStake(uint8 quorumNumber) external view returns (uint96); /** * @notice Called by the registry coordinator to update an operator's stake for one diff --git a/test/mocks/StakeRegistryMock.sol b/test/mocks/StakeRegistryMock.sol index dfe31cd5..e50ff865 100644 --- a/test/mocks/StakeRegistryMock.sol +++ b/test/mocks/StakeRegistryMock.sol @@ -104,7 +104,7 @@ contract StakeRegistryMock is IStakeRegistry { * @param operatorId The id of the operator of interest. * @param quorumNumber The quorum number to get the stake for. */ - function getOperatorStakeHistory(bytes32 operatorId, uint8 quorumNumber) external view returns (OperatorStakeUpdate[] memory) {} + function getStakeHistory(bytes32 operatorId, uint8 quorumNumber) external view returns (OperatorStakeUpdate[] memory) {} function getTotalStakeHistoryLength(uint8 quorumNumber) external view returns (uint256) {} @@ -116,7 +116,7 @@ contract StakeRegistryMock is IStakeRegistry { function getTotalStakeUpdateAtIndex(uint8 quorumNumber, uint256 index) external view returns (OperatorStakeUpdate memory) {} /// @notice Returns the indices of the operator stakes for the provided `quorumNumber` at the given `blockNumber` - function getStakeUpdateIndexForOperatorAtBlockNumber(bytes32 operatorId, uint8 quorumNumber, uint32 blockNumber) + function getStakeUpdateIndexAtBlockNumber(bytes32 operatorId, uint8 quorumNumber, uint32 blockNumber) external view returns (uint32) {} @@ -131,7 +131,7 @@ contract StakeRegistryMock is IStakeRegistry { * @param index Array index for lookup, within the dynamic array `operatorIdToStakeHistory[operatorId][quorumNumber]`. * @dev Function will revert if `index` is out-of-bounds. */ - function getStakeUpdateForOperatorAtIndex(uint8 quorumNumber, bytes32 operatorId, uint256 index) + function getStakeUpdateAtIndex(uint8 quorumNumber, bytes32 operatorId, uint256 index) external view returns (OperatorStakeUpdate memory) {} @@ -140,7 +140,7 @@ contract StakeRegistryMock is IStakeRegistry { * @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) external view returns (OperatorStakeUpdate memory) {} + function getLatestStakeUpdate(bytes32 operatorId, uint8 quorumNumber) external view returns (OperatorStakeUpdate memory) {} /** * @notice Returns the stake weight corresponding to `operatorId` for quorum `quorumNumber`, at the @@ -153,7 +153,7 @@ contract StakeRegistryMock is IStakeRegistry { * @dev Function will revert if `index` is out-of-bounds. * @dev used the BLSSignatureChecker to get past stakes of signing operators */ - function getOperatorStakeAtBlockNumberAndIndex(uint8 quorumNumber, uint32 blockNumber, bytes32 operatorId, uint256 index) + function getStakeAtBlockNumberAndIndex(uint8 quorumNumber, uint32 blockNumber, bytes32 operatorId, uint256 index) external view returns (uint96) {} @@ -174,10 +174,10 @@ contract StakeRegistryMock is IStakeRegistry { * @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) {} + function getCurrentStake(bytes32 operatorId, uint8 quorumNumber) external view returns (uint96) {} /// @notice Returns the stake of the operator for the provided `quorumNumber` at the given `blockNumber` - function getOperatorStakeAtBlockNumber(bytes32 operatorId, uint8 quorumNumber, uint32 blockNumber) + function getStakeAtBlockNumber(bytes32 operatorId, uint8 quorumNumber, uint32 blockNumber) external view returns (uint96){} @@ -186,7 +186,7 @@ contract StakeRegistryMock is IStakeRegistry { * @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) {} + function getCurrentTotalStake(uint8 quorumNumber) external view returns (uint96) {} /** * @notice Called by the registry coordinator to update an operator's stake for one From 041cc55e040aee9c48f762f9916e7b3daa14816a Mon Sep 17 00:00:00 2001 From: wadealexc Date: Wed, 8 Nov 2023 16:23:44 +0000 Subject: [PATCH 043/101] style: rename upper bound in bitmap utils, and rename Operator to OperatorInfo --- src/BLSRegistryCoordinatorWithIndices.sol | 11 +++++------ src/interfaces/IRegistryCoordinator.sol | 4 ++-- src/libraries/BitmapUtils.sol | 10 +++++----- test/mocks/RegistryCoordinatorMock.sol | 2 +- 4 files changed, 13 insertions(+), 14 deletions(-) diff --git a/src/BLSRegistryCoordinatorWithIndices.sol b/src/BLSRegistryCoordinatorWithIndices.sol index caa49df6..2380ffeb 100644 --- a/src/BLSRegistryCoordinatorWithIndices.sol +++ b/src/BLSRegistryCoordinatorWithIndices.sol @@ -68,8 +68,7 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr /// @notice maps operator id => historical quorums they registered for mapping(bytes32 => QuorumBitmapUpdate[]) internal _operatorBitmapHistory; /// @notice maps operator address => operator id and status - // TODO rename struct to OperatorInfo - mapping(address => Operator) internal _operatorInfo; + mapping(address => OperatorInfo) internal _operatorInfo; /// @notice whether the salt has been used for an operator churn approval mapping(bytes32 => bool) public isChurnApproverSaltUsed; @@ -264,7 +263,7 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr for (uint256 i = 0; i < operators.length; i++) { address operator = operators[i]; - Operator storage operatorInfo = _operatorInfo[operator]; + OperatorInfo storage operatorInfo = _operatorInfo[operator]; bytes32 operatorId = operatorInfo.operatorId; // Only update operators currently registered for at least one quorum @@ -408,7 +407,7 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr emit OperatorSocketUpdate(idToRegister, socket); if (_operatorInfo[operatorToRegister].status != OperatorStatus.REGISTERED) { - _operatorInfo[operatorToRegister] = Operator({ + _operatorInfo[operatorToRegister] = OperatorInfo({ operatorId: idToRegister, status: OperatorStatus.REGISTERED }); @@ -468,7 +467,7 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr bytes memory quorumNumbers ) internal virtual { // Fetch the operator's info and ensure they are registered - Operator storage operatorInfo = _operatorInfo[operator]; + OperatorInfo storage operatorInfo = _operatorInfo[operator]; bytes32 operatorId = operatorInfo.operatorId; require(operatorInfo.status == OperatorStatus.REGISTERED, "BLSRegistryCoordinatorWithIndices._deregisterOperator: operator is not registered"); @@ -636,7 +635,7 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr } /// @notice Returns the operator struct for the given `operator` - function getOperator(address operator) external view returns (Operator memory) { + function getOperator(address operator) external view returns (OperatorInfo memory) { return _operatorInfo[operator]; } diff --git a/src/interfaces/IRegistryCoordinator.sol b/src/interfaces/IRegistryCoordinator.sol index badf5890..360b95b1 100644 --- a/src/interfaces/IRegistryCoordinator.sol +++ b/src/interfaces/IRegistryCoordinator.sol @@ -27,7 +27,7 @@ interface IRegistryCoordinator { /** * @notice Data structure for storing info on operators */ - struct Operator { + struct OperatorInfo { // the id of the operator, which is likely the keccak256 hash of the operator's public key if using BLSRegsitry bytes32 operatorId; // indicates whether the operator is actively registered for serving the middleware or not @@ -49,7 +49,7 @@ interface IRegistryCoordinator { function quorumCount() external view returns (uint8); /// @notice Returns the operator struct for the given `operator` - function getOperator(address operator) external view returns (Operator memory); + function getOperator(address operator) external view returns (OperatorInfo memory); /// @notice Returns the operatorId for the given `operator` function getOperatorId(address operator) external view returns (bytes32); diff --git a/src/libraries/BitmapUtils.sol b/src/libraries/BitmapUtils.sol index 83cc7b13..e5f789a9 100644 --- a/src/libraries/BitmapUtils.sol +++ b/src/libraries/BitmapUtils.sol @@ -92,17 +92,17 @@ library BitmapUtils { } /** - * @notice Converts an ordered byte array to a bitmap, validating that all bits are less than `maxSetBit` + * @notice Converts an ordered byte array to a bitmap, validating that all bits are less than `bitUpperBound` * @param orderedBytesArray The array to convert to a bitmap; must be in strictly ascending order - * @param maxSetBit The bitmap may not contain a bit greater than or equal to this value - * @dev Reverts if bitmap contains a bit greater than or equal to `maxSetBit` + * @param bitUpperBound The exclusive largest bit. Each bit must be strictly less than this value. + * @dev Reverts if bitmap contains a bit greater than or equal to `bitUpperBound` */ - function orderedBytesArrayToBitmap(bytes memory orderedBytesArray, uint8 maxSetBit) internal pure returns (uint256) { + function orderedBytesArrayToBitmap(bytes memory orderedBytesArray, uint8 bitUpperBound) internal pure returns (uint256) { uint256 bitmap = orderedBytesArrayToBitmap(orderedBytesArray); if (bitmap != 0) { require( - uint8(orderedBytesArray[orderedBytesArray.length - 1]) < maxSetBit, + uint8(orderedBytesArray[orderedBytesArray.length - 1]) < bitUpperBound, "BitmapUtils.orderedBytesArrayToBitmap: bitmap exceeds max value" ); } diff --git a/test/mocks/RegistryCoordinatorMock.sol b/test/mocks/RegistryCoordinatorMock.sol index 9d0e7c4a..715a74c6 100644 --- a/test/mocks/RegistryCoordinatorMock.sol +++ b/test/mocks/RegistryCoordinatorMock.sol @@ -10,7 +10,7 @@ contract RegistryCoordinatorMock is IRegistryCoordinator { /// @notice Returns the bitmap of the quorums the operator is registered for. function operatorIdToQuorumBitmap(bytes32 pubkeyHash) external view returns (uint256){} - function getOperator(address operator) external view returns (Operator memory){} + function getOperator(address operator) external view returns (OperatorInfo memory){} /// @notice Returns the stored id for the specified `operator`. function getOperatorId(address operator) external view returns (bytes32){} From 408ea4e30d952741a0181bab1635a8ef4ddfcfb3 Mon Sep 17 00:00:00 2001 From: wadealexc Date: Wed, 8 Nov 2023 16:46:13 +0000 Subject: [PATCH 044/101] fix: operator has minimum if weight is equal to minimum --- src/StakeRegistry.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/StakeRegistry.sol b/src/StakeRegistry.sol index c3ec8b24..074728da 100644 --- a/src/StakeRegistry.sol +++ b/src/StakeRegistry.sol @@ -488,7 +488,7 @@ contract StakeRegistry is StakeRegistryStorage { } // Return the weight, and `true` if the operator meets the quorum's minimum stake - bool hasMinimumStake = weight > minimumStakeForQuorum[quorumNumber]; + bool hasMinimumStake = weight >= minimumStakeForQuorum[quorumNumber]; return (weight, hasMinimumStake); } From 614c4332d3f663d3104a4a400c2d9697c698e924 Mon Sep 17 00:00:00 2001 From: wadealexc Date: Wed, 8 Nov 2023 17:15:21 +0000 Subject: [PATCH 045/101] test: update tests to support refactor --- test/harnesses/StakeRegistryHarness.sol | 6 +- ...LSRegistryCoordinatorWithIndicesUnit.t.sol | 72 ++++++++++--------- test/unit/StakeRegistryUnit.t.sol | 71 ++++++------------ 3 files changed, 67 insertions(+), 82 deletions(-) diff --git a/test/harnesses/StakeRegistryHarness.sol b/test/harnesses/StakeRegistryHarness.sol index 824000ed..ece3feee 100644 --- a/test/harnesses/StakeRegistryHarness.sol +++ b/test/harnesses/StakeRegistryHarness.sol @@ -28,7 +28,11 @@ contract StakeRegistryHarness is StakeRegistry { } function _weightOfOperatorForQuorum(uint8 quorumNumber, address operator) internal override view returns(uint96, bool) { - return (__weightOfOperatorForQuorum[quorumNumber][operator], true); + uint96 weight = __weightOfOperatorForQuorum[quorumNumber][operator]; + return ( + weight, + weight >= minimumStakeForQuorum[quorumNumber] + ); } // mocked function so we can set this arbitrarily without having to mock other elements diff --git a/test/unit/BLSRegistryCoordinatorWithIndicesUnit.t.sol b/test/unit/BLSRegistryCoordinatorWithIndicesUnit.t.sol index c9710fdc..cab43e49 100644 --- a/test/unit/BLSRegistryCoordinatorWithIndicesUnit.t.sol +++ b/test/unit/BLSRegistryCoordinatorWithIndicesUnit.t.sol @@ -134,7 +134,8 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { function testRegisterOperatorWithCoordinator_QuorumNumbersTooLarge_Reverts() public { bytes memory quorumNumbersTooLarge = new bytes(1); quorumNumbersTooLarge[0] = 0xC0; - cheats.expectRevert("BLSRegistryCoordinatorWithIndices._registerOperator: bitmap exceeds max bitmap size"); + cheats.expectRevert("BitmapUtils.orderedBytesArrayToBitmap: bitmap exceeds max value"); + cheats.prank(defaultOperator); registryCoordinator.registerOperator(quorumNumbersTooLarge, defaultSocket); } @@ -143,7 +144,7 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { bytes memory quorumNumbersNotCreated = new bytes(1); quorumNumbersNotCreated[0] = 0x0B; cheats.prank(defaultOperator); - cheats.expectRevert("BLSPubkeyRegistry._processQuorumApkUpdate: quorum does not exist"); + cheats.expectRevert("BitmapUtils.orderedBytesArrayToBitmap: bitmap exceeds max value"); registryCoordinator.registerOperator(quorumNumbersNotCreated, defaultSocket); } @@ -153,17 +154,17 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { stakeRegistry.setOperatorWeight(uint8(quorumNumbers[0]), defaultOperator, defaultStake); - cheats.prank(defaultOperator); + cheats.expectEmit(true, true, true, true, address(registryCoordinator)); + emit OperatorSocketUpdate(defaultOperatorId, defaultSocket); 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(); + cheats.prank(defaultOperator); registryCoordinator.registerOperator(quorumNumbers, defaultSocket); uint256 gasAfter = gasleft(); emit log_named_uint("gasUsed", gasBefore - gasAfter); @@ -173,7 +174,7 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { assertEq(registryCoordinator.getOperatorId(defaultOperator), defaultOperatorId); assertEq( keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), - keccak256(abi.encode(IRegistryCoordinator.Operator({ + keccak256(abi.encode(IRegistryCoordinator.OperatorInfo({ operatorId: defaultOperatorId, status: IRegistryCoordinator.OperatorStatus.REGISTERED }))) @@ -198,7 +199,9 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { stakeRegistry.setOperatorWeight(uint8(quorumNumbers[i]), defaultOperator, defaultStake); } - cheats.prank(defaultOperator); + cheats.expectEmit(true, true, true, true, address(registryCoordinator)); + emit OperatorSocketUpdate(defaultOperatorId, defaultSocket); + cheats.expectEmit(true, true, true, true, address(blsPubkeyRegistry)); emit OperatorAddedToQuorums(defaultOperator, quorumNumbers); @@ -211,10 +214,9 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { 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(); + cheats.prank(defaultOperator); registryCoordinator.registerOperator(quorumNumbers, defaultSocket); uint256 gasAfter = gasleft(); emit log_named_uint("gasUsed", gasBefore - gasAfter); @@ -223,7 +225,7 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { assertEq(registryCoordinator.getOperatorId(defaultOperator), defaultOperatorId); assertEq( keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), - keccak256(abi.encode(IRegistryCoordinator.Operator({ + keccak256(abi.encode(IRegistryCoordinator.OperatorInfo({ operatorId: defaultOperatorId, status: IRegistryCoordinator.OperatorStatus.REGISTERED }))) @@ -255,16 +257,16 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { newQuorumNumbers[0] = bytes1(defaultQuorumNumber+1); stakeRegistry.setOperatorWeight(uint8(newQuorumNumbers[0]), defaultOperator, defaultStake); - cheats.prank(defaultOperator); + cheats.expectEmit(true, true, true, true, address(registryCoordinator)); + emit OperatorSocketUpdate(defaultOperatorId, defaultSocket); 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); + cheats.prank(defaultOperator); registryCoordinator.registerOperator(newQuorumNumbers, defaultSocket); uint256 quorumBitmap = BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers) | BitmapUtils.orderedBytesArrayToBitmap(newQuorumNumbers); @@ -272,7 +274,7 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { assertEq(registryCoordinator.getOperatorId(defaultOperator), defaultOperatorId); assertEq( keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), - keccak256(abi.encode(IRegistryCoordinator.Operator({ + keccak256(abi.encode(IRegistryCoordinator.OperatorInfo({ operatorId: defaultOperatorId, status: IRegistryCoordinator.OperatorStatus.REGISTERED }))) @@ -322,7 +324,7 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { stakeRegistry.setOperatorWeight(defaultQuorumNumber, operatorToRegister, defaultStake); cheats.prank(operatorToRegister); - cheats.expectRevert("BLSRegistryCoordinatorWithIndices.registerOperator: quorum is overfilled"); + cheats.expectRevert("BLSRegistryCoordinatorWithIndices.registerOperator: operator count exceeds maximum"); registryCoordinator.registerOperator(quorumNumbers, defaultSocket); } @@ -380,7 +382,7 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { quorumNumbers[0] = bytes1(defaultQuorumNumber + 1); quorumNumbers[1] = bytes1(defaultQuorumNumber + 2); - cheats.expectRevert("BLSRegistryCoordinatorWithIndices._deregisterOperator: operator is not registered for any of the provided quorums"); + cheats.expectRevert("BLSRegistryCoordinatorWithIndices._deregisterOperator: operator is not registered for specified quorums"); cheats.prank(defaultOperator); registryCoordinator.deregisterOperator(quorumNumbers); } @@ -416,7 +418,7 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { assertEq( keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), - keccak256(abi.encode(IRegistryCoordinator.Operator({ + keccak256(abi.encode(IRegistryCoordinator.OperatorInfo({ operatorId: defaultOperatorId, status: IRegistryCoordinator.OperatorStatus.DEREGISTERED }))) @@ -467,7 +469,7 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { assertEq( keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), - keccak256(abi.encode(IRegistryCoordinator.Operator({ + keccak256(abi.encode(IRegistryCoordinator.OperatorInfo({ operatorId: defaultOperatorId, status: IRegistryCoordinator.OperatorStatus.DEREGISTERED }))) @@ -541,7 +543,7 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { assertEq( keccak256(abi.encode(registryCoordinator.getOperator(operatorToDerigister))), - keccak256(abi.encode(IRegistryCoordinator.Operator({ + keccak256(abi.encode(IRegistryCoordinator.OperatorInfo({ operatorId: operatorToDerigisterId, status: IRegistryCoordinator.OperatorStatus.DEREGISTERED }))) @@ -579,28 +581,32 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { // check success of registration uint256 quorumBitmap = BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers); - assertEq(registryCoordinator.getOperatorId(defaultOperator), defaultOperatorId); + assertEq(registryCoordinator.getOperatorId(defaultOperator), defaultOperatorId, "1"); assertEq( keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), - keccak256(abi.encode(IRegistryCoordinator.Operator({ + keccak256(abi.encode(IRegistryCoordinator.OperatorInfo({ operatorId: defaultOperatorId, status: IRegistryCoordinator.OperatorStatus.REGISTERED - }))) + }))), + "2" ); - assertEq(registryCoordinator.getCurrentQuorumBitmap(defaultOperatorId), quorumBitmap); + assertEq(registryCoordinator.getCurrentQuorumBitmap(defaultOperatorId), quorumBitmap, "3"); // check that previous entry in bitmap history was not changed assertEq( keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0))), - keccak256(abi.encode(previousQuorumBitmapUpdate)) + keccak256(abi.encode(previousQuorumBitmapUpdate)), + "4" ); // check that new entry in bitmap history is as expected + uint historyLength = registryCoordinator.getQuorumBitmapHistoryLength(defaultOperatorId); assertEq( - keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 1))), + keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, historyLength - 1))), keccak256(abi.encode(IRegistryCoordinator.QuorumBitmapUpdate({ quorumBitmap: uint192(quorumBitmap), updateBlockNumber: uint32(reregistrationBlockNumber), nextUpdateBlockNumber: 0 - }))) + }))), + "5" ); } @@ -684,14 +690,14 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { assertEq( keccak256(abi.encode(registryCoordinator.getOperator(operatorToRegister))), - keccak256(abi.encode(IRegistryCoordinator.Operator({ + keccak256(abi.encode(IRegistryCoordinator.OperatorInfo({ operatorId: operatorToRegisterId, status: IRegistryCoordinator.OperatorStatus.REGISTERED }))) ); assertEq( keccak256(abi.encode(registryCoordinator.getOperator(operatorToKick))), - keccak256(abi.encode(IRegistryCoordinator.Operator({ + keccak256(abi.encode(IRegistryCoordinator.OperatorInfo({ operatorId: operatorToKickId, status: IRegistryCoordinator.OperatorStatus.DEREGISTERED }))) @@ -722,7 +728,7 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { cheats.roll(registrationBlockNumber); ISignatureUtils.SignatureWithSaltAndExpiry memory signatureWithExpiry = _signOperatorChurnApproval(operatorToRegisterId, operatorKickParams, defaultSalt, block.timestamp + 10); cheats.prank(operatorToRegister); - cheats.expectRevert("BLSRegistryCoordinatorWithIndices.registerOperatorWithChurn: registering operator has less than kickBIPsOfOperatorStake"); + cheats.expectRevert("BLSRegistryCoordinatorWithIndices._validateChurn: incoming operator has insufficient stake for churn"); registryCoordinator.registerOperatorWithChurn(quorumNumbers, defaultSocket, operatorKickParams, signatureWithExpiry); } @@ -745,7 +751,7 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { cheats.roll(registrationBlockNumber); ISignatureUtils.SignatureWithSaltAndExpiry memory signatureWithExpiry = _signOperatorChurnApproval(operatorToRegisterId, operatorKickParams, defaultSalt, block.timestamp + 10); cheats.prank(operatorToRegister); - cheats.expectRevert("BLSRegistryCoordinatorWithIndices.registerOperatorWithChurn: operator to kick has more than kickBIPSOfTotalStake"); + cheats.expectRevert("BLSRegistryCoordinatorWithIndices._validateChurn: cannot kick operator with more than kickBIPsOfTotalStake"); registryCoordinator.registerOperatorWithChurn(quorumNumbers, defaultSocket, operatorKickParams, signatureWithExpiry); } @@ -816,7 +822,7 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { // make sure the operator is deregistered assertEq( keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), - keccak256(abi.encode(IRegistryCoordinator.Operator({ + keccak256(abi.encode(IRegistryCoordinator.OperatorInfo({ operatorId: defaultOperatorId, status: IRegistryCoordinator.OperatorStatus.DEREGISTERED }))) @@ -854,7 +860,7 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { // make sure the operator is registered assertEq( keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), - keccak256(abi.encode(IRegistryCoordinator.Operator({ + keccak256(abi.encode(IRegistryCoordinator.OperatorInfo({ operatorId: defaultOperatorId, status: IRegistryCoordinator.OperatorStatus.REGISTERED }))) diff --git a/test/unit/StakeRegistryUnit.t.sol b/test/unit/StakeRegistryUnit.t.sol index 2a362c8f..cd0f2da9 100644 --- a/test/unit/StakeRegistryUnit.t.sol +++ b/test/unit/StakeRegistryUnit.t.sol @@ -207,10 +207,10 @@ contract StakeRegistryUnitTests is Test { 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.getOperatorStakeHistoryLength(defaultOperatorId, i), 1); + assertEq(stakeRegistry.getStakeHistoryLength(defaultOperatorId, i), 1); // make sure that the stake update is as expected IStakeRegistry.OperatorStakeUpdate memory stakeUpdate = - stakeRegistry.getStakeUpdateForOperatorAtIndex(i, defaultOperatorId, 0); + stakeRegistry.getStakeUpdateAtIndex(i, defaultOperatorId, 0); emit log_named_uint("length of paddedStakesForQuorum", paddedStakesForQuorum.length); assertEq(stakeUpdate.stake, paddedStakesForQuorum[quorumNumberIndex]); assertEq(stakeUpdate.updateBlockNumber, uint32(block.number)); @@ -227,7 +227,7 @@ contract StakeRegistryUnitTests is Test { quorumNumberIndex++; } else { // check that the operator has 0 stake updates in the quorum numbers they did not register for - assertEq(stakeRegistry.getOperatorStakeHistoryLength(defaultOperatorId, i), 0); + assertEq(stakeRegistry.getStakeHistoryLength(defaultOperatorId, i), 0); // make the analogous check for total stake history assertEq(stakeRegistry.getTotalStakeHistoryLength(i), 1); } @@ -382,10 +382,10 @@ contract StakeRegistryUnitTests is Test { 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.getOperatorStakeHistoryLength(operatorIdToDeregister, i), 2, "testDeregisterFirstOperator_Valid_0"); + assertEq(stakeRegistry.getStakeHistoryLength(operatorIdToDeregister, i), 2, "testDeregisterFirstOperator_Valid_0"); // make sure that the last stake update is as expected IStakeRegistry.OperatorStakeUpdate memory lastStakeUpdate = - stakeRegistry.getStakeUpdateForOperatorAtIndex(i, operatorIdToDeregister, 1); + stakeRegistry.getStakeUpdateAtIndex(i, operatorIdToDeregister, 1); assertEq(lastStakeUpdate.stake, 0, "testDeregisterFirstOperator_Valid_1"); assertEq(lastStakeUpdate.updateBlockNumber, cumulativeBlockNumber, "testDeregisterFirstOperator_Valid_2"); assertEq(lastStakeUpdate.nextUpdateBlockNumber, 0, "testDeregisterFirstOperator_Valid_3"); @@ -404,12 +404,12 @@ contract StakeRegistryUnitTests is Test { assertEq(lastTotalStakeUpdate.nextUpdateBlockNumber, 0, "testDeregisterFirstOperator_Valid_7"); quorumNumberIndex++; } else if (quorumBitmap >> i & 1 == 1) { - assertEq(stakeRegistry.getOperatorStakeHistoryLength(operatorIdToDeregister, i), 1, "testDeregisterFirstOperator_Valid_8"); + assertEq(stakeRegistry.getStakeHistoryLength(operatorIdToDeregister, i), 1, "testDeregisterFirstOperator_Valid_8"); assertEq(stakeRegistry.getTotalStakeHistoryLength(i), numOperatorsInQuorum[i] + 1, "testDeregisterFirstOperator_Valid_9"); quorumNumberIndex++; } else { // check that the operator has 0 stake updates in the quorum numbers they did not register for - assertEq(stakeRegistry.getOperatorStakeHistoryLength(operatorIdToDeregister, i), 0, "testDeregisterFirstOperator_Valid_10"); + assertEq(stakeRegistry.getStakeHistoryLength(operatorIdToDeregister, i), 0, "testDeregisterFirstOperator_Valid_10"); } } } @@ -425,21 +425,30 @@ contract StakeRegistryUnitTests is Test { 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++) { + uint i = 0; + for (; i < blocksPassed.length; i++) { + uint96 weight = stakes[i]; + uint96 minimum = stakeRegistry.minimumStakeForQuorum(uint8(defaultQuorumNumber)); + emit log_named_uint("set weight: ", weight); + emit log_named_uint("minimum: ", minimum); 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); + bytes memory quorumNumbers = new bytes(1); + quorumNumbers[0] = bytes1(defaultQuorumNumber); + cheats.prank(address(registryCoordinator)); + stakeRegistry.updateOperatorStake(defaultOperator, defaultOperatorId, quorumNumbers); + + uint96 curWeight = stakeRegistry.getCurrentStake(defaultOperatorId, defaultQuorumNumber); + emit log_named_uint("new weight: ", curWeight); cumulativeBlockNumber += blocksPassed[i]; cheats.roll(cumulativeBlockNumber); } // make sure that the last stake update is as expected - IStakeRegistry.OperatorStakeUpdate memory lastOperatorStakeUpdate = stakeRegistry.getMostRecentStakeUpdateByOperatorId(defaultOperatorId, defaultQuorumNumber); - assertEq(lastOperatorStakeUpdate.stake, stakes[blocksPassed.length - 1]); - assertEq(lastOperatorStakeUpdate.nextUpdateBlockNumber, uint32(0)); + IStakeRegistry.OperatorStakeUpdate memory lastOperatorStakeUpdate = stakeRegistry.getLatestStakeUpdate(defaultOperatorId, defaultQuorumNumber); + assertEq(lastOperatorStakeUpdate.stake, stakes[i - 1], "1"); + assertEq(lastOperatorStakeUpdate.nextUpdateBlockNumber, uint32(0), "2"); } function testRecordTotalStakeUpdate_Valid( @@ -476,40 +485,6 @@ contract StakeRegistryUnitTests is Test { } } - 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++) { - emit log_named_uint("quorum", uint8(quorumNumbers[i])); - emit log_named_uint("stake", uint96(stakesForQuorum[i])); - } - - for(uint i = 0; i < stakesForQuorum.length; i++) { - stakeRegistry.setOperatorWeight(uint8(quorumNumbers[i]), defaultOperator, stakesForQuorum[i] + 1); - emit log_named_uint("updating quorum", uint8(quorumNumbers[i])); - emit log_named_uint("to stake", uint96(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.getMostRecentStakeUpdateByOperatorId(defaultOperatorId, uint8(quorumNumbers[i])); - emit log_named_uint("quorum", uint8(quorumNumbers[i])); - emit log_named_uint("most recent stake", operatorStakeUpdate.stake); - assertEq(operatorStakeUpdate.stake, stakesForQuorum[i] + 1); - } - } - function _initializeQuorum( uint8 quorumNumber, uint96 minimumStake, From 48cfdc33bbffd6512afdc3cd4fae3249775ce6ca Mon Sep 17 00:00:00 2001 From: wadealexc Date: Wed, 8 Nov 2023 17:48:10 +0000 Subject: [PATCH 046/101] style: reword comments and remove duplicate check --- src/BLSRegistryCoordinatorWithIndices.sol | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/BLSRegistryCoordinatorWithIndices.sol b/src/BLSRegistryCoordinatorWithIndices.sol index 2380ffeb..e452f630 100644 --- a/src/BLSRegistryCoordinatorWithIndices.sol +++ b/src/BLSRegistryCoordinatorWithIndices.sol @@ -156,7 +156,7 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr ) external onlyWhenNotPaused(PAUSED_REGISTER_OPERATOR) { bytes32 operatorId = blsPubkeyRegistry.getOperatorId(msg.sender); - // Register the operator, failing if churn is needed + // Register the operator in each of the registry contracts RegisterResults memory results = _registerOperator({ operatorToRegister: msg.sender, idToRegister: operatorId, @@ -205,7 +205,7 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr churnApproverSignature: churnApproverSignature }); - // Register the operator and perform churn if needed + // Register the operator in each of the registry contracts RegisterResults memory results = _registerOperator({ operatorToRegister: msg.sender, idToRegister: idToRegister, @@ -441,7 +441,6 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr ) internal view { address operatorToKick = kickParams.operator; bytes32 idToKick = _operatorInfo[operatorToKick].operatorId; - require(_operatorInfo[operatorToKick].status == OperatorStatus.REGISTERED, "BLSRegistryCoordinatorWithIndices._validateChurn: cannot remove unregistered operator"); require(newOperator != operatorToKick, "BLSRegistryCoordinatorWithIndices._validateChurn: cannot churn self"); require(kickParams.quorumNumber == quorumNumber, "BLSRegistryCoordinatorWithIndices._validateChurn: quorumNumber not the same as signed"); From c5569cb24a240ef8e8fb950527c69b432eb82897 Mon Sep 17 00:00:00 2001 From: wadealexc Date: Wed, 8 Nov 2023 19:25:10 +0000 Subject: [PATCH 047/101] style: remove unneeded parenthesis and fix _registerOperator naming --- src/BLSRegistryCoordinatorWithIndices.sol | 40 +++++++++++------------ 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/src/BLSRegistryCoordinatorWithIndices.sol b/src/BLSRegistryCoordinatorWithIndices.sol index e452f630..a97d93ce 100644 --- a/src/BLSRegistryCoordinatorWithIndices.sol +++ b/src/BLSRegistryCoordinatorWithIndices.sol @@ -158,8 +158,8 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr // Register the operator in each of the registry contracts RegisterResults memory results = _registerOperator({ - operatorToRegister: msg.sender, - idToRegister: operatorId, + operator: msg.sender, + operatorId: operatorId, quorumNumbers: quorumNumbers, socket: socket }); @@ -196,19 +196,19 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr ) external onlyWhenNotPaused(PAUSED_REGISTER_OPERATOR) { require(operatorKickParams.length == quorumNumbers.length, "BLSRegistryCoordinatorWithIndices.registerOperatorWithChurn: input length mismatch"); - bytes32 idToRegister = blsPubkeyRegistry.getOperatorId(msg.sender); + bytes32 operatorId = blsPubkeyRegistry.getOperatorId(msg.sender); // Verify the churn approver's signature for the registering operator and kick params _verifyChurnApproverSignature({ - registeringOperatorId: idToRegister, + registeringOperatorId: operatorId, operatorKickParams: operatorKickParams, churnApproverSignature: churnApproverSignature }); // Register the operator in each of the registry contracts RegisterResults memory results = _registerOperator({ - operatorToRegister: msg.sender, - idToRegister: idToRegister, + operator: msg.sender, + operatorId: operatorId, quorumNumbers: quorumNumbers, socket: socket }); @@ -278,7 +278,7 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr * Update the operator's stake for their active quorums. The stakeRegistry returns a bitmap * of quorums where the operator no longer meets the minimum stake, and should be deregistered. */ - (uint192 quorumsToRemove) = stakeRegistry.updateOperatorStake(operator, operatorId, currentQuorums); + uint192 quorumsToRemove = stakeRegistry.updateOperatorStake(operator, operatorId, currentQuorums); if (!quorumsToRemove.isEmpty()) { _deregisterOperator({ @@ -378,8 +378,8 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr * operator's quorum bitmap, socket, and status, then registers them with each registry. */ function _registerOperator( - address operatorToRegister, - bytes32 idToRegister, + address operator, + bytes32 operatorId, bytes calldata quorumNumbers, string memory socket ) internal virtual returns (RegisterResults memory) { @@ -390,7 +390,7 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr * Then, calculate the operator's new bitmap after registration */ uint192 quorumsToAdd = uint192(BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers, quorumCount)); - uint192 currentBitmap = _currentOperatorBitmap(idToRegister); + uint192 currentBitmap = _currentOperatorBitmap(operatorId); require(!quorumsToAdd.isEmpty(), "BLSRegistryCoordinatorWithIndices._registerOperator: bitmap cannot be 0"); require(quorumsToAdd.noBitsInCommon(currentBitmap), "BLSRegistryCoordinatorWithIndices._registerOperator: operator already registered for some quorums being registered for"); uint192 newBitmap = uint192(currentBitmap.plus(quorumsToAdd)); @@ -400,29 +400,29 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr * if we're `REGISTERED`, the operatorId and status are already correct. */ _updateOperatorBitmap({ - operatorId: idToRegister, + operatorId: operatorId, newBitmap: newBitmap }); - emit OperatorSocketUpdate(idToRegister, socket); + emit OperatorSocketUpdate(operatorId, socket); - if (_operatorInfo[operatorToRegister].status != OperatorStatus.REGISTERED) { - _operatorInfo[operatorToRegister] = OperatorInfo({ - operatorId: idToRegister, + if (_operatorInfo[operator].status != OperatorStatus.REGISTERED) { + _operatorInfo[operator] = OperatorInfo({ + operatorId: operatorId, status: OperatorStatus.REGISTERED }); - emit OperatorRegistered(operatorToRegister, idToRegister); + emit OperatorRegistered(operator, operatorId); } /** * Register the operator with the BLSPubkeyRegistry, StakeRegistry, and IndexRegistry */ - bytes32 registeredId = blsPubkeyRegistry.registerOperator(operatorToRegister, quorumNumbers); - require(registeredId == idToRegister, "BLSRegistryCoordinatorWithIndices._registerOperator: operatorId mismatch"); + bytes32 registeredId = blsPubkeyRegistry.registerOperator(operator, quorumNumbers); + require(registeredId == operatorId, "BLSRegistryCoordinatorWithIndices._registerOperator: operatorId mismatch"); (uint96[] memory operatorStakes, uint96[] memory totalStakes) = - stakeRegistry.registerOperator(operatorToRegister, idToRegister, quorumNumbers); - uint32[] memory numOperatorsPerQuorum = indexRegistry.registerOperator(idToRegister, quorumNumbers); + stakeRegistry.registerOperator(operator, operatorId, quorumNumbers); + uint32[] memory numOperatorsPerQuorum = indexRegistry.registerOperator(operatorId, quorumNumbers); return RegisterResults({ numOperatorsPerQuorum: numOperatorsPerQuorum, From 8c0ee3f749e34475769192b2cae5de7eaeb6863c Mon Sep 17 00:00:00 2001 From: Alex <18387287+wadealexc@users.noreply.github.com> Date: Thu, 9 Nov 2023 16:13:41 -0500 Subject: [PATCH 048/101] style: rename OperatorStakeUpdate struct to StakeUpdate, and swap event to match (#61) --- src/StakeRegistry.sol | 50 ++++++++----------- src/StakeRegistryStorage.sol | 4 +- src/interfaces/IStakeRegistry.sol | 14 +++--- test/mocks/StakeRegistryMock.sol | 10 ++-- ...LSRegistryCoordinatorWithIndicesUnit.t.sol | 22 ++++---- test/unit/StakeRegistryUnit.t.sol | 16 +++--- 6 files changed, 53 insertions(+), 63 deletions(-) diff --git a/src/StakeRegistry.sol b/src/StakeRegistry.sol index 074728da..acb25268 100644 --- a/src/StakeRegistry.sol +++ b/src/StakeRegistry.sol @@ -116,7 +116,7 @@ contract StakeRegistry is StakeRegistryStorage { * For each quorum, remove the operator's stake for the quorum and update * the quorum's total stake to account for the removal */ - for (uint256 i = 0; i < quorumNumbers.length; ) { + for (uint256 i = 0; i < quorumNumbers.length; i++) { uint8 quorumNumber = uint8(quorumNumbers[i]); require(_quorumExists(quorumNumber), "StakeRegistry.deregisterOperator: quorum does not exist"); @@ -129,10 +129,6 @@ contract StakeRegistry is StakeRegistryStorage { // Apply the operator's stake delta to the total stake for this quorum _recordTotalStakeUpdate(quorumNumber, stakeDelta); - - unchecked { - ++i; - } } } @@ -199,7 +195,7 @@ contract StakeRegistry is StakeRegistryStorage { _addStrategyParams(quorumNumber, _strategyParams); _setMinimumStakeForQuorum(quorumNumber, minimumStake); - _totalStakeHistory[quorumNumber].push(OperatorStakeUpdate({ + _totalStakeHistory[quorumNumber].push(StakeUpdate({ updateBlockNumber: uint32(block.number), nextUpdateBlockNumber: 0, stake: 0 @@ -320,14 +316,14 @@ contract StakeRegistry is StakeRegistryStorage { if (historyLength == 0) { // No prior stake history - push our first entry - operatorStakeHistory[operatorId][quorumNumber].push(OperatorStakeUpdate({ + operatorStakeHistory[operatorId][quorumNumber].push(StakeUpdate({ updateBlockNumber: uint32(block.number), nextUpdateBlockNumber: 0, stake: newStake })); } else { // We have prior stake history - fetch our last-recorded stake - OperatorStakeUpdate storage lastUpdate = operatorStakeHistory[operatorId][quorumNumber][historyLength-1]; + StakeUpdate storage lastUpdate = operatorStakeHistory[operatorId][quorumNumber][historyLength-1]; prevStake = lastUpdate.stake; // Short-circuit in case there's no change in stake @@ -343,7 +339,7 @@ contract StakeRegistry is StakeRegistryStorage { lastUpdate.stake = newStake; } else { lastUpdate.nextUpdateBlockNumber = uint32(block.number); - operatorStakeHistory[operatorId][quorumNumber].push(OperatorStakeUpdate({ + operatorStakeHistory[operatorId][quorumNumber].push(StakeUpdate({ updateBlockNumber: uint32(block.number), nextUpdateBlockNumber: 0, stake: newStake @@ -352,7 +348,7 @@ contract StakeRegistry is StakeRegistryStorage { } // Log update and return stake delta - emit StakeUpdate(operatorId, quorumNumber, newStake); + emit OperatorStakeUpdate(operatorId, quorumNumber, newStake); return _calculateDelta({ prev: prevStake, cur: newStake }); } @@ -361,7 +357,7 @@ contract StakeRegistry is StakeRegistryStorage { function _recordTotalStakeUpdate(uint8 quorumNumber, int256 stakeDelta) internal returns (uint96) { // Get our last-recorded stake update uint256 historyLength = _totalStakeHistory[quorumNumber].length; - OperatorStakeUpdate storage lastStakeUpdate = _totalStakeHistory[quorumNumber][historyLength - 1]; + StakeUpdate storage lastStakeUpdate = _totalStakeHistory[quorumNumber][historyLength - 1]; // Return early if no update is needed if (stakeDelta == 0) { @@ -379,7 +375,7 @@ contract StakeRegistry is StakeRegistryStorage { lastStakeUpdate.stake = newStake; } else { lastStakeUpdate.nextUpdateBlockNumber = uint32(block.number); - _totalStakeHistory[quorumNumber].push(OperatorStakeUpdate({ + _totalStakeHistory[quorumNumber].push(StakeUpdate({ updateBlockNumber: uint32(block.number), nextUpdateBlockNumber: 0, stake: newStake @@ -406,16 +402,13 @@ contract StakeRegistry is StakeRegistryStorage { numStratsExisting + numStratsToAdd <= MAX_WEIGHING_FUNCTION_LENGTH, "StakeRegistry._addStrategyParams: exceed MAX_WEIGHING_FUNCTION_LENGTH" ); - for (uint256 i = 0; i < numStratsToAdd; ) { + for (uint256 i = 0; i < numStratsToAdd; i++) { // fairly gas-expensive internal loop to make sure that the *same* strategy cannot be added multiple times - for (uint256 j = 0; j < (numStratsExisting + i); ) { + for (uint256 j = 0; j < (numStratsExisting + i); j++) { require( strategyParams[quorumNumber][j].strategy != _strategyParams[i].strategy, "StakeRegistry._addStrategyParams: cannot add same strategy 2x" ); - unchecked { - ++j; - } } require( _strategyParams[i].multiplier > 0, @@ -428,9 +421,6 @@ contract StakeRegistry is StakeRegistryStorage { _strategyParams[i].strategy, _strategyParams[i].multiplier ); - unchecked { - ++i; - } } } @@ -450,7 +440,7 @@ contract StakeRegistry is StakeRegistryStorage { /// @notice Validates that the `operatorStake` was accurate at the given `blockNumber` function _validateOperatorStakeUpdateAtBlockNumber( - OperatorStakeUpdate memory operatorStakeUpdate, + StakeUpdate memory operatorStakeUpdate, uint32 blockNumber ) internal pure { require( @@ -549,7 +539,7 @@ contract StakeRegistry is StakeRegistryStorage { function getStakeHistory( bytes32 operatorId, uint8 quorumNumber - ) external view returns (OperatorStakeUpdate[] memory) { + ) external view returns (StakeUpdate[] memory) { return operatorStakeHistory[operatorId][quorumNumber]; } @@ -558,20 +548,20 @@ contract StakeRegistry is StakeRegistryStorage { * @dev Function returns weight of **0** in the event that the operator has no stake history */ function getCurrentStake(bytes32 operatorId, uint8 quorumNumber) external view returns (uint96) { - OperatorStakeUpdate memory operatorStakeUpdate = getLatestStakeUpdate(operatorId, quorumNumber); + StakeUpdate memory operatorStakeUpdate = getLatestStakeUpdate(operatorId, quorumNumber); return operatorStakeUpdate.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 + * @dev Function returns an StakeUpdate struct with **every entry equal to 0** in the event that the operator has no stake history */ function getLatestStakeUpdate( bytes32 operatorId, uint8 quorumNumber - ) public view returns (OperatorStakeUpdate memory) { + ) public view returns (StakeUpdate memory) { uint256 historyLength = operatorStakeHistory[operatorId][quorumNumber].length; - OperatorStakeUpdate memory operatorStakeUpdate; + StakeUpdate memory operatorStakeUpdate; if (historyLength == 0) { return operatorStakeUpdate; } else { @@ -591,7 +581,7 @@ contract StakeRegistry is StakeRegistryStorage { uint8 quorumNumber, bytes32 operatorId, uint256 index - ) external view returns (OperatorStakeUpdate memory) { + ) external view returns (StakeUpdate memory) { return operatorStakeHistory[operatorId][quorumNumber][index]; } @@ -632,7 +622,7 @@ contract StakeRegistry is StakeRegistryStorage { bytes32 operatorId, uint256 index ) external view returns (uint96) { - OperatorStakeUpdate memory operatorStakeUpdate = operatorStakeHistory[operatorId][quorumNumber][index]; + StakeUpdate memory operatorStakeUpdate = operatorStakeHistory[operatorId][quorumNumber][index]; _validateOperatorStakeUpdateAtBlockNumber(operatorStakeUpdate, blockNumber); return operatorStakeUpdate.stake; } @@ -664,7 +654,7 @@ contract StakeRegistry is StakeRegistryStorage { function getTotalStakeUpdateAtIndex( uint8 quorumNumber, uint256 index - ) external view returns (OperatorStakeUpdate memory) { + ) external view returns (StakeUpdate memory) { return _totalStakeHistory[quorumNumber][index]; } @@ -681,7 +671,7 @@ contract StakeRegistry is StakeRegistryStorage { uint32 blockNumber, uint256 index ) external view returns (uint96) { - OperatorStakeUpdate memory totalStakeUpdate = _totalStakeHistory[quorumNumber][index]; + StakeUpdate memory totalStakeUpdate = _totalStakeHistory[quorumNumber][index]; _validateOperatorStakeUpdateAtBlockNumber(totalStakeUpdate, blockNumber); return totalStakeUpdate.stake; } diff --git a/src/StakeRegistryStorage.sol b/src/StakeRegistryStorage.sol index c5e2f305..7a950597 100644 --- a/src/StakeRegistryStorage.sol +++ b/src/StakeRegistryStorage.sol @@ -42,10 +42,10 @@ abstract contract StakeRegistryStorage is IStakeRegistry { 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; + StakeUpdate[][256] internal _totalStakeHistory; /// @notice mapping from operator's operatorId to the history of their stake updates - mapping(bytes32 => mapping(uint8 => OperatorStakeUpdate[])) internal operatorStakeHistory; + mapping(bytes32 => mapping(uint8 => StakeUpdate[])) internal operatorStakeHistory; /** * @notice mapping from quorum number to the list of strategies considered and their diff --git a/src/interfaces/IStakeRegistry.sol b/src/interfaces/IStakeRegistry.sol index 5a7409b0..880557b6 100644 --- a/src/interfaces/IStakeRegistry.sol +++ b/src/interfaces/IStakeRegistry.sol @@ -16,7 +16,7 @@ interface IStakeRegistry is IRegistry { // DATA STRUCTURES /// @notice struct used to store the stakes of an individual operator or the sum of all operators' stakes, for storage - struct OperatorStakeUpdate { + struct StakeUpdate { // the block number at which the stake amounts were updated and stored uint32 updateBlockNumber; // the block number at which the *next update* occurred. @@ -38,7 +38,7 @@ interface IStakeRegistry is IRegistry { // EVENTS /// @notice emitted whenever the stake of `operator` is updated - event StakeUpdate( + event OperatorStakeUpdate( bytes32 indexed operatorId, uint8 quorumNumber, uint96 stake @@ -151,7 +151,7 @@ interface IStakeRegistry is IRegistry { * @param operatorId The id of the operator of interest. * @param quorumNumber The quorum number to get the stake for. */ - function getStakeHistory(bytes32 operatorId, uint8 quorumNumber) external view returns (OperatorStakeUpdate[] memory); + function getStakeHistory(bytes32 operatorId, uint8 quorumNumber) external view returns (StakeUpdate[] memory); function getTotalStakeHistoryLength(uint8 quorumNumber) external view returns (uint256); @@ -160,7 +160,7 @@ interface IStakeRegistry is IRegistry { * @param quorumNumber The quorum number to get the stake for. * @param index Array index for lookup, within the dynamic array `totalStakeHistory[quorumNumber]`. */ - function getTotalStakeUpdateAtIndex(uint8 quorumNumber, uint256 index) external view returns (OperatorStakeUpdate memory); + function getTotalStakeUpdateAtIndex(uint8 quorumNumber, uint256 index) external view returns (StakeUpdate memory); /// @notice Returns the indices of the operator stakes for the provided `quorumNumber` at the given `blockNumber` function getStakeUpdateIndexAtBlockNumber(bytes32 operatorId, uint8 quorumNumber, uint32 blockNumber) @@ -181,13 +181,13 @@ interface IStakeRegistry is IRegistry { function getStakeUpdateAtIndex(uint8 quorumNumber, bytes32 operatorId, uint256 index) external view - returns (OperatorStakeUpdate memory); + returns (StakeUpdate memory); /** * @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 + * @dev Function returns an StakeUpdate struct with **every entry equal to 0** in the event that the operator has no stake history */ - function getLatestStakeUpdate(bytes32 operatorId, uint8 quorumNumber) external view returns (OperatorStakeUpdate memory); + function getLatestStakeUpdate(bytes32 operatorId, uint8 quorumNumber) external view returns (StakeUpdate memory); /** * @notice Returns the stake weight corresponding to `operatorId` for quorum `quorumNumber`, at the diff --git a/test/mocks/StakeRegistryMock.sol b/test/mocks/StakeRegistryMock.sol index e50ff865..bc2394b9 100644 --- a/test/mocks/StakeRegistryMock.sol +++ b/test/mocks/StakeRegistryMock.sol @@ -104,7 +104,7 @@ contract StakeRegistryMock is IStakeRegistry { * @param operatorId The id of the operator of interest. * @param quorumNumber The quorum number to get the stake for. */ - function getStakeHistory(bytes32 operatorId, uint8 quorumNumber) external view returns (OperatorStakeUpdate[] memory) {} + function getStakeHistory(bytes32 operatorId, uint8 quorumNumber) external view returns (StakeUpdate[] memory) {} function getTotalStakeHistoryLength(uint8 quorumNumber) external view returns (uint256) {} @@ -113,7 +113,7 @@ contract StakeRegistryMock is IStakeRegistry { * @param quorumNumber The quorum number to get the stake for. * @param index Array index for lookup, within the dynamic array `totalStakeHistory[quorumNumber]`. */ - function getTotalStakeUpdateAtIndex(uint8 quorumNumber, uint256 index) external view returns (OperatorStakeUpdate memory) {} + function getTotalStakeUpdateAtIndex(uint8 quorumNumber, uint256 index) external view returns (StakeUpdate memory) {} /// @notice Returns the indices of the operator stakes for the provided `quorumNumber` at the given `blockNumber` function getStakeUpdateIndexAtBlockNumber(bytes32 operatorId, uint8 quorumNumber, uint32 blockNumber) @@ -134,13 +134,13 @@ contract StakeRegistryMock is IStakeRegistry { function getStakeUpdateAtIndex(uint8 quorumNumber, bytes32 operatorId, uint256 index) external view - returns (OperatorStakeUpdate memory) {} + returns (StakeUpdate memory) {} /** * @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 + * @dev Function returns an StakeUpdate struct with **every entry equal to 0** in the event that the operator has no stake history */ - function getLatestStakeUpdate(bytes32 operatorId, uint8 quorumNumber) external view returns (OperatorStakeUpdate memory) {} + function getLatestStakeUpdate(bytes32 operatorId, uint8 quorumNumber) external view returns (StakeUpdate memory) {} /** * @notice Returns the stake weight corresponding to `operatorId` for quorum `quorumNumber`, at the diff --git a/test/unit/BLSRegistryCoordinatorWithIndicesUnit.t.sol b/test/unit/BLSRegistryCoordinatorWithIndicesUnit.t.sol index cab43e49..4ae3900c 100644 --- a/test/unit/BLSRegistryCoordinatorWithIndicesUnit.t.sol +++ b/test/unit/BLSRegistryCoordinatorWithIndicesUnit.t.sol @@ -12,7 +12,7 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { event OperatorSocketUpdate(bytes32 indexed operatorId, string socket); /// @notice emitted whenever the stake of `operator` is updated - event StakeUpdate( + event OperatorStakeUpdate( bytes32 indexed operatorId, uint8 quorumNumber, uint96 stake @@ -159,7 +159,7 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { 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); + emit OperatorStakeUpdate(defaultOperatorId, defaultQuorumNumber, defaultStake); cheats.expectEmit(true, true, true, true, address(indexRegistry)); emit QuorumIndexUpdate(defaultOperatorId, defaultQuorumNumber, 0); @@ -207,7 +207,7 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { for (uint i = 0; i < quorumNumbers.length; i++) { cheats.expectEmit(true, true, true, true, address(stakeRegistry)); - emit StakeUpdate(defaultOperatorId, uint8(quorumNumbers[i]), defaultStake); + emit OperatorStakeUpdate(defaultOperatorId, uint8(quorumNumbers[i]), defaultStake); } for (uint i = 0; i < quorumNumbers.length; i++) { @@ -262,7 +262,7 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { 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); + emit OperatorStakeUpdate(defaultOperatorId, uint8(newQuorumNumbers[0]), defaultStake); cheats.expectEmit(true, true, true, true, address(indexRegistry)); emit QuorumIndexUpdate(defaultOperatorId, uint8(newQuorumNumbers[0]), 0); cheats.roll(nextRegistrationBlockNumber); @@ -407,7 +407,7 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { 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); + emit OperatorStakeUpdate(defaultOperatorId, defaultQuorumNumber, 0); cheats.roll(deregistrationBlockNumber); @@ -456,7 +456,7 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { 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); + emit OperatorStakeUpdate(defaultOperatorId, uint8(quorumNumbers[i]), 0); } cheats.roll(deregistrationBlockNumber); @@ -533,7 +533,7 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { for (uint i = 0; i < operatorToDeregisterQuorumNumbers.length; i++) { cheats.expectEmit(true, true, true, true, address(stakeRegistry)); - emit StakeUpdate(operatorToDerigisterId, uint8(operatorToDeregisterQuorumNumbers[i]), 0); + emit OperatorStakeUpdate(operatorToDerigisterId, uint8(operatorToDeregisterQuorumNumbers[i]), 0); } cheats.roll(deregistrationBlockNumber); @@ -663,14 +663,14 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { 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); + emit OperatorStakeUpdate(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); + emit OperatorStakeUpdate(operatorToKickId, defaultQuorumNumber, 0); cheats.expectEmit(true, true, true, true, address(indexRegistry)); emit QuorumIndexUpdate(operatorToRegisterId, defaultQuorumNumber, numOperators - 1); @@ -813,7 +813,7 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { emit OperatorRemovedFromQuorums(defaultOperator, quorumNumbers); cheats.expectEmit(true, true, true, true, address(stakeRegistry)); - emit StakeUpdate(defaultOperatorId, uint8(quorumNumbers[0]), 0); + emit OperatorStakeUpdate(defaultOperatorId, uint8(quorumNumbers[0]), 0); // eject cheats.prank(ejector); @@ -852,7 +852,7 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { emit OperatorRemovedFromQuorums(defaultOperator, quorumNumbersToEject); cheats.expectEmit(true, true, true, true, address(stakeRegistry)); - emit StakeUpdate(defaultOperatorId, uint8(quorumNumbersToEject[0]), 0); + emit OperatorStakeUpdate(defaultOperatorId, uint8(quorumNumbersToEject[0]), 0); cheats.prank(ejector); registryCoordinator.ejectOperator(defaultOperator, quorumNumbersToEject); diff --git a/test/unit/StakeRegistryUnit.t.sol b/test/unit/StakeRegistryUnit.t.sol index cd0f2da9..a404baf9 100644 --- a/test/unit/StakeRegistryUnit.t.sol +++ b/test/unit/StakeRegistryUnit.t.sol @@ -72,7 +72,7 @@ contract StakeRegistryUnitTests is Test { uint256 gasUsed; /// @notice emitted whenever the stake of `operator` is updated - event StakeUpdate( + event OperatorStakeUpdate( bytes32 indexed operatorId, uint8 quorumNumber, uint96 stake @@ -209,7 +209,7 @@ contract StakeRegistryUnitTests is Test { // check that the operator has 1 stake update in the quorum numbers they registered for assertEq(stakeRegistry.getStakeHistoryLength(defaultOperatorId, i), 1); // make sure that the stake update is as expected - IStakeRegistry.OperatorStakeUpdate memory stakeUpdate = + IStakeRegistry.StakeUpdate memory stakeUpdate = stakeRegistry.getStakeUpdateAtIndex(i, defaultOperatorId, 0); emit log_named_uint("length of paddedStakesForQuorum", paddedStakesForQuorum.length); assertEq(stakeUpdate.stake, paddedStakesForQuorum[quorumNumberIndex]); @@ -306,13 +306,13 @@ contract StakeRegistryUnitTests is Test { } // make sure that the stake update is as expected - IStakeRegistry.OperatorStakeUpdate memory totalStakeUpdate = + IStakeRegistry.StakeUpdate memory totalStakeUpdate = stakeRegistry.getTotalStakeUpdateAtIndex(i, historyLength-1); assertEq(totalStakeUpdate.stake, cumulativeStake); // make sure that the next update block number of the previous stake update is as expected if (historyLength >= 2) { - IStakeRegistry.OperatorStakeUpdate memory prevTotalStakeUpdate = + IStakeRegistry.StakeUpdate memory prevTotalStakeUpdate = stakeRegistry.getTotalStakeUpdateAtIndex(i, historyLength-2); assertEq(prevTotalStakeUpdate.nextUpdateBlockNumber, cumulativeBlockNumber); } @@ -384,7 +384,7 @@ contract StakeRegistryUnitTests is Test { // check that the operator has 2 stake updates in the quorum numbers they registered for assertEq(stakeRegistry.getStakeHistoryLength(operatorIdToDeregister, i), 2, "testDeregisterFirstOperator_Valid_0"); // make sure that the last stake update is as expected - IStakeRegistry.OperatorStakeUpdate memory lastStakeUpdate = + IStakeRegistry.StakeUpdate memory lastStakeUpdate = stakeRegistry.getStakeUpdateAtIndex(i, operatorIdToDeregister, 1); assertEq(lastStakeUpdate.stake, 0, "testDeregisterFirstOperator_Valid_1"); assertEq(lastStakeUpdate.updateBlockNumber, cumulativeBlockNumber, "testDeregisterFirstOperator_Valid_2"); @@ -393,7 +393,7 @@ contract StakeRegistryUnitTests is Test { // Get history length for quorum uint historyLength = stakeRegistry.getTotalStakeHistoryLength(i); // make sure that the last stake update is as expected - IStakeRegistry.OperatorStakeUpdate memory lastTotalStakeUpdate + IStakeRegistry.StakeUpdate memory lastTotalStakeUpdate = stakeRegistry.getTotalStakeUpdateAtIndex(i, historyLength-1); assertEq(lastTotalStakeUpdate.stake, stakeRegistry.getTotalStakeUpdateAtIndex(i, historyLength-2).stake // the previous total stake @@ -446,7 +446,7 @@ contract StakeRegistryUnitTests is Test { } // make sure that the last stake update is as expected - IStakeRegistry.OperatorStakeUpdate memory lastOperatorStakeUpdate = stakeRegistry.getLatestStakeUpdate(defaultOperatorId, defaultQuorumNumber); + IStakeRegistry.StakeUpdate memory lastOperatorStakeUpdate = stakeRegistry.getLatestStakeUpdate(defaultOperatorId, defaultQuorumNumber); assertEq(lastOperatorStakeUpdate.stake, stakes[i - 1], "1"); assertEq(lastOperatorStakeUpdate.nextUpdateBlockNumber, uint32(0), "2"); } @@ -472,7 +472,7 @@ contract StakeRegistryUnitTests is Test { // Perform the update stakeRegistry.recordTotalStakeUpdate(defaultQuorumNumber, stakeDelta); - IStakeRegistry.OperatorStakeUpdate memory newStakeUpdate; + IStakeRegistry.StakeUpdate memory newStakeUpdate; uint historyLength = stakeRegistry.getTotalStakeHistoryLength(defaultQuorumNumber); if (historyLength != 0) { newStakeUpdate = stakeRegistry.getTotalStakeUpdateAtIndex(defaultQuorumNumber, historyLength-1); From 1a2024bf41dc2510c543051e005424cf11c1fd51 Mon Sep 17 00:00:00 2001 From: quaq <56312047+0x0aa0@users.noreply.github.com> Date: Wed, 29 Nov 2023 17:12:14 -0600 Subject: [PATCH 049/101] cleanup imports (#78) --- src/BLSOperatorStateRetriever.sol | 10 ++--- src/BLSPubkeyRegistry.sol | 7 +++- src/BLSPubkeyRegistryStorage.sol | 12 +++--- src/BLSPublicKeyCompendium.sol | 5 +-- src/BLSRegistryCoordinatorWithIndices.sol | 38 +++++++++---------- src/BLSSignatureChecker.sol | 20 +++++----- src/IndexRegistry.sol | 3 +- src/IndexRegistryStorage.sol | 6 +-- src/ServiceManagerBase.sol | 16 ++++---- src/StakeRegistry.sol | 15 +++++--- src/StakeRegistryStorage.sol | 10 +---- src/interfaces/IBLSRegistry.sol | 1 - .../IBLSRegistryCoordinatorWithIndices.sol | 12 +++--- src/interfaces/IBLSSignatureChecker.sol | 11 +++--- src/interfaces/IIndexRegistry.sol | 2 +- src/interfaces/IQuorumRegistry.sol | 2 +- src/interfaces/IStakeRegistry.sol | 4 +- 17 files changed, 85 insertions(+), 89 deletions(-) diff --git a/src/BLSOperatorStateRetriever.sol b/src/BLSOperatorStateRetriever.sol index 0ce58b15..5d2c89ed 100644 --- a/src/BLSOperatorStateRetriever.sol +++ b/src/BLSOperatorStateRetriever.sol @@ -1,12 +1,12 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity =0.8.12; -import "./interfaces/IStakeRegistry.sol"; -import "./interfaces/IBLSPubkeyRegistry.sol"; -import "./interfaces/IIndexRegistry.sol"; -import "./interfaces/IBLSRegistryCoordinatorWithIndices.sol"; +import {IBLSRegistryCoordinatorWithIndices} from "src/interfaces/IBLSRegistryCoordinatorWithIndices.sol"; +import {IBLSPubkeyRegistry} from "src/interfaces/IBLSPubkeyRegistry.sol"; +import {IStakeRegistry} from "src/interfaces/IStakeRegistry.sol"; +import {IIndexRegistry} from "src/interfaces/IIndexRegistry.sol"; -import "./libraries/BitmapUtils.sol"; +import {BitmapUtils} from "src/libraries/BitmapUtils.sol"; /** * @title BLSOperatorStateRetriever with view functions that allow to retrieve the state of an AVSs registry system. diff --git a/src/BLSPubkeyRegistry.sol b/src/BLSPubkeyRegistry.sol index 1d8028d2..56f404de 100644 --- a/src/BLSPubkeyRegistry.sol +++ b/src/BLSPubkeyRegistry.sol @@ -1,9 +1,12 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity =0.8.12; -import "./libraries/BN254.sol"; +import {BLSPubkeyRegistryStorage} from "src/BLSPubkeyRegistryStorage.sol"; -import "./BLSPubkeyRegistryStorage.sol"; +import {IBLSPublicKeyCompendium} from "src/interfaces/IBLSPublicKeyCompendium.sol"; +import {IRegistryCoordinator} from "src/interfaces/IRegistryCoordinator.sol"; + +import {BN254} from "src/libraries/BN254.sol"; contract BLSPubkeyRegistry is BLSPubkeyRegistryStorage { using BN254 for BN254.G1Point; diff --git a/src/BLSPubkeyRegistryStorage.sol b/src/BLSPubkeyRegistryStorage.sol index 42359387..2c0ce9e7 100644 --- a/src/BLSPubkeyRegistryStorage.sol +++ b/src/BLSPubkeyRegistryStorage.sol @@ -1,13 +1,11 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity =0.8.12; -import "./interfaces/IBLSPubkeyRegistry.sol"; -import "./interfaces/IRegistryCoordinator.sol"; -import "./interfaces/IBLSPublicKeyCompendium.sol"; - -import "./libraries/BN254.sol"; - -import "@openzeppelin-upgrades/contracts/proxy/utils/Initializable.sol"; +import {IBLSPubkeyRegistry} from "src/interfaces/IBLSPubkeyRegistry.sol"; +import {IRegistryCoordinator} from "src/interfaces/IRegistryCoordinator.sol"; +import {IBLSPublicKeyCompendium} from "src/interfaces/IBLSPublicKeyCompendium.sol"; +import {BN254} from "src/libraries/BN254.sol"; +import {Initializable} from "@openzeppelin-upgrades/contracts/proxy/utils/Initializable.sol"; abstract contract BLSPubkeyRegistryStorage is Initializable, IBLSPubkeyRegistry { /// @notice the registry coordinator contract diff --git a/src/BLSPublicKeyCompendium.sol b/src/BLSPublicKeyCompendium.sol index 28c49a97..f9aad7f1 100644 --- a/src/BLSPublicKeyCompendium.sol +++ b/src/BLSPublicKeyCompendium.sol @@ -1,9 +1,8 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity =0.8.12; -import "./interfaces/IBLSPublicKeyCompendium.sol"; - -import "./libraries/BN254.sol"; +import {IBLSPublicKeyCompendium} from "src/interfaces/IBLSPublicKeyCompendium.sol"; +import {BN254} from "src/libraries/BN254.sol"; /** * @title A shared contract for EigenLayer operators to register their BLS public keys. diff --git a/src/BLSRegistryCoordinatorWithIndices.sol b/src/BLSRegistryCoordinatorWithIndices.sol index a97d93ce..c200b59f 100644 --- a/src/BLSRegistryCoordinatorWithIndices.sol +++ b/src/BLSRegistryCoordinatorWithIndices.sol @@ -1,26 +1,24 @@ // 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 "eigenlayer-contracts/src/contracts/interfaces/IPauserRegistry.sol"; -import "eigenlayer-contracts/src/contracts/interfaces/ISlasher.sol"; -import "eigenlayer-contracts/src/contracts/libraries/EIP1271SignatureUtils.sol"; -import "eigenlayer-contracts/src/contracts/permissions/Pausable.sol"; - -import "src/interfaces/IBLSRegistryCoordinatorWithIndices.sol"; -import "src/interfaces/ISocketUpdater.sol"; -import "src/interfaces/IServiceManager.sol"; -import "src/interfaces/IBLSPubkeyRegistry.sol"; -import "src/interfaces/IStakeRegistry.sol"; -import "src/interfaces/IIndexRegistry.sol"; -import "src/interfaces/IRegistryCoordinator.sol"; - -import "src/libraries/BitmapUtils.sol"; -import "src/libraries/BN254.sol"; - - +import {Initializable} from "@openzeppelin-upgrades/contracts/proxy/utils/Initializable.sol"; +import {EIP712} from "@openzeppelin/contracts/utils/cryptography/draft-EIP712.sol"; + +import {EIP1271SignatureUtils} from "eigenlayer-contracts/src/contracts/libraries/EIP1271SignatureUtils.sol"; +import {IPauserRegistry} from "eigenlayer-contracts/src/contracts/interfaces/IPauserRegistry.sol"; +import {Pausable} from "eigenlayer-contracts/src/contracts/permissions/Pausable.sol"; +import {ISlasher} from "eigenlayer-contracts/src/contracts/interfaces/ISlasher.sol"; + +import {IBLSRegistryCoordinatorWithIndices} from "src/interfaces/IBLSRegistryCoordinatorWithIndices.sol"; +import {IRegistryCoordinator} from "src/interfaces/IRegistryCoordinator.sol"; +import {IBLSPubkeyRegistry} from "src/interfaces/IBLSPubkeyRegistry.sol"; +import {IServiceManager} from "src/interfaces/IServiceManager.sol"; +import {ISocketUpdater} from "src/interfaces/ISocketUpdater.sol"; +import {IStakeRegistry} from "src/interfaces/IStakeRegistry.sol"; +import {IIndexRegistry} from "src/interfaces/IIndexRegistry.sol"; + +import {BitmapUtils} from "src/libraries/BitmapUtils.sol"; +import {BN254} from "src/libraries/BN254.sol"; /** * @title A `RegistryCoordinator` that has three registries: diff --git a/src/BLSSignatureChecker.sol b/src/BLSSignatureChecker.sol index 4d440e92..d5cb998a 100644 --- a/src/BLSSignatureChecker.sol +++ b/src/BLSSignatureChecker.sol @@ -1,14 +1,14 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity =0.8.12; -import "./interfaces/IRegistryCoordinator.sol"; -import "./interfaces/IStakeRegistry.sol"; -import "./interfaces/IBLSPubkeyRegistry.sol"; -import "./interfaces/IBLSRegistryCoordinatorWithIndices.sol"; -import "./interfaces/IBLSSignatureChecker.sol"; +import {IBLSRegistryCoordinatorWithIndices} from "src/interfaces/IBLSRegistryCoordinatorWithIndices.sol"; +import {IBLSSignatureChecker} from "src/interfaces/IBLSSignatureChecker.sol"; +import {IRegistryCoordinator} from "src/interfaces/IRegistryCoordinator.sol"; +import {IBLSPubkeyRegistry} from "src/interfaces/IBLSPubkeyRegistry.sol"; +import {IStakeRegistry} from "src/interfaces/IStakeRegistry.sol"; -import "./libraries/BitmapUtils.sol"; -import "./libraries/BN254.sol"; +import {BitmapUtils} from "src/libraries/BitmapUtils.sol"; +import {BN254} from "src/libraries/BN254.sol"; /** * @title Used for checking BLS aggregate signatures from the operators of a `BLSRegistry`. @@ -132,13 +132,11 @@ contract BLSSignatureChecker is IBLSSignatureChecker { stakeRegistry.getTotalStakeAtBlockNumberFromIndex(quorumNumber, referenceBlockNumber, nonSignerStakesAndSignature.totalStakeIndices[quorumNumberIndex]); // copy total stake to signed stake quorumStakeTotals.signedStakeForQuorum[quorumNumberIndex] = quorumStakeTotals.totalStakeForQuorum[quorumNumberIndex]; - - // keep track of the nonSigners index in the quorum - uint32 nonSignerForQuorumIndex = 0; - // 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] -= diff --git a/src/IndexRegistry.sol b/src/IndexRegistry.sol index 571d8960..0d9a8340 100644 --- a/src/IndexRegistry.sol +++ b/src/IndexRegistry.sol @@ -1,7 +1,8 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity =0.8.12; -import "./IndexRegistryStorage.sol"; +import {IndexRegistryStorage} from "src/IndexRegistryStorage.sol"; +import {IRegistryCoordinator} from "src/interfaces/IRegistryCoordinator.sol"; /** * @title A `Registry` that keeps track of an ordered list of operators for each quorum diff --git a/src/IndexRegistryStorage.sol b/src/IndexRegistryStorage.sol index d69c436c..570d5fa8 100644 --- a/src/IndexRegistryStorage.sol +++ b/src/IndexRegistryStorage.sol @@ -1,10 +1,10 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity =0.8.12; -import "./interfaces/IIndexRegistry.sol"; -import "./interfaces/IRegistryCoordinator.sol"; +import {Initializable} from "@openzeppelin-upgrades/contracts/proxy/utils/Initializable.sol"; -import "@openzeppelin-upgrades/contracts/proxy/utils/Initializable.sol"; +import {IRegistryCoordinator} from "src/interfaces/IRegistryCoordinator.sol"; +import {IIndexRegistry} from "src/interfaces/IIndexRegistry.sol"; /** * @title Storage variables for the `IndexRegistry` contract. diff --git a/src/ServiceManagerBase.sol b/src/ServiceManagerBase.sol index 325065d4..3a794a00 100644 --- a/src/ServiceManagerBase.sol +++ b/src/ServiceManagerBase.sol @@ -1,17 +1,17 @@ // 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 {OwnableUpgradeable} from "@openzeppelin-upgrades/contracts/access/OwnableUpgradeable.sol"; +import {Initializable} from "@openzeppelin-upgrades/contracts/proxy/utils/Initializable.sol"; -import "eigenlayer-contracts/src/contracts/interfaces/IPauserRegistry.sol"; -import "eigenlayer-contracts/src/contracts/interfaces/ISlasher.sol"; -import "eigenlayer-contracts/src/contracts/permissions/Pausable.sol"; +import {IPauserRegistry} from "eigenlayer-contracts/src/contracts/interfaces/IPauserRegistry.sol"; +import {ISlasher} from "eigenlayer-contracts/src/contracts/interfaces/ISlasher.sol"; +import {Pausable} from "eigenlayer-contracts/src/contracts/permissions/Pausable.sol"; -import "./interfaces/IBLSRegistryCoordinatorWithIndices.sol"; -import "./interfaces/IServiceManager.sol"; +import {BLSSignatureChecker} from "src/BLSSignatureChecker.sol"; -import "./BLSSignatureChecker.sol"; +import {IBLSRegistryCoordinatorWithIndices} from "src/interfaces/IBLSRegistryCoordinatorWithIndices.sol"; +import {IServiceManager} from "src/interfaces/IServiceManager.sol"; /** * @title Base implementation of `IServiceManager` interface, designed to be inherited from by more complex ServiceManagers. diff --git a/src/StakeRegistry.sol b/src/StakeRegistry.sol index acb25268..da2d04ee 100644 --- a/src/StakeRegistry.sol +++ b/src/StakeRegistry.sol @@ -1,12 +1,15 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity =0.8.12; -import "@openzeppelin/contracts/utils/math/Math.sol"; -import "eigenlayer-contracts/src/contracts/libraries/BitmapUtils.sol"; -import "src/interfaces/IServiceManager.sol"; -import "src/interfaces/IStakeRegistry.sol"; -import "src/interfaces/IRegistryCoordinator.sol"; -import "src/StakeRegistryStorage.sol"; +import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; + +import {StakeRegistryStorage} from "src/StakeRegistryStorage.sol"; + +import {IRegistryCoordinator} from "src/interfaces/IRegistryCoordinator.sol"; +import {IServiceManager} from "src/interfaces/IServiceManager.sol"; +import {IStakeRegistry} from "src/interfaces/IStakeRegistry.sol"; + +import {BitmapUtils} from "src/libraries/BitmapUtils.sol"; /** * @title A `Registry` that keeps track of stakes of operators for up to 256 quorums. diff --git a/src/StakeRegistryStorage.sol b/src/StakeRegistryStorage.sol index 7a950597..2d08d8ce 100644 --- a/src/StakeRegistryStorage.sol +++ b/src/StakeRegistryStorage.sol @@ -1,18 +1,12 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity =0.8.12; -import {IStrategyManager} from "eigenlayer-contracts/src/contracts/interfaces/IStrategyManager.sol"; -<<<<<<< HEAD -import {IServiceManager} from "./interfaces/IServiceManager.sol"; -import {IStakeRegistry} from "./interfaces/IStakeRegistry.sol"; -import {IRegistryCoordinator} from "./interfaces/IRegistryCoordinator.sol"; -======= import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; +import {IStrategyManager} from "eigenlayer-contracts/src/contracts/interfaces/IStrategyManager.sol"; +import {IRegistryCoordinator} from "src/interfaces/IRegistryCoordinator.sol"; import {IServiceManager} from "src/interfaces/IServiceManager.sol"; import {IStakeRegistry} from "src/interfaces/IStakeRegistry.sol"; -import {IRegistryCoordinator} from "src/interfaces/IRegistryCoordinator.sol"; ->>>>>>> 12b09de (fix: fix compilation issues and tests) /** * @title Storage variables for the `StakeRegistry` contract. diff --git a/src/interfaces/IBLSRegistry.sol b/src/interfaces/IBLSRegistry.sol index d9122263..7e712471 100644 --- a/src/interfaces/IBLSRegistry.sol +++ b/src/interfaces/IBLSRegistry.sol @@ -4,7 +4,6 @@ pragma solidity >=0.5.0; import {IQuorumRegistry} from "./IQuorumRegistry.sol"; import {BN254} from "../libraries/BN254.sol"; - /** * @title Minimal interface extension to `IQuorumRegistry`. * @author Layr Labs, Inc. diff --git a/src/interfaces/IBLSRegistryCoordinatorWithIndices.sol b/src/interfaces/IBLSRegistryCoordinatorWithIndices.sol index 6966af04..04b64815 100644 --- a/src/interfaces/IBLSRegistryCoordinatorWithIndices.sol +++ b/src/interfaces/IBLSRegistryCoordinatorWithIndices.sol @@ -2,11 +2,13 @@ pragma solidity =0.8.12; import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol"; -import {IRegistryCoordinator} from "./IRegistryCoordinator.sol"; -import {IStakeRegistry} from "./IStakeRegistry.sol"; -import {IBLSPubkeyRegistry} from "./IBLSPubkeyRegistry.sol"; -import {IIndexRegistry} from "./IIndexRegistry.sol"; -import {BN254} from "../libraries/BN254.sol"; + +import {IRegistryCoordinator} from "src/interfaces/IRegistryCoordinator.sol"; +import {IBLSPubkeyRegistry} from "src/interfaces/IBLSPubkeyRegistry.sol"; +import {IStakeRegistry} from "src/interfaces/IStakeRegistry.sol"; +import {IIndexRegistry} from "src/interfaces/IIndexRegistry.sol"; + +import {BN254} from "src/libraries/BN254.sol"; /** * @title Minimal interface for the `IBLSStakeRegistryCoordinator` contract. diff --git a/src/interfaces/IBLSSignatureChecker.sol b/src/interfaces/IBLSSignatureChecker.sol index e2759f95..0162e0c6 100644 --- a/src/interfaces/IBLSSignatureChecker.sol +++ b/src/interfaces/IBLSSignatureChecker.sol @@ -1,11 +1,12 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity =0.8.12; -import {IBLSRegistryCoordinatorWithIndices} from "./IBLSRegistryCoordinatorWithIndices.sol"; -import {IBLSPubkeyRegistry} from "./IBLSPubkeyRegistry.sol"; -import {IRegistryCoordinator} from "./IRegistryCoordinator.sol"; -import {IStakeRegistry} from "./IStakeRegistry.sol"; -import {BN254} from "../libraries/BN254.sol"; +import {IBLSRegistryCoordinatorWithIndices} from "src/interfaces/IBLSRegistryCoordinatorWithIndices.sol"; +import {IRegistryCoordinator} from "src/interfaces/IRegistryCoordinator.sol"; +import {IBLSPubkeyRegistry} from "src/interfaces/IBLSPubkeyRegistry.sol"; +import {IStakeRegistry} from "src/interfaces/IStakeRegistry.sol"; + +import {BN254} from "src/libraries/BN254.sol"; /** * @title Used for checking BLS aggregate signatures from the operators of a EigenLayer AVS with the RegistryCoordinator/BLSPubkeyRegistry/StakeRegistry architechture. diff --git a/src/interfaces/IIndexRegistry.sol b/src/interfaces/IIndexRegistry.sol index 552c1a4d..1765802d 100644 --- a/src/interfaces/IIndexRegistry.sol +++ b/src/interfaces/IIndexRegistry.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity =0.8.12; -import "./IRegistry.sol"; +import {IRegistry} from "./IRegistry.sol"; /** * @title Interface for a `Registry`-type contract that keeps track of an ordered list of operators for up to 256 quorums. diff --git a/src/interfaces/IQuorumRegistry.sol b/src/interfaces/IQuorumRegistry.sol index d59dfcf1..21c10dee 100644 --- a/src/interfaces/IQuorumRegistry.sol +++ b/src/interfaces/IQuorumRegistry.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity >=0.5.0; -import "./IRegistry.sol"; +import {IRegistry} from "./IRegistry.sol"; /** * @title Interface for a `Registry`-type contract that uses either 1 or 2 quorums. diff --git a/src/interfaces/IStakeRegistry.sol b/src/interfaces/IStakeRegistry.sol index 880557b6..63a16b9a 100644 --- a/src/interfaces/IStakeRegistry.sol +++ b/src/interfaces/IStakeRegistry.sol @@ -1,11 +1,11 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity =0.8.12; -import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; +import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; -import {IRegistry} from "./IRegistry.sol"; import {IServiceManager} from "./IServiceManager.sol"; +import {IRegistry} from "./IRegistry.sol"; /** * @title Interface for a `Registry` that keeps track of stakes of operators for up to 256 quorums. From 4a16d34953c5cb924a37a55e52d35e21f4fb1f98 Mon Sep 17 00:00:00 2001 From: Alex <18387287+wadealexc@users.noreply.github.com> Date: Thu, 30 Nov 2023 17:09:36 -0500 Subject: [PATCH 050/101] chore: merge master into m2-mainnet (#79) --------- Co-authored-by: steven Co-authored-by: gpsanant Co-authored-by: ChaoticWalrus <93558947+ChaoticWalrus@users.noreply.github.com> Co-authored-by: Gautham Anant <32277907+gpsanant@users.noreply.github.com> --- src/BLSSignatureChecker.sol | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/BLSSignatureChecker.sol b/src/BLSSignatureChecker.sol index d5cb998a..36e9af4e 100644 --- a/src/BLSSignatureChecker.sol +++ b/src/BLSSignatureChecker.sol @@ -132,11 +132,13 @@ contract BLSSignatureChecker is IBLSSignatureChecker { stakeRegistry.getTotalStakeAtBlockNumberFromIndex(quorumNumber, referenceBlockNumber, nonSignerStakesAndSignature.totalStakeIndices[quorumNumberIndex]); // copy total stake to signed stake quorumStakeTotals.signedStakeForQuorum[quorumNumberIndex] = quorumStakeTotals.totalStakeForQuorum[quorumNumberIndex]; + + // keep track of the nonSigners index in the quorum + uint32 nonSignerForQuorumIndex = 0; + // 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] -= From eadba0e73760d1bf4b001b371f495d244caaa788 Mon Sep 17 00:00:00 2001 From: Michael Sun <35479365+8sunyuan@users.noreply.github.com> Date: Fri, 1 Dec 2023 12:43:12 -0500 Subject: [PATCH 051/101] build: point eigenlayer gitmodule to m2-mainnet (#80) --- .gitmodules | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitmodules b/.gitmodules index f9d2ed1c..63102e3d 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,6 +4,7 @@ [submodule "lib/eigenlayer-contracts"] path = lib/eigenlayer-contracts url = https://github.com/Layr-labs/eigenlayer-contracts + branch = m2-mainnet [submodule "lib/ds-test"] path = lib/ds-test url = https://github.com/dapphub/ds-test From 648f8d7bb50a1c2c611e0eae5cea88474a994c47 Mon Sep 17 00:00:00 2001 From: steven Date: Mon, 4 Dec 2023 10:54:57 -0500 Subject: [PATCH 052/101] ci: add rule to run CI on m2-mainnet branch --- .github/workflows/tests.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 09b4388f..953c2e95 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -8,6 +8,7 @@ on: push: branches: - master + - m2-mainnet pull_request: env: From 94d40295a3f16c2466e3eb61cf9828db4f3544ea Mon Sep 17 00:00:00 2001 From: steven <12021290+stevennevins@users.noreply.github.com> Date: Mon, 4 Dec 2023 15:20:30 -0500 Subject: [PATCH 053/101] chore: remove old tree file (#83) Co-authored-by: steven --- test/unit/DelegationManager.tree | 82 -------------------------------- 1 file changed, 82 deletions(-) delete mode 100644 test/unit/DelegationManager.tree diff --git a/test/unit/DelegationManager.tree b/test/unit/DelegationManager.tree deleted file mode 100644 index 768e9788..00000000 --- a/test/unit/DelegationManager.tree +++ /dev/null @@ -1,82 +0,0 @@ -# DelegationManager -# initialize -## when called on deployment -### it should initialize correctly -## when called after deployment -### it should revert -# modifyOperatorDetails -## when caller is an operator -### when operator tries to update their earningsReceiver to address 0 -#### it should revert -### when operator tries to decrease their stakerOptOutWindowBlocks -#### it should revert -### when operator tries to increase their stakerOptOutWindowBlocks more than maxStakerOptOutWindowBlocks -#### it should revert -## when caller is not an operator -### it should revert -# updateOperatorMetadata -## when caller is an operator -### it should emit the operators metadataURI -## when caller is not an operator -### it should revert -# registerAsOperator -## when OperatorDetails earningsReceiver is address 0 -### it should revert -## when caller is already delegated -### it should revert -## when caller is already an operator -### it should revert -## when caller is undelegated -### it should register the caller as an operator -# delegateTo -## when caller is already delegated -### it should revert -## when caller isnt already delegated -### when operator does NOT require approver sig -#### when operator is NOT a registered operator -##### it should revert -#### when operator is a registered operator -##### it should delegate staker to operator -### when operator requires approver sig -#### when the approver sig is valid -##### it should delegate the staker to operator -##### when the approver sig is invalid -###### when the signature is expired -####### it should revert -###### when the approver signature is from non-erc1271 compliant wallet -####### it should revert -###### when the signer of the approver sig is NOT approver -####### it should revert -###### when the nonce is invalid -####### it should revert -###### when the no signature provided -####### it should revert -### when operator is frozen -#### it should revert -### when staker tries delegating to operator that isn't registered -#### it should revert -### when delegation is paused -#### it should revert -### when staker is an operator themselves -#### it should revert -# delegateToWithSignature -## when staker delegation signature is valid -### it should delegate staker to operator -## when staker delegation signature is invalid -### when nonce is invalid -#### it should revert -### when signature is expired -#### it should revert -### when no signature provided -#### it should revert -# undelegate -## when caller is delegated to an operator -### it should undelegate the staker -## when the caller is an operator -### it should revert -## when caller isnt delegated to an operator -### it should revert -# increaseDelegatedShares -# decreaseDelegatedShares -# queueWithdrawal -# completeQueuedWithdrawal From 49ab3073c83b9a5675b54d0f2b2878f6c436119e Mon Sep 17 00:00:00 2001 From: Alex <18387287+wadealexc@users.noreply.github.com> Date: Wed, 6 Dec 2023 19:07:14 -0500 Subject: [PATCH 054/101] chore: rename several contracts and fix interfaces and tests (#95) - BLSRegistryCoordinatorWithIndices is now RegistryCoordinator - BLSOperatorStateRetriever is now OperatorStateRetriever - BLSPubkeyRegistry is now BLSApkRegistry --- script/DeploySharedContracts.s.sol | 6 +- ...SPubkeyRegistry.sol => BLSApkRegistry.sol} | 18 ++-- ...yStorage.sol => BLSApkRegistryStorage.sol} | 8 +- src/BLSSignatureChecker.sol | 11 ++- ...triever.sol => OperatorStateRetriever.sol} | 18 ++-- ...ithIndices.sol => RegistryCoordinator.sol} | 82 +++++++++---------- src/ServiceManagerBase.sol | 6 +- ...PubkeyRegistry.sol => IBLSApkRegistry.sol} | 7 +- .../IBLSRegistryCoordinatorWithIndices.sol | 67 --------------- src/interfaces/IBLSSignatureChecker.sol | 7 +- src/interfaces/IRegistryCoordinator.sol | 53 +++++++++++- test/ffi/BLSSignatureCheckerFFI.t.sol | 14 ++-- ...ess.sol => RegistryCoordinatorHarness.sol} | 10 +-- test/mocks/RegistryCoordinatorMock.sol | 13 +++ ...tryUnit.t.sol => BLSApkRegistryUnit.t.sol} | 74 ++++++++--------- test/unit/BLSSignatureCheckerUnit.t.sol | 4 +- ...t.sol => OperatorStateRetrieverUnit.t.sol} | 10 +-- ...it.t.sol => RegistryCoordinatorUnit.t.sol} | 70 ++++++++-------- test/unit/StakeRegistryUnit.t.sol | 13 ++- test/utils/BLSMockAVSDeployer.sol | 12 +-- test/utils/MockAVSDeployer.sol | 77 +++++++---------- 21 files changed, 278 insertions(+), 302 deletions(-) rename src/{BLSPubkeyRegistry.sol => BLSApkRegistry.sol} (92%) rename src/{BLSPubkeyRegistryStorage.sol => BLSApkRegistryStorage.sol} (89%) rename src/{BLSOperatorStateRetriever.sol => OperatorStateRetriever.sol} (91%) rename src/{BLSRegistryCoordinatorWithIndices.sol => RegistryCoordinator.sol} (87%) rename src/interfaces/{IBLSPubkeyRegistry.sol => IBLSApkRegistry.sol} (96%) delete mode 100644 src/interfaces/IBLSRegistryCoordinatorWithIndices.sol rename test/harnesses/{BLSRegistryCoordinatorWithIndicesHarness.sol => RegistryCoordinatorHarness.sol} (71%) rename test/unit/{BLSPubkeyRegistryUnit.t.sol => BLSApkRegistryUnit.t.sol} (72%) rename test/unit/{BLSOperatorStateRetrieverUnit.t.sol => OperatorStateRetrieverUnit.t.sol} (96%) rename test/unit/{BLSRegistryCoordinatorWithIndicesUnit.t.sol => RegistryCoordinatorUnit.t.sol} (92%) diff --git a/script/DeploySharedContracts.s.sol b/script/DeploySharedContracts.s.sol index 347f14fe..d139f8fb 100644 --- a/script/DeploySharedContracts.s.sol +++ b/script/DeploySharedContracts.s.sol @@ -2,7 +2,7 @@ pragma solidity =0.8.12; import "../src/BLSPublicKeyCompendium.sol"; -import "../src/BLSOperatorStateRetriever.sol"; +import "../src/OperatorStateRetriever.sol"; import "forge-std/Script.sol"; import "forge-std/Test.sol"; @@ -16,12 +16,12 @@ contract DeploySharedContracts is Script, Test { Vm cheats = Vm(HEVM_ADDRESS); BLSPublicKeyCompendium public blsPublicKeyCompendium; - BLSOperatorStateRetriever public blsOperatorStateRetriever; + OperatorStateRetriever public blsOperatorStateRetriever; function run() external { vm.startBroadcast(); blsPublicKeyCompendium = new BLSPublicKeyCompendium(); - blsOperatorStateRetriever = new BLSOperatorStateRetriever(); + blsOperatorStateRetriever = new OperatorStateRetriever(); vm.stopBroadcast(); string memory deployed_addresses = "addresses"; diff --git a/src/BLSPubkeyRegistry.sol b/src/BLSApkRegistry.sol similarity index 92% rename from src/BLSPubkeyRegistry.sol rename to src/BLSApkRegistry.sol index 56f404de..14093fa0 100644 --- a/src/BLSPubkeyRegistry.sol +++ b/src/BLSApkRegistry.sol @@ -1,21 +1,21 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity =0.8.12; -import {BLSPubkeyRegistryStorage} from "src/BLSPubkeyRegistryStorage.sol"; +import {BLSApkRegistryStorage} from "src/BLSApkRegistryStorage.sol"; import {IBLSPublicKeyCompendium} from "src/interfaces/IBLSPublicKeyCompendium.sol"; import {IRegistryCoordinator} from "src/interfaces/IRegistryCoordinator.sol"; import {BN254} from "src/libraries/BN254.sol"; -contract BLSPubkeyRegistry is BLSPubkeyRegistryStorage { +contract BLSApkRegistry is BLSApkRegistryStorage { 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" + "BLSApkRegistry.onlyRegistryCoordinator: caller is not the registry coordinator" ); _; } @@ -24,7 +24,7 @@ contract BLSPubkeyRegistry is BLSPubkeyRegistryStorage { constructor( IRegistryCoordinator _registryCoordinator, IBLSPublicKeyCompendium _pubkeyCompendium - ) BLSPubkeyRegistryStorage(_registryCoordinator, _pubkeyCompendium) {} + ) BLSApkRegistryStorage(_registryCoordinator, _pubkeyCompendium) {} /******************************************************************************* EXTERNAL FUNCTIONS - REGISTRY COORDINATOR @@ -86,7 +86,7 @@ contract BLSPubkeyRegistry is BLSPubkeyRegistryStorage { * @param quorumNumber The number of the new quorum */ function initializeQuorum(uint8 quorumNumber) public virtual onlyRegistryCoordinator { - require(apkHistory[quorumNumber].length == 0, "BLSPubkeyRegistry.initializeQuorum: quorum already exists"); + require(apkHistory[quorumNumber].length == 0, "BLSApkRegistry.initializeQuorum: quorum already exists"); apkHistory[quorumNumber].push(ApkUpdate({ apkHash: bytes24(0), @@ -106,7 +106,7 @@ contract BLSPubkeyRegistry is BLSPubkeyRegistryStorage { // Validate quorum exists and get history length uint8 quorumNumber = uint8(quorumNumbers[i]); uint256 historyLength = apkHistory[quorumNumber].length; - require(historyLength != 0, "BLSPubkeyRegistry._processQuorumApkUpdate: quorum does not exist"); + require(historyLength != 0, "BLSApkRegistry._processQuorumApkUpdate: quorum does not exist"); // Update aggregate public key for this quorum newApk = currentApk[quorumNumber].plus(point); @@ -133,7 +133,7 @@ contract BLSPubkeyRegistry is BLSPubkeyRegistryStorage { function _validateApkHashAtBlockNumber(ApkUpdate memory apkUpdate, uint32 blockNumber) internal pure { require( blockNumber >= apkUpdate.updateBlockNumber, - "BLSPubkeyRegistry._validateApkHashAtBlockNumber: index too recent" + "BLSApkRegistry._validateApkHashAtBlockNumber: index too recent" ); /** * if there is a next update, check that the blockNumber is before the next update or if @@ -141,7 +141,7 @@ contract BLSPubkeyRegistry is BLSPubkeyRegistryStorage { */ require( apkUpdate.nextUpdateBlockNumber == 0 || blockNumber < apkUpdate.nextUpdateBlockNumber, - "BLSPubkeyRegistry._validateApkHashAtBlockNumber: not latest apk update" + "BLSApkRegistry._validateApkHashAtBlockNumber: not latest apk update" ); } @@ -164,7 +164,7 @@ contract BLSPubkeyRegistry is BLSPubkeyRegistryStorage { if (quorumApkUpdatesLength == 0 || blockNumber < apkHistory[quorumNumber][0].updateBlockNumber) { revert( - "BLSPubkeyRegistry.getApkIndicesAtBlockNumber: blockNumber is before the first update" + "BLSApkRegistry.getApkIndicesAtBlockNumber: blockNumber is before the first update" ); } diff --git a/src/BLSPubkeyRegistryStorage.sol b/src/BLSApkRegistryStorage.sol similarity index 89% rename from src/BLSPubkeyRegistryStorage.sol rename to src/BLSApkRegistryStorage.sol index 2c0ce9e7..1629d708 100644 --- a/src/BLSPubkeyRegistryStorage.sol +++ b/src/BLSApkRegistryStorage.sol @@ -1,13 +1,15 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity =0.8.12; -import {IBLSPubkeyRegistry} from "src/interfaces/IBLSPubkeyRegistry.sol"; +import {IBLSApkRegistry} from "src/interfaces/IBLSApkRegistry.sol"; import {IRegistryCoordinator} from "src/interfaces/IRegistryCoordinator.sol"; import {IBLSPublicKeyCompendium} from "src/interfaces/IBLSPublicKeyCompendium.sol"; -import {BN254} from "src/libraries/BN254.sol"; + import {Initializable} from "@openzeppelin-upgrades/contracts/proxy/utils/Initializable.sol"; -abstract contract BLSPubkeyRegistryStorage is Initializable, IBLSPubkeyRegistry { +import {BN254} from "src/libraries/BN254.sol"; + +abstract contract BLSApkRegistryStorage is Initializable, IBLSApkRegistry { /// @notice the registry coordinator contract IRegistryCoordinator public immutable registryCoordinator; /// @notice the BLSPublicKeyCompendium contract against which pubkey ownership is checked diff --git a/src/BLSSignatureChecker.sol b/src/BLSSignatureChecker.sol index 36e9af4e..3b79e6f4 100644 --- a/src/BLSSignatureChecker.sol +++ b/src/BLSSignatureChecker.sol @@ -1,10 +1,9 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity =0.8.12; -import {IBLSRegistryCoordinatorWithIndices} from "src/interfaces/IBLSRegistryCoordinatorWithIndices.sol"; import {IBLSSignatureChecker} from "src/interfaces/IBLSSignatureChecker.sol"; import {IRegistryCoordinator} from "src/interfaces/IRegistryCoordinator.sol"; -import {IBLSPubkeyRegistry} from "src/interfaces/IBLSPubkeyRegistry.sol"; +import {IBLSApkRegistry} from "src/interfaces/IBLSApkRegistry.sol"; import {IStakeRegistry} from "src/interfaces/IStakeRegistry.sol"; import {BitmapUtils} from "src/libraries/BitmapUtils.sol"; @@ -26,12 +25,12 @@ contract BLSSignatureChecker is IBLSSignatureChecker { IRegistryCoordinator public immutable registryCoordinator; IStakeRegistry public immutable stakeRegistry; - IBLSPubkeyRegistry public immutable blsPubkeyRegistry; + IBLSApkRegistry public immutable blsApkRegistry; - constructor(IBLSRegistryCoordinatorWithIndices _registryCoordinator) { + constructor(IRegistryCoordinator _registryCoordinator) { registryCoordinator = IRegistryCoordinator(_registryCoordinator); stakeRegistry = _registryCoordinator.stakeRegistry(); - blsPubkeyRegistry = _registryCoordinator.blsPubkeyRegistry(); + blsApkRegistry = _registryCoordinator.blsApkRegistry(); } /** @@ -75,7 +74,7 @@ contract BLSSignatureChecker is IBLSSignatureChecker { for (uint i = 0; i < quorumNumbers.length; i++) { require( bytes24(nonSignerStakesAndSignature.quorumApks[i].hashG1Point()) == - IBLSPubkeyRegistry(blsPubkeyRegistry).getApkHashAtBlockNumberAndIndex( + blsApkRegistry.getApkHashAtBlockNumberAndIndex( uint8(quorumNumbers[i]), referenceBlockNumber, nonSignerStakesAndSignature.quorumApkIndices[i] diff --git a/src/BLSOperatorStateRetriever.sol b/src/OperatorStateRetriever.sol similarity index 91% rename from src/BLSOperatorStateRetriever.sol rename to src/OperatorStateRetriever.sol index 5d2c89ed..f87439b3 100644 --- a/src/BLSOperatorStateRetriever.sol +++ b/src/OperatorStateRetriever.sol @@ -1,18 +1,18 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity =0.8.12; -import {IBLSRegistryCoordinatorWithIndices} from "src/interfaces/IBLSRegistryCoordinatorWithIndices.sol"; -import {IBLSPubkeyRegistry} from "src/interfaces/IBLSPubkeyRegistry.sol"; +import {IRegistryCoordinator} from "src/interfaces/IRegistryCoordinator.sol"; +import {IBLSApkRegistry} from "src/interfaces/IBLSApkRegistry.sol"; import {IStakeRegistry} from "src/interfaces/IStakeRegistry.sol"; import {IIndexRegistry} from "src/interfaces/IIndexRegistry.sol"; import {BitmapUtils} from "src/libraries/BitmapUtils.sol"; /** - * @title BLSOperatorStateRetriever with view functions that allow to retrieve the state of an AVSs registry system. + * @title OperatorStateRetriever with view functions that allow to retrieve the state of an AVSs registry system. * @author Layr Labs Inc. */ -contract BLSOperatorStateRetriever { +contract OperatorStateRetriever { struct Operator { bytes32 operatorId; uint96 stake; @@ -37,7 +37,7 @@ contract BLSOperatorStateRetriever { * was a part of at `blockNumber`, an ordered list of operators. */ function getOperatorState( - IBLSRegistryCoordinatorWithIndices registryCoordinator, + IRegistryCoordinator registryCoordinator, bytes32 operatorId, uint32 blockNumber ) external view returns (uint256, Operator[][] memory) { @@ -61,7 +61,7 @@ contract BLSOperatorStateRetriever { * @return 2d array of operators. For each quorum, an ordered list of operators */ function getOperatorState( - IBLSRegistryCoordinatorWithIndices registryCoordinator, + IRegistryCoordinator registryCoordinator, bytes memory quorumNumbers, uint32 blockNumber ) public view returns(Operator[][] memory) { @@ -100,7 +100,7 @@ contract BLSOperatorStateRetriever { * 4) the indices of the quorum apks for each of the provided quorums at the given blocknumber */ function getCheckSignaturesIndices( - IBLSRegistryCoordinatorWithIndices registryCoordinator, + IRegistryCoordinator registryCoordinator, uint32 referenceBlockNumber, bytes calldata quorumNumbers, bytes32[] calldata nonSignerOperatorIds @@ -148,9 +148,9 @@ contract BLSOperatorStateRetriever { checkSignaturesIndices.nonSignerStakeIndices[quorumNumberIndex] = nonSignerStakeIndicesForQuorum; } - IBLSPubkeyRegistry blsPubkeyRegistry = registryCoordinator.blsPubkeyRegistry(); + IBLSApkRegistry blsApkRegistry = registryCoordinator.blsApkRegistry(); // get the indices of the quorum apks for each of the provided quorums at the given blocknumber - checkSignaturesIndices.quorumApkIndices = blsPubkeyRegistry.getApkIndicesAtBlockNumber(quorumNumbers, referenceBlockNumber); + checkSignaturesIndices.quorumApkIndices = blsApkRegistry.getApkIndicesAtBlockNumber(quorumNumbers, referenceBlockNumber); return checkSignaturesIndices; } diff --git a/src/BLSRegistryCoordinatorWithIndices.sol b/src/RegistryCoordinator.sol similarity index 87% rename from src/BLSRegistryCoordinatorWithIndices.sol rename to src/RegistryCoordinator.sol index c200b59f..615879f3 100644 --- a/src/BLSRegistryCoordinatorWithIndices.sol +++ b/src/RegistryCoordinator.sol @@ -9,27 +9,25 @@ import {IPauserRegistry} from "eigenlayer-contracts/src/contracts/interfaces/IPa import {Pausable} from "eigenlayer-contracts/src/contracts/permissions/Pausable.sol"; import {ISlasher} from "eigenlayer-contracts/src/contracts/interfaces/ISlasher.sol"; -import {IBLSRegistryCoordinatorWithIndices} from "src/interfaces/IBLSRegistryCoordinatorWithIndices.sol"; import {IRegistryCoordinator} from "src/interfaces/IRegistryCoordinator.sol"; -import {IBLSPubkeyRegistry} from "src/interfaces/IBLSPubkeyRegistry.sol"; +import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol"; +import {IBLSApkRegistry} from "src/interfaces/IBLSApkRegistry.sol"; import {IServiceManager} from "src/interfaces/IServiceManager.sol"; import {ISocketUpdater} from "src/interfaces/ISocketUpdater.sol"; import {IStakeRegistry} from "src/interfaces/IStakeRegistry.sol"; import {IIndexRegistry} from "src/interfaces/IIndexRegistry.sol"; import {BitmapUtils} from "src/libraries/BitmapUtils.sol"; -import {BN254} from "src/libraries/BN254.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 + * 2) a `BLSApkRegistry` 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; +contract RegistryCoordinator is EIP712, Initializable, IRegistryCoordinator, ISocketUpdater, ISignatureUtils, Pausable { using BitmapUtils for *; /// @notice The EIP-712 typehash for the `DelegationApproval` struct used by the contract @@ -52,8 +50,8 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr 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 BLS Aggregate Pubkey Registry contract that will keep track of operators' aggregate BLS public keys per quorum + IBLSApkRegistry public immutable blsApkRegistry; /// @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 @@ -78,19 +76,19 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr address public ejector; modifier onlyServiceManagerOwner { - require(msg.sender == serviceManager.owner(), "BLSRegistryCoordinatorWithIndices.onlyServiceManagerOwner: caller is not the service manager owner"); + require(msg.sender == serviceManager.owner(), "RegistryCoordinator.onlyServiceManagerOwner: caller is not the service manager owner"); _; } modifier onlyEjector { - require(msg.sender == ejector, "BLSRegistryCoordinatorWithIndices.onlyEjector: caller is not the ejector"); + require(msg.sender == ejector, "RegistryCoordinator.onlyEjector: caller is not the ejector"); _; } modifier quorumExists(uint8 quorumNumber) { require( quorumNumber < quorumCount, - "BLSRegistryCoordinatorWithIndices.quorumExists: quorum does not exist" + "RegistryCoordinator.quorumExists: quorum does not exist" ); _; } @@ -99,13 +97,13 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr ISlasher _slasher, IServiceManager _serviceManager, IStakeRegistry _stakeRegistry, - IBLSPubkeyRegistry _blsPubkeyRegistry, + IBLSApkRegistry _blsApkRegistry, IIndexRegistry _indexRegistry ) EIP712("AVSRegistryCoordinator", "v0.0.1") { slasher = _slasher; serviceManager = _serviceManager; stakeRegistry = _stakeRegistry; - blsPubkeyRegistry = _blsPubkeyRegistry; + blsApkRegistry = _blsApkRegistry; indexRegistry = _indexRegistry; } @@ -120,7 +118,7 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr ) external initializer { require( _operatorSetParams.length == _minimumStakes.length && _minimumStakes.length == _strategyParams.length, - "BLSRegistryCoordinatorWithIndices.initialize: input length mismatch" + "RegistryCoordinator.initialize: input length mismatch" ); // Initialize roles @@ -130,7 +128,7 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr // Add registry contracts to the registries array registries.push(address(stakeRegistry)); - registries.push(address(blsPubkeyRegistry)); + registries.push(address(blsApkRegistry)); registries.push(address(indexRegistry)); // Create quorums @@ -152,7 +150,7 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr bytes calldata quorumNumbers, string calldata socket ) external onlyWhenNotPaused(PAUSED_REGISTER_OPERATOR) { - bytes32 operatorId = blsPubkeyRegistry.getOperatorId(msg.sender); + bytes32 operatorId = blsApkRegistry.getOperatorId(msg.sender); // Register the operator in each of the registry contracts RegisterResults memory results = _registerOperator({ @@ -173,7 +171,7 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr */ require( results.numOperatorsPerQuorum[i] <= operatorSetParams.maxOperatorCount, - "BLSRegistryCoordinatorWithIndices.registerOperator: operator count exceeds maximum" + "RegistryCoordinator.registerOperator: operator count exceeds maximum" ); } } @@ -192,9 +190,9 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr OperatorKickParam[] calldata operatorKickParams, SignatureWithSaltAndExpiry memory churnApproverSignature ) external onlyWhenNotPaused(PAUSED_REGISTER_OPERATOR) { - require(operatorKickParams.length == quorumNumbers.length, "BLSRegistryCoordinatorWithIndices.registerOperatorWithChurn: input length mismatch"); + require(operatorKickParams.length == quorumNumbers.length, "RegistryCoordinator.registerOperatorWithChurn: input length mismatch"); - bytes32 operatorId = blsPubkeyRegistry.getOperatorId(msg.sender); + bytes32 operatorId = blsApkRegistry.getOperatorId(msg.sender); // Verify the churn approver's signature for the registering operator and kick params _verifyChurnApproverSignature({ @@ -292,7 +290,7 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr * @param socket is the new socket of the operator */ function updateSocket(string memory socket) external { - require(_operatorInfo[msg.sender].status == OperatorStatus.REGISTERED, "BLSRegistryCoordinatorWithIndices.updateSocket: operator is not registered"); + require(_operatorInfo[msg.sender].status == OperatorStatus.REGISTERED, "RegistryCoordinator.updateSocket: operator is not registered"); emit OperatorSocketUpdate(_operatorInfo[msg.sender].operatorId, socket); } @@ -389,8 +387,8 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr */ uint192 quorumsToAdd = uint192(BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers, quorumCount)); uint192 currentBitmap = _currentOperatorBitmap(operatorId); - require(!quorumsToAdd.isEmpty(), "BLSRegistryCoordinatorWithIndices._registerOperator: bitmap cannot be 0"); - require(quorumsToAdd.noBitsInCommon(currentBitmap), "BLSRegistryCoordinatorWithIndices._registerOperator: operator already registered for some quorums being registered for"); + require(!quorumsToAdd.isEmpty(), "RegistryCoordinator._registerOperator: bitmap cannot be 0"); + require(quorumsToAdd.noBitsInCommon(currentBitmap), "RegistryCoordinator._registerOperator: operator already registered for some quorums being registered for"); uint192 newBitmap = uint192(currentBitmap.plus(quorumsToAdd)); /** @@ -414,10 +412,10 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr } /** - * Register the operator with the BLSPubkeyRegistry, StakeRegistry, and IndexRegistry + * Register the operator with the BLSApkRegistry, StakeRegistry, and IndexRegistry */ - bytes32 registeredId = blsPubkeyRegistry.registerOperator(operator, quorumNumbers); - require(registeredId == operatorId, "BLSRegistryCoordinatorWithIndices._registerOperator: operatorId mismatch"); + bytes32 registeredId = blsApkRegistry.registerOperator(operator, quorumNumbers); + require(registeredId == operatorId, "RegistryCoordinator._registerOperator: operatorId mismatch"); (uint96[] memory operatorStakes, uint96[] memory totalStakes) = stakeRegistry.registerOperator(operator, operatorId, quorumNumbers); uint32[] memory numOperatorsPerQuorum = indexRegistry.registerOperator(operatorId, quorumNumbers); @@ -439,25 +437,25 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr ) internal view { address operatorToKick = kickParams.operator; bytes32 idToKick = _operatorInfo[operatorToKick].operatorId; - require(newOperator != operatorToKick, "BLSRegistryCoordinatorWithIndices._validateChurn: cannot churn self"); - require(kickParams.quorumNumber == quorumNumber, "BLSRegistryCoordinatorWithIndices._validateChurn: quorumNumber not the same as signed"); + require(newOperator != operatorToKick, "RegistryCoordinator._validateChurn: cannot churn self"); + require(kickParams.quorumNumber == quorumNumber, "RegistryCoordinator._validateChurn: quorumNumber not the same as signed"); // Get the target operator's stake and check that it is below the kick thresholds uint96 operatorToKickStake = stakeRegistry.getCurrentStake(idToKick, quorumNumber); require( newOperatorStake > _individualKickThreshold(operatorToKickStake, setParams), - "BLSRegistryCoordinatorWithIndices._validateChurn: incoming operator has insufficient stake for churn" + "RegistryCoordinator._validateChurn: incoming operator has insufficient stake for churn" ); require( operatorToKickStake < _totalKickThreshold(totalQuorumStake, setParams), - "BLSRegistryCoordinatorWithIndices._validateChurn: cannot kick operator with more than kickBIPsOfTotalStake" + "RegistryCoordinator._validateChurn: cannot kick operator with more than kickBIPsOfTotalStake" ); } /** * @dev Deregister the operator from one or more quorums * This method updates the operator's quorum bitmap and status, then deregisters - * the operator with the BLSPubkeyRegistry, IndexRegistry, and StakeRegistry + * the operator with the BLSApkRegistry, IndexRegistry, and StakeRegistry */ function _deregisterOperator( address operator, @@ -466,7 +464,7 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr // Fetch the operator's info and ensure they are registered OperatorInfo storage operatorInfo = _operatorInfo[operator]; bytes32 operatorId = operatorInfo.operatorId; - require(operatorInfo.status == OperatorStatus.REGISTERED, "BLSRegistryCoordinatorWithIndices._deregisterOperator: operator is not registered"); + require(operatorInfo.status == OperatorStatus.REGISTERED, "RegistryCoordinator._deregisterOperator: operator is not registered"); /** * Get bitmap of quorums to deregister from and operator's current bitmap. Validate that: @@ -476,8 +474,8 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr */ uint192 quorumsToRemove = uint192(BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers, quorumCount)); uint192 currentBitmap = _currentOperatorBitmap(operatorId); - require(!quorumsToRemove.isEmpty(), "BLSRegistryCoordinatorWithIndices._deregisterOperator: bitmap cannot be 0"); - require(quorumsToRemove.isSubsetOf(currentBitmap), "BLSRegistryCoordinatorWithIndices._deregisterOperator: operator is not registered for specified quorums"); + require(!quorumsToRemove.isEmpty(), "RegistryCoordinator._deregisterOperator: bitmap cannot be 0"); + require(quorumsToRemove.isSubsetOf(currentBitmap), "RegistryCoordinator._deregisterOperator: operator is not registered for specified quorums"); uint192 newBitmap = uint192(currentBitmap.minus(quorumsToRemove)); /** @@ -495,7 +493,7 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr } // Deregister operator with each of the registry contracts: - blsPubkeyRegistry.deregisterOperator(operator, quorumNumbers); + blsApkRegistry.deregisterOperator(operator, quorumNumbers); stakeRegistry.deregisterOperator(operatorId, quorumNumbers); indexRegistry.deregisterOperator(operatorId, quorumNumbers); } @@ -523,8 +521,8 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr SignatureWithSaltAndExpiry memory churnApproverSignature ) internal { // make sure the salt hasn't been used already - require(!isChurnApproverSaltUsed[churnApproverSignature.salt], "BLSRegistryCoordinatorWithIndices._verifyChurnApproverSignature: churnApprover salt already used"); - require(churnApproverSignature.expiry >= block.timestamp, "BLSRegistryCoordinatorWithIndices._verifyChurnApproverSignature: churnApprover signature expired"); + require(!isChurnApproverSaltUsed[churnApproverSignature.salt], "RegistryCoordinator._verifyChurnApproverSignature: churnApprover salt already used"); + require(churnApproverSignature.expiry >= block.timestamp, "RegistryCoordinator._verifyChurnApproverSignature: churnApprover signature expired"); // set salt used to true isChurnApproverSaltUsed[churnApproverSignature.salt] = true; @@ -547,7 +545,7 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr ) internal { // Increment the total quorum count. Fails if we're already at the max uint8 prevQuorumCount = quorumCount; - require(prevQuorumCount < MAX_QUORUM_COUNT, "BLSRegistryCoordinatorWithIndices.createQuorum: max quorums reached"); + require(prevQuorumCount < MAX_QUORUM_COUNT, "RegistryCoordinator.createQuorum: max quorums reached"); quorumCount = prevQuorumCount + 1; // The previous count is the new quorum's number @@ -557,7 +555,7 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr _setOperatorSetParams(quorumNumber, operatorSetParams); stakeRegistry.initializeQuorum(quorumNumber, minimumStake, strategyParams); indexRegistry.initializeQuorum(quorumNumber); - blsPubkeyRegistry.initializeQuorum(quorumNumber); + blsApkRegistry.initializeQuorum(quorumNumber); } /** @@ -643,7 +641,7 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr /// @notice Returns the operator address for the given `operatorId` function getOperatorFromId(bytes32 operatorId) external view returns (address) { - return blsPubkeyRegistry.getOperatorFromPubkeyHash(operatorId); + return blsApkRegistry.getOperatorFromPubkeyHash(operatorId); } /// @notice Returns the status for the given `operator` @@ -665,7 +663,7 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr _operatorBitmapHistory[operatorIds[i]][length - j - 1].nextUpdateBlockNumber; require( nextUpdateBlockNumber == 0 || nextUpdateBlockNumber > blockNumber, - "BLSRegistryCoordinatorWithIndices.getQuorumBitmapIndicesAtBlockNumber: operatorId has no quorumBitmaps at blockNumber" + "RegistryCoordinator.getQuorumBitmapIndicesAtBlockNumber: operatorId has no quorumBitmaps at blockNumber" ); indices[i] = uint32(length - j - 1); break; @@ -687,13 +685,13 @@ contract BLSRegistryCoordinatorWithIndices is EIP712, Initializable, IBLSRegistr QuorumBitmapUpdate memory quorumBitmapUpdate = _operatorBitmapHistory[operatorId][index]; require( quorumBitmapUpdate.updateBlockNumber <= blockNumber, - "BLSRegistryCoordinatorWithIndices.getQuorumBitmapAtBlockNumberByIndex: quorumBitmapUpdate is from after blockNumber" + "RegistryCoordinator.getQuorumBitmapAtBlockNumberByIndex: 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.getQuorumBitmapAtBlockNumberByIndex: quorumBitmapUpdate is from before blockNumber" + "RegistryCoordinator.getQuorumBitmapAtBlockNumberByIndex: quorumBitmapUpdate is from before blockNumber" ); return quorumBitmapUpdate.quorumBitmap; } diff --git a/src/ServiceManagerBase.sol b/src/ServiceManagerBase.sol index 3a794a00..6537b7ee 100644 --- a/src/ServiceManagerBase.sol +++ b/src/ServiceManagerBase.sol @@ -10,7 +10,7 @@ import {Pausable} from "eigenlayer-contracts/src/contracts/permissions/Pausable. import {BLSSignatureChecker} from "src/BLSSignatureChecker.sol"; -import {IBLSRegistryCoordinatorWithIndices} from "src/interfaces/IBLSRegistryCoordinatorWithIndices.sol"; +import {IRegistryCoordinator} from "src/interfaces/IRegistryCoordinator.sol"; import {IServiceManager} from "src/interfaces/IServiceManager.sol"; /** @@ -52,7 +52,7 @@ abstract contract ServiceManagerBase is (msg.sender == address(registryCoordinator)) || (msg.sender == address( - IBLSRegistryCoordinatorWithIndices( + IRegistryCoordinator( address(registryCoordinator) ).stakeRegistry() )), @@ -62,7 +62,7 @@ abstract contract ServiceManagerBase is } constructor( - IBLSRegistryCoordinatorWithIndices _registryCoordinator, + IRegistryCoordinator _registryCoordinator, ISlasher _slasher ) BLSSignatureChecker(_registryCoordinator) { slasher = _slasher; diff --git a/src/interfaces/IBLSPubkeyRegistry.sol b/src/interfaces/IBLSApkRegistry.sol similarity index 96% rename from src/interfaces/IBLSPubkeyRegistry.sol rename to src/interfaces/IBLSApkRegistry.sol index 86ba835f..58cdc10b 100644 --- a/src/interfaces/IBLSPubkeyRegistry.sol +++ b/src/interfaces/IBLSApkRegistry.sol @@ -1,14 +1,15 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity =0.8.12; -import {IRegistry} from "./IRegistry.sol"; -import {BN254} from "../libraries/BN254.sol"; +import {IRegistry} from "src/interfaces/IRegistry.sol"; + +import {BN254} from "src/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 { +interface IBLSApkRegistry is IRegistry { // EVENTS // Emitted when a new operator pubkey is registered for a set of quorums event OperatorAddedToQuorums( diff --git a/src/interfaces/IBLSRegistryCoordinatorWithIndices.sol b/src/interfaces/IBLSRegistryCoordinatorWithIndices.sol deleted file mode 100644 index 04b64815..00000000 --- a/src/interfaces/IBLSRegistryCoordinatorWithIndices.sol +++ /dev/null @@ -1,67 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity =0.8.12; - -import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol"; - -import {IRegistryCoordinator} from "src/interfaces/IRegistryCoordinator.sol"; -import {IBLSPubkeyRegistry} from "src/interfaces/IBLSPubkeyRegistry.sol"; -import {IStakeRegistry} from "src/interfaces/IStakeRegistry.sol"; -import {IIndexRegistry} from "src/interfaces/IIndexRegistry.sol"; - -import {BN254} from "src/libraries/BN254.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. - * `operator` is the address of the operator to kick - */ - struct OperatorKickParam { - uint8 quorumNumber; - address operator; - } - - // 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 - */ - function ejectOperator( - address operator, - bytes calldata quorumNumbers - ) external; -} diff --git a/src/interfaces/IBLSSignatureChecker.sol b/src/interfaces/IBLSSignatureChecker.sol index 0162e0c6..85b324f1 100644 --- a/src/interfaces/IBLSSignatureChecker.sol +++ b/src/interfaces/IBLSSignatureChecker.sol @@ -1,15 +1,14 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity =0.8.12; -import {IBLSRegistryCoordinatorWithIndices} from "src/interfaces/IBLSRegistryCoordinatorWithIndices.sol"; import {IRegistryCoordinator} from "src/interfaces/IRegistryCoordinator.sol"; -import {IBLSPubkeyRegistry} from "src/interfaces/IBLSPubkeyRegistry.sol"; +import {IBLSApkRegistry} from "src/interfaces/IBLSApkRegistry.sol"; import {IStakeRegistry} from "src/interfaces/IStakeRegistry.sol"; import {BN254} from "src/libraries/BN254.sol"; /** - * @title Used for checking BLS aggregate signatures from the operators of a EigenLayer AVS with the RegistryCoordinator/BLSPubkeyRegistry/StakeRegistry architechture. + * @title Used for checking BLS aggregate signatures from the operators of a EigenLayer AVS with the RegistryCoordinator/BLSApkRegistry/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. @@ -44,7 +43,7 @@ interface IBLSSignatureChecker { function registryCoordinator() external view returns (IRegistryCoordinator); function stakeRegistry() external view returns (IStakeRegistry); - function blsPubkeyRegistry() external view returns (IBLSPubkeyRegistry); + function blsApkRegistry() external view returns (IBLSApkRegistry); /** * @notice This function is called by disperser when it has aggregated all the signatures of the operators diff --git a/src/interfaces/IRegistryCoordinator.sol b/src/interfaces/IRegistryCoordinator.sol index 360b95b1..ca6f8629 100644 --- a/src/interfaces/IRegistryCoordinator.sol +++ b/src/interfaces/IRegistryCoordinator.sol @@ -1,18 +1,29 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity =0.8.12; +import {IBLSApkRegistry} from "src/interfaces/IBLSApkRegistry.sol"; +import {IStakeRegistry} from "src/interfaces/IStakeRegistry.sol"; +import {IIndexRegistry} from "src/interfaces/IIndexRegistry.sol"; + /** * @title Interface for a contract that coordinates between various registries for an AVS. * @author Layr Labs, Inc. */ interface IRegistryCoordinator { // EVENTS + /// Emits when an operator is registered event OperatorRegistered(address indexed operator, bytes32 indexed operatorId); /// Emits when an operator is deregistered event OperatorDeregistered(address indexed operator, bytes32 indexed operatorId); - + + event OperatorSetParamsUpdated(uint8 indexed quorumNumber, OperatorSetParam operatorSetParams); + + event ChurnApproverUpdated(address prevChurnApprover, address newChurnApprover); + + event EjectorUpdated(address prevEjector, address newEjector); + // DATA STRUCTURES enum OperatorStatus { @@ -45,6 +56,46 @@ interface IRegistryCoordinator { uint192 quorumBitmap; } + /** + * @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. + * `operator` is the address of the operator to kick + */ + struct OperatorKickParam { + uint8 quorumNumber; + address operator; + } + + /// @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 Aggregate Pubkey Registry contract that will keep track of operators' BLS aggregate pubkeys per quorum + function blsApkRegistry() external view returns (IBLSApkRegistry); + /// @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 + */ + function ejectOperator( + address operator, + bytes calldata quorumNumbers + ) external; + /// @notice Returns the number of quorums the registry coordinator has created function quorumCount() external view returns (uint8); diff --git a/test/ffi/BLSSignatureCheckerFFI.t.sol b/test/ffi/BLSSignatureCheckerFFI.t.sol index dbe71f00..6161cdc1 100644 --- a/test/ffi/BLSSignatureCheckerFFI.t.sol +++ b/test/ffi/BLSSignatureCheckerFFI.t.sol @@ -1,12 +1,12 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity =0.8.12; -import {G2Operations} from "./util/G2Operations.sol"; -import {MockAVSDeployer} from "../utils/MockAVSDeployer.sol"; -import {BLSSignatureChecker} from "../../src/BLSSignatureChecker.sol"; -import {BLSOperatorStateRetriever} from "../../src/BLSOperatorStateRetriever.sol"; -import {BN254} from "../../src/libraries/BN254.sol"; -import {BitmapUtils} from "../../src/libraries/BitmapUtils.sol"; +import {G2Operations} from "test/ffi/util/G2Operations.sol"; +import {MockAVSDeployer} from "test/utils/MockAVSDeployer.sol"; +import {BLSSignatureChecker} from "src/BLSSignatureChecker.sol"; +import {OperatorStateRetriever} from "src/OperatorStateRetriever.sol"; +import {BN254} from "src/libraries/BN254.sol"; +import {BitmapUtils} from "src/libraries/BitmapUtils.sol"; contract BLSSignatureCheckerFFITests is MockAVSDeployer, G2Operations { @@ -166,7 +166,7 @@ contract BLSSignatureCheckerFFITests is MockAVSDeployer, G2Operations { uint32 referenceBlockNumber = registrationBlockNumber + blocksBetweenRegistrations * uint32(maxOperatorsToRegister) + 1; cheats.roll(referenceBlockNumber + 100); - BLSOperatorStateRetriever.CheckSignaturesIndices memory checkSignaturesIndices = operatorStateRetriever.getCheckSignaturesIndices( + OperatorStateRetriever.CheckSignaturesIndices memory checkSignaturesIndices = operatorStateRetriever.getCheckSignaturesIndices( registryCoordinator, referenceBlockNumber, quorumNumbers, diff --git a/test/harnesses/BLSRegistryCoordinatorWithIndicesHarness.sol b/test/harnesses/RegistryCoordinatorHarness.sol similarity index 71% rename from test/harnesses/BLSRegistryCoordinatorWithIndicesHarness.sol rename to test/harnesses/RegistryCoordinatorHarness.sol index 25b97b59..9816a332 100644 --- a/test/harnesses/BLSRegistryCoordinatorWithIndicesHarness.sol +++ b/test/harnesses/RegistryCoordinatorHarness.sol @@ -1,17 +1,17 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity =0.8.12; -import "../../src/BLSRegistryCoordinatorWithIndices.sol"; +import "src/RegistryCoordinator.sol"; -// wrapper around the BLSRegistryCoordinatorWithIndices contract that exposes the internal functions for unit testing. -contract BLSRegistryCoordinatorWithIndicesHarness is BLSRegistryCoordinatorWithIndices { +// wrapper around the RegistryCoordinator contract that exposes the internal functions for unit testing. +contract RegistryCoordinatorHarness is RegistryCoordinator { constructor( ISlasher _slasher, IServiceManager _serviceManager, IStakeRegistry _stakeRegistry, - IBLSPubkeyRegistry _blsPubkeyRegistry, + IBLSApkRegistry _blsApkRegistry, IIndexRegistry _indexRegistry - ) BLSRegistryCoordinatorWithIndices(_slasher, _serviceManager, _stakeRegistry, _blsPubkeyRegistry, _indexRegistry) { + ) RegistryCoordinator(_slasher, _serviceManager, _stakeRegistry, _blsApkRegistry, _indexRegistry) { } function setQuorumCount(uint8 count) external { diff --git a/test/mocks/RegistryCoordinatorMock.sol b/test/mocks/RegistryCoordinatorMock.sol index 715a74c6..1dcf9383 100644 --- a/test/mocks/RegistryCoordinatorMock.sol +++ b/test/mocks/RegistryCoordinatorMock.sol @@ -6,6 +6,19 @@ import "../../src/interfaces/IRegistryCoordinator.sol"; contract RegistryCoordinatorMock is IRegistryCoordinator { + function blsApkRegistry() external view returns (IBLSApkRegistry) {} + + function ejectOperator( + address operator, + bytes calldata quorumNumbers + ) external {} + + function getOperatorSetParams(uint8 quorumNumber) external view returns (OperatorSetParam memory) {} + + function indexRegistry() external view returns (IIndexRegistry) {} + + function stakeRegistry() external view returns (IStakeRegistry) {} + function quorumCount() external view returns (uint8) {} /// @notice Returns the bitmap of the quorums the operator is registered for. function operatorIdToQuorumBitmap(bytes32 pubkeyHash) external view returns (uint256){} diff --git a/test/unit/BLSPubkeyRegistryUnit.t.sol b/test/unit/BLSApkRegistryUnit.t.sol similarity index 72% rename from test/unit/BLSPubkeyRegistryUnit.t.sol rename to test/unit/BLSApkRegistryUnit.t.sol index 5c5c326f..fe82c373 100644 --- a/test/unit/BLSPubkeyRegistryUnit.t.sol +++ b/test/unit/BLSApkRegistryUnit.t.sol @@ -3,13 +3,13 @@ pragma solidity =0.8.12; import "forge-std/Test.sol"; -import "../../src/BLSPubkeyRegistry.sol"; -import "../../src/interfaces/IRegistryCoordinator.sol"; -import "../mocks/BLSPublicKeyCompendiumMock.sol"; -import "../mocks/RegistryCoordinatorMock.sol"; +import "src/BLSApkRegistry.sol"; +import "src/interfaces/IRegistryCoordinator.sol"; +import "test/mocks/BLSPublicKeyCompendiumMock.sol"; +import "test/mocks/RegistryCoordinatorMock.sol"; -contract BLSPubkeyRegistryUnitTests is Test { +contract BLSApkRegistryUnitTests is Test { using BN254 for BN254.G1Point; Vm cheats = Vm(HEVM_ADDRESS); @@ -18,7 +18,7 @@ contract BLSPubkeyRegistryUnitTests is Test { bytes32 internal constant ZERO_PK_HASH = hex"ad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5"; - BLSPubkeyRegistry public blsPubkeyRegistry; + BLSApkRegistry public blsApkRegistry; BLSPublicKeyCompendiumMock public pkCompendium; RegistryCoordinatorMock public registryCoordinator; @@ -32,23 +32,23 @@ contract BLSPubkeyRegistryUnitTests is Test { function setUp() external { registryCoordinator = new RegistryCoordinatorMock(); pkCompendium = new BLSPublicKeyCompendiumMock(); - blsPubkeyRegistry = new BLSPubkeyRegistry(registryCoordinator, pkCompendium); + blsApkRegistry = new BLSApkRegistry(registryCoordinator, pkCompendium); // Initialize a quorum _initializeQuorum(defaultQuorumNumber); } function testConstructorArgs() public view { - require(blsPubkeyRegistry.registryCoordinator() == registryCoordinator, "registryCoordinator not set correctly"); - require(blsPubkeyRegistry.pubkeyCompendium() == pkCompendium, "pubkeyCompendium not set correctly"); + require(blsApkRegistry.registryCoordinator() == registryCoordinator, "registryCoordinator not set correctly"); + require(blsApkRegistry.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)); + cheats.expectRevert(bytes("BLSApkRegistry.onlyRegistryCoordinator: caller is not the registry coordinator")); + blsApkRegistry.registerOperator(nonCoordinatorAddress, new bytes(0)); cheats.stopPrank(); } @@ -56,15 +56,15 @@ contract BLSPubkeyRegistryUnitTests is Test { 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)); + cheats.expectRevert(bytes("BLSApkRegistry.onlyRegistryCoordinator: caller is not the registry coordinator")); + blsApkRegistry.deregisterOperator(nonCoordinatorAddress, new bytes(0)); cheats.stopPrank(); } function testOperatorDoesNotOwnPubKeyRegister() public { cheats.startPrank(address(registryCoordinator)); cheats.expectRevert(bytes("BLSPublicKeyCompendium.getRegisteredPubkey: operator is not registered")); - blsPubkeyRegistry.registerOperator(defaultOperator, new bytes(1)); + blsApkRegistry.registerOperator(defaultOperator, new bytes(1)); cheats.stopPrank(); } @@ -82,7 +82,7 @@ contract BLSPubkeyRegistryUnitTests is Test { quorumNumbers[0] = bytes1(defaultQuorumNumber); cheats.startPrank(address(registryCoordinator)); - bytes32 registeredpkHash = blsPubkeyRegistry.registerOperator(operator, quorumNumbers); + bytes32 registeredpkHash = blsApkRegistry.registerOperator(operator, quorumNumbers); cheats.stopPrank(); @@ -107,18 +107,18 @@ contract BLSPubkeyRegistryUnitTests is Test { BN254.G1Point[] memory quorumApksBefore = new BN254.G1Point[](quorumNumbers.length); for(uint8 i = 0; i < quorumNumbers.length; i++){ - quorumApksBefore[i] = blsPubkeyRegistry.getApk(uint8(quorumNumbers[i])); + quorumApksBefore[i] = blsApkRegistry.getApk(uint8(quorumNumbers[i])); } cheats.prank(defaultOperator); pkCompendium.registerPublicKey(defaultPubKey); cheats.prank(address(registryCoordinator)); - blsPubkeyRegistry.registerOperator(defaultOperator, quorumNumbers); + blsApkRegistry.registerOperator(defaultOperator, quorumNumbers); //check quorum apk updates for(uint8 i = 0; i < quorumNumbers.length; i++){ - BN254.G1Point memory quorumApkAfter = blsPubkeyRegistry.getApk(uint8(quorumNumbers[i])); + BN254.G1Point memory quorumApkAfter = blsApkRegistry.getApk(uint8(quorumNumbers[i])); bytes32 temp = BN254.hashG1Point(BN254.plus(quorumApkAfter, BN254.negate(quorumApksBefore[i]))); require(temp == BN254.hashG1Point(defaultPubKey), "quorum apk not updated correctly"); } @@ -127,7 +127,7 @@ contract BLSPubkeyRegistryUnitTests is Test { function testRegisterWithNegativeQuorumApk(address operator, bytes32 x) external { testRegisterOperatorBLSPubkey(defaultOperator, x); - BN254.G1Point memory quorumApk = blsPubkeyRegistry.getApk(defaultQuorumNumber); + BN254.G1Point memory quorumApk = blsApkRegistry.getApk(defaultQuorumNumber); BN254.G1Point memory negatedQuorumApk = BN254.negate(quorumApk); @@ -140,10 +140,10 @@ contract BLSPubkeyRegistryUnitTests is Test { cheats.stopPrank(); cheats.startPrank(address(registryCoordinator)); - blsPubkeyRegistry.registerOperator(operator, quorumNumbers); + blsApkRegistry.registerOperator(operator, quorumNumbers); cheats.stopPrank(); - require(BN254.hashG1Point(blsPubkeyRegistry.getApk(defaultQuorumNumber)) == ZERO_PK_HASH, "quorumApk not set correctly"); + require(BN254.hashG1Point(blsApkRegistry.getApk(defaultQuorumNumber)) == ZERO_PK_HASH, "quorumApk not set correctly"); } function testQuorumApkUpdatesDeregistration(uint8 quorumNumber1, uint8 quorumNumber2) external { @@ -158,17 +158,17 @@ contract BLSPubkeyRegistryUnitTests is Test { BN254.G1Point[] memory quorumApksBefore = new BN254.G1Point[](2); for(uint8 i = 0; i < quorumNumbers.length; i++){ - quorumApksBefore[i] = blsPubkeyRegistry.getApk(uint8(quorumNumbers[i])); + quorumApksBefore[i] = blsApkRegistry.getApk(uint8(quorumNumbers[i])); } cheats.startPrank(address(registryCoordinator)); - blsPubkeyRegistry.deregisterOperator(defaultOperator, quorumNumbers); + blsApkRegistry.deregisterOperator(defaultOperator, quorumNumbers); cheats.stopPrank(); BN254.G1Point memory quorumApkAfter; for(uint8 i = 0; i < quorumNumbers.length; i++){ - quorumApkAfter = blsPubkeyRegistry.getApk(uint8(quorumNumbers[i])); + quorumApkAfter = blsApkRegistry.getApk(uint8(quorumNumbers[i])); require(BN254.hashG1Point(quorumApksBefore[i].plus(defaultPubKey.negate())) == BN254.hashG1Point(quorumApkAfter), "quorum apk not updated correctly"); } } @@ -177,7 +177,7 @@ contract BLSPubkeyRegistryUnitTests is Test { testRegisterOperatorBLSPubkey(defaultOperator, x1); testRegisterOperatorBLSPubkey(defaultOperator2, x2); - BN254.G1Point memory quorumApksBefore= blsPubkeyRegistry.getApk(defaultQuorumNumber); + BN254.G1Point memory quorumApksBefore= blsApkRegistry.getApk(defaultQuorumNumber); bytes memory quorumNumbers = new bytes(1); quorumNumbers[0] = bytes1(defaultQuorumNumber); @@ -187,9 +187,9 @@ contract BLSPubkeyRegistryUnitTests is Test { cheats.stopPrank(); cheats.prank(address(registryCoordinator)); - blsPubkeyRegistry.deregisterOperator(defaultOperator, quorumNumbers); + blsApkRegistry.deregisterOperator(defaultOperator, quorumNumbers); - BN254.G1Point memory pk = blsPubkeyRegistry.getApk(defaultQuorumNumber); + BN254.G1Point memory pk = blsApkRegistry.getApk(defaultQuorumNumber); require(pk.X == 0, "quorum apk not set to zero"); require(pk.Y == 0, "quorum apk not set to zero"); } @@ -205,15 +205,15 @@ contract BLSPubkeyRegistryUnitTests is Test { testRegisterOperatorBLSPubkey(defaultOperator, pk); quorumApk = quorumApk.plus(BN254.hashToG1(pk)); quorumApkHash = bytes24(BN254.hashG1Point(quorumApk)); - uint historyLength = blsPubkeyRegistry.getApkHistoryLength(defaultQuorumNumber); - assertEq(quorumApkHash, blsPubkeyRegistry.getApkHashAtBlockNumberAndIndex(defaultQuorumNumber, uint32(block.number + blockGap), historyLength-1), "incorrect quorum apk update"); + uint historyLength = blsApkRegistry.getApkHistoryLength(defaultQuorumNumber); + assertEq(quorumApkHash, blsApkRegistry.getApkHashAtBlockNumberAndIndex(defaultQuorumNumber, uint32(block.number + blockGap), historyLength-1), "incorrect quorum apk update"); cheats.roll(block.number + 100); if(_generateRandomNumber(i) % 2 == 0){ _deregisterOperator(); quorumApk = quorumApk.plus(BN254.hashToG1(pk).negate()); quorumApkHash = bytes24(BN254.hashG1Point(quorumApk)); - historyLength = blsPubkeyRegistry.getApkHistoryLength(defaultQuorumNumber); - assertEq(quorumApkHash, blsPubkeyRegistry.getApkHashAtBlockNumberAndIndex(defaultQuorumNumber, uint32(block.number + blockGap), historyLength-1), "incorrect quorum apk update"); + historyLength = blsApkRegistry.getApkHistoryLength(defaultQuorumNumber); + assertEq(quorumApkHash, blsApkRegistry.getApkHashAtBlockNumberAndIndex(defaultQuorumNumber, uint32(block.number + blockGap), historyLength-1), "incorrect quorum apk update"); cheats.roll(block.number + 100); i++; } @@ -234,13 +234,13 @@ contract BLSPubkeyRegistryUnitTests is Test { } if(wrongBlockNumber < startingBlockNumber + indexToCheck*100){ emit log_named_uint("index too recent: ", indexToCheck); - cheats.expectRevert(bytes("BLSPubkeyRegistry._validateApkHashAtBlockNumber: index too recent")); - blsPubkeyRegistry.getApkHashAtBlockNumberAndIndex(defaultQuorumNumber, wrongBlockNumber, indexToCheck); + cheats.expectRevert(bytes("BLSApkRegistry._validateApkHashAtBlockNumber: index too recent")); + blsApkRegistry.getApkHashAtBlockNumberAndIndex(defaultQuorumNumber, wrongBlockNumber, indexToCheck); } if (wrongBlockNumber >= startingBlockNumber + (indexToCheck+1)*100){ emit log_named_uint("index not latest: ", indexToCheck); - cheats.expectRevert(bytes("BLSPubkeyRegistry._validateApkHashAtBlockNumber: not latest apk update")); - blsPubkeyRegistry.getApkHashAtBlockNumberAndIndex(defaultQuorumNumber, wrongBlockNumber, indexToCheck); + cheats.expectRevert(bytes("BLSApkRegistry._validateApkHashAtBlockNumber: not latest apk update")); + blsApkRegistry.getApkHashAtBlockNumberAndIndex(defaultQuorumNumber, wrongBlockNumber, indexToCheck); } } @@ -249,7 +249,7 @@ contract BLSPubkeyRegistryUnitTests is Test { ) internal { cheats.prank(address(registryCoordinator)); - blsPubkeyRegistry.initializeQuorum(quorumNumber); + blsApkRegistry.initializeQuorum(quorumNumber); initializedQuorums[quorumNumber] = true; } @@ -273,7 +273,7 @@ contract BLSPubkeyRegistryUnitTests is Test { bytes memory quorumNumbers = new bytes(1); quorumNumbers[0] = bytes1(defaultQuorumNumber); cheats.startPrank(address(registryCoordinator)); - blsPubkeyRegistry.deregisterOperator(defaultOperator, quorumNumbers); + blsApkRegistry.deregisterOperator(defaultOperator, quorumNumbers); cheats.stopPrank(); } diff --git a/test/unit/BLSSignatureCheckerUnit.t.sol b/test/unit/BLSSignatureCheckerUnit.t.sol index aa8de5b7..46974297 100644 --- a/test/unit/BLSSignatureCheckerUnit.t.sol +++ b/test/unit/BLSSignatureCheckerUnit.t.sol @@ -88,7 +88,7 @@ contract BLSSignatureCheckerUnitTests is BLSMockAVSDeployer { // set the nonSignerQuorumBitmapIndices to a different value nonSignerStakesAndSignature.nonSignerQuorumBitmapIndices[0] = 1; - cheats.expectRevert("BLSRegistryCoordinatorWithIndices.getQuorumBitmapAtBlockNumberByIndex: quorumBitmapUpdate is from after blockNumber"); + cheats.expectRevert("RegistryCoordinator.getQuorumBitmapAtBlockNumberByIndex: quorumBitmapUpdate is from after blockNumber"); blsSignatureChecker.checkSignatures( msgHash, quorumNumbers, @@ -161,7 +161,7 @@ contract BLSSignatureCheckerUnitTests is BLSMockAVSDeployer { // set the quorumApkIndices to a different value nonSignerStakesAndSignature.quorumApkIndices[0] = 0; - cheats.expectRevert("BLSPubkeyRegistry._validateApkHashAtBlockNumber: not latest apk update"); + cheats.expectRevert("BLSApkRegistry._validateApkHashAtBlockNumber: not latest apk update"); blsSignatureChecker.checkSignatures( msgHash, quorumNumbers, diff --git a/test/unit/BLSOperatorStateRetrieverUnit.t.sol b/test/unit/OperatorStateRetrieverUnit.t.sol similarity index 96% rename from test/unit/BLSOperatorStateRetrieverUnit.t.sol rename to test/unit/OperatorStateRetrieverUnit.t.sol index 9ae96b7e..b332e2d3 100644 --- a/test/unit/BLSOperatorStateRetrieverUnit.t.sol +++ b/test/unit/OperatorStateRetrieverUnit.t.sol @@ -3,7 +3,7 @@ pragma solidity =0.8.12; import "../utils/MockAVSDeployer.sol"; -contract BLSOperatorStateRetrieverUnitTests is MockAVSDeployer { +contract OperatorStateRetrieverUnitTests is MockAVSDeployer { using BN254 for BN254.G1Point; function setUp() virtual public { @@ -22,7 +22,7 @@ contract BLSOperatorStateRetrieverUnitTests is MockAVSDeployer { uint256 gasBefore = gasleft(); // retrieve the ordered list of operators for each quorum along with their id and stake - (uint256 quorumBitmap, BLSOperatorStateRetriever.Operator[][] memory operators) = + (uint256 quorumBitmap, OperatorStateRetriever.Operator[][] memory operators) = operatorStateRetriever.getOperatorState(registryCoordinator, operatorMetadatas[i].operatorId, blockNumber); uint256 gasAfter = gasleft(); emit log_named_uint("gasUsed", gasBefore - gasAfter); @@ -92,7 +92,7 @@ contract BLSOperatorStateRetrieverUnitTests is MockAVSDeployer { bytes32[] memory nonSignerOperatorIds = new bytes32[](0); - BLSOperatorStateRetriever.CheckSignaturesIndices memory checkSignaturesIndices = + OperatorStateRetriever.CheckSignaturesIndices memory checkSignaturesIndices = operatorStateRetriever.getCheckSignaturesIndices( registryCoordinator, cumulativeBlockNumber, @@ -135,7 +135,7 @@ contract BLSOperatorStateRetrieverUnitTests is MockAVSDeployer { nonSignerOperatorIds[i] = operatorMetadatas[(randomIndex + i) % operatorMetadatas.length].operatorId; } - BLSOperatorStateRetriever.CheckSignaturesIndices memory checkSignaturesIndices = + OperatorStateRetriever.CheckSignaturesIndices memory checkSignaturesIndices = operatorStateRetriever.getCheckSignaturesIndices( registryCoordinator, cumulativeBlockNumber, @@ -168,7 +168,7 @@ contract BLSOperatorStateRetrieverUnitTests is MockAVSDeployer { function _assertExpectedOperators( bytes memory quorumNumbers, - BLSOperatorStateRetriever.Operator[][] memory operators, + OperatorStateRetriever.Operator[][] memory operators, uint256[][] memory expectedOperatorOverallIndices, OperatorMetadata[] memory operatorMetadatas ) internal { diff --git a/test/unit/BLSRegistryCoordinatorWithIndicesUnit.t.sol b/test/unit/RegistryCoordinatorUnit.t.sol similarity index 92% rename from test/unit/BLSRegistryCoordinatorWithIndicesUnit.t.sol rename to test/unit/RegistryCoordinatorUnit.t.sol index 4ae3900c..0ea0e15a 100644 --- a/test/unit/BLSRegistryCoordinatorWithIndicesUnit.t.sol +++ b/test/unit/RegistryCoordinatorUnit.t.sol @@ -3,7 +3,7 @@ pragma solidity =0.8.12; import "../utils/MockAVSDeployer.sol"; -contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { +contract RegistryCoordinatorUnit is MockAVSDeployer { using BN254 for BN254.G1Point; uint8 internal constant PAUSED_REGISTER_OPERATOR = 0; @@ -33,7 +33,7 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { // 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 OperatorSetParamsUpdated(uint8 indexed quorumNumber, IRegistryCoordinator.OperatorSetParam operatorSetParams); event ChurnApproverUpdated(address prevChurnApprover, address newChurnApprover); @@ -45,7 +45,7 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { function testCorrectConstruction() public { assertEq(address(registryCoordinator.stakeRegistry()), address(stakeRegistry)); - assertEq(address(registryCoordinator.blsPubkeyRegistry()), address(blsPubkeyRegistry)); + assertEq(address(registryCoordinator.blsApkRegistry()), address(blsApkRegistry)); assertEq(address(registryCoordinator.indexRegistry()), address(indexRegistry)); assertEq(address(registryCoordinator.slasher()), address(slasher)); @@ -70,7 +70,7 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { } function testSetOperatorSetParams_NotServiceManagerOwner_Reverts() public { - cheats.expectRevert("BLSRegistryCoordinatorWithIndices.onlyServiceManagerOwner: caller is not the service manager owner"); + cheats.expectRevert("RegistryCoordinator.onlyServiceManagerOwner: caller is not the service manager owner"); cheats.prank(defaultOperator); registryCoordinator.setOperatorSetParams(0, operatorSetParams[0]); } @@ -84,7 +84,7 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { function testSetChurnApprover_NotServiceManagerOwner_Reverts() public { address newChurnApprover = address(uint160(uint256(keccak256("newChurnApprover")))); - cheats.expectRevert("BLSRegistryCoordinatorWithIndices.onlyServiceManagerOwner: caller is not the service manager owner"); + cheats.expectRevert("RegistryCoordinator.onlyServiceManagerOwner: caller is not the service manager owner"); cheats.prank(defaultOperator); registryCoordinator.setChurnApprover(newChurnApprover); } @@ -99,7 +99,7 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { function testSetEjector_NotServiceManagerOwner_Reverts() public { address newEjector = address(uint160(uint256(keccak256("newEjector")))); - cheats.expectRevert("BLSRegistryCoordinatorWithIndices.onlyServiceManagerOwner: caller is not the service manager owner"); + cheats.expectRevert("RegistryCoordinator.onlyServiceManagerOwner: caller is not the service manager owner"); cheats.prank(defaultOperator); registryCoordinator.setEjector(newEjector); } @@ -126,7 +126,7 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { function testRegisterOperatorWithCoordinator_EmptyQuorumNumbers_Reverts() public { bytes memory emptyQuorumNumbers = new bytes(0); - cheats.expectRevert("BLSRegistryCoordinatorWithIndices._registerOperator: bitmap cannot be 0"); + cheats.expectRevert("RegistryCoordinator._registerOperator: bitmap cannot be 0"); cheats.prank(defaultOperator); registryCoordinator.registerOperator(emptyQuorumNumbers, defaultSocket); } @@ -156,7 +156,7 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { cheats.expectEmit(true, true, true, true, address(registryCoordinator)); emit OperatorSocketUpdate(defaultOperatorId, defaultSocket); - cheats.expectEmit(true, true, true, true, address(blsPubkeyRegistry)); + cheats.expectEmit(true, true, true, true, address(blsApkRegistry)); emit OperatorAddedToQuorums(defaultOperator, quorumNumbers); cheats.expectEmit(true, true, true, true, address(stakeRegistry)); emit OperatorStakeUpdate(defaultOperatorId, defaultQuorumNumber, defaultStake); @@ -202,7 +202,7 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { cheats.expectEmit(true, true, true, true, address(registryCoordinator)); emit OperatorSocketUpdate(defaultOperatorId, defaultSocket); - cheats.expectEmit(true, true, true, true, address(blsPubkeyRegistry)); + cheats.expectEmit(true, true, true, true, address(blsApkRegistry)); emit OperatorAddedToQuorums(defaultOperator, quorumNumbers); for (uint i = 0; i < quorumNumbers.length; i++) { @@ -259,7 +259,7 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { stakeRegistry.setOperatorWeight(uint8(newQuorumNumbers[0]), defaultOperator, defaultStake); cheats.expectEmit(true, true, true, true, address(registryCoordinator)); emit OperatorSocketUpdate(defaultOperatorId, defaultSocket); - cheats.expectEmit(true, true, true, true, address(blsPubkeyRegistry)); + cheats.expectEmit(true, true, true, true, address(blsApkRegistry)); emit OperatorAddedToQuorums(defaultOperator, newQuorumNumbers); cheats.expectEmit(true, true, true, true, address(stakeRegistry)); emit OperatorStakeUpdate(defaultOperatorId, uint8(newQuorumNumbers[0]), defaultStake); @@ -324,7 +324,7 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { stakeRegistry.setOperatorWeight(defaultQuorumNumber, operatorToRegister, defaultStake); cheats.prank(operatorToRegister); - cheats.expectRevert("BLSRegistryCoordinatorWithIndices.registerOperator: operator count exceeds maximum"); + cheats.expectRevert("RegistryCoordinator.registerOperator: operator count exceeds maximum"); registryCoordinator.registerOperator(quorumNumbers, defaultSocket); } @@ -342,7 +342,7 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { cheats.prank(defaultOperator); cheats.roll(nextRegistrationBlockNumber); - cheats.expectRevert("BLSRegistryCoordinatorWithIndices._registerOperator: operator already registered for some quorums being registered for"); + cheats.expectRevert("RegistryCoordinator._registerOperator: operator already registered for some quorums being registered for"); registryCoordinator.registerOperator(quorumNumbers, defaultSocket); } @@ -366,7 +366,7 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { bytes memory quorumNumbers = new bytes(1); quorumNumbers[0] = bytes1(defaultQuorumNumber); - cheats.expectRevert("BLSRegistryCoordinatorWithIndices._deregisterOperator: operator is not registered"); + cheats.expectRevert("RegistryCoordinator._deregisterOperator: operator is not registered"); cheats.prank(defaultOperator); registryCoordinator.deregisterOperator(quorumNumbers); } @@ -382,7 +382,7 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { quorumNumbers[0] = bytes1(defaultQuorumNumber + 1); quorumNumbers[1] = bytes1(defaultQuorumNumber + 2); - cheats.expectRevert("BLSRegistryCoordinatorWithIndices._deregisterOperator: operator is not registered for specified quorums"); + cheats.expectRevert("RegistryCoordinator._deregisterOperator: operator is not registered for specified quorums"); cheats.prank(defaultOperator); registryCoordinator.deregisterOperator(quorumNumbers); } @@ -404,7 +404,7 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { uint256 quorumBitmap = BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers); - cheats.expectEmit(true, true, true, true, address(blsPubkeyRegistry)); + cheats.expectEmit(true, true, true, true, address(blsApkRegistry)); emit OperatorRemovedFromQuorums(defaultOperator, quorumNumbers); cheats.expectEmit(true, true, true, true, address(stakeRegistry)); emit OperatorStakeUpdate(defaultOperatorId, defaultQuorumNumber, 0); @@ -452,7 +452,7 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { registryCoordinator.registerOperator(quorumNumbers, defaultSocket); - cheats.expectEmit(true, true, true, true, address(blsPubkeyRegistry)); + cheats.expectEmit(true, true, true, true, address(blsApkRegistry)); emit OperatorRemovedFromQuorums(defaultOperator, quorumNumbers); for (uint i = 0; i < quorumNumbers.length; i++) { cheats.expectEmit(true, true, true, true, address(stakeRegistry)); @@ -528,7 +528,7 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { operatorIdsToSwap[i] = lastOperatorInQuorum[uint8(operatorToDeregisterQuorumNumbers[i])]; } - cheats.expectEmit(true, true, true, true, address(blsPubkeyRegistry)); + cheats.expectEmit(true, true, true, true, address(blsApkRegistry)); emit OperatorRemovedFromQuorums(operatorToDerigister, operatorToDeregisterQuorumNumbers); for (uint i = 0; i < operatorToDeregisterQuorumNumbers.length; i++) { @@ -636,7 +636,7 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { address operatorToKick; // register last operator before kick - IBLSRegistryCoordinatorWithIndices.OperatorKickParam[] memory operatorKickParams = new IBLSRegistryCoordinatorWithIndices.OperatorKickParam[](1); + IRegistryCoordinator.OperatorKickParam[] memory operatorKickParams = new IRegistryCoordinator.OperatorKickParam[](1); { BN254.G1Point memory pubKey = BN254.hashToG1(keccak256(abi.encodePacked(pseudoRandomNumber, numOperators - 1))); operatorToKickId = pubKey.hashG1Point(); @@ -648,7 +648,7 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { // operatorIdsToSwap[0] = operatorToRegisterId operatorIdsToSwap[0] = operatorToRegisterId; - operatorKickParams[0] = IBLSRegistryCoordinatorWithIndices.OperatorKickParam({ + operatorKickParams[0] = IRegistryCoordinator.OperatorKickParam({ quorumNumber: defaultQuorumNumber, operator: operatorToKick }); @@ -660,14 +660,14 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { stakeRegistry.setOperatorWeight(defaultQuorumNumber, operatorToRegister, registeringStake); cheats.roll(registrationBlockNumber); - cheats.expectEmit(true, true, true, true, address(blsPubkeyRegistry)); + cheats.expectEmit(true, true, true, true, address(blsApkRegistry)); emit OperatorAddedToQuorums(operatorToRegister, quorumNumbers); cheats.expectEmit(true, true, true, true, address(stakeRegistry)); emit OperatorStakeUpdate(operatorToRegisterId, defaultQuorumNumber, registeringStake); cheats.expectEmit(true, true, true, true, address(indexRegistry)); emit QuorumIndexUpdate(operatorToRegisterId, defaultQuorumNumber, numOperators); - cheats.expectEmit(true, true, true, true, address(blsPubkeyRegistry)); + cheats.expectEmit(true, true, true, true, address(blsApkRegistry)); emit OperatorRemovedFromQuorums(operatorKickParams[0].operator, quorumNumbers); cheats.expectEmit(true, true, true, true, address(stakeRegistry)); emit OperatorStakeUpdate(operatorToKickId, defaultQuorumNumber, 0); @@ -719,7 +719,7 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { ( address operatorToRegister, BN254.G1Point memory operatorToRegisterPubKey, - IBLSRegistryCoordinatorWithIndices.OperatorKickParam[] memory operatorKickParams + IRegistryCoordinator.OperatorKickParam[] memory operatorKickParams ) = _testRegisterOperatorWithKicks_SetUp(pseudoRandomNumber, quorumNumbers, defaultStake); bytes32 operatorToRegisterId = operatorToRegisterPubKey.hashG1Point(); @@ -728,7 +728,7 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { cheats.roll(registrationBlockNumber); ISignatureUtils.SignatureWithSaltAndExpiry memory signatureWithExpiry = _signOperatorChurnApproval(operatorToRegisterId, operatorKickParams, defaultSalt, block.timestamp + 10); cheats.prank(operatorToRegister); - cheats.expectRevert("BLSRegistryCoordinatorWithIndices._validateChurn: incoming operator has insufficient stake for churn"); + cheats.expectRevert("RegistryCoordinator._validateChurn: incoming operator has insufficient stake for churn"); registryCoordinator.registerOperatorWithChurn(quorumNumbers, defaultSocket, operatorKickParams, signatureWithExpiry); } @@ -740,7 +740,7 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { ( address operatorToRegister, BN254.G1Point memory operatorToRegisterPubKey, - IBLSRegistryCoordinatorWithIndices.OperatorKickParam[] memory operatorKickParams + IRegistryCoordinator.OperatorKickParam[] memory operatorKickParams ) = _testRegisterOperatorWithKicks_SetUp(pseudoRandomNumber, quorumNumbers, operatorToKickStake); bytes32 operatorToRegisterId = operatorToRegisterPubKey.hashG1Point(); @@ -751,7 +751,7 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { cheats.roll(registrationBlockNumber); ISignatureUtils.SignatureWithSaltAndExpiry memory signatureWithExpiry = _signOperatorChurnApproval(operatorToRegisterId, operatorKickParams, defaultSalt, block.timestamp + 10); cheats.prank(operatorToRegister); - cheats.expectRevert("BLSRegistryCoordinatorWithIndices._validateChurn: cannot kick operator with more than kickBIPsOfTotalStake"); + cheats.expectRevert("RegistryCoordinator._validateChurn: cannot kick operator with more than kickBIPsOfTotalStake"); registryCoordinator.registerOperatorWithChurn(quorumNumbers, defaultSocket, operatorKickParams, signatureWithExpiry); } @@ -762,7 +762,7 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { ( address operatorToRegister, , - IBLSRegistryCoordinatorWithIndices.OperatorKickParam[] memory operatorKickParams + IRegistryCoordinator.OperatorKickParam[] memory operatorKickParams ) = _testRegisterOperatorWithKicks_SetUp(pseudoRandomNumber, quorumNumbers, defaultStake); uint96 registeringStake = defaultKickBIPsOfOperatorStake * defaultStake; @@ -785,7 +785,7 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { ( address operatorToRegister, BN254.G1Point memory operatorToRegisterPubKey, - IBLSRegistryCoordinatorWithIndices.OperatorKickParam[] memory operatorKickParams + IRegistryCoordinator.OperatorKickParam[] memory operatorKickParams ) = _testRegisterOperatorWithKicks_SetUp(pseudoRandomNumber, quorumNumbers, defaultStake); bytes32 operatorToRegisterId = operatorToRegisterPubKey.hashG1Point(); @@ -795,7 +795,7 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { cheats.roll(registrationBlockNumber); ISignatureUtils.SignatureWithSaltAndExpiry memory signatureWithSaltAndExpiry = _signOperatorChurnApproval(operatorToRegisterId, operatorKickParams, defaultSalt, block.timestamp - 1); cheats.prank(operatorToRegister); - cheats.expectRevert("BLSRegistryCoordinatorWithIndices._verifyChurnApproverSignature: churnApprover signature expired"); + cheats.expectRevert("RegistryCoordinator._verifyChurnApproverSignature: churnApprover signature expired"); registryCoordinator.registerOperatorWithChurn(quorumNumbers, defaultSocket, operatorKickParams, signatureWithSaltAndExpiry); } @@ -809,7 +809,7 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { cheats.prank(defaultOperator); registryCoordinator.registerOperator(quorumNumbers, defaultSocket); - cheats.expectEmit(true, true, true, true, address(blsPubkeyRegistry)); + cheats.expectEmit(true, true, true, true, address(blsApkRegistry)); emit OperatorRemovedFromQuorums(defaultOperator, quorumNumbers); cheats.expectEmit(true, true, true, true, address(stakeRegistry)); @@ -848,7 +848,7 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { bytes memory quorumNumbersToEject = new bytes(1); quorumNumbersToEject[0] = quorumNumbers[0]; - cheats.expectEmit(true, true, true, true, address(blsPubkeyRegistry)); + cheats.expectEmit(true, true, true, true, address(blsApkRegistry)); emit OperatorRemovedFromQuorums(defaultOperator, quorumNumbersToEject); cheats.expectEmit(true, true, true, true, address(stakeRegistry)); @@ -881,7 +881,7 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { cheats.prank(defaultOperator); registryCoordinator.registerOperator(quorumNumbers, defaultSocket); - cheats.expectRevert("BLSRegistryCoordinatorWithIndices.onlyEjector: caller is not the ejector"); + cheats.expectRevert("RegistryCoordinator.onlyEjector: caller is not the ejector"); cheats.prank(defaultOperator); registryCoordinator.ejectOperator(defaultOperator, quorumNumbers); } @@ -902,11 +902,11 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { function testUpdateSocket_NotRegistered_Reverts() public { cheats.prank(defaultOperator); - cheats.expectRevert("BLSRegistryCoordinatorWithIndices.updateSocket: operator is not registered"); + cheats.expectRevert("RegistryCoordinator.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) { + function _testRegisterOperatorWithKicks_SetUp(uint256 pseudoRandomNumber, bytes memory quorumNumbers, uint96 operatorToKickStake) internal returns(address operatorToRegister, BN254.G1Point memory operatorToRegisterPubKey, IRegistryCoordinator.OperatorKickParam[] memory operatorKickParams) { uint32 kickRegistrationBlockNumber = 100; uint256 quorumBitmap = BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers); @@ -927,7 +927,7 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { address operatorToKick; // register last operator before kick - operatorKickParams = new IBLSRegistryCoordinatorWithIndices.OperatorKickParam[](1); + operatorKickParams = new IRegistryCoordinator.OperatorKickParam[](1); { BN254.G1Point memory pubKey = BN254.hashToG1(keccak256(abi.encodePacked(pseudoRandomNumber, defaultMaxOperatorCount - 1))); operatorToKickId = pubKey.hashG1Point(); @@ -940,7 +940,7 @@ contract BLSRegistryCoordinatorWithIndicesUnit is MockAVSDeployer { // operatorIdsToSwap[0] = operatorToRegisterId operatorIdsToSwap[0] = operatorToRegisterId; - operatorKickParams[0] = IBLSRegistryCoordinatorWithIndices.OperatorKickParam({ + operatorKickParams[0] = IRegistryCoordinator.OperatorKickParam({ quorumNumber: uint8(quorumNumbers[0]), operator: operatorToKick }); diff --git a/test/unit/StakeRegistryUnit.t.sol b/test/unit/StakeRegistryUnit.t.sol index a404baf9..2232f0e0 100644 --- a/test/unit/StakeRegistryUnit.t.sol +++ b/test/unit/StakeRegistryUnit.t.sol @@ -14,8 +14,7 @@ import {IStakeRegistry} from "src/interfaces/IStakeRegistry.sol"; import {IServiceManager} from "src/interfaces/IServiceManager.sol"; import {IIndexRegistry} from "src/interfaces/IIndexRegistry.sol"; import {IRegistryCoordinator} from "src/interfaces/IRegistryCoordinator.sol"; -import {IBLSPubkeyRegistry} from "src/interfaces/IBLSPubkeyRegistry.sol"; -import {IBLSRegistryCoordinatorWithIndices} from "src/interfaces/IBLSRegistryCoordinatorWithIndices.sol"; +import {IBLSApkRegistry} from "src/interfaces/IBLSApkRegistry.sol"; import {BitmapUtils} from "eigenlayer-contracts/src/contracts/libraries/BitmapUtils.sol"; @@ -28,7 +27,7 @@ import {SlasherMock} from "eigenlayer-contracts/src/test/mocks/SlasherMock.sol"; import {StakeRegistryHarness} from "test/harnesses/StakeRegistryHarness.sol"; import {StakeRegistry} from "src/StakeRegistry.sol"; -import {BLSRegistryCoordinatorWithIndicesHarness} from "test/harnesses/BLSRegistryCoordinatorWithIndicesHarness.sol"; +import {RegistryCoordinatorHarness} from "test/harnesses/RegistryCoordinatorHarness.sol"; import "forge-std/Test.sol"; @@ -43,7 +42,7 @@ contract StakeRegistryUnitTests is Test { Slasher public slasherImplementation; StakeRegistryHarness public stakeRegistryImplementation; StakeRegistryHarness public stakeRegistry; - BLSRegistryCoordinatorWithIndicesHarness public registryCoordinator; + RegistryCoordinatorHarness public registryCoordinator; ServiceManagerMock public serviceManagerMock; StrategyManagerMock public strategyManagerMock; @@ -53,7 +52,7 @@ contract StakeRegistryUnitTests is Test { 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 apkRegistry = address(uint160(uint256(keccak256("apkRegistry")))); address public indexRegistry = address(uint160(uint256(keccak256("indexRegistry")))); uint256 churnApproverPrivateKey = uint256(keccak256("churnApproverPrivateKey")); @@ -105,11 +104,11 @@ contract StakeRegistryUnitTests is Test { slasher ); - registryCoordinator = new BLSRegistryCoordinatorWithIndicesHarness( + registryCoordinator = new RegistryCoordinatorHarness( slasher, serviceManagerMock, stakeRegistry, - IBLSPubkeyRegistry(pubkeyRegistry), + IBLSApkRegistry(apkRegistry), IIndexRegistry(indexRegistry) ); diff --git a/test/utils/BLSMockAVSDeployer.sol b/test/utils/BLSMockAVSDeployer.sol index 0f4c5d27..ab0122e2 100644 --- a/test/utils/BLSMockAVSDeployer.sol +++ b/test/utils/BLSMockAVSDeployer.sol @@ -1,11 +1,11 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity =0.8.12; -import {BLSSignatureChecker} from "../../src/BLSSignatureChecker.sol"; -import {MockAVSDeployer} from "./MockAVSDeployer.sol"; -import {BN254} from "../../src/libraries/BN254.sol"; -import {BLSOperatorStateRetriever} from "../../src/BLSOperatorStateRetriever.sol"; -import {BitmapUtils} from "../../src/libraries/BitmapUtils.sol"; +import {BLSSignatureChecker} from "src/BLSSignatureChecker.sol"; +import {MockAVSDeployer} from "test/utils/MockAVSDeployer.sol"; +import {BN254} from "src/libraries/BN254.sol"; +import {OperatorStateRetriever} from "src/OperatorStateRetriever.sol"; +import {BitmapUtils} from "src/libraries/BitmapUtils.sol"; contract BLSMockAVSDeployer is MockAVSDeployer { using BN254 for BN254.G1Point; @@ -114,7 +114,7 @@ contract BLSMockAVSDeployer is MockAVSDeployer { uint32 referenceBlockNumber = registrationBlockNumber + blocksBetweenRegistrations * uint32(maxOperatorsToRegister) + 1; cheats.roll(referenceBlockNumber + 100); - BLSOperatorStateRetriever.CheckSignaturesIndices memory checkSignaturesIndices = operatorStateRetriever.getCheckSignaturesIndices( + OperatorStateRetriever.CheckSignaturesIndices memory checkSignaturesIndices = operatorStateRetriever.getCheckSignaturesIndices( registryCoordinator, referenceBlockNumber, quorumNumbers, diff --git a/test/utils/MockAVSDeployer.sol b/test/utils/MockAVSDeployer.sol index e0c8573f..d8396966 100644 --- a/test/utils/MockAVSDeployer.sol +++ b/test/utils/MockAVSDeployer.sol @@ -10,52 +10,33 @@ import {PauserRegistry} from "eigenlayer-contracts/src/contracts/permissions/Pau import {IStrategyManager} from "eigenlayer-contracts/src/contracts/interfaces/IStrategyManager.sol"; import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol"; -import {BitmapUtils} from "../../src/libraries/BitmapUtils.sol"; -import {BN254} from "../../src/libraries/BN254.sol"; - -<<<<<<< HEAD -import {BLSPublicKeyCompendium} from "../../src/BLSPublicKeyCompendium.sol"; -import {BLSOperatorStateRetriever} from "../../src/BLSOperatorStateRetriever.sol"; -import {BLSRegistryCoordinatorWithIndices} from "../../src/BLSRegistryCoordinatorWithIndices.sol"; -import {BLSRegistryCoordinatorWithIndicesHarness} from "../harnesses/BLSRegistryCoordinatorWithIndicesHarness.sol"; -import {BLSPubkeyRegistry} from "../../src/BLSPubkeyRegistry.sol"; -import {StakeRegistry} from "../../src/StakeRegistry.sol"; -import {IndexRegistry} from "../../src/IndexRegistry.sol"; -import {IServiceManager} from "../../src/interfaces/IServiceManager.sol"; -import {IBLSPubkeyRegistry} from "../../src/interfaces/IBLSPubkeyRegistry.sol"; -import {IRegistryCoordinator} from "../../src/interfaces/IRegistryCoordinator.sol"; -import {IVoteWeigher} from "../../src/interfaces/IVoteWeigher.sol"; -import {IStakeRegistry} from "../../src/interfaces/IStakeRegistry.sol"; -import {IIndexRegistry} from "../../src/interfaces/IIndexRegistry.sol"; -import {IBLSRegistryCoordinatorWithIndices} from "../../src/interfaces/IBLSRegistryCoordinatorWithIndices.sol"; -======= +import {BitmapUtils} from "src/libraries/BitmapUtils.sol"; +import {BN254} from "src/libraries/BN254.sol"; + import {BLSPublicKeyCompendium} from "src/BLSPublicKeyCompendium.sol"; -import {BLSOperatorStateRetriever} from "src/BLSOperatorStateRetriever.sol"; -import {BLSRegistryCoordinatorWithIndices} from "src/BLSRegistryCoordinatorWithIndices.sol"; -import {BLSRegistryCoordinatorWithIndicesHarness} from "test/harnesses/BLSRegistryCoordinatorWithIndicesHarness.sol"; -import {BLSPubkeyRegistry} from "src/BLSPubkeyRegistry.sol"; +import {OperatorStateRetriever} from "src/OperatorStateRetriever.sol"; +import {RegistryCoordinator} from "src/RegistryCoordinator.sol"; +import {RegistryCoordinatorHarness} from "test/harnesses/RegistryCoordinatorHarness.sol"; +import {BLSApkRegistry} from "src/BLSApkRegistry.sol"; import {StakeRegistry} from "src/StakeRegistry.sol"; import {IndexRegistry} from "src/IndexRegistry.sol"; import {IServiceManager} from "src/interfaces/IServiceManager.sol"; -import {IBLSPubkeyRegistry} from "src/interfaces/IBLSPubkeyRegistry.sol"; -import {IRegistryCoordinator} from "src/interfaces/IRegistryCoordinator.sol"; +import {IBLSApkRegistry} from "src/interfaces/IBLSApkRegistry.sol"; import {IStakeRegistry} from "src/interfaces/IStakeRegistry.sol"; import {IIndexRegistry} from "src/interfaces/IIndexRegistry.sol"; -import {IBLSRegistryCoordinatorWithIndices} from "src/interfaces/IBLSRegistryCoordinatorWithIndices.sol"; ->>>>>>> 12b09de (fix: fix compilation issues and tests) +import {IRegistryCoordinator} from "src/interfaces/IRegistryCoordinator.sol"; import {StrategyManagerMock} from "eigenlayer-contracts/src/test/mocks/StrategyManagerMock.sol"; import {EigenPodManagerMock} from "eigenlayer-contracts/src/test/mocks/EigenPodManagerMock.sol"; -import {ServiceManagerMock} from "../mocks/ServiceManagerMock.sol"; +import {ServiceManagerMock} from "test/mocks/ServiceManagerMock.sol"; import {OwnableMock} from "eigenlayer-contracts/src/test/mocks/OwnableMock.sol"; import {DelegationManagerMock} from "eigenlayer-contracts/src/test/mocks/DelegationManagerMock.sol"; import {SlasherMock} from "eigenlayer-contracts/src/test/mocks/SlasherMock.sol"; -import {BLSPublicKeyCompendiumMock} from "../mocks/BLSPublicKeyCompendiumMock.sol"; +import {BLSPublicKeyCompendiumMock} from "test/mocks/BLSPublicKeyCompendiumMock.sol"; import {EmptyContract} from "eigenlayer-contracts/src/test/mocks/EmptyContract.sol"; -import {StakeRegistryHarness} from "../harnesses/StakeRegistryHarness.sol"; -import {BLSRegistryCoordinatorWithIndices} from "../harnesses/BLSRegistryCoordinatorWithIndicesHarness.sol"; +import {StakeRegistryHarness} from "test/harnesses/StakeRegistryHarness.sol"; import "forge-std/Test.sol"; @@ -73,15 +54,15 @@ contract MockAVSDeployer is Test { EmptyContract public emptyContract; BLSPublicKeyCompendiumMock public pubkeyCompendium; - BLSRegistryCoordinatorWithIndicesHarness public registryCoordinatorImplementation; + RegistryCoordinatorHarness public registryCoordinatorImplementation; StakeRegistryHarness public stakeRegistryImplementation; - IBLSPubkeyRegistry public blsPubkeyRegistryImplementation; + IBLSApkRegistry public blsApkRegistryImplementation; IIndexRegistry public indexRegistryImplementation; - BLSOperatorStateRetriever public operatorStateRetriever; - BLSRegistryCoordinatorWithIndicesHarness public registryCoordinator; + OperatorStateRetriever public operatorStateRetriever; + RegistryCoordinatorHarness public registryCoordinator; StakeRegistryHarness public stakeRegistry; - IBLSPubkeyRegistry public blsPubkeyRegistry; + IBLSApkRegistry public blsApkRegistry; IIndexRegistry public indexRegistry; ServiceManagerMock public serviceManagerMock; @@ -112,7 +93,7 @@ contract MockAVSDeployer is Test { uint16 defaultKickBIPsOfTotalStake = 150; uint8 numQuorums = 192; - IBLSRegistryCoordinatorWithIndices.OperatorSetParam[] operatorSetParams; + IRegistryCoordinator.OperatorSetParam[] operatorSetParams; uint8 maxQuorumsToRegisterFor = 4; uint256 maxOperatorsToRegister = 4; @@ -173,7 +154,7 @@ contract MockAVSDeployer is Test { cheats.startPrank(serviceManagerOwner); // make the serviceManagerOwner the owner of the serviceManager contract serviceManagerMock = new ServiceManagerMock(slasher); - registryCoordinator = BLSRegistryCoordinatorWithIndicesHarness(address( + registryCoordinator = RegistryCoordinatorHarness(address( new TransparentUpgradeableProxy( address(emptyContract), address(proxyAdmin), @@ -201,7 +182,7 @@ contract MockAVSDeployer is Test { ) ); - blsPubkeyRegistry = BLSPubkeyRegistry( + blsApkRegistry = BLSApkRegistry( address( new TransparentUpgradeableProxy( address(emptyContract), @@ -225,14 +206,14 @@ contract MockAVSDeployer is Test { address(stakeRegistryImplementation) ); - blsPubkeyRegistryImplementation = new BLSPubkeyRegistry( + blsApkRegistryImplementation = new BLSApkRegistry( registryCoordinator, BLSPublicKeyCompendium(address(pubkeyCompendium)) ); proxyAdmin.upgrade( - TransparentUpgradeableProxy(payable(address(blsPubkeyRegistry))), - address(blsPubkeyRegistryImplementation) + TransparentUpgradeableProxy(payable(address(blsApkRegistry))), + address(blsApkRegistryImplementation) ); indexRegistryImplementation = new IndexRegistry( @@ -261,18 +242,18 @@ contract MockAVSDeployer is Test { ); } - registryCoordinatorImplementation = new BLSRegistryCoordinatorWithIndicesHarness( + registryCoordinatorImplementation = new RegistryCoordinatorHarness( slasher, serviceManagerMock, stakeRegistry, - blsPubkeyRegistry, + blsApkRegistry, indexRegistry ); { delete operatorSetParams; for (uint i = 0; i < numQuorumsToAdd; i++) { // hard code these for now - operatorSetParams.push(IBLSRegistryCoordinatorWithIndices.OperatorSetParam({ + operatorSetParams.push(IRegistryCoordinator.OperatorSetParam({ maxOperatorCount: defaultMaxOperatorCount, kickBIPsOfOperatorStake: defaultKickBIPsOfOperatorStake, kickBIPsOfTotalStake: defaultKickBIPsOfTotalStake @@ -283,7 +264,7 @@ contract MockAVSDeployer is Test { TransparentUpgradeableProxy(payable(address(registryCoordinator))), address(registryCoordinatorImplementation), abi.encodeWithSelector( - BLSRegistryCoordinatorWithIndices.initialize.selector, + RegistryCoordinator.initialize.selector, churnApprover, ejector, pauserRegistry, @@ -295,7 +276,7 @@ contract MockAVSDeployer is Test { ); } - operatorStateRetriever = new BLSOperatorStateRetriever(); + operatorStateRetriever = new OperatorStateRetriever(); cheats.stopPrank(); } @@ -395,7 +376,7 @@ contract MockAVSDeployer is Test { return bytes32(uint256(start) + inc); } - function _signOperatorChurnApproval(bytes32 registeringOperatorId, IBLSRegistryCoordinatorWithIndices.OperatorKickParam[] memory operatorKickParams, bytes32 salt, uint256 expiry) internal view returns(ISignatureUtils.SignatureWithSaltAndExpiry memory) { + function _signOperatorChurnApproval(bytes32 registeringOperatorId, IRegistryCoordinator.OperatorKickParam[] memory operatorKickParams, bytes32 salt, uint256 expiry) internal view returns(ISignatureUtils.SignatureWithSaltAndExpiry memory) { bytes32 digestHash = registryCoordinator.calculateOperatorChurnApprovalDigestHash( registeringOperatorId, operatorKickParams, From 52c1ef51424b65afe35acc65c6a1af44dae1aa97 Mon Sep 17 00:00:00 2001 From: ChaoticWalrus <93558947+ChaoticWalrus@users.noreply.github.com> Date: Thu, 7 Dec 2023 14:05:36 -0800 Subject: [PATCH 055/101] chore: copy BLSPublicKeyCompendium storage and functions into BLSApkRegistry --- src/BLSApkRegistry.sol | 82 ++++++++++++++++++++++++++++++ src/BLSApkRegistryStorage.sol | 14 ++++- src/interfaces/IBLSApkRegistry.sol | 40 ++++++++++++++- 3 files changed, 133 insertions(+), 3 deletions(-) diff --git a/src/BLSApkRegistry.sol b/src/BLSApkRegistry.sol index 14093fa0..b5e9ca23 100644 --- a/src/BLSApkRegistry.sol +++ b/src/BLSApkRegistry.sol @@ -95,6 +95,60 @@ contract BLSApkRegistry is BLSApkRegistryStorage { })); } + /** + * @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( + pubkeyHash != ZERO_PK_HASH, "BLSPublicKeyCompendium.registerBLSPublicKey: cannot register zero pubkey" + ); + 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: either the G1 signature is wrong, or G1 and G2 private key do not match"); + + operatorToPubkey[msg.sender] = pubkeyG1; + operatorToPubkeyHash[msg.sender] = pubkeyHash; + pubkeyHashToOperator[pubkeyHash] = msg.sender; + + emit NewPubkeyRegistration(msg.sender, pubkeyG1, pubkeyG2); + } + /******************************************************************************* INTERNAL FUNCTIONS *******************************************************************************/ @@ -148,6 +202,34 @@ contract BLSApkRegistry is BLSApkRegistryStorage { /******************************************************************************* VIEW FUNCTIONS *******************************************************************************/ + /** + * @notice Returns the pubkey and pubkey hash of an operator + * @dev Reverts if the operator has not registered a valid pubkey + */ + function getRegisteredPubkey(address operator) public view returns (BN254.G1Point memory, bytes32) { + BN254.G1Point memory pubkey = operatorToPubkey[operator]; + bytes32 pubkeyHash = operatorToPubkeyHash[operator]; + + require( + pubkeyHash != bytes32(0), + "BLSApkRegistry.getRegisteredPubkey: operator is not registered" + ); + + return (pubkey, pubkeyHash); + } + + /** + * @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" + ))); + } /** * @notice Returns the indices of the quorumApks index at `blockNumber` for the provided `quorumNumbers` diff --git a/src/BLSApkRegistryStorage.sol b/src/BLSApkRegistryStorage.sol index 1629d708..3f56cd42 100644 --- a/src/BLSApkRegistryStorage.sol +++ b/src/BLSApkRegistryStorage.sol @@ -10,11 +10,23 @@ import {Initializable} from "@openzeppelin-upgrades/contracts/proxy/utils/Initia import {BN254} from "src/libraries/BN254.sol"; abstract contract BLSApkRegistryStorage is Initializable, IBLSApkRegistry { + /// @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; + // storage for individual pubkeys + /// @notice maps operator address to pubkey hash + mapping(address => bytes32) public operatorToPubkeyHash; + /// @notice maps pubkey hash to operator address + mapping(bytes32 => address) public pubkeyHashToOperator; + /// @notice maps operator address to pubkeyG1 + mapping(address => BN254.G1Point) public operatorToPubkey; + + // storage for aggregate pubkeys (APKs) /// @notice maps quorumNumber => historical aggregate pubkey updates mapping(uint8 => ApkUpdate[]) public apkHistory; /// @notice maps quorumNumber => current aggregate pubkey of quorum @@ -28,5 +40,5 @@ abstract contract BLSApkRegistryStorage is Initializable, IBLSApkRegistry { } // storage gap for upgradeability - uint256[48] private __GAP; + uint256[45] private __GAP; } diff --git a/src/interfaces/IBLSApkRegistry.sol b/src/interfaces/IBLSApkRegistry.sol index 58cdc10b..0569bc9d 100644 --- a/src/interfaces/IBLSApkRegistry.sol +++ b/src/interfaces/IBLSApkRegistry.sol @@ -11,13 +11,16 @@ import {BN254} from "src/libraries/BN254.sol"; */ interface IBLSApkRegistry is IRegistry { // EVENTS - // Emitted when a new operator pubkey is registered for a set of quorums + /// @notice Emitted when `operator` registers with the public keys `pubkeyG1` and `pubkeyG2`. + event NewPubkeyRegistration(address indexed operator, BN254.G1Point pubkeyG1, BN254.G2Point pubkeyG2); + + // @notice 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 + // @notice Emitted when an operator pubkey is removed from a set of quorums event OperatorRemovedFromQuorums( address operator, bytes quorumNumbers @@ -66,6 +69,39 @@ interface IBLSApkRegistry is IRegistry { */ function initializeQuorum(uint8 quorumNumber) external; + /** + * @notice mapping from operator address to pubkey hash. + * Returns *zero* if the `operator` has never registered, and otherwise returns the hash of the public key of the operator. + */ + function operatorToPubkeyHash(address operator) external view returns (bytes32); + + /** + * @notice mapping from pubkey hash to operator address. + * Returns *zero* if no operator has ever registered the public key corresponding to `pubkeyHash`, + * and otherwise returns the (unique) registered operator who owns the BLS public key that is the preimage of `pubkeyHash`. + */ + function pubkeyHashToOperator(bytes32 pubkeyHash) external view returns (address); + + /** + * @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; + + /** + * @notice Returns the pubkey and pubkey hash of an operator + * @dev Reverts if the operator has not registered a valid pubkey + */ + function getRegisteredPubkey(address operator) external view returns (BN254.G1Point memory, bytes32); + + /** + * @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) external view returns (BN254.G1Point memory); + /// @notice Returns the current APK for the provided `quorumNumber ` function getApk(uint8 quorumNumber) external view returns (BN254.G1Point memory); From 8c8c0ab5ffbb0d07bd59f1d0ca636fe1a5f86d3a Mon Sep 17 00:00:00 2001 From: ChaoticWalrus <93558947+ChaoticWalrus@users.noreply.github.com> Date: Thu, 7 Dec 2023 15:34:49 -0800 Subject: [PATCH 056/101] chore: delete BLSPublicKeyCompendium and associated interface requires changing a bunch of files to make things work. I had to comment out a few calls in tests (now marked with a `TODO`) to get this past the compiler. Will have to do additional cleanup to get this ready for review. --- script/AVSContractsDeploy.s.sol | 4 +- script/DeploySharedContracts.s.sol | 15 +-- src/BLSApkRegistry.sol | 29 +++-- src/BLSApkRegistryStorage.sol | 6 +- src/BLSPublicKeyCompendium.sol | 115 ------------------- src/interfaces/IBLSApkRegistry.sol | 2 +- src/interfaces/IBLSPublicKeyCompendium.sol | 49 -------- test/ffi/BLSPubKeyCompendiumFFI.t.sol | 20 ++-- test/mocks/BLSApkRegistryMock.sol | 127 +++++++++++++++++++++ test/mocks/BLSPublicKeyCompendiumMock.sol | 63 ---------- test/unit/BLSApkRegistryUnit.t.sol | 89 +++++++++++++-- test/unit/BLSPublicKeyCompendiumUnit.t.sol | 80 ------------- test/unit/RegistryCoordinatorUnit.t.sol | 6 +- test/utils/MockAVSDeployer.sol | 22 ++-- 14 files changed, 253 insertions(+), 374 deletions(-) delete mode 100644 src/BLSPublicKeyCompendium.sol delete mode 100644 src/interfaces/IBLSPublicKeyCompendium.sol create mode 100644 test/mocks/BLSApkRegistryMock.sol delete mode 100644 test/mocks/BLSPublicKeyCompendiumMock.sol delete mode 100644 test/unit/BLSPublicKeyCompendiumUnit.t.sol diff --git a/script/AVSContractsDeploy.s.sol b/script/AVSContractsDeploy.s.sol index 0abe9c8b..9b069b14 100644 --- a/script/AVSContractsDeploy.s.sol +++ b/script/AVSContractsDeploy.s.sol @@ -19,10 +19,12 @@ import "eigenlayer-contracts/src/contracts/pods/EigenPodManager.sol"; import "eigenlayer-contracts/src/contracts/pods/DelayedWithdrawalRouter.sol"; import "eigenlayer-contracts/src/contracts/permissions/PauserRegistry.sol"; +<<<<<<< HEAD import "../src/BLSPublicKeyCompendium.sol"; +======= +>>>>>>> 68f3817 (chore: delete BLSPublicKeyCompendium and associated interface) import "eigenlayer-contracts/src/test/mocks/EmptyContract.sol"; -import "eigenlayer-contracts/src/test/mocks/ETHDepositMock.sol"; import "eigenlayer-contracts/src/test/mocks/ERC20Mock.sol"; import "forge-std/Script.sol"; diff --git a/script/DeploySharedContracts.s.sol b/script/DeploySharedContracts.s.sol index d139f8fb..56535aec 100644 --- a/script/DeploySharedContracts.s.sol +++ b/script/DeploySharedContracts.s.sol @@ -1,7 +1,6 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity =0.8.12; -import "../src/BLSPublicKeyCompendium.sol"; import "../src/OperatorStateRetriever.sol"; import "forge-std/Script.sol"; @@ -15,26 +14,24 @@ import "forge-std/Test.sol"; contract DeploySharedContracts is Script, Test { Vm cheats = Vm(HEVM_ADDRESS); - BLSPublicKeyCompendium public blsPublicKeyCompendium; OperatorStateRetriever public blsOperatorStateRetriever; function run() external { vm.startBroadcast(); - blsPublicKeyCompendium = new BLSPublicKeyCompendium(); blsOperatorStateRetriever = new OperatorStateRetriever(); vm.stopBroadcast(); string memory deployed_addresses = "addresses"; - vm.serializeAddress( + // vm.serializeAddress( + // deployed_addresses, + // "blsOperatorStateRetriever", + // address(blsOperatorStateRetriever) + // ); + string memory finalJson = vm.serializeAddress( deployed_addresses, "blsOperatorStateRetriever", address(blsOperatorStateRetriever) ); - string memory finalJson = vm.serializeAddress( - deployed_addresses, - "blsPublicKeyCompendium", - address(blsPublicKeyCompendium) - ); vm.writeJson(finalJson, outputFileName()); } diff --git a/src/BLSApkRegistry.sol b/src/BLSApkRegistry.sol index b5e9ca23..99f48288 100644 --- a/src/BLSApkRegistry.sol +++ b/src/BLSApkRegistry.sol @@ -3,7 +3,6 @@ pragma solidity =0.8.12; import {BLSApkRegistryStorage} from "src/BLSApkRegistryStorage.sol"; -import {IBLSPublicKeyCompendium} from "src/interfaces/IBLSPublicKeyCompendium.sol"; import {IRegistryCoordinator} from "src/interfaces/IRegistryCoordinator.sol"; import {BN254} from "src/libraries/BN254.sol"; @@ -20,11 +19,10 @@ contract BLSApkRegistry is BLSApkRegistryStorage { _; } - /// @notice Sets the (immutable) `registryCoordinator` and `pubkeyCompendium` addresses + /// @notice Sets the (immutable) `registryCoordinator` address constructor( - IRegistryCoordinator _registryCoordinator, - IBLSPublicKeyCompendium _pubkeyCompendium - ) BLSApkRegistryStorage(_registryCoordinator, _pubkeyCompendium) {} + IRegistryCoordinator _registryCoordinator + ) BLSApkRegistryStorage(_registryCoordinator) {} /******************************************************************************* EXTERNAL FUNCTIONS - REGISTRY COORDINATOR @@ -46,8 +44,8 @@ contract BLSApkRegistry is BLSApkRegistryStorage { address operator, bytes memory quorumNumbers ) public virtual onlyRegistryCoordinator returns (bytes32) { - // Get the operator's pubkey from the compendium. Reverts if they have not registered a key - (BN254.G1Point memory pubkey, bytes32 pubkeyHash) = pubkeyCompendium.getRegisteredPubkey(operator); + // Get the operator's pubkey. Reverts if they have not registered a key + (BN254.G1Point memory pubkey, bytes32 pubkeyHash) = getRegisteredPubkey(operator); // Update each quorum's aggregate pubkey _processQuorumApkUpdate(quorumNumbers, pubkey); @@ -73,8 +71,8 @@ contract BLSApkRegistry is BLSApkRegistryStorage { address operator, bytes memory quorumNumbers ) public virtual onlyRegistryCoordinator { - // Get the operator's pubkey from the compendium. Reverts if they have not registered a key - (BN254.G1Point memory pubkey, ) = pubkeyCompendium.getRegisteredPubkey(operator); + // Get the operator's pubkey. Reverts if they have not registered a key + (BN254.G1Point memory pubkey, ) = getRegisteredPubkey(operator); // Update each quorum's aggregate pubkey _processQuorumApkUpdate(quorumNumbers, pubkey.negate()); @@ -108,15 +106,15 @@ contract BLSApkRegistry is BLSApkRegistryStorage { ) external { bytes32 pubkeyHash = BN254.hashG1Point(pubkeyG1); require( - pubkeyHash != ZERO_PK_HASH, "BLSPublicKeyCompendium.registerBLSPublicKey: cannot register zero pubkey" + pubkeyHash != ZERO_PK_HASH, "BLSApkRegistry.registerBLSPublicKey: cannot register zero pubkey" ); require( operatorToPubkeyHash[msg.sender] == bytes32(0), - "BLSPublicKeyCompendium.registerBLSPublicKey: operator already registered pubkey" + "BLSApkRegistry.registerBLSPublicKey: operator already registered pubkey" ); require( pubkeyHashToOperator[pubkeyHash] == address(0), - "BLSPublicKeyCompendium.registerBLSPublicKey: public key already registered" + "BLSApkRegistry.registerBLSPublicKey: public key already registered" ); // H(m) @@ -140,7 +138,7 @@ contract BLSApkRegistry is BLSApkRegistryStorage { BN254.negGeneratorG2(), messageHash.plus(BN254.generatorG1().scalar_mul(gamma)), pubkeyG2 - ), "BLSPublicKeyCompendium.registerBLSPublicKey: either the G1 signature is wrong, or G1 and G2 private key do not match"); + ), "BLSApkRegistry.registerBLSPublicKey: either the G1 signature is wrong, or G1 and G2 private key do not match"); operatorToPubkey[msg.sender] = pubkeyG1; operatorToPubkeyHash[msg.sender] = pubkeyHash; @@ -294,11 +292,10 @@ contract BLSApkRegistry is BLSApkRegistryStorage { /// @notice Returns the operator address for the given `pubkeyHash` function getOperatorFromPubkeyHash(bytes32 pubkeyHash) public view returns (address) { - return pubkeyCompendium.pubkeyHashToOperator(pubkeyHash); + return pubkeyHashToOperator[pubkeyHash]; } function getOperatorId(address operator) public view returns (bytes32) { - (, bytes32 pubkeyHash) = pubkeyCompendium.getRegisteredPubkey(operator); - return pubkeyHash; + return operatorToPubkeyHash[operator]; } } diff --git a/src/BLSApkRegistryStorage.sol b/src/BLSApkRegistryStorage.sol index 3f56cd42..4676a6f5 100644 --- a/src/BLSApkRegistryStorage.sol +++ b/src/BLSApkRegistryStorage.sol @@ -3,7 +3,6 @@ pragma solidity =0.8.12; import {IBLSApkRegistry} from "src/interfaces/IBLSApkRegistry.sol"; import {IRegistryCoordinator} from "src/interfaces/IRegistryCoordinator.sol"; -import {IBLSPublicKeyCompendium} from "src/interfaces/IBLSPublicKeyCompendium.sol"; import {Initializable} from "@openzeppelin-upgrades/contracts/proxy/utils/Initializable.sol"; @@ -15,8 +14,6 @@ abstract contract BLSApkRegistryStorage is Initializable, IBLSApkRegistry { /// @notice the registry coordinator contract IRegistryCoordinator public immutable registryCoordinator; - /// @notice the BLSPublicKeyCompendium contract against which pubkey ownership is checked - IBLSPublicKeyCompendium public immutable pubkeyCompendium; // storage for individual pubkeys /// @notice maps operator address to pubkey hash @@ -32,9 +29,8 @@ abstract contract BLSApkRegistryStorage is Initializable, IBLSApkRegistry { /// @notice maps quorumNumber => current aggregate pubkey of quorum mapping(uint8 => BN254.G1Point) public currentApk; - constructor(IRegistryCoordinator _registryCoordinator, IBLSPublicKeyCompendium _pubkeyCompendium) { + constructor(IRegistryCoordinator _registryCoordinator) { registryCoordinator = _registryCoordinator; - pubkeyCompendium = _pubkeyCompendium; // disable initializers so that the implementation contract cannot be initialized _disableInitializers(); } diff --git a/src/BLSPublicKeyCompendium.sol b/src/BLSPublicKeyCompendium.sol deleted file mode 100644 index f9aad7f1..00000000 --- a/src/BLSPublicKeyCompendium.sol +++ /dev/null @@ -1,115 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity =0.8.12; - -import {IBLSPublicKeyCompendium} from "src/interfaces/IBLSPublicKeyCompendium.sol"; -import {BN254} from "src/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 the hash of the zero pubkey aka BN254.G1Point(0,0) - bytes32 internal constant ZERO_PK_HASH = hex"ad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5"; - - /// @notice maps operator address to pubkey hash - mapping(address => bytes32) public operatorToPubkeyHash; - /// @notice maps pubkey hash to operator address - mapping(bytes32 => address) public pubkeyHashToOperator; - /// @notice maps operator address to pubkeyG1 - mapping(address => BN254.G1Point) public operatorToPubkey; - - /******************************************************************************* - EXTERNAL FUNCTIONS - *******************************************************************************/ - - /** - * @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( - pubkeyHash != ZERO_PK_HASH, "BLSPublicKeyCompendium.registerBLSPublicKey: cannot register zero pubkey" - ); - 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: either the G1 signature is wrong, or G1 and G2 private key do not match"); - - operatorToPubkey[msg.sender] = pubkeyG1; - operatorToPubkeyHash[msg.sender] = pubkeyHash; - pubkeyHashToOperator[pubkeyHash] = msg.sender; - - emit NewPubkeyRegistration(msg.sender, pubkeyG1, pubkeyG2); - } - - /******************************************************************************* - VIEW FUNCTIONS - *******************************************************************************/ - - /** - * @notice Returns the pubkey and pubkey hash of an operator - * @dev Reverts if the operator has not registered a valid pubkey - */ - function getRegisteredPubkey(address operator) public view returns (BN254.G1Point memory, bytes32) { - BN254.G1Point memory pubkey = operatorToPubkey[operator]; - bytes32 pubkeyHash = operatorToPubkeyHash[operator]; - - require( - pubkeyHash != bytes32(0), - "BLSPublicKeyCompendium.getRegisteredPubkey: operator is not registered" - ); - - return (pubkey, pubkeyHash); - } - - /** - * @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/interfaces/IBLSApkRegistry.sol b/src/interfaces/IBLSApkRegistry.sol index 0569bc9d..89550777 100644 --- a/src/interfaces/IBLSApkRegistry.sol +++ b/src/interfaces/IBLSApkRegistry.sol @@ -35,7 +35,7 @@ interface IBLSApkRegistry is IRegistry { // 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. diff --git a/src/interfaces/IBLSPublicKeyCompendium.sol b/src/interfaces/IBLSPublicKeyCompendium.sol deleted file mode 100644 index 10342213..00000000 --- a/src/interfaces/IBLSPublicKeyCompendium.sol +++ /dev/null @@ -1,49 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity >=0.5.0; - -import {BN254} from "../libraries/BN254.sol"; - -/** - * @title Minimal interface for the `BLSPublicKeyCompendium` contract. - * @author Layr Labs, Inc. - * @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service - */ -interface IBLSPublicKeyCompendium { - - // EVENTS - /// @notice Emitted when `operator` registers with the public keys `pubkeyG1` and `pubkeyG2`. - event NewPubkeyRegistration(address indexed operator, BN254.G1Point pubkeyG1, BN254.G2Point pubkeyG2); - - /** - * @notice mapping from operator address to pubkey hash. - * Returns *zero* if the `operator` has never registered, and otherwise returns the hash of the public key of the operator. - */ - function operatorToPubkeyHash(address operator) external view returns (bytes32); - - /** - * @notice mapping from pubkey hash to operator address. - * Returns *zero* if no operator has ever registered the public key corresponding to `pubkeyHash`, - * and otherwise returns the (unique) registered operator who owns the BLS public key that is the preimage of `pubkeyHash`. - */ - function pubkeyHashToOperator(bytes32 pubkeyHash) external view returns (address); - - /** - * @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; - - /** - * @notice Returns the pubkey and pubkey hash of an operator - * @dev Reverts if the operator has not registered a valid pubkey - */ - function getRegisteredPubkey(address operator) external view returns (BN254.G1Point memory, bytes32); - - /** - * @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) external view returns (BN254.G1Point memory); -} diff --git a/test/ffi/BLSPubKeyCompendiumFFI.t.sol b/test/ffi/BLSPubKeyCompendiumFFI.t.sol index 0fd9b4d4..0178cec1 100644 --- a/test/ffi/BLSPubKeyCompendiumFFI.t.sol +++ b/test/ffi/BLSPubKeyCompendiumFFI.t.sol @@ -1,16 +1,21 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity =0.8.12; +<<<<<<< HEAD import "../../src/BLSPublicKeyCompendium.sol"; import "./util/G2Operations.sol"; +======= +import "src/BLSApkRegistry.sol"; +import "test/ffi/util/G2Operations.sol"; +>>>>>>> 68f3817 (chore: delete BLSPublicKeyCompendium and associated interface) -contract BLSPublicKeyCompendiumFFITests is G2Operations { +contract BLSApkRegistryFFITests is G2Operations { using BN254 for BN254.G1Point; using Strings for uint256; Vm cheats = Vm(HEVM_ADDRESS); - BLSPublicKeyCompendium compendium; + BLSApkRegistry blsApkRegistry; uint256 privKey; BN254.G1Point pubKeyG1; @@ -20,7 +25,8 @@ contract BLSPublicKeyCompendiumFFITests is G2Operations { address alice = address(0x69); function setUp() public { - compendium = new BLSPublicKeyCompendium(); + IRegistryCoordinator registryCoordinator; + blsApkRegistry = new BLSApkRegistry(registryCoordinator); } function testRegisterBLSPublicKey(uint256 _privKey) public { @@ -30,10 +36,10 @@ contract BLSPublicKeyCompendiumFFITests is G2Operations { signedMessageHash = _signMessage(alice); vm.prank(alice); - compendium.registerBLSPublicKey(signedMessageHash, pubKeyG1, pubKeyG2); + blsApkRegistry.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"); + assertEq(blsApkRegistry.operatorToPubkeyHash(alice), BN254.hashG1Point(pubKeyG1), "pubkey hash not stored correctly"); + assertEq(blsApkRegistry.pubkeyHashToOperator(BN254.hashG1Point(pubKeyG1)), alice, "operator address not stored correctly"); } function _setKeys(uint256 _privKey) internal { @@ -43,7 +49,7 @@ contract BLSPublicKeyCompendiumFFITests is G2Operations { } function _signMessage(address signer) internal view returns(BN254.G1Point memory) { - BN254.G1Point memory messageHash = compendium.getMessageHash(signer); + BN254.G1Point memory messageHash = blsApkRegistry.getMessageHash(signer); return BN254.scalar_mul(messageHash, privKey); } } diff --git a/test/mocks/BLSApkRegistryMock.sol b/test/mocks/BLSApkRegistryMock.sol new file mode 100644 index 00000000..61cb3d95 --- /dev/null +++ b/test/mocks/BLSApkRegistryMock.sol @@ -0,0 +1,127 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity =0.8.12; + +import {IRegistryCoordinator} from "src/interfaces/IRegistryCoordinator.sol"; +import {IBLSApkRegistry} from "src/interfaces/IBLSApkRegistry.sol"; +import {BN254} from "src/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 BLSApkRegistryMock is IBLSApkRegistry { + + /// @notice the hash of the zero pubkey aka BN254.G1Point(0,0) + bytes32 internal constant ZERO_PK_HASH = hex"ad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5"; + + /// @notice maps operator address to pubkey hash + mapping(address => bytes32) public operatorToPubkeyHash; + /// @notice maps pubkey hash to operator address + mapping(bytes32 => address) public pubkeyHashToOperator; + /// @notice maps operator address to pubkeyG1 + mapping(address => BN254.G1Point) public operatorToPubkey; + + function registryCoordinator() external view returns (IRegistryCoordinator) {} + + /** + * @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. + * @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) 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. + * @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(address operator, bytes calldata quorumNumbers) external {} + + /** + * @notice Initializes a new quorum by pushing its first apk update + * @param quorumNumber The number of the new quorum + */ + function initializeQuorum(uint8 quorumNumber) external {} + + /** + * @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 {} + + /** + * @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) external view returns (BN254.G1Point memory) {} + + /// @notice Returns the current APK for the provided `quorumNumber ` + function getApk(uint8 quorumNumber) external view returns (BN254.G1Point memory) {} + + /// @notice Returns the index of the quorumApk index at `blockNumber` for the provided `quorumNumber` + function getApkIndicesAtBlockNumber(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 getApkUpdateAtIndex(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 getApkHashAtBlockNumberAndIndex(uint8 quorumNumber, uint32 blockNumber, uint256 index) external view returns (bytes24) {} + + function getOperatorId(address operator) external view returns (bytes32) {} + + function registerPublicKey(BN254.G1Point memory pk) external { + + bytes32 pubkeyHash = BN254.hashG1Point(pk); + // store updates + operatorToPubkeyHash[msg.sender] = pubkeyHash; + pubkeyHashToOperator[pubkeyHash] = msg.sender; + operatorToPubkey[msg.sender] = pk; + } + + function setBLSPublicKey(address account, BN254.G1Point memory pk) external { + + bytes32 pubkeyHash = BN254.hashG1Point(pk); + // store updates + operatorToPubkeyHash[account] = pubkeyHash; + pubkeyHashToOperator[pubkeyHash] = account; + operatorToPubkey[account] = pk; + } + + function getRegisteredPubkey(address operator) public view returns (BN254.G1Point memory, bytes32) { + BN254.G1Point memory pubkey = operatorToPubkey[operator]; + bytes32 pubkeyHash = operatorToPubkeyHash[operator]; + + require( + pubkeyHash != bytes32(0), + "BLSPublicKeyCompendium.getRegisteredPubkey: operator is not registered" + ); + + return (pubkey, pubkeyHash); + } + +} diff --git a/test/mocks/BLSPublicKeyCompendiumMock.sol b/test/mocks/BLSPublicKeyCompendiumMock.sol deleted file mode 100644 index 9c5ba938..00000000 --- a/test/mocks/BLSPublicKeyCompendiumMock.sol +++ /dev/null @@ -1,63 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity =0.8.12; - -import "../../src/interfaces/IBLSPublicKeyCompendium.sol"; -import "../../src/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 BLSPublicKeyCompendiumMock is IBLSPublicKeyCompendium{ - - /// @notice the hash of the zero pubkey aka BN254.G1Point(0,0) - bytes32 internal constant ZERO_PK_HASH = hex"ad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5"; - - /// @notice maps operator address to pubkey hash - mapping(address => bytes32) public operatorToPubkeyHash; - /// @notice maps pubkey hash to operator address - mapping(bytes32 => address) public pubkeyHashToOperator; - /// @notice maps operator address to pubkeyG1 - mapping(address => BN254.G1Point) public operatorToPubkey; - - /** - * @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; - operatorToPubkey[msg.sender] = pk; - } - - function setBLSPublicKey(address account, BN254.G1Point memory pk) external { - - bytes32 pubkeyHash = BN254.hashG1Point(pk); - // store updates - operatorToPubkeyHash[account] = pubkeyHash; - pubkeyHashToOperator[pubkeyHash] = account; - operatorToPubkey[account] = pk; - } - - function getRegisteredPubkey(address operator) public view returns (BN254.G1Point memory, bytes32) { - BN254.G1Point memory pubkey = operatorToPubkey[operator]; - bytes32 pubkeyHash = operatorToPubkeyHash[operator]; - - require( - pubkeyHash != bytes32(0), - "BLSPublicKeyCompendium.getRegisteredPubkey: operator is not registered" - ); - - return (pubkey, pubkeyHash); - } - - function getMessageHash(address operator) external view returns (BN254.G1Point memory) {} -} diff --git a/test/unit/BLSApkRegistryUnit.t.sol b/test/unit/BLSApkRegistryUnit.t.sol index fe82c373..2e26086e 100644 --- a/test/unit/BLSApkRegistryUnit.t.sol +++ b/test/unit/BLSApkRegistryUnit.t.sol @@ -4,8 +4,6 @@ pragma solidity =0.8.12; import "forge-std/Test.sol"; import "src/BLSApkRegistry.sol"; -import "src/interfaces/IRegistryCoordinator.sol"; -import "test/mocks/BLSPublicKeyCompendiumMock.sol"; import "test/mocks/RegistryCoordinatorMock.sol"; @@ -19,11 +17,19 @@ contract BLSApkRegistryUnitTests is Test { bytes32 internal constant ZERO_PK_HASH = hex"ad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5"; BLSApkRegistry public blsApkRegistry; - BLSPublicKeyCompendiumMock public pkCompendium; RegistryCoordinatorMock public registryCoordinator; BN254.G1Point internal defaultPubKey = BN254.G1Point(18260007818883133054078754218619977578772505796600400998181738095793040006897,3432351341799135763167709827653955074218841517684851694584291831827675065899); + BN254.G1Point pubKeyG1; + BN254.G2Point pubKeyG2; + BN254.G1Point signedMessageHash; + + address alice = address(1); + address bob = address(2); + + uint256 privKey = 9001; + uint8 internal defaultQuorumNumber = 0; // Track initialized quorums so we can filter these out when fuzzing @@ -31,8 +37,16 @@ contract BLSApkRegistryUnitTests is Test { function setUp() external { registryCoordinator = new RegistryCoordinatorMock(); - pkCompendium = new BLSPublicKeyCompendiumMock(); - blsApkRegistry = new BLSApkRegistry(registryCoordinator, pkCompendium); + blsApkRegistry = new BLSApkRegistry(registryCoordinator); + + pubKeyG1 = BN254.generatorG1().scalar_mul(privKey); + + //privKey*G2 + pubKeyG2.X[1] = 19101821850089705274637533855249918363070101489527618151493230256975900223847; + pubKeyG2.X[0] = 5334410886741819556325359147377682006012228123419628681352847439302316235957; + pubKeyG2.Y[1] = 354176189041917478648604979334478067325821134838555150300539079146482658331; + pubKeyG2.Y[0] = 4185483097059047421902184823581361466320657066600218863748375739772335928910; + // Initialize a quorum _initializeQuorum(defaultQuorumNumber); @@ -40,7 +54,6 @@ contract BLSApkRegistryUnitTests is Test { function testConstructorArgs() public view { require(blsApkRegistry.registryCoordinator() == registryCoordinator, "registryCoordinator not set correctly"); - require(blsApkRegistry.pubkeyCompendium() == pkCompendium, "pubkeyCompendium not set correctly"); } function testCallRegisterOperatorFromNonCoordinatorAddress(address nonCoordinatorAddress) public { @@ -63,7 +76,7 @@ contract BLSApkRegistryUnitTests is Test { function testOperatorDoesNotOwnPubKeyRegister() public { cheats.startPrank(address(registryCoordinator)); - cheats.expectRevert(bytes("BLSPublicKeyCompendium.getRegisteredPubkey: operator is not registered")); + cheats.expectRevert(bytes("BLSApkRegistry.getRegisteredPubkey: operator is not registered")); blsApkRegistry.registerOperator(defaultOperator, new bytes(1)); cheats.stopPrank(); } @@ -74,7 +87,8 @@ contract BLSApkRegistryUnitTests is Test { bytes32 pkHash = BN254.hashG1Point(pubkey); cheats.startPrank(operator); - pkCompendium.registerPublicKey(pubkey); + // TODO: fix this. it was using a mock contract but is now internal to this contract + // blsApkRegistry.registerBLSPublicKey(pubkey); cheats.stopPrank(); //register for one quorum @@ -111,7 +125,8 @@ contract BLSApkRegistryUnitTests is Test { } cheats.prank(defaultOperator); - pkCompendium.registerPublicKey(defaultPubKey); + // TODO: fix this. it was using a mock contract but is now internal to this contract + // blsApkRegistry.registerBLSPublicKey(defaultPubKey); cheats.prank(address(registryCoordinator)); blsApkRegistry.registerOperator(defaultOperator, quorumNumbers); @@ -136,7 +151,8 @@ contract BLSApkRegistryUnitTests is Test { quorumNumbers[0] = bytes1(defaultQuorumNumber); cheats.startPrank(operator); - pkCompendium.registerPublicKey(negatedQuorumApk); + // TODO: fix this. it was using a mock contract but is now internal to this contract + // blsApkRegistry.registerBLSPublicKey(negatedQuorumApk); cheats.stopPrank(); cheats.startPrank(address(registryCoordinator)); @@ -183,7 +199,8 @@ contract BLSApkRegistryUnitTests is Test { quorumNumbers[0] = bytes1(defaultQuorumNumber); cheats.startPrank(defaultOperator); - pkCompendium.registerPublicKey(quorumApksBefore); + // TODO: fix this. it was using a mock contract but is now internal to this contract + // blsApkRegistry.registerBLSPublicKey(quorumApksBefore); cheats.stopPrank(); cheats.prank(address(registryCoordinator)); @@ -277,4 +294,54 @@ contract BLSApkRegistryUnitTests is Test { cheats.stopPrank(); } + + // TODO: better organize / integrate tests migrated from `BLSPublicKeyCompendium` unit tests + function testRegisterBLSPublicKey() public { + signedMessageHash = _signMessage(alice); + vm.prank(alice); + blsApkRegistry.registerBLSPublicKey(signedMessageHash, pubKeyG1, pubKeyG2); + + assertEq(blsApkRegistry.operatorToPubkeyHash(alice), BN254.hashG1Point(pubKeyG1), "pubkey hash not stored correctly"); + assertEq(blsApkRegistry.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("BLSApkRegistry.registerBLSPublicKey: either the G1 signature is wrong, or G1 and G2 private key do not match")); + blsApkRegistry.registerBLSPublicKey(signedMessageHash, badPubKeyG1, pubKeyG2); + } + + function testRegisterBLSPublicKey_BadSig_Reverts() public { + signedMessageHash = _signMessage(bob); // sign with wrong private key + + vm.prank(alice); + vm.expectRevert(bytes("BLSApkRegistry.registerBLSPublicKey: either the G1 signature is wrong, or G1 and G2 private key do not match")); + blsApkRegistry.registerBLSPublicKey(signedMessageHash, pubKeyG1, pubKeyG2); + } + + function testRegisterBLSPublicKey_OpRegistered_Reverts() public { + testRegisterBLSPublicKey(); // register alice + + vm.prank(alice); + vm.expectRevert(bytes("BLSApkRegistry.registerBLSPublicKey: operator already registered pubkey")); + blsApkRegistry.registerBLSPublicKey(signedMessageHash, pubKeyG1, pubKeyG2); + } + + function testRegisterBLSPublicKey_PkRegistered_Reverts() public { + testRegisterBLSPublicKey(); + signedMessageHash = _signMessage(bob); // same private key different operator + + vm.prank(bob); + vm.expectRevert(bytes("BLSApkRegistry.registerBLSPublicKey: public key already registered")); + blsApkRegistry.registerBLSPublicKey(signedMessageHash, pubKeyG1, pubKeyG2); + } + + function _signMessage(address signer) internal view returns(BN254.G1Point memory) { + BN254.G1Point memory messageHash = blsApkRegistry.getMessageHash(signer); + return BN254.scalar_mul(messageHash, privKey); + } + } diff --git a/test/unit/BLSPublicKeyCompendiumUnit.t.sol b/test/unit/BLSPublicKeyCompendiumUnit.t.sol deleted file mode 100644 index a9181132..00000000 --- a/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 "../../src/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: either the G1 signature is wrong, or 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: either the G1 signature is wrong, or 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); - } - -} diff --git a/test/unit/RegistryCoordinatorUnit.t.sol b/test/unit/RegistryCoordinatorUnit.t.sol index 0ea0e15a..abaa5117 100644 --- a/test/unit/RegistryCoordinatorUnit.t.sol +++ b/test/unit/RegistryCoordinatorUnit.t.sol @@ -319,7 +319,7 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { address operatorToRegister = _incrementAddress(defaultOperator, numOperators); BN254.G1Point memory operatorToRegisterPubKey = BN254.hashToG1(keccak256(abi.encodePacked(pseudoRandomNumber, numOperators))); - pubkeyCompendium.setBLSPublicKey(operatorToRegister, operatorToRegisterPubKey); + blsApkRegistry.setBLSPublicKey(operatorToRegister, operatorToRegisterPubKey); stakeRegistry.setOperatorWeight(defaultQuorumNumber, operatorToRegister, defaultStake); @@ -654,7 +654,7 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { }); } - pubkeyCompendium.setBLSPublicKey(operatorToRegister, operatorToRegisterPubKey); + blsApkRegistry.setBLSPublicKey(operatorToRegister, operatorToRegisterPubKey); uint96 registeringStake = defaultKickBIPsOfOperatorStake * defaultStake; stakeRegistry.setOperatorWeight(defaultQuorumNumber, operatorToRegister, registeringStake); @@ -946,6 +946,6 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { }); } - pubkeyCompendium.setBLSPublicKey(operatorToRegister, operatorToRegisterPubKey); + blsApkRegistry.setBLSPublicKey(operatorToRegister, operatorToRegisterPubKey); } } diff --git a/test/utils/MockAVSDeployer.sol b/test/utils/MockAVSDeployer.sol index d8396966..1d975727 100644 --- a/test/utils/MockAVSDeployer.sol +++ b/test/utils/MockAVSDeployer.sol @@ -7,13 +7,11 @@ import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.so import {Slasher} from "eigenlayer-contracts/src/contracts/core/Slasher.sol"; import {ISlasher} from "eigenlayer-contracts/src/contracts/interfaces/ISlasher.sol"; import {PauserRegistry} from "eigenlayer-contracts/src/contracts/permissions/PauserRegistry.sol"; -import {IStrategyManager} from "eigenlayer-contracts/src/contracts/interfaces/IStrategyManager.sol"; import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol"; import {BitmapUtils} from "src/libraries/BitmapUtils.sol"; import {BN254} from "src/libraries/BN254.sol"; -import {BLSPublicKeyCompendium} from "src/BLSPublicKeyCompendium.sol"; import {OperatorStateRetriever} from "src/OperatorStateRetriever.sol"; import {RegistryCoordinator} from "src/RegistryCoordinator.sol"; import {RegistryCoordinatorHarness} from "test/harnesses/RegistryCoordinatorHarness.sol"; @@ -30,10 +28,8 @@ import {IRegistryCoordinator} from "src/interfaces/IRegistryCoordinator.sol"; import {StrategyManagerMock} from "eigenlayer-contracts/src/test/mocks/StrategyManagerMock.sol"; import {EigenPodManagerMock} from "eigenlayer-contracts/src/test/mocks/EigenPodManagerMock.sol"; import {ServiceManagerMock} from "test/mocks/ServiceManagerMock.sol"; -import {OwnableMock} from "eigenlayer-contracts/src/test/mocks/OwnableMock.sol"; import {DelegationManagerMock} from "eigenlayer-contracts/src/test/mocks/DelegationManagerMock.sol"; -import {SlasherMock} from "eigenlayer-contracts/src/test/mocks/SlasherMock.sol"; -import {BLSPublicKeyCompendiumMock} from "test/mocks/BLSPublicKeyCompendiumMock.sol"; +import {BLSApkRegistryMock} from "test/mocks/BLSApkRegistryMock.sol"; import {EmptyContract} from "eigenlayer-contracts/src/test/mocks/EmptyContract.sol"; import {StakeRegistryHarness} from "test/harnesses/StakeRegistryHarness.sol"; @@ -52,7 +48,6 @@ contract MockAVSDeployer is Test { Slasher public slasherImplementation; EmptyContract public emptyContract; - BLSPublicKeyCompendiumMock public pubkeyCompendium; RegistryCoordinatorHarness public registryCoordinatorImplementation; StakeRegistryHarness public stakeRegistryImplementation; @@ -62,7 +57,7 @@ contract MockAVSDeployer is Test { OperatorStateRetriever public operatorStateRetriever; RegistryCoordinatorHarness public registryCoordinator; StakeRegistryHarness public stakeRegistry; - IBLSApkRegistry public blsApkRegistry; + BLSApkRegistryMock public blsApkRegistry; IIndexRegistry public indexRegistry; ServiceManagerMock public serviceManagerMock; @@ -146,8 +141,8 @@ contract MockAVSDeployer is Test { slasher ); - pubkeyCompendium = new BLSPublicKeyCompendiumMock(); - pubkeyCompendium.setBLSPublicKey(defaultOperator, defaultPubKey); + blsApkRegistry = new BLSApkRegistryMock(); + blsApkRegistry.setBLSPublicKey(defaultOperator, defaultPubKey); cheats.stopPrank(); @@ -182,7 +177,7 @@ contract MockAVSDeployer is Test { ) ); - blsApkRegistry = BLSApkRegistry( + blsApkRegistry = BLSApkRegistryMock( address( new TransparentUpgradeableProxy( address(emptyContract), @@ -207,8 +202,7 @@ contract MockAVSDeployer is Test { ); blsApkRegistryImplementation = new BLSApkRegistry( - registryCoordinator, - BLSPublicKeyCompendium(address(pubkeyCompendium)) + registryCoordinator ); proxyAdmin.upgrade( @@ -295,7 +289,7 @@ contract MockAVSDeployer is Test { // quorumBitmap can only have 192 least significant bits quorumBitmap &= MAX_QUORUM_BITMAP; - pubkeyCompendium.setBLSPublicKey(operator, pubKey); + blsApkRegistry.setBLSPublicKey(operator, pubKey); bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); for (uint i = 0; i < quorumNumbers.length; i++) { @@ -313,7 +307,7 @@ contract MockAVSDeployer is Test { // quorumBitmap can only have 192 least significant bits quorumBitmap &= MAX_QUORUM_BITMAP; - pubkeyCompendium.setBLSPublicKey(operator, pubKey); + blsApkRegistry.setBLSPublicKey(operator, pubKey); bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); for (uint i = 0; i < quorumNumbers.length; i++) { From df3f14a187775e62ae56943af2d2092005f90b95 Mon Sep 17 00:00:00 2001 From: ChaoticWalrus <93558947+ChaoticWalrus@users.noreply.github.com> Date: Mon, 11 Dec 2023 13:12:44 -0800 Subject: [PATCH 057/101] feat: create harness for BLSApkRegistry and use it in tests These changes appear to fix all existing tests. I've done what I can to at least comment specifically where harnessed functions are used, as this pattern could give some false assurance and I'd like to avoid that. --- test/harnesses/BLSApkRegistryHarness.sol | 21 ++++++++++++++++++ test/mocks/BLSApkRegistryMock.sol | 11 +++++++++- test/unit/BLSApkRegistryUnit.t.sol | 28 ++++++++++-------------- test/utils/MockAVSDeployer.sol | 16 +++++++------- 4 files changed, 51 insertions(+), 25 deletions(-) create mode 100644 test/harnesses/BLSApkRegistryHarness.sol diff --git a/test/harnesses/BLSApkRegistryHarness.sol b/test/harnesses/BLSApkRegistryHarness.sol new file mode 100644 index 00000000..a888986e --- /dev/null +++ b/test/harnesses/BLSApkRegistryHarness.sol @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity =0.8.12; + +import "src/BLSApkRegistry.sol"; + +// wrapper around the BLSApkRegistry contract that exposes internal functionality, for unit testing _other functionality_. +contract BLSApkRegistryHarness is BLSApkRegistry { + + constructor( + IRegistryCoordinator _registryCoordinator + ) BLSApkRegistry(_registryCoordinator) {} + + function setBLSPublicKey(address account, BN254.G1Point memory pk) external { + + bytes32 pubkeyHash = BN254.hashG1Point(pk); + // store updates + operatorToPubkeyHash[account] = pubkeyHash; + pubkeyHashToOperator[pubkeyHash] = account; + operatorToPubkey[account] = pk; + } +} diff --git a/test/mocks/BLSApkRegistryMock.sol b/test/mocks/BLSApkRegistryMock.sol index 61cb3d95..1f050f4c 100644 --- a/test/mocks/BLSApkRegistryMock.sol +++ b/test/mocks/BLSApkRegistryMock.sol @@ -90,7 +90,16 @@ contract BLSApkRegistryMock is IBLSApkRegistry { * @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 getApkHashAtBlockNumberAndIndex(uint8 quorumNumber, uint32 blockNumber, uint256 index) external view returns (bytes24) {} + function getApkHashAtBlockNumberAndIndex( + uint8 quorumNumber, + uint32 blockNumber, + uint256 index + ) external view returns (bytes24) { + // ApkUpdate memory quorumApkUpdate = apkHistory[quorumNumber][index]; + // _validateApkHashAtBlockNumber(quorumApkUpdate, blockNumber); + ApkUpdate memory quorumApkUpdate; + return quorumApkUpdate.apkHash; + } function getOperatorId(address operator) external view returns (bytes32) {} diff --git a/test/unit/BLSApkRegistryUnit.t.sol b/test/unit/BLSApkRegistryUnit.t.sol index 2e26086e..c30e1bdd 100644 --- a/test/unit/BLSApkRegistryUnit.t.sol +++ b/test/unit/BLSApkRegistryUnit.t.sol @@ -3,7 +3,7 @@ pragma solidity =0.8.12; import "forge-std/Test.sol"; -import "src/BLSApkRegistry.sol"; +import "test/harnesses/BLSApkRegistryHarness.sol"; import "test/mocks/RegistryCoordinatorMock.sol"; @@ -16,7 +16,7 @@ contract BLSApkRegistryUnitTests is Test { bytes32 internal constant ZERO_PK_HASH = hex"ad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5"; - BLSApkRegistry public blsApkRegistry; + BLSApkRegistryHarness public blsApkRegistry; RegistryCoordinatorMock public registryCoordinator; BN254.G1Point internal defaultPubKey = BN254.G1Point(18260007818883133054078754218619977578772505796600400998181738095793040006897,3432351341799135763167709827653955074218841517684851694584291831827675065899); @@ -28,7 +28,7 @@ contract BLSApkRegistryUnitTests is Test { address alice = address(1); address bob = address(2); - uint256 privKey = 9001; + uint256 privKey = 69; uint8 internal defaultQuorumNumber = 0; @@ -37,7 +37,7 @@ contract BLSApkRegistryUnitTests is Test { function setUp() external { registryCoordinator = new RegistryCoordinatorMock(); - blsApkRegistry = new BLSApkRegistry(registryCoordinator); + blsApkRegistry = new BLSApkRegistryHarness(registryCoordinator); pubKeyG1 = BN254.generatorG1().scalar_mul(privKey); @@ -86,9 +86,8 @@ contract BLSApkRegistryUnitTests is Test { BN254.G1Point memory pubkey = BN254.hashToG1(x); bytes32 pkHash = BN254.hashG1Point(pubkey); - cheats.startPrank(operator); - // TODO: fix this. it was using a mock contract but is now internal to this contract - // blsApkRegistry.registerBLSPublicKey(pubkey); + // use harnessed function to directly set the pubkey, bypassing the ordinary checks + blsApkRegistry.setBLSPublicKey(operator, pubkey); cheats.stopPrank(); //register for one quorum @@ -124,9 +123,8 @@ contract BLSApkRegistryUnitTests is Test { quorumApksBefore[i] = blsApkRegistry.getApk(uint8(quorumNumbers[i])); } - cheats.prank(defaultOperator); - // TODO: fix this. it was using a mock contract but is now internal to this contract - // blsApkRegistry.registerBLSPublicKey(defaultPubKey); + // use harnessed function to directly set the pubkey, bypassing the ordinary checks + blsApkRegistry.setBLSPublicKey(defaultOperator, defaultPubKey); cheats.prank(address(registryCoordinator)); blsApkRegistry.registerOperator(defaultOperator, quorumNumbers); @@ -150,9 +148,8 @@ contract BLSApkRegistryUnitTests is Test { bytes memory quorumNumbers = new bytes(1); quorumNumbers[0] = bytes1(defaultQuorumNumber); - cheats.startPrank(operator); - // TODO: fix this. it was using a mock contract but is now internal to this contract - // blsApkRegistry.registerBLSPublicKey(negatedQuorumApk); + // use harnessed function to directly set the pubkey, bypassing the ordinary checks + blsApkRegistry.setBLSPublicKey(operator, negatedQuorumApk); cheats.stopPrank(); cheats.startPrank(address(registryCoordinator)); @@ -198,9 +195,8 @@ contract BLSApkRegistryUnitTests is Test { bytes memory quorumNumbers = new bytes(1); quorumNumbers[0] = bytes1(defaultQuorumNumber); - cheats.startPrank(defaultOperator); - // TODO: fix this. it was using a mock contract but is now internal to this contract - // blsApkRegistry.registerBLSPublicKey(quorumApksBefore); + // use harnessed function to directly set the pubkey, bypassing the ordinary checks + blsApkRegistry.setBLSPublicKey(defaultOperator, quorumApksBefore); cheats.stopPrank(); cheats.prank(address(registryCoordinator)); diff --git a/test/utils/MockAVSDeployer.sol b/test/utils/MockAVSDeployer.sol index 1d975727..c40de9a9 100644 --- a/test/utils/MockAVSDeployer.sol +++ b/test/utils/MockAVSDeployer.sol @@ -29,7 +29,7 @@ import {StrategyManagerMock} from "eigenlayer-contracts/src/test/mocks/StrategyM import {EigenPodManagerMock} from "eigenlayer-contracts/src/test/mocks/EigenPodManagerMock.sol"; import {ServiceManagerMock} from "test/mocks/ServiceManagerMock.sol"; import {DelegationManagerMock} from "eigenlayer-contracts/src/test/mocks/DelegationManagerMock.sol"; -import {BLSApkRegistryMock} from "test/mocks/BLSApkRegistryMock.sol"; +import {BLSApkRegistryHarness} from "test/harnesses/BLSApkRegistryHarness.sol"; import {EmptyContract} from "eigenlayer-contracts/src/test/mocks/EmptyContract.sol"; import {StakeRegistryHarness} from "test/harnesses/StakeRegistryHarness.sol"; @@ -57,7 +57,7 @@ contract MockAVSDeployer is Test { OperatorStateRetriever public operatorStateRetriever; RegistryCoordinatorHarness public registryCoordinator; StakeRegistryHarness public stakeRegistry; - BLSApkRegistryMock public blsApkRegistry; + BLSApkRegistryHarness public blsApkRegistry; IIndexRegistry public indexRegistry; ServiceManagerMock public serviceManagerMock; @@ -140,10 +140,6 @@ contract MockAVSDeployer is Test { eigenPodManagerMock, slasher ); - - blsApkRegistry = new BLSApkRegistryMock(); - blsApkRegistry.setBLSPublicKey(defaultOperator, defaultPubKey); - cheats.stopPrank(); cheats.startPrank(serviceManagerOwner); @@ -177,7 +173,7 @@ contract MockAVSDeployer is Test { ) ); - blsApkRegistry = BLSApkRegistryMock( + blsApkRegistry = BLSApkRegistryHarness( address( new TransparentUpgradeableProxy( address(emptyContract), @@ -188,6 +184,7 @@ contract MockAVSDeployer is Test { ); cheats.stopPrank(); + cheats.startPrank(proxyAdminOwner); stakeRegistryImplementation = new StakeRegistryHarness( @@ -201,7 +198,7 @@ contract MockAVSDeployer is Test { address(stakeRegistryImplementation) ); - blsApkRegistryImplementation = new BLSApkRegistry( + blsApkRegistryImplementation = new BLSApkRegistryHarness( registryCoordinator ); @@ -219,6 +216,9 @@ contract MockAVSDeployer is Test { address(indexRegistryImplementation) ); + // set the public key for an operator, using harnessed function to bypass checks + blsApkRegistry.setBLSPublicKey(defaultOperator, defaultPubKey); + // setup the dummy minimum stake for quorum uint96[] memory minimumStakeForQuorum = new uint96[](numQuorumsToAdd); for (uint256 i = 0; i < minimumStakeForQuorum.length; i++) { From 6d1a2567c1a9018cbbc7ba4595431d4077e18d2e Mon Sep 17 00:00:00 2001 From: ChaoticWalrus <93558947+ChaoticWalrus@users.noreply.github.com> Date: Mon, 11 Dec 2023 13:14:28 -0800 Subject: [PATCH 058/101] chore: delete unused mock file I created this mock while trying to sort out compilation issues and test failures This mock is no longer being used anywhere, so I am deleting it for cleanup --- test/mocks/BLSApkRegistryMock.sol | 136 ------------------------------ 1 file changed, 136 deletions(-) delete mode 100644 test/mocks/BLSApkRegistryMock.sol diff --git a/test/mocks/BLSApkRegistryMock.sol b/test/mocks/BLSApkRegistryMock.sol deleted file mode 100644 index 1f050f4c..00000000 --- a/test/mocks/BLSApkRegistryMock.sol +++ /dev/null @@ -1,136 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity =0.8.12; - -import {IRegistryCoordinator} from "src/interfaces/IRegistryCoordinator.sol"; -import {IBLSApkRegistry} from "src/interfaces/IBLSApkRegistry.sol"; -import {BN254} from "src/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 BLSApkRegistryMock is IBLSApkRegistry { - - /// @notice the hash of the zero pubkey aka BN254.G1Point(0,0) - bytes32 internal constant ZERO_PK_HASH = hex"ad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5"; - - /// @notice maps operator address to pubkey hash - mapping(address => bytes32) public operatorToPubkeyHash; - /// @notice maps pubkey hash to operator address - mapping(bytes32 => address) public pubkeyHashToOperator; - /// @notice maps operator address to pubkeyG1 - mapping(address => BN254.G1Point) public operatorToPubkey; - - function registryCoordinator() external view returns (IRegistryCoordinator) {} - - /** - * @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. - * @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) 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. - * @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(address operator, bytes calldata quorumNumbers) external {} - - /** - * @notice Initializes a new quorum by pushing its first apk update - * @param quorumNumber The number of the new quorum - */ - function initializeQuorum(uint8 quorumNumber) external {} - - /** - * @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 {} - - /** - * @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) external view returns (BN254.G1Point memory) {} - - /// @notice Returns the current APK for the provided `quorumNumber ` - function getApk(uint8 quorumNumber) external view returns (BN254.G1Point memory) {} - - /// @notice Returns the index of the quorumApk index at `blockNumber` for the provided `quorumNumber` - function getApkIndicesAtBlockNumber(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 getApkUpdateAtIndex(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 getApkHashAtBlockNumberAndIndex( - uint8 quorumNumber, - uint32 blockNumber, - uint256 index - ) external view returns (bytes24) { - // ApkUpdate memory quorumApkUpdate = apkHistory[quorumNumber][index]; - // _validateApkHashAtBlockNumber(quorumApkUpdate, blockNumber); - ApkUpdate memory quorumApkUpdate; - return quorumApkUpdate.apkHash; - } - - function getOperatorId(address operator) external view returns (bytes32) {} - - function registerPublicKey(BN254.G1Point memory pk) external { - - bytes32 pubkeyHash = BN254.hashG1Point(pk); - // store updates - operatorToPubkeyHash[msg.sender] = pubkeyHash; - pubkeyHashToOperator[pubkeyHash] = msg.sender; - operatorToPubkey[msg.sender] = pk; - } - - function setBLSPublicKey(address account, BN254.G1Point memory pk) external { - - bytes32 pubkeyHash = BN254.hashG1Point(pk); - // store updates - operatorToPubkeyHash[account] = pubkeyHash; - pubkeyHashToOperator[pubkeyHash] = account; - operatorToPubkey[account] = pk; - } - - function getRegisteredPubkey(address operator) public view returns (BN254.G1Point memory, bytes32) { - BN254.G1Point memory pubkey = operatorToPubkey[operator]; - bytes32 pubkeyHash = operatorToPubkeyHash[operator]; - - require( - pubkeyHash != bytes32(0), - "BLSPublicKeyCompendium.getRegisteredPubkey: operator is not registered" - ); - - return (pubkey, pubkeyHash); - } - -} From f5443e8fa13f4d9f41314c8cb410db9993c42087 Mon Sep 17 00:00:00 2001 From: ChaoticWalrus <93558947+ChaoticWalrus@users.noreply.github.com> Date: Mon, 11 Dec 2023 13:23:51 -0800 Subject: [PATCH 059/101] chore: cleanup unused file and reduce mutability of test utils functions this commit fixes a ton of compiler warnings on build, in particular. --- test/mocks/MiddlewareRegistryMock.sol | 39 --------------------------- test/mocks/StakeRegistryMock.sol | 2 +- test/utils/Operators.sol | 14 +++++----- test/utils/Owners.sol | 4 +-- test/utils/ProofParsing.sol | 26 +++++++++--------- 5 files changed, 23 insertions(+), 62 deletions(-) delete mode 100644 test/mocks/MiddlewareRegistryMock.sol diff --git a/test/mocks/MiddlewareRegistryMock.sol b/test/mocks/MiddlewareRegistryMock.sol deleted file mode 100644 index d848c802..00000000 --- a/test/mocks/MiddlewareRegistryMock.sol +++ /dev/null @@ -1,39 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity =0.8.12; - -import "../../src/interfaces/IServiceManager.sol"; -import "eigenlayer-contracts/src/contracts/interfaces/IStrategyManager.sol"; -import "eigenlayer-contracts/src/contracts/interfaces/ISlasher.sol"; - -contract MiddlewareRegistryMock{ - IServiceManager public serviceManager; - IStrategyManager public strategyManager; - ISlasher public slasher; - - - constructor( - IServiceManager _serviceManager, - IStrategyManager _strategyManager - ) { - serviceManager = _serviceManager; - strategyManager = _strategyManager; - slasher = _strategyManager.slasher(); - } - - function registerOperator(address operator, uint32 serveUntil) public { - require(slasher.canSlash(operator, address(serviceManager)), "Not opted into slashing"); - - } - - function deregisterOperator(address operator) public { - } - - function isActiveOperator(address operator) external pure returns (bool) { - if (operator != address(0)) { - return true; - } else { - return false; - } - } - -} diff --git a/test/mocks/StakeRegistryMock.sol b/test/mocks/StakeRegistryMock.sol index bc2394b9..8088f2d1 100644 --- a/test/mocks/StakeRegistryMock.sol +++ b/test/mocks/StakeRegistryMock.sol @@ -201,7 +201,7 @@ contract StakeRegistryMock is IStakeRegistry { bytes calldata quorumNumbers ) external returns (uint192) {} - function getMockOperatorId(address operator) external returns(bytes32) { + function getMockOperatorId(address operator) external pure returns(bytes32) { return bytes32(uint256(keccak256(abi.encodePacked(operator, "operatorId")))); } } diff --git a/test/utils/Operators.sol b/test/utils/Operators.sol index 7414cdf7..dd32c23c 100644 --- a/test/utils/Operators.sol +++ b/test/utils/Operators.sol @@ -16,15 +16,15 @@ contract Operators is Test { return string.concat(".operators[", string.concat(vm.toString(index), "].")); } - function getNumOperators() public returns(uint256) { + function getNumOperators() public view returns(uint256) { return stdJson.readUint(operatorConfigJson, ".numOperators"); } - function getOperatorAddress(uint256 index) public returns(address) { + function getOperatorAddress(uint256 index) public view returns(address) { return stdJson.readAddress(operatorConfigJson, string.concat(operatorPrefix(index), "Address")); } - function getOperatorSchnorrSignature(uint256 index) public returns(uint256, BN254.G1Point memory) { + function getOperatorSchnorrSignature(uint256 index) public view returns(uint256, BN254.G1Point memory) { uint256 s = readUint(operatorConfigJson, index, "SField"); BN254.G1Point memory pubkey = BN254.G1Point({ X: readUint(operatorConfigJson, index, "RPoint.X"), @@ -33,11 +33,11 @@ contract Operators is Test { return (s, pubkey); } - function getOperatorSecretKey(uint256 index) public returns(uint256) { + function getOperatorSecretKey(uint256 index) public view returns(uint256) { return readUint(operatorConfigJson, index, "SecretKey"); } - function getOperatorPubkeyG1(uint256 index) public returns(BN254.G1Point memory) { + function getOperatorPubkeyG1(uint256 index) public view returns(BN254.G1Point memory) { BN254.G1Point memory pubkey = BN254.G1Point({ X: readUint(operatorConfigJson, index, "PubkeyG1.X"), Y: readUint(operatorConfigJson, index, "PubkeyG1.Y") @@ -45,7 +45,7 @@ contract Operators is Test { return pubkey; } - function getOperatorPubkeyG2(uint256 index) public returns(BN254.G2Point memory) { + function getOperatorPubkeyG2(uint256 index) public view returns(BN254.G2Point memory) { BN254.G2Point memory pubkey = BN254.G2Point({ X: [ readUint(operatorConfigJson, index, "PubkeyG2.X.A1"), @@ -59,7 +59,7 @@ contract Operators is Test { return pubkey; } - function readUint(string memory json, uint256 index, string memory key) public returns (uint256) { + function readUint(string memory json, uint256 index, string memory key) public pure returns (uint256) { return stringToUint(stdJson.readString(json, string.concat(operatorPrefix(index), key))); } diff --git a/test/utils/Owners.sol b/test/utils/Owners.sol index 5f93d50b..0a0643e7 100644 --- a/test/utils/Owners.sol +++ b/test/utils/Owners.sol @@ -17,11 +17,11 @@ contract Owners is Test { return string.concat(".owners[", string.concat(vm.toString(index), "].")); } - function getNumOperators() public returns(uint256) { + function getNumOperators() public view returns(uint256) { return stdJson.readUint(ownersConfigJson, ".numOwners"); } - function getOwnerAddress(uint256 index) public returns(address) { + function getOwnerAddress(uint256 index) public view returns(address) { return stdJson.readAddress(ownersConfigJson, string.concat(ownerPrefix(index), "Address")); } diff --git a/test/utils/ProofParsing.sol b/test/utils/ProofParsing.sol index a61fb127..193256c4 100644 --- a/test/utils/ProofParsing.sol +++ b/test/utils/ProofParsing.sol @@ -29,55 +29,55 @@ contract ProofParsing is Test{ proofConfigJson = vm.readFile(path); } - function getSlot() public returns(uint256) { + function getSlot() public view returns(uint256) { return stdJson.readUint(proofConfigJson, ".slot"); } - function getValidatorIndex() public returns(uint256) { + function getValidatorIndex() public view returns(uint256) { return stdJson.readUint(proofConfigJson, ".validatorIndex"); } - function getValidatorPubkeyHash() public returns(bytes32) { + function getValidatorPubkeyHash() public view returns(bytes32) { return stdJson.readBytes32(proofConfigJson, ".ValidatorFields[0]"); } - function getWithdrawalIndex() public returns(uint256) { + function getWithdrawalIndex() public view returns(uint256) { return stdJson.readUint(proofConfigJson, ".withdrawalIndex"); } - function getBlockRootIndex() public returns(uint256) { + function getBlockRootIndex() public view returns(uint256) { return stdJson.readUint(proofConfigJson, ".blockHeaderRootIndex"); } - function getHistoricalSummaryIndex() public returns(uint256) { + function getHistoricalSummaryIndex() public view returns(uint256) { return stdJson.readUint(proofConfigJson, ".historicalSummaryIndex"); } - function getBeaconStateRoot() public returns(bytes32) { + function getBeaconStateRoot() public view returns(bytes32) { return stdJson.readBytes32(proofConfigJson, ".beaconStateRoot"); } - function getBlockRoot() public returns(bytes32) { + function getBlockRoot() public view returns(bytes32) { return stdJson.readBytes32(proofConfigJson, ".blockHeaderRoot"); } - function getSlotRoot() public returns(bytes32) { + function getSlotRoot() public view returns(bytes32) { return stdJson.readBytes32(proofConfigJson, ".slotRoot"); } - function getBalanceRoot() public returns(bytes32) { + function getBalanceRoot() public view returns(bytes32) { return stdJson.readBytes32(proofConfigJson, ".balanceRoot"); } - function getTimestampRoot() public returns(bytes32) { + function getTimestampRoot() public view returns(bytes32) { return stdJson.readBytes32(proofConfigJson, ".timestampRoot"); } - function getExecutionPayloadRoot() public returns(bytes32) { + function getExecutionPayloadRoot() public view returns(bytes32) { return stdJson.readBytes32(proofConfigJson, ".executionPayloadRoot"); } - function getLatestBlockRoot() public returns(bytes32) { + function getLatestBlockRoot() public view returns(bytes32) { return stdJson.readBytes32(proofConfigJson, ".latestBlockHeaderRoot"); } function getExecutionPayloadProof () public returns(bytes32[7] memory) { From e0b2cc924ac48133c18a616762ae0199239f195f Mon Sep 17 00:00:00 2001 From: ChaoticWalrus <93558947+ChaoticWalrus@users.noreply.github.com> Date: Mon, 11 Dec 2023 15:27:38 -0800 Subject: [PATCH 060/101] feat: make BLSApkRegistry. registerBLSPublicKey only callable by RegistryCoordinator also make a version of `RegistryCoordinator. registerOperator` that accepts these inputs new function passes these inputs on appropriately TODO: remove old `registerOperator` function and fix associated tests --- src/BLSApkRegistry.sol | 18 +++++----- src/RegistryCoordinator.sol | 47 +++++++++++++++++++++++++++ src/interfaces/IBLSApkRegistry.sol | 10 ++++-- test/ffi/BLSPubKeyCompendiumFFI.t.sol | 6 ++-- test/unit/BLSApkRegistryUnit.t.sol | 20 ++++++------ 5 files changed, 78 insertions(+), 23 deletions(-) diff --git a/src/BLSApkRegistry.sol b/src/BLSApkRegistry.sol index 99f48288..880ef9c2 100644 --- a/src/BLSApkRegistry.sol +++ b/src/BLSApkRegistry.sol @@ -94,22 +94,24 @@ contract BLSApkRegistry is BLSApkRegistryStorage { } /** - * @notice Called by an operator to register themselves as the owner of a BLS public key and reveal their G1 and G2 public key. + * @notice Called by the RegistryCoordinator register an operator as the owner of a BLS public key. + * @param operator is the operator for whom the key is being registered * @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( + address operator, BN254.G1Point memory signedMessageHash, BN254.G1Point memory pubkeyG1, BN254.G2Point memory pubkeyG2 - ) external { + ) external onlyRegistryCoordinator { bytes32 pubkeyHash = BN254.hashG1Point(pubkeyG1); require( pubkeyHash != ZERO_PK_HASH, "BLSApkRegistry.registerBLSPublicKey: cannot register zero pubkey" ); require( - operatorToPubkeyHash[msg.sender] == bytes32(0), + operatorToPubkeyHash[operator] == bytes32(0), "BLSApkRegistry.registerBLSPublicKey: operator already registered pubkey" ); require( @@ -118,7 +120,7 @@ contract BLSApkRegistry is BLSApkRegistryStorage { ); // H(m) - BN254.G1Point memory messageHash = getMessageHash(msg.sender); + BN254.G1Point memory messageHash = getMessageHash(operator); // gamma = h(sigma, P, P', H(m)) uint256 gamma = uint256(keccak256(abi.encodePacked( @@ -140,11 +142,11 @@ contract BLSApkRegistry is BLSApkRegistryStorage { pubkeyG2 ), "BLSApkRegistry.registerBLSPublicKey: either the G1 signature is wrong, or G1 and G2 private key do not match"); - operatorToPubkey[msg.sender] = pubkeyG1; - operatorToPubkeyHash[msg.sender] = pubkeyHash; - pubkeyHashToOperator[pubkeyHash] = msg.sender; + operatorToPubkey[operator] = pubkeyG1; + operatorToPubkeyHash[operator] = pubkeyHash; + pubkeyHashToOperator[pubkeyHash] = operator; - emit NewPubkeyRegistration(msg.sender, pubkeyG1, pubkeyG2); + emit NewPubkeyRegistration(operator, pubkeyG1, pubkeyG2); } /******************************************************************************* diff --git a/src/RegistryCoordinator.sol b/src/RegistryCoordinator.sol index 615879f3..9a2b034f 100644 --- a/src/RegistryCoordinator.sol +++ b/src/RegistryCoordinator.sol @@ -18,6 +18,7 @@ import {IStakeRegistry} from "src/interfaces/IStakeRegistry.sol"; import {IIndexRegistry} from "src/interfaces/IIndexRegistry.sol"; import {BitmapUtils} from "src/libraries/BitmapUtils.sol"; +import {BN254} from "src/libraries/BN254.sol"; /** * @title A `RegistryCoordinator` that has three registries: @@ -29,6 +30,7 @@ import {BitmapUtils} from "src/libraries/BitmapUtils.sol"; */ contract RegistryCoordinator is EIP712, Initializable, IRegistryCoordinator, ISocketUpdater, ISignatureUtils, Pausable { using BitmapUtils for *; + 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 = @@ -141,6 +143,51 @@ contract RegistryCoordinator is EIP712, Initializable, IRegistryCoordinator, ISo EXTERNAL FUNCTIONS *******************************************************************************/ + /** + * @notice Registers msg.sender as an operator for one or more quorums. If any quorum reaches its maximum + * operator capacity, this method will fail. + * @param quorumNumbers is an ordered byte array containing the quorum numbers being registered for + */ + function registerOperator( + bytes calldata quorumNumbers, + string calldata socket, + BN254.G1Point memory pubkeyRegistrationSignature, + BN254.G1Point memory pubkeyG1, + BN254.G2Point memory pubkeyG2 + ) external onlyWhenNotPaused(PAUSED_REGISTER_OPERATOR) { + /** + * IF the operator has never registered a pubkey before, THEN register their pubkey + * OTHERWISE, simply ignore the `pubkeyRegistrationSignature`, `pubkeyG1`, and `pubkeyG2` inputs + */ + if (blsApkRegistry.operatorToPubkeyHash(msg.sender) == 0) { + blsApkRegistry.registerBLSPublicKey(msg.sender, pubkeyRegistrationSignature, pubkeyG1, pubkeyG2); + } + bytes32 operatorId = blsApkRegistry.getOperatorId(msg.sender); + + // Register the operator in each of the registry contracts + RegisterResults memory results = _registerOperator({ + operator: msg.sender, + operatorId: operatorId, + quorumNumbers: quorumNumbers, + socket: socket + }); + + for (uint256 i = 0; i < quorumNumbers.length; i++) { + uint8 quorumNumber = uint8(quorumNumbers[i]); + + OperatorSetParam memory operatorSetParams = _quorumParams[quorumNumber]; + + /** + * The new operator count for each quorum may not exceed the configured maximum + * If it does, use `registerOperatorWithChurn` instead. + */ + require( + results.numOperatorsPerQuorum[i] <= operatorSetParams.maxOperatorCount, + "RegistryCoordinator.registerOperator: operator count exceeds maximum" + ); + } + } + /** * @notice Registers msg.sender as an operator for one or more quorums. If any quorum reaches its maximum * operator capacity, this method will fail. diff --git a/src/interfaces/IBLSApkRegistry.sol b/src/interfaces/IBLSApkRegistry.sol index 89550777..af241658 100644 --- a/src/interfaces/IBLSApkRegistry.sol +++ b/src/interfaces/IBLSApkRegistry.sol @@ -83,12 +83,18 @@ interface IBLSApkRegistry is IRegistry { function pubkeyHashToOperator(bytes32 pubkeyHash) external view returns (address); /** - * @notice Called by an operator to register themselves as the owner of a BLS public key and reveal their G1 and G2 public key. + * @notice Called by the RegistryCoordinator register an operator as the owner of a BLS public key. + * @param operator is the operator for whom the key is being registered * @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 registerBLSPublicKey( + address operator, + BN254.G1Point memory signedMessageHash, + BN254.G1Point memory pubkeyG1, + BN254.G2Point memory pubkeyG2 + ) external; /** * @notice Returns the pubkey and pubkey hash of an operator diff --git a/test/ffi/BLSPubKeyCompendiumFFI.t.sol b/test/ffi/BLSPubKeyCompendiumFFI.t.sol index 0178cec1..9a09653e 100644 --- a/test/ffi/BLSPubKeyCompendiumFFI.t.sol +++ b/test/ffi/BLSPubKeyCompendiumFFI.t.sol @@ -16,6 +16,7 @@ contract BLSApkRegistryFFITests is G2Operations { Vm cheats = Vm(HEVM_ADDRESS); BLSApkRegistry blsApkRegistry; + IRegistryCoordinator registryCoordinator; uint256 privKey; BN254.G1Point pubKeyG1; @@ -25,7 +26,6 @@ contract BLSApkRegistryFFITests is G2Operations { address alice = address(0x69); function setUp() public { - IRegistryCoordinator registryCoordinator; blsApkRegistry = new BLSApkRegistry(registryCoordinator); } @@ -35,8 +35,8 @@ contract BLSApkRegistryFFITests is G2Operations { signedMessageHash = _signMessage(alice); - vm.prank(alice); - blsApkRegistry.registerBLSPublicKey(signedMessageHash, pubKeyG1, pubKeyG2); + vm.prank(address(registryCoordinator)); + blsApkRegistry.registerBLSPublicKey(alice, signedMessageHash, pubKeyG1, pubKeyG2); assertEq(blsApkRegistry.operatorToPubkeyHash(alice), BN254.hashG1Point(pubKeyG1), "pubkey hash not stored correctly"); assertEq(blsApkRegistry.pubkeyHashToOperator(BN254.hashG1Point(pubKeyG1)), alice, "operator address not stored correctly"); diff --git a/test/unit/BLSApkRegistryUnit.t.sol b/test/unit/BLSApkRegistryUnit.t.sol index c30e1bdd..493a44d4 100644 --- a/test/unit/BLSApkRegistryUnit.t.sol +++ b/test/unit/BLSApkRegistryUnit.t.sol @@ -294,8 +294,8 @@ contract BLSApkRegistryUnitTests is Test { // TODO: better organize / integrate tests migrated from `BLSPublicKeyCompendium` unit tests function testRegisterBLSPublicKey() public { signedMessageHash = _signMessage(alice); - vm.prank(alice); - blsApkRegistry.registerBLSPublicKey(signedMessageHash, pubKeyG1, pubKeyG2); + vm.prank(address(registryCoordinator)); + blsApkRegistry.registerBLSPublicKey(alice, signedMessageHash, pubKeyG1, pubKeyG2); assertEq(blsApkRegistry.operatorToPubkeyHash(alice), BN254.hashG1Point(pubKeyG1), "pubkey hash not stored correctly"); assertEq(blsApkRegistry.pubkeyHashToOperator(BN254.hashG1Point(pubKeyG1)), alice, "operator address not stored correctly"); @@ -305,34 +305,34 @@ contract BLSApkRegistryUnitTests is Test { signedMessageHash = _signMessage(alice); BN254.G1Point memory badPubKeyG1 = BN254.generatorG1().scalar_mul(420); // mismatch public keys - vm.prank(alice); + vm.prank(address(registryCoordinator)); vm.expectRevert(bytes("BLSApkRegistry.registerBLSPublicKey: either the G1 signature is wrong, or G1 and G2 private key do not match")); - blsApkRegistry.registerBLSPublicKey(signedMessageHash, badPubKeyG1, pubKeyG2); + blsApkRegistry.registerBLSPublicKey(alice, signedMessageHash, badPubKeyG1, pubKeyG2); } function testRegisterBLSPublicKey_BadSig_Reverts() public { signedMessageHash = _signMessage(bob); // sign with wrong private key - vm.prank(alice); + vm.prank(address(registryCoordinator)); vm.expectRevert(bytes("BLSApkRegistry.registerBLSPublicKey: either the G1 signature is wrong, or G1 and G2 private key do not match")); - blsApkRegistry.registerBLSPublicKey(signedMessageHash, pubKeyG1, pubKeyG2); + blsApkRegistry.registerBLSPublicKey(alice, signedMessageHash, pubKeyG1, pubKeyG2); } function testRegisterBLSPublicKey_OpRegistered_Reverts() public { testRegisterBLSPublicKey(); // register alice - vm.prank(alice); + vm.prank(address(registryCoordinator)); vm.expectRevert(bytes("BLSApkRegistry.registerBLSPublicKey: operator already registered pubkey")); - blsApkRegistry.registerBLSPublicKey(signedMessageHash, pubKeyG1, pubKeyG2); + blsApkRegistry.registerBLSPublicKey(alice, signedMessageHash, pubKeyG1, pubKeyG2); } function testRegisterBLSPublicKey_PkRegistered_Reverts() public { testRegisterBLSPublicKey(); signedMessageHash = _signMessage(bob); // same private key different operator - vm.prank(bob); + vm.prank(address(registryCoordinator)); vm.expectRevert(bytes("BLSApkRegistry.registerBLSPublicKey: public key already registered")); - blsApkRegistry.registerBLSPublicKey(signedMessageHash, pubKeyG1, pubKeyG2); + blsApkRegistry.registerBLSPublicKey(bob, signedMessageHash, pubKeyG1, pubKeyG2); } function _signMessage(address signer) internal view returns(BN254.G1Point memory) { From efa343ee32b09d267976938e269956e8e0be4c6a Mon Sep 17 00:00:00 2001 From: ChaoticWalrus <93558947+ChaoticWalrus@users.noreply.github.com> Date: Mon, 11 Dec 2023 16:16:19 -0800 Subject: [PATCH 061/101] chore: remove `RegistryCoordinator.registerOperator` method without pubkey registration inputs also fix tests to use the new method --- src/RegistryCoordinator.sol | 35 ----------- test/unit/RegistryCoordinatorUnit.t.sol | 83 ++++++++++++++++++++----- test/utils/MockAVSDeployer.sol | 12 +++- 3 files changed, 76 insertions(+), 54 deletions(-) diff --git a/src/RegistryCoordinator.sol b/src/RegistryCoordinator.sol index 9a2b034f..31398cae 100644 --- a/src/RegistryCoordinator.sol +++ b/src/RegistryCoordinator.sol @@ -188,41 +188,6 @@ contract RegistryCoordinator is EIP712, Initializable, IRegistryCoordinator, ISo } } - /** - * @notice Registers msg.sender as an operator for one or more quorums. If any quorum reaches its maximum - * operator capacity, this method will fail. - * @param quorumNumbers is an ordered byte array containing the quorum numbers being registered for - */ - function registerOperator( - bytes calldata quorumNumbers, - string calldata socket - ) external onlyWhenNotPaused(PAUSED_REGISTER_OPERATOR) { - bytes32 operatorId = blsApkRegistry.getOperatorId(msg.sender); - - // Register the operator in each of the registry contracts - RegisterResults memory results = _registerOperator({ - operator: msg.sender, - operatorId: operatorId, - quorumNumbers: quorumNumbers, - socket: socket - }); - - for (uint256 i = 0; i < quorumNumbers.length; i++) { - uint8 quorumNumber = uint8(quorumNumbers[i]); - - OperatorSetParam memory operatorSetParams = _quorumParams[quorumNumber]; - - /** - * The new operator count for each quorum may not exceed the configured maximum - * If it does, use `registerOperatorWithChurn` instead. - */ - require( - results.numOperatorsPerQuorum[i] <= operatorSetParams.maxOperatorCount, - "RegistryCoordinator.registerOperator: operator count exceeds maximum" - ); - } - } - /** * @notice Registers msg.sender as an operator for one or more quorums. If any quorum reaches its maximum operator * capacity, `operatorKickParams` is used to replace an old operator with the new one. diff --git a/test/unit/RegistryCoordinatorUnit.t.sol b/test/unit/RegistryCoordinatorUnit.t.sol index abaa5117..a9ebc493 100644 --- a/test/unit/RegistryCoordinatorUnit.t.sol +++ b/test/unit/RegistryCoordinatorUnit.t.sol @@ -115,42 +115,61 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { function testRegisterOperatorWithCoordinator_WhenPaused_Reverts() public { bytes memory emptyQuorumNumbers = new bytes(0); + BN254.G1Point memory pubkeyRegistrationSignature; + BN254.G1Point memory pubkeyG1; + BN254.G2Point memory pubkeyG2; + // pause registerOperator cheats.prank(pauser); registryCoordinator.pause(2 ** PAUSED_REGISTER_OPERATOR); cheats.startPrank(defaultOperator); cheats.expectRevert(bytes("Pausable: index is paused")); - registryCoordinator.registerOperator(emptyQuorumNumbers, defaultSocket); + registryCoordinator.registerOperator(emptyQuorumNumbers, defaultSocket, pubkeyRegistrationSignature, pubkeyG1, pubkeyG2); } function testRegisterOperatorWithCoordinator_EmptyQuorumNumbers_Reverts() public { bytes memory emptyQuorumNumbers = new bytes(0); + BN254.G1Point memory pubkeyRegistrationSignature; + BN254.G1Point memory pubkeyG1; + BN254.G2Point memory pubkeyG2; + cheats.expectRevert("RegistryCoordinator._registerOperator: bitmap cannot be 0"); cheats.prank(defaultOperator); - registryCoordinator.registerOperator(emptyQuorumNumbers, defaultSocket); + registryCoordinator.registerOperator(emptyQuorumNumbers, defaultSocket, pubkeyRegistrationSignature, pubkeyG1, pubkeyG2); } function testRegisterOperatorWithCoordinator_QuorumNumbersTooLarge_Reverts() public { bytes memory quorumNumbersTooLarge = new bytes(1); quorumNumbersTooLarge[0] = 0xC0; + BN254.G1Point memory pubkeyRegistrationSignature; + BN254.G1Point memory pubkeyG1; + BN254.G2Point memory pubkeyG2; + cheats.expectRevert("BitmapUtils.orderedBytesArrayToBitmap: bitmap exceeds max value"); cheats.prank(defaultOperator); - registryCoordinator.registerOperator(quorumNumbersTooLarge, defaultSocket); + registryCoordinator.registerOperator(quorumNumbersTooLarge, defaultSocket, pubkeyRegistrationSignature, pubkeyG1, pubkeyG2); } function testRegisterOperatorWithCoordinator_QuorumNotCreated_Reverts() public { _deployMockEigenLayerAndAVS(10); bytes memory quorumNumbersNotCreated = new bytes(1); quorumNumbersNotCreated[0] = 0x0B; + BN254.G1Point memory pubkeyRegistrationSignature; + BN254.G1Point memory pubkeyG1; + BN254.G2Point memory pubkeyG2; + cheats.prank(defaultOperator); cheats.expectRevert("BitmapUtils.orderedBytesArrayToBitmap: bitmap exceeds max value"); - registryCoordinator.registerOperator(quorumNumbersNotCreated, defaultSocket); + registryCoordinator.registerOperator(quorumNumbersNotCreated, defaultSocket, pubkeyRegistrationSignature, pubkeyG1, pubkeyG2); } function testRegisterOperatorWithCoordinatorForSingleQuorum_Valid() public { bytes memory quorumNumbers = new bytes(1); quorumNumbers[0] = bytes1(defaultQuorumNumber); + BN254.G1Point memory pubkeyRegistrationSignature; + BN254.G1Point memory pubkeyG1; + BN254.G2Point memory pubkeyG2; stakeRegistry.setOperatorWeight(uint8(quorumNumbers[0]), defaultOperator, defaultStake); @@ -165,7 +184,7 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { uint256 gasBefore = gasleft(); cheats.prank(defaultOperator); - registryCoordinator.registerOperator(quorumNumbers, defaultSocket); + registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationSignature, pubkeyG1, pubkeyG2); uint256 gasAfter = gasleft(); emit log_named_uint("gasUsed", gasBefore - gasAfter); @@ -194,6 +213,9 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { quorumBitmap = quorumBitmap & MAX_QUORUM_BITMAP; cheats.assume(quorumBitmap != 0); bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); + BN254.G1Point memory pubkeyRegistrationSignature; + BN254.G1Point memory pubkeyG1; + BN254.G2Point memory pubkeyG2; for (uint i = 0; i < quorumNumbers.length; i++) { stakeRegistry.setOperatorWeight(uint8(quorumNumbers[i]), defaultOperator, defaultStake); @@ -217,7 +239,7 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { uint256 gasBefore = gasleft(); cheats.prank(defaultOperator); - registryCoordinator.registerOperator(quorumNumbers, defaultSocket); + registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationSignature, pubkeyG1, pubkeyG2); uint256 gasAfter = gasleft(); emit log_named_uint("gasUsed", gasBefore - gasAfter); emit log_named_uint("numQuorums", quorumNumbers.length); @@ -247,11 +269,14 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { bytes memory quorumNumbers = new bytes(1); quorumNumbers[0] = bytes1(defaultQuorumNumber); + BN254.G1Point memory pubkeyRegistrationSignature; + BN254.G1Point memory pubkeyG1; + BN254.G2Point memory pubkeyG2; stakeRegistry.setOperatorWeight(uint8(quorumNumbers[0]), defaultOperator, defaultStake); cheats.prank(defaultOperator); cheats.roll(registrationBlockNumber); - registryCoordinator.registerOperator(quorumNumbers, defaultSocket); + registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationSignature, pubkeyG1, pubkeyG2); bytes memory newQuorumNumbers = new bytes(1); newQuorumNumbers[0] = bytes1(defaultQuorumNumber+1); @@ -267,7 +292,7 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { emit QuorumIndexUpdate(defaultOperatorId, uint8(newQuorumNumbers[0]), 0); cheats.roll(nextRegistrationBlockNumber); cheats.prank(defaultOperator); - registryCoordinator.registerOperator(newQuorumNumbers, defaultSocket); + registryCoordinator.registerOperator(newQuorumNumbers, defaultSocket, pubkeyRegistrationSignature, pubkeyG1, pubkeyG2); uint256 quorumBitmap = BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers) | BitmapUtils.orderedBytesArrayToBitmap(newQuorumNumbers); @@ -304,6 +329,9 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { bytes memory quorumNumbers = new bytes(1); quorumNumbers[0] = bytes1(defaultQuorumNumber); + BN254.G1Point memory pubkeyRegistrationSignature; + BN254.G1Point memory pubkeyG1; + BN254.G2Point memory pubkeyG2; uint256 quorumBitmap = BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers); @@ -325,7 +353,7 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { cheats.prank(operatorToRegister); cheats.expectRevert("RegistryCoordinator.registerOperator: operator count exceeds maximum"); - registryCoordinator.registerOperator(quorumNumbers, defaultSocket); + registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationSignature, pubkeyG1, pubkeyG2); } function testRegisterOperatorWithCoordinator_RegisteredOperatorForSameQuorums_Reverts() public { @@ -334,16 +362,19 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { bytes memory quorumNumbers = new bytes(1); quorumNumbers[0] = bytes1(defaultQuorumNumber); + BN254.G1Point memory pubkeyRegistrationSignature; + BN254.G1Point memory pubkeyG1; + BN254.G2Point memory pubkeyG2; stakeRegistry.setOperatorWeight(uint8(quorumNumbers[0]), defaultOperator, defaultStake); cheats.prank(defaultOperator); cheats.roll(registrationBlockNumber); - registryCoordinator.registerOperator(quorumNumbers, defaultSocket); + registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationSignature, pubkeyG1, pubkeyG2); cheats.prank(defaultOperator); cheats.roll(nextRegistrationBlockNumber); cheats.expectRevert("RegistryCoordinator._registerOperator: operator already registered for some quorums being registered for"); - registryCoordinator.registerOperator(quorumNumbers, defaultSocket); + registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationSignature, pubkeyG1, pubkeyG2); } function testDeregisterOperatorWithCoordinator_WhenPaused_Reverts() public { @@ -393,6 +424,9 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { bytes memory quorumNumbers = new bytes(1); quorumNumbers[0] = bytes1(defaultQuorumNumber); + BN254.G1Point memory pubkeyRegistrationSignature; + BN254.G1Point memory pubkeyG1; + BN254.G2Point memory pubkeyG2; stakeRegistry.setOperatorWeight(uint8(quorumNumbers[0]), defaultOperator, defaultStake); @@ -400,7 +434,7 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { cheats.roll(registrationBlockNumber); - registryCoordinator.registerOperator(quorumNumbers, defaultSocket); + registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationSignature, pubkeyG1, pubkeyG2); uint256 quorumBitmap = BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers); @@ -437,6 +471,9 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { function testDeregisterOperatorWithCoordinatorForFuzzedQuorumAndSingleOperator_Valid(uint256 quorumBitmap) public { uint32 registrationBlockNumber = 100; uint32 deregistrationBlockNumber = 200; + BN254.G1Point memory pubkeyRegistrationSignature; + BN254.G1Point memory pubkeyG1; + BN254.G2Point memory pubkeyG2; quorumBitmap = quorumBitmap & MAX_QUORUM_BITMAP; cheats.assume(quorumBitmap != 0); @@ -450,7 +487,7 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { cheats.roll(registrationBlockNumber); - registryCoordinator.registerOperator(quorumNumbers, defaultSocket); + registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationSignature, pubkeyG1, pubkeyG2); cheats.expectEmit(true, true, true, true, address(blsApkRegistry)); emit OperatorRemovedFromQuorums(defaultOperator, quorumNumbers); @@ -567,6 +604,9 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { bytes memory quorumNumbers = new bytes(1); quorumNumbers[0] = bytes1(defaultQuorumNumber); + BN254.G1Point memory pubkeyRegistrationSignature; + BN254.G1Point memory pubkeyG1; + BN254.G2Point memory pubkeyG2; cheats.startPrank(defaultOperator); @@ -577,7 +617,7 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0); // re-register the operator - registryCoordinator.registerOperator(quorumNumbers, defaultSocket); + registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationSignature, pubkeyG1, pubkeyG2); // check success of registration uint256 quorumBitmap = BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers); @@ -803,11 +843,14 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { // register operator with default stake with default quorum number bytes memory quorumNumbers = new bytes(1); quorumNumbers[0] = bytes1(defaultQuorumNumber); + BN254.G1Point memory pubkeyRegistrationSignature; + BN254.G1Point memory pubkeyG1; + BN254.G2Point memory pubkeyG2; stakeRegistry.setOperatorWeight(uint8(quorumNumbers[0]), defaultOperator, defaultStake); cheats.prank(defaultOperator); - registryCoordinator.registerOperator(quorumNumbers, defaultSocket); + registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationSignature, pubkeyG1, pubkeyG2); cheats.expectEmit(true, true, true, true, address(blsApkRegistry)); emit OperatorRemovedFromQuorums(defaultOperator, quorumNumbers); @@ -836,13 +879,16 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { bytes memory quorumNumbers = new bytes(2); quorumNumbers[0] = bytes1(defaultQuorumNumber); quorumNumbers[1] = bytes1(defaultQuorumNumber + 1); + BN254.G1Point memory pubkeyRegistrationSignature; + BN254.G1Point memory pubkeyG1; + BN254.G2Point memory pubkeyG2; for (uint i = 0; i < quorumNumbers.length; i++) { stakeRegistry.setOperatorWeight(uint8(quorumNumbers[i]), defaultOperator, defaultStake); } cheats.prank(defaultOperator); - registryCoordinator.registerOperator(quorumNumbers, defaultSocket); + registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationSignature, pubkeyG1, pubkeyG2); // eject from only first quorum bytes memory quorumNumbersToEject = new bytes(1); @@ -875,11 +921,14 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { function testEjectOperatorFromCoordinator_NotEjector_Reverts() public { bytes memory quorumNumbers = new bytes(1); quorumNumbers[0] = bytes1(defaultQuorumNumber); + BN254.G1Point memory pubkeyRegistrationSignature; + BN254.G1Point memory pubkeyG1; + BN254.G2Point memory pubkeyG2; stakeRegistry.setOperatorWeight(uint8(quorumNumbers[0]), defaultOperator, defaultStake); cheats.prank(defaultOperator); - registryCoordinator.registerOperator(quorumNumbers, defaultSocket); + registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationSignature, pubkeyG1, pubkeyG2); cheats.expectRevert("RegistryCoordinator.onlyEjector: caller is not the ejector"); cheats.prank(defaultOperator); diff --git a/test/utils/MockAVSDeployer.sol b/test/utils/MockAVSDeployer.sol index c40de9a9..5112746d 100644 --- a/test/utils/MockAVSDeployer.sol +++ b/test/utils/MockAVSDeployer.sol @@ -291,13 +291,17 @@ contract MockAVSDeployer is Test { blsApkRegistry.setBLSPublicKey(operator, pubKey); + BN254.G1Point memory pubkeyRegistrationSignature; + BN254.G1Point memory pubkeyG1; + BN254.G2Point memory pubkeyG2; + 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.registerOperator(quorumNumbers, defaultSocket); + registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationSignature, pubkeyG1, pubkeyG2); } /** @@ -309,13 +313,17 @@ contract MockAVSDeployer is Test { blsApkRegistry.setBLSPublicKey(operator, pubKey); + BN254.G1Point memory pubkeyRegistrationSignature; + BN254.G1Point memory pubkeyG1; + BN254.G2Point memory pubkeyG2; + 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.registerOperator(quorumNumbers, defaultSocket); + registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationSignature, pubkeyG1, pubkeyG2); } function _registerRandomOperators(uint256 pseudoRandomNumber) internal returns(OperatorMetadata[] memory, uint256[][] memory) { From c54b1ed4da2622492b8983d045a7fd7579637c6c Mon Sep 17 00:00:00 2001 From: ChaoticWalrus <93558947+ChaoticWalrus@users.noreply.github.com> Date: Mon, 11 Dec 2023 17:04:31 -0800 Subject: [PATCH 062/101] feat: create struct for pubkey registration params note: code is currently not compiling due to a stack-too-deep error error here: src/RegistryCoordinator.sol:254:69: | 254 | _deregisterOperator(operatorKickParams[i].operator, quorumNumbers[i:i+1]); --- src/BLSApkRegistry.sol | 30 +++--- src/RegistryCoordinator.sol | 20 +++- src/interfaces/IBLSApkRegistry.sol | 41 +++++---- test/ffi/BLSPubKeyCompendiumFFI.t.sol | 24 ++--- test/unit/BLSApkRegistryUnit.t.sol | 42 +++++---- test/unit/RegistryCoordinatorUnit.t.sol | 117 ++++++++++-------------- test/utils/MockAVSDeployer.sol | 14 +-- 7 files changed, 139 insertions(+), 149 deletions(-) diff --git a/src/BLSApkRegistry.sol b/src/BLSApkRegistry.sol index 880ef9c2..03abe2b5 100644 --- a/src/BLSApkRegistry.sol +++ b/src/BLSApkRegistry.sol @@ -96,17 +96,13 @@ contract BLSApkRegistry is BLSApkRegistryStorage { /** * @notice Called by the RegistryCoordinator register an operator as the owner of a BLS public key. * @param operator is the operator for whom the key is being registered - * @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 + * @param pubkeyRegistrationParams contains the G1 & G2 public keys of the operator, and a signature proving their ownership */ function registerBLSPublicKey( address operator, - BN254.G1Point memory signedMessageHash, - BN254.G1Point memory pubkeyG1, - BN254.G2Point memory pubkeyG2 + PubkeyRegistrationParams calldata pubkeyRegistrationParams ) external onlyRegistryCoordinator { - bytes32 pubkeyHash = BN254.hashG1Point(pubkeyG1); + bytes32 pubkeyHash = BN254.hashG1Point(pubkeyRegistrationParams.pubkeyG1); require( pubkeyHash != ZERO_PK_HASH, "BLSApkRegistry.registerBLSPublicKey: cannot register zero pubkey" ); @@ -124,29 +120,29 @@ contract BLSApkRegistry is BLSApkRegistryStorage { // 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, + pubkeyRegistrationParams.pubkeyRegistrationSignature.X, + pubkeyRegistrationParams.pubkeyRegistrationSignature.Y, + pubkeyRegistrationParams.pubkeyG1.X, + pubkeyRegistrationParams.pubkeyG1.Y, + pubkeyRegistrationParams.pubkeyG2.X, + pubkeyRegistrationParams.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)), + pubkeyRegistrationParams.pubkeyRegistrationSignature.plus(pubkeyRegistrationParams.pubkeyG1.scalar_mul(gamma)), BN254.negGeneratorG2(), messageHash.plus(BN254.generatorG1().scalar_mul(gamma)), - pubkeyG2 + pubkeyRegistrationParams.pubkeyG2 ), "BLSApkRegistry.registerBLSPublicKey: either the G1 signature is wrong, or G1 and G2 private key do not match"); - operatorToPubkey[operator] = pubkeyG1; + operatorToPubkey[operator] = pubkeyRegistrationParams.pubkeyG1; operatorToPubkeyHash[operator] = pubkeyHash; pubkeyHashToOperator[pubkeyHash] = operator; - emit NewPubkeyRegistration(operator, pubkeyG1, pubkeyG2); + emit NewPubkeyRegistration(operator, pubkeyRegistrationParams.pubkeyG1, pubkeyRegistrationParams.pubkeyG2); } /******************************************************************************* diff --git a/src/RegistryCoordinator.sol b/src/RegistryCoordinator.sol index 31398cae..68b5d4bd 100644 --- a/src/RegistryCoordinator.sol +++ b/src/RegistryCoordinator.sol @@ -147,20 +147,20 @@ contract RegistryCoordinator is EIP712, Initializable, IRegistryCoordinator, ISo * @notice Registers msg.sender as an operator for one or more quorums. If any quorum reaches its maximum * operator capacity, this method will fail. * @param quorumNumbers is an ordered byte array containing the quorum numbers being registered for + * @param pubkeyRegistrationParams contains the G1 & G2 public keys of the operator, and a signature proving their ownership + * @dev the `pubkeyRegistrationParams` input param is ignored if the caller has previously registered a public key */ function registerOperator( bytes calldata quorumNumbers, string calldata socket, - BN254.G1Point memory pubkeyRegistrationSignature, - BN254.G1Point memory pubkeyG1, - BN254.G2Point memory pubkeyG2 + IBLSApkRegistry.PubkeyRegistrationParams calldata pubkeyRegistrationParams ) external onlyWhenNotPaused(PAUSED_REGISTER_OPERATOR) { /** * IF the operator has never registered a pubkey before, THEN register their pubkey - * OTHERWISE, simply ignore the `pubkeyRegistrationSignature`, `pubkeyG1`, and `pubkeyG2` inputs + * OTHERWISE, simply ignore the provided `pubkeyRegistrationParams` */ if (blsApkRegistry.operatorToPubkeyHash(msg.sender) == 0) { - blsApkRegistry.registerBLSPublicKey(msg.sender, pubkeyRegistrationSignature, pubkeyG1, pubkeyG2); + blsApkRegistry.registerBLSPublicKey(msg.sender, pubkeyRegistrationParams); } bytes32 operatorId = blsApkRegistry.getOperatorId(msg.sender); @@ -192,18 +192,28 @@ contract RegistryCoordinator is EIP712, Initializable, IRegistryCoordinator, ISo * @notice Registers msg.sender as an operator for one or more quorums. If any quorum reaches its maximum operator * capacity, `operatorKickParams` is used to replace an old operator with the new one. * @param quorumNumbers is an ordered byte array containing the quorum numbers being registered for + * @param pubkeyRegistrationParams contains the G1 & G2 public keys of the operator, and a signature proving their ownership * @param operatorKickParams are used to determine which operator is removed to maintain quorum capacity as the * operator registers for quorums. * @param churnApproverSignature is the signature of the churnApprover on the operator kick params + * @dev the `pubkeyRegistrationParams` input param is ignored if the caller has previously registered a public key */ function registerOperatorWithChurn( bytes calldata quorumNumbers, string calldata socket, + IBLSApkRegistry.PubkeyRegistrationParams calldata pubkeyRegistrationParams, OperatorKickParam[] calldata operatorKickParams, SignatureWithSaltAndExpiry memory churnApproverSignature ) external onlyWhenNotPaused(PAUSED_REGISTER_OPERATOR) { require(operatorKickParams.length == quorumNumbers.length, "RegistryCoordinator.registerOperatorWithChurn: input length mismatch"); + /** + * IF the operator has never registered a pubkey before, THEN register their pubkey + * OTHERWISE, simply ignore the `pubkeyRegistrationSignature`, `pubkeyG1`, and `pubkeyG2` inputs + */ + if (blsApkRegistry.operatorToPubkeyHash(msg.sender) == 0) { + blsApkRegistry.registerBLSPublicKey(msg.sender, pubkeyRegistrationParams); + } bytes32 operatorId = blsApkRegistry.getOperatorId(msg.sender); // Verify the churn approver's signature for the registering operator and kick params diff --git a/src/interfaces/IBLSApkRegistry.sol b/src/interfaces/IBLSApkRegistry.sol index af241658..3c5a0c27 100644 --- a/src/interfaces/IBLSApkRegistry.sol +++ b/src/interfaces/IBLSApkRegistry.sol @@ -10,6 +10,29 @@ import {BN254} from "src/libraries/BN254.sol"; * @author Layr Labs, Inc. */ interface IBLSApkRegistry is IRegistry { + // STRUCTS + /// @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 Struct used when registering a new 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 + */ + struct PubkeyRegistrationParams { + BN254.G1Point pubkeyRegistrationSignature; + BN254.G1Point pubkeyG1; + BN254.G2Point pubkeyG2; + } + // EVENTS /// @notice Emitted when `operator` registers with the public keys `pubkeyG1` and `pubkeyG2`. event NewPubkeyRegistration(address indexed operator, BN254.G1Point pubkeyG1, BN254.G2Point pubkeyG2); @@ -26,16 +49,6 @@ interface IBLSApkRegistry is IRegistry { 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. @@ -85,15 +98,11 @@ interface IBLSApkRegistry is IRegistry { /** * @notice Called by the RegistryCoordinator register an operator as the owner of a BLS public key. * @param operator is the operator for whom the key is being registered - * @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 + * @param pubkeyRegistrationParams contains the G1 & G2 public keys of the operator, and a signature proving their ownership */ function registerBLSPublicKey( address operator, - BN254.G1Point memory signedMessageHash, - BN254.G1Point memory pubkeyG1, - BN254.G2Point memory pubkeyG2 + PubkeyRegistrationParams calldata pubkeyRegistrationParams ) external; /** diff --git a/test/ffi/BLSPubKeyCompendiumFFI.t.sol b/test/ffi/BLSPubKeyCompendiumFFI.t.sol index 9a09653e..0910ab5d 100644 --- a/test/ffi/BLSPubKeyCompendiumFFI.t.sol +++ b/test/ffi/BLSPubKeyCompendiumFFI.t.sol @@ -1,13 +1,9 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity =0.8.12; -<<<<<<< HEAD -import "../../src/BLSPublicKeyCompendium.sol"; -import "./util/G2Operations.sol"; -======= import "src/BLSApkRegistry.sol"; import "test/ffi/util/G2Operations.sol"; ->>>>>>> 68f3817 (chore: delete BLSPublicKeyCompendium and associated interface) +import {IBLSApkRegistry} from "src/interfaces/IBLSApkRegistry.sol"; contract BLSApkRegistryFFITests is G2Operations { using BN254 for BN254.G1Point; @@ -19,9 +15,7 @@ contract BLSApkRegistryFFITests is G2Operations { IRegistryCoordinator registryCoordinator; uint256 privKey; - BN254.G1Point pubKeyG1; - BN254.G2Point pubKeyG2; - BN254.G1Point signedMessageHash; + IBLSApkRegistry.PubkeyRegistrationParams pubkeyRegistrationParams; address alice = address(0x69); @@ -33,19 +27,21 @@ contract BLSApkRegistryFFITests is G2Operations { cheats.assume(_privKey != 0); _setKeys(_privKey); - signedMessageHash = _signMessage(alice); + pubkeyRegistrationParams.pubkeyRegistrationSignature = _signMessage(alice); vm.prank(address(registryCoordinator)); - blsApkRegistry.registerBLSPublicKey(alice, signedMessageHash, pubKeyG1, pubKeyG2); + blsApkRegistry.registerBLSPublicKey(alice, pubkeyRegistrationParams); - assertEq(blsApkRegistry.operatorToPubkeyHash(alice), BN254.hashG1Point(pubKeyG1), "pubkey hash not stored correctly"); - assertEq(blsApkRegistry.pubkeyHashToOperator(BN254.hashG1Point(pubKeyG1)), alice, "operator address not stored correctly"); + assertEq(blsApkRegistry.operatorToPubkeyHash(alice), BN254.hashG1Point(pubkeyRegistrationParams.pubkeyG1), + "pubkey hash not stored correctly"); + assertEq(blsApkRegistry.pubkeyHashToOperator(BN254.hashG1Point(pubkeyRegistrationParams.pubkeyG1)), alice, + "operator address not stored correctly"); } function _setKeys(uint256 _privKey) internal { privKey = _privKey; - pubKeyG1 = BN254.generatorG1().scalar_mul(_privKey); - pubKeyG2 = G2Operations.mul(_privKey); + pubkeyRegistrationParams.pubkeyG1 = BN254.generatorG1().scalar_mul(_privKey); + pubkeyRegistrationParams.pubkeyG2 = G2Operations.mul(_privKey); } function _signMessage(address signer) internal view returns(BN254.G1Point memory) { diff --git a/test/unit/BLSApkRegistryUnit.t.sol b/test/unit/BLSApkRegistryUnit.t.sol index 493a44d4..683d59ff 100644 --- a/test/unit/BLSApkRegistryUnit.t.sol +++ b/test/unit/BLSApkRegistryUnit.t.sol @@ -21,9 +21,7 @@ contract BLSApkRegistryUnitTests is Test { BN254.G1Point internal defaultPubKey = BN254.G1Point(18260007818883133054078754218619977578772505796600400998181738095793040006897,3432351341799135763167709827653955074218841517684851694584291831827675065899); - BN254.G1Point pubKeyG1; - BN254.G2Point pubKeyG2; - BN254.G1Point signedMessageHash; + IBLSApkRegistry.PubkeyRegistrationParams pubkeyRegistrationParams; address alice = address(1); address bob = address(2); @@ -39,13 +37,13 @@ contract BLSApkRegistryUnitTests is Test { registryCoordinator = new RegistryCoordinatorMock(); blsApkRegistry = new BLSApkRegistryHarness(registryCoordinator); - pubKeyG1 = BN254.generatorG1().scalar_mul(privKey); + pubkeyRegistrationParams.pubkeyG1 = BN254.generatorG1().scalar_mul(privKey); //privKey*G2 - pubKeyG2.X[1] = 19101821850089705274637533855249918363070101489527618151493230256975900223847; - pubKeyG2.X[0] = 5334410886741819556325359147377682006012228123419628681352847439302316235957; - pubKeyG2.Y[1] = 354176189041917478648604979334478067325821134838555150300539079146482658331; - pubKeyG2.Y[0] = 4185483097059047421902184823581361466320657066600218863748375739772335928910; + pubkeyRegistrationParams.pubkeyG2.X[1] = 19101821850089705274637533855249918363070101489527618151493230256975900223847; + pubkeyRegistrationParams.pubkeyG2.X[0] = 5334410886741819556325359147377682006012228123419628681352847439302316235957; + pubkeyRegistrationParams.pubkeyG2.Y[1] = 354176189041917478648604979334478067325821134838555150300539079146482658331; + pubkeyRegistrationParams.pubkeyG2.Y[0] = 4185483097059047421902184823581361466320657066600218863748375739772335928910; // Initialize a quorum @@ -293,29 +291,33 @@ contract BLSApkRegistryUnitTests is Test { // TODO: better organize / integrate tests migrated from `BLSPublicKeyCompendium` unit tests function testRegisterBLSPublicKey() public { - signedMessageHash = _signMessage(alice); + pubkeyRegistrationParams.pubkeyRegistrationSignature = _signMessage(alice); vm.prank(address(registryCoordinator)); - blsApkRegistry.registerBLSPublicKey(alice, signedMessageHash, pubKeyG1, pubKeyG2); + blsApkRegistry.registerBLSPublicKey(alice, pubkeyRegistrationParams); - assertEq(blsApkRegistry.operatorToPubkeyHash(alice), BN254.hashG1Point(pubKeyG1), "pubkey hash not stored correctly"); - assertEq(blsApkRegistry.pubkeyHashToOperator(BN254.hashG1Point(pubKeyG1)), alice, "operator address not stored correctly"); + assertEq(blsApkRegistry.operatorToPubkeyHash(alice), BN254.hashG1Point(pubkeyRegistrationParams.pubkeyG1), + "pubkey hash not stored correctly"); + assertEq(blsApkRegistry.pubkeyHashToOperator(BN254.hashG1Point(pubkeyRegistrationParams.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 + pubkeyRegistrationParams.pubkeyRegistrationSignature = _signMessage(alice); + BN254.G1Point memory badPubkeyG1 = BN254.generatorG1().scalar_mul(420); // mismatch public keys + + pubkeyRegistrationParams.pubkeyG1 = badPubkeyG1; vm.prank(address(registryCoordinator)); vm.expectRevert(bytes("BLSApkRegistry.registerBLSPublicKey: either the G1 signature is wrong, or G1 and G2 private key do not match")); - blsApkRegistry.registerBLSPublicKey(alice, signedMessageHash, badPubKeyG1, pubKeyG2); + blsApkRegistry.registerBLSPublicKey(alice, pubkeyRegistrationParams); } function testRegisterBLSPublicKey_BadSig_Reverts() public { - signedMessageHash = _signMessage(bob); // sign with wrong private key + pubkeyRegistrationParams.pubkeyRegistrationSignature = _signMessage(bob); // sign with wrong private key vm.prank(address(registryCoordinator)); vm.expectRevert(bytes("BLSApkRegistry.registerBLSPublicKey: either the G1 signature is wrong, or G1 and G2 private key do not match")); - blsApkRegistry.registerBLSPublicKey(alice, signedMessageHash, pubKeyG1, pubKeyG2); + blsApkRegistry.registerBLSPublicKey(alice, pubkeyRegistrationParams); } function testRegisterBLSPublicKey_OpRegistered_Reverts() public { @@ -323,16 +325,16 @@ contract BLSApkRegistryUnitTests is Test { vm.prank(address(registryCoordinator)); vm.expectRevert(bytes("BLSApkRegistry.registerBLSPublicKey: operator already registered pubkey")); - blsApkRegistry.registerBLSPublicKey(alice, signedMessageHash, pubKeyG1, pubKeyG2); + blsApkRegistry.registerBLSPublicKey(alice, pubkeyRegistrationParams); } function testRegisterBLSPublicKey_PkRegistered_Reverts() public { testRegisterBLSPublicKey(); - signedMessageHash = _signMessage(bob); // same private key different operator + pubkeyRegistrationParams.pubkeyRegistrationSignature = _signMessage(bob); // same private key different operator vm.prank(address(registryCoordinator)); vm.expectRevert(bytes("BLSApkRegistry.registerBLSPublicKey: public key already registered")); - blsApkRegistry.registerBLSPublicKey(bob, signedMessageHash, pubKeyG1, pubKeyG2); + blsApkRegistry.registerBLSPublicKey(bob, pubkeyRegistrationParams); } function _signMessage(address signer) internal view returns(BN254.G1Point memory) { diff --git a/test/unit/RegistryCoordinatorUnit.t.sol b/test/unit/RegistryCoordinatorUnit.t.sol index a9ebc493..d7a81b2f 100644 --- a/test/unit/RegistryCoordinatorUnit.t.sol +++ b/test/unit/RegistryCoordinatorUnit.t.sol @@ -115,9 +115,6 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { function testRegisterOperatorWithCoordinator_WhenPaused_Reverts() public { bytes memory emptyQuorumNumbers = new bytes(0); - BN254.G1Point memory pubkeyRegistrationSignature; - BN254.G1Point memory pubkeyG1; - BN254.G2Point memory pubkeyG2; // pause registerOperator cheats.prank(pauser); @@ -125,51 +122,39 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { cheats.startPrank(defaultOperator); cheats.expectRevert(bytes("Pausable: index is paused")); - registryCoordinator.registerOperator(emptyQuorumNumbers, defaultSocket, pubkeyRegistrationSignature, pubkeyG1, pubkeyG2); + registryCoordinator.registerOperator(emptyQuorumNumbers, defaultSocket, pubkeyRegistrationParams); } function testRegisterOperatorWithCoordinator_EmptyQuorumNumbers_Reverts() public { bytes memory emptyQuorumNumbers = new bytes(0); - BN254.G1Point memory pubkeyRegistrationSignature; - BN254.G1Point memory pubkeyG1; - BN254.G2Point memory pubkeyG2; cheats.expectRevert("RegistryCoordinator._registerOperator: bitmap cannot be 0"); cheats.prank(defaultOperator); - registryCoordinator.registerOperator(emptyQuorumNumbers, defaultSocket, pubkeyRegistrationSignature, pubkeyG1, pubkeyG2); + registryCoordinator.registerOperator(emptyQuorumNumbers, defaultSocket, pubkeyRegistrationParams); } function testRegisterOperatorWithCoordinator_QuorumNumbersTooLarge_Reverts() public { bytes memory quorumNumbersTooLarge = new bytes(1); quorumNumbersTooLarge[0] = 0xC0; - BN254.G1Point memory pubkeyRegistrationSignature; - BN254.G1Point memory pubkeyG1; - BN254.G2Point memory pubkeyG2; cheats.expectRevert("BitmapUtils.orderedBytesArrayToBitmap: bitmap exceeds max value"); cheats.prank(defaultOperator); - registryCoordinator.registerOperator(quorumNumbersTooLarge, defaultSocket, pubkeyRegistrationSignature, pubkeyG1, pubkeyG2); + registryCoordinator.registerOperator(quorumNumbersTooLarge, defaultSocket, pubkeyRegistrationParams); } function testRegisterOperatorWithCoordinator_QuorumNotCreated_Reverts() public { _deployMockEigenLayerAndAVS(10); bytes memory quorumNumbersNotCreated = new bytes(1); quorumNumbersNotCreated[0] = 0x0B; - BN254.G1Point memory pubkeyRegistrationSignature; - BN254.G1Point memory pubkeyG1; - BN254.G2Point memory pubkeyG2; cheats.prank(defaultOperator); cheats.expectRevert("BitmapUtils.orderedBytesArrayToBitmap: bitmap exceeds max value"); - registryCoordinator.registerOperator(quorumNumbersNotCreated, defaultSocket, pubkeyRegistrationSignature, pubkeyG1, pubkeyG2); + registryCoordinator.registerOperator(quorumNumbersNotCreated, defaultSocket, pubkeyRegistrationParams); } function testRegisterOperatorWithCoordinatorForSingleQuorum_Valid() public { bytes memory quorumNumbers = new bytes(1); quorumNumbers[0] = bytes1(defaultQuorumNumber); - BN254.G1Point memory pubkeyRegistrationSignature; - BN254.G1Point memory pubkeyG1; - BN254.G2Point memory pubkeyG2; stakeRegistry.setOperatorWeight(uint8(quorumNumbers[0]), defaultOperator, defaultStake); @@ -184,7 +169,7 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { uint256 gasBefore = gasleft(); cheats.prank(defaultOperator); - registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationSignature, pubkeyG1, pubkeyG2); + registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams); uint256 gasAfter = gasleft(); emit log_named_uint("gasUsed", gasBefore - gasAfter); @@ -213,9 +198,6 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { quorumBitmap = quorumBitmap & MAX_QUORUM_BITMAP; cheats.assume(quorumBitmap != 0); bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); - BN254.G1Point memory pubkeyRegistrationSignature; - BN254.G1Point memory pubkeyG1; - BN254.G2Point memory pubkeyG2; for (uint i = 0; i < quorumNumbers.length; i++) { stakeRegistry.setOperatorWeight(uint8(quorumNumbers[i]), defaultOperator, defaultStake); @@ -239,7 +221,7 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { uint256 gasBefore = gasleft(); cheats.prank(defaultOperator); - registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationSignature, pubkeyG1, pubkeyG2); + registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams); uint256 gasAfter = gasleft(); emit log_named_uint("gasUsed", gasBefore - gasAfter); emit log_named_uint("numQuorums", quorumNumbers.length); @@ -269,14 +251,11 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { bytes memory quorumNumbers = new bytes(1); quorumNumbers[0] = bytes1(defaultQuorumNumber); - BN254.G1Point memory pubkeyRegistrationSignature; - BN254.G1Point memory pubkeyG1; - BN254.G2Point memory pubkeyG2; stakeRegistry.setOperatorWeight(uint8(quorumNumbers[0]), defaultOperator, defaultStake); cheats.prank(defaultOperator); cheats.roll(registrationBlockNumber); - registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationSignature, pubkeyG1, pubkeyG2); + registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams); bytes memory newQuorumNumbers = new bytes(1); newQuorumNumbers[0] = bytes1(defaultQuorumNumber+1); @@ -292,7 +271,7 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { emit QuorumIndexUpdate(defaultOperatorId, uint8(newQuorumNumbers[0]), 0); cheats.roll(nextRegistrationBlockNumber); cheats.prank(defaultOperator); - registryCoordinator.registerOperator(newQuorumNumbers, defaultSocket, pubkeyRegistrationSignature, pubkeyG1, pubkeyG2); + registryCoordinator.registerOperator(newQuorumNumbers, defaultSocket, pubkeyRegistrationParams); uint256 quorumBitmap = BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers) | BitmapUtils.orderedBytesArrayToBitmap(newQuorumNumbers); @@ -329,9 +308,6 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { bytes memory quorumNumbers = new bytes(1); quorumNumbers[0] = bytes1(defaultQuorumNumber); - BN254.G1Point memory pubkeyRegistrationSignature; - BN254.G1Point memory pubkeyG1; - BN254.G2Point memory pubkeyG2; uint256 quorumBitmap = BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers); @@ -353,7 +329,7 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { cheats.prank(operatorToRegister); cheats.expectRevert("RegistryCoordinator.registerOperator: operator count exceeds maximum"); - registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationSignature, pubkeyG1, pubkeyG2); + registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams); } function testRegisterOperatorWithCoordinator_RegisteredOperatorForSameQuorums_Reverts() public { @@ -362,19 +338,16 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { bytes memory quorumNumbers = new bytes(1); quorumNumbers[0] = bytes1(defaultQuorumNumber); - BN254.G1Point memory pubkeyRegistrationSignature; - BN254.G1Point memory pubkeyG1; - BN254.G2Point memory pubkeyG2; stakeRegistry.setOperatorWeight(uint8(quorumNumbers[0]), defaultOperator, defaultStake); cheats.prank(defaultOperator); cheats.roll(registrationBlockNumber); - registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationSignature, pubkeyG1, pubkeyG2); + registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams); cheats.prank(defaultOperator); cheats.roll(nextRegistrationBlockNumber); cheats.expectRevert("RegistryCoordinator._registerOperator: operator already registered for some quorums being registered for"); - registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationSignature, pubkeyG1, pubkeyG2); + registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams); } function testDeregisterOperatorWithCoordinator_WhenPaused_Reverts() public { @@ -424,9 +397,6 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { bytes memory quorumNumbers = new bytes(1); quorumNumbers[0] = bytes1(defaultQuorumNumber); - BN254.G1Point memory pubkeyRegistrationSignature; - BN254.G1Point memory pubkeyG1; - BN254.G2Point memory pubkeyG2; stakeRegistry.setOperatorWeight(uint8(quorumNumbers[0]), defaultOperator, defaultStake); @@ -434,7 +404,7 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { cheats.roll(registrationBlockNumber); - registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationSignature, pubkeyG1, pubkeyG2); + registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams); uint256 quorumBitmap = BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers); @@ -471,9 +441,6 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { function testDeregisterOperatorWithCoordinatorForFuzzedQuorumAndSingleOperator_Valid(uint256 quorumBitmap) public { uint32 registrationBlockNumber = 100; uint32 deregistrationBlockNumber = 200; - BN254.G1Point memory pubkeyRegistrationSignature; - BN254.G1Point memory pubkeyG1; - BN254.G2Point memory pubkeyG2; quorumBitmap = quorumBitmap & MAX_QUORUM_BITMAP; cheats.assume(quorumBitmap != 0); @@ -487,7 +454,7 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { cheats.roll(registrationBlockNumber); - registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationSignature, pubkeyG1, pubkeyG2); + registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams); cheats.expectEmit(true, true, true, true, address(blsApkRegistry)); emit OperatorRemovedFromQuorums(defaultOperator, quorumNumbers); @@ -604,9 +571,6 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { bytes memory quorumNumbers = new bytes(1); quorumNumbers[0] = bytes1(defaultQuorumNumber); - BN254.G1Point memory pubkeyRegistrationSignature; - BN254.G1Point memory pubkeyG1; - BN254.G2Point memory pubkeyG2; cheats.startPrank(defaultOperator); @@ -617,7 +581,7 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0); // re-register the operator - registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationSignature, pubkeyG1, pubkeyG2); + registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams); // check success of registration uint256 quorumBitmap = BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers); @@ -715,12 +679,16 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { emit QuorumIndexUpdate(operatorToRegisterId, defaultQuorumNumber, numOperators - 1); { + BN254.G1Point memory pubkeyRegistrationSignature; + BN254.G1Point memory pubkeyG1; + BN254.G2Point memory pubkeyG2; ISignatureUtils.SignatureWithSaltAndExpiry memory signatureWithExpiry = _signOperatorChurnApproval(operatorToRegisterId, operatorKickParams, defaultSalt, block.timestamp + 10); cheats.prank(operatorToRegister); uint256 gasBefore = gasleft(); registryCoordinator.registerOperatorWithChurn( quorumNumbers, - defaultSocket, + defaultSocket, + pubkeyRegistrationParams, operatorKickParams, signatureWithExpiry ); @@ -769,7 +737,13 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { ISignatureUtils.SignatureWithSaltAndExpiry memory signatureWithExpiry = _signOperatorChurnApproval(operatorToRegisterId, operatorKickParams, defaultSalt, block.timestamp + 10); cheats.prank(operatorToRegister); cheats.expectRevert("RegistryCoordinator._validateChurn: incoming operator has insufficient stake for churn"); - registryCoordinator.registerOperatorWithChurn(quorumNumbers, defaultSocket, operatorKickParams, signatureWithExpiry); + registryCoordinator.registerOperatorWithChurn( + quorumNumbers, + defaultSocket, + pubkeyRegistrationParams, + operatorKickParams, + signatureWithExpiry + ); } function testRegisterOperatorWithCoordinatorWithKicks_LessThanKickBIPsOfTotalStake_Reverts(uint256 pseudoRandomNumber) public { @@ -792,7 +766,13 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { ISignatureUtils.SignatureWithSaltAndExpiry memory signatureWithExpiry = _signOperatorChurnApproval(operatorToRegisterId, operatorKickParams, defaultSalt, block.timestamp + 10); cheats.prank(operatorToRegister); cheats.expectRevert("RegistryCoordinator._validateChurn: cannot kick operator with more than kickBIPsOfTotalStake"); - registryCoordinator.registerOperatorWithChurn(quorumNumbers, defaultSocket, operatorKickParams, signatureWithExpiry); + registryCoordinator.registerOperatorWithChurn( + quorumNumbers, + defaultSocket, + pubkeyRegistrationParams, + operatorKickParams, + signatureWithExpiry + ); } function testRegisterOperatorWithCoordinatorWithKicks_InvalidSignatures_Reverts(uint256 pseudoRandomNumber) public { @@ -815,7 +795,13 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { signatureWithSaltAndExpiry.salt = defaultSalt; cheats.prank(operatorToRegister); cheats.expectRevert("ECDSA: invalid signature"); - registryCoordinator.registerOperatorWithChurn(quorumNumbers, defaultSocket, operatorKickParams, signatureWithSaltAndExpiry); + registryCoordinator.registerOperatorWithChurn( + quorumNumbers, + defaultSocket, + pubkeyRegistrationParams, + operatorKickParams, + signatureWithSaltAndExpiry + ); } function testRegisterOperatorWithCoordinatorWithKicks_ExpiredSignatures_Reverts(uint256 pseudoRandomNumber) public { @@ -836,21 +822,24 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { ISignatureUtils.SignatureWithSaltAndExpiry memory signatureWithSaltAndExpiry = _signOperatorChurnApproval(operatorToRegisterId, operatorKickParams, defaultSalt, block.timestamp - 1); cheats.prank(operatorToRegister); cheats.expectRevert("RegistryCoordinator._verifyChurnApproverSignature: churnApprover signature expired"); - registryCoordinator.registerOperatorWithChurn(quorumNumbers, defaultSocket, operatorKickParams, signatureWithSaltAndExpiry); + registryCoordinator.registerOperatorWithChurn( + quorumNumbers, + defaultSocket, + pubkeyRegistrationParams, + 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); - BN254.G1Point memory pubkeyRegistrationSignature; - BN254.G1Point memory pubkeyG1; - BN254.G2Point memory pubkeyG2; stakeRegistry.setOperatorWeight(uint8(quorumNumbers[0]), defaultOperator, defaultStake); cheats.prank(defaultOperator); - registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationSignature, pubkeyG1, pubkeyG2); + registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams); cheats.expectEmit(true, true, true, true, address(blsApkRegistry)); emit OperatorRemovedFromQuorums(defaultOperator, quorumNumbers); @@ -879,16 +868,13 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { bytes memory quorumNumbers = new bytes(2); quorumNumbers[0] = bytes1(defaultQuorumNumber); quorumNumbers[1] = bytes1(defaultQuorumNumber + 1); - BN254.G1Point memory pubkeyRegistrationSignature; - BN254.G1Point memory pubkeyG1; - BN254.G2Point memory pubkeyG2; for (uint i = 0; i < quorumNumbers.length; i++) { stakeRegistry.setOperatorWeight(uint8(quorumNumbers[i]), defaultOperator, defaultStake); } cheats.prank(defaultOperator); - registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationSignature, pubkeyG1, pubkeyG2); + registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams); // eject from only first quorum bytes memory quorumNumbersToEject = new bytes(1); @@ -921,14 +907,11 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { function testEjectOperatorFromCoordinator_NotEjector_Reverts() public { bytes memory quorumNumbers = new bytes(1); quorumNumbers[0] = bytes1(defaultQuorumNumber); - BN254.G1Point memory pubkeyRegistrationSignature; - BN254.G1Point memory pubkeyG1; - BN254.G2Point memory pubkeyG2; stakeRegistry.setOperatorWeight(uint8(quorumNumbers[0]), defaultOperator, defaultStake); cheats.prank(defaultOperator); - registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationSignature, pubkeyG1, pubkeyG2); + registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams); cheats.expectRevert("RegistryCoordinator.onlyEjector: caller is not the ejector"); cheats.prank(defaultOperator); diff --git a/test/utils/MockAVSDeployer.sol b/test/utils/MockAVSDeployer.sol index 5112746d..89fac517 100644 --- a/test/utils/MockAVSDeployer.sol +++ b/test/utils/MockAVSDeployer.sol @@ -95,6 +95,8 @@ contract MockAVSDeployer is Test { uint32 registrationBlockNumber = 100; uint32 blocksBetweenRegistrations = 10; + IBLSApkRegistry.PubkeyRegistrationParams pubkeyRegistrationParams; + struct OperatorMetadata { uint256 quorumBitmap; address operator; @@ -291,17 +293,13 @@ contract MockAVSDeployer is Test { blsApkRegistry.setBLSPublicKey(operator, pubKey); - BN254.G1Point memory pubkeyRegistrationSignature; - BN254.G1Point memory pubkeyG1; - BN254.G2Point memory pubkeyG2; - 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.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationSignature, pubkeyG1, pubkeyG2); + registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams); } /** @@ -313,17 +311,13 @@ contract MockAVSDeployer is Test { blsApkRegistry.setBLSPublicKey(operator, pubKey); - BN254.G1Point memory pubkeyRegistrationSignature; - BN254.G1Point memory pubkeyG1; - BN254.G2Point memory pubkeyG2; - 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.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationSignature, pubkeyG1, pubkeyG2); + registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams); } function _registerRandomOperators(uint256 pseudoRandomNumber) internal returns(OperatorMetadata[] memory, uint256[][] memory) { From 5b55366dad4f4173949ea272e3140fa6db64f3b5 Mon Sep 17 00:00:00 2001 From: ChaoticWalrus <93558947+ChaoticWalrus@users.noreply.github.com> Date: Mon, 11 Dec 2023 17:27:39 -0800 Subject: [PATCH 063/101] fix: address stack-too-deep issue in RegistryCoordinator --- src/RegistryCoordinator.sol | 7 +++---- test/unit/RegistryCoordinatorUnit.t.sol | 3 --- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/RegistryCoordinator.sol b/src/RegistryCoordinator.sol index 68b5d4bd..5fc49713 100644 --- a/src/RegistryCoordinator.sol +++ b/src/RegistryCoordinator.sol @@ -233,9 +233,8 @@ contract RegistryCoordinator is EIP712, Initializable, IRegistryCoordinator, ISo uint256 kickIndex = 0; for (uint256 i = 0; i < quorumNumbers.length; i++) { - uint8 quorumNumber = uint8(quorumNumbers[i]); - - OperatorSetParam memory operatorSetParams = _quorumParams[quorumNumber]; + // reference: uint8 quorumNumber = uint8(quorumNumbers[i]); + OperatorSetParam memory operatorSetParams = _quorumParams[uint8(quorumNumbers[i])]; /** * If the new operator count for any quorum exceeds the maximum, validate @@ -243,7 +242,7 @@ contract RegistryCoordinator is EIP712, Initializable, IRegistryCoordinator, ISo */ if (results.numOperatorsPerQuorum[i] > operatorSetParams.maxOperatorCount) { _validateChurn({ - quorumNumber: quorumNumber, + quorumNumber: uint8(quorumNumbers[i]), totalQuorumStake: results.totalStakes[i], newOperator: msg.sender, newOperatorStake: results.operatorStakes[i], diff --git a/test/unit/RegistryCoordinatorUnit.t.sol b/test/unit/RegistryCoordinatorUnit.t.sol index d7a81b2f..cda318cb 100644 --- a/test/unit/RegistryCoordinatorUnit.t.sol +++ b/test/unit/RegistryCoordinatorUnit.t.sol @@ -679,9 +679,6 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { emit QuorumIndexUpdate(operatorToRegisterId, defaultQuorumNumber, numOperators - 1); { - BN254.G1Point memory pubkeyRegistrationSignature; - BN254.G1Point memory pubkeyG1; - BN254.G2Point memory pubkeyG2; ISignatureUtils.SignatureWithSaltAndExpiry memory signatureWithExpiry = _signOperatorChurnApproval(operatorToRegisterId, operatorKickParams, defaultSalt, block.timestamp + 10); cheats.prank(operatorToRegister); uint256 gasBefore = gasleft(); From 2ac5c901626bf9e4f28cca53a454b4375422f90e Mon Sep 17 00:00:00 2001 From: ChaoticWalrus <93558947+ChaoticWalrus@users.noreply.github.com> Date: Tue, 12 Dec 2023 13:51:17 -0800 Subject: [PATCH 064/101] chore: delete unused index see reasoning here https://github.com/Layr-Labs/eigenlayer-middleware/pull/100#discussion_r1424577318 this allows undoing a previous fix for a stack-too-deep error, which... helps make the logic a little clearer / easier to parse here. --- src/RegistryCoordinator.sol | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/RegistryCoordinator.sol b/src/RegistryCoordinator.sol index 5fc49713..9941f269 100644 --- a/src/RegistryCoordinator.sol +++ b/src/RegistryCoordinator.sol @@ -231,10 +231,9 @@ contract RegistryCoordinator is EIP712, Initializable, IRegistryCoordinator, ISo socket: socket }); - uint256 kickIndex = 0; for (uint256 i = 0; i < quorumNumbers.length; i++) { - // reference: uint8 quorumNumber = uint8(quorumNumbers[i]); - OperatorSetParam memory operatorSetParams = _quorumParams[uint8(quorumNumbers[i])]; + uint8 quorumNumber = uint8(quorumNumbers[i]); + OperatorSetParam memory operatorSetParams = _quorumParams[quorumNumber]; /** * If the new operator count for any quorum exceeds the maximum, validate @@ -242,7 +241,7 @@ contract RegistryCoordinator is EIP712, Initializable, IRegistryCoordinator, ISo */ if (results.numOperatorsPerQuorum[i] > operatorSetParams.maxOperatorCount) { _validateChurn({ - quorumNumber: uint8(quorumNumbers[i]), + quorumNumber: quorumNumber, totalQuorumStake: results.totalStakes[i], newOperator: msg.sender, newOperatorStake: results.operatorStakes[i], @@ -251,7 +250,6 @@ contract RegistryCoordinator is EIP712, Initializable, IRegistryCoordinator, ISo }); _deregisterOperator(operatorKickParams[i].operator, quorumNumbers[i:i+1]); - kickIndex++; } } } From f9d020a34ffbfc5eadecb0d9d099c518c265db8b Mon Sep 17 00:00:00 2001 From: ChaoticWalrus <93558947+ChaoticWalrus@users.noreply.github.com> Date: Tue, 12 Dec 2023 13:56:39 -0800 Subject: [PATCH 065/101] feat: optimization for fetching operatorId NOTE: this changes the IBLSApkRegistry interface to make `registerBLSPublicKey` return a bytes32 object --- src/BLSApkRegistry.sol | 3 ++- src/RegistryCoordinator.sol | 12 ++++++------ src/interfaces/IBLSApkRegistry.sol | 2 +- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/BLSApkRegistry.sol b/src/BLSApkRegistry.sol index 03abe2b5..1dd52d2b 100644 --- a/src/BLSApkRegistry.sol +++ b/src/BLSApkRegistry.sol @@ -101,7 +101,7 @@ contract BLSApkRegistry is BLSApkRegistryStorage { function registerBLSPublicKey( address operator, PubkeyRegistrationParams calldata pubkeyRegistrationParams - ) external onlyRegistryCoordinator { + ) external onlyRegistryCoordinator returns (bytes32 operatorId) { bytes32 pubkeyHash = BN254.hashG1Point(pubkeyRegistrationParams.pubkeyG1); require( pubkeyHash != ZERO_PK_HASH, "BLSApkRegistry.registerBLSPublicKey: cannot register zero pubkey" @@ -143,6 +143,7 @@ contract BLSApkRegistry is BLSApkRegistryStorage { pubkeyHashToOperator[pubkeyHash] = operator; emit NewPubkeyRegistration(operator, pubkeyRegistrationParams.pubkeyG1, pubkeyRegistrationParams.pubkeyG2); + return pubkeyHash; } /******************************************************************************* diff --git a/src/RegistryCoordinator.sol b/src/RegistryCoordinator.sol index 9941f269..f4c4184e 100644 --- a/src/RegistryCoordinator.sol +++ b/src/RegistryCoordinator.sol @@ -159,10 +159,10 @@ contract RegistryCoordinator is EIP712, Initializable, IRegistryCoordinator, ISo * IF the operator has never registered a pubkey before, THEN register their pubkey * OTHERWISE, simply ignore the provided `pubkeyRegistrationParams` */ - if (blsApkRegistry.operatorToPubkeyHash(msg.sender) == 0) { - blsApkRegistry.registerBLSPublicKey(msg.sender, pubkeyRegistrationParams); - } bytes32 operatorId = blsApkRegistry.getOperatorId(msg.sender); + if (operatorId == 0) { + operatorId = blsApkRegistry.registerBLSPublicKey(msg.sender, pubkeyRegistrationParams); + } // Register the operator in each of the registry contracts RegisterResults memory results = _registerOperator({ @@ -211,10 +211,10 @@ contract RegistryCoordinator is EIP712, Initializable, IRegistryCoordinator, ISo * IF the operator has never registered a pubkey before, THEN register their pubkey * OTHERWISE, simply ignore the `pubkeyRegistrationSignature`, `pubkeyG1`, and `pubkeyG2` inputs */ - if (blsApkRegistry.operatorToPubkeyHash(msg.sender) == 0) { - blsApkRegistry.registerBLSPublicKey(msg.sender, pubkeyRegistrationParams); - } bytes32 operatorId = blsApkRegistry.getOperatorId(msg.sender); + if (operatorId == 0) { + operatorId = blsApkRegistry.registerBLSPublicKey(msg.sender, pubkeyRegistrationParams); + } // Verify the churn approver's signature for the registering operator and kick params _verifyChurnApproverSignature({ diff --git a/src/interfaces/IBLSApkRegistry.sol b/src/interfaces/IBLSApkRegistry.sol index 3c5a0c27..47a9dd47 100644 --- a/src/interfaces/IBLSApkRegistry.sol +++ b/src/interfaces/IBLSApkRegistry.sol @@ -103,7 +103,7 @@ interface IBLSApkRegistry is IRegistry { function registerBLSPublicKey( address operator, PubkeyRegistrationParams calldata pubkeyRegistrationParams - ) external; + ) external returns (bytes32 operatorId); /** * @notice Returns the pubkey and pubkey hash of an operator From 96311bba51ec332274336dd026693016d9b8d619 Mon Sep 17 00:00:00 2001 From: ChaoticWalrus <93558947+ChaoticWalrus@users.noreply.github.com> Date: Tue, 12 Dec 2023 14:00:46 -0800 Subject: [PATCH 066/101] chore: shorten variable name that is quite clear from context `pubkeyRegistrationParams` => `params` see discussion here: https://github.com/Layr-Labs/eigenlayer-middleware/pull/100#discussion_r1424469756 --- src/BLSApkRegistry.sol | 26 +++++++++++++------------- src/RegistryCoordinator.sol | 18 +++++++++--------- src/interfaces/IBLSApkRegistry.sol | 4 ++-- 3 files changed, 24 insertions(+), 24 deletions(-) diff --git a/src/BLSApkRegistry.sol b/src/BLSApkRegistry.sol index 1dd52d2b..060de2d0 100644 --- a/src/BLSApkRegistry.sol +++ b/src/BLSApkRegistry.sol @@ -96,13 +96,13 @@ contract BLSApkRegistry is BLSApkRegistryStorage { /** * @notice Called by the RegistryCoordinator register an operator as the owner of a BLS public key. * @param operator is the operator for whom the key is being registered - * @param pubkeyRegistrationParams contains the G1 & G2 public keys of the operator, and a signature proving their ownership + * @param params contains the G1 & G2 public keys of the operator, and a signature proving their ownership */ function registerBLSPublicKey( address operator, - PubkeyRegistrationParams calldata pubkeyRegistrationParams + PubkeyRegistrationParams calldata params ) external onlyRegistryCoordinator returns (bytes32 operatorId) { - bytes32 pubkeyHash = BN254.hashG1Point(pubkeyRegistrationParams.pubkeyG1); + bytes32 pubkeyHash = BN254.hashG1Point(params.pubkeyG1); require( pubkeyHash != ZERO_PK_HASH, "BLSApkRegistry.registerBLSPublicKey: cannot register zero pubkey" ); @@ -120,29 +120,29 @@ contract BLSApkRegistry is BLSApkRegistryStorage { // gamma = h(sigma, P, P', H(m)) uint256 gamma = uint256(keccak256(abi.encodePacked( - pubkeyRegistrationParams.pubkeyRegistrationSignature.X, - pubkeyRegistrationParams.pubkeyRegistrationSignature.Y, - pubkeyRegistrationParams.pubkeyG1.X, - pubkeyRegistrationParams.pubkeyG1.Y, - pubkeyRegistrationParams.pubkeyG2.X, - pubkeyRegistrationParams.pubkeyG2.Y, + params.pubkeyRegistrationSignature.X, + params.pubkeyRegistrationSignature.Y, + params.pubkeyG1.X, + params.pubkeyG1.Y, + params.pubkeyG2.X, + params.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( - pubkeyRegistrationParams.pubkeyRegistrationSignature.plus(pubkeyRegistrationParams.pubkeyG1.scalar_mul(gamma)), + params.pubkeyRegistrationSignature.plus(params.pubkeyG1.scalar_mul(gamma)), BN254.negGeneratorG2(), messageHash.plus(BN254.generatorG1().scalar_mul(gamma)), - pubkeyRegistrationParams.pubkeyG2 + params.pubkeyG2 ), "BLSApkRegistry.registerBLSPublicKey: either the G1 signature is wrong, or G1 and G2 private key do not match"); - operatorToPubkey[operator] = pubkeyRegistrationParams.pubkeyG1; + operatorToPubkey[operator] = params.pubkeyG1; operatorToPubkeyHash[operator] = pubkeyHash; pubkeyHashToOperator[pubkeyHash] = operator; - emit NewPubkeyRegistration(operator, pubkeyRegistrationParams.pubkeyG1, pubkeyRegistrationParams.pubkeyG2); + emit NewPubkeyRegistration(operator, params.pubkeyG1, params.pubkeyG2); return pubkeyHash; } diff --git a/src/RegistryCoordinator.sol b/src/RegistryCoordinator.sol index f4c4184e..0eef3632 100644 --- a/src/RegistryCoordinator.sol +++ b/src/RegistryCoordinator.sol @@ -147,21 +147,21 @@ contract RegistryCoordinator is EIP712, Initializable, IRegistryCoordinator, ISo * @notice Registers msg.sender as an operator for one or more quorums. If any quorum reaches its maximum * operator capacity, this method will fail. * @param quorumNumbers is an ordered byte array containing the quorum numbers being registered for - * @param pubkeyRegistrationParams contains the G1 & G2 public keys of the operator, and a signature proving their ownership - * @dev the `pubkeyRegistrationParams` input param is ignored if the caller has previously registered a public key + * @param params contains the G1 & G2 public keys of the operator, and a signature proving their ownership + * @dev the `params` input param is ignored if the caller has previously registered a public key */ function registerOperator( bytes calldata quorumNumbers, string calldata socket, - IBLSApkRegistry.PubkeyRegistrationParams calldata pubkeyRegistrationParams + IBLSApkRegistry.PubkeyRegistrationParams calldata params ) external onlyWhenNotPaused(PAUSED_REGISTER_OPERATOR) { /** * IF the operator has never registered a pubkey before, THEN register their pubkey - * OTHERWISE, simply ignore the provided `pubkeyRegistrationParams` + * OTHERWISE, simply ignore the provided `params` */ bytes32 operatorId = blsApkRegistry.getOperatorId(msg.sender); if (operatorId == 0) { - operatorId = blsApkRegistry.registerBLSPublicKey(msg.sender, pubkeyRegistrationParams); + operatorId = blsApkRegistry.registerBLSPublicKey(msg.sender, params); } // Register the operator in each of the registry contracts @@ -192,16 +192,16 @@ contract RegistryCoordinator is EIP712, Initializable, IRegistryCoordinator, ISo * @notice Registers msg.sender as an operator for one or more quorums. If any quorum reaches its maximum operator * capacity, `operatorKickParams` is used to replace an old operator with the new one. * @param quorumNumbers is an ordered byte array containing the quorum numbers being registered for - * @param pubkeyRegistrationParams contains the G1 & G2 public keys of the operator, and a signature proving their ownership + * @param params contains the G1 & G2 public keys of the operator, and a signature proving their ownership * @param operatorKickParams are used to determine which operator is removed to maintain quorum capacity as the * operator registers for quorums. * @param churnApproverSignature is the signature of the churnApprover on the operator kick params - * @dev the `pubkeyRegistrationParams` input param is ignored if the caller has previously registered a public key + * @dev the `params` input param is ignored if the caller has previously registered a public key */ function registerOperatorWithChurn( bytes calldata quorumNumbers, string calldata socket, - IBLSApkRegistry.PubkeyRegistrationParams calldata pubkeyRegistrationParams, + IBLSApkRegistry.PubkeyRegistrationParams calldata params, OperatorKickParam[] calldata operatorKickParams, SignatureWithSaltAndExpiry memory churnApproverSignature ) external onlyWhenNotPaused(PAUSED_REGISTER_OPERATOR) { @@ -213,7 +213,7 @@ contract RegistryCoordinator is EIP712, Initializable, IRegistryCoordinator, ISo */ bytes32 operatorId = blsApkRegistry.getOperatorId(msg.sender); if (operatorId == 0) { - operatorId = blsApkRegistry.registerBLSPublicKey(msg.sender, pubkeyRegistrationParams); + operatorId = blsApkRegistry.registerBLSPublicKey(msg.sender, params); } // Verify the churn approver's signature for the registering operator and kick params diff --git a/src/interfaces/IBLSApkRegistry.sol b/src/interfaces/IBLSApkRegistry.sol index 47a9dd47..b35b45fe 100644 --- a/src/interfaces/IBLSApkRegistry.sol +++ b/src/interfaces/IBLSApkRegistry.sol @@ -98,11 +98,11 @@ interface IBLSApkRegistry is IRegistry { /** * @notice Called by the RegistryCoordinator register an operator as the owner of a BLS public key. * @param operator is the operator for whom the key is being registered - * @param pubkeyRegistrationParams contains the G1 & G2 public keys of the operator, and a signature proving their ownership + * @param params contains the G1 & G2 public keys of the operator, and a signature proving their ownership */ function registerBLSPublicKey( address operator, - PubkeyRegistrationParams calldata pubkeyRegistrationParams + PubkeyRegistrationParams calldata params ) external returns (bytes32 operatorId); /** From 266d426d03a14a0cf79778fc5a9cdc84595f161a Mon Sep 17 00:00:00 2001 From: ChaoticWalrus <93558947+ChaoticWalrus@users.noreply.github.com> Date: Tue, 12 Dec 2023 15:36:48 -0800 Subject: [PATCH 067/101] feat: use EIP712 for the pubkeyRegistrationMessageHash this commit also moves the definition to the RegistryCoordinator contract and updates the function naming to be clearer --- src/BLSApkRegistry.sol | 26 ++++++------------------- src/RegistryCoordinator.sol | 18 +++++++++++++++-- src/interfaces/IBLSApkRegistry.sol | 10 +++------- src/interfaces/IRegistryCoordinator.sol | 7 +++++++ test/ffi/BLSPubKeyCompendiumFFI.t.sol | 4 ++-- test/mocks/RegistryCoordinatorMock.sol | 6 ++++++ test/unit/BLSApkRegistryUnit.t.sol | 17 ++++++++++------ 7 files changed, 51 insertions(+), 37 deletions(-) diff --git a/src/BLSApkRegistry.sol b/src/BLSApkRegistry.sol index 060de2d0..5bb14fba 100644 --- a/src/BLSApkRegistry.sol +++ b/src/BLSApkRegistry.sol @@ -97,10 +97,12 @@ contract BLSApkRegistry is BLSApkRegistryStorage { * @notice Called by the RegistryCoordinator register an operator as the owner of a BLS public key. * @param operator is the operator for whom the key is being registered * @param params contains the G1 & G2 public keys of the operator, and a signature proving their ownership + * @param pubkeyRegistrationMessageHash is a hash that the operator must sign to prove key ownership */ function registerBLSPublicKey( address operator, - PubkeyRegistrationParams calldata params + PubkeyRegistrationParams calldata params, + BN254.G1Point calldata pubkeyRegistrationMessageHash ) external onlyRegistryCoordinator returns (bytes32 operatorId) { bytes32 pubkeyHash = BN254.hashG1Point(params.pubkeyG1); require( @@ -115,9 +117,6 @@ contract BLSApkRegistry is BLSApkRegistryStorage { "BLSApkRegistry.registerBLSPublicKey: public key already registered" ); - // H(m) - BN254.G1Point memory messageHash = getMessageHash(operator); - // gamma = h(sigma, P, P', H(m)) uint256 gamma = uint256(keccak256(abi.encodePacked( params.pubkeyRegistrationSignature.X, @@ -126,15 +125,15 @@ contract BLSApkRegistry is BLSApkRegistryStorage { params.pubkeyG1.Y, params.pubkeyG2.X, params.pubkeyG2.Y, - messageHash.X, - messageHash.Y + pubkeyRegistrationMessageHash.X, + pubkeyRegistrationMessageHash.Y ))) % BN254.FR_MODULUS; // e(sigma + P * gamma, [-1]_2) = e(H(m) + [1]_1 * gamma, P') require(BN254.pairing( params.pubkeyRegistrationSignature.plus(params.pubkeyG1.scalar_mul(gamma)), BN254.negGeneratorG2(), - messageHash.plus(BN254.generatorG1().scalar_mul(gamma)), + pubkeyRegistrationMessageHash.plus(BN254.generatorG1().scalar_mul(gamma)), params.pubkeyG2 ), "BLSApkRegistry.registerBLSPublicKey: either the G1 signature is wrong, or G1 and G2 private key do not match"); @@ -215,19 +214,6 @@ contract BLSApkRegistry is BLSApkRegistryStorage { return (pubkey, pubkeyHash); } - /** - * @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" - ))); - } - /** * @notice Returns the indices of the quorumApks index at `blockNumber` for the provided `quorumNumbers` * @dev Returns the current indices if `blockNumber >= block.number` diff --git a/src/RegistryCoordinator.sol b/src/RegistryCoordinator.sol index 0eef3632..c96e969b 100644 --- a/src/RegistryCoordinator.sol +++ b/src/RegistryCoordinator.sol @@ -35,6 +35,8 @@ contract RegistryCoordinator is EIP712, Initializable, IRegistryCoordinator, ISo /// @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,bytes32[] operatorIdsToSwap)"); + /// @notice The EIP-712 typehash used for registering BLS public keys + bytes32 public constant PUBKEY_REGISTRATION_TYPEHASH = keccak256("BN254PubkeyRegistration(address operator)"); /// @notice The maximum value of a quorum bitmap uint256 internal constant MAX_QUORUM_BITMAP = type(uint192).max; /// @notice The basis point denominator @@ -161,7 +163,7 @@ contract RegistryCoordinator is EIP712, Initializable, IRegistryCoordinator, ISo */ bytes32 operatorId = blsApkRegistry.getOperatorId(msg.sender); if (operatorId == 0) { - operatorId = blsApkRegistry.registerBLSPublicKey(msg.sender, params); + operatorId = blsApkRegistry.registerBLSPublicKey(msg.sender, params, pubkeyRegistrationMessageHash(msg.sender)); } // Register the operator in each of the registry contracts @@ -213,7 +215,7 @@ contract RegistryCoordinator is EIP712, Initializable, IRegistryCoordinator, ISo */ bytes32 operatorId = blsApkRegistry.getOperatorId(msg.sender); if (operatorId == 0) { - operatorId = blsApkRegistry.registerBLSPublicKey(msg.sender, params); + operatorId = blsApkRegistry.registerBLSPublicKey(msg.sender, params, pubkeyRegistrationMessageHash(msg.sender)); } // Verify the churn approver's signature for the registering operator and kick params @@ -760,4 +762,16 @@ contract RegistryCoordinator is EIP712, Initializable, IRegistryCoordinator, ISo // calculate the digest hash return _hashTypedDataV4(keccak256(abi.encode(OPERATOR_CHURN_APPROVAL_TYPEHASH, registeringOperatorId, operatorKickParams, salt, expiry))); } + + /** + * @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 pubkeyRegistrationMessageHash(address operator) public view returns (BN254.G1Point memory) { + return BN254.hashToG1( + _hashTypedDataV4( + keccak256(abi.encode(PUBKEY_REGISTRATION_TYPEHASH, operator)) + ) + ); + } } diff --git a/src/interfaces/IBLSApkRegistry.sol b/src/interfaces/IBLSApkRegistry.sol index b35b45fe..48fc680f 100644 --- a/src/interfaces/IBLSApkRegistry.sol +++ b/src/interfaces/IBLSApkRegistry.sol @@ -99,10 +99,12 @@ interface IBLSApkRegistry is IRegistry { * @notice Called by the RegistryCoordinator register an operator as the owner of a BLS public key. * @param operator is the operator for whom the key is being registered * @param params contains the G1 & G2 public keys of the operator, and a signature proving their ownership + * @param pubkeyRegistrationMessageHash is a hash that the operator must sign to prove key ownership */ function registerBLSPublicKey( address operator, - PubkeyRegistrationParams calldata params + PubkeyRegistrationParams calldata params, + BN254.G1Point calldata pubkeyRegistrationMessageHash ) external returns (bytes32 operatorId); /** @@ -111,12 +113,6 @@ interface IBLSApkRegistry is IRegistry { */ function getRegisteredPubkey(address operator) external view returns (BN254.G1Point memory, bytes32); - /** - * @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) external view returns (BN254.G1Point memory); - /// @notice Returns the current APK for the provided `quorumNumber ` function getApk(uint8 quorumNumber) external view returns (BN254.G1Point memory); diff --git a/src/interfaces/IRegistryCoordinator.sol b/src/interfaces/IRegistryCoordinator.sol index ca6f8629..6d2c153d 100644 --- a/src/interfaces/IRegistryCoordinator.sol +++ b/src/interfaces/IRegistryCoordinator.sol @@ -4,6 +4,7 @@ pragma solidity =0.8.12; import {IBLSApkRegistry} from "src/interfaces/IBLSApkRegistry.sol"; import {IStakeRegistry} from "src/interfaces/IStakeRegistry.sol"; import {IIndexRegistry} from "src/interfaces/IIndexRegistry.sol"; +import {BN254} from "src/libraries/BN254.sol"; /** * @title Interface for a contract that coordinates between various registries for an AVS. @@ -134,4 +135,10 @@ interface IRegistryCoordinator { /// @notice Returns the number of registries function numRegistries() external view returns (uint256); + + /** + * @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 pubkeyRegistrationMessageHash(address operator) external view returns (BN254.G1Point memory); } \ No newline at end of file diff --git a/test/ffi/BLSPubKeyCompendiumFFI.t.sol b/test/ffi/BLSPubKeyCompendiumFFI.t.sol index 0910ab5d..3bb75118 100644 --- a/test/ffi/BLSPubKeyCompendiumFFI.t.sol +++ b/test/ffi/BLSPubKeyCompendiumFFI.t.sol @@ -30,7 +30,7 @@ contract BLSApkRegistryFFITests is G2Operations { pubkeyRegistrationParams.pubkeyRegistrationSignature = _signMessage(alice); vm.prank(address(registryCoordinator)); - blsApkRegistry.registerBLSPublicKey(alice, pubkeyRegistrationParams); + blsApkRegistry.registerBLSPublicKey(alice, pubkeyRegistrationParams, registryCoordinator.pubkeyRegistrationMessageHash(alice)); assertEq(blsApkRegistry.operatorToPubkeyHash(alice), BN254.hashG1Point(pubkeyRegistrationParams.pubkeyG1), "pubkey hash not stored correctly"); @@ -45,7 +45,7 @@ contract BLSApkRegistryFFITests is G2Operations { } function _signMessage(address signer) internal view returns(BN254.G1Point memory) { - BN254.G1Point memory messageHash = blsApkRegistry.getMessageHash(signer); + BN254.G1Point memory messageHash = registryCoordinator.pubkeyRegistrationMessageHash(signer); return BN254.scalar_mul(messageHash, privKey); } } diff --git a/test/mocks/RegistryCoordinatorMock.sol b/test/mocks/RegistryCoordinatorMock.sol index 1dcf9383..2819ce3c 100644 --- a/test/mocks/RegistryCoordinatorMock.sol +++ b/test/mocks/RegistryCoordinatorMock.sol @@ -58,4 +58,10 @@ contract RegistryCoordinatorMock is IRegistryCoordinator { function registerOperator(bytes memory quorumNumbers, bytes calldata) external {} function deregisterOperator(bytes calldata quorumNumbers, bytes calldata) external {} + + function pubkeyRegistrationMessageHash(address operator) public view returns (BN254.G1Point memory) { + return BN254.hashToG1( + keccak256(abi.encode(operator)) + ); + } } diff --git a/test/unit/BLSApkRegistryUnit.t.sol b/test/unit/BLSApkRegistryUnit.t.sol index 683d59ff..0d8ed07a 100644 --- a/test/unit/BLSApkRegistryUnit.t.sol +++ b/test/unit/BLSApkRegistryUnit.t.sol @@ -292,8 +292,9 @@ contract BLSApkRegistryUnitTests is Test { // TODO: better organize / integrate tests migrated from `BLSPublicKeyCompendium` unit tests function testRegisterBLSPublicKey() public { pubkeyRegistrationParams.pubkeyRegistrationSignature = _signMessage(alice); + BN254.G1Point memory messageHash = registryCoordinator.pubkeyRegistrationMessageHash(alice); vm.prank(address(registryCoordinator)); - blsApkRegistry.registerBLSPublicKey(alice, pubkeyRegistrationParams); + blsApkRegistry.registerBLSPublicKey(alice, pubkeyRegistrationParams, messageHash); assertEq(blsApkRegistry.operatorToPubkeyHash(alice), BN254.hashG1Point(pubkeyRegistrationParams.pubkeyG1), "pubkey hash not stored correctly"); @@ -307,38 +308,42 @@ contract BLSApkRegistryUnitTests is Test { pubkeyRegistrationParams.pubkeyG1 = badPubkeyG1; + BN254.G1Point memory messageHash = registryCoordinator.pubkeyRegistrationMessageHash(alice); vm.prank(address(registryCoordinator)); vm.expectRevert(bytes("BLSApkRegistry.registerBLSPublicKey: either the G1 signature is wrong, or G1 and G2 private key do not match")); - blsApkRegistry.registerBLSPublicKey(alice, pubkeyRegistrationParams); + blsApkRegistry.registerBLSPublicKey(alice, pubkeyRegistrationParams, messageHash); } function testRegisterBLSPublicKey_BadSig_Reverts() public { pubkeyRegistrationParams.pubkeyRegistrationSignature = _signMessage(bob); // sign with wrong private key + BN254.G1Point memory messageHash = registryCoordinator.pubkeyRegistrationMessageHash(alice); vm.prank(address(registryCoordinator)); vm.expectRevert(bytes("BLSApkRegistry.registerBLSPublicKey: either the G1 signature is wrong, or G1 and G2 private key do not match")); - blsApkRegistry.registerBLSPublicKey(alice, pubkeyRegistrationParams); + blsApkRegistry.registerBLSPublicKey(alice, pubkeyRegistrationParams, messageHash); } function testRegisterBLSPublicKey_OpRegistered_Reverts() public { testRegisterBLSPublicKey(); // register alice + BN254.G1Point memory messageHash = registryCoordinator.pubkeyRegistrationMessageHash(alice); vm.prank(address(registryCoordinator)); vm.expectRevert(bytes("BLSApkRegistry.registerBLSPublicKey: operator already registered pubkey")); - blsApkRegistry.registerBLSPublicKey(alice, pubkeyRegistrationParams); + blsApkRegistry.registerBLSPublicKey(alice, pubkeyRegistrationParams, messageHash); } function testRegisterBLSPublicKey_PkRegistered_Reverts() public { testRegisterBLSPublicKey(); pubkeyRegistrationParams.pubkeyRegistrationSignature = _signMessage(bob); // same private key different operator + BN254.G1Point memory messageHash = registryCoordinator.pubkeyRegistrationMessageHash(bob); vm.prank(address(registryCoordinator)); vm.expectRevert(bytes("BLSApkRegistry.registerBLSPublicKey: public key already registered")); - blsApkRegistry.registerBLSPublicKey(bob, pubkeyRegistrationParams); + blsApkRegistry.registerBLSPublicKey(bob, pubkeyRegistrationParams, messageHash); } function _signMessage(address signer) internal view returns(BN254.G1Point memory) { - BN254.G1Point memory messageHash = blsApkRegistry.getMessageHash(signer); + BN254.G1Point memory messageHash = registryCoordinator.pubkeyRegistrationMessageHash(signer); return BN254.scalar_mul(messageHash, privKey); } From 19b23f83ae5107cb520e4f1c15ee747e797301a2 Mon Sep 17 00:00:00 2001 From: ChaoticWalrus <93558947+ChaoticWalrus@users.noreply.github.com> Date: Tue, 12 Dec 2023 15:43:21 -0800 Subject: [PATCH 068/101] chore: add NatSpec to getter function --- src/BLSApkRegistry.sol | 2 ++ src/interfaces/IBLSApkRegistry.sol | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/BLSApkRegistry.sol b/src/BLSApkRegistry.sol index 5bb14fba..71e3a77b 100644 --- a/src/BLSApkRegistry.sol +++ b/src/BLSApkRegistry.sol @@ -280,6 +280,8 @@ contract BLSApkRegistry is BLSApkRegistryStorage { return pubkeyHashToOperator[pubkeyHash]; } + /// @notice returns the ID used to identify the `operator` within this AVS + /// @dev Returns zero in the event that the `operator` has never registered for the AVS function getOperatorId(address operator) public view returns (bytes32) { return operatorToPubkeyHash[operator]; } diff --git a/src/interfaces/IBLSApkRegistry.sol b/src/interfaces/IBLSApkRegistry.sol index 48fc680f..916320ec 100644 --- a/src/interfaces/IBLSApkRegistry.sol +++ b/src/interfaces/IBLSApkRegistry.sol @@ -134,5 +134,7 @@ interface IBLSApkRegistry is IRegistry { */ function getApkHashAtBlockNumberAndIndex(uint8 quorumNumber, uint32 blockNumber, uint256 index) external view returns (bytes24); + /// @notice returns the ID used to identify the `operator` within this AVS. + /// @dev Returns zero in the event that the `operator` has never registered for the AVS function getOperatorId(address operator) external view returns (bytes32); } From 1ddc9730ddda231255bfc2c14897ae01ab882f89 Mon Sep 17 00:00:00 2001 From: Michael Sun <35479365+8sunyuan@users.noreply.github.com> Date: Fri, 8 Dec 2023 17:12:39 -0500 Subject: [PATCH 069/101] Feat(M2-Mainnet): StakeRegistry pull updates per quorum (#62) * feat: initial implementation w/o timestamp * feat: enforce stake updates for quorum in sigcheck * fix: update operator stake for just one quorum Additionally - renamed updateOperatorsPerQuorum to updateOperatorsForQuorum - sort by operator addresses instead of operatorIds * fix: requested changes and optimizations - sorted by ascending address instead of operatorId - sanitization checks on quorumNumbers, added quorumsAllExist modifier - performing `OperatorStatus.REGISTERED` checks now - Removed uneccesary bitmapToBytes calls and doing bytes slicing for quorumNumbers - using internal function and scoping to fix stack-too-deep * refactor: move quorumUpdateTimestamp to reg coord * refactor: remove modifier in favor of helper method (#64) * fix: initializedQuorumBitmap fix small off by 1 error, for example of quorumCount = 1,3 1 << 1 = 2, bit rep is 10 1 << 1 - 1 = 1, bit rep is 01 1 << 3 = 8, bit rep is 1000 1 << 3 - 1 = 7, bit rep is 0111 We should be subtracting by 1 instead of 2 here to get the bitmap * fix: bitshifting error, needs to shift first before subtracting * fix: moved 1 weeks to a constant for now May change this in the future to be configurable * chore: require statement * refactor: move shared logic to `_updateOperator` * refactor: added status check to `_updateOperator` * feat: Delegation.withdrawalDelayBlocks in sigcheck * fix: use blocknumber instead of timestamp * fix: unused constant and require error * feat: timestamp requirement is able to be toggled * fix: interface imports * chore: renamed to staleStakesForbidden * chore: typo with BlockNumber * fix: rebase remove deleted file * fix: prevOperatorAddress not being updated --------- Co-authored-by: Alex <18387287+wadealexc@users.noreply.github.com> --- src/BLSSignatureChecker.sol | 32 ++++++- src/RegistryCoordinator.sol | 119 +++++++++++++++++++----- src/interfaces/IBLSSignatureChecker.sol | 9 +- src/interfaces/IRegistryCoordinator.sol | 13 ++- test/mocks/RegistryCoordinatorMock.sol | 8 +- 5 files changed, 140 insertions(+), 41 deletions(-) diff --git a/src/BLSSignatureChecker.sol b/src/BLSSignatureChecker.sol index 3b79e6f4..d0469d93 100644 --- a/src/BLSSignatureChecker.sol +++ b/src/BLSSignatureChecker.sol @@ -4,7 +4,7 @@ pragma solidity =0.8.12; import {IBLSSignatureChecker} from "src/interfaces/IBLSSignatureChecker.sol"; import {IRegistryCoordinator} from "src/interfaces/IRegistryCoordinator.sol"; import {IBLSApkRegistry} from "src/interfaces/IBLSApkRegistry.sol"; -import {IStakeRegistry} from "src/interfaces/IStakeRegistry.sol"; +import {IStakeRegistry, IDelegationManager, IServiceManager} from "src/interfaces/IStakeRegistry.sol"; import {BitmapUtils} from "src/libraries/BitmapUtils.sol"; import {BN254} from "src/libraries/BN254.sol"; @@ -26,11 +26,23 @@ contract BLSSignatureChecker is IBLSSignatureChecker { IRegistryCoordinator public immutable registryCoordinator; IStakeRegistry public immutable stakeRegistry; IBLSApkRegistry public immutable blsApkRegistry; + IDelegationManager public immutable delegation; + IServiceManager public immutable serviceManager; + /// @notice If true, check the staleness of the operator stakes and that its within the delegation withdrawalDelayBlocks window. + bool public staleStakesForbidden; + + modifier onlyServiceManagerOwner { + require(msg.sender == serviceManager.owner(), "BLSSignatureChecker.onlyServiceManagerOwner: caller is not the service manager owner"); + _; + } constructor(IRegistryCoordinator _registryCoordinator) { - registryCoordinator = IRegistryCoordinator(_registryCoordinator); + registryCoordinator = _registryCoordinator; stakeRegistry = _registryCoordinator.stakeRegistry(); blsApkRegistry = _registryCoordinator.blsApkRegistry(); + delegation = stakeRegistry.delegation(); + serviceManager = stakeRegistry.serviceManager(); + staleStakesForbidden = true; } /** @@ -72,6 +84,12 @@ contract BLSSignatureChecker is IBLSSignatureChecker { // 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++) { + if (staleStakesForbidden) { + require( + registryCoordinator.quorumUpdateBlockNumber(uint8(quorumNumbers[i])) + delegation.withdrawalDelayBlocks() <= block.number, + "BLSSignatureChecker.checkSignatures: StakeRegistry updates must be within withdrawalDelayBlocks window" + ); + } require( bytes24(nonSignerStakesAndSignature.quorumApks[i].hashG1Point()) == blsApkRegistry.getApkHashAtBlockNumberAndIndex( @@ -202,4 +220,14 @@ contract BLSSignatureChecker is IBLSSignatureChecker { PAIRING_EQUALITY_CHECK_GAS ); } + + /** + * ServiceManager owner can either enforce or not that operator stakes are staler + * than the delegation.withdrawalDelayBlocks() window. + * @param value to toggle staleStakesForbidden + */ + function setStaleStakesForbidden(bool value) external onlyServiceManagerOwner { + staleStakesForbidden = value; + emit StaleStakesForbiddenUpdate(value); + } } diff --git a/src/RegistryCoordinator.sol b/src/RegistryCoordinator.sol index c96e969b..def0c7ce 100644 --- a/src/RegistryCoordinator.sol +++ b/src/RegistryCoordinator.sol @@ -71,6 +71,9 @@ contract RegistryCoordinator is EIP712, Initializable, IRegistryCoordinator, ISo mapping(address => OperatorInfo) internal _operatorInfo; /// @notice whether the salt has been used for an operator churn approval mapping(bytes32 => bool) public isChurnApproverSaltUsed; + /// @notice mapping from quorum number to the latest block that all quorums were updated all at once + mapping(uint8 => uint256) public quorumUpdateBlockNumber; + /// @notice the dynamic-length array of the registries this coordinator is coordinating address[] public registries; @@ -278,31 +281,70 @@ contract RegistryCoordinator is EIP712, Initializable, IRegistryCoordinator, ISo */ function updateOperators(address[] calldata operators) external onlyWhenNotPaused(PAUSED_UPDATE_OPERATOR) { for (uint256 i = 0; i < operators.length; i++) { - address operator = operators[i]; - OperatorInfo storage operatorInfo = _operatorInfo[operator]; + OperatorInfo memory operatorInfo = _operatorInfo[operator]; bytes32 operatorId = operatorInfo.operatorId; - // Only update operators currently registered for at least one quorum - if (operatorInfo.status != OperatorStatus.REGISTERED) { - continue; - } - + // Update the operator's stake for their active quorums uint192 currentBitmap = _currentOperatorBitmap(operatorId); - bytes memory currentQuorums = BitmapUtils.bitmapToBytesArray(currentBitmap); + bytes memory quorumsToUpdate = BitmapUtils.bitmapToBytesArray(currentBitmap); + _updateOperator(operator, operatorInfo, quorumsToUpdate); + } + } - /** - * Update the operator's stake for their active quorums. The stakeRegistry returns a bitmap - * of quorums where the operator no longer meets the minimum stake, and should be deregistered. - */ - uint192 quorumsToRemove = stakeRegistry.updateOperatorStake(operator, operatorId, currentQuorums); + /** + * @notice Updates the stakes of all operators for each of the specified quorums in the StakeRegistry. Each quorum also + * has their StakeRegistry.quorumTimestamp updated. which is meant to keep track of when operators were last all updated at once. + * @param operatorsPerQuorum is an array of arrays of operators to update for each quorum. Note that each nested array + * of operators must be sorted in ascending address order to ensure that all operators in the quorum are updated + * @param quorumNumbers is an array of quorum numbers to update + * @dev This method is used to update the stakes of all operators in a quorum at once, rather than individually. Performs + * sanitization checks on the input array lengths, quorumNumbers existing, and that quorumNumbers are ordered. Function must + * also not be paused by the PAUSED_UPDATE_OPERATOR flag. + */ + function updateOperatorsForQuorum( + address[][] calldata operatorsPerQuorum, + bytes calldata quorumNumbers + ) external onlyWhenNotPaused(PAUSED_UPDATE_OPERATOR) { + uint192 quorumBitmap = uint192(BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers)); + require(_quorumsAllExist(quorumBitmap), "RegistryCoordinator.updateOperatorsForQuorum: some quorums do not exist"); + require( + operatorsPerQuorum.length == quorumNumbers.length, + "RegistryCoordinator.updateOperatorsForQuorum: input length mismatch" + ); - if (!quorumsToRemove.isEmpty()) { - _deregisterOperator({ - operator: operator, - quorumNumbers: BitmapUtils.bitmapToBytesArray(quorumsToRemove) - }); + for (uint256 i = 0; i < quorumNumbers.length; ++i) { + uint8 quorumNumber = uint8(quorumNumbers[i]); + address[] calldata currQuorumOperators = operatorsPerQuorum[i]; + require( + currQuorumOperators.length == indexRegistry.totalOperatorsForQuorum(quorumNumber), + "RegistryCoordinator.updateOperatorsForQuorum: number of updated operators does not match quorum total" + ); + address prevOperatorAddress = address(0); + // Update stakes for each operator in this quorum + for (uint256 j = 0; j < currQuorumOperators.length; ++j) { + address operator = currQuorumOperators[j]; + OperatorInfo memory operatorInfo = _operatorInfo[operator]; + bytes32 operatorId = operatorInfo.operatorId; + { + uint192 currentBitmap = _currentOperatorBitmap(operatorId); + require( + BitmapUtils.numberIsInBitmap(currentBitmap, quorumNumber), + "RegistryCoordinator.updateOperatorsForQuorum: operator not in quorum" + ); + // Require check is to prevent duplicate operators and that all quorum operators are updated + require( + operator > prevOperatorAddress, + "RegistryCoordinator.updateOperatorsForQuorum: operators array must be sorted in ascending address order" + ); + } + _updateOperator(operator, operatorInfo, quorumNumbers[i:i+1]); + prevOperatorAddress = operator; } + + // Update timestamp that all operators in quorum have been updated all at once + quorumUpdateBlockNumber[quorumNumber] = block.number; + emit QuorumBlockNumberUpdated(quorumNumber, block.number); } } @@ -409,6 +451,7 @@ contract RegistryCoordinator is EIP712, Initializable, IRegistryCoordinator, ISo uint192 quorumsToAdd = uint192(BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers, quorumCount)); uint192 currentBitmap = _currentOperatorBitmap(operatorId); require(!quorumsToAdd.isEmpty(), "RegistryCoordinator._registerOperator: bitmap cannot be 0"); + require(_quorumsAllExist(quorumsToAdd), "RegistryCoordinator._registerOperator: some quorums do not exist"); require(quorumsToAdd.noBitsInCommon(currentBitmap), "RegistryCoordinator._registerOperator: operator already registered for some quorums being registered for"); uint192 newBitmap = uint192(currentBitmap.plus(quorumsToAdd)); @@ -496,6 +539,7 @@ contract RegistryCoordinator is EIP712, Initializable, IRegistryCoordinator, ISo uint192 quorumsToRemove = uint192(BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers, quorumCount)); uint192 currentBitmap = _currentOperatorBitmap(operatorId); require(!quorumsToRemove.isEmpty(), "RegistryCoordinator._deregisterOperator: bitmap cannot be 0"); + require(_quorumsAllExist(quorumsToRemove), "RegistryCoordinator._deregisterOperator: some quorums do not exist"); require(quorumsToRemove.isSubsetOf(currentBitmap), "RegistryCoordinator._deregisterOperator: operator is not registered for specified quorums"); uint192 newBitmap = uint192(currentBitmap.minus(quorumsToRemove)); @@ -519,6 +563,29 @@ contract RegistryCoordinator is EIP712, Initializable, IRegistryCoordinator, ISo indexRegistry.deregisterOperator(operatorId, quorumNumbers); } + /** + * @notice update operator stake for specified quorumsToUpdate, and deregister if necessary + * does nothing if operator is not registered for any quorums. + */ + function _updateOperator( + address operator, + OperatorInfo memory operatorInfo, + bytes memory quorumsToUpdate + ) internal { + if (operatorInfo.status != OperatorStatus.REGISTERED) { + return; + } + bytes32 operatorId = operatorInfo.operatorId; + uint192 quorumsToRemove = stakeRegistry.updateOperatorStake(operator, operatorId, quorumsToUpdate); + + if (!quorumsToRemove.isEmpty()) { + _deregisterOperator({ + operator: operator, + quorumNumbers: BitmapUtils.bitmapToBytesArray(quorumsToRemove) + }); + } + } + /** * @notice Returns the stake threshold required for an incoming operator to replace an existing operator * The incoming operator must have more stake than the return value. @@ -615,6 +682,14 @@ contract RegistryCoordinator is EIP712, Initializable, IRegistryCoordinator, ISo } } + /** + * @notice Returns true iff all of the bits in `quorumBitmap` belong to initialized quorums + */ + function _quorumsAllExist(uint192 quorumBitmap) internal view returns (bool) { + uint192 initializedQuorumBitmap = uint192((1 << quorumCount) - 1); + return quorumBitmap.isSubsetOf(initializedQuorumBitmap); + } + /// @notice Get the most recent bitmap for the operator, returning an empty bitmap if /// the operator is not registered. function _currentOperatorBitmap(bytes32 operatorId) internal view returns (uint192) { @@ -727,13 +802,7 @@ contract RegistryCoordinator is EIP712, Initializable, IRegistryCoordinator, ISo /// @notice Returns the current quorum bitmap for the given `operatorId` or 0 if the operator is not registered for any quorum function getCurrentQuorumBitmap(bytes32 operatorId) external view returns (uint192) { - uint256 quorumBitmapHistoryLength = _operatorBitmapHistory[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 || _operatorBitmapHistory[operatorId][quorumBitmapHistoryLength - 1].nextUpdateBlockNumber != 0) { - return 0; - } - return _operatorBitmapHistory[operatorId][quorumBitmapHistoryLength - 1].quorumBitmap; + return _currentOperatorBitmap(operatorId); } /// @notice Returns the length of the quorum bitmap history for the given `operatorId` diff --git a/src/interfaces/IBLSSignatureChecker.sol b/src/interfaces/IBLSSignatureChecker.sol index 85b324f1..ffc73a7b 100644 --- a/src/interfaces/IBLSSignatureChecker.sol +++ b/src/interfaces/IBLSSignatureChecker.sol @@ -3,7 +3,7 @@ pragma solidity =0.8.12; import {IRegistryCoordinator} from "src/interfaces/IRegistryCoordinator.sol"; import {IBLSApkRegistry} from "src/interfaces/IBLSApkRegistry.sol"; -import {IStakeRegistry} from "src/interfaces/IStakeRegistry.sol"; +import {IStakeRegistry, IDelegationManager, IServiceManager} from "src/interfaces/IStakeRegistry.sol"; import {BN254} from "src/libraries/BN254.sol"; @@ -38,12 +38,19 @@ interface IBLSSignatureChecker { // total amount staked by all operators in each quorum uint96[] totalStakeForQuorum; } + + // EVENTS + + /// @notice Emitted when `staleStakesForbiddenUpdate` is set. Value only set by serviceManagerOwner. + event StaleStakesForbiddenUpdate(bool value); // CONSTANTS & IMMUTABLES function registryCoordinator() external view returns (IRegistryCoordinator); function stakeRegistry() external view returns (IStakeRegistry); function blsApkRegistry() external view returns (IBLSApkRegistry); + function delegation() external view returns (IDelegationManager); + function serviceManager() external view returns (IServiceManager); /** * @notice This function is called by disperser when it has aggregated all the signatures of the operators diff --git a/src/interfaces/IRegistryCoordinator.sol b/src/interfaces/IRegistryCoordinator.sol index 6d2c153d..bd6fecd3 100644 --- a/src/interfaces/IRegistryCoordinator.sol +++ b/src/interfaces/IRegistryCoordinator.sol @@ -4,7 +4,6 @@ pragma solidity =0.8.12; import {IBLSApkRegistry} from "src/interfaces/IBLSApkRegistry.sol"; import {IStakeRegistry} from "src/interfaces/IStakeRegistry.sol"; import {IIndexRegistry} from "src/interfaces/IIndexRegistry.sol"; -import {BN254} from "src/libraries/BN254.sol"; /** * @title Interface for a contract that coordinates between various registries for an AVS. @@ -25,6 +24,9 @@ interface IRegistryCoordinator { event EjectorUpdated(address prevEjector, address newEjector); + /// @notice emitted when all the operators for a quorum are updated at once + event QuorumBlockNumberUpdated(uint8 indexed quorumNumber, uint256 blocknumber); + // DATA STRUCTURES enum OperatorStatus { @@ -136,9 +138,6 @@ interface IRegistryCoordinator { /// @notice Returns the number of registries function numRegistries() external view returns (uint256); - /** - * @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 pubkeyRegistrationMessageHash(address operator) external view returns (BN254.G1Point memory); -} \ No newline at end of file + /// @notice returns the blocknumber the quorum was last updated all at once for all operators + function quorumUpdateBlockNumber(uint8 quorumNumber) external view returns (uint256); +} diff --git a/test/mocks/RegistryCoordinatorMock.sol b/test/mocks/RegistryCoordinatorMock.sol index 2819ce3c..89e4feee 100644 --- a/test/mocks/RegistryCoordinatorMock.sol +++ b/test/mocks/RegistryCoordinatorMock.sol @@ -2,7 +2,7 @@ pragma solidity =0.8.12; -import "../../src/interfaces/IRegistryCoordinator.sol"; +import "src/interfaces/IRegistryCoordinator.sol"; contract RegistryCoordinatorMock is IRegistryCoordinator { @@ -59,9 +59,5 @@ contract RegistryCoordinatorMock is IRegistryCoordinator { function deregisterOperator(bytes calldata quorumNumbers, bytes calldata) external {} - function pubkeyRegistrationMessageHash(address operator) public view returns (BN254.G1Point memory) { - return BN254.hashToG1( - keccak256(abi.encode(operator)) - ); - } + function quorumUpdateBlockNumber(uint8 quorumNumber) external view returns (uint256) {} } From af96516be90d750e943d233623d2e5b7eefbeb9a Mon Sep 17 00:00:00 2001 From: steven <12021290+stevennevins@users.noreply.github.com> Date: Mon, 11 Dec 2023 15:03:49 -0500 Subject: [PATCH 070/101] feat: issue templates (#87) Co-authored-by: steven --- .github/ISSUE_TEMPLATE/bug_report.md | 34 +++++++++++++++++++++++ .github/ISSUE_TEMPLATE/design.md | 30 ++++++++++++++++++++ .github/ISSUE_TEMPLATE/feature_request.md | 24 ++++++++++++++++ .github/ISSUE_TEMPLATE/test.md | 26 +++++++++++++++++ 4 files changed, 114 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/design.md create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md create mode 100644 .github/ISSUE_TEMPLATE/test.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 00000000..29844c73 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,34 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: '' +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, could you add screenshots to help explain your problem? + +**Environment** +Enter important environment info needed to reproduce the bug. + - [e.g. chrome, safari] + - [e.g. version] + +**Don't Forget To** +* Assign this to a project (our default is [eigenlayer](https://github.com/orgs/Layr-Labs/projects/3/)) +* Add priority + size estimate +* Set status to New diff --git a/.github/ISSUE_TEMPLATE/design.md b/.github/ISSUE_TEMPLATE/design.md new file mode 100644 index 00000000..107a8a18 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/design.md @@ -0,0 +1,30 @@ +--- +name: Design +about: Design +title: "Design: Title" +labels: design +--- + +## Description + +Add a summary and description. What are we trying to do and why? + +### Action Items + +- [ ] Come up with scheme for... +- [ ] Spec it in a HackMD or Google Doc that is linked here +- [ ] Get @ to review design +- [ ] Discuss design and tradeoffs with @ +- [ ] Finalize the doc, and come to consensus +- [ ] Create ticket for implementation and link the doc to it + +### Blocking Issues + +Is anything blocking for this? (Does this depend on other unfinished designs or pending changes?) +Please link blocking issues here. If something is blocking and doesn't have an issue yet, create it! + +### Don't Forget To + +- Assign this to a project (our default is [eigenlayer](https://github.com/orgs/Layr-Labs/projects/3/)) +- Add priority + size estimate +- Set status to New diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 00000000..1d6eb94e --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,24 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: '' +assignees: '' + +--- + +**User story** +User stories are often expressed in a simple sentence, structured as follows: 'As a PERSONA, I want to GOAL_DESCRIPTION, so that CUSTOMER_VALUE_DESCRIPTION.' Alternatively for + +**Actions** +- [ ] An action item list describing the work to be done +- [ ] Link to all of the related tasks needed to complete the feature. See the [tasks](https://github.com/Layr-Labs/docs/blob/949bd6b4ddd0ef08880c6775c2d9a6222e2e7eb3/.github/ISSUE_TEMPLATE/task.md) template. +- [ ] Include everything in the definition of done e.g. unit tests and documentation + +**Acceptance criteria** +Acceptance criteria are the requirements that need to be met in order to mark a user story as complete. For example, if your user story is, "As a New Relic customer, I want to know how to interpret AI anomalies in order to monitor my site and protect myself against incidents," then the acceptance criteria would be: "A complete and published doc describing AI anomalies," and all related quality checks. + +**Don't Forget To** +* Assign this to a project (our default is [eigenlayer](https://github.com/orgs/Layr-Labs/projects/3/)) +* Add priority + size estimate +* Set status to New diff --git a/.github/ISSUE_TEMPLATE/test.md b/.github/ISSUE_TEMPLATE/test.md new file mode 100644 index 00000000..cb86cede --- /dev/null +++ b/.github/ISSUE_TEMPLATE/test.md @@ -0,0 +1,26 @@ +--- +name: Test +about: Indentified areas that require additional testing +title: '' +labels: '' +assignees: '' + +--- + +## Description +Add a summary and description. What are we trying to do and why? + +### Action Items +- [ ] Discuss scoping for tests with the team +- [ ] List possible test cases in this issue +- [ ] Get @ to review design +- [ ] Implement tests + +### Blocking Issues +Is anything blocking for this? (Does this depend on other unfinished designs or pending changes?) +Please link blocking issues here. If something is blocking and doesn't have an issue yet, create it! + +### Don't Forget To +* Assign this to a project (our default is [eigenlayer](https://github.com/orgs/Layr-Labs/projects/3/)) +* Add priority + size estimate +* Set status to New From 29ac14f45c2b6b8ee1017eaadaf2f8715c99fd17 Mon Sep 17 00:00:00 2001 From: iwantanode <87604944+tudorpintea999@users.noreply.github.com> Date: Sat, 2 Dec 2023 14:23:55 +0200 Subject: [PATCH 071/101] fix typo BLSPublicKeyCompendium.md --- docs/BLSPublicKeyCompendium.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/BLSPublicKeyCompendium.md b/docs/BLSPublicKeyCompendium.md index 75026d74..c97adc32 100644 --- a/docs/BLSPublicKeyCompendium.md +++ b/docs/BLSPublicKeyCompendium.md @@ -18,10 +18,10 @@ The contract then This verifies that the operator owns the secret key corresponding to the public keys and that the $pk_1$ and $pk_2$ have the same discrete logarithm according to their respective curve's generators. -We do this particular verification because aggregation of public keys and hasing to the curve is cheap in $\mathbb{G}_1$ on ethereum, and the above scheme allows for both! (aggregation to be done in the [BLSSignatureChecker](./BLSSignatureChecker.md)) More detailed notes exist [here](https://geometry.xyz/notebook/Optimized-BLS-multisignatures-on-EVM). +We do this particular verification because aggregation of public keys and hashing to the curve is cheap in $\mathbb{G}_1$ on ethereum, and the above scheme allows for both! (aggregation to be done in the [BLSSignatureChecker](./BLSSignatureChecker.md)) More detailed notes exist [here](https://geometry.xyz/notebook/Optimized-BLS-multisignatures-on-EVM). The contract then stores a map from the execution layer address to the hash of the operator's $\mathbb{G}_1$ public key and the other way around. ### Upstream Dependencies -The [BLSPubkeyRegistry](./BLSPubkeyRegistry.md) looks up the public key hashes in this contract when operators register with a certain public key. \ No newline at end of file +The [BLSPubkeyRegistry](./BLSPubkeyRegistry.md) looks up the public key hashes in this contract when operators register with a certain public key. From f45df7e3376aeca22d35b7caf4c96c22b242f429 Mon Sep 17 00:00:00 2001 From: iwantanode <87604944+tudorpintea999@users.noreply.github.com> Date: Sat, 2 Dec 2023 14:24:57 +0200 Subject: [PATCH 072/101] fix typo BLSSignatureChecker.md --- docs/BLSSignatureChecker.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/BLSSignatureChecker.md b/docs/BLSSignatureChecker.md index a18d6592..1d1ec4cc 100644 --- a/docs/BLSSignatureChecker.md +++ b/docs/BLSSignatureChecker.md @@ -24,9 +24,9 @@ struct NonSignerStakesAndSignature { uint32[][] nonSignerStakeIndices; // nonSignerStakeIndices[quorumNumberIndex][nonSignerIndex] } ``` -This function is called by a AVS aggregator when confirming that a `msgHash` is signed by certain `quorumNumbers`. +This function is called by an AVS aggregator when confirming that a `msgHash` is signed by certain `quorumNumbers`. -The function calculates the sum (`apk`) of the aggregate public keys for all the quorums in question using the provided `quorumApkIndices` which point to hashes of the quorum aggregate public keys at the `referenceBlockNumber` of the signature of which `quorumApks` are the preimages. Since there may be nodes from the quorums that don't sign, the `nonSignerPubkeys` are subtracted from `apk`. There is a detail here that since an operator may serve more than one of the quorums in question, the number of quorums in `quorumNumbers` that the nonsigner served at the `referenceBlockNumber` is calculated using the provided `nonSignerQuorumBitmapIndices` and their public key is multiplied by the number before it is subtracted from `apk`. This gets rid of the duplicate additions of their public key because their public key is in more than one of the added `quorumApks`. Now the contract has `apk` set to the claimed aggregate public key of the signers. +The function calculates the sum (`apk`) of the aggregate public keys for all the quorums in question using the provided `quorumApkIndices` which points to hashes of the quorum aggregate public keys at the `referenceBlockNumber` of the signature of which `quorumApks` are the preimages. Since there may be nodes from the quorums that don't sign, the `nonSignerPubkeys` are subtracted from `apk`. There is a detail here that since an operator may serve more than one of the quorums in question, the number of quorums in `quorumNumbers` that the nonsigner served at the `referenceBlockNumber` is calculated using the provided `nonSignerQuorumBitmapIndices` and their public key is multiplied by the number before it is subtracted from `apk`. This gets rid of the duplicate additions of their public key because their public key is in more than one of the added `quorumApks`. Now the contract has `apk` set to the claimed aggregate public key of the signers. Next, the contract fetches the total stakes of each of the `quorumNumbers` at the `referenceBlockNumber` using the provided `totalStakeIndices`. The stakes of each of the nonsigners for each of the quorums are fetched using `nonSignerStakeIndices` and subtracted from the total stakes. Now the contract has the claimed signing stake for each of the quorums. From 7ad4768d65bca0d5ec4f493bbb7f4681d823165d Mon Sep 17 00:00:00 2001 From: iwantanode <87604944+tudorpintea999@users.noreply.github.com> Date: Sat, 2 Dec 2023 14:27:57 +0200 Subject: [PATCH 073/101] fix typo StakeRegistry.md --- docs/StakeRegistry.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/StakeRegistry.md b/docs/StakeRegistry.md index a8d8b9a0..3ca598f9 100644 --- a/docs/StakeRegistry.md +++ b/docs/StakeRegistry.md @@ -1,6 +1,6 @@ # StakeRegistry -This contract is deployed for every AVS and keeps track of the AVS's operators' stakes over time and the total stakes for each quorum. In addition, this contract also handles the adding and modification of quorum. +This contract is deployed for every AVS and keeps track of the AVS's operators' stakes over time and the total stakes for each quorum. In addition, this contract also handles the addition and modification of quorum. # Definitions @@ -53,4 +53,4 @@ This has more implications after slashing is enabled... TODO ## Upstream Dependencies -The main integration with the StakeRegistry is used by the AVSs [BLSSignatureChecker](./BLSSignatureChecker.md). An offchain actor provides an operator id, a quorum id, and an index in the array of the operator's stake updates to verify the stake of an operator at a particular block number. They also provide a quorum id and an index in the array of total stake updates to verify the stake of the entire quorum at a particular block number. \ No newline at end of file +The main integration with the StakeRegistry is used by the AVSs [BLSSignatureChecker](./BLSSignatureChecker.md). An offchain actor provides an operator id, a quorum id, and an index in the array of the operator's stake updates to verify the stake of an operator at a particular block number. They also provide a quorum id and an index in the array of total stake updates to verify the stake of the entire quorum at a particular block number. From 02555d68bee21e2f6c0bcd3707343d40321913b1 Mon Sep 17 00:00:00 2001 From: iwantanode <87604944+tudorpintea999@users.noreply.github.com> Date: Sat, 2 Dec 2023 14:30:05 +0200 Subject: [PATCH 074/101] fix typo README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e7343bfd..8b213a27 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # AVS Smart Contract Architecture

-🚧 The Slasher contract is under active development and its interface expected to change. We recommend writing slashing logic without integrating with the Slasher at this point in time. 🚧 +🚧 The Slasher contract is under active development and its interface is expected to change. We recommend writing slashing logic without integrating with the Slasher at this point in time. 🚧

## Introduction From bbea0dbc211a7f56531517a0390e398bb48d913a Mon Sep 17 00:00:00 2001 From: Alex <18387287+wadealexc@users.noreply.github.com> Date: Tue, 12 Dec 2023 10:18:03 -0500 Subject: [PATCH 075/101] chore: remove ServiceManagerBase and add RegistryCoordinator owner (#98) --- src/BLSSignatureChecker.sol | 29 ++-- src/RegistryCoordinator.sol | 52 +++--- src/ServiceManagerBase.sol | 91 ---------- src/StakeRegistry.sol | 18 +- src/StakeRegistryStorage.sol | 8 +- src/interfaces/IBLSRegistry.sol | 69 -------- src/interfaces/IBLSSignatureChecker.sol | 5 +- src/interfaces/IQuorumRegistry.sol | 155 ------------------ src/interfaces/IRegistryCoordinator.sol | 3 + src/interfaces/IServiceManager.sol | 23 --- src/interfaces/IStakeRegistry.sol | 4 - test/harnesses/RegistryCoordinatorHarness.sol | 4 +- test/harnesses/StakeRegistryHarness.sol | 5 +- test/mocks/RegistryCoordinatorMock.sol | 2 + test/mocks/ServiceManagerMock.sol | 30 ---- test/mocks/StakeRegistryMock.sol | 1 - test/unit/RegistryCoordinatorUnit.t.sol | 19 ++- test/unit/StakeRegistryUnit.t.sol | 19 +-- test/unit/VoteWeigherBaseUnit.t.sol | 15 +- test/utils/MockAVSDeployer.sol | 17 +- 20 files changed, 98 insertions(+), 471 deletions(-) delete mode 100644 src/ServiceManagerBase.sol delete mode 100644 src/interfaces/IBLSRegistry.sol delete mode 100644 src/interfaces/IQuorumRegistry.sol delete mode 100644 src/interfaces/IServiceManager.sol delete mode 100644 test/mocks/ServiceManagerMock.sol diff --git a/src/BLSSignatureChecker.sol b/src/BLSSignatureChecker.sol index d0469d93..accbba81 100644 --- a/src/BLSSignatureChecker.sol +++ b/src/BLSSignatureChecker.sol @@ -4,7 +4,7 @@ pragma solidity =0.8.12; import {IBLSSignatureChecker} from "src/interfaces/IBLSSignatureChecker.sol"; import {IRegistryCoordinator} from "src/interfaces/IRegistryCoordinator.sol"; import {IBLSApkRegistry} from "src/interfaces/IBLSApkRegistry.sol"; -import {IStakeRegistry, IDelegationManager, IServiceManager} from "src/interfaces/IStakeRegistry.sol"; +import {IStakeRegistry, IDelegationManager} from "src/interfaces/IStakeRegistry.sol"; import {BitmapUtils} from "src/libraries/BitmapUtils.sol"; import {BN254} from "src/libraries/BN254.sol"; @@ -27,12 +27,11 @@ contract BLSSignatureChecker is IBLSSignatureChecker { IStakeRegistry public immutable stakeRegistry; IBLSApkRegistry public immutable blsApkRegistry; IDelegationManager public immutable delegation; - IServiceManager public immutable serviceManager; /// @notice If true, check the staleness of the operator stakes and that its within the delegation withdrawalDelayBlocks window. bool public staleStakesForbidden; - modifier onlyServiceManagerOwner { - require(msg.sender == serviceManager.owner(), "BLSSignatureChecker.onlyServiceManagerOwner: caller is not the service manager owner"); + modifier onlyCoordinatorOwner() { + require(msg.sender == registryCoordinator.owner(), "BLSSignatureChecker.onlyCoordinatorOwner: caller is not the owner of the registryCoordinator"); _; } @@ -41,10 +40,20 @@ contract BLSSignatureChecker is IBLSSignatureChecker { stakeRegistry = _registryCoordinator.stakeRegistry(); blsApkRegistry = _registryCoordinator.blsApkRegistry(); delegation = stakeRegistry.delegation(); - serviceManager = stakeRegistry.serviceManager(); + staleStakesForbidden = true; } + /** + * RegistryCoordinator owner can either enforce or not that operator stakes are staler + * than the delegation.withdrawalDelayBlocks() window. + * @param value to toggle staleStakesForbidden + */ + function setStaleStakesForbidden(bool value) external onlyCoordinatorOwner { + staleStakesForbidden = value; + emit StaleStakesForbiddenUpdate(value); + } + /** * @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 @@ -220,14 +229,4 @@ contract BLSSignatureChecker is IBLSSignatureChecker { PAIRING_EQUALITY_CHECK_GAS ); } - - /** - * ServiceManager owner can either enforce or not that operator stakes are staler - * than the delegation.withdrawalDelayBlocks() window. - * @param value to toggle staleStakesForbidden - */ - function setStaleStakesForbidden(bool value) external onlyServiceManagerOwner { - staleStakesForbidden = value; - emit StaleStakesForbiddenUpdate(value); - } } diff --git a/src/RegistryCoordinator.sol b/src/RegistryCoordinator.sol index def0c7ce..af5de04b 100644 --- a/src/RegistryCoordinator.sol +++ b/src/RegistryCoordinator.sol @@ -1,6 +1,7 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity =0.8.12; +import {OwnableUpgradeable} from "@openzeppelin-upgrades/contracts/access/OwnableUpgradeable.sol"; import {Initializable} from "@openzeppelin-upgrades/contracts/proxy/utils/Initializable.sol"; import {EIP712} from "@openzeppelin/contracts/utils/cryptography/draft-EIP712.sol"; @@ -12,7 +13,6 @@ import {ISlasher} from "eigenlayer-contracts/src/contracts/interfaces/ISlasher.s import {IRegistryCoordinator} from "src/interfaces/IRegistryCoordinator.sol"; import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol"; import {IBLSApkRegistry} from "src/interfaces/IBLSApkRegistry.sol"; -import {IServiceManager} from "src/interfaces/IServiceManager.sol"; import {ISocketUpdater} from "src/interfaces/ISocketUpdater.sol"; import {IStakeRegistry} from "src/interfaces/IStakeRegistry.sol"; import {IIndexRegistry} from "src/interfaces/IIndexRegistry.sol"; @@ -28,7 +28,15 @@ import {BN254} from "src/libraries/BN254.sol"; * * @author Layr Labs, Inc. */ -contract RegistryCoordinator is EIP712, Initializable, IRegistryCoordinator, ISocketUpdater, ISignatureUtils, Pausable { +contract RegistryCoordinator is + EIP712, + Initializable, + Pausable, + OwnableUpgradeable, + IRegistryCoordinator, + ISocketUpdater, + ISignatureUtils +{ using BitmapUtils for *; using BN254 for BN254.G1Point; @@ -52,8 +60,6 @@ contract RegistryCoordinator is EIP712, Initializable, IRegistryCoordinator, ISo /// @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 Aggregate Pubkey Registry contract that will keep track of operators' aggregate BLS public keys per quorum IBLSApkRegistry public immutable blsApkRegistry; /// @notice the Stake Registry contract that will keep track of operators' stakes @@ -82,11 +88,6 @@ contract RegistryCoordinator is EIP712, Initializable, IRegistryCoordinator, ISo /// @notice the address of the entity allowed to eject operators from the AVS address public ejector; - modifier onlyServiceManagerOwner { - require(msg.sender == serviceManager.owner(), "RegistryCoordinator.onlyServiceManagerOwner: caller is not the service manager owner"); - _; - } - modifier onlyEjector { require(msg.sender == ejector, "RegistryCoordinator.onlyEjector: caller is not the ejector"); _; @@ -102,19 +103,20 @@ contract RegistryCoordinator is EIP712, Initializable, IRegistryCoordinator, ISo constructor( ISlasher _slasher, - IServiceManager _serviceManager, IStakeRegistry _stakeRegistry, IBLSApkRegistry _blsApkRegistry, IIndexRegistry _indexRegistry ) EIP712("AVSRegistryCoordinator", "v0.0.1") { slasher = _slasher; - serviceManager = _serviceManager; stakeRegistry = _stakeRegistry; blsApkRegistry = _blsApkRegistry; indexRegistry = _indexRegistry; + + _disableInitializers(); } function initialize( + address _initialOwner, address _churnApprover, address _ejector, IPauserRegistry _pauserRegistry, @@ -129,6 +131,7 @@ contract RegistryCoordinator is EIP712, Initializable, IRegistryCoordinator, ISo ); // Initialize roles + _transferOwnership(_initialOwner); _initializePauser(_pauserRegistry, _initialPausedStatus); _setChurnApprover(_churnApprover); _setEjector(_ejector); @@ -377,7 +380,7 @@ contract RegistryCoordinator is EIP712, Initializable, IRegistryCoordinator, ISo } /******************************************************************************* - EXTERNAL FUNCTIONS - SERVICE MANAGER OWNER + EXTERNAL FUNCTIONS - OWNER *******************************************************************************/ /** @@ -387,7 +390,7 @@ contract RegistryCoordinator is EIP712, Initializable, IRegistryCoordinator, ISo OperatorSetParam memory operatorSetParams, uint96 minimumStake, IStakeRegistry.StrategyParams[] memory strategyParams - ) external virtual onlyServiceManagerOwner { + ) external virtual onlyOwner { _createQuorum(operatorSetParams, minimumStake, strategyParams); } @@ -395,30 +398,30 @@ contract RegistryCoordinator is EIP712, Initializable, IRegistryCoordinator, ISo * @notice Updates a quorum's OperatorSetParams * @param quorumNumber is the quorum number to set the maximum number of operators for * @param operatorSetParams is the parameters of the operator set for the `quorumNumber` - * @dev only callable by the service manager owner + * @dev only callable by the owner */ function setOperatorSetParams( uint8 quorumNumber, OperatorSetParam memory operatorSetParams - ) external onlyServiceManagerOwner quorumExists(quorumNumber) { + ) external onlyOwner quorumExists(quorumNumber) { _setOperatorSetParams(quorumNumber, operatorSetParams); } /** * @notice Sets the churnApprover * @param _churnApprover is the address of the churnApprover - * @dev only callable by the service manager owner + * @dev only callable by the owner */ - function setChurnApprover(address _churnApprover) external onlyServiceManagerOwner { + function setChurnApprover(address _churnApprover) external onlyOwner { _setChurnApprover(_churnApprover); } /** * @notice Sets the ejector * @param _ejector is the address of the ejector - * @dev only callable by the service manager owner + * @dev only callable by the owner */ - function setEjector(address _ejector) external onlyServiceManagerOwner { + function setEjector(address _ejector) external onlyOwner { _setEjector(_ejector); } @@ -832,6 +835,7 @@ contract RegistryCoordinator is EIP712, Initializable, IRegistryCoordinator, ISo return _hashTypedDataV4(keccak256(abi.encode(OPERATOR_CHURN_APPROVAL_TYPEHASH, registeringOperatorId, operatorKickParams, salt, expiry))); } +<<<<<<< HEAD /** * @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 @@ -842,5 +846,15 @@ contract RegistryCoordinator is EIP712, Initializable, IRegistryCoordinator, ISo keccak256(abi.encode(PUBKEY_REGISTRATION_TYPEHASH, operator)) ) ); +======= + /// @dev need to override function here since its defined in both these contracts + function owner() + public + view + override(OwnableUpgradeable, IRegistryCoordinator) + returns (address) + { + return OwnableUpgradeable.owner(); +>>>>>>> ecf7849 (chore: remove ServiceManagerBase and add RegistryCoordinator owner (#98)) } } diff --git a/src/ServiceManagerBase.sol b/src/ServiceManagerBase.sol deleted file mode 100644 index 6537b7ee..00000000 --- a/src/ServiceManagerBase.sol +++ /dev/null @@ -1,91 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.9; - -import {OwnableUpgradeable} from "@openzeppelin-upgrades/contracts/access/OwnableUpgradeable.sol"; -import {Initializable} from "@openzeppelin-upgrades/contracts/proxy/utils/Initializable.sol"; - -import {IPauserRegistry} from "eigenlayer-contracts/src/contracts/interfaces/IPauserRegistry.sol"; -import {ISlasher} from "eigenlayer-contracts/src/contracts/interfaces/ISlasher.sol"; -import {Pausable} from "eigenlayer-contracts/src/contracts/permissions/Pausable.sol"; - -import {BLSSignatureChecker} from "src/BLSSignatureChecker.sol"; - -import {IRegistryCoordinator} from "src/interfaces/IRegistryCoordinator.sol"; -import {IServiceManager} from "src/interfaces/IServiceManager.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; - - 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( - IRegistryCoordinator( - address(registryCoordinator) - ).stakeRegistry() - )), - "onlyRegistryCoordinatorOrStakeRegistry: not from registry coordinator or stake registry" - ); - _; - } - - constructor( - IRegistryCoordinator _registryCoordinator, - ISlasher _slasher - ) BLSSignatureChecker(_registryCoordinator) { - slasher = _slasher; - _disableInitializers(); - } - - function initialize( - IPauserRegistry _pauserRegistry, - address initialOwner - ) public initializer { - _initializePauser(_pauserRegistry, UNPAUSE_ALL); - _transferOwnership(initialOwner); - } - - // 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/StakeRegistry.sol b/src/StakeRegistry.sol index da2d04ee..c4bca769 100644 --- a/src/StakeRegistry.sol +++ b/src/StakeRegistry.sol @@ -6,7 +6,6 @@ import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/ import {StakeRegistryStorage} from "src/StakeRegistryStorage.sol"; import {IRegistryCoordinator} from "src/interfaces/IRegistryCoordinator.sol"; -import {IServiceManager} from "src/interfaces/IServiceManager.sol"; import {IStakeRegistry} from "src/interfaces/IStakeRegistry.sol"; import {BitmapUtils} from "src/libraries/BitmapUtils.sol"; @@ -30,8 +29,8 @@ contract StakeRegistry is StakeRegistryStorage { _; } - modifier onlyServiceManagerOwner() { - require(msg.sender == serviceManager.owner(), "StakeRegistry.onlyServiceManagerOwner: caller is not the owner of the serviceManager"); + modifier onlyCoordinatorOwner() { + require(msg.sender == registryCoordinator.owner(), "StakeRegistry.onlyCoordinatorOwner: caller is not the owner of the registryCoordinator"); _; } @@ -42,9 +41,8 @@ contract StakeRegistry is StakeRegistryStorage { constructor( IRegistryCoordinator _registryCoordinator, - IDelegationManager _delegationManager, - IServiceManager _serviceManager - ) StakeRegistryStorage(_registryCoordinator, _delegationManager, _serviceManager) {} + IDelegationManager _delegationManager + ) StakeRegistryStorage(_registryCoordinator, _delegationManager) {} /******************************************************************************* EXTERNAL FUNCTIONS - REGISTRY COORDINATOR @@ -208,7 +206,7 @@ contract StakeRegistry is StakeRegistryStorage { function setMinimumStakeForQuorum( uint8 quorumNumber, uint96 minimumStake - ) public virtual onlyServiceManagerOwner quorumExists(quorumNumber) { + ) public virtual onlyCoordinatorOwner quorumExists(quorumNumber) { _setMinimumStakeForQuorum(quorumNumber, minimumStake); } @@ -221,7 +219,7 @@ contract StakeRegistry is StakeRegistryStorage { function addStrategies( uint8 quorumNumber, StrategyParams[] memory _strategyParams - ) public virtual onlyServiceManagerOwner quorumExists(quorumNumber) { + ) public virtual onlyCoordinatorOwner quorumExists(quorumNumber) { _addStrategyParams(quorumNumber, _strategyParams); } @@ -233,7 +231,7 @@ contract StakeRegistry is StakeRegistryStorage { function removeStrategies( uint8 quorumNumber, uint256[] memory indicesToRemove - ) public virtual onlyServiceManagerOwner quorumExists(quorumNumber) { + ) public virtual onlyCoordinatorOwner quorumExists(quorumNumber) { uint256 toRemoveLength = indicesToRemove.length; require(toRemoveLength > 0, "StakeRegistry.removeStrategies: no indices to remove provided"); @@ -259,7 +257,7 @@ contract StakeRegistry is StakeRegistryStorage { uint8 quorumNumber, uint256[] calldata strategyIndices, uint96[] calldata newMultipliers - ) public virtual onlyServiceManagerOwner quorumExists(quorumNumber) { + ) public virtual onlyCoordinatorOwner quorumExists(quorumNumber) { uint256 numStrats = strategyIndices.length; require(numStrats > 0, "StakeRegistry.modifyStrategyParams: no strategy indices provided"); require(newMultipliers.length == numStrats, "StakeRegistry.modifyStrategyParams: input length mismatch"); diff --git a/src/StakeRegistryStorage.sol b/src/StakeRegistryStorage.sol index 2d08d8ce..81b4ba42 100644 --- a/src/StakeRegistryStorage.sol +++ b/src/StakeRegistryStorage.sol @@ -5,7 +5,6 @@ import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/ import {IStrategyManager} from "eigenlayer-contracts/src/contracts/interfaces/IStrategyManager.sol"; import {IRegistryCoordinator} from "src/interfaces/IRegistryCoordinator.sol"; -import {IServiceManager} from "src/interfaces/IServiceManager.sol"; import {IStakeRegistry} from "src/interfaces/IStakeRegistry.sol"; /** @@ -25,9 +24,6 @@ abstract contract StakeRegistryStorage is IStakeRegistry { /// @notice The address of the Delegation contract for EigenLayer. IDelegationManager public immutable delegation; - /// @notice The ServiceManager contract for this middleware, where tasks are created / initiated. - IServiceManager public immutable serviceManager; - /// @notice the coordinator contract that this registry is associated with IRegistryCoordinator public immutable registryCoordinator; @@ -49,12 +45,10 @@ abstract contract StakeRegistryStorage is IStakeRegistry { constructor( IRegistryCoordinator _registryCoordinator, - IDelegationManager _delegationManager, - IServiceManager _serviceManager + IDelegationManager _delegationManager ) { registryCoordinator = _registryCoordinator; delegation = _delegationManager; - serviceManager = _serviceManager; } // storage gap for upgradeability diff --git a/src/interfaces/IBLSRegistry.sol b/src/interfaces/IBLSRegistry.sol deleted file mode 100644 index 7e712471..00000000 --- a/src/interfaces/IBLSRegistry.sol +++ /dev/null @@ -1,69 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity >=0.5.0; - -import {IQuorumRegistry} from "./IQuorumRegistry.sol"; -import {BN254} from "../libraries/BN254.sol"; - -/** - * @title Minimal interface extension to `IQuorumRegistry`. - * @author Layr Labs, Inc. - * @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service - * @notice Adds BLS-specific functions to the base interface. - */ -interface IBLSRegistry is IQuorumRegistry { - /// @notice Data structure used to track the history of the Aggregate Public Key of all operators - struct ApkUpdate { - // keccak256(apk_x0, apk_x1, apk_y0, apk_y1) - bytes32 apkHash; - // block number at which the update occurred - uint32 blockNumber; - } - - // EVENTS - /** - * @notice Emitted upon the registration of a new operator for the middleware - * @param operator Address of the new operator - * @param pkHash The keccak256 hash of the operator's public key - * @param pk The operator's public key itself - * @param apkHashIndex The index of the latest (i.e. the new) APK update - * @param apkHash The keccak256 hash of the new Aggregate Public Key - */ - event Registration( - address indexed operator, - bytes32 pkHash, - BN254.G1Point pk, - uint32 apkHashIndex, - bytes32 apkHash, - string socket - ); - - /// @notice Emitted when the `operatorWhitelister` role is transferred. - event OperatorWhitelisterTransferred(address previousAddress, address newAddress); - - /** - * @notice get hash of a historical aggregated public key corresponding to a given index; - * called by checkSignatures in BLSSignatureChecker.sol. - */ - function getCorrectApkHash(uint256 index, uint32 blockNumber) external returns (bytes32); - - /// @notice returns the `ApkUpdate` struct at `index` in the list of APK updates - function apkUpdates(uint256 index) external view returns (ApkUpdate memory); - - /// @notice returns the APK hash that resulted from the `index`th APK update - function apkHashes(uint256 index) external view returns (bytes32); - - /// @notice returns the block number at which the `index`th APK update occurred - function apkUpdateBlockNumbers(uint256 index) external view returns (uint32); - - function operatorWhitelister() external view returns (address); - - function operatorWhitelistEnabled() external view returns (bool); - - function whitelisted(address) external view returns (bool); - - function setOperatorWhitelistStatus(bool _operatorWhitelistEnabled) external; - - function addToOperatorWhitelist(address[] calldata) external; - - function removeFromWhitelist(address[] calldata operators) external; -} diff --git a/src/interfaces/IBLSSignatureChecker.sol b/src/interfaces/IBLSSignatureChecker.sol index ffc73a7b..ac63096c 100644 --- a/src/interfaces/IBLSSignatureChecker.sol +++ b/src/interfaces/IBLSSignatureChecker.sol @@ -3,7 +3,7 @@ pragma solidity =0.8.12; import {IRegistryCoordinator} from "src/interfaces/IRegistryCoordinator.sol"; import {IBLSApkRegistry} from "src/interfaces/IBLSApkRegistry.sol"; -import {IStakeRegistry, IDelegationManager, IServiceManager} from "src/interfaces/IStakeRegistry.sol"; +import {IStakeRegistry, IDelegationManager} from "src/interfaces/IStakeRegistry.sol"; import {BN254} from "src/libraries/BN254.sol"; @@ -41,7 +41,7 @@ interface IBLSSignatureChecker { // EVENTS - /// @notice Emitted when `staleStakesForbiddenUpdate` is set. Value only set by serviceManagerOwner. + /// @notice Emitted when `staleStakesForbiddenUpdate` is set event StaleStakesForbiddenUpdate(bool value); // CONSTANTS & IMMUTABLES @@ -50,7 +50,6 @@ interface IBLSSignatureChecker { function stakeRegistry() external view returns (IStakeRegistry); function blsApkRegistry() external view returns (IBLSApkRegistry); function delegation() external view returns (IDelegationManager); - function serviceManager() external view returns (IServiceManager); /** * @notice This function is called by disperser when it has aggregated all the signatures of the operators diff --git a/src/interfaces/IQuorumRegistry.sol b/src/interfaces/IQuorumRegistry.sol deleted file mode 100644 index 21c10dee..00000000 --- a/src/interfaces/IQuorumRegistry.sol +++ /dev/null @@ -1,155 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity >=0.5.0; - -import {IRegistry} from "./IRegistry.sol"; - -/** - * @title Interface for a `Registry`-type contract that uses either 1 or 2 quorums. - * @author Layr Labs, Inc. - * @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service - * @notice This contract does not currently support n-quorums where n >= 3. - * Note in particular the presence of only `firstQuorumStake` and `secondQuorumStake` in the `OperatorStake` struct. - */ -interface IQuorumRegistry is IRegistry { - // DATA STRUCTURES - enum Status { - // default is inactive - INACTIVE, - ACTIVE - } - - /** - * @notice Data structure for storing info on operators to be used for: - * - sending data by the sequencer - * - payment and associated challenges - */ - struct Operator { - // hash of pubkey of the operator - bytes32 pubkeyHash; - // start taskNumber from which the operator has been registered - uint32 fromTaskNumber; - // indicates whether the operator is actively registered for serving the middleware or not - Status status; - } - - // struct used to give definitive ordering to operators at each blockNumber - struct OperatorIndex { - // blockNumber number at which operator index changed - // note that the operator's index is different *for this block number*, i.e. the *new* index is *inclusive* of this value - uint32 toBlockNumber; - // index of the operator in array of operators, or the total number of operators if in the 'totalOperatorsHistory' - uint32 index; - } - - /// @notice struct used to store the stakes of an individual operator or the sum of all operators' stakes, for storage - struct OperatorStake { - // the block number at which the stake amounts were updated and stored - uint32 updateBlockNumber; - // the block number at which the *next update* occurred. - /// @notice This entry has the value **0** until another update takes place. - uint32 nextUpdateBlockNumber; - // stake weight for the first quorum - uint96 firstQuorumStake; - // stake weight for the second quorum. Will always be zero in the event that only one quorum is used - uint96 secondQuorumStake; - } - - function getLengthOfTotalStakeHistory() external view returns (uint256); - - /** - * @notice Returns the `index`-th entry in the dynamic array of total stake, `totalStakeHistory`. - * @dev Function will revert in the event that `index` is out-of-bounds. - */ - function getTotalStakeFromIndex(uint256 index) external view returns (OperatorStake memory); - - /// @notice Returns the stored pubkeyHash for the specified `operator`. - function getOperatorPubkeyHash(address operator) external view returns (bytes32); - - /// @notice Returns task number from when `operator` has been registered. - function getFromTaskNumberForOperator(address operator) external view returns (uint32); - - /** - * @notice Returns the stake weight corresponding to `pubkeyHash`, at the - * `index`-th entry in the `pubkeyHashToStakeHistory[pubkeyHash]` array. - * @param pubkeyHash Hash of the public key of the operator of interest. - * @param index Array index for lookup, within the dynamic array `pubkeyHashToStakeHistory[pubkeyHash]`. - * @dev Function will revert if `index` is out-of-bounds. - */ - function getStakeFromPubkeyHashAndIndex( - bytes32 pubkeyHash, - uint256 index - ) external view returns (OperatorStake memory); - - /** - * @notice Checks that the `operator` was active at the `blockNumber`, using the specified `stakeHistoryIndex` as proof. - * @param operator is the operator of interest - * @param blockNumber is the block number of interest - * @param stakeHistoryIndex specifies an index in `pubkeyHashToStakeHistory[pubkeyHash]`, where `pubkeyHash` is looked up - * in `registry[operator].pubkeyHash` - * @return 'true' if it is successfully proven that the `operator` was active at the `blockNumber`, and 'false' otherwise - * @dev In order for this function to return 'true', the inputs must satisfy all of the following list: - * 1) `pubkeyHashToStakeHistory[pubkeyHash][index].updateBlockNumber <= blockNumber` - * 2) `pubkeyHashToStakeHistory[pubkeyHash][index].nextUpdateBlockNumber` must be either `0` (signifying no next update) or - * is must be strictly greater than `blockNumber` - * 3) `pubkeyHashToStakeHistory[pubkeyHash][index].firstQuorumStake > 0` - * or `pubkeyHashToStakeHistory[pubkeyHash][index].secondQuorumStake > 0`, i.e. the operator had nonzero stake - * @dev Note that a return value of 'false' does not guarantee that the `operator` was inactive at `blockNumber`, since a - * bad `stakeHistoryIndex` can be supplied in order to obtain a response of 'false'. - */ - function checkOperatorActiveAtBlockNumber( - address operator, - uint256 blockNumber, - uint256 stakeHistoryIndex - ) external view returns (bool); - - /** - * @notice Checks that the `operator` was inactive at the `blockNumber`, using the specified `stakeHistoryIndex` as proof. - * @param operator is the operator of interest - * @param blockNumber is the block number of interest - * @param stakeHistoryIndex specifies an index in `pubkeyHashToStakeHistory[pubkeyHash]`, where `pubkeyHash` is looked up - * in `registry[operator].pubkeyHash` - * @return 'true' if it is successfully proven that the `operator` was inactive at the `blockNumber`, and 'false' otherwise - * @dev In order for this function to return 'true', the inputs must satisfy all of the following list: - * 1) `pubkeyHashToStakeHistory[pubkeyHash][index].updateBlockNumber <= blockNumber` - * 2) `pubkeyHashToStakeHistory[pubkeyHash][index].nextUpdateBlockNumber` must be either `0` (signifying no next update) or - * is must be strictly greater than `blockNumber` - * 3) `pubkeyHashToStakeHistory[pubkeyHash][index].firstQuorumStake > 0` - * or `pubkeyHashToStakeHistory[pubkeyHash][index].secondQuorumStake > 0`, i.e. the operator had nonzero stake - * @dev Note that a return value of 'false' does not guarantee that the `operator` was active at `blockNumber`, since a - * bad `stakeHistoryIndex` can be supplied in order to obtain a response of 'false'. - */ - function checkOperatorInactiveAtBlockNumber( - address operator, - uint256 blockNumber, - uint256 stakeHistoryIndex - ) external view returns (bool); - - /** - * @notice Looks up the `operator`'s index in the dynamic array `operatorList` at the specified `blockNumber`. - * @param index Used to specify the entry within the dynamic array `pubkeyHashToIndexHistory[pubkeyHash]` to - * read data from, where `pubkeyHash` is looked up from `operator`'s registration info - * @param blockNumber Is the desired block number at which we wish to query the operator's position in the `operatorList` array - * @dev Function will revert in the event that the specified `index` input does not identify the appropriate entry in the - * array `pubkeyHashToIndexHistory[pubkeyHash]` to pull the info from. - */ - function getOperatorIndex(address operator, uint32 blockNumber, uint32 index) external view returns (uint32); - - /** - * @notice Looks up the number of total operators at the specified `blockNumber`. - * @param index Input used to specify the entry within the dynamic array `totalOperatorsHistory` to read data from. - * @dev This function will revert if the provided `index` is out of bounds. - */ - function getTotalOperators(uint32 blockNumber, uint32 index) external view returns (uint32); - - /// @notice Returns the current number of operators of this service. - function numOperators() external view returns (uint32); - - /** - * @notice Returns the most recent stake weights for the `operator` - * @dev Function returns weights of **0** in the event that the operator has no stake history - */ - function operatorStakes(address operator) external view returns (uint96, uint96); - - /// @notice Returns the stake amounts from the latest entry in `totalStakeHistory`. - function totalStake() external view returns (uint96, uint96); -} diff --git a/src/interfaces/IRegistryCoordinator.sol b/src/interfaces/IRegistryCoordinator.sol index bd6fecd3..ccbd8a86 100644 --- a/src/interfaces/IRegistryCoordinator.sol +++ b/src/interfaces/IRegistryCoordinator.sol @@ -140,4 +140,7 @@ interface IRegistryCoordinator { /// @notice returns the blocknumber the quorum was last updated all at once for all operators function quorumUpdateBlockNumber(uint8 quorumNumber) external view returns (uint256); + + /// @notice The owner of the registry coordinator + function owner() external view returns (address); } diff --git a/src/interfaces/IServiceManager.sol b/src/interfaces/IServiceManager.sol deleted file mode 100644 index 1dc4fe72..00000000 --- a/src/interfaces/IServiceManager.sol +++ /dev/null @@ -1,23 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity >=0.5.0; - -import {ISlasher} from "eigenlayer-contracts/src/contracts/interfaces/ISlasher.sol"; - -/** - * @title Interface for a `ServiceManager`-type contract. - * @author Layr Labs, Inc. - * @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service - */ -interface IServiceManager { - - // ServiceManager proxies to the slasher - function slasher() external view returns (ISlasher); - - /// @notice function that causes the ServiceManager to freeze the operator on EigenLayer, through a call to the Slasher contract - /// @dev this function should contain slashing logic to make sure operators are not needlessly being slashed - /// THIS IS ONLY A TEMPORARY PLACE HOLDER UNTIL SLASHING IS FULLY IMPLEMENTED - function freezeOperator(address operator) external; - - /// @notice required since the registry contract will call this function to permission its upgrades to be done by the same owner as the service manager - function owner() external view returns (address); -} diff --git a/src/interfaces/IStakeRegistry.sol b/src/interfaces/IStakeRegistry.sol index 63a16b9a..51196a2f 100644 --- a/src/interfaces/IStakeRegistry.sol +++ b/src/interfaces/IStakeRegistry.sol @@ -4,7 +4,6 @@ pragma solidity =0.8.12; import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; -import {IServiceManager} from "./IServiceManager.sol"; import {IRegistry} from "./IRegistry.sol"; /** @@ -125,9 +124,6 @@ interface IStakeRegistry is IRegistry { /// @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); - /// @notice In order to register for a quorum i, an operator must have at least `minimumStakeForQuorum[i]` function minimumStakeForQuorum(uint256 quorumNumber) external view returns (uint96); diff --git a/test/harnesses/RegistryCoordinatorHarness.sol b/test/harnesses/RegistryCoordinatorHarness.sol index 9816a332..eff9bead 100644 --- a/test/harnesses/RegistryCoordinatorHarness.sol +++ b/test/harnesses/RegistryCoordinatorHarness.sol @@ -7,11 +7,11 @@ import "src/RegistryCoordinator.sol"; contract RegistryCoordinatorHarness is RegistryCoordinator { constructor( ISlasher _slasher, - IServiceManager _serviceManager, IStakeRegistry _stakeRegistry, IBLSApkRegistry _blsApkRegistry, IIndexRegistry _indexRegistry - ) RegistryCoordinator(_slasher, _serviceManager, _stakeRegistry, _blsApkRegistry, _indexRegistry) { + ) RegistryCoordinator(_slasher, _stakeRegistry, _blsApkRegistry, _indexRegistry) { + _transferOwnership(msg.sender); } function setQuorumCount(uint8 count) external { diff --git a/test/harnesses/StakeRegistryHarness.sol b/test/harnesses/StakeRegistryHarness.sol index ece3feee..96f529ba 100644 --- a/test/harnesses/StakeRegistryHarness.sol +++ b/test/harnesses/StakeRegistryHarness.sol @@ -9,9 +9,8 @@ contract StakeRegistryHarness is StakeRegistry { constructor( IRegistryCoordinator _registryCoordinator, - IDelegationManager _delegationManager, - IServiceManager _serviceManager - ) StakeRegistry(_registryCoordinator, _delegationManager, _serviceManager) { + IDelegationManager _delegationManager + ) StakeRegistry(_registryCoordinator, _delegationManager) { } function recordOperatorStakeUpdate(bytes32 operatorId, uint8 quorumNumber, uint96 newStake) external returns(int256) { diff --git a/test/mocks/RegistryCoordinatorMock.sol b/test/mocks/RegistryCoordinatorMock.sol index 89e4feee..f6ca981d 100644 --- a/test/mocks/RegistryCoordinatorMock.sol +++ b/test/mocks/RegistryCoordinatorMock.sol @@ -60,4 +60,6 @@ contract RegistryCoordinatorMock is IRegistryCoordinator { function deregisterOperator(bytes calldata quorumNumbers, bytes calldata) external {} function quorumUpdateBlockNumber(uint8 quorumNumber) external view returns (uint256) {} + + function owner() external view returns (address) {} } diff --git a/test/mocks/ServiceManagerMock.sol b/test/mocks/ServiceManagerMock.sol deleted file mode 100644 index 2f87e8f0..00000000 --- a/test/mocks/ServiceManagerMock.sol +++ /dev/null @@ -1,30 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity =0.8.12; - -import "../../src/interfaces/IServiceManager.sol"; -import "eigenlayer-contracts/src/contracts/interfaces/ISlasher.sol"; - -contract ServiceManagerMock is IServiceManager{ - address public owner; - ISlasher public slasher; - - constructor(ISlasher _slasher) { - owner = msg.sender; - slasher = _slasher; - - } - - /// @notice Permissioned function that causes the ServiceManager to freeze the operator on EigenLayer, through a call to the Slasher contract - function freezeOperator(address operator) external { - slasher.freezeOperator(operator); - } - - function paymentChallengeToken() external pure returns (IERC20) { - return IERC20(address(0)); - } - - /// @notice Returns the `latestServeUntilBlock` until which operators must serve. - function latestServeUntilBlock() external pure returns (uint32) { - return type(uint32).max; - } -} diff --git a/test/mocks/StakeRegistryMock.sol b/test/mocks/StakeRegistryMock.sol index 8088f2d1..a092944c 100644 --- a/test/mocks/StakeRegistryMock.sol +++ b/test/mocks/StakeRegistryMock.sol @@ -78,7 +78,6 @@ contract StakeRegistryMock is IStakeRegistry { ) external {} function delegation() external view returns (IDelegationManager) {} - function serviceManager() external view returns (IServiceManager) {} function WEIGHTING_DIVISOR() external pure returns (uint256) {} diff --git a/test/unit/RegistryCoordinatorUnit.t.sol b/test/unit/RegistryCoordinatorUnit.t.sol index cda318cb..bc398ff5 100644 --- a/test/unit/RegistryCoordinatorUnit.t.sol +++ b/test/unit/RegistryCoordinatorUnit.t.sol @@ -59,6 +59,7 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { // make sure the contract intializers are disabled cheats.expectRevert(bytes("Initializable: contract is already initialized")); registryCoordinator.initialize( + registryCoordinatorOwner, churnApprover, ejector, pauserRegistry, @@ -69,44 +70,44 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { ); } - function testSetOperatorSetParams_NotServiceManagerOwner_Reverts() public { - cheats.expectRevert("RegistryCoordinator.onlyServiceManagerOwner: caller is not the service manager owner"); + function testSetOperatorSetParams_NotOwner_Reverts() public { + cheats.expectRevert("Ownable: caller is not the owner"); cheats.prank(defaultOperator); registryCoordinator.setOperatorSetParams(0, operatorSetParams[0]); } function testSetOperatorSetParams_Valid() public { - cheats.prank(serviceManagerOwner); + cheats.prank(registryCoordinatorOwner); cheats.expectEmit(true, true, true, true, address(registryCoordinator)); emit OperatorSetParamsUpdated(0, operatorSetParams[1]); registryCoordinator.setOperatorSetParams(0, operatorSetParams[1]); } - function testSetChurnApprover_NotServiceManagerOwner_Reverts() public { + function testSetChurnApprover_NotOwner_Reverts() public { address newChurnApprover = address(uint160(uint256(keccak256("newChurnApprover")))); - cheats.expectRevert("RegistryCoordinator.onlyServiceManagerOwner: caller is not the service manager owner"); + cheats.expectRevert("Ownable: caller is not the owner"); cheats.prank(defaultOperator); registryCoordinator.setChurnApprover(newChurnApprover); } function testSetChurnApprover_Valid() public { address newChurnApprover = address(uint160(uint256(keccak256("newChurnApprover")))); - cheats.prank(serviceManagerOwner); + cheats.prank(registryCoordinatorOwner); cheats.expectEmit(true, true, true, true, address(registryCoordinator)); emit ChurnApproverUpdated(churnApprover, newChurnApprover); registryCoordinator.setChurnApprover(newChurnApprover); } - function testSetEjector_NotServiceManagerOwner_Reverts() public { + function testSetEjector_NotOwner_Reverts() public { address newEjector = address(uint160(uint256(keccak256("newEjector")))); - cheats.expectRevert("RegistryCoordinator.onlyServiceManagerOwner: caller is not the service manager owner"); + cheats.expectRevert("Ownable: caller is not the owner"); cheats.prank(defaultOperator); registryCoordinator.setEjector(newEjector); } function testSetEjector_Valid() public { address newEjector = address(uint160(uint256(keccak256("newEjector")))); - cheats.prank(serviceManagerOwner); + cheats.prank(registryCoordinatorOwner); cheats.expectEmit(true, true, true, true, address(registryCoordinator)); emit EjectorUpdated(ejector, newEjector); registryCoordinator.setEjector(newEjector); diff --git a/test/unit/StakeRegistryUnit.t.sol b/test/unit/StakeRegistryUnit.t.sol index 2232f0e0..e6bd3c6a 100644 --- a/test/unit/StakeRegistryUnit.t.sol +++ b/test/unit/StakeRegistryUnit.t.sol @@ -11,7 +11,6 @@ import {IStrategyManager} from "eigenlayer-contracts/src/contracts/interfaces/IS import {ISlasher} from "eigenlayer-contracts/src/contracts/interfaces/ISlasher.sol"; import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; import {IStakeRegistry} from "src/interfaces/IStakeRegistry.sol"; -import {IServiceManager} from "src/interfaces/IServiceManager.sol"; import {IIndexRegistry} from "src/interfaces/IIndexRegistry.sol"; import {IRegistryCoordinator} from "src/interfaces/IRegistryCoordinator.sol"; import {IBLSApkRegistry} from "src/interfaces/IBLSApkRegistry.sol"; @@ -20,7 +19,6 @@ import {BitmapUtils} from "eigenlayer-contracts/src/contracts/libraries/BitmapUt import {StrategyManagerMock} from "eigenlayer-contracts/src/test/mocks/StrategyManagerMock.sol"; import {EigenPodManagerMock} from "eigenlayer-contracts/src/test/mocks/EigenPodManagerMock.sol"; -import {ServiceManagerMock} from "test/mocks/ServiceManagerMock.sol"; import {OwnableMock} from "eigenlayer-contracts/src/test/mocks/OwnableMock.sol"; import {DelegationManagerMock} from "eigenlayer-contracts/src/test/mocks/DelegationManagerMock.sol"; import {SlasherMock} from "eigenlayer-contracts/src/test/mocks/SlasherMock.sol"; @@ -44,12 +42,11 @@ contract StakeRegistryUnitTests is Test { StakeRegistryHarness public stakeRegistry; RegistryCoordinatorHarness public registryCoordinator; - ServiceManagerMock public serviceManagerMock; StrategyManagerMock public strategyManagerMock; DelegationManagerMock public delegationMock; EigenPodManagerMock public eigenPodManagerMock; - address public serviceManagerOwner = address(uint160(uint256(keccak256("serviceManagerOwner")))); + address public registryCoordinatorOwner = address(uint160(uint256(keccak256("registryCoordinatorOwner")))); address public pauser = address(uint160(uint256(keccak256("pauser")))); address public unpauser = address(uint160(uint256(keccak256("unpauser")))); address public apkRegistry = address(uint160(uint256(keccak256("apkRegistry")))); @@ -104,21 +101,17 @@ contract StakeRegistryUnitTests is Test { slasher ); + cheats.startPrank(registryCoordinatorOwner); registryCoordinator = new RegistryCoordinatorHarness( slasher, - serviceManagerMock, stakeRegistry, IBLSApkRegistry(apkRegistry), IIndexRegistry(indexRegistry) ); - cheats.startPrank(serviceManagerOwner); - // make the serviceManagerOwner the owner of the serviceManager contract - serviceManagerMock = new ServiceManagerMock(slasher); stakeRegistryImplementation = new StakeRegistryHarness( IRegistryCoordinator(address(registryCoordinator)), - delegationMock, - IServiceManager(address(serviceManagerMock)) + delegationMock ); stakeRegistry = StakeRegistryHarness( @@ -149,8 +142,8 @@ contract StakeRegistryUnitTests is Test { registryCoordinator.setQuorumCount(maxQuorumsToRegisterFor); } - function testSetMinimumStakeForQuorum_NotFromServiceManager_Reverts() public { - cheats.expectRevert("StakeRegistry.onlyServiceManagerOwner: caller is not the owner of the serviceManager"); + function testSetMinimumStakeForQuorum_NotFromCoordinatorOwner_Reverts() public { + cheats.expectRevert("StakeRegistry.onlyCoordinatorOwner: caller is not the owner of the registryCoordinator"); stakeRegistry.setMinimumStakeForQuorum(defaultQuorumNumber, 0); } @@ -159,7 +152,7 @@ contract StakeRegistryUnitTests is Test { cheats.assume(initializedQuorums[quorumNumber]); // set the minimum stake for quorum - cheats.prank(serviceManagerOwner); + cheats.prank(registryCoordinatorOwner); stakeRegistry.setMinimumStakeForQuorum(quorumNumber, minimumStakeForQuorum); // make sure the minimum stake for quorum is as expected diff --git a/test/unit/VoteWeigherBaseUnit.t.sol b/test/unit/VoteWeigherBaseUnit.t.sol index 23db3a5f..0b415ab8 100644 --- a/test/unit/VoteWeigherBaseUnit.t.sol +++ b/test/unit/VoteWeigherBaseUnit.t.sol @@ -10,11 +10,14 @@ import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy import {IEigenPodManager} from "eigenlayer-contracts/src/contracts/interfaces/IEigenPodManager.sol"; import {ISlasher} from "eigenlayer-contracts/src/contracts/interfaces/ISlasher.sol"; <<<<<<< HEAD +<<<<<<< HEAD import {IServiceManager} from "../../src/interfaces/IServiceManager.sol"; import {IVoteWeigher} from "../../src/interfaces/IVoteWeigher.sol"; import {StakeRegistry} from "../../src/StakeRegistry.sol"; ======= import {IServiceManager} from "src/interfaces/IServiceManager.sol"; +======= +>>>>>>> ecf7849 (chore: remove ServiceManagerBase and add RegistryCoordinator owner (#98)) import {IStakeRegistry} from "src/interfaces/IStakeRegistry.sol"; import {StakeRegistry} from "src/StakeRegistry.sol"; >>>>>>> 12b09de (fix: fix compilation issues and tests) @@ -31,8 +34,7 @@ contract VoteWeigherBaseUnitTests is Test { ProxyAdmin public proxyAdmin; PauserRegistry public pauserRegistry; - address serviceManagerOwner; - IServiceManager public serviceManager; + address public registryCoordinatorOwner = address(uint160(uint256(keccak256("registryCoordinatorOwner")))); DelegationManagerMock delegationMock; RegistryCoordinatorMock registryCoordinatorMock; @@ -76,14 +78,12 @@ contract VoteWeigherBaseUnitTests is Test { pausers[0] = pauser; pauserRegistry = new PauserRegistry(pausers, unpauser); + cheats.prank(registryCoordinatorOwner); registryCoordinatorMock = new RegistryCoordinatorMock(); - delegationMock = new DelegationManagerMock(); - // make the serviceManagerOwner the owner of the serviceManager contract - cheats.prank(serviceManagerOwner); - serviceManager = IServiceManager(address(new OwnableMock())); + delegationMock = new DelegationManagerMock(); - voteWeigherImplementation = new StakeRegistry(registryCoordinatorMock, delegationMock, serviceManager); + voteWeigherImplementation = new StakeRegistry(registryCoordinatorMock, delegationMock); voteWeigher = StakeRegistry(address(new TransparentUpgradeableProxy(address(voteWeigherImplementation), address(proxyAdmin), ""))); @@ -93,7 +93,6 @@ contract VoteWeigherBaseUnitTests is Test { function testCorrectConstructionParameters() public { assertEq(address(voteWeigherImplementation.registryCoordinator()), address(registryCoordinatorMock)); assertEq(address(voteWeigherImplementation.delegation()), address(delegationMock)); - assertEq(address(voteWeigherImplementation.serviceManager()), address(serviceManager)); } /// TODO - migrate tests to registry coordinator diff --git a/test/utils/MockAVSDeployer.sol b/test/utils/MockAVSDeployer.sol index 89fac517..5e6b9cb5 100644 --- a/test/utils/MockAVSDeployer.sol +++ b/test/utils/MockAVSDeployer.sol @@ -18,7 +18,6 @@ import {RegistryCoordinatorHarness} from "test/harnesses/RegistryCoordinatorHarn import {BLSApkRegistry} from "src/BLSApkRegistry.sol"; import {StakeRegistry} from "src/StakeRegistry.sol"; import {IndexRegistry} from "src/IndexRegistry.sol"; -import {IServiceManager} from "src/interfaces/IServiceManager.sol"; import {IBLSApkRegistry} from "src/interfaces/IBLSApkRegistry.sol"; import {IStakeRegistry} from "src/interfaces/IStakeRegistry.sol"; import {IIndexRegistry} from "src/interfaces/IIndexRegistry.sol"; @@ -27,7 +26,11 @@ import {IRegistryCoordinator} from "src/interfaces/IRegistryCoordinator.sol"; import {StrategyManagerMock} from "eigenlayer-contracts/src/test/mocks/StrategyManagerMock.sol"; import {EigenPodManagerMock} from "eigenlayer-contracts/src/test/mocks/EigenPodManagerMock.sol"; +<<<<<<< HEAD import {ServiceManagerMock} from "test/mocks/ServiceManagerMock.sol"; +======= +import {OwnableMock} from "eigenlayer-contracts/src/test/mocks/OwnableMock.sol"; +>>>>>>> ecf7849 (chore: remove ServiceManagerBase and add RegistryCoordinator owner (#98)) import {DelegationManagerMock} from "eigenlayer-contracts/src/test/mocks/DelegationManagerMock.sol"; import {BLSApkRegistryHarness} from "test/harnesses/BLSApkRegistryHarness.sol"; import {EmptyContract} from "eigenlayer-contracts/src/test/mocks/EmptyContract.sol"; @@ -60,13 +63,12 @@ contract MockAVSDeployer is Test { BLSApkRegistryHarness public blsApkRegistry; 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 registryCoordinatorOwner = address(uint160(uint256(keccak256("registryCoordinatorOwner")))); address public pauser = address(uint160(uint256(keccak256("pauser")))); address public unpauser = address(uint160(uint256(keccak256("unpauser")))); @@ -144,9 +146,7 @@ contract MockAVSDeployer is Test { ); cheats.stopPrank(); - cheats.startPrank(serviceManagerOwner); - // make the serviceManagerOwner the owner of the serviceManager contract - serviceManagerMock = new ServiceManagerMock(slasher); + cheats.startPrank(registryCoordinatorOwner); registryCoordinator = RegistryCoordinatorHarness(address( new TransparentUpgradeableProxy( address(emptyContract), @@ -191,8 +191,7 @@ contract MockAVSDeployer is Test { stakeRegistryImplementation = new StakeRegistryHarness( IRegistryCoordinator(registryCoordinator), - delegationMock, - IServiceManager(address(serviceManagerMock)) + delegationMock ); proxyAdmin.upgrade( @@ -240,7 +239,6 @@ contract MockAVSDeployer is Test { registryCoordinatorImplementation = new RegistryCoordinatorHarness( slasher, - serviceManagerMock, stakeRegistry, blsApkRegistry, indexRegistry @@ -261,6 +259,7 @@ contract MockAVSDeployer is Test { address(registryCoordinatorImplementation), abi.encodeWithSelector( RegistryCoordinator.initialize.selector, + registryCoordinatorOwner, churnApprover, ejector, pauserRegistry, From cce5430c608124b8bf840b01f0a1b8e56f580bc7 Mon Sep 17 00:00:00 2001 From: Yash Patil <40046473+ypatil12@users.noreply.github.com> Date: Tue, 12 Dec 2023 11:21:29 -0500 Subject: [PATCH 076/101] Feat: Add AVS/Operator Registration Support in RegistryCoordinator (#99) --- src/RegistryCoordinator.sol | 74 ++++---- src/interfaces/IRegistryCoordinator.sol | 2 +- test/harnesses/RegistryCoordinatorHarness.sol | 3 +- test/integration/CoreRegistration.t.sol | 175 ++++++++++++++++++ test/unit/RegistryCoordinatorUnit.t.sol | 104 +++++------ test/unit/StakeRegistryUnit.t.sol | 1 + test/utils/MockAVSDeployer.sol | 42 +++-- 7 files changed, 283 insertions(+), 118 deletions(-) create mode 100644 test/integration/CoreRegistration.t.sol diff --git a/src/RegistryCoordinator.sol b/src/RegistryCoordinator.sol index af5de04b..714285bb 100644 --- a/src/RegistryCoordinator.sol +++ b/src/RegistryCoordinator.sol @@ -9,6 +9,7 @@ import {EIP1271SignatureUtils} from "eigenlayer-contracts/src/contracts/librarie import {IPauserRegistry} from "eigenlayer-contracts/src/contracts/interfaces/IPauserRegistry.sol"; import {Pausable} from "eigenlayer-contracts/src/contracts/permissions/Pausable.sol"; import {ISlasher} from "eigenlayer-contracts/src/contracts/interfaces/ISlasher.sol"; +import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; import {IRegistryCoordinator} from "src/interfaces/IRegistryCoordinator.sol"; import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol"; @@ -18,7 +19,6 @@ import {IStakeRegistry} from "src/interfaces/IStakeRegistry.sol"; import {IIndexRegistry} from "src/interfaces/IIndexRegistry.sol"; import {BitmapUtils} from "src/libraries/BitmapUtils.sol"; -import {BN254} from "src/libraries/BN254.sol"; /** * @title A `RegistryCoordinator` that has three registries: @@ -38,13 +38,10 @@ contract RegistryCoordinator is ISignatureUtils { using BitmapUtils for *; - 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,bytes32[] operatorIdsToSwap)"); - /// @notice The EIP-712 typehash used for registering BLS public keys - bytes32 public constant PUBKEY_REGISTRATION_TYPEHASH = keccak256("BN254PubkeyRegistration(address operator)"); /// @notice The maximum value of a quorum bitmap uint256 internal constant MAX_QUORUM_BITMAP = type(uint192).max; /// @notice The basis point denominator @@ -66,6 +63,8 @@ contract RegistryCoordinator is IStakeRegistry public immutable stakeRegistry; /// @notice the Index Registry contract that will keep track of operators' indexes IIndexRegistry public immutable indexRegistry; + /// @notice The Delegation Manager contract to record operator avs relationships + IDelegationManager public immutable delegationManager; /// @notice the current number of quorums supported by the registry coordinator uint8 public quorumCount; @@ -102,11 +101,13 @@ contract RegistryCoordinator is } constructor( + IDelegationManager _delegationManager, ISlasher _slasher, IStakeRegistry _stakeRegistry, IBLSApkRegistry _blsApkRegistry, IIndexRegistry _indexRegistry ) EIP712("AVSRegistryCoordinator", "v0.0.1") { + delegationManager = _delegationManager; slasher = _slasher; stakeRegistry = _stakeRegistry; blsApkRegistry = _blsApkRegistry; @@ -155,29 +156,23 @@ contract RegistryCoordinator is * @notice Registers msg.sender as an operator for one or more quorums. If any quorum reaches its maximum * operator capacity, this method will fail. * @param quorumNumbers is an ordered byte array containing the quorum numbers being registered for - * @param params contains the G1 & G2 public keys of the operator, and a signature proving their ownership - * @dev the `params` input param is ignored if the caller has previously registered a public key + * @param socket is the socket of the operator + * @param operatorSignature is the signature of the operator used by the AVS to register the operator in the delegation manager */ function registerOperator( bytes calldata quorumNumbers, string calldata socket, - IBLSApkRegistry.PubkeyRegistrationParams calldata params + SignatureWithSaltAndExpiry memory operatorSignature ) external onlyWhenNotPaused(PAUSED_REGISTER_OPERATOR) { - /** - * IF the operator has never registered a pubkey before, THEN register their pubkey - * OTHERWISE, simply ignore the provided `params` - */ bytes32 operatorId = blsApkRegistry.getOperatorId(msg.sender); - if (operatorId == 0) { - operatorId = blsApkRegistry.registerBLSPublicKey(msg.sender, params, pubkeyRegistrationMessageHash(msg.sender)); - } // Register the operator in each of the registry contracts RegisterResults memory results = _registerOperator({ operator: msg.sender, operatorId: operatorId, quorumNumbers: quorumNumbers, - socket: socket + socket: socket, + operatorSignature: operatorSignature }); for (uint256 i = 0; i < quorumNumbers.length; i++) { @@ -200,29 +195,21 @@ contract RegistryCoordinator is * @notice Registers msg.sender as an operator for one or more quorums. If any quorum reaches its maximum operator * capacity, `operatorKickParams` is used to replace an old operator with the new one. * @param quorumNumbers is an ordered byte array containing the quorum numbers being registered for - * @param params contains the G1 & G2 public keys of the operator, and a signature proving their ownership * @param operatorKickParams are used to determine which operator is removed to maintain quorum capacity as the * operator registers for quorums. * @param churnApproverSignature is the signature of the churnApprover on the operator kick params - * @dev the `params` input param is ignored if the caller has previously registered a public key + * @param operatorSignature is the signature of the operator used by the AVS to register the operator in the delegation manager */ function registerOperatorWithChurn( bytes calldata quorumNumbers, string calldata socket, - IBLSApkRegistry.PubkeyRegistrationParams calldata params, OperatorKickParam[] calldata operatorKickParams, - SignatureWithSaltAndExpiry memory churnApproverSignature + SignatureWithSaltAndExpiry memory churnApproverSignature, + SignatureWithSaltAndExpiry memory operatorSignature ) external onlyWhenNotPaused(PAUSED_REGISTER_OPERATOR) { require(operatorKickParams.length == quorumNumbers.length, "RegistryCoordinator.registerOperatorWithChurn: input length mismatch"); - /** - * IF the operator has never registered a pubkey before, THEN register their pubkey - * OTHERWISE, simply ignore the `pubkeyRegistrationSignature`, `pubkeyG1`, and `pubkeyG2` inputs - */ bytes32 operatorId = blsApkRegistry.getOperatorId(msg.sender); - if (operatorId == 0) { - operatorId = blsApkRegistry.registerBLSPublicKey(msg.sender, params, pubkeyRegistrationMessageHash(msg.sender)); - } // Verify the churn approver's signature for the registering operator and kick params _verifyChurnApproverSignature({ @@ -236,11 +223,13 @@ contract RegistryCoordinator is operator: msg.sender, operatorId: operatorId, quorumNumbers: quorumNumbers, - socket: socket + socket: socket, + operatorSignature: operatorSignature }); for (uint256 i = 0; i < quorumNumbers.length; i++) { uint8 quorumNumber = uint8(quorumNumbers[i]); + OperatorSetParam memory operatorSetParams = _quorumParams[quorumNumber]; /** @@ -425,6 +414,15 @@ contract RegistryCoordinator is _setEjector(_ejector); } + /** + * @notice Sets the metadata URI for the AVS + * @param _metadataURI is the metadata URI for the AVS + * @dev only callable by the service manager owner + */ + function setMetadataURI(string memory _metadataURI) external onlyOwner { + delegationManager.updateAVSMetadataURI(_metadataURI); + } + /******************************************************************************* INTERNAL FUNCTIONS *******************************************************************************/ @@ -443,7 +441,8 @@ contract RegistryCoordinator is address operator, bytes32 operatorId, bytes calldata quorumNumbers, - string memory socket + string memory socket, + SignatureWithSaltAndExpiry memory operatorSignature ) internal virtual returns (RegisterResults memory) { /** * Get bitmap of quorums to register for and operator's current bitmap. Validate that: @@ -475,6 +474,9 @@ contract RegistryCoordinator is status: OperatorStatus.REGISTERED }); + // Register the operator with the delegation manager + delegationManager.registerOperatorToAVS(operator, operatorSignature); + emit OperatorRegistered(operator, operatorId); } @@ -554,9 +556,10 @@ contract RegistryCoordinator is newBitmap: newBitmap }); - // If the operator is no longer registered for any quorums, update their status + // If the operator is no longer registered for any quorums, update their status and deregister from delegationManager if (newBitmap.isEmpty()) { operatorInfo.status = OperatorStatus.DEREGISTERED; + delegationManager.deregisterOperatorFromAVS(operator); emit OperatorDeregistered(operator, operatorId); } @@ -835,18 +838,6 @@ contract RegistryCoordinator is return _hashTypedDataV4(keccak256(abi.encode(OPERATOR_CHURN_APPROVAL_TYPEHASH, registeringOperatorId, operatorKickParams, salt, expiry))); } -<<<<<<< HEAD - /** - * @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 pubkeyRegistrationMessageHash(address operator) public view returns (BN254.G1Point memory) { - return BN254.hashToG1( - _hashTypedDataV4( - keccak256(abi.encode(PUBKEY_REGISTRATION_TYPEHASH, operator)) - ) - ); -======= /// @dev need to override function here since its defined in both these contracts function owner() public @@ -855,6 +846,5 @@ contract RegistryCoordinator is returns (address) { return OwnableUpgradeable.owner(); ->>>>>>> ecf7849 (chore: remove ServiceManagerBase and add RegistryCoordinator owner (#98)) } } diff --git a/src/interfaces/IRegistryCoordinator.sol b/src/interfaces/IRegistryCoordinator.sol index ccbd8a86..02b72a5e 100644 --- a/src/interfaces/IRegistryCoordinator.sol +++ b/src/interfaces/IRegistryCoordinator.sol @@ -26,7 +26,7 @@ interface IRegistryCoordinator { /// @notice emitted when all the operators for a quorum are updated at once event QuorumBlockNumberUpdated(uint8 indexed quorumNumber, uint256 blocknumber); - + // DATA STRUCTURES enum OperatorStatus { diff --git a/test/harnesses/RegistryCoordinatorHarness.sol b/test/harnesses/RegistryCoordinatorHarness.sol index eff9bead..246ea705 100644 --- a/test/harnesses/RegistryCoordinatorHarness.sol +++ b/test/harnesses/RegistryCoordinatorHarness.sol @@ -6,11 +6,12 @@ import "src/RegistryCoordinator.sol"; // wrapper around the RegistryCoordinator contract that exposes the internal functions for unit testing. contract RegistryCoordinatorHarness is RegistryCoordinator { constructor( + IDelegationManager _delegationManager, ISlasher _slasher, IStakeRegistry _stakeRegistry, IBLSApkRegistry _blsApkRegistry, IIndexRegistry _indexRegistry - ) RegistryCoordinator(_slasher, _stakeRegistry, _blsApkRegistry, _indexRegistry) { + ) RegistryCoordinator(_delegationManager, _slasher, _stakeRegistry, _blsApkRegistry, _indexRegistry) { _transferOwnership(msg.sender); } diff --git a/test/integration/CoreRegistration.t.sol b/test/integration/CoreRegistration.t.sol new file mode 100644 index 00000000..1df04dfc --- /dev/null +++ b/test/integration/CoreRegistration.t.sol @@ -0,0 +1,175 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity =0.8.12; + +import "test/utils/MockAVSDeployer.sol"; +import { DelegationManager } from "eigenlayer-contracts/src/contracts/core/DelegationManager.sol"; +import { IDelegationManager } from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; + +contract Test_CoreRegistration is MockAVSDeployer { + // Contracts + DelegationManager public delegationManager; + + // Operator info + uint256 operatorPrivateKey = 420; + address operator; + + // Dummy vals used across tests + bytes32 emptySalt; + uint256 maxExpiry = type(uint256).max; + string emptyStringForMetadataURI; + + function setUp() public { + _deployMockEigenLayerAndAVS(); + + // Deploy New DelegationManager + DelegationManager delegationManagerImplementation = new DelegationManager(strategyManagerMock, slasher, eigenPodManagerMock); + delegationManager = DelegationManager( + address( + new TransparentUpgradeableProxy( + address(delegationManagerImplementation), + address(proxyAdmin), + abi.encodeWithSelector( + DelegationManager.initialize.selector, + address(this), + pauserRegistry, + 0, // 0 is initialPausedStatus + 50400 // Initial withdrawal delay blocks + ) + ) + ) + ); + + // Deploy New RegistryCoordinator + registryCoordinatorImplementation = new RegistryCoordinatorHarness( + delegationManager, + slasher, + stakeRegistry, + blsApkRegistry, + indexRegistry + ); + + // Upgrade Registry Coordinator + cheats.prank(proxyAdminOwner); + proxyAdmin.upgrade( + TransparentUpgradeableProxy(payable(address(registryCoordinator))), + address(registryCoordinatorImplementation) + ); + + // Set operator address + operator = cheats.addr(operatorPrivateKey); + pubkeyCompendium.setBLSPublicKey(operator, defaultPubKey); + + // Register operator to EigenLayer + cheats.prank(operator); + delegationManager.registerAsOperator( + IDelegationManager.OperatorDetails({ + earningsReceiver: operator, + delegationApprover: address(0), + stakerOptOutWindowBlocks: 0 + }), + emptyStringForMetadataURI + ); + + // Set operator weight in single quorum + bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(MAX_QUORUM_BITMAP); + for (uint i = 0; i < quorumNumbers.length; i++) { + stakeRegistry.setOperatorWeight(uint8(quorumNumbers[i]), operator, defaultStake); + } + } + + function test_registerOperator_coreStateChanges() public { + bytes memory quorumNumbers = new bytes(1); + + // Get operator signature + ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature = _getOperatorSignature( + operatorPrivateKey, + operator, + address(registryCoordinator), + emptySalt, + maxExpiry + ); + + // Register operator + cheats.prank(operator); + registryCoordinator.registerOperator(quorumNumbers, defaultSocket, operatorSignature); + + // Check operator is registered + IDelegationManager.OperatorAVSRegistrationStatus operatorStatus = delegationManager.avsOperatorStatus(address(registryCoordinator), operator); + assertEq(uint8(operatorStatus), uint8(IDelegationManager.OperatorAVSRegistrationStatus.REGISTERED)); + } + + function test_deregisterOperator_coreStateChanges() public { + // Register operator + bytes memory quorumNumbers = new bytes(1); + _registerOperator(quorumNumbers); + + // Deregister Operator + cheats.prank(operator); + registryCoordinator.deregisterOperator(quorumNumbers); + + // Check operator is deregistered + IDelegationManager.OperatorAVSRegistrationStatus operatorStatus = delegationManager.avsOperatorStatus(address(registryCoordinator), operator); + assertEq(uint8(operatorStatus), uint8(IDelegationManager.OperatorAVSRegistrationStatus.UNREGISTERED)); + } + + function test_deregisterOperator_notGloballyDeregistered() public { + // Register operator with all quorums + bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(MAX_QUORUM_BITMAP); + emit log_named_bytes("quorumNumbers", quorumNumbers); + _registerOperator(quorumNumbers); + + // Deregister Operator with single quorum + quorumNumbers = new bytes(1); + cheats.prank(operator); + registryCoordinator.deregisterOperator(quorumNumbers); + + // Check operator is still registered + IDelegationManager.OperatorAVSRegistrationStatus operatorStatus = delegationManager.avsOperatorStatus(address(registryCoordinator), operator); + assertEq(uint8(operatorStatus), uint8(IDelegationManager.OperatorAVSRegistrationStatus.REGISTERED)); + } + + function test_setMetadataURI_fail_notServiceManagerOwner() public { + cheats.prank(operator); + cheats.expectRevert("Ownable: caller is not the owner"); + registryCoordinator.setMetadataURI("Test MetadataURI"); + } + + function test_setMetadataURI() public { + cheats.prank(registryCoordinatorOwner); + registryCoordinator.setMetadataURI("Test MetadataURI"); + } + + // Utils + function _registerOperator(bytes memory quorumNumbers) internal { + // Get operator signature + ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature = _getOperatorSignature( + operatorPrivateKey, + operator, + address(registryCoordinator), + emptySalt, + maxExpiry + ); + + // Register operator + cheats.prank(operator); + registryCoordinator.registerOperator(quorumNumbers, defaultSocket, operatorSignature); + } + + function _getOperatorSignature( + uint256 _operatorPrivateKey, + address operatorToSign, + address avs, + bytes32 salt, + uint256 expiry + ) internal view returns (ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature) { + operatorSignature.salt = salt; + operatorSignature.expiry = expiry; + { + bytes32 digestHash = delegationManager.calculateOperatorAVSRegistrationDigestHash(operatorToSign, avs, salt, expiry); + (uint8 v, bytes32 r, bytes32 s) = cheats.sign(_operatorPrivateKey, digestHash); + operatorSignature.signature = abi.encodePacked(r, s, v); + } + return operatorSignature; + } + +} \ No newline at end of file diff --git a/test/unit/RegistryCoordinatorUnit.t.sol b/test/unit/RegistryCoordinatorUnit.t.sol index bc398ff5..3f20e23d 100644 --- a/test/unit/RegistryCoordinatorUnit.t.sol +++ b/test/unit/RegistryCoordinatorUnit.t.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity =0.8.12; -import "../utils/MockAVSDeployer.sol"; +import "test/utils/MockAVSDeployer.sol"; contract RegistryCoordinatorUnit is MockAVSDeployer { using BN254 for BN254.G1Point; @@ -116,6 +116,7 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { function testRegisterOperatorWithCoordinator_WhenPaused_Reverts() public { bytes memory emptyQuorumNumbers = new bytes(0); + ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; // pause registerOperator cheats.prank(pauser); @@ -123,38 +124,42 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { cheats.startPrank(defaultOperator); cheats.expectRevert(bytes("Pausable: index is paused")); - registryCoordinator.registerOperator(emptyQuorumNumbers, defaultSocket, pubkeyRegistrationParams); + registryCoordinator.registerOperator(emptyQuorumNumbers, defaultSocket, emptySig); } function testRegisterOperatorWithCoordinator_EmptyQuorumNumbers_Reverts() public { bytes memory emptyQuorumNumbers = new bytes(0); + ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; cheats.expectRevert("RegistryCoordinator._registerOperator: bitmap cannot be 0"); cheats.prank(defaultOperator); - registryCoordinator.registerOperator(emptyQuorumNumbers, defaultSocket, pubkeyRegistrationParams); + registryCoordinator.registerOperator(emptyQuorumNumbers, defaultSocket, emptySig); } function testRegisterOperatorWithCoordinator_QuorumNumbersTooLarge_Reverts() public { bytes memory quorumNumbersTooLarge = new bytes(1); - quorumNumbersTooLarge[0] = 0xC0; + ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; + quorumNumbersTooLarge[0] = 0xC0; cheats.expectRevert("BitmapUtils.orderedBytesArrayToBitmap: bitmap exceeds max value"); cheats.prank(defaultOperator); - registryCoordinator.registerOperator(quorumNumbersTooLarge, defaultSocket, pubkeyRegistrationParams); + registryCoordinator.registerOperator(quorumNumbersTooLarge, defaultSocket, emptySig); } function testRegisterOperatorWithCoordinator_QuorumNotCreated_Reverts() public { _deployMockEigenLayerAndAVS(10); bytes memory quorumNumbersNotCreated = new bytes(1); - quorumNumbersNotCreated[0] = 0x0B; + ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; + quorumNumbersNotCreated[0] = 0x0B; cheats.prank(defaultOperator); cheats.expectRevert("BitmapUtils.orderedBytesArrayToBitmap: bitmap exceeds max value"); - registryCoordinator.registerOperator(quorumNumbersNotCreated, defaultSocket, pubkeyRegistrationParams); + registryCoordinator.registerOperator(quorumNumbersNotCreated, defaultSocket, emptySig); } function testRegisterOperatorWithCoordinatorForSingleQuorum_Valid() public { bytes memory quorumNumbers = new bytes(1); + ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; quorumNumbers[0] = bytes1(defaultQuorumNumber); stakeRegistry.setOperatorWeight(uint8(quorumNumbers[0]), defaultOperator, defaultStake); @@ -170,7 +175,7 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { uint256 gasBefore = gasleft(); cheats.prank(defaultOperator); - registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams); + registryCoordinator.registerOperator(quorumNumbers, defaultSocket, emptySig); uint256 gasAfter = gasleft(); emit log_named_uint("gasUsed", gasBefore - gasAfter); @@ -197,6 +202,7 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { function testRegisterOperatorWithCoordinatorForFuzzedQuorums_Valid(uint256 quorumBitmap) public { quorumBitmap = quorumBitmap & MAX_QUORUM_BITMAP; + ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; cheats.assume(quorumBitmap != 0); bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); @@ -222,7 +228,7 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { uint256 gasBefore = gasleft(); cheats.prank(defaultOperator); - registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams); + registryCoordinator.registerOperator(quorumNumbers, defaultSocket, emptySig); uint256 gasAfter = gasleft(); emit log_named_uint("gasUsed", gasBefore - gasAfter); emit log_named_uint("numQuorums", quorumNumbers.length); @@ -249,6 +255,7 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { function testRegisterOperatorWithCoordinator_RegisteredOperatorForNewQuorums_Valid() public { uint256 registrationBlockNumber = block.number + 100; uint256 nextRegistrationBlockNumber = registrationBlockNumber + 100; + ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; bytes memory quorumNumbers = new bytes(1); quorumNumbers[0] = bytes1(defaultQuorumNumber); @@ -256,7 +263,7 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { stakeRegistry.setOperatorWeight(uint8(quorumNumbers[0]), defaultOperator, defaultStake); cheats.prank(defaultOperator); cheats.roll(registrationBlockNumber); - registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams); + registryCoordinator.registerOperator(quorumNumbers, defaultSocket, emptySig); bytes memory newQuorumNumbers = new bytes(1); newQuorumNumbers[0] = bytes1(defaultQuorumNumber+1); @@ -272,7 +279,7 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { emit QuorumIndexUpdate(defaultOperatorId, uint8(newQuorumNumbers[0]), 0); cheats.roll(nextRegistrationBlockNumber); cheats.prank(defaultOperator); - registryCoordinator.registerOperator(newQuorumNumbers, defaultSocket, pubkeyRegistrationParams); + registryCoordinator.registerOperator(newQuorumNumbers, defaultSocket, emptySig); uint256 quorumBitmap = BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers) | BitmapUtils.orderedBytesArrayToBitmap(newQuorumNumbers); @@ -306,6 +313,7 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { function testRegisterOperatorWithCoordinator_OverFilledQuorum_Reverts(uint256 pseudoRandomNumber) public { uint32 numOperators = defaultMaxOperatorCount; uint32 registrationBlockNumber = 200; + ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; bytes memory quorumNumbers = new bytes(1); quorumNumbers[0] = bytes1(defaultQuorumNumber); @@ -324,18 +332,19 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { address operatorToRegister = _incrementAddress(defaultOperator, numOperators); BN254.G1Point memory operatorToRegisterPubKey = BN254.hashToG1(keccak256(abi.encodePacked(pseudoRandomNumber, numOperators))); - blsApkRegistry.setBLSPublicKey(operatorToRegister, operatorToRegisterPubKey); + pubkeyCompendium.setBLSPublicKey(operatorToRegister, operatorToRegisterPubKey); stakeRegistry.setOperatorWeight(defaultQuorumNumber, operatorToRegister, defaultStake); cheats.prank(operatorToRegister); cheats.expectRevert("RegistryCoordinator.registerOperator: operator count exceeds maximum"); - registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams); + registryCoordinator.registerOperator(quorumNumbers, defaultSocket, emptySig); } function testRegisterOperatorWithCoordinator_RegisteredOperatorForSameQuorums_Reverts() public { uint256 registrationBlockNumber = block.number + 100; uint256 nextRegistrationBlockNumber = registrationBlockNumber + 100; + ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; bytes memory quorumNumbers = new bytes(1); quorumNumbers[0] = bytes1(defaultQuorumNumber); @@ -343,12 +352,12 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { stakeRegistry.setOperatorWeight(uint8(quorumNumbers[0]), defaultOperator, defaultStake); cheats.prank(defaultOperator); cheats.roll(registrationBlockNumber); - registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams); + registryCoordinator.registerOperator(quorumNumbers, defaultSocket, emptySig); cheats.prank(defaultOperator); cheats.roll(nextRegistrationBlockNumber); cheats.expectRevert("RegistryCoordinator._registerOperator: operator already registered for some quorums being registered for"); - registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams); + registryCoordinator.registerOperator(quorumNumbers, defaultSocket, emptySig); } function testDeregisterOperatorWithCoordinator_WhenPaused_Reverts() public { @@ -393,6 +402,7 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { } function testDeregisterOperatorWithCoordinatorForSingleQuorumAndSingleOperator_Valid() public { + ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; uint32 registrationBlockNumber = 100; uint32 deregistrationBlockNumber = 200; @@ -405,7 +415,7 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { cheats.roll(registrationBlockNumber); - registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams); + registryCoordinator.registerOperator(quorumNumbers, defaultSocket, emptySig); uint256 quorumBitmap = BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers); @@ -440,6 +450,7 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { } function testDeregisterOperatorWithCoordinatorForFuzzedQuorumAndSingleOperator_Valid(uint256 quorumBitmap) public { + ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; uint32 registrationBlockNumber = 100; uint32 deregistrationBlockNumber = 200; @@ -455,7 +466,7 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { cheats.roll(registrationBlockNumber); - registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams); + registryCoordinator.registerOperator(quorumNumbers, defaultSocket, emptySig); cheats.expectEmit(true, true, true, true, address(blsApkRegistry)); emit OperatorRemovedFromQuorums(defaultOperator, quorumNumbers); @@ -568,6 +579,7 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { function testReregisterOperatorWithCoordinator_Valid() public { testDeregisterOperatorWithCoordinatorForSingleQuorumAndSingleOperator_Valid(); + ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; uint32 reregistrationBlockNumber = 201; bytes memory quorumNumbers = new bytes(1); @@ -582,7 +594,7 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0); // re-register the operator - registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams); + registryCoordinator.registerOperator(quorumNumbers, defaultSocket, emptySig); // check success of registration uint256 quorumBitmap = BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers); @@ -659,7 +671,7 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { }); } - blsApkRegistry.setBLSPublicKey(operatorToRegister, operatorToRegisterPubKey); + pubkeyCompendium.setBLSPublicKey(operatorToRegister, operatorToRegisterPubKey); uint96 registeringStake = defaultKickBIPsOfOperatorStake * defaultStake; stakeRegistry.setOperatorWeight(defaultQuorumNumber, operatorToRegister, registeringStake); @@ -680,15 +692,16 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { emit QuorumIndexUpdate(operatorToRegisterId, defaultQuorumNumber, numOperators - 1); { + ISignatureUtils.SignatureWithSaltAndExpiry memory emptyAVSRegSig; ISignatureUtils.SignatureWithSaltAndExpiry memory signatureWithExpiry = _signOperatorChurnApproval(operatorToRegisterId, operatorKickParams, defaultSalt, block.timestamp + 10); cheats.prank(operatorToRegister); uint256 gasBefore = gasleft(); registryCoordinator.registerOperatorWithChurn( quorumNumbers, - defaultSocket, - pubkeyRegistrationParams, + defaultSocket, operatorKickParams, - signatureWithExpiry + signatureWithExpiry, + emptyAVSRegSig ); uint256 gasAfter = gasleft(); emit log_named_uint("gasUsed", gasBefore - gasAfter); @@ -721,6 +734,7 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { function testRegisterOperatorWithCoordinatorWithKicks_LessThanKickBIPsOfOperatorStake_Reverts(uint256 pseudoRandomNumber) public { bytes memory quorumNumbers = new bytes(1); quorumNumbers[0] = bytes1(defaultQuorumNumber); + ISignatureUtils.SignatureWithSaltAndExpiry memory emptyAVSRegSig; ( address operatorToRegister, @@ -735,18 +749,13 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { ISignatureUtils.SignatureWithSaltAndExpiry memory signatureWithExpiry = _signOperatorChurnApproval(operatorToRegisterId, operatorKickParams, defaultSalt, block.timestamp + 10); cheats.prank(operatorToRegister); cheats.expectRevert("RegistryCoordinator._validateChurn: incoming operator has insufficient stake for churn"); - registryCoordinator.registerOperatorWithChurn( - quorumNumbers, - defaultSocket, - pubkeyRegistrationParams, - operatorKickParams, - signatureWithExpiry - ); + registryCoordinator.registerOperatorWithChurn(quorumNumbers, defaultSocket, operatorKickParams, signatureWithExpiry, emptyAVSRegSig); } function testRegisterOperatorWithCoordinatorWithKicks_LessThanKickBIPsOfTotalStake_Reverts(uint256 pseudoRandomNumber) public { bytes memory quorumNumbers = new bytes(1); quorumNumbers[0] = bytes1(defaultQuorumNumber); + ISignatureUtils.SignatureWithSaltAndExpiry memory emptyAVSRegSig; uint96 operatorToKickStake = defaultMaxOperatorCount * defaultStake; ( @@ -764,18 +773,13 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { ISignatureUtils.SignatureWithSaltAndExpiry memory signatureWithExpiry = _signOperatorChurnApproval(operatorToRegisterId, operatorKickParams, defaultSalt, block.timestamp + 10); cheats.prank(operatorToRegister); cheats.expectRevert("RegistryCoordinator._validateChurn: cannot kick operator with more than kickBIPsOfTotalStake"); - registryCoordinator.registerOperatorWithChurn( - quorumNumbers, - defaultSocket, - pubkeyRegistrationParams, - operatorKickParams, - signatureWithExpiry - ); + registryCoordinator.registerOperatorWithChurn(quorumNumbers, defaultSocket, operatorKickParams, signatureWithExpiry, emptyAVSRegSig); } function testRegisterOperatorWithCoordinatorWithKicks_InvalidSignatures_Reverts(uint256 pseudoRandomNumber) public { bytes memory quorumNumbers = new bytes(1); quorumNumbers[0] = bytes1(defaultQuorumNumber); + ISignatureUtils.SignatureWithSaltAndExpiry memory emptyAVSRegSig; ( address operatorToRegister, @@ -793,18 +797,13 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { signatureWithSaltAndExpiry.salt = defaultSalt; cheats.prank(operatorToRegister); cheats.expectRevert("ECDSA: invalid signature"); - registryCoordinator.registerOperatorWithChurn( - quorumNumbers, - defaultSocket, - pubkeyRegistrationParams, - operatorKickParams, - signatureWithSaltAndExpiry - ); + registryCoordinator.registerOperatorWithChurn(quorumNumbers, defaultSocket, operatorKickParams, signatureWithSaltAndExpiry, emptyAVSRegSig); } function testRegisterOperatorWithCoordinatorWithKicks_ExpiredSignatures_Reverts(uint256 pseudoRandomNumber) public { bytes memory quorumNumbers = new bytes(1); quorumNumbers[0] = bytes1(defaultQuorumNumber); + ISignatureUtils.SignatureWithSaltAndExpiry memory emptyAVSRegSig; ( address operatorToRegister, @@ -820,24 +819,19 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { ISignatureUtils.SignatureWithSaltAndExpiry memory signatureWithSaltAndExpiry = _signOperatorChurnApproval(operatorToRegisterId, operatorKickParams, defaultSalt, block.timestamp - 1); cheats.prank(operatorToRegister); cheats.expectRevert("RegistryCoordinator._verifyChurnApproverSignature: churnApprover signature expired"); - registryCoordinator.registerOperatorWithChurn( - quorumNumbers, - defaultSocket, - pubkeyRegistrationParams, - operatorKickParams, - signatureWithSaltAndExpiry - ); + registryCoordinator.registerOperatorWithChurn(quorumNumbers, defaultSocket, operatorKickParams, signatureWithSaltAndExpiry, emptyAVSRegSig); } function testEjectOperatorFromCoordinator_AllQuorums_Valid() public { // register operator with default stake with default quorum number bytes memory quorumNumbers = new bytes(1); quorumNumbers[0] = bytes1(defaultQuorumNumber); + ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; stakeRegistry.setOperatorWeight(uint8(quorumNumbers[0]), defaultOperator, defaultStake); cheats.prank(defaultOperator); - registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams); + registryCoordinator.registerOperator(quorumNumbers, defaultSocket, emptySig); cheats.expectEmit(true, true, true, true, address(blsApkRegistry)); emit OperatorRemovedFromQuorums(defaultOperator, quorumNumbers); @@ -866,13 +860,14 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { bytes memory quorumNumbers = new bytes(2); quorumNumbers[0] = bytes1(defaultQuorumNumber); quorumNumbers[1] = bytes1(defaultQuorumNumber + 1); + ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; for (uint i = 0; i < quorumNumbers.length; i++) { stakeRegistry.setOperatorWeight(uint8(quorumNumbers[i]), defaultOperator, defaultStake); } cheats.prank(defaultOperator); - registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams); + registryCoordinator.registerOperator(quorumNumbers, defaultSocket, emptySig); // eject from only first quorum bytes memory quorumNumbersToEject = new bytes(1); @@ -905,11 +900,12 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { function testEjectOperatorFromCoordinator_NotEjector_Reverts() public { bytes memory quorumNumbers = new bytes(1); quorumNumbers[0] = bytes1(defaultQuorumNumber); + ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; stakeRegistry.setOperatorWeight(uint8(quorumNumbers[0]), defaultOperator, defaultStake); cheats.prank(defaultOperator); - registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams); + registryCoordinator.registerOperator(quorumNumbers, defaultSocket, emptySig); cheats.expectRevert("RegistryCoordinator.onlyEjector: caller is not the ejector"); cheats.prank(defaultOperator); @@ -976,6 +972,6 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { }); } - blsApkRegistry.setBLSPublicKey(operatorToRegister, operatorToRegisterPubKey); + pubkeyCompendium.setBLSPublicKey(operatorToRegister, operatorToRegisterPubKey); } } diff --git a/test/unit/StakeRegistryUnit.t.sol b/test/unit/StakeRegistryUnit.t.sol index e6bd3c6a..7ab78b1e 100644 --- a/test/unit/StakeRegistryUnit.t.sol +++ b/test/unit/StakeRegistryUnit.t.sol @@ -103,6 +103,7 @@ contract StakeRegistryUnitTests is Test { cheats.startPrank(registryCoordinatorOwner); registryCoordinator = new RegistryCoordinatorHarness( + delegationMock, slasher, stakeRegistry, IBLSApkRegistry(apkRegistry), diff --git a/test/utils/MockAVSDeployer.sol b/test/utils/MockAVSDeployer.sol index 5e6b9cb5..45ea8c4c 100644 --- a/test/utils/MockAVSDeployer.sol +++ b/test/utils/MockAVSDeployer.sol @@ -7,11 +7,13 @@ import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.so import {Slasher} from "eigenlayer-contracts/src/contracts/core/Slasher.sol"; import {ISlasher} from "eigenlayer-contracts/src/contracts/interfaces/ISlasher.sol"; import {PauserRegistry} from "eigenlayer-contracts/src/contracts/permissions/PauserRegistry.sol"; +import {IStrategyManager} from "eigenlayer-contracts/src/contracts/interfaces/IStrategyManager.sol"; import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol"; import {BitmapUtils} from "src/libraries/BitmapUtils.sol"; import {BN254} from "src/libraries/BN254.sol"; +import {BLSPublicKeyCompendium} from "src/BLSPublicKeyCompendium.sol"; import {OperatorStateRetriever} from "src/OperatorStateRetriever.sol"; import {RegistryCoordinator} from "src/RegistryCoordinator.sol"; import {RegistryCoordinatorHarness} from "test/harnesses/RegistryCoordinatorHarness.sol"; @@ -26,13 +28,10 @@ import {IRegistryCoordinator} from "src/interfaces/IRegistryCoordinator.sol"; import {StrategyManagerMock} from "eigenlayer-contracts/src/test/mocks/StrategyManagerMock.sol"; import {EigenPodManagerMock} from "eigenlayer-contracts/src/test/mocks/EigenPodManagerMock.sol"; -<<<<<<< HEAD -import {ServiceManagerMock} from "test/mocks/ServiceManagerMock.sol"; -======= import {OwnableMock} from "eigenlayer-contracts/src/test/mocks/OwnableMock.sol"; ->>>>>>> ecf7849 (chore: remove ServiceManagerBase and add RegistryCoordinator owner (#98)) import {DelegationManagerMock} from "eigenlayer-contracts/src/test/mocks/DelegationManagerMock.sol"; -import {BLSApkRegistryHarness} from "test/harnesses/BLSApkRegistryHarness.sol"; +import {SlasherMock} from "eigenlayer-contracts/src/test/mocks/SlasherMock.sol"; +import {BLSPublicKeyCompendiumMock} from "test/mocks/BLSPublicKeyCompendiumMock.sol"; import {EmptyContract} from "eigenlayer-contracts/src/test/mocks/EmptyContract.sol"; import {StakeRegistryHarness} from "test/harnesses/StakeRegistryHarness.sol"; @@ -51,6 +50,7 @@ contract MockAVSDeployer is Test { Slasher public slasherImplementation; EmptyContract public emptyContract; + BLSPublicKeyCompendiumMock public pubkeyCompendium; RegistryCoordinatorHarness public registryCoordinatorImplementation; StakeRegistryHarness public stakeRegistryImplementation; @@ -60,7 +60,7 @@ contract MockAVSDeployer is Test { OperatorStateRetriever public operatorStateRetriever; RegistryCoordinatorHarness public registryCoordinator; StakeRegistryHarness public stakeRegistry; - BLSApkRegistryHarness public blsApkRegistry; + IBLSApkRegistry public blsApkRegistry; IIndexRegistry public indexRegistry; StrategyManagerMock public strategyManagerMock; @@ -77,7 +77,7 @@ contract MockAVSDeployer is Test { 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); @@ -97,8 +97,6 @@ contract MockAVSDeployer is Test { uint32 registrationBlockNumber = 100; uint32 blocksBetweenRegistrations = 10; - IBLSApkRegistry.PubkeyRegistrationParams pubkeyRegistrationParams; - struct OperatorMetadata { uint256 quorumBitmap; address operator; @@ -144,6 +142,10 @@ contract MockAVSDeployer is Test { eigenPodManagerMock, slasher ); + + pubkeyCompendium = new BLSPublicKeyCompendiumMock(); + pubkeyCompendium.setBLSPublicKey(defaultOperator, defaultPubKey); + cheats.stopPrank(); cheats.startPrank(registryCoordinatorOwner); @@ -175,7 +177,7 @@ contract MockAVSDeployer is Test { ) ); - blsApkRegistry = BLSApkRegistryHarness( + blsApkRegistry = BLSApkRegistry( address( new TransparentUpgradeableProxy( address(emptyContract), @@ -186,7 +188,6 @@ contract MockAVSDeployer is Test { ); cheats.stopPrank(); - cheats.startPrank(proxyAdminOwner); stakeRegistryImplementation = new StakeRegistryHarness( @@ -199,8 +200,9 @@ contract MockAVSDeployer is Test { address(stakeRegistryImplementation) ); - blsApkRegistryImplementation = new BLSApkRegistryHarness( - registryCoordinator + blsApkRegistryImplementation = new BLSApkRegistry( + registryCoordinator, + BLSPublicKeyCompendium(address(pubkeyCompendium)) ); proxyAdmin.upgrade( @@ -217,9 +219,6 @@ contract MockAVSDeployer is Test { address(indexRegistryImplementation) ); - // set the public key for an operator, using harnessed function to bypass checks - blsApkRegistry.setBLSPublicKey(defaultOperator, defaultPubKey); - // setup the dummy minimum stake for quorum uint96[] memory minimumStakeForQuorum = new uint96[](numQuorumsToAdd); for (uint256 i = 0; i < minimumStakeForQuorum.length; i++) { @@ -238,6 +237,7 @@ contract MockAVSDeployer is Test { } registryCoordinatorImplementation = new RegistryCoordinatorHarness( + delegationMock, slasher, stakeRegistry, blsApkRegistry, @@ -290,15 +290,16 @@ contract MockAVSDeployer is Test { // quorumBitmap can only have 192 least significant bits quorumBitmap &= MAX_QUORUM_BITMAP; - blsApkRegistry.setBLSPublicKey(operator, pubKey); + 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); } + ISignatureUtils.SignatureWithSaltAndExpiry memory emptySignatureAndExpiry; cheats.prank(operator); - registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams); + registryCoordinator.registerOperator(quorumNumbers, defaultSocket, emptySignatureAndExpiry); } /** @@ -308,15 +309,16 @@ contract MockAVSDeployer is Test { // quorumBitmap can only have 192 least significant bits quorumBitmap &= MAX_QUORUM_BITMAP; - blsApkRegistry.setBLSPublicKey(operator, pubKey); + 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])]); } + ISignatureUtils.SignatureWithSaltAndExpiry memory emptySignatureAndExpiry; cheats.prank(operator); - registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams); + registryCoordinator.registerOperator(quorumNumbers, defaultSocket, emptySignatureAndExpiry); } function _registerRandomOperators(uint256 pseudoRandomNumber) internal returns(OperatorMetadata[] memory, uint256[][] memory) { From 25bbad9ed332f1575da9e875ef9871dc7724f1b5 Mon Sep 17 00:00:00 2001 From: ChaoticWalrus <93558947+ChaoticWalrus@users.noreply.github.com> Date: Wed, 13 Dec 2023 12:12:01 -0800 Subject: [PATCH 077/101] chore: fix merge artifacts / broken calls these weren't caught in merging but were causing compiler errors --- test/integration/CoreRegistration.t.sol | 6 ++--- test/utils/MockAVSDeployer.sol | 34 +++++++++++-------------- 2 files changed, 18 insertions(+), 22 deletions(-) diff --git a/test/integration/CoreRegistration.t.sol b/test/integration/CoreRegistration.t.sol index 1df04dfc..fa16bbb3 100644 --- a/test/integration/CoreRegistration.t.sol +++ b/test/integration/CoreRegistration.t.sol @@ -57,7 +57,7 @@ contract Test_CoreRegistration is MockAVSDeployer { // Set operator address operator = cheats.addr(operatorPrivateKey); - pubkeyCompendium.setBLSPublicKey(operator, defaultPubKey); + blsApkRegistry.setBLSPublicKey(operator, defaultPubKey); // Register operator to EigenLayer cheats.prank(operator); @@ -91,7 +91,7 @@ contract Test_CoreRegistration is MockAVSDeployer { // Register operator cheats.prank(operator); - registryCoordinator.registerOperator(quorumNumbers, defaultSocket, operatorSignature); + registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, operatorSignature); // Check operator is registered IDelegationManager.OperatorAVSRegistrationStatus operatorStatus = delegationManager.avsOperatorStatus(address(registryCoordinator), operator); @@ -152,7 +152,7 @@ contract Test_CoreRegistration is MockAVSDeployer { // Register operator cheats.prank(operator); - registryCoordinator.registerOperator(quorumNumbers, defaultSocket, operatorSignature); + registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, operatorSignature); } function _getOperatorSignature( diff --git a/test/utils/MockAVSDeployer.sol b/test/utils/MockAVSDeployer.sol index 45ea8c4c..c7f5a757 100644 --- a/test/utils/MockAVSDeployer.sol +++ b/test/utils/MockAVSDeployer.sol @@ -7,13 +7,11 @@ import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.so import {Slasher} from "eigenlayer-contracts/src/contracts/core/Slasher.sol"; import {ISlasher} from "eigenlayer-contracts/src/contracts/interfaces/ISlasher.sol"; import {PauserRegistry} from "eigenlayer-contracts/src/contracts/permissions/PauserRegistry.sol"; -import {IStrategyManager} from "eigenlayer-contracts/src/contracts/interfaces/IStrategyManager.sol"; import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol"; import {BitmapUtils} from "src/libraries/BitmapUtils.sol"; import {BN254} from "src/libraries/BN254.sol"; -import {BLSPublicKeyCompendium} from "src/BLSPublicKeyCompendium.sol"; import {OperatorStateRetriever} from "src/OperatorStateRetriever.sol"; import {RegistryCoordinator} from "src/RegistryCoordinator.sol"; import {RegistryCoordinatorHarness} from "test/harnesses/RegistryCoordinatorHarness.sol"; @@ -28,10 +26,8 @@ import {IRegistryCoordinator} from "src/interfaces/IRegistryCoordinator.sol"; import {StrategyManagerMock} from "eigenlayer-contracts/src/test/mocks/StrategyManagerMock.sol"; import {EigenPodManagerMock} from "eigenlayer-contracts/src/test/mocks/EigenPodManagerMock.sol"; -import {OwnableMock} from "eigenlayer-contracts/src/test/mocks/OwnableMock.sol"; import {DelegationManagerMock} from "eigenlayer-contracts/src/test/mocks/DelegationManagerMock.sol"; -import {SlasherMock} from "eigenlayer-contracts/src/test/mocks/SlasherMock.sol"; -import {BLSPublicKeyCompendiumMock} from "test/mocks/BLSPublicKeyCompendiumMock.sol"; +import {BLSApkRegistryHarness} from "test/harnesses/BLSApkRegistryHarness.sol"; import {EmptyContract} from "eigenlayer-contracts/src/test/mocks/EmptyContract.sol"; import {StakeRegistryHarness} from "test/harnesses/StakeRegistryHarness.sol"; @@ -50,7 +46,6 @@ contract MockAVSDeployer is Test { Slasher public slasherImplementation; EmptyContract public emptyContract; - BLSPublicKeyCompendiumMock public pubkeyCompendium; RegistryCoordinatorHarness public registryCoordinatorImplementation; StakeRegistryHarness public stakeRegistryImplementation; @@ -60,7 +55,7 @@ contract MockAVSDeployer is Test { OperatorStateRetriever public operatorStateRetriever; RegistryCoordinatorHarness public registryCoordinator; StakeRegistryHarness public stakeRegistry; - IBLSApkRegistry public blsApkRegistry; + BLSApkRegistryHarness public blsApkRegistry; IIndexRegistry public indexRegistry; StrategyManagerMock public strategyManagerMock; @@ -97,6 +92,8 @@ contract MockAVSDeployer is Test { uint32 registrationBlockNumber = 100; uint32 blocksBetweenRegistrations = 10; + IBLSApkRegistry.PubkeyRegistrationParams pubkeyRegistrationParams; + struct OperatorMetadata { uint256 quorumBitmap; address operator; @@ -142,10 +139,6 @@ contract MockAVSDeployer is Test { eigenPodManagerMock, slasher ); - - pubkeyCompendium = new BLSPublicKeyCompendiumMock(); - pubkeyCompendium.setBLSPublicKey(defaultOperator, defaultPubKey); - cheats.stopPrank(); cheats.startPrank(registryCoordinatorOwner); @@ -177,7 +170,7 @@ contract MockAVSDeployer is Test { ) ); - blsApkRegistry = BLSApkRegistry( + blsApkRegistry = BLSApkRegistryHarness( address( new TransparentUpgradeableProxy( address(emptyContract), @@ -188,6 +181,7 @@ contract MockAVSDeployer is Test { ); cheats.stopPrank(); + cheats.startPrank(proxyAdminOwner); stakeRegistryImplementation = new StakeRegistryHarness( @@ -200,9 +194,8 @@ contract MockAVSDeployer is Test { address(stakeRegistryImplementation) ); - blsApkRegistryImplementation = new BLSApkRegistry( - registryCoordinator, - BLSPublicKeyCompendium(address(pubkeyCompendium)) + blsApkRegistryImplementation = new BLSApkRegistryHarness( + registryCoordinator ); proxyAdmin.upgrade( @@ -219,6 +212,9 @@ contract MockAVSDeployer is Test { address(indexRegistryImplementation) ); + // set the public key for an operator, using harnessed function to bypass checks + blsApkRegistry.setBLSPublicKey(defaultOperator, defaultPubKey); + // setup the dummy minimum stake for quorum uint96[] memory minimumStakeForQuorum = new uint96[](numQuorumsToAdd); for (uint256 i = 0; i < minimumStakeForQuorum.length; i++) { @@ -290,7 +286,7 @@ contract MockAVSDeployer is Test { // quorumBitmap can only have 192 least significant bits quorumBitmap &= MAX_QUORUM_BITMAP; - pubkeyCompendium.setBLSPublicKey(operator, pubKey); + blsApkRegistry.setBLSPublicKey(operator, pubKey); bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); for (uint i = 0; i < quorumNumbers.length; i++) { @@ -299,7 +295,7 @@ contract MockAVSDeployer is Test { ISignatureUtils.SignatureWithSaltAndExpiry memory emptySignatureAndExpiry; cheats.prank(operator); - registryCoordinator.registerOperator(quorumNumbers, defaultSocket, emptySignatureAndExpiry); + registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySignatureAndExpiry); } /** @@ -309,7 +305,7 @@ contract MockAVSDeployer is Test { // quorumBitmap can only have 192 least significant bits quorumBitmap &= MAX_QUORUM_BITMAP; - pubkeyCompendium.setBLSPublicKey(operator, pubKey); + blsApkRegistry.setBLSPublicKey(operator, pubKey); bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); for (uint i = 0; i < quorumNumbers.length; i++) { @@ -318,7 +314,7 @@ contract MockAVSDeployer is Test { ISignatureUtils.SignatureWithSaltAndExpiry memory emptySignatureAndExpiry; cheats.prank(operator); - registryCoordinator.registerOperator(quorumNumbers, defaultSocket, emptySignatureAndExpiry); + registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySignatureAndExpiry); } function _registerRandomOperators(uint256 pseudoRandomNumber) internal returns(OperatorMetadata[] memory, uint256[][] memory) { From 1598e0bb4752b9dd8e124b1c331231c227f2ee0f Mon Sep 17 00:00:00 2001 From: ChaoticWalrus <93558947+ChaoticWalrus@users.noreply.github.com> Date: Wed, 13 Dec 2023 12:13:56 -0800 Subject: [PATCH 078/101] chore: re-implement stack-too-deep fix I previously implemented this fix, then reverted it as not needed. Now upon merging with m2-mainnet, `registerOperatorWithChurn` has an additional input. This is reintroducing the same stack-too-deep error so I am reintroducing the same fix (one less local var) --- src/RegistryCoordinator.sol | 43 +++++++++++++++++++++++++++++++++---- 1 file changed, 39 insertions(+), 4 deletions(-) diff --git a/src/RegistryCoordinator.sol b/src/RegistryCoordinator.sol index 714285bb..3bef4fea 100644 --- a/src/RegistryCoordinator.sol +++ b/src/RegistryCoordinator.sol @@ -19,6 +19,7 @@ import {IStakeRegistry} from "src/interfaces/IStakeRegistry.sol"; import {IIndexRegistry} from "src/interfaces/IIndexRegistry.sol"; import {BitmapUtils} from "src/libraries/BitmapUtils.sol"; +import {BN254} from "src/libraries/BN254.sol"; /** * @title A `RegistryCoordinator` that has three registries: @@ -38,10 +39,13 @@ contract RegistryCoordinator is ISignatureUtils { using BitmapUtils for *; + 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,bytes32[] operatorIdsToSwap)"); + /// @notice The EIP-712 typehash used for registering BLS public keys + bytes32 public constant PUBKEY_REGISTRATION_TYPEHASH = keccak256("BN254PubkeyRegistration(address operator)"); /// @notice The maximum value of a quorum bitmap uint256 internal constant MAX_QUORUM_BITMAP = type(uint192).max; /// @notice The basis point denominator @@ -157,14 +161,24 @@ contract RegistryCoordinator is * operator capacity, this method will fail. * @param quorumNumbers is an ordered byte array containing the quorum numbers being registered for * @param socket is the socket of the operator + * @param params contains the G1 & G2 public keys of the operator, and a signature proving their ownership + * @dev the `params` input param is ignored if the caller has previously registered a public key * @param operatorSignature is the signature of the operator used by the AVS to register the operator in the delegation manager */ function registerOperator( bytes calldata quorumNumbers, string calldata socket, + IBLSApkRegistry.PubkeyRegistrationParams calldata params, SignatureWithSaltAndExpiry memory operatorSignature ) external onlyWhenNotPaused(PAUSED_REGISTER_OPERATOR) { + /** + * IF the operator has never registered a pubkey before, THEN register their pubkey + * OTHERWISE, simply ignore the provided `params` + */ bytes32 operatorId = blsApkRegistry.getOperatorId(msg.sender); + if (operatorId == 0) { + operatorId = blsApkRegistry.registerBLSPublicKey(msg.sender, params, pubkeyRegistrationMessageHash(msg.sender)); + } // Register the operator in each of the registry contracts RegisterResults memory results = _registerOperator({ @@ -195,21 +209,31 @@ contract RegistryCoordinator is * @notice Registers msg.sender as an operator for one or more quorums. If any quorum reaches its maximum operator * capacity, `operatorKickParams` is used to replace an old operator with the new one. * @param quorumNumbers is an ordered byte array containing the quorum numbers being registered for + * @param params contains the G1 & G2 public keys of the operator, and a signature proving their ownership * @param operatorKickParams are used to determine which operator is removed to maintain quorum capacity as the * operator registers for quorums. * @param churnApproverSignature is the signature of the churnApprover on the operator kick params * @param operatorSignature is the signature of the operator used by the AVS to register the operator in the delegation manager + * @dev the `params` input param is ignored if the caller has previously registered a public key */ function registerOperatorWithChurn( bytes calldata quorumNumbers, string calldata socket, + IBLSApkRegistry.PubkeyRegistrationParams calldata params, OperatorKickParam[] calldata operatorKickParams, SignatureWithSaltAndExpiry memory churnApproverSignature, SignatureWithSaltAndExpiry memory operatorSignature ) external onlyWhenNotPaused(PAUSED_REGISTER_OPERATOR) { require(operatorKickParams.length == quorumNumbers.length, "RegistryCoordinator.registerOperatorWithChurn: input length mismatch"); + /** + * IF the operator has never registered a pubkey before, THEN register their pubkey + * OTHERWISE, simply ignore the `pubkeyRegistrationSignature`, `pubkeyG1`, and `pubkeyG2` inputs + */ bytes32 operatorId = blsApkRegistry.getOperatorId(msg.sender); + if (operatorId == 0) { + operatorId = blsApkRegistry.registerBLSPublicKey(msg.sender, params, pubkeyRegistrationMessageHash(msg.sender)); + } // Verify the churn approver's signature for the registering operator and kick params _verifyChurnApproverSignature({ @@ -228,9 +252,8 @@ contract RegistryCoordinator is }); for (uint256 i = 0; i < quorumNumbers.length; i++) { - uint8 quorumNumber = uint8(quorumNumbers[i]); - - OperatorSetParam memory operatorSetParams = _quorumParams[quorumNumber]; + // reference: uint8 quorumNumber = uint8(quorumNumbers[i]); + OperatorSetParam memory operatorSetParams = _quorumParams[uint8(quorumNumbers[i])]; /** * If the new operator count for any quorum exceeds the maximum, validate @@ -238,7 +261,7 @@ contract RegistryCoordinator is */ if (results.numOperatorsPerQuorum[i] > operatorSetParams.maxOperatorCount) { _validateChurn({ - quorumNumber: quorumNumber, + quorumNumber: uint8(quorumNumbers[i]), totalQuorumStake: results.totalStakes[i], newOperator: msg.sender, newOperatorStake: results.operatorStakes[i], @@ -838,6 +861,18 @@ contract RegistryCoordinator is return _hashTypedDataV4(keccak256(abi.encode(OPERATOR_CHURN_APPROVAL_TYPEHASH, registeringOperatorId, operatorKickParams, salt, expiry))); } + /** + * @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 pubkeyRegistrationMessageHash(address operator) public view returns (BN254.G1Point memory) { + return BN254.hashToG1( + _hashTypedDataV4( + keccak256(abi.encode(PUBKEY_REGISTRATION_TYPEHASH, operator)) + ) + ); + } + /// @dev need to override function here since its defined in both these contracts function owner() public From 57c9a22d1a99c57c9c7601a2719f8ca085f53a6d Mon Sep 17 00:00:00 2001 From: ChaoticWalrus <93558947+ChaoticWalrus@users.noreply.github.com> Date: Wed, 13 Dec 2023 12:37:25 -0800 Subject: [PATCH 079/101] chore: de-duplicate code into an internal function see suggestion here: https://github.com/Layr-Labs/eigenlayer-middleware/pull/102#discussion_r1425509482 saves ~0.25kb in code size --- src/RegistryCoordinator.sol | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/src/RegistryCoordinator.sol b/src/RegistryCoordinator.sol index 3bef4fea..3a84a0f9 100644 --- a/src/RegistryCoordinator.sol +++ b/src/RegistryCoordinator.sol @@ -173,12 +173,9 @@ contract RegistryCoordinator is ) external onlyWhenNotPaused(PAUSED_REGISTER_OPERATOR) { /** * IF the operator has never registered a pubkey before, THEN register their pubkey - * OTHERWISE, simply ignore the provided `params` + * OTHERWISE, simply ignore the provided `params` input */ - bytes32 operatorId = blsApkRegistry.getOperatorId(msg.sender); - if (operatorId == 0) { - operatorId = blsApkRegistry.registerBLSPublicKey(msg.sender, params, pubkeyRegistrationMessageHash(msg.sender)); - } + bytes32 operatorId = _getOrCreateOperatorId(msg.sender, params); // Register the operator in each of the registry contracts RegisterResults memory results = _registerOperator({ @@ -228,12 +225,9 @@ contract RegistryCoordinator is /** * IF the operator has never registered a pubkey before, THEN register their pubkey - * OTHERWISE, simply ignore the `pubkeyRegistrationSignature`, `pubkeyG1`, and `pubkeyG2` inputs + * OTHERWISE, simply ignore the provided `params` input */ - bytes32 operatorId = blsApkRegistry.getOperatorId(msg.sender); - if (operatorId == 0) { - operatorId = blsApkRegistry.registerBLSPublicKey(msg.sender, params, pubkeyRegistrationMessageHash(msg.sender)); - } + bytes32 operatorId = _getOrCreateOperatorId(msg.sender, params); // Verify the churn approver's signature for the registering operator and kick params _verifyChurnApproverSignature({ @@ -519,6 +513,17 @@ contract RegistryCoordinator is }); } + function _getOrCreateOperatorId( + address operator, + IBLSApkRegistry.PubkeyRegistrationParams calldata params + ) internal returns (bytes32 operatorId) { + operatorId = blsApkRegistry.getOperatorId(msg.sender); + if (operatorId == 0) { + operatorId = blsApkRegistry.registerBLSPublicKey(msg.sender, params, pubkeyRegistrationMessageHash(msg.sender)); + } + return operatorId; + } + function _validateChurn( uint8 quorumNumber, uint96 totalQuorumStake, From 3e1ac840ae32c5b001e30b54a496b16733366fbe Mon Sep 17 00:00:00 2001 From: ChaoticWalrus <93558947+ChaoticWalrus@users.noreply.github.com> Date: Wed, 13 Dec 2023 12:53:40 -0800 Subject: [PATCH 080/101] fix: actually use the `operator` input to `_getOrCreateOperatorId` also reduce memory copying operations (I think, at least) in the `registerOperator` function specifically, only copy the `numOperatorsPerQuorum` returned by the internal `_registerOperator` function --- src/RegistryCoordinator.sol | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/RegistryCoordinator.sol b/src/RegistryCoordinator.sol index 3a84a0f9..d4e2f930 100644 --- a/src/RegistryCoordinator.sol +++ b/src/RegistryCoordinator.sol @@ -178,25 +178,23 @@ contract RegistryCoordinator is bytes32 operatorId = _getOrCreateOperatorId(msg.sender, params); // Register the operator in each of the registry contracts - RegisterResults memory results = _registerOperator({ + uint32[] memory numOperatorsPerQuorum = _registerOperator({ operator: msg.sender, operatorId: operatorId, quorumNumbers: quorumNumbers, socket: socket, operatorSignature: operatorSignature - }); + }).numOperatorsPerQuorum; for (uint256 i = 0; i < quorumNumbers.length; i++) { uint8 quorumNumber = uint8(quorumNumbers[i]); - - OperatorSetParam memory operatorSetParams = _quorumParams[quorumNumber]; - + /** * The new operator count for each quorum may not exceed the configured maximum * If it does, use `registerOperatorWithChurn` instead. */ require( - results.numOperatorsPerQuorum[i] <= operatorSetParams.maxOperatorCount, + numOperatorsPerQuorum[i] <= _quorumParams[quorumNumber].maxOperatorCount, "RegistryCoordinator.registerOperator: operator count exceeds maximum" ); } @@ -517,9 +515,9 @@ contract RegistryCoordinator is address operator, IBLSApkRegistry.PubkeyRegistrationParams calldata params ) internal returns (bytes32 operatorId) { - operatorId = blsApkRegistry.getOperatorId(msg.sender); + operatorId = blsApkRegistry.getOperatorId(operator); if (operatorId == 0) { - operatorId = blsApkRegistry.registerBLSPublicKey(msg.sender, params, pubkeyRegistrationMessageHash(msg.sender)); + operatorId = blsApkRegistry.registerBLSPublicKey(operator, params, pubkeyRegistrationMessageHash(operator)); } return operatorId; } From bb228bbe122845f885ed8fc0d8c69825b564ec0a Mon Sep 17 00:00:00 2001 From: ChaoticWalrus <93558947+ChaoticWalrus@users.noreply.github.com> Date: Wed, 13 Dec 2023 13:11:25 -0800 Subject: [PATCH 081/101] feat: add optimizer runs count to foundry config --- foundry.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/foundry.toml b/foundry.toml index 6a48adda..5c1f5469 100644 --- a/foundry.toml +++ b/foundry.toml @@ -6,4 +6,7 @@ fs_permissions = [{ access = "read-write", path = "./" }] ffi = true +# The number of optimizer runs +optimizer_runs = 200 + # See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options From 7b8e7c803788572587015e3481b565bd766fa3ab Mon Sep 17 00:00:00 2001 From: ChaoticWalrus <93558947+ChaoticWalrus@users.noreply.github.com> Date: Wed, 13 Dec 2023 13:18:12 -0800 Subject: [PATCH 082/101] chore: remove redundant check and return data The `operatorId` in this check is already fetched from the `blsApkRegistry` With the other changes, this return data is no longer necessary at all --- src/BLSApkRegistry.sol | 4 +--- src/RegistryCoordinator.sol | 3 +-- src/interfaces/IBLSApkRegistry.sol | 2 +- test/unit/BLSApkRegistryUnit.t.sol | 8 +++----- 4 files changed, 6 insertions(+), 11 deletions(-) diff --git a/src/BLSApkRegistry.sol b/src/BLSApkRegistry.sol index 71e3a77b..fe16c7fa 100644 --- a/src/BLSApkRegistry.sol +++ b/src/BLSApkRegistry.sol @@ -32,7 +32,6 @@ contract BLSApkRegistry is BLSApkRegistryStorage { * @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. - * @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 @@ -43,7 +42,7 @@ contract BLSApkRegistry is BLSApkRegistryStorage { function registerOperator( address operator, bytes memory quorumNumbers - ) public virtual onlyRegistryCoordinator returns (bytes32) { + ) public virtual onlyRegistryCoordinator { // Get the operator's pubkey. Reverts if they have not registered a key (BN254.G1Point memory pubkey, bytes32 pubkeyHash) = getRegisteredPubkey(operator); @@ -52,7 +51,6 @@ contract BLSApkRegistry is BLSApkRegistryStorage { // Return pubkeyHash, which will become the operator's unique id emit OperatorAddedToQuorums(operator, quorumNumbers); - return pubkeyHash; } /** diff --git a/src/RegistryCoordinator.sol b/src/RegistryCoordinator.sol index d4e2f930..810ef6cb 100644 --- a/src/RegistryCoordinator.sol +++ b/src/RegistryCoordinator.sol @@ -498,8 +498,7 @@ contract RegistryCoordinator is /** * Register the operator with the BLSApkRegistry, StakeRegistry, and IndexRegistry */ - bytes32 registeredId = blsApkRegistry.registerOperator(operator, quorumNumbers); - require(registeredId == operatorId, "RegistryCoordinator._registerOperator: operatorId mismatch"); + blsApkRegistry.registerOperator(operator, quorumNumbers); (uint96[] memory operatorStakes, uint96[] memory totalStakes) = stakeRegistry.registerOperator(operator, operatorId, quorumNumbers); uint32[] memory numOperatorsPerQuorum = indexRegistry.registerOperator(operatorId, quorumNumbers); diff --git a/src/interfaces/IBLSApkRegistry.sol b/src/interfaces/IBLSApkRegistry.sol index 916320ec..62a3e6b1 100644 --- a/src/interfaces/IBLSApkRegistry.sol +++ b/src/interfaces/IBLSApkRegistry.sol @@ -60,7 +60,7 @@ interface IBLSApkRegistry is IRegistry { * 3) `quorumNumbers` is ordered in ascending order * 4) the operator is not already registered */ - function registerOperator(address operator, bytes calldata quorumNumbers) external returns(bytes32); + function registerOperator(address operator, bytes calldata quorumNumbers) external; /** * @notice Deregisters the `operator`'s pubkey for the specified `quorumNumbers`. diff --git a/test/unit/BLSApkRegistryUnit.t.sol b/test/unit/BLSApkRegistryUnit.t.sol index 0d8ed07a..81c02654 100644 --- a/test/unit/BLSApkRegistryUnit.t.sol +++ b/test/unit/BLSApkRegistryUnit.t.sol @@ -92,13 +92,11 @@ contract BLSApkRegistryUnitTests is Test { bytes memory quorumNumbers = new bytes(1); quorumNumbers[0] = bytes1(defaultQuorumNumber); - cheats.startPrank(address(registryCoordinator)); - bytes32 registeredpkHash = blsApkRegistry.registerOperator(operator, quorumNumbers); - cheats.stopPrank(); - + cheats.prank(address(registryCoordinator)); + blsApkRegistry.registerOperator(operator, quorumNumbers); + (, bytes32 registeredpkHash) = blsApkRegistry.getRegisteredPubkey(operator); require(registeredpkHash == pkHash, "registeredpkHash not set correctly"); - emit log("ehey"); return pkHash; } From 055ddec2225fc145c1fcd1fc7927e9315c06d0b2 Mon Sep 17 00:00:00 2001 From: ChaoticWalrus <93558947+ChaoticWalrus@users.noreply.github.com> Date: Wed, 13 Dec 2023 13:20:35 -0800 Subject: [PATCH 083/101] chore: fewer memory operations(?) I believe this change cuts down on the memory copying being done here --- src/RegistryCoordinator.sol | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/RegistryCoordinator.sol b/src/RegistryCoordinator.sol index 810ef6cb..041e7586 100644 --- a/src/RegistryCoordinator.sol +++ b/src/RegistryCoordinator.sol @@ -458,7 +458,7 @@ contract RegistryCoordinator is bytes calldata quorumNumbers, string memory socket, SignatureWithSaltAndExpiry memory operatorSignature - ) internal virtual returns (RegisterResults memory) { + ) internal virtual returns (RegisterResults memory results) { /** * Get bitmap of quorums to register for and operator's current bitmap. Validate that: * - we're trying to register for at least 1 quorum @@ -499,15 +499,11 @@ contract RegistryCoordinator is * Register the operator with the BLSApkRegistry, StakeRegistry, and IndexRegistry */ blsApkRegistry.registerOperator(operator, quorumNumbers); - (uint96[] memory operatorStakes, uint96[] memory totalStakes) = + (results.operatorStakes, results.totalStakes) = stakeRegistry.registerOperator(operator, operatorId, quorumNumbers); - uint32[] memory numOperatorsPerQuorum = indexRegistry.registerOperator(operatorId, quorumNumbers); + results.numOperatorsPerQuorum = indexRegistry.registerOperator(operatorId, quorumNumbers); - return RegisterResults({ - numOperatorsPerQuorum: numOperatorsPerQuorum, - operatorStakes: operatorStakes, - totalStakes: totalStakes - }); + return results; } function _getOrCreateOperatorId( From 4b026fd1d9495cae252b1a87b3a17231db439817 Mon Sep 17 00:00:00 2001 From: ChaoticWalrus <93558947+ChaoticWalrus@users.noreply.github.com> Date: Wed, 13 Dec 2023 13:25:26 -0800 Subject: [PATCH 084/101] fix: reduce optimizer runs to meet contract code size limits Likely not the ideal fix, but it gets the job done for the moment, at least. --- foundry.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/foundry.toml b/foundry.toml index 5c1f5469..f8f557c1 100644 --- a/foundry.toml +++ b/foundry.toml @@ -7,6 +7,6 @@ fs_permissions = [{ access = "read-write", path = "./" }] ffi = true # The number of optimizer runs -optimizer_runs = 200 +optimizer_runs = 100 # See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options From e40e59c62e51d9cbdb0de08daf015d23e895cd04 Mon Sep 17 00:00:00 2001 From: ChaoticWalrus <93558947+ChaoticWalrus@users.noreply.github.com> Date: Wed, 13 Dec 2023 13:39:13 -0800 Subject: [PATCH 085/101] chore: rename file to reflect it only being used in tests `RegistryCoordinatorHarness.sol` -> `RegistryCoordinatorHarness.t.sol` --- src/RegistryCoordinator.sol | 2 +- ...yCoordinatorHarness.sol => RegistryCoordinatorHarness.t.sol} | 0 test/unit/StakeRegistryUnit.t.sol | 2 +- test/utils/MockAVSDeployer.sol | 2 +- 4 files changed, 3 insertions(+), 3 deletions(-) rename test/harnesses/{RegistryCoordinatorHarness.sol => RegistryCoordinatorHarness.t.sol} (100%) diff --git a/src/RegistryCoordinator.sol b/src/RegistryCoordinator.sol index 041e7586..b86f9dc6 100644 --- a/src/RegistryCoordinator.sol +++ b/src/RegistryCoordinator.sol @@ -432,7 +432,7 @@ contract RegistryCoordinator is /** * @notice Sets the metadata URI for the AVS * @param _metadataURI is the metadata URI for the AVS - * @dev only callable by the service manager owner + * @dev only callable by the owner */ function setMetadataURI(string memory _metadataURI) external onlyOwner { delegationManager.updateAVSMetadataURI(_metadataURI); diff --git a/test/harnesses/RegistryCoordinatorHarness.sol b/test/harnesses/RegistryCoordinatorHarness.t.sol similarity index 100% rename from test/harnesses/RegistryCoordinatorHarness.sol rename to test/harnesses/RegistryCoordinatorHarness.t.sol diff --git a/test/unit/StakeRegistryUnit.t.sol b/test/unit/StakeRegistryUnit.t.sol index 7ab78b1e..405a542b 100644 --- a/test/unit/StakeRegistryUnit.t.sol +++ b/test/unit/StakeRegistryUnit.t.sol @@ -25,7 +25,7 @@ import {SlasherMock} from "eigenlayer-contracts/src/test/mocks/SlasherMock.sol"; import {StakeRegistryHarness} from "test/harnesses/StakeRegistryHarness.sol"; import {StakeRegistry} from "src/StakeRegistry.sol"; -import {RegistryCoordinatorHarness} from "test/harnesses/RegistryCoordinatorHarness.sol"; +import {RegistryCoordinatorHarness} from "test/harnesses/RegistryCoordinatorHarness.t.sol"; import "forge-std/Test.sol"; diff --git a/test/utils/MockAVSDeployer.sol b/test/utils/MockAVSDeployer.sol index c7f5a757..118cbd03 100644 --- a/test/utils/MockAVSDeployer.sol +++ b/test/utils/MockAVSDeployer.sol @@ -14,7 +14,7 @@ import {BN254} from "src/libraries/BN254.sol"; import {OperatorStateRetriever} from "src/OperatorStateRetriever.sol"; import {RegistryCoordinator} from "src/RegistryCoordinator.sol"; -import {RegistryCoordinatorHarness} from "test/harnesses/RegistryCoordinatorHarness.sol"; +import {RegistryCoordinatorHarness} from "test/harnesses/RegistryCoordinatorHarness.t.sol"; import {BLSApkRegistry} from "src/BLSApkRegistry.sol"; import {StakeRegistry} from "src/StakeRegistry.sol"; import {IndexRegistry} from "src/IndexRegistry.sol"; From e53a400c2593ffe482543f518a8dec1792f1e10b Mon Sep 17 00:00:00 2001 From: ChaoticWalrus <93558947+ChaoticWalrus@users.noreply.github.com> Date: Wed, 13 Dec 2023 13:40:22 -0800 Subject: [PATCH 086/101] chore: delete unused (memory) variable --- src/BLSApkRegistry.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/BLSApkRegistry.sol b/src/BLSApkRegistry.sol index fe16c7fa..a732a44a 100644 --- a/src/BLSApkRegistry.sol +++ b/src/BLSApkRegistry.sol @@ -44,7 +44,7 @@ contract BLSApkRegistry is BLSApkRegistryStorage { bytes memory quorumNumbers ) public virtual onlyRegistryCoordinator { // Get the operator's pubkey. Reverts if they have not registered a key - (BN254.G1Point memory pubkey, bytes32 pubkeyHash) = getRegisteredPubkey(operator); + (BN254.G1Point memory pubkey, ) = getRegisteredPubkey(operator); // Update each quorum's aggregate pubkey _processQuorumApkUpdate(quorumNumbers, pubkey); From 851849abe5b438901f40bc792a380f0081cac1f1 Mon Sep 17 00:00:00 2001 From: wadealexc Date: Wed, 13 Dec 2023 21:55:09 +0000 Subject: [PATCH 087/101] fix: have the harness import Test so it gets ignored in build sizes --- test/harnesses/RegistryCoordinatorHarness.t.sol | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/harnesses/RegistryCoordinatorHarness.t.sol b/test/harnesses/RegistryCoordinatorHarness.t.sol index 246ea705..f3ce41a7 100644 --- a/test/harnesses/RegistryCoordinatorHarness.t.sol +++ b/test/harnesses/RegistryCoordinatorHarness.t.sol @@ -3,8 +3,10 @@ pragma solidity =0.8.12; import "src/RegistryCoordinator.sol"; +import "forge-std/Test.sol"; + // wrapper around the RegistryCoordinator contract that exposes the internal functions for unit testing. -contract RegistryCoordinatorHarness is RegistryCoordinator { +contract RegistryCoordinatorHarness is RegistryCoordinator, Test { constructor( IDelegationManager _delegationManager, ISlasher _slasher, From 7a6e4a8d8a5917aeeff715ad15baa57d124eb97f Mon Sep 17 00:00:00 2001 From: Alex <18387287+wadealexc@users.noreply.github.com> Date: Wed, 13 Dec 2023 17:41:59 -0500 Subject: [PATCH 088/101] fix: remove circular dependency (#108) --- src/BLSApkRegistryStorage.sol | 4 ++-- src/IndexRegistryStorage.sol | 4 ++-- src/StakeRegistry.sol | 2 +- src/StakeRegistryStorage.sol | 4 ++-- src/interfaces/IRegistry.sol | 4 +--- test/mocks/StakeRegistryMock.sol | 2 +- test/unit/BLSApkRegistryUnit.t.sol | 2 +- 7 files changed, 10 insertions(+), 12 deletions(-) diff --git a/src/BLSApkRegistryStorage.sol b/src/BLSApkRegistryStorage.sol index 4676a6f5..878d8a10 100644 --- a/src/BLSApkRegistryStorage.sol +++ b/src/BLSApkRegistryStorage.sol @@ -13,7 +13,7 @@ abstract contract BLSApkRegistryStorage is Initializable, IBLSApkRegistry { bytes32 internal constant ZERO_PK_HASH = hex"ad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5"; /// @notice the registry coordinator contract - IRegistryCoordinator public immutable registryCoordinator; + address public immutable registryCoordinator; // storage for individual pubkeys /// @notice maps operator address to pubkey hash @@ -30,7 +30,7 @@ abstract contract BLSApkRegistryStorage is Initializable, IBLSApkRegistry { mapping(uint8 => BN254.G1Point) public currentApk; constructor(IRegistryCoordinator _registryCoordinator) { - registryCoordinator = _registryCoordinator; + registryCoordinator = address(_registryCoordinator); // disable initializers so that the implementation contract cannot be initialized _disableInitializers(); } diff --git a/src/IndexRegistryStorage.sol b/src/IndexRegistryStorage.sol index 570d5fa8..2b4a57eb 100644 --- a/src/IndexRegistryStorage.sol +++ b/src/IndexRegistryStorage.sol @@ -17,7 +17,7 @@ abstract contract IndexRegistryStorage is Initializable, IIndexRegistry { bytes32 public constant OPERATOR_DOES_NOT_EXIST_ID = bytes32(0); /// @notice The RegistryCoordinator contract for this middleware - IRegistryCoordinator public immutable registryCoordinator; + address public immutable registryCoordinator; /// @notice maps quorumNumber => operator id => current index mapping(uint8 => mapping(bytes32 => uint32)) public currentOperatorIndex; @@ -29,7 +29,7 @@ abstract contract IndexRegistryStorage is Initializable, IIndexRegistry { constructor( IRegistryCoordinator _registryCoordinator ){ - registryCoordinator = _registryCoordinator; + registryCoordinator = address(_registryCoordinator); // disable initializers so that the implementation contract cannot be initialized _disableInitializers(); } diff --git a/src/StakeRegistry.sol b/src/StakeRegistry.sol index c4bca769..6eb09808 100644 --- a/src/StakeRegistry.sol +++ b/src/StakeRegistry.sol @@ -30,7 +30,7 @@ contract StakeRegistry is StakeRegistryStorage { } modifier onlyCoordinatorOwner() { - require(msg.sender == registryCoordinator.owner(), "StakeRegistry.onlyCoordinatorOwner: caller is not the owner of the registryCoordinator"); + require(msg.sender == IRegistryCoordinator(registryCoordinator).owner(), "StakeRegistry.onlyCoordinatorOwner: caller is not the owner of the registryCoordinator"); _; } diff --git a/src/StakeRegistryStorage.sol b/src/StakeRegistryStorage.sol index 81b4ba42..30f8365e 100644 --- a/src/StakeRegistryStorage.sol +++ b/src/StakeRegistryStorage.sol @@ -25,7 +25,7 @@ abstract contract StakeRegistryStorage is IStakeRegistry { IDelegationManager public immutable delegation; /// @notice the coordinator contract that this registry is associated with - IRegistryCoordinator public immutable registryCoordinator; + address 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. @@ -47,7 +47,7 @@ abstract contract StakeRegistryStorage is IStakeRegistry { IRegistryCoordinator _registryCoordinator, IDelegationManager _delegationManager ) { - registryCoordinator = _registryCoordinator; + registryCoordinator = address(_registryCoordinator); delegation = _delegationManager; } diff --git a/src/interfaces/IRegistry.sol b/src/interfaces/IRegistry.sol index d5bfacb8..8a483cba 100644 --- a/src/interfaces/IRegistry.sol +++ b/src/interfaces/IRegistry.sol @@ -1,8 +1,6 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity >=0.5.0; -import {IRegistryCoordinator} from "./IRegistryCoordinator.sol"; - /** * @title Minimal interface for a `Registry`-type contract. * @author Layr Labs, Inc. @@ -11,5 +9,5 @@ import {IRegistryCoordinator} from "./IRegistryCoordinator.sol"; * because their function signatures may vary significantly. */ interface IRegistry { - function registryCoordinator() external view returns (IRegistryCoordinator); + function registryCoordinator() external view returns (address); } diff --git a/test/mocks/StakeRegistryMock.sol b/test/mocks/StakeRegistryMock.sol index a092944c..455163a1 100644 --- a/test/mocks/StakeRegistryMock.sol +++ b/test/mocks/StakeRegistryMock.sol @@ -10,7 +10,7 @@ import "../../src/interfaces/IRegistryCoordinator.sol"; */ contract StakeRegistryMock is IStakeRegistry { - function registryCoordinator() external view returns (IRegistryCoordinator) {} + function registryCoordinator() external view returns (address) {} /** * @notice Registers the `operator` with `operatorId` for the specified `quorumNumbers`. diff --git a/test/unit/BLSApkRegistryUnit.t.sol b/test/unit/BLSApkRegistryUnit.t.sol index 81c02654..3a055f13 100644 --- a/test/unit/BLSApkRegistryUnit.t.sol +++ b/test/unit/BLSApkRegistryUnit.t.sol @@ -51,7 +51,7 @@ contract BLSApkRegistryUnitTests is Test { } function testConstructorArgs() public view { - require(blsApkRegistry.registryCoordinator() == registryCoordinator, "registryCoordinator not set correctly"); + require(blsApkRegistry.registryCoordinator() == address(registryCoordinator), "registryCoordinator not set correctly"); } function testCallRegisterOperatorFromNonCoordinatorAddress(address nonCoordinatorAddress) public { From cb63e215d5654f39720ab54ebfae0c38557ea8a4 Mon Sep 17 00:00:00 2001 From: Alex <18387287+wadealexc@users.noreply.github.com> Date: Thu, 14 Dec 2023 11:02:50 -0500 Subject: [PATCH 089/101] fix: address multiple issues in BLSSignatureChecker, optimize, and clean (#104) * fix: swap sign on sig checker withdrawal delay check * fix: tiny_scalar_mul in BN254 and additional cleanup - unify validateXAtBlockNumber style checks in registries - checkSignatures changes below - rename variables to be more readable - validate input lengths for quorum and nonsigner params - create signingQuorumBitmap using method with additional validation to avoid duplicates - clarify commenting and style to be more consistent with rest of codebase - cache state to avoid lookups in loop - move total stake query to initial loop * fix: refactor checkSignatures to remove an unneeded loop - also optimizes BN254.hashG1Point - also refactors checkSignatures to avoid negating pubkeys multiple times * fix: stale stakes check should care about referenceBlockNumber * fix: remove repeated storage read from loop * chore: remove TODOs --- src/BLSApkRegistry.sol | 32 +++-- src/BLSSignatureChecker.sol | 217 ++++++++++++++++++++------------- src/RegistryCoordinator.sol | 13 +- src/StakeRegistry.sol | 9 +- src/libraries/BN254.sol | 12 +- test/mocks/DelegationMock.sol | 185 ++++++++++++++++++++++++++++ test/utils/MockAVSDeployer.sol | 6 +- 7 files changed, 360 insertions(+), 114 deletions(-) create mode 100644 test/mocks/DelegationMock.sol diff --git a/src/BLSApkRegistry.sol b/src/BLSApkRegistry.sol index a732a44a..ea6d318f 100644 --- a/src/BLSApkRegistry.sol +++ b/src/BLSApkRegistry.sol @@ -177,22 +177,6 @@ contract BLSApkRegistry is BLSApkRegistryStorage { } } - // TODO - should this fail if apkUpdate.apkHash == 0? This will be the case for the first entry in each quorum - function _validateApkHashAtBlockNumber(ApkUpdate memory apkUpdate, uint32 blockNumber) internal pure { - require( - blockNumber >= apkUpdate.updateBlockNumber, - "BLSApkRegistry._validateApkHashAtBlockNumber: 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, - "BLSApkRegistry._validateApkHashAtBlockNumber: not latest apk update" - ); - } - /******************************************************************************* VIEW FUNCTIONS *******************************************************************************/ @@ -264,7 +248,21 @@ contract BLSApkRegistry is BLSApkRegistryStorage { uint256 index ) external view returns (bytes24) { ApkUpdate memory quorumApkUpdate = apkHistory[quorumNumber][index]; - _validateApkHashAtBlockNumber(quorumApkUpdate, blockNumber); + + /** + * Validate that the update is valid for the given blockNumber: + * - blockNumber should be >= the update block number + * - the next update block number should be either 0 or strictly greater than blockNumber + */ + require( + blockNumber >= quorumApkUpdate.updateBlockNumber, + "BLSApkRegistry._validateApkHashAtBlockNumber: index too recent" + ); + require( + quorumApkUpdate.nextUpdateBlockNumber == 0 || blockNumber < quorumApkUpdate.nextUpdateBlockNumber, + "BLSApkRegistry._validateApkHashAtBlockNumber: not latest apk update" + ); + return quorumApkUpdate.apkHash; } diff --git a/src/BLSSignatureChecker.sol b/src/BLSSignatureChecker.sol index accbba81..99d501ba 100644 --- a/src/BLSSignatureChecker.sol +++ b/src/BLSSignatureChecker.sol @@ -54,6 +54,11 @@ contract BLSSignatureChecker is IBLSSignatureChecker { emit StaleStakesForbiddenUpdate(value); } + struct NonSignerInfo { + uint256[] quorumBitmaps; + bytes32[] pubkeyHashes; + } + /** * @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 @@ -72,7 +77,7 @@ contract BLSSignatureChecker is IBLSSignatureChecker { * @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 + * @param params 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 */ @@ -80,7 +85,7 @@ contract BLSSignatureChecker is IBLSSignatureChecker { bytes32 msgHash, bytes calldata quorumNumbers, uint32 referenceBlockNumber, - NonSignerStakesAndSignature memory nonSignerStakesAndSignature + NonSignerStakesAndSignature memory params ) public view @@ -88,101 +93,145 @@ contract BLSSignatureChecker is IBLSSignatureChecker { QuorumStakeTotals memory, bytes32 ) - { - // verify the provided apk was the apk at referenceBlockNumber - // loop through every quorumNumber and keep track of the apk + { + require( + (quorumNumbers.length == params.quorumApks.length) && + (quorumNumbers.length == params.quorumApkIndices.length) && + (quorumNumbers.length == params.totalStakeIndices.length) && + (quorumNumbers.length == params.nonSignerStakeIndices.length), + "BLSSignatureChecker.checkSignatures: input quorum length mismatch" + ); + + require( + params.nonSignerPubkeys.length == params.nonSignerQuorumBitmapIndices.length, + "BLSSignatureChecker.checkSignatures: input nonsigner length mismatch" + ); + + require(referenceBlockNumber <= uint32(block.number), "BLSSignatureChecker.checkSignatures: invalid reference block"); + + // This method needs to calculate the aggregate pubkey for all signing operators across + // all signing quorums. To do that, we can query the aggregate pubkey for each quorum + // and subtract out the pubkey for each nonsigning operator registered to that quorum. + // + // In practice, we do this in reverse - calculating an aggregate pubkey for all nonsigners, + // negating that pubkey, then adding the aggregate pubkey for each quorum. BN254.G1Point memory apk = BN254.G1Point(0, 0); - for (uint i = 0; i < quorumNumbers.length; i++) { - if (staleStakesForbidden) { - require( - registryCoordinator.quorumUpdateBlockNumber(uint8(quorumNumbers[i])) + delegation.withdrawalDelayBlocks() <= block.number, - "BLSSignatureChecker.checkSignatures: StakeRegistry updates must be within withdrawalDelayBlocks window" + + // For each quorum, we're also going to query the total stake for all registered operators + // at the referenceBlockNumber, and derive the stake held by signers by subtracting out + // stakes held by nonsigners. + QuorumStakeTotals memory stakeTotals; + stakeTotals.totalStakeForQuorum = new uint96[](quorumNumbers.length); + stakeTotals.signedStakeForQuorum = new uint96[](quorumNumbers.length); + + NonSignerInfo memory nonSigners; + nonSigners.quorumBitmaps = new uint256[](params.nonSignerPubkeys.length); + nonSigners.pubkeyHashes = new bytes32[](params.nonSignerPubkeys.length); + + { + // Get a bitmap of the quorums signing the message, and validate that + // quorumNumbers contains only unique, valid quorum numbers + uint256 signingQuorumBitmap = BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers, registryCoordinator.quorumCount()); + + for (uint256 j = 0; j < params.nonSignerPubkeys.length; j++) { + // The nonsigner's pubkey hash doubles as their operatorId + // The check below validates that these operatorIds are sorted (and therefore + // free of duplicates) + nonSigners.pubkeyHashes[j] = params.nonSignerPubkeys[j].hashG1Point(); + if (j != 0) { + require( + uint256(nonSigners.pubkeyHashes[j]) > uint256(nonSigners.pubkeyHashes[j - 1]), + "BLSSignatureChecker.checkSignatures: nonSignerPubkeys not sorted" + ); + } + + // Get the quorums the nonsigner was registered for at referenceBlockNumber + nonSigners.quorumBitmaps[j] = + registryCoordinator.getQuorumBitmapAtBlockNumberByIndex({ + operatorId: nonSigners.pubkeyHashes[j], + blockNumber: referenceBlockNumber, + index: params.nonSignerQuorumBitmapIndices[j] + }); + + // Add the nonsigner's pubkey to the total apk, multiplied by the number + // of quorums they have in common with the signing quorums, because their + // public key will be a part of each signing quorum's aggregate pubkey + apk = apk.plus( + params.nonSignerPubkeys[j] + .scalar_mul_tiny( + BitmapUtils.countNumOnes(nonSigners.quorumBitmaps[j] & signingQuorumBitmap) + ) ); } - require( - bytes24(nonSignerStakesAndSignature.quorumApks[i].hashG1Point()) == - blsApkRegistry.getApkHashAtBlockNumberAndIndex( - 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); + + // Negate the sum of the nonsigner aggregate pubkeys - from here, we'll add the + // total aggregate pubkey from each quorum. Because the nonsigners' pubkeys are + // in these quorums, this initial negation ensures they're cancelled out + apk = apk.negate(); + + /** + * For each quorum (at referenceBlockNumber): + * - add the apk for all registered operators + * - query the total stake for each quorum + * - subtract the stake for each nonsigner to calculate the stake belonging to signers + */ { - // 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"); - } + uint256 withdrawalDelayBlocks = delegation.withdrawalDelayBlocks(); + bool _staleStakesForbidden = staleStakesForbidden; - nonSignerQuorumBitmaps[i] = - registryCoordinator.getQuorumBitmapAtBlockNumberByIndex( - 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) - ) + for (uint256 i = 0; i < quorumNumbers.length; i++) { + // If we're disallowing stale stake updates, check that each quorum's last update block + // is within withdrawalDelayBlocks + if (_staleStakesForbidden) { + require( + registryCoordinator.quorumUpdateBlockNumber(uint8(quorumNumbers[i])) + withdrawalDelayBlocks >= referenceBlockNumber, + "BLSSignatureChecker.checkSignatures: StakeRegistry updates must be within withdrawalDelayBlocks window" ); } - } - // 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]; - - // keep track of the nonSigners index in the quorum - uint32 nonSignerForQuorumIndex = 0; + + // Validate params.quorumApks is correct for this quorum at the referenceBlockNumber, + // then add it to the total apk + require( + bytes24(params.quorumApks[i].hashG1Point()) == + blsApkRegistry.getApkHashAtBlockNumberAndIndex({ + quorumNumber: uint8(quorumNumbers[i]), + blockNumber: referenceBlockNumber, + index: params.quorumApkIndices[i] + }), + "BLSSignatureChecker.checkSignatures: quorumApk hash in storage does not match provided quorum apk" + ); + apk = apk.plus(params.quorumApks[i]); + + // Get the total and starting signed stake for the quorum at referenceBlockNumber + stakeTotals.totalStakeForQuorum[i] = + stakeRegistry.getTotalStakeAtBlockNumberFromIndex({ + quorumNumber: uint8(quorumNumbers[i]), + blockNumber: referenceBlockNumber, + index: params.totalStakeIndices[i] + }); + stakeTotals.signedStakeForQuorum[i] = stakeTotals.totalStakeForQuorum[i]; + + // Keep track of the nonSigners index in the quorum + uint256 nonSignerForQuorumIndex = 0; // 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++) { + for (uint256 j = 0; j < params.nonSignerPubkeys.length; j++) { // 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.getStakeAtBlockNumberAndIndex( - quorumNumber, - referenceBlockNumber, - nonSignerPubkeyHashes[i], - nonSignerStakesAndSignature.nonSignerStakeIndices[quorumNumberIndex][nonSignerForQuorumIndex] - ); + if (BitmapUtils.numberIsInBitmap(nonSigners.quorumBitmaps[j], uint8(quorumNumbers[i]))) { + stakeTotals.signedStakeForQuorum[i] -= + stakeRegistry.getStakeAtBlockNumberAndIndex({ + quorumNumber: uint8(quorumNumbers[i]), + blockNumber: referenceBlockNumber, + operatorId: nonSigners.pubkeyHashes[j], + index: params.nonSignerStakeIndices[i][nonSignerForQuorumIndex] + }); unchecked { ++nonSignerForQuorumIndex; } } } - - unchecked { - ++quorumNumberIndex; - } } } { @@ -190,17 +239,17 @@ contract BLSSignatureChecker is IBLSSignatureChecker { (bool pairingSuccessful, bool signatureIsValid) = trySignatureAndApkVerification( msgHash, apk, - nonSignerStakesAndSignature.apkG2, - nonSignerStakesAndSignature.sigma + params.apkG2, + params.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)); + bytes32 signatoryRecordHash = keccak256(abi.encodePacked(referenceBlockNumber, nonSigners.pubkeyHashes)); // 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); + return (stakeTotals, signatoryRecordHash); } /** diff --git a/src/RegistryCoordinator.sol b/src/RegistryCoordinator.sol index b86f9dc6..55b7654f 100644 --- a/src/RegistryCoordinator.sol +++ b/src/RegistryCoordinator.sol @@ -806,16 +806,21 @@ contract RegistryCoordinator is uint256 index ) external view returns (uint192) { QuorumBitmapUpdate memory quorumBitmapUpdate = _operatorBitmapHistory[operatorId][index]; + + /** + * Validate that the update is valid for the given blockNumber: + * - blockNumber should be >= the update block number + * - the next update block number should be either 0 or strictly greater than blockNumber + */ require( - quorumBitmapUpdate.updateBlockNumber <= blockNumber, + blockNumber >= quorumBitmapUpdate.updateBlockNumber, "RegistryCoordinator.getQuorumBitmapAtBlockNumberByIndex: 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, + quorumBitmapUpdate.nextUpdateBlockNumber == 0 || blockNumber < quorumBitmapUpdate.nextUpdateBlockNumber, "RegistryCoordinator.getQuorumBitmapAtBlockNumberByIndex: quorumBitmapUpdate is from before blockNumber" ); + return quorumBitmapUpdate.quorumBitmap; } diff --git a/src/StakeRegistry.sol b/src/StakeRegistry.sol index 6eb09808..d1c66018 100644 --- a/src/StakeRegistry.sol +++ b/src/StakeRegistry.sol @@ -444,12 +444,17 @@ contract StakeRegistry is StakeRegistryStorage { StakeUpdate memory operatorStakeUpdate, uint32 blockNumber ) internal pure { + /** + * Validate that the update is valid for the given blockNumber: + * - blockNumber should be >= the update block number + * - the next update block number should be either 0 or strictly greater than blockNumber + */ require( - operatorStakeUpdate.updateBlockNumber <= blockNumber, + blockNumber >= operatorStakeUpdate.updateBlockNumber, "StakeRegistry._validateOperatorStakeAtBlockNumber: operatorStakeUpdate is from after blockNumber" ); require( - operatorStakeUpdate.nextUpdateBlockNumber == 0 || operatorStakeUpdate.nextUpdateBlockNumber > blockNumber, + operatorStakeUpdate.nextUpdateBlockNumber == 0 || blockNumber < operatorStakeUpdate.nextUpdateBlockNumber, "StakeRegistry._validateOperatorStakeAtBlockNumber: there is a newer operatorStakeUpdate available before blockNumber" ); } diff --git a/src/libraries/BN254.sol b/src/libraries/BN254.sol index 42703bda..9a378ebd 100644 --- a/src/libraries/BN254.sol +++ b/src/libraries/BN254.sol @@ -141,7 +141,7 @@ library BN254 { uint8 i = 0; //loop until we reach the most significant bit - while(s > m){ + while(s >= m){ unchecked { // if the current bit is 1, add the 2^n*p to the accumulated product if ((s >> i) & 1 == 1) { @@ -268,10 +268,14 @@ library BN254 { return (success, out[0] != 0); } - /// @return the keccak256 hash of the G1 Point + /// @return hashedG1 the keccak256 hash of the G1 Point /// @dev used for BLS signatures - function hashG1Point(BN254.G1Point memory pk) internal pure returns (bytes32) { - return keccak256(abi.encodePacked(pk.X, pk.Y)); + function hashG1Point(BN254.G1Point memory pk) internal pure returns (bytes32 hashedG1) { + assembly { + mstore(0, mload(pk)) + mstore(0x20, mload(add(0x20, pk))) + hashedG1 := keccak256(0, 0x40) + } } /// @return the keccak256 hash of the G2 Point diff --git a/test/mocks/DelegationMock.sol b/test/mocks/DelegationMock.sol new file mode 100644 index 00000000..1a737744 --- /dev/null +++ b/test/mocks/DelegationMock.sol @@ -0,0 +1,185 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity =0.8.12; + +import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; + +import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; +import {IStrategyManager} from "eigenlayer-contracts/src/contracts/interfaces/IStrategyManager.sol"; +import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; +import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol"; + +contract DelegationMock is IDelegationManager { + mapping(address => bool) public isOperator; + mapping(address => mapping(IStrategy => uint256)) public operatorShares; + + function setIsOperator(address operator, bool _isOperatorReturnValue) external { + isOperator[operator] = _isOperatorReturnValue; + } + + /// @notice returns the total number of shares in `strategy` that are delegated to `operator`. + function setOperatorShares(address operator, IStrategy strategy, uint256 shares) external { + operatorShares[operator][strategy] = shares; + } + + mapping (address => address) public delegatedTo; + + function registerAsOperator(OperatorDetails calldata /*registeringOperatorDetails*/, string calldata /*metadataURI*/) external pure {} + + function updateOperatorMetadataURI(string calldata /*metadataURI*/) external pure {} + + function updateAVSMetadataURI(string calldata /*metadataURI*/) external pure {} + + function delegateTo(address operator, SignatureWithExpiry memory /*approverSignatureAndExpiry*/, bytes32 /*approverSalt*/) external { + delegatedTo[msg.sender] = operator; + } + + function modifyOperatorDetails(OperatorDetails calldata /*newOperatorDetails*/) external pure {} + + function delegateToBySignature( + address /*staker*/, + address /*operator*/, + SignatureWithExpiry memory /*stakerSignatureAndExpiry*/, + SignatureWithExpiry memory /*approverSignatureAndExpiry*/, + bytes32 /*approverSalt*/ + ) external pure {} + + function undelegate(address staker) external returns (bytes32 withdrawalRoot) { + delegatedTo[staker] = address(0); + return withdrawalRoot; + } + + function increaseDelegatedShares(address /*staker*/, IStrategy /*strategy*/, uint256 /*shares*/) external pure {} + + function decreaseDelegatedShares( + address /*staker*/, + IStrategy /*strategy*/, + uint256 /*shares*/ + ) external pure {} + + function operatorDetails(address operator) external pure returns (OperatorDetails memory) { + OperatorDetails memory returnValue = OperatorDetails({ + earningsReceiver: operator, + delegationApprover: operator, + stakerOptOutWindowBlocks: 0 + }); + return returnValue; + } + + function earningsReceiver(address operator) external pure returns (address) { + return operator; + } + + function delegationApprover(address operator) external pure returns (address) { + return operator; + } + + function stakerOptOutWindowBlocks(address /*operator*/) external pure returns (uint256) { + return 0; + } + + function withdrawalDelayBlocks() external pure returns (uint256) { + return 50400; + } + + function isDelegated(address staker) external view returns (bool) { + return (delegatedTo[staker] != address(0)); + } + + function isNotDelegated(address /*staker*/) external pure returns (bool) {} + + // function isOperator(address /*operator*/) external pure returns (bool) {} + + function stakerNonce(address /*staker*/) external pure returns (uint256) {} + + function delegationApproverSaltIsSpent(address /*delegationApprover*/, bytes32 /*salt*/) external pure returns (bool) {} + + function calculateCurrentStakerDelegationDigestHash(address /*staker*/, address /*operator*/, uint256 /*expiry*/) external view returns (bytes32) {} + + function calculateStakerDelegationDigestHash(address /*staker*/, uint256 /*stakerNonce*/, address /*operator*/, uint256 /*expiry*/) external view returns (bytes32) {} + + function calculateDelegationApprovalDigestHash( + address /*staker*/, + address /*operator*/, + address /*_delegationApprover*/, + bytes32 /*approverSalt*/, + uint256 /*expiry*/ + ) external view returns (bytes32) {} + + function calculateStakerDigestHash(address /*staker*/, address /*operator*/, uint256 /*expiry*/) + external pure returns (bytes32 stakerDigestHash) {} + + function calculateApproverDigestHash(address /*staker*/, address /*operator*/, uint256 /*expiry*/) + external pure returns (bytes32 approverDigestHash) {} + + function calculateOperatorAVSRegistrationDigestHash(address /*operator*/, address /*avs*/, bytes32 /*salt*/, uint256 /*expiry*/) + external pure returns (bytes32 digestHash) {} + + function DOMAIN_TYPEHASH() external view returns (bytes32) {} + + function STAKER_DELEGATION_TYPEHASH() external view returns (bytes32) {} + + function DELEGATION_APPROVAL_TYPEHASH() external view returns (bytes32) {} + + function OPERATOR_AVS_REGISTRATION_TYPEHASH() external view returns (bytes32) {} + + function domainSeparator() external view returns (bytes32) {} + + function cumulativeWithdrawalsQueued(address staker) external view returns (uint256) {} + + function calculateWithdrawalRoot(Withdrawal memory withdrawal) external pure returns (bytes32) {} + + function registerOperatorToAVS(address operator, ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature) external {} + + function deregisterOperatorFromAVS(address operator) external {} + + function operatorSaltIsSpent(address avs, bytes32 salt) external view returns (bool) {} + + function queueWithdrawals( + QueuedWithdrawalParams[] calldata queuedWithdrawalParams + ) external returns (bytes32[] memory) {} + + function completeQueuedWithdrawal( + Withdrawal calldata withdrawal, + IERC20[] calldata tokens, + uint256 middlewareTimesIndex, + bool receiveAsTokens + ) external {} + + function completeQueuedWithdrawals( + Withdrawal[] calldata withdrawals, + IERC20[][] calldata tokens, + uint256[] calldata middlewareTimesIndexes, + bool[] calldata receiveAsTokens + ) external {} + + function migrateQueuedWithdrawals(IStrategyManager.DeprecatedStruct_QueuedWithdrawal[] memory withdrawalsToQueue) external {} + + // onlyDelegationManager functions in StrategyManager + function addShares( + IStrategyManager strategyManager, + address staker, + IStrategy strategy, + uint256 shares + ) external { + strategyManager.addShares(staker, strategy, shares); + } + + function removeShares( + IStrategyManager strategyManager, + address staker, + IStrategy strategy, + uint256 shares + ) external { + strategyManager.removeShares(staker, strategy, shares); + } + + function withdrawSharesAsTokens( + IStrategyManager strategyManager, + address recipient, + IStrategy strategy, + uint256 shares, + IERC20 token + ) external { + strategyManager.withdrawSharesAsTokens(recipient, strategy, shares, token); + } +} \ No newline at end of file diff --git a/test/utils/MockAVSDeployer.sol b/test/utils/MockAVSDeployer.sol index 118cbd03..9352a413 100644 --- a/test/utils/MockAVSDeployer.sol +++ b/test/utils/MockAVSDeployer.sol @@ -26,7 +26,7 @@ import {IRegistryCoordinator} from "src/interfaces/IRegistryCoordinator.sol"; import {StrategyManagerMock} from "eigenlayer-contracts/src/test/mocks/StrategyManagerMock.sol"; import {EigenPodManagerMock} from "eigenlayer-contracts/src/test/mocks/EigenPodManagerMock.sol"; -import {DelegationManagerMock} from "eigenlayer-contracts/src/test/mocks/DelegationManagerMock.sol"; +import {DelegationMock} from "test/mocks/DelegationMock.sol"; import {BLSApkRegistryHarness} from "test/harnesses/BLSApkRegistryHarness.sol"; import {EmptyContract} from "eigenlayer-contracts/src/test/mocks/EmptyContract.sol"; @@ -59,7 +59,7 @@ contract MockAVSDeployer is Test { IIndexRegistry public indexRegistry; StrategyManagerMock public strategyManagerMock; - DelegationManagerMock public delegationMock; + DelegationMock public delegationMock; EigenPodManagerMock public eigenPodManagerMock; address public proxyAdminOwner = address(uint160(uint256(keccak256("proxyAdminOwner")))); @@ -120,7 +120,7 @@ contract MockAVSDeployer is Test { pausers[0] = pauser; pauserRegistry = new PauserRegistry(pausers, unpauser); - delegationMock = new DelegationManagerMock(); + delegationMock = new DelegationMock(); eigenPodManagerMock = new EigenPodManagerMock(); strategyManagerMock = new StrategyManagerMock(); slasherImplementation = new Slasher(strategyManagerMock, delegationMock); From 497f758be22d3d3409296c697684e79af5e3982e Mon Sep 17 00:00:00 2001 From: Alex <18387287+wadealexc@users.noreply.github.com> Date: Thu, 14 Dec 2023 11:52:05 -0500 Subject: [PATCH 090/101] docs: add wip docs and clean main README (#96) * docs: add docs very wip * docs: wip add docs and main README * docs: wip main README * docs: more wip * docs: get docs to a good ish state * docs: address feedback --- README.md | 108 ++---- docs/BLSSignatureChecker.md | 45 +-- docs/Middleware-registration-operator-flow.md | 15 - docs/README.md | 139 ++++++++ docs/RegistryCoordinator.md | 325 ++++++++++++++++++ docs/{ => experimental}/AVS-Guide.md | 8 +- docs/{ => old}/BLSPublicKeyCompendium.md | 0 docs/old/BLSSignatureChecker.md | 44 +++ .../Old_BLSPubkeyRegistry.md} | 0 .../Old_BLSRegistryCoordinatorWithIndices.md} | 0 .../Old_IndexRegistry.md} | 0 docs/old/Old_README.md | 101 ++++++ .../Old_StakeRegistry.md} | 0 .../OperatorStateRetriever.md} | 0 docs/registries/BLSApkRegistry.md | 88 +++++ docs/registries/IndexRegistry.md | 88 +++++ docs/registries/StakeRegistry.md | 158 +++++++++ src/RegistryCoordinator.sol | 4 +- src/interfaces/IRegistryCoordinator.sol | 2 +- 19 files changed, 978 insertions(+), 147 deletions(-) delete mode 100644 docs/Middleware-registration-operator-flow.md create mode 100644 docs/README.md create mode 100644 docs/RegistryCoordinator.md rename docs/{ => experimental}/AVS-Guide.md (98%) rename docs/{ => old}/BLSPublicKeyCompendium.md (100%) create mode 100644 docs/old/BLSSignatureChecker.md rename docs/{BLSPubkeyRegistry.md => old/Old_BLSPubkeyRegistry.md} (100%) rename docs/{BLSRegistryCoordinatorWithIndices.md => old/Old_BLSRegistryCoordinatorWithIndices.md} (100%) rename docs/{IndexRegistry.md => old/Old_IndexRegistry.md} (100%) create mode 100644 docs/old/Old_README.md rename docs/{StakeRegistry.md => old/Old_StakeRegistry.md} (100%) rename docs/{BLSOperatorStateRetriever.md => old/OperatorStateRetriever.md} (100%) create mode 100644 docs/registries/BLSApkRegistry.md create mode 100644 docs/registries/IndexRegistry.md create mode 100644 docs/registries/StakeRegistry.md diff --git a/README.md b/README.md index 8b213a27..18a7dcb5 100644 --- a/README.md +++ b/README.md @@ -1,105 +1,51 @@ -# AVS Smart Contract Architecture +[core-docs-m2]: https://github.com/Layr-Labs/eigenlayer-contracts/tree/m2-mainnet/docs +[core-repo]: https://github.com/Layr-Labs/eigenlayer-contracts -

-🚧 The Slasher contract is under active development and its interface is expected to change. We recommend writing slashing logic without integrating with the Slasher at this point in time. 🚧 -

+# EigenLayer Middleware -## Introduction +EigenLayer is a set of smart contracts deployed on Ethereum that enable restaking of assets to secure new services called AVSs (actively validated services). The core contracts that enable these features can be found in the [`eigenlayer-contracts` repo][core-repo]. -EigenLayer AVSs are a new type of protocol that makes use of EigenLayer’s restaking primitive. AVSs are different from current chains and other smart contract protocols in that they are validated by EigenLayer operators. There are 3 specific types of conditions that AVSs implement in smart contracts onchain: +This repo contains smart contracts used to create an AVS that interacts with the EigenLayer core contracts. -- Registration/Deregistration conditions: What requirements do operators need to have in order to register for/deregister from the AVS? -- Payment conditions: How much does a certain operator deserve to be paid for their validation? In what form are they paid? -- Slashing conditions: What behavior is not allowed of operators by the AVS? What exact mechanism should be used to determine this behavior onchain? +## Getting Started -The EigenLabs dev team has been building out a smart contract architecture for AVSs to provide a base for AVSs to build on top of for their specific use case. They have been using it internally for the first AVS on EigenLayer: EigenDA. +* [Documentation](#documentation) +* [Building and Running Tests](#building-and-running-tests) +* [Deployments](#deployments) -## The Registry Coordinator and Registries +## Documentation -![Registry Architecture](./docs/images/registry_architecture.png) +### Basics -There are two things that matter in terms of operators’ onchain interaction with AVS contracts: +To get a basic understanding of EigenLayer, check out [You Could've Invented EigenLayer](https://www.blog.eigenlayer.xyz/ycie/). Note that some of the document's content describes features that do not exist yet (like the Slasher). To understand more about how restakers and operators interact with EigenLayer, check out these guides: +* [Restaking User Guide](https://docs.eigenlayer.xyz/restaking-guides/restaking-user-guide) +* [Operator Guide](https://docs.eigenlayer.xyz/operator-guides/operator-introduction) -- What qualities of operators need to be kept track of onchain? (e.g. stake, BLS pubkeys, etc.) -- Registration/Deregistration conditions +Most of this content is intro-level and describes user interactions with the EigenLayer core contracts, but it should give you a good enough starting point. -These two points have been addressed through the architecture of Registry Coordinator and Registry. +### Deep Dive -### Definitions +The most up-to-date and technical documentation can be found in [/docs](/docs). If you're a shadowy super coder, this is a great place to get an overview of the contracts before diving into the code. -#### Quorums +It might be helpful to explore the EigenLayer core technical documentation as well, which you can find [here][core-docs-m2]. -Quorums are the different divisions of the operator set for an AVS. One can think of a quorum being defined by the token staked for that quorum, although [it is slightly more complicated than that](./docs/StakeRegistry.md#definitions). One often wants to make trust assumptions on quorums, but wants many quorums for the same AVS. +## Building and Running Tests -One example of the quorum concept is in EigenDA, where we have a single ETH quorum for which LSTs and native beacon chain ETH are accepted as stake and another quorum for each rollup that wants to stake their own token for security. +This repository uses Foundry. See the [Foundry docs](https://book.getfoundry.sh/) for more info on installation and usage. If you already have foundry, you can build this project and run tests with these commands: -### RegistryCoordinator - -The Registry Coordinator is a contract that is deployed by each AVS. It handles - -- Keeping track of what quorums operators are a part of -- Handling operator churn (registration/deregistration) -- Communicating to registries - The current implementation of this contract is the [BLSRegistryCoordinatorWithIndices](./docs/BLSRegistryCoordinatorWithIndices.md). - -### Registries - -The registries are contracts that keep track of the attributes of individual operators. For example, we have initially built the - -- [StakeRegistry](./docs/StakeRegistry.md) which keeps track of the stakes of different operators for different quorums at different times -- [BLSPubkeyRegistry](./docs/BLSPubkeyRegistry.md) which keeps track of the aggregate public key of different quorums at different times. Note that the AVS contracts use [BLS aggregate signatures](#bls-signature-checker) due to their favorable scalability to large operator sets. -- [IndexRegistry](./docs/IndexRegistry.md) which keeps track of an ordered list of the operators in each quorum at different times. (note that this behavior is likely only needed for EigenDA) - Registries are meant to be read from and indexed by offchain AVS actors (operators and AVS coordinators). - -A registry coordinator has 1 or more registries connected to it and all of them are called when an operator registers or deregisters. They are a plug and play system that are meant to be added to the RegistryCoordinator as needed. AVSs should create registries they need for their purpose. - -### Note on (active) block ranges - -Note that the registry contract implementations use lists structs similar to the following type (with the `Value` type altered): - -```solidity -struct ValueUpdateA { - Value value; - uint32 updateBlockNumber; // when the value started being valid - uint32 nextUpdateBlockNumber; // then the value stopped being valid or 0, if the value is still valid -} ``` +foundryup -or - -```solidity -struct ValueUpdateB { - Value value; - uint32 toBlockNumber; // when the value expired -} +forge build +forge test ``` -These structs, consecutively, are a history of the `Value` over certain ranges of blocks. These are (aptly) called block ranges, and _active_ block ranges for the `ValueUpdate` that contains the current block number. - -## TODO: Service Manager - -## BLS Signature Checker - -At the core of many AVSs on EigenLayer (almost all except those that affect Ethereum block production) is the verification of a quorum signature of an AVS's operator set on a certain message and slashing if some quality of that message and other state is true. The registry architecture is optimized for making this signature as cheap as possible to verify (it is still relatively expensive). - -The current implementation of this contract is the [BLSSignatureChecker](./docs/BLSSignatureChecker.md). - ## Deployments -### M2 Testnet (Current Goerli Deployment) - -| Name | Solidity | Contract | Notes | -| ------------------------- | ----------------------------- | ------------------------------------------------------------------------------------------------- | ----- | -| BLSOperatorStateRetriever | BLSOperatorStateRetriever.sol | [`0x737D...A3a3`](https://goerli.etherscan.io/address/0x737Dd62816a9392e84Fa21C531aF77C00816A3a3) | | -| BLSPubkeyCompendium | BLSPubkeyCompendium.sol | [`0xc81d...1b19`](https://goerli.etherscan.io/address/0xc81d3963087Fe09316cd1E032457989C7aC91b19) | | +### Current Mainnet Deployment -## Further reading +No contracts have been deployed to mainnet yet. -More detailed functional docs have been written on the AVS architecture implemented in the middleware contracts. The recommended order for reading the other docs in this folder is +### Current Testnet Deployment -1. [BLSRegistryCoordinatorWithIndices](./docs/BLSRegistryCoordinatorWithIndices.md) -2. [BLSPublicKeyCompendium](./docs/BLSPublicKeyCompendium.md) and [BLSPublicKeyRegistry](./docs/BLSPubkeyRegistry.md) -3. [StakeRegistry](./docs/StakeRegistry.md) -4. [IndexRegistry](./docs/IndexRegistry.md) -5. [BLSOperatorStateRetriever](./docs/BLSOperatorStateRetriever.md) -6. [BLSSignatureChecker](./docs/BLSSignatureChecker.md) +TODO \ No newline at end of file diff --git a/docs/BLSSignatureChecker.md b/docs/BLSSignatureChecker.md index 1d1ec4cc..bc860a6f 100644 --- a/docs/BLSSignatureChecker.md +++ b/docs/BLSSignatureChecker.md @@ -1,44 +1 @@ -# BLSSignatureChecker - -This contract is deployed per AVS. It verifies the signatures of operators in an efficient way given the rest of the registry architecture. A lot of EigenLayer AVSs can be summarized as a quorum signature on a message and slashing if some quality of that message and other state is true. - -## Flows - -### checkSignatures -``` -function checkSignatures( - bytes32 msgHash, - bytes calldata quorumNumbers, - uint32 referenceBlockNumber, - NonSignerStakesAndSignature memory nonSignerStakesAndSignature - ) - -struct NonSignerStakesAndSignature { - uint32[] nonSignerQuorumBitmapIndices; - BN254.G1Point[] nonSignerPubkeys; - BN254.G1Point[] quorumApks; - BN254.G2Point apkG2; - BN254.G1Point sigma; - uint32[] quorumApkIndices; - uint32[] totalStakeIndices; - uint32[][] nonSignerStakeIndices; // nonSignerStakeIndices[quorumNumberIndex][nonSignerIndex] - } -``` -This function is called by an AVS aggregator when confirming that a `msgHash` is signed by certain `quorumNumbers`. - -The function calculates the sum (`apk`) of the aggregate public keys for all the quorums in question using the provided `quorumApkIndices` which points to hashes of the quorum aggregate public keys at the `referenceBlockNumber` of the signature of which `quorumApks` are the preimages. Since there may be nodes from the quorums that don't sign, the `nonSignerPubkeys` are subtracted from `apk`. There is a detail here that since an operator may serve more than one of the quorums in question, the number of quorums in `quorumNumbers` that the nonsigner served at the `referenceBlockNumber` is calculated using the provided `nonSignerQuorumBitmapIndices` and their public key is multiplied by the number before it is subtracted from `apk`. This gets rid of the duplicate additions of their public key because their public key is in more than one of the added `quorumApks`. Now the contract has `apk` set to the claimed aggregate public key of the signers. - -Next, the contract fetches the total stakes of each of the `quorumNumbers` at the `referenceBlockNumber` using the provided `totalStakeIndices`. The stakes of each of the nonsigners for each of the quorums are fetched using `nonSignerStakeIndices` and subtracted from the total stakes. Now the contract has the claimed signing stake for each of the quorums. - -Finally, the contract does a similar check to the [BLSPublicKeyCompendium](./BLSPublicKeyCompendium.md): - -- Calculates $\gamma = keccak256(apk, apkG2, sigma)$ -- Verifies the paring $e(\sigma + \gamma apk, [1]_2) = e(H(msgHash) + \gamma[1]_1, apkG2)$ - -More detailed notes exist on the signature check [here](https://geometry.xyz/notebook/Optimized-BLS-multisignatures-on-EVM). - -If it checks out, the contract returns the stake that signed the message for each quorum and the hash of the reference block number and the list of public key hashes of the nonsigners for future use. - -## Upstream Dependencies - -AVSs are expected to use this contract's method for their specific tasks. For example, EigenDA uses this function in their contracts when confirming batches of blobs on their DA layer onchain. +TODO! \ No newline at end of file diff --git a/docs/Middleware-registration-operator-flow.md b/docs/Middleware-registration-operator-flow.md deleted file mode 100644 index 4faf78bf..00000000 --- a/docs/Middleware-registration-operator-flow.md +++ /dev/null @@ -1,15 +0,0 @@ -# Middleware Registration Operator Flow - -EigenLayer is a three-sided platform, bringing together middlewares/services, operators, and stakers. Middlewares have an attached risk-reward profile, based on the risk of getting slashed while running their service, and the rewards that they offer operators taking on those risks. Operators have full control over the middlewares that they decide to register with and run, and have an aggregated risk-reward profile of their own, based on these choices and their personal reputation. All of a users' assets that are deposited into EigenLayer are delegated to a single operator. - -Operators must thus make an informed decision as to which middlewares to run. This document is meant to give a high-level overview of the registration process that they must then follow in order to register with the middlewares that they have decided to operate. - -## Registration Process - -Any operator must go through the following sequence of steps: -- [Register as an operator](./EigenLayer-delegation-flow.md#operator-registration) in EigenLayer -- Get stakers to [delegate to them](./EigenLayer-delegation-flow.md#staker-delegation) -- For each middleware: - - Opt into [slashing](./EigenLayer-tech-spec.md#slasher) by the middleware's [ServiceManager](../src/contracts/interfaces/IServiceManager.sol) contract - - Make sure to respect any other preconditions set by the middleware — for [EigenDA](https://docs.eigenda.xyz/), these are registering a BLS key in the [BLSPublicKeyCompendium](](../src/contracts/interfaces/IBLSPublicKeyCompendium.sol) and having sufficient (delegated) stake in EigenLayer to meet the minimum stake requirements set by EigenDA - - Call a function on one of the middleware's contracts to complete the registration — for EigenDA, this is registering on its [BLSRegistry](../src/contracts/interfaces/IBLSRegistry.sol) \ No newline at end of file diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 00000000..54bf44ec --- /dev/null +++ b/docs/README.md @@ -0,0 +1,139 @@ + +[core-contracts-repo]: https://github.com/Layr-Labs/eigenlayer-contracts +[core-docs-m2]: https://github.com/Layr-Labs/eigenlayer-contracts/tree/m2-mainnet/docs +[eigenda-repo]: https://github.com/Layr-Labs/eigenda/ +[bitmaputils-lib]: ../src/libraries/BitmapUtils.sol + +[core-registerToAVS]: https://github.com/Layr-Labs/eigenlayer-contracts/blob/m2-mainnet/docs/core/DelegationManager.md#registeroperatortoavs +[core-deregisterFromAVS]: https://github.com/Layr-Labs/eigenlayer-contracts/blob/m2-mainnet/docs/core/DelegationManager.md#deregisteroperatorfromavs + +## EigenLayer Middleware Docs + +EigenLayer AVSs ("actively validated services") are protocols that make use of EigenLayer's restaking primitives. AVSs are validated by EigenLayer operators, who are backed by delegated restaked assets via the [EigenLayer core contracts][core-contracts-repo]. Each AVS will deploy or modify instances of the contracts in this repo to hook into the EigenLayer core contracts and ensure their service has an up-to-date view of its currently-registered operators. + +**Currently, each AVS needs to implement one thing on-chain:** registration/deregistration conditions that define how an operator registers for/deregisters from the AVS. This repo provides building blocks to support these functions. + +*Eventually,* the core contracts and this repo will be extended to cover other conditions, including: +* payment conditions that define how an operator is paid for the services it provides +* slashing conditions that define "malicious behavior" in the context of the AVS, and the punishments for this behavior + +*... however, the design for these conditions is still in progress.* + +**Additional Note**: Although the goal of this repo is to eventually provide general-purpose building blocks for all kinds of AVSs, many of the contracts and design considerations were made to support EigenDA. When these docs provide examples, they will often cite EigenDA. + +For more information on EigenDA, check out the repo: [Layr-Labs/eigenda][eigenda-repo]. + +### Contents + +* [Important Concepts](#important-concepts) + * [Quorums](#quorums) + * [Strategies and Stake](#strategies-and-stake) + * [Operator Sets and Churn](#operator-sets-and-churn) + * [State Histories](#state-histories) + * [Hooking Into EigenLayer Core](#hooking-into-eigenlayer-core) +* [System Components](#system-components) + * [Registries](#registries) + * [BLSSignatureChecker](#blssignaturechecker) + +--- + +### Important Concepts + +##### Quorums + +A quorum is a grouping and configuration of specific kinds of stake that an AVS considers when interacting with operators. When operators register for an AVS, they select one or more quorums within the AVS to register for. Depending on its configuration, each quorum evaluates a specific subset of the operator's restaked tokens and uses this to determine a specific weight for the operator for that quorum. This weight is ultimately used to determine when an AVS has reached consensus. + +The purpose of having a quorum is that an AVS can customize the makeup of its security offering by choosing which kinds of stake/security it would like to utilize. + +As an example, an AVS might want to support primarily native ETH stakers. It would do so by configuring a quorum to only weigh operators that control shares belonging to the native eth strategy (defined in the core contracts). + +The Owner initializes quorums in the `RegistryCoordinator`, and may configure them further in both the `RegistryCoordinator` and `StakeRegistry` contracts. When quorums are initialized, they are assigned a unique, sequential quorum number. + +*Note:* For the most part, quorum numbers are passed between contracts as `bytes` arrays containing an ordered list of quorum numbers. However, when storing lists of quorums in state (and for other operations), these `bytes` arrays are converted to bitmaps using the [`BitmapUtils` library][bitmaputils-lib]. + +##### Strategies and Stake + +Each quorum has an associated list of `StrategyParams`, which the Owner can configure via the `StakeRegistry`. + +`StrategyParams` define pairs of strategies and multipliers for the quorum: +* Strategies refer to the `DelegationManager` in the EigenLayer core contracts, which tracks shares delegated to each operator for each supported strategy. Basically, a strategy is a wrapper around an underlying token - either an LST or Native ETH. +* Multipliers determine the relative weight given to shares belonging to the corresponding strategy. + +When the `StakeRegistry` updates its view of an operator's stake for a given quorum, it queries the `DelegationManager` to get the operator's shares in each of the quorum's strategies and applies the multiplier to the returned share count. + +For more information on the `DelegationManager`, see the [EigenLayer core docs][core-docs-m2]. + +##### Operator Sets and Churn + +Quorums define a maximum operator count as well as parameters that determine when a new operator can replace an existing operator when this max count is reached. The process of replacing an existing operator when the max count is reached is called "churn," and requires a signature from the Churn Approver. + +These definitions are contained in a quorum's `OperatorSetParam`, which the Owner can configure via the `RegistryCoordinator`. A quorum's `OperatorSetParam` defines both a max operator count, as well as stake thresholds that the incoming and existing operators need to meet to qualify for churn. + +*Additional context*: + +Currently for EigenDA, the max operator count is 200. This maximum exists because EigenDA requires that completed "jobs" validate a signature by the aggregate BLS pubkey of the operator set over some job parameters. Although an aggregate BLS pubkey's signature should have a fixed cost no matter the number of operators, it may be the case that not all operators sign off on a job. + +When this happens, EigenDA needs to provide a list of the pubkeys of the non-signers to subtract them out from the quorum's aggregate pubkey ("Apk"). The limit of 200 operators keeps the gas costs reasonable in a worst case scenario. See `BLSSignatureChecker.checkSignatures` for this part of the implementation. + +In order to prevent the operator set from getting calcified, the churn mechanism was introduced to allow operators to be replaced in some cases. Future work is being done to increase the max operator count and refine the churn mechanism. + +##### State Histories + +Many of the contracts in this repo keep histories of states over time. Generally, you'll see structs that look like this: + +```solidity +struct ValueUpdate { + Value value; + uint32 updateBlockNumber; // when the value started being valid + uint32 nextUpdateBlockNumber; // when the value stopped being valid (or 0, if the value is still valid) +} +``` + +These histories are used by offchain code to query state at particular blocks, and are ultimately used to validate specific actions on-chain. See the [`BLSSignatureChecker` section](#blssignaturechecker) below. + +##### Hooking Into EigenLayer Core + +The main thing that links an AVS to the EigenLayer core contracts is that when EigenLayer operators register/deregister with an AVS, the AVS calls these functions in EigenLayer core: +* [`DelegationManager.registerOperatorToAVS`][core-registerToAVS] +* [`DelegationManager.deregisterOperatorFromAVS`][core-deregisterFromAVS] + +These methods ensure that the operator registering with the AVS is also registered as an operator in EigenLayer core. In this repo, these methods are called by the `ServiceManagerBase`. + +Eventually, operator slashing and payment for services will be part of the middleware/core relationship, but these features aren't implemented yet and their design is a work in progress. + +### System Components + +#### Registries + +| Code | Type | Proxy | +| -------- | -------- | -------- | +| [`RegistryCoordinator.sol`](../src/RegistryCoordinator.sol) | Singleton | Transparent proxy | +| [`BLSApkRegistry.sol`](../src/BLSApkRegistry.sol) | Singleton | Transparent proxy | +| [`StakeRegistry.sol`](../src/StakeRegistry.sol) | Singleton | Transparent proxy | +| [`IndexRegistry.sol`](../src/IndexRegistry.sol) | Singleton | Transparent proxy | + +The `RegistryCoordinator` keeps track of which quorums exist and have been initialized. It is also the primary entry point for operators as they register for and deregister from an AVS's quorums. + +When operators register or deregister, the registry coordinator updates that operator's currently-registered quorums, and pushes the registration/deregistration to each of the three registries it controls: +* `BLSApkRegistry`: tracks the aggregate BLS pubkey hash for the operators registered to each quorum. Also maintains a history of these aggregate pubkey hashes. +* `StakeRegistry`: interfaces with the EigenLayer core contracts to determine the weight of operators according to their stake and each quorum's configuration. Also maintains a history of these weights. +* `IndexRegistry`: assigns indices to operators within each quorum, and tracks historical indices and operators per quorum. Used primarily by offchain infrastructure to fetch ordered lists of operators in quorums. + +Both the registry coordinator and each of the registries maintain historical state for the specific information they track. This historical state tracking can be used to query state at a particular block, which is primarily used in offchain infrastructure. + +See full documentation for the registry coordinator in [`RegistryCoordinator.md`](./RegistryCoordinator.md), and for each registry in [`registries/`](./registries/). + +#### BLSSignatureChecker + +| Code | Type | Proxy | +| -------- | -------- | -------- | +| [`BLSSignatureChecker.sol`](../src/BLSSignatureChecker.sol) | Singleton | Transparent proxy | +| [`OperatorStateRetriever.sol`](../src/OperatorStateRetriever.sol) | Singleton | Transparent proxy | + +The BLSSignatureChecker verifies signatures made by the aggregate pubkeys ("Apk") of operators in one or more quorums. The primary function, `checkSignatures`, is called by an AVS when confirming that a given message hash is signed by operators belonging to one or more quorums. + +The `OperatorStateRetriever` is used by offchain code to query the `RegistryCoordinator` (and its registries) for information that will ultimately be passed into `BLSSignatureChecker.checkSignatures`. + +See full documentation in [`BLSSignatureChecker.md`](./BLSSignatureChecker.md). \ No newline at end of file diff --git a/docs/RegistryCoordinator.md b/docs/RegistryCoordinator.md new file mode 100644 index 00000000..ae82c64b --- /dev/null +++ b/docs/RegistryCoordinator.md @@ -0,0 +1,325 @@ +[core-dmgr-docs]: https://github.com/Layr-Labs/eigenlayer-contracts/blob/m2-mainnet/docs/core/DelegationManager.md +[core-dmgr-register]: https://github.com/Layr-Labs/eigenlayer-contracts/blob/m2-mainnet/docs/core/DelegationManager.md#registeroperatortoavs +[core-dmgr-deregister]: https://github.com/Layr-Labs/eigenlayer-contracts/blob/m2-mainnet/docs/core/DelegationManager.md#deregisteroperatorfromavs + +## RegistryCoordinator + +| File | Type | Proxy? | +| -------- | -------- | -------- | +| [`RegistryCoordinator.sol`](../src/RegistryCoordinator.sol) | Singleton | Transparent proxy | + +The `RegistryCoordinator` has three primary functions: +1. It is the primary entry and exit point for operators as they register for and deregister from quorums, and manages registration and deregistration in the `BLSApkRegistry`, `StakeRegistry`, and `IndexRegistry`. It also hooks into the EigenLayer core contracts, updating the core `DelegationManager` when an Operator registers/deregisters. +2. It allows anyone to update the current stake of any registered operator +3. It allows the Owner to initialize and configure new quorums + +#### High-level Concepts + +This document organizes methods according to the following themes (click each to be taken to the relevant section): +* [Registering and Deregistering](#registering-and-deregistering) +* [Updating Registered Operators](#updating-registered-operators) +* [System Configuration](#system-configuration) + +#### Roles + +* Owner: a permissioned role that can create and configure quorums as well as manage other roles +* Ejector: a permissioned role that can forcibly eject an operator from a quorum via `RegistryCoordinator.ejectOperator` +* Churn Approver: a permissioned role that signs off on operator churn in `RegistryCoordinator.registerOperatorWithChurn` + +--- + +### Registering and Deregistering + +These methods allow operators to register for/deregister from one or more quorums, and are the primary entry points to the middleware contracts as a whole: +* [`registerOperator`](#registeroperator) +* [`registerOperatorWithChurn`](#registeroperatorwithchurn) +* [`deregisterOperator`](#deregisteroperator) +* [`ejectOperator`](#ejectoperator) + +#### `registerOperator` + +```solidity +function registerOperator( + bytes calldata quorumNumbers, + string calldata socket, + SignatureWithSaltAndExpiry memory operatorSignature +) + external + onlyWhenNotPaused(PAUSED_REGISTER_OPERATOR) +``` + +Registers the caller as an Operator for one or more quorums, as long as registration doesn't place any quorum's operator count above the configured cap. This method updates the Operator's current and historical bitmap of registered quorums, and forwards a call to each of the registry contracts: +* `BLSApkRegistry.registerOperator` +* `StakeRegistry.registerOperator` +* `IndexRegistry.registerOperator` + +If the Operator was not currently registered for any quorums, this method will register the Operator to the AVS in the EigenLayer core contracts (`DelegationManager.registerOperatorToAVS`), passing in the provided `operatorSignature`. See the [`DelegationManager` docs][core-dmgr-docs] for more details. + +*Effects*: +* If the Operator was not currently registered for any quorums: + * Updates their status to `REGISTERED` + * Registers them in the core contracts (see [`DelegationManager.registerOperatorToAVS`][core-dmgr-register]) +* Adds the new quorums to the Operator's current registered quorums, and updates the Operator's bitmap history +* See [`BLSApkRegistry.registerOperator`](./registries/BLSApkRegistry.md#registeroperator) +* See [`StakeRegistry.registerOperator`](./registries/StakeRegistry.md#registeroperator) +* See [`IndexRegistry.registerOperator`](./registries/IndexRegistry.md#registeroperator) + +*Requirements*: +* Pause status MUST NOT be set: `PAUSED_REGISTER_OPERATOR` +* Caller MUST have a valid operator ID in the `BLSApkRegistry` +* `quorumNumbers` MUST be an ordered array of quorum numbers, with no entry exceeding the current `quorumCount` +* `quorumNumbers` MUST contain at least one valid quorum +* `quorumNumbers` MUST NOT contain any quorums the Operator is already registered for +* If the Operator was not currently registered for any quorums: + * See [`DelegationManager.registerOperatorToAVS`][core-dmgr-register] +* See [`BLSApkRegistry.registerOperator`](./registries/BLSApkRegistry.md#registeroperator) +* See [`StakeRegistry.registerOperator`](./registries/StakeRegistry.md#registeroperator) +* See [`IndexRegistry.registerOperator`](./registries/IndexRegistry.md#registeroperator) +* For each quorum being registered for, the Operator's addition MUST NOT put the total number of operators registered for the quorum above the quorum's configured `maxOperatorCount` + +#### `registerOperatorWithChurn` + +```solidity +function registerOperatorWithChurn( + bytes calldata quorumNumbers, + string calldata socket, + OperatorKickParam[] calldata operatorKickParams, + SignatureWithSaltAndExpiry memory churnApproverSignature, + SignatureWithSaltAndExpiry memory operatorSignature +) + external + onlyWhenNotPaused(PAUSED_REGISTER_OPERATOR) +``` + +This method performs similar steps to `registerOperator` above, except that for each quorum where the new Operator total exceeds the `maxOperatorCount`, the `operatorKickParams` are used to deregister a current Operator to make room for the new one. + +This operation requires a valid signature from the Churn Approver. Additionally, the incoming and outgoing Operators must meet these requirements: +* The new Operator's stake must be greater than the old Operator's stake by a factor given by the quorum's configured `kickBIPsOfOperatorStake` +* The old Operator's stake must be lower than the total stake for the quorum by a factor given by the quorum's configured `kickBIPsOfTotalStake` + +*Effects*: +* The new Operator is registered for one or more quorums (see `registerOperator` above) +* For any quorum where registration causes the operator count to exceed `maxOperatorCount`, the Operator selected to be replaced is deregistered (see `deregisterOperator` below) + +*Requirements*: +* Pause status MUST NOT be set: `PAUSED_REGISTER_OPERATOR` +* The `churnApproverSignature` MUST be a valid, unexpired signature from the Churn Approver over the Operator's id and the `operatorKickParams` +* The old and new Operators MUST meet the stake requirements described above +* See `registerOperator` above +* See `deregisterOperator` below + +#### `deregisterOperator` + +```solidity +function deregisterOperator( + bytes calldata quorumNumbers +) + external + onlyWhenNotPaused(PAUSED_DEREGISTER_OPERATOR) +``` + +Allows an Operator to deregister themselves from one or more quorums. + +*Effects*: +* If the Operator is no longer registered for any quorums, updates their status to `DEREGISTERED` +* Removes the new quorums from the Operator's current registered quorums, and updates the Operator's bitmap history +* See [`BLSApkRegistry.deregisterOperator`](./registries/BLSApkRegistry.md#deregisteroperator) +* See [`StakeRegistry.deregisterOperator`](./registries/StakeRegistry.md#deregisteroperator) +* See [`IndexRegistry.deregisterOperator`](./registries/IndexRegistry.md#deregisteroperator) + +*Requirements*: +* Pause status MUST NOT be set: `PAUSED_DEREGISTER_OPERATOR` +* The Operator MUST currently be in the `REGISTERED` status (i.e. registered for at least one quorum) +* `quorumNumbers` MUST be an ordered array of quorum numbers, with no entry exceeding the current `quorumCount` +* `quorumNumbers` MUST contain at least one valid quorum +* `quorumNumbers` MUST ONLY contain bits that are also set in the Operator's current registered quorum bitmap +* See [`BLSApkRegistry.deregisterOperator`](./registries/BLSApkRegistry.md#deregisteroperator) +* See [`StakeRegistry.deregisterOperator`](./registries/StakeRegistry.md#deregisteroperator) +* See [`IndexRegistry.deregisterOperator`](./registries/IndexRegistry.md#deregisteroperator) + +#### `ejectOperator` + +```solidity +function ejectOperator( + address operator, + bytes calldata quorumNumbers +) + external + onlyEjector +``` + +Allows the Ejector to forcibly deregister an Operator from one or more quorums. + +*Effects*: +* See `deregisterOperator` above + +*Requirements*: +* Caller MUST be the Ejector +* See `deregisterOperator` above + +--- + +### Updating Registered Operators + +These methods concern Operators that are currently registered for at least one quorum: +* [`updateOperators`](#updateoperators) +* [`updateOperatorsForQuorum`](#updateoperatorsforquorum) +* [`updateSocket`](#updatesocket) + +#### `updateOperators` + +```solidity +function updateOperators( + address[] calldata operators +) + external + onlyWhenNotPaused(PAUSED_UPDATE_OPERATOR) +``` + +Allows anyone to update the contracts' view of one or more Operators' stakes. For each currently-registered `operator`, this method calls `StakeRegistry.updateOperatorStake`, triggering an update of that Operator's stake. + +The `StakeRegistry` returns a bitmap of quorums where the Operator no longer meets the minimum stake required for registration. The Operator is then deregistered from those quorums. + +*Effects*: +* See [`StakeRegistry.updateOperatorStake`](./registries/StakeRegistry.md#updateoperatorstake) +* For any quorums where the Operator no longer meets the minimum stake, they are deregistered (see `deregisterOperator` above). + +*Requirements*: +* Pause status MUST NOT be set: `PAUSED_UPDATE_OPERATOR` +* See [`StakeRegistry.updateOperatorStake`](./registries/StakeRegistry.md#updateoperatorstake) + +#### `updateOperatorsForQuorum` + +```solidity +function updateOperatorsForQuorum( + address[][] calldata operatorsPerQuorum, + bytes calldata quorumNumbers +) + external + onlyWhenNotPaused(PAUSED_UPDATE_OPERATOR) +``` + +Can be called by anyone to update the stake of ALL Operators in one or more quorums simultaneously. This method works similarly to `updateOperators` above, but with the requirement that, for each quorum being updated, the respective `operatorsPerQuorum` passed in is the complete set of Operators currently registered for that quorum. + +This method also updates each quorum's `quorumUpdateBlockNumber`, signifying that the quorum's entire Operator set was updated at the current block number. (This is used by the `BLSSignatureChecker` to ensure that signature and stake validation is performed on up-to-date stake.) + +*Effects*: +* See `updateOperators` above +* Updates each quorum's `quorumUpdateBlockNumber` to the current block +* For any quorums where the Operator no longer meets the minimum stake, they are deregistered (see `deregisterOperator` above). + +*Requirements*: +* Pause status MUST NOT be set: `PAUSED_UPDATE_OPERATOR` +* See `updateOperators` above +* `quorumNumbers` MUST be an ordered array of quorum numbers, with no entry exceeding the current `quorumCount` +* All `quorumNumbers` MUST correspond to valid, initialized quorums +* `operatorsPerQuorum` and `quorumNumbers` MUST have the same lengths +* Each entry in `operatorsPerQuorum` MUST contain an order list of the currently-registered Operator addresses in the corresponding quorum +* See [`StakeRegistry.updateOperatorStake`](./registries/StakeRegistry.md#updateoperatorstake) + +#### `updateSocket` + +```solidity +function updateSocket(string memory socket) external +``` + +Allows a registered Operator to emit an event, updating their socket. + +*Effects*: +* Emits an `OperatorSocketUpdate` event + +*Requirements*: +* Caller MUST be a registered Operator + +--- + +### System Configuration + +These methods are used by the Owner to configure the `RegistryCoordinator`: +* [`createQuorum`](#createquorum) +* [`setOperatorSetParams`](#setoperatorsetparams) +* [`setChurnApprover`](#setchurnapprover) +* [`setEjector`](#setejector) + +#### `createQuorum` + +```solidity +function createQuorum( + OperatorSetParam memory operatorSetParams, + uint96 minimumStake, + IStakeRegistry.StrategyParams[] memory strategyParams +) + external + virtual + onlyOwner +``` + +Allows the Owner to initialize a new quorum with the given configuration. The new quorum is assigned a sequential quorum number. + +The new quorum is also initialized in each of the registry contracts. + +*Effects*: +* `quorumCount` is incremented by 1 +* The new quorum's `OperatorSetParams` are initialized (see `setOperatorSetParams` below) +* See [`BLSApkRegistry.initializeQuorum`](./registries/BLSApkRegistry.md#initializequorum) +* See [`StakeRegistry.initializeQuorum`](./registries/StakeRegistry.md#initializequorum) +* See [`IndexRegistry.initializeQuorum`](./registries/IndexRegistry.md#initializequorum) + +*Requirements*: +* Caller MUST be the Owner +* Quorum count before creation MUST be less than `MAX_QUORUM_COUNT` +* See [`BLSApkRegistry.initializeQuorum`](./registries/BLSApkRegistry.md#initializequorum) +* See [`StakeRegistry.initializeQuorum`](./registries/StakeRegistry.md#initializequorum) +* See [`IndexRegistry.initializeQuorum`](./registries/IndexRegistry.md#initializequorum) + +#### `setOperatorSetParams` + +```solidity +function setOperatorSetParams( + uint8 quorumNumber, + OperatorSetParam memory operatorSetParams +) + external + onlyOwner + quorumExists(quorumNumber) +``` + +Allows the Owner to update an existing quorum's `OperatorSetParams`, which determine: +* `maxOperatorCount`: The max number of operators that can be in this quorum +* `kickBIPsOfOperatorStake`: The basis points a new Operator needs over an old Operator's stake to replace them in `registerOperatorWithChurn` +* `kickBIPsOfTotalStake`: The basis points a replaced Operator needs under the quorum's total stake to be replaced in `registerOperatorWithChurn` + +*Effects*: +* Updates the quorum's `OperatorSetParams` + +*Requirements*: +* Caller MUST be the Owner +* `quorumNumber` MUST correspond to an existing, initialized quorum + +#### `setChurnApprover` + +```solidity +function setChurnApprover(address _churnApprover) external onlyOwner +``` + +Allows the Owner to update the Churn Approver address. + +*Effects*: +* Updates the Churn Approver address + +*Requirements*: +* Caller MUST be the Owner + +#### `setEjector` + +```solidity +function setEjector(address _ejector) external onlyOwner +``` + +Allows the Owner to update the Ejector address. + +*Effects*: +* Updates the Ejector address + +*Requirements*: +* Caller MUST be the Owner \ No newline at end of file diff --git a/docs/AVS-Guide.md b/docs/experimental/AVS-Guide.md similarity index 98% rename from docs/AVS-Guide.md rename to docs/experimental/AVS-Guide.md index 7734d52c..46dc9a64 100644 --- a/docs/AVS-Guide.md +++ b/docs/experimental/AVS-Guide.md @@ -10,7 +10,7 @@ This document aims to describe and summarize how actively validated services (AV We are currently in the process of implementing the API for payment flow from AVSs to operators in EigenLayer. Details of this API will be added to this document in the near future. The following figure summarizes scope of this document: -![Doc Outline](./images/middleware_outline_doc.png) +![Doc Outline](../images/middleware_outline_doc.png) # Introduction In designing EigenLayer, the EigenLabs team aspired to make minimal assumptions about the structure of AVSs built on top of it. If you are getting started looking at [EigenLayer][eigenlayer-repo-link]'s codebase, the `Slasher.sol` contains most of the logic that actually mediates the interactions between EigenLayer and AVSs. This repo contains code that can be extended, used directly, or consulted as a reference in building an AVS on top of EigenLayer. Note that there will be a single, EigenLayer-owned, `Slasher.sol` contract, but all the `middleware` contracts are AVS-specific and need to be deployed separately by AVS teams. @@ -47,7 +47,7 @@ In order for any EigenLayer operator to be able to opt-in to an AVS, EigenLayer 2. Next, the operator needs to register with the AVS on chain via an AVS-specific registry contract (see [this][middleware-guide-link] section for examples). To integrate with EigenLayer, the AVS's Registry contract provides a registration endpoint that calls on the AVS's `ServiceManager.recordFirstStakeUpdate(..)` which in turn calls `Slasher.recordFirstStakeUpdate(..)`. On successful execution of this function call, the event `MiddlewareTimesAdded(..)` is emitted and the operator has to start serving the tasks from the AVS. The following figure illustrates the above flow: -![Operator opting-in](./images/operator_opting.png) +![Operator opting-in](../images/operator_opting.png) ### *Recording Stake Updates* EigenLayer is a dynamic system where stakers and operators are constantly adjusting amounts of stake delegated via the system. It is therefore imperative for an AVS to be aware of any changes to stake delegated to its operators. In order to facilitate this, EigenLayer offers the `Slasher.recordStakeUpdate(..)`. @@ -58,13 +58,13 @@ Let us illustrate the usage of this facility with an example: A staker has deleg - The AVS provider now is aware of the change in stake, and the staker can eventually complete their withdrawal. Refer [here](https://github.com/Layr-Labs/eigenlayer-contracts/blob/master/docs/EigenLayer-withdrawal-flow.md) for more details The following figure illustrates the above flow: -![Stake update](./images/staker_withdrawing.png) +![Stake update](../images/staker_withdrawing.png) ### *Deregistering from AVS* In order for any EigenLayer operator to be able to de-register from an AVS, EigenLayer provides the interface `Slasher.recordLastStakeUpdateAndRevokeSlashingAbility(..)`. Essentially, in order for an operator to deregister from an AVS, the operator has to call `Slasher.recordLastStakeUpdateAndRevokeSlashingAbility(..)` via the AVS's ServiceManager contract. It is important to note that the latest block number until which the operator is required to serve tasks for the service must be known by the service and included in the ServiceManager's call to `Slasher.recordLastStakeUpdateAndRevokeSlashingAbility`. The following figure illustrates the above flow in which the operator calls the `deregister(..)` function in a sample Registry contract. -![Operator deregistering](./images/operator_deregister.png) +![Operator deregistering](../images/operator_deregister.png) ### *Slashing* As mentioned above, EigenLayer is built to support slashing as a result of an on-chain-checkable, objectively attributable action. In order for an AVS to be able to slash an operator in an objective manner, the AVS needs to deploy a DisputeResolution contract which anyone can call to raise a challenge against an EigenLayer operator for its adversarial action. On successful challenge, the DisputeResolution contract calls `ServiceManager.freezeOperator(..)`; the ServiceManager in turn calls `Slasher.freezeOperator(..)` to freeze the operator in EigenLayer. EigenLayer's Slasher contract emits a `OperatorFrozen(..)` event whenever an operator is (successfully) frozen diff --git a/docs/BLSPublicKeyCompendium.md b/docs/old/BLSPublicKeyCompendium.md similarity index 100% rename from docs/BLSPublicKeyCompendium.md rename to docs/old/BLSPublicKeyCompendium.md diff --git a/docs/old/BLSSignatureChecker.md b/docs/old/BLSSignatureChecker.md new file mode 100644 index 00000000..1d1ec4cc --- /dev/null +++ b/docs/old/BLSSignatureChecker.md @@ -0,0 +1,44 @@ +# BLSSignatureChecker + +This contract is deployed per AVS. It verifies the signatures of operators in an efficient way given the rest of the registry architecture. A lot of EigenLayer AVSs can be summarized as a quorum signature on a message and slashing if some quality of that message and other state is true. + +## Flows + +### checkSignatures +``` +function checkSignatures( + bytes32 msgHash, + bytes calldata quorumNumbers, + uint32 referenceBlockNumber, + NonSignerStakesAndSignature memory nonSignerStakesAndSignature + ) + +struct NonSignerStakesAndSignature { + uint32[] nonSignerQuorumBitmapIndices; + BN254.G1Point[] nonSignerPubkeys; + BN254.G1Point[] quorumApks; + BN254.G2Point apkG2; + BN254.G1Point sigma; + uint32[] quorumApkIndices; + uint32[] totalStakeIndices; + uint32[][] nonSignerStakeIndices; // nonSignerStakeIndices[quorumNumberIndex][nonSignerIndex] + } +``` +This function is called by an AVS aggregator when confirming that a `msgHash` is signed by certain `quorumNumbers`. + +The function calculates the sum (`apk`) of the aggregate public keys for all the quorums in question using the provided `quorumApkIndices` which points to hashes of the quorum aggregate public keys at the `referenceBlockNumber` of the signature of which `quorumApks` are the preimages. Since there may be nodes from the quorums that don't sign, the `nonSignerPubkeys` are subtracted from `apk`. There is a detail here that since an operator may serve more than one of the quorums in question, the number of quorums in `quorumNumbers` that the nonsigner served at the `referenceBlockNumber` is calculated using the provided `nonSignerQuorumBitmapIndices` and their public key is multiplied by the number before it is subtracted from `apk`. This gets rid of the duplicate additions of their public key because their public key is in more than one of the added `quorumApks`. Now the contract has `apk` set to the claimed aggregate public key of the signers. + +Next, the contract fetches the total stakes of each of the `quorumNumbers` at the `referenceBlockNumber` using the provided `totalStakeIndices`. The stakes of each of the nonsigners for each of the quorums are fetched using `nonSignerStakeIndices` and subtracted from the total stakes. Now the contract has the claimed signing stake for each of the quorums. + +Finally, the contract does a similar check to the [BLSPublicKeyCompendium](./BLSPublicKeyCompendium.md): + +- Calculates $\gamma = keccak256(apk, apkG2, sigma)$ +- Verifies the paring $e(\sigma + \gamma apk, [1]_2) = e(H(msgHash) + \gamma[1]_1, apkG2)$ + +More detailed notes exist on the signature check [here](https://geometry.xyz/notebook/Optimized-BLS-multisignatures-on-EVM). + +If it checks out, the contract returns the stake that signed the message for each quorum and the hash of the reference block number and the list of public key hashes of the nonsigners for future use. + +## Upstream Dependencies + +AVSs are expected to use this contract's method for their specific tasks. For example, EigenDA uses this function in their contracts when confirming batches of blobs on their DA layer onchain. diff --git a/docs/BLSPubkeyRegistry.md b/docs/old/Old_BLSPubkeyRegistry.md similarity index 100% rename from docs/BLSPubkeyRegistry.md rename to docs/old/Old_BLSPubkeyRegistry.md diff --git a/docs/BLSRegistryCoordinatorWithIndices.md b/docs/old/Old_BLSRegistryCoordinatorWithIndices.md similarity index 100% rename from docs/BLSRegistryCoordinatorWithIndices.md rename to docs/old/Old_BLSRegistryCoordinatorWithIndices.md diff --git a/docs/IndexRegistry.md b/docs/old/Old_IndexRegistry.md similarity index 100% rename from docs/IndexRegistry.md rename to docs/old/Old_IndexRegistry.md diff --git a/docs/old/Old_README.md b/docs/old/Old_README.md new file mode 100644 index 00000000..a6901cb2 --- /dev/null +++ b/docs/old/Old_README.md @@ -0,0 +1,101 @@ +# EigenLayer Middleware + +## Introduction + +EigenLayer AVSs are a new type of protocol that makes use of EigenLayer’s restaking primitive. AVSs are different from current chains and other smart contract protocols in that they are validated by EigenLayer operators. There are 3 specific types of conditions that AVSs implement in smart contracts onchain: + +- Registration/Deregistration conditions: What requirements do operators need to have in order to register for/deregister from the AVS? +- Payment conditions: How much does a certain operator deserve to be paid for their validation? In what form are they paid? +- Slashing conditions: What behavior is not allowed of operators by the AVS? What exact mechanism should be used to determine this behavior onchain? + +The EigenLabs dev team has been building out a smart contract architecture for AVSs to provide a base for AVSs to build on top of for their specific use case. They have been using it internally for the first AVS on EigenLayer: EigenDA. + +## The Registry Coordinator and Registries + +![Registry Architecture](./docs/images/registry_architecture.png) + +There are two things that matter in terms of operators’ onchain interaction with AVS contracts: + +- What qualities of operators need to be kept track of onchain? (e.g. stake, BLS pubkeys, etc.) +- Registration/Deregistration conditions + +These two points have been addressed through the Registry Coordinator/Registry Architecture. + +### Definitions + +#### Quorums + +Quorums are the different divisions of the operator set for an AVS. One can think of a quorum being defined by the token staked for that quorum, although [it is slightly more complicated than that](./docs/StakeRegistry.md#definitions). One often wants to make trust assumptions on quorums, but wants many quorums for the same AVS. + +One example of the quorum concept is in EigenDA, where we have a single ETH quorum for which LSTs and native beacon chain ETH are accepted as stake and another quorum for each rollup that wants to stake their own token for security. + +### RegistryCoordinator + +The Registry Coordinator is a contract that is deployed by each AVS. It handles + +- Keeping track of what quorums operators are a part of +- Handling operator churn (registration/deregistration) +- Communicating to registries + The current implementation of this contract is the [BLSRegistryCoordinatorWithIndices](./docs/BLSRegistryCoordinatorWithIndices.md). + +### Registries + +The registries are contracts that keep track of the attributes of individual operators. For example, we have initially built the + +- [StakeRegistry](./docs/StakeRegistry.md) which keeps track of the stakes of different operators for different quorums at different times +- [BLSPubkeyRegistry](./docs/BLSPubkeyRegistry.md) which keeps track of the aggregate public key of different quorums at different times. Note that the AVS contracts use [BLS aggregate signatures](#bls-signature-checker) due to their favorable scalability to large operator sets. +- [IndexRegistry](./docs/IndexRegistry.md) which keeps track of an ordered list of the operators in each quorum at different times. (note that this behavior is likely only needed for EigenDA) + Registries are meant to be read from and indexed by offchain AVS actors (operators and AVS coordinators). + +A registry coordinator has 1 or more registries connected to it and all of them are called when an operator registers or deregisters. They are a plug and play system that are meant to be added to the RegistryCoordinator as needed. AVSs should create registries they need for their purpose. + +### Note on (active) block ranges + +Note that the registry contract implementations use lists structs similar to the following type (with the `Value` type altered): + +```solidity +struct ValueUpdateA { + Value value; + uint32 updateBlockNumber; // when the value started being valid + uint32 nextUpdateBlockNumber; // then the value stopped being valid or 0, if the value is still valid +} +``` + +or + +```solidity +struct ValueUpdateB { + Value value; + uint32 toBlockNumber; // when the value expired +} +``` + +These structs, consecutively, are a history of the `Value` over certain ranges of blocks. These are (aptly) called block ranges, and _active_ block ranges for the `ValueUpdate` that contains the current block number. + +## TODO: Service Manager + +## BLS Signature Checker + +At the core of many AVSs on EigenLayer (almost all except those that affect Ethereum block production) is the verification of a quorum signature of an AVS's operator set on a certain message and slashing if some quality of that message and other state is true. The registry architecture is optimized for making this signature as cheap as possible to verify (it is still relatively expensive). + +The current implementation of this contract is the [BLSSignatureChecker](./docs/BLSSignatureChecker.md). + +## Deployments + +### M2 Testnet (Current Goerli Deployment) + +| Name | Solidity | Contract | Notes | +| ------------------------- | ----------------------------- | ------------------------------------------------------------------------------------------------- | ----- | +| BLSOperatorStateRetriever | BLSOperatorStateRetriever.sol | [`0x737D...A3a3`](https://goerli.etherscan.io/address/0x737Dd62816a9392e84Fa21C531aF77C00816A3a3) | | +| BLSPubkeyCompendium | BLSPubkeyCompendium.sol | [`0xc81d...1b19`](https://goerli.etherscan.io/address/0xc81d3963087Fe09316cd1E032457989C7aC91b19) | | + +## Further reading + +More detailed functional docs have been written on the AVS architecture implemented in the middleware contracts. The recommended order for reading the other docs in this folder is + +1. [BLSRegistryCoordinatorWithIndices](./docs/BLSRegistryCoordinatorWithIndices.md) +2. [BLSPublicKeyCompendium](./docs/BLSPublicKeyCompendium.md) and [BLSPublicKeyRegistry](./docs/BLSPubkeyRegistry.md) +3. [StakeRegistry](./docs/StakeRegistry.md) +4. [IndexRegistry](./docs/IndexRegistry.md) +5. [BLSOperatorStateRetriever](./docs/BLSOperatorStateRetriever.md) +6. [BLSSignatureChecker](./docs/BLSSignatureChecker.md) diff --git a/docs/StakeRegistry.md b/docs/old/Old_StakeRegistry.md similarity index 100% rename from docs/StakeRegistry.md rename to docs/old/Old_StakeRegistry.md diff --git a/docs/BLSOperatorStateRetriever.md b/docs/old/OperatorStateRetriever.md similarity index 100% rename from docs/BLSOperatorStateRetriever.md rename to docs/old/OperatorStateRetriever.md diff --git a/docs/registries/BLSApkRegistry.md b/docs/registries/BLSApkRegistry.md new file mode 100644 index 00000000..4c707ca3 --- /dev/null +++ b/docs/registries/BLSApkRegistry.md @@ -0,0 +1,88 @@ +## BLSApkRegistry + +| File | Type | Proxy? | +| -------- | -------- | -------- | +| [`BLSApkRegistry.sol`](../../src/BLSApkRegistry.sol) | Singleton | Transparent proxy | + +TODO + +#### High-level Concepts + +TODO + +This document organizes methods according to the following themes (click each to be taken to the relevant section): + +TODO + + +#### Important State Variables + +TODO + + + +--- + +### Theme + +TODO + + + +#### `registerOperator` + +```solidity + +``` + +TODO + +*Effects*: +* + +*Requirements*: +* + +#### `deregisterOperator` + +```solidity + +``` + +TODO + +*Effects*: +* + +*Requirements*: +* + +#### `initializeQuorum` + +```solidity + +``` + +TODO + +*Effects*: +* + +*Requirements*: +* + +--- \ No newline at end of file diff --git a/docs/registries/IndexRegistry.md b/docs/registries/IndexRegistry.md new file mode 100644 index 00000000..8f3aa89d --- /dev/null +++ b/docs/registries/IndexRegistry.md @@ -0,0 +1,88 @@ +## IndexRegistry + +| File | Type | Proxy? | +| -------- | -------- | -------- | +| [`IndexRegistry.sol`](../../src/IndexRegistry.sol) | Singleton | Transparent proxy | + +TODO + +#### High-level Concepts + +TODO + +This document organizes methods according to the following themes (click each to be taken to the relevant section): + +TODO + + +#### Important State Variables + +TODO + + + +--- + +### Theme + +TODO + + + +#### `registerOperator` + +```solidity + +``` + +TODO + +*Effects*: +* + +*Requirements*: +* + +#### `deregisterOperator` + +```solidity + +``` + +TODO + +*Effects*: +* + +*Requirements*: +* + +#### `initializeQuorum` + +```solidity + +``` + +TODO + +*Effects*: +* + +*Requirements*: +* + +--- \ No newline at end of file diff --git a/docs/registries/StakeRegistry.md b/docs/registries/StakeRegistry.md new file mode 100644 index 00000000..ba0e67b4 --- /dev/null +++ b/docs/registries/StakeRegistry.md @@ -0,0 +1,158 @@ +## StakeRegistry + +| File | Type | Proxy? | +| -------- | -------- | -------- | +| [`StakeRegistry.sol`](../src/StakeRegistry.sol) | Singleton | Transparent proxy | + +TODO + +#### High-level Concepts + +TODO + +This document organizes methods according to the following themes (click each to be taken to the relevant section): + +TODO + + +#### Important State Variables + +TODO + + + +--- + +### Theme + +TODO + + + +#### `registerOperator` + +```solidity + +``` + +TODO + +*Effects*: +* + +*Requirements*: +* + +#### `deregisterOperator` + +```solidity + +``` + +TODO + +*Effects*: +* + +*Requirements*: +* + +#### `updateOperatorStake` + +```solidity + +``` + +TODO + +*Effects*: +* + +*Requirements*: +* + +#### `initializeQuorum` + +```solidity + +``` + +TODO + +*Effects*: +* + +*Requirements*: +* + +#### `setMinimumStakeForQuorum` + +```solidity + +``` + +TODO + +*Effects*: +* + +*Requirements*: +* + +#### `addStrategies` + +```solidity + +``` + +TODO + +*Effects*: +* + +*Requirements*: +* + +#### `removeStrategies` + +```solidity + +``` + +TODO + +*Effects*: +* + +*Requirements*: +* + +#### `modifyStrategyParams` + +```solidity + +``` + +TODO + +*Effects*: +* + +*Requirements*: +* + +--- \ No newline at end of file diff --git a/src/RegistryCoordinator.sol b/src/RegistryCoordinator.sol index 55b7654f..9c732332 100644 --- a/src/RegistryCoordinator.sol +++ b/src/RegistryCoordinator.sol @@ -301,7 +301,7 @@ contract RegistryCoordinator is /** * @notice Updates the stakes of all operators for each of the specified quorums in the StakeRegistry. Each quorum also - * has their StakeRegistry.quorumTimestamp updated. which is meant to keep track of when operators were last all updated at once. + * has their quorumUpdateBlockNumber updated. which is meant to keep track of when operators were last all updated at once. * @param operatorsPerQuorum is an array of arrays of operators to update for each quorum. Note that each nested array * of operators must be sorted in ascending address order to ensure that all operators in the quorum are updated * @param quorumNumbers is an array of quorum numbers to update @@ -313,7 +313,7 @@ contract RegistryCoordinator is address[][] calldata operatorsPerQuorum, bytes calldata quorumNumbers ) external onlyWhenNotPaused(PAUSED_UPDATE_OPERATOR) { - uint192 quorumBitmap = uint192(BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers)); + uint192 quorumBitmap = uint192(BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers, quorumCount)); require(_quorumsAllExist(quorumBitmap), "RegistryCoordinator.updateOperatorsForQuorum: some quorums do not exist"); require( operatorsPerQuorum.length == quorumNumbers.length, diff --git a/src/interfaces/IRegistryCoordinator.sol b/src/interfaces/IRegistryCoordinator.sol index 02b72a5e..c2ace699 100644 --- a/src/interfaces/IRegistryCoordinator.sol +++ b/src/interfaces/IRegistryCoordinator.sol @@ -42,7 +42,7 @@ interface IRegistryCoordinator { * @notice Data structure for storing info on operators */ struct OperatorInfo { - // the id of the operator, which is likely the keccak256 hash of the operator's public key if using BLSRegsitry + // the id of the operator, which is likely the keccak256 hash of the operator's public key if using BLSRegistry bytes32 operatorId; // indicates whether the operator is actively registered for serving the middleware or not OperatorStatus status; From 23185d86893c01750ec70742ba1ed378075f46bb Mon Sep 17 00:00:00 2001 From: Alex <18387287+wadealexc@users.noreply.github.com> Date: Thu, 14 Dec 2023 12:31:23 -0500 Subject: [PATCH 091/101] chore: add eigenda goerli deployment (#111) --- README.md | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 18a7dcb5..705a25f9 100644 --- a/README.md +++ b/README.md @@ -42,10 +42,24 @@ forge test ## Deployments +The contracts in this repo are meant to be deployed by each AVS that wants to use them. The addresses listed below refer to EigenDA's deployment, and are included as an example. + ### Current Mainnet Deployment No contracts have been deployed to mainnet yet. ### Current Testnet Deployment -TODO \ No newline at end of file +The current testnet deployment is from our M2 beta release, which is a slightly older version of this repo. You can view the deployed contract addresses below, or check out the [`v0.1.0`](https://github.com/Layr-Labs/eigenlayer-middleware/tree/v0.1.0-m2-goerli) branch in "Releases". + + +| Name | Solidity | Proxy | Implementation | Notes | +| -------- | -------- | -------- | -------- | -------- | +| RegistryCoordinator | [`BLSRegistryCoordinatorWithIndices.sol`](https://github.com/Layr-Labs/eigenlayer-middleware/blob/v0.1.0-m2-goerli/src/BLSRegistryCoordinatorWithIndices.sol) | [`0x0b30...4C0B`](https://goerli.etherscan.io/address/0x0b30a3427765f136754368a4500bAca8d2a54C0B) | [`0x9A70...a0e4`](https://goerli.etherscan.io/address/0x9A70ED111FaFEC41856202536AFAA38841a9a0e4) | Proxy: [OpenZeppelin TUP@4.7.1](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v4.7.1/contracts/proxy/transparent/TransparentUpgradeableProxy.sol) | +| StakeRegistry | [`StakeRegistry.sol`](https://github.com/Layr-Labs/eigenlayer-middleware/blob/v0.1.0-m2-goerli/src/StakeRegistry.sol) | [`0x5a83...A206`](https://goerli.etherscan.io/address/0x5a834d58D22742503D8f92dd2f28c866C166A206) | [`0x8741...5B98`](https://goerli.etherscan.io/address/0x8741e3a24d9517Aa19E63122A34680a9A85F5B98) | Proxy: [OpenZeppelin TUP@4.7.1](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v4.7.1/contracts/proxy/transparent/TransparentUpgradeableProxy.sol) | +| IndexRegistry | [`IndexRegistry.sol`](https://github.com/Layr-Labs/eigenlayer-middleware/blob/v0.1.0-m2-goerli/src/IndexRegistry.sol) | [`0xa8A1...BDF7`](https://goerli.etherscan.io/address/0xa8A14B97d556cEc3f4384C186fB99d72F015BDF7) | [`0x8cd4...8117`](https://goerli.etherscan.io/address/0x8cd4c39B713B026319e35f20B7f19baE28648117) | Proxy: [OpenZeppelin TUP@4.7.1](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v4.7.1/contracts/proxy/transparent/TransparentUpgradeableProxy.sol) | +| BLSApkRegistry | [`BLSPubkeyRegistry.sol`](https://github.com/Layr-Labs/eigenlayer-middleware/blob/v0.1.0-m2-goerli/src/BLSPubkeyRegistry.sol) | [`0xD8fC...BEcA`](https://goerli.etherscan.io/address/0xD8fCD5c9103962DE37E375EF9dB62cCf39D5BEcA) | [`0x4C9D...aFb8`](https://goerli.etherscan.io/address/0x4C9D23fd901d3d98e75BdcC6a8AC9bA81d8DaFb8) | Proxy: [OpenZeppelin TUP@4.7.1](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v4.7.1/contracts/proxy/transparent/TransparentUpgradeableProxy.sol) | +| BLSPubkeyCompendium
(deprecated) | [`BLSPublicKeyCompendium.sol`](https://github.com/Layr-Labs/eigenlayer-middleware/blob/v0.1.0-m2-goerli/src/BLSPublicKeyCompendium.sol) | - | [`0xc81d...1b19`](https://goerli.etherscan.io/address/0xc81d3963087fe09316cd1e032457989c7ac91b19) | | +| OperatorStateRetriever | [`BLSOperatorStateRetriever.sol`](https://github.com/Layr-Labs/eigenlayer-middleware/blob/v0.1.0-m2-goerli/src/BLSOperatorStateRetriever.sol) | - | [`0x737d...a3a3`](https://goerli.etherscan.io/address/0x737dd62816a9392e84fa21c531af77c00816a3a3) | | +| ProxyAdmin | [OpenZeppelin ProxyAdmin@4.7.1](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v4.7.1/contracts/proxy/transparent/ProxyAdmin.sol) | - | [`0xbe85...aF3e`](https://goerli.etherscan.io/address/0xbe85B38b6086A45350947DD6dA6d78cc2E4BaF3e) | | +| EigenDAServiceManager | [`eigenda/EigenDAServiceManager.sol`](https://github.com/Layr-Labs/eigenda/blob/f599513723a17ad7bd5693287f75325007deec19/contracts/EigenDAServiceManager.sol#L4831) | [`0x9FcE...0010`](https://goerli.etherscan.io/address/0x9FcE30E01a740660189bD8CbEaA48Abd36040010) | [`0x1261...9606`](https://goerli.etherscan.io/address/0x12612f42bc1f09680c3d0c8dae72d5cd534c9606) | Proxy: [OpenZeppelin TUP@4.7.1](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v4.7.1/contracts/proxy/transparent/TransparentUpgradeableProxy.sol) | \ No newline at end of file From c570c42d235f0ff6b968c018973ea471f6beab6b Mon Sep 17 00:00:00 2001 From: ChaoticWalrus <93558947+ChaoticWalrus@users.noreply.github.com> Date: Thu, 14 Dec 2023 10:19:11 -0800 Subject: [PATCH 092/101] feat: concentrate eigenlayer avs interactions into service manager (#109) * feat: reintroduce IServiceManager & ServiceManagerBase These are useful concepts to form the single interaction point where AVSs push data to EigenLayer. The previous version focused on (unused) Slashing interactions, whereas this version focused on the newer registration interactions. TODO: modify the RegistryCoordinator to call the ServiceManagerBase instead of calling the DelegationManager directly. * feat: integrate RegistryCoordinator with ServiceManager(Base) achieves the goal of a single interaction point with EigenLayer (per AVS). all calls which push data to EigenLayer core are now forwarded via the ServiceManager(Base) * fix: use correct addresses in test setup + functions simple follow-up commit to fix breaking tests from last commit * chore: restore optimizer_runs to 200 other changes in this PR remove enough from the size of the RegistryCoordinator contract to make this possible. 200 runs is a wider industry default + this should slightly decrease gas costs for users. we can switch back to 100 in the future if we ever need to * chore: add `setMetadataURI` function to IServiceManager interface * feat: add operator strategy indexing to service manager (#110) --------- Co-authored-by: Yash Patil <40046473+ypatil12@users.noreply.github.com> --- foundry.toml | 2 +- src/RegistryCoordinator.sol | 32 ++--- src/ServiceManagerBase.sol | 115 ++++++++++++++++++ src/StakeRegistry.sol | 2 +- src/interfaces/IServiceManager.sol | 42 +++++++ .../RegistryCoordinatorHarness.t.sol | 5 +- test/integration/CoreRegistration.t.sol | 41 ++++--- test/unit/RegistryCoordinatorUnit.t.sol | 2 +- test/unit/StakeRegistryUnit.t.sol | 11 +- test/utils/MockAVSDeployer.sol | 30 ++++- 10 files changed, 230 insertions(+), 52 deletions(-) create mode 100644 src/ServiceManagerBase.sol create mode 100644 src/interfaces/IServiceManager.sol diff --git a/foundry.toml b/foundry.toml index f8f557c1..5c1f5469 100644 --- a/foundry.toml +++ b/foundry.toml @@ -7,6 +7,6 @@ fs_permissions = [{ access = "read-write", path = "./" }] ffi = true # The number of optimizer runs -optimizer_runs = 100 +optimizer_runs = 200 # See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options diff --git a/src/RegistryCoordinator.sol b/src/RegistryCoordinator.sol index 9c732332..c3ec5aa6 100644 --- a/src/RegistryCoordinator.sol +++ b/src/RegistryCoordinator.sol @@ -8,8 +8,6 @@ import {EIP712} from "@openzeppelin/contracts/utils/cryptography/draft-EIP712.so import {EIP1271SignatureUtils} from "eigenlayer-contracts/src/contracts/libraries/EIP1271SignatureUtils.sol"; import {IPauserRegistry} from "eigenlayer-contracts/src/contracts/interfaces/IPauserRegistry.sol"; import {Pausable} from "eigenlayer-contracts/src/contracts/permissions/Pausable.sol"; -import {ISlasher} from "eigenlayer-contracts/src/contracts/interfaces/ISlasher.sol"; -import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; import {IRegistryCoordinator} from "src/interfaces/IRegistryCoordinator.sol"; import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol"; @@ -17,6 +15,7 @@ import {IBLSApkRegistry} from "src/interfaces/IBLSApkRegistry.sol"; import {ISocketUpdater} from "src/interfaces/ISocketUpdater.sol"; import {IStakeRegistry} from "src/interfaces/IStakeRegistry.sol"; import {IIndexRegistry} from "src/interfaces/IIndexRegistry.sol"; +import {IServiceManager} from "src/interfaces/IServiceManager.sol"; import {BitmapUtils} from "src/libraries/BitmapUtils.sol"; import {BN254} from "src/libraries/BN254.sol"; @@ -59,16 +58,14 @@ contract RegistryCoordinator is /// @notice The maximum number of quorums this contract supports uint8 internal constant MAX_QUORUM_COUNT = 192; - /// @notice the EigenLayer Slasher - ISlasher public immutable slasher; + /// @notice the ServiceManager for this AVS, which forwards calls onto EigenLayer's core contracts + IServiceManager public immutable serviceManager; /// @notice the BLS Aggregate Pubkey Registry contract that will keep track of operators' aggregate BLS public keys per quorum IBLSApkRegistry public immutable blsApkRegistry; /// @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 Delegation Manager contract to record operator avs relationships - IDelegationManager public immutable delegationManager; /// @notice the current number of quorums supported by the registry coordinator uint8 public quorumCount; @@ -105,14 +102,12 @@ contract RegistryCoordinator is } constructor( - IDelegationManager _delegationManager, - ISlasher _slasher, + IServiceManager _serviceManager, IStakeRegistry _stakeRegistry, IBLSApkRegistry _blsApkRegistry, IIndexRegistry _indexRegistry ) EIP712("AVSRegistryCoordinator", "v0.0.1") { - delegationManager = _delegationManager; - slasher = _slasher; + serviceManager = _serviceManager; stakeRegistry = _stakeRegistry; blsApkRegistry = _blsApkRegistry; indexRegistry = _indexRegistry; @@ -429,15 +424,6 @@ contract RegistryCoordinator is _setEjector(_ejector); } - /** - * @notice Sets the metadata URI for the AVS - * @param _metadataURI is the metadata URI for the AVS - * @dev only callable by the owner - */ - function setMetadataURI(string memory _metadataURI) external onlyOwner { - delegationManager.updateAVSMetadataURI(_metadataURI); - } - /******************************************************************************* INTERNAL FUNCTIONS *******************************************************************************/ @@ -489,8 +475,8 @@ contract RegistryCoordinator is status: OperatorStatus.REGISTERED }); - // Register the operator with the delegation manager - delegationManager.registerOperatorToAVS(operator, operatorSignature); + // Register the operator with the EigenLayer via this AVS's ServiceManager + serviceManager.registerOperatorToAVS(operator, operatorSignature); emit OperatorRegistered(operator, operatorId); } @@ -577,10 +563,10 @@ contract RegistryCoordinator is newBitmap: newBitmap }); - // If the operator is no longer registered for any quorums, update their status and deregister from delegationManager + // If the operator is no longer registered for any quorums, update their status and deregister from EigenLayer via this AVS's ServiceManager if (newBitmap.isEmpty()) { operatorInfo.status = OperatorStatus.DEREGISTERED; - delegationManager.deregisterOperatorFromAVS(operator); + serviceManager.deregisterOperatorFromAVS(operator); emit OperatorDeregistered(operator, operatorId); } diff --git a/src/ServiceManagerBase.sol b/src/ServiceManagerBase.sol new file mode 100644 index 00000000..9c63fa06 --- /dev/null +++ b/src/ServiceManagerBase.sol @@ -0,0 +1,115 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity =0.8.12; + +import {OwnableUpgradeable} from "@openzeppelin-upgrades/contracts/access/OwnableUpgradeable.sol"; + +import {BitmapUtils} from "src/libraries/BitmapUtils.sol"; +import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol"; +import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; + +import {IServiceManager} from "src/interfaces/IServiceManager.sol"; +import {IRegistryCoordinator} from "src/interfaces/IRegistryCoordinator.sol"; +import {IStakeRegistry} from "src/interfaces/IStakeRegistry.sol"; + +/** + * @title Minimal implementation of a ServiceManager-type contract. + * This contract can inherited from or simply used as a point-of-reference. + * @author Layr Labs, Inc. + */ +contract ServiceManagerBase is IServiceManager, OwnableUpgradeable { + using BitmapUtils for *; + + IRegistryCoordinator immutable registryCoordinator; + IDelegationManager immutable delegationManager; + IStakeRegistry immutable stakeRegistry; + + /// @notice when applied to a function, only allows the RegistryCoordinator to call it + modifier onlyRegistryCoordinator() { + require( + msg.sender == address(registryCoordinator), + "ServiceManagerBase.onlyRegistryCoordinator: caller is not the registry coordinator" + ); + _; + } + + /// @notice Sets the (immutable) `registryCoordinator` address + constructor( + IDelegationManager _delegationManager, + IRegistryCoordinator _registryCoordinator, + IStakeRegistry _stakeRegistry + ) { + delegationManager = _delegationManager; + registryCoordinator = _registryCoordinator; + stakeRegistry = _stakeRegistry; + _disableInitializers(); + } + + function initialize(address initialOwner) external initializer { + _transferOwnership(initialOwner); + } + + /** + * @notice Sets the metadata URI for the AVS + * @param _metadataURI is the metadata URI for the AVS + * @dev only callable by the owner + */ + function setMetadataURI(string memory _metadataURI) public virtual onlyOwner { + delegationManager.updateAVSMetadataURI(_metadataURI); + } + + /** + * @notice Forwards a call to EigenLayer's DelegationManager contract to confirm operator registration with the AVS + * @param operator The address of the operator to register. + * @param operatorSignature The signature, salt, and expiry of the operator's signature. + */ + function registerOperatorToAVS( + address operator, + ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature + ) public virtual onlyRegistryCoordinator { + delegationManager.registerOperatorToAVS(operator, operatorSignature); + } + + /** + * @notice Forwards a call to EigenLayer's DelegationManager contract to confirm operator deregistration from the AVS + * @param operator The address of the operator to deregister. + */ + function deregisterOperatorFromAVS(address operator) public virtual onlyRegistryCoordinator { + delegationManager.deregisterOperatorFromAVS(operator); + } + + /** + * @notice Returns the list of strategies that the operator has potentially restaked on the AVS + * @param operator The address of the operator to get restaked strategies for + * @dev This function is intended to be called off-chain + * @dev No guarantee is made on whether the operator has shares for a strategy in a quorum or uniqueness + * of each element in the returned array. The off-chain service should do that validation separately + */ + function getOperatorRestakedStrategies(address operator) external view returns (address[] memory) { + bytes32 operatorId = registryCoordinator.getOperatorId(operator); + uint192 operatorBitmap = registryCoordinator.getCurrentQuorumBitmap(operatorId); + + if (operatorBitmap == 0 || registryCoordinator.quorumCount() == 0) { + return new address[](0); + } + + // Get number of strategies for each quorum in operator bitmap + bytes memory operatorRestakedQuorums = BitmapUtils.bitmapToBytesArray(operatorBitmap); + uint256 strategyCount; + for(uint256 i = 0; i < operatorRestakedQuorums.length; i++) { + strategyCount += stakeRegistry.strategyParamsLength(uint8(operatorRestakedQuorums[i])); + } + + // Get strategies for each quorum in operator bitmap + address[] memory restakedStrategies = new address[](strategyCount); + uint256 index = 0; + for(uint256 i = 0; i < operatorRestakedQuorums.length; i++) { + uint8 quorum = uint8(operatorRestakedQuorums[i]); + uint256 strategyParamsLength = stakeRegistry.strategyParamsLength(quorum); + for (uint256 j = 0; j < strategyParamsLength; j++) { + restakedStrategies[index] = address(stakeRegistry.strategyParamsByIndex(quorum, j).strategy); + index++; + } + } + return restakedStrategies; + } +} diff --git a/src/StakeRegistry.sol b/src/StakeRegistry.sol index d1c66018..5f059796 100644 --- a/src/StakeRegistry.sol +++ b/src/StakeRegistry.sol @@ -521,7 +521,7 @@ contract StakeRegistry is StakeRegistryStorage { ) public view returns (StrategyParams memory) { return strategyParams[quorumNumber][index]; - } + } /******************************************************************************* VIEW FUNCTIONS - Operator Stake History diff --git a/src/interfaces/IServiceManager.sol b/src/interfaces/IServiceManager.sol new file mode 100644 index 00000000..1367fa41 --- /dev/null +++ b/src/interfaces/IServiceManager.sol @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity >=0.5.0; + +import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol"; +import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; + +/** + * @title Minimal interface for a ServiceManager-type contract that forms the single point for an AVS to push updates to EigenLayer + * @author Layr Labs, Inc. + */ +interface IServiceManager { + /** + * @notice Sets the metadata URI for the AVS + * @param _metadataURI is the metadata URI for the AVS + */ + function setMetadataURI(string memory _metadataURI) external; + + /** + * @notice Forwards a call to EigenLayer's DelegationManager contract to confirm operator registration with the AVS + * @param operator The address of the operator to register. + * @param operatorSignature The signature, salt, and expiry of the operator's signature. + */ + function registerOperatorToAVS( + address operator, + ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature + ) external; + + /** + * @notice Forwards a call to EigenLayer's DelegationManager contract to confirm operator deregistration from the AVS + * @param operator The address of the operator to deregister. + */ + function deregisterOperatorFromAVS(address operator) external; + + /** + * @notice Returns the list of strategies that the operator has potentially restaked on the AVS + * @param operator The address of the operator to get restaked strategies for + * @dev This function is intended to be called off-chain + * @dev No guarantee is made on whether the operator has shares for a strategy in a quorum or uniqueness + * of each element in the returned array. The off-chain service should do that validation separately + */ + function getOperatorRestakedStrategies(address operator) external view returns (address[] memory); +} diff --git a/test/harnesses/RegistryCoordinatorHarness.t.sol b/test/harnesses/RegistryCoordinatorHarness.t.sol index f3ce41a7..0429b50f 100644 --- a/test/harnesses/RegistryCoordinatorHarness.t.sol +++ b/test/harnesses/RegistryCoordinatorHarness.t.sol @@ -8,12 +8,11 @@ import "forge-std/Test.sol"; // wrapper around the RegistryCoordinator contract that exposes the internal functions for unit testing. contract RegistryCoordinatorHarness is RegistryCoordinator, Test { constructor( - IDelegationManager _delegationManager, - ISlasher _slasher, + IServiceManager _serviceManager, IStakeRegistry _stakeRegistry, IBLSApkRegistry _blsApkRegistry, IIndexRegistry _indexRegistry - ) RegistryCoordinator(_delegationManager, _slasher, _stakeRegistry, _blsApkRegistry, _indexRegistry) { + ) RegistryCoordinator(_serviceManager, _stakeRegistry, _blsApkRegistry, _indexRegistry) { _transferOwnership(msg.sender); } diff --git a/test/integration/CoreRegistration.t.sol b/test/integration/CoreRegistration.t.sol index fa16bbb3..88254c10 100644 --- a/test/integration/CoreRegistration.t.sol +++ b/test/integration/CoreRegistration.t.sol @@ -39,21 +39,31 @@ contract Test_CoreRegistration is MockAVSDeployer { ) ); - // Deploy New RegistryCoordinator - registryCoordinatorImplementation = new RegistryCoordinatorHarness( + // Deploy New ServiceManager & RegistryCoordinator implementations + serviceManagerImplementation = new ServiceManagerBase( delegationManager, - slasher, + registryCoordinator, + stakeRegistry + ); + + registryCoordinatorImplementation = new RegistryCoordinatorHarness( + serviceManager, stakeRegistry, blsApkRegistry, indexRegistry ); - // Upgrade Registry Coordinator - cheats.prank(proxyAdminOwner); + // Upgrade Registry Coordinator & ServiceManager + cheats.startPrank(proxyAdminOwner); proxyAdmin.upgrade( TransparentUpgradeableProxy(payable(address(registryCoordinator))), address(registryCoordinatorImplementation) ); + proxyAdmin.upgrade( + TransparentUpgradeableProxy(payable(address(serviceManager))), + address(serviceManagerImplementation) + ); + cheats.stopPrank(); // Set operator address operator = cheats.addr(operatorPrivateKey); @@ -84,7 +94,7 @@ contract Test_CoreRegistration is MockAVSDeployer { ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature = _getOperatorSignature( operatorPrivateKey, operator, - address(registryCoordinator), + address(serviceManager), emptySalt, maxExpiry ); @@ -94,7 +104,7 @@ contract Test_CoreRegistration is MockAVSDeployer { registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, operatorSignature); // Check operator is registered - IDelegationManager.OperatorAVSRegistrationStatus operatorStatus = delegationManager.avsOperatorStatus(address(registryCoordinator), operator); + IDelegationManager.OperatorAVSRegistrationStatus operatorStatus = delegationManager.avsOperatorStatus(address(serviceManager), operator); assertEq(uint8(operatorStatus), uint8(IDelegationManager.OperatorAVSRegistrationStatus.REGISTERED)); } @@ -108,7 +118,7 @@ contract Test_CoreRegistration is MockAVSDeployer { registryCoordinator.deregisterOperator(quorumNumbers); // Check operator is deregistered - IDelegationManager.OperatorAVSRegistrationStatus operatorStatus = delegationManager.avsOperatorStatus(address(registryCoordinator), operator); + IDelegationManager.OperatorAVSRegistrationStatus operatorStatus = delegationManager.avsOperatorStatus(address(serviceManager), operator); assertEq(uint8(operatorStatus), uint8(IDelegationManager.OperatorAVSRegistrationStatus.UNREGISTERED)); } @@ -124,19 +134,22 @@ contract Test_CoreRegistration is MockAVSDeployer { registryCoordinator.deregisterOperator(quorumNumbers); // Check operator is still registered - IDelegationManager.OperatorAVSRegistrationStatus operatorStatus = delegationManager.avsOperatorStatus(address(registryCoordinator), operator); + IDelegationManager.OperatorAVSRegistrationStatus operatorStatus = delegationManager.avsOperatorStatus(address(serviceManager), operator); assertEq(uint8(operatorStatus), uint8(IDelegationManager.OperatorAVSRegistrationStatus.REGISTERED)); } function test_setMetadataURI_fail_notServiceManagerOwner() public { + require(operator != serviceManager.owner(), "bad test setup"); cheats.prank(operator); cheats.expectRevert("Ownable: caller is not the owner"); - registryCoordinator.setMetadataURI("Test MetadataURI"); + serviceManager.setMetadataURI("Test MetadataURI"); } - function test_setMetadataURI() public { - cheats.prank(registryCoordinatorOwner); - registryCoordinator.setMetadataURI("Test MetadataURI"); + function test_setMetadataURI() public { + address toPrankFrom = serviceManager.owner(); + cheats.prank(toPrankFrom); + serviceManager.setMetadataURI("Test MetadataURI"); + // TODO: check effects here } // Utils @@ -145,7 +158,7 @@ contract Test_CoreRegistration is MockAVSDeployer { ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature = _getOperatorSignature( operatorPrivateKey, operator, - address(registryCoordinator), + address(serviceManager), emptySalt, maxExpiry ); diff --git a/test/unit/RegistryCoordinatorUnit.t.sol b/test/unit/RegistryCoordinatorUnit.t.sol index 3f20e23d..922c1964 100644 --- a/test/unit/RegistryCoordinatorUnit.t.sol +++ b/test/unit/RegistryCoordinatorUnit.t.sol @@ -47,7 +47,7 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { assertEq(address(registryCoordinator.stakeRegistry()), address(stakeRegistry)); assertEq(address(registryCoordinator.blsApkRegistry()), address(blsApkRegistry)); assertEq(address(registryCoordinator.indexRegistry()), address(indexRegistry)); - assertEq(address(registryCoordinator.slasher()), address(slasher)); + assertEq(address(registryCoordinator.serviceManager()), address(serviceManager)); for (uint i = 0; i < numQuorums; i++) { assertEq( diff --git a/test/unit/StakeRegistryUnit.t.sol b/test/unit/StakeRegistryUnit.t.sol index 405a542b..4add656b 100644 --- a/test/unit/StakeRegistryUnit.t.sol +++ b/test/unit/StakeRegistryUnit.t.sol @@ -1,27 +1,24 @@ // 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 {Slasher} from "eigenlayer-contracts/src/contracts/core/Slasher.sol"; import {PauserRegistry} from "eigenlayer-contracts/src/contracts/permissions/PauserRegistry.sol"; -import {IStrategyManager} from "eigenlayer-contracts/src/contracts/interfaces/IStrategyManager.sol"; import {ISlasher} from "eigenlayer-contracts/src/contracts/interfaces/ISlasher.sol"; import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; import {IStakeRegistry} from "src/interfaces/IStakeRegistry.sol"; import {IIndexRegistry} from "src/interfaces/IIndexRegistry.sol"; import {IRegistryCoordinator} from "src/interfaces/IRegistryCoordinator.sol"; import {IBLSApkRegistry} from "src/interfaces/IBLSApkRegistry.sol"; +import {IServiceManager} from "src/interfaces/IServiceManager.sol"; -import {BitmapUtils} from "eigenlayer-contracts/src/contracts/libraries/BitmapUtils.sol"; +import {BitmapUtils} from "src/libraries/BitmapUtils.sol"; import {StrategyManagerMock} from "eigenlayer-contracts/src/test/mocks/StrategyManagerMock.sol"; import {EigenPodManagerMock} from "eigenlayer-contracts/src/test/mocks/EigenPodManagerMock.sol"; -import {OwnableMock} from "eigenlayer-contracts/src/test/mocks/OwnableMock.sol"; import {DelegationManagerMock} from "eigenlayer-contracts/src/test/mocks/DelegationManagerMock.sol"; -import {SlasherMock} from "eigenlayer-contracts/src/test/mocks/SlasherMock.sol"; import {StakeRegistryHarness} from "test/harnesses/StakeRegistryHarness.sol"; import {StakeRegistry} from "src/StakeRegistry.sol"; @@ -41,6 +38,7 @@ contract StakeRegistryUnitTests is Test { StakeRegistryHarness public stakeRegistryImplementation; StakeRegistryHarness public stakeRegistry; RegistryCoordinatorHarness public registryCoordinator; + IServiceManager public serviceManager; StrategyManagerMock public strategyManagerMock; DelegationManagerMock public delegationMock; @@ -103,8 +101,7 @@ contract StakeRegistryUnitTests is Test { cheats.startPrank(registryCoordinatorOwner); registryCoordinator = new RegistryCoordinatorHarness( - delegationMock, - slasher, + serviceManager, stakeRegistry, IBLSApkRegistry(apkRegistry), IIndexRegistry(indexRegistry) diff --git a/test/utils/MockAVSDeployer.sol b/test/utils/MockAVSDeployer.sol index 9352a413..85eea82e 100644 --- a/test/utils/MockAVSDeployer.sol +++ b/test/utils/MockAVSDeployer.sol @@ -16,12 +16,14 @@ import {OperatorStateRetriever} from "src/OperatorStateRetriever.sol"; import {RegistryCoordinator} from "src/RegistryCoordinator.sol"; import {RegistryCoordinatorHarness} from "test/harnesses/RegistryCoordinatorHarness.t.sol"; import {BLSApkRegistry} from "src/BLSApkRegistry.sol"; +import {ServiceManagerBase} from "src/ServiceManagerBase.sol"; import {StakeRegistry} from "src/StakeRegistry.sol"; import {IndexRegistry} from "src/IndexRegistry.sol"; import {IBLSApkRegistry} from "src/interfaces/IBLSApkRegistry.sol"; import {IStakeRegistry} from "src/interfaces/IStakeRegistry.sol"; import {IIndexRegistry} from "src/interfaces/IIndexRegistry.sol"; import {IRegistryCoordinator} from "src/interfaces/IRegistryCoordinator.sol"; +import {IServiceManager} from "src/interfaces/IServiceManager.sol"; import {StrategyManagerMock} from "eigenlayer-contracts/src/test/mocks/StrategyManagerMock.sol"; @@ -51,12 +53,14 @@ contract MockAVSDeployer is Test { StakeRegistryHarness public stakeRegistryImplementation; IBLSApkRegistry public blsApkRegistryImplementation; IIndexRegistry public indexRegistryImplementation; + ServiceManagerBase public serviceManagerImplementation; OperatorStateRetriever public operatorStateRetriever; RegistryCoordinatorHarness public registryCoordinator; StakeRegistryHarness public stakeRegistry; BLSApkRegistryHarness public blsApkRegistry; IIndexRegistry public indexRegistry; + ServiceManagerBase public serviceManager; StrategyManagerMock public strategyManagerMock; DelegationMock public delegationMock; @@ -180,6 +184,16 @@ contract MockAVSDeployer is Test { ) ); + serviceManager = ServiceManagerBase( + address( + new TransparentUpgradeableProxy( + address(emptyContract), + address(proxyAdmin), + "" + ) + ) + ); + cheats.stopPrank(); cheats.startPrank(proxyAdminOwner); @@ -212,6 +226,19 @@ contract MockAVSDeployer is Test { address(indexRegistryImplementation) ); + serviceManagerImplementation = new ServiceManagerBase( + delegationMock, + registryCoordinator, + stakeRegistry + ); + + proxyAdmin.upgrade( + TransparentUpgradeableProxy(payable(address(serviceManager))), + address(serviceManagerImplementation) + ); + + serviceManager.initialize({initialOwner: registryCoordinatorOwner}); + // set the public key for an operator, using harnessed function to bypass checks blsApkRegistry.setBLSPublicKey(defaultOperator, defaultPubKey); @@ -233,8 +260,7 @@ contract MockAVSDeployer is Test { } registryCoordinatorImplementation = new RegistryCoordinatorHarness( - delegationMock, - slasher, + serviceManager, stakeRegistry, blsApkRegistry, indexRegistry From fd1e1e8605bd7490e34288d216adebf93f2afddf Mon Sep 17 00:00:00 2001 From: Alex <18387287+wadealexc@users.noreply.github.com> Date: Thu, 14 Dec 2023 13:40:01 -0500 Subject: [PATCH 093/101] fix: correctly set bit in stake registry (#112) --- src/BLSSignatureChecker.sol | 2 +- src/RegistryCoordinator.sol | 2 +- src/StakeRegistry.sol | 6 ++++-- src/libraries/BitmapUtils.sol | 16 +++++++++++++--- test/harnesses/BitmapUtilsWrapper.sol | 4 ++-- test/unit/BitmapUtils.t.sol | 14 +++++++------- 6 files changed, 28 insertions(+), 16 deletions(-) diff --git a/src/BLSSignatureChecker.sol b/src/BLSSignatureChecker.sol index 99d501ba..03dbe42d 100644 --- a/src/BLSSignatureChecker.sol +++ b/src/BLSSignatureChecker.sol @@ -219,7 +219,7 @@ contract BLSSignatureChecker is IBLSSignatureChecker { // if so, load their stake at referenceBlockNumber and subtract it from running stake signed for (uint256 j = 0; j < params.nonSignerPubkeys.length; j++) { // if the nonSigner is a part of the quorum, subtract their stake from the running total - if (BitmapUtils.numberIsInBitmap(nonSigners.quorumBitmaps[j], uint8(quorumNumbers[i]))) { + if (BitmapUtils.isSet(nonSigners.quorumBitmaps[j], uint8(quorumNumbers[i]))) { stakeTotals.signedStakeForQuorum[i] -= stakeRegistry.getStakeAtBlockNumberAndIndex({ quorumNumber: uint8(quorumNumbers[i]), diff --git a/src/RegistryCoordinator.sol b/src/RegistryCoordinator.sol index c3ec5aa6..9345ac5b 100644 --- a/src/RegistryCoordinator.sol +++ b/src/RegistryCoordinator.sol @@ -331,7 +331,7 @@ contract RegistryCoordinator is { uint192 currentBitmap = _currentOperatorBitmap(operatorId); require( - BitmapUtils.numberIsInBitmap(currentBitmap, quorumNumber), + BitmapUtils.isSet(currentBitmap, quorumNumber), "RegistryCoordinator.updateOperatorsForQuorum: operator not in quorum" ); // Require check is to prevent duplicate operators and that all quorum operators are updated diff --git a/src/StakeRegistry.sol b/src/StakeRegistry.sol index 5f059796..e83b3278 100644 --- a/src/StakeRegistry.sol +++ b/src/StakeRegistry.sol @@ -20,6 +20,8 @@ import {BitmapUtils} from "src/libraries/BitmapUtils.sol"; * @author Layr Labs, Inc. */ contract StakeRegistry is StakeRegistryStorage { + + using BitmapUtils for *; modifier onlyRegistryCoordinator() { require( @@ -138,7 +140,7 @@ contract StakeRegistry is StakeRegistryStorage { * or more quorums. * * If the operator no longer has the minimum stake required for a quorum, they are - * added to the + * added to the `quorumsToRemove`, which is returned to the registry coordinator * @return A bitmap of quorums where the operator no longer meets the minimum stake * and should be deregistered. */ @@ -168,7 +170,7 @@ contract StakeRegistry is StakeRegistryStorage { // If the operator no longer meets the minimum stake, set their stake to zero and mark them for removal if (!hasMinimumStake) { stakeWeight = 0; - quorumsToRemove |= quorumNumber; + quorumsToRemove = uint192(quorumsToRemove.setBit(quorumNumber)); } // Update the operator's stake and retrieve the delta diff --git a/src/libraries/BitmapUtils.sol b/src/libraries/BitmapUtils.sol index e5f789a9..58315e08 100644 --- a/src/libraries/BitmapUtils.sol +++ b/src/libraries/BitmapUtils.sol @@ -295,9 +295,19 @@ library BitmapUtils { return count; } - /// @notice returns 'true' if `numberToCheckForInclusion` is in `bitmap` and 'false' otherwise. - function numberIsInBitmap(uint256 bitmap, uint8 numberToCheckForInclusion) internal pure returns (bool) { - return (((bitmap >> numberToCheckForInclusion) & 1) == 1); + /// @notice Returns `true` if `bit` is in `bitmap`. Returns `false` otherwise. + function isSet(uint256 bitmap, uint8 bit) internal pure returns (bool) { + return 1 == ((bitmap >> bit) & 1); + } + + /** + * @notice Returns a copy of `bitmap` with `bit` set. + * @dev IMPORTANT: we're dealing with stack values here, so this doesn't modify + * the original bitmap. Using this correctly requires an assignment statement: + * `bitmap = bitmap.setBit(bit);` + */ + function setBit(uint256 bitmap, uint8 bit) internal pure returns (uint256) { + return bitmap | (1 << bit); } /** diff --git a/test/harnesses/BitmapUtilsWrapper.sol b/test/harnesses/BitmapUtilsWrapper.sol index 68d2ab38..4a231916 100644 --- a/test/harnesses/BitmapUtilsWrapper.sol +++ b/test/harnesses/BitmapUtilsWrapper.sol @@ -33,7 +33,7 @@ contract BitmapUtilsWrapper { return BitmapUtils.countNumOnes(n); } - function numberIsInBitmap(uint256 bitmap, uint8 numberToCheckForInclusion) external pure returns (bool) { - return BitmapUtils.numberIsInBitmap(bitmap, numberToCheckForInclusion); + function isSet(uint256 bitmap, uint8 numberToCheckForInclusion) external pure returns (bool) { + return BitmapUtils.isSet(bitmap, numberToCheckForInclusion); } } diff --git a/test/unit/BitmapUtils.t.sol b/test/unit/BitmapUtils.t.sol index 76225aa9..fedaefb5 100644 --- a/test/unit/BitmapUtils.t.sol +++ b/test/unit/BitmapUtils.t.sol @@ -166,15 +166,15 @@ contract BitmapUtilsUnitTests is Test { require(libraryOutput == numOnes, "inconsistency in countNumOnes function"); } - // @notice some simple sanity checks on the `numberIsInBitmap` function + // @notice some simple sanity checks on the `isSet` function function testNumberIsInBitmap() public view { - require(bitmapUtilsWrapper.numberIsInBitmap(2 ** 6, 6), "numberIsInBitmap function is broken 0"); - require(bitmapUtilsWrapper.numberIsInBitmap(1, 0), "numberIsInBitmap function is broken 1"); - require(bitmapUtilsWrapper.numberIsInBitmap(255, 7), "numberIsInBitmap function is broken 2"); - require(bitmapUtilsWrapper.numberIsInBitmap(1024, 10), "numberIsInBitmap function is broken 3"); + require(bitmapUtilsWrapper.isSet(2 ** 6, 6), "isSet function is broken 0"); + require(bitmapUtilsWrapper.isSet(1, 0), "isSet function is broken 1"); + require(bitmapUtilsWrapper.isSet(255, 7), "isSet function is broken 2"); + require(bitmapUtilsWrapper.isSet(1024, 10), "isSet function is broken 3"); for (uint256 i = 0; i < 256; ++i) { - require(bitmapUtilsWrapper.numberIsInBitmap(type(uint256).max, uint8(i)), "numberIsInBitmap function is broken 4"); - require(!bitmapUtilsWrapper.numberIsInBitmap(0, uint8(i)), "numberIsInBitmap function is broken 5"); + require(bitmapUtilsWrapper.isSet(type(uint256).max, uint8(i)), "isSet function is broken 4"); + require(!bitmapUtilsWrapper.isSet(0, uint8(i)), "isSet function is broken 5"); } } } \ No newline at end of file From a7f4b9862dd406fc87b635db97ed3da358958f6e Mon Sep 17 00:00:00 2001 From: Yash Patil <40046473+ypatil12@users.noreply.github.com> Date: Fri, 15 Dec 2023 15:08:29 -0500 Subject: [PATCH 094/101] feat: add restakeable strategies to service manager for indexing (#113) --- src/ServiceManagerBase.sol | 30 ++++++++++++++++++++++++++++++ src/interfaces/IServiceManager.sol | 8 ++++++++ 2 files changed, 38 insertions(+) diff --git a/src/ServiceManagerBase.sol b/src/ServiceManagerBase.sol index 9c63fa06..2bdf3e07 100644 --- a/src/ServiceManagerBase.sol +++ b/src/ServiceManagerBase.sol @@ -77,6 +77,36 @@ contract ServiceManagerBase is IServiceManager, OwnableUpgradeable { delegationManager.deregisterOperatorFromAVS(operator); } + /** + * @notice Returns the list of strategies that the AVS supports for restaking + * @dev This function is intended to be called off-chain + * @dev No guarantee is made on uniqueness of each element in the returned array. + * The off-chain service should do that validation separately + */ + function getRestakeableStrategies() external view returns (address[] memory) { + uint256 quorumCount = registryCoordinator.quorumCount(); + + if (quorumCount == 0) { + return new address[](0); + } + + uint256 strategyCount; + for(uint256 i = 0; i < quorumCount; i++) { + strategyCount += stakeRegistry.strategyParamsLength(uint8(i)); + } + + address[] memory restakedStrategies = new address[](strategyCount); + uint256 index = 0; + for(uint256 i = 0; i < registryCoordinator.quorumCount(); i++) { + uint256 strategyParamsLength = stakeRegistry.strategyParamsLength(uint8(i)); + for (uint256 j = 0; j < strategyParamsLength; j++) { + restakedStrategies[index] = address(stakeRegistry.strategyParamsByIndex(uint8(i), j).strategy); + index++; + } + } + return restakedStrategies; + } + /** * @notice Returns the list of strategies that the operator has potentially restaked on the AVS * @param operator The address of the operator to get restaked strategies for diff --git a/src/interfaces/IServiceManager.sol b/src/interfaces/IServiceManager.sol index 1367fa41..ca22de58 100644 --- a/src/interfaces/IServiceManager.sol +++ b/src/interfaces/IServiceManager.sol @@ -39,4 +39,12 @@ interface IServiceManager { * of each element in the returned array. The off-chain service should do that validation separately */ function getOperatorRestakedStrategies(address operator) external view returns (address[] memory); + + /** + * @notice Returns the list of strategies that the AVS supports for restaking + * @dev This function is intended to be called off-chain + * @dev No guarantee is made on uniqueness of each element in the returned array. + * The off-chain service should do that validation separately + */ + function getRestakeableStrategies() external view returns (address[] memory); } From 51171fcbc5901777cc36592203df42059fe9030f Mon Sep 17 00:00:00 2001 From: steven Date: Mon, 18 Dec 2023 14:48:26 -0500 Subject: [PATCH 095/101] chore: resolve imports --- script/AVSContractsDeploy.s.sol | 5 ----- test/unit/VoteWeigherBaseUnit.t.sol | 11 ----------- 2 files changed, 16 deletions(-) diff --git a/script/AVSContractsDeploy.s.sol b/script/AVSContractsDeploy.s.sol index 9b069b14..d16c3b2b 100644 --- a/script/AVSContractsDeploy.s.sol +++ b/script/AVSContractsDeploy.s.sol @@ -19,11 +19,6 @@ import "eigenlayer-contracts/src/contracts/pods/EigenPodManager.sol"; import "eigenlayer-contracts/src/contracts/pods/DelayedWithdrawalRouter.sol"; import "eigenlayer-contracts/src/contracts/permissions/PauserRegistry.sol"; -<<<<<<< HEAD -import "../src/BLSPublicKeyCompendium.sol"; -======= ->>>>>>> 68f3817 (chore: delete BLSPublicKeyCompendium and associated interface) - import "eigenlayer-contracts/src/test/mocks/EmptyContract.sol"; import "eigenlayer-contracts/src/test/mocks/ERC20Mock.sol"; diff --git a/test/unit/VoteWeigherBaseUnit.t.sol b/test/unit/VoteWeigherBaseUnit.t.sol index 0b415ab8..a2ba2cfc 100644 --- a/test/unit/VoteWeigherBaseUnit.t.sol +++ b/test/unit/VoteWeigherBaseUnit.t.sol @@ -9,19 +9,8 @@ import {IStrategyManager} from "eigenlayer-contracts/src/contracts/interfaces/IS import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; import {IEigenPodManager} from "eigenlayer-contracts/src/contracts/interfaces/IEigenPodManager.sol"; import {ISlasher} from "eigenlayer-contracts/src/contracts/interfaces/ISlasher.sol"; -<<<<<<< HEAD -<<<<<<< HEAD import {IServiceManager} from "../../src/interfaces/IServiceManager.sol"; -import {IVoteWeigher} from "../../src/interfaces/IVoteWeigher.sol"; import {StakeRegistry} from "../../src/StakeRegistry.sol"; -======= -import {IServiceManager} from "src/interfaces/IServiceManager.sol"; -======= ->>>>>>> ecf7849 (chore: remove ServiceManagerBase and add RegistryCoordinator owner (#98)) -import {IStakeRegistry} from "src/interfaces/IStakeRegistry.sol"; -import {StakeRegistry} from "src/StakeRegistry.sol"; ->>>>>>> 12b09de (fix: fix compilation issues and tests) - import {RegistryCoordinatorMock} from "../mocks/RegistryCoordinatorMock.sol"; import {OwnableMock} from "eigenlayer-contracts/src/test/mocks/OwnableMock.sol"; import {DelegationManagerMock} from "eigenlayer-contracts/src/test/mocks/DelegationManagerMock.sol"; From cd7600ec2fa3fc1de10c38cdaf412c068440c4e7 Mon Sep 17 00:00:00 2001 From: steven Date: Mon, 18 Dec 2023 15:51:13 -0500 Subject: [PATCH 096/101] fix: remove eigenlayer deployer --- test/EigenLayerDeployer.t.sol | 374 ---------------------------------- 1 file changed, 374 deletions(-) delete mode 100644 test/EigenLayerDeployer.t.sol diff --git a/test/EigenLayerDeployer.t.sol b/test/EigenLayerDeployer.t.sol deleted file mode 100644 index d33f523a..00000000 --- a/test/EigenLayerDeployer.t.sol +++ /dev/null @@ -1,374 +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/IBeacon.sol"; -import "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol"; - -import "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; -import "eigenlayer-contracts/src/contracts/core/DelegationManager.sol"; -import {StakeRegistry} from "src/StakeRegistry.sol"; - -import {IETHPOSDeposit} from "eigenlayer-contracts/src/contracts/interfaces/IETHPOSDeposit.sol"; -import {IBeaconChainOracle} from "eigenlayer-contracts/src/contracts/interfaces/IBeaconChainOracle.sol"; - -import {StrategyManager} from "eigenlayer-contracts/src/contracts/core/StrategyManager.sol"; -import {StrategyBase} from "eigenlayer-contracts/src/contracts/strategies/StrategyBase.sol"; -import {Slasher} from "eigenlayer-contracts/src/contracts/core/Slasher.sol"; - -import {EigenPod} from "eigenlayer-contracts/src/contracts/pods/EigenPod.sol"; -import {EigenPodManager} from "eigenlayer-contracts/src/contracts/pods/EigenPodManager.sol"; -import {DelayedWithdrawalRouter} from "eigenlayer-contracts/src/contracts/pods/DelayedWithdrawalRouter.sol"; - -import {PauserRegistry} from "eigenlayer-contracts/src/contracts/permissions/PauserRegistry.sol"; - - -import {Operators} from "test/utils/Operators.sol"; - -import {WETH} from "eigenlayer-contracts/src/test/mocks/LiquidStakingToken.sol"; -import {IDelayedWithdrawalRouter} from "eigenlayer-contracts/src/contracts/interfaces/IDelayedWithdrawalRouter.sol"; -import {EmptyContract} from "eigenlayer-contracts/src/test/mocks/EmptyContract.sol"; -import {ETHPOSDepositMock } from "eigenlayer-contracts/src/test/mocks/ETHDepositMock.sol"; -import {BeaconChainOracleMock} from "eigenlayer-contracts/src/test/mocks/BeaconChainOracleMock.sol"; - -import "forge-std/Test.sol"; - -contract EigenLayerDeployer is Operators { - - Vm cheats = Vm(HEVM_ADDRESS); - - // EigenLayer contracts - ProxyAdmin public eigenLayerProxyAdmin; - PauserRegistry public eigenLayerPauserReg; - - Slasher public slasher; - DelegationManager public delegation; - StrategyManager public strategyManager; - EigenPodManager public eigenPodManager; - IEigenPod public pod; - IDelayedWithdrawalRouter public delayedWithdrawalRouter; - IETHPOSDeposit public ethPOSDeposit; - IBeacon public eigenPodBeacon; - - // testing/mock contracts - IERC20 public eigenToken; - IERC20 public weth; - StrategyBase public wethStrat; - StrategyBase public eigenStrat; - StrategyBase public baseStrategyImplementation; - EmptyContract public emptyContract; - - mapping(uint256 => IStrategy) public strategies; - - //from testing seed phrase - bytes32 priv_key_0 = 0x1234567812345678123456781234567812345678123456781234567812345678; - bytes32 priv_key_1 = 0x1234567812345678123456781234567812345698123456781234567812348976; - - //strategy indexes for undelegation (see commitUndelegation function) - uint256[] public strategyIndexes; - address[2] public stakers; - address sample_registrant = cheats.addr(436364636); - - address[] public slashingContracts; - - uint256 wethInitialSupply = 10e50; - uint256 public constant eigenTotalSupply = 1000e18; - uint256 nonce = 69; - uint256 public gasLimit = 750000; - uint32 PARTIAL_WITHDRAWAL_FRAUD_PROOF_PERIOD_BLOCKS = 7 days / 12 seconds; - uint256 REQUIRED_BALANCE_WEI = 31 ether; - uint64 MAX_PARTIAL_WTIHDRAWAL_AMOUNT_GWEI = 1 ether / 1e9; - uint64 MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR = 32e9; - uint64 GOERLI_GENESIS_TIME = 1616508000; - - address pauser; - address unpauser; - address theMultiSig = address(420); - address operator = address(0x4206904396bF2f8b173350ADdEc5007A52664293); //sk: e88d9d864d5d731226020c5d2f02b62a4ce2a4534a39c225d32d3db795f83319 - address acct_0 = cheats.addr(uint256(priv_key_0)); - address acct_1 = cheats.addr(uint256(priv_key_1)); - address _challenger = address(0x6966904396bF2f8b173350bCcec5007A52669873); - address public eigenLayerReputedMultisig = address(this); - - address eigenLayerProxyAdminAddress; - address eigenLayerPauserRegAddress; - address slasherAddress; - address delegationAddress; - address strategyManagerAddress; - address eigenPodManagerAddress; - address podAddress; - address delayedWithdrawalRouterAddress; - address eigenPodBeaconAddress; - address beaconChainOracleAddress; - address emptyContractAddress; - 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; - - - //ensures that a passed in address is not set to true in the fuzzedAddressMapping - modifier fuzzedAddress(address addr) virtual { - cheats.assume(fuzzedAddressMapping[addr] == false); - _; - } - - modifier cannotReinit() { - cheats.expectRevert(bytes("Initializable: contract is already initialized")); - _; - } - - //performs basic deployment before each test - // for fork tests run: forge test -vv --fork-url https://eth-goerli.g.alchemy.com/v2/demo -vv - function setUp() public virtual { - try vm.envUint("CHAIN_ID") returns (uint256 chainId) { - if (chainId == 31337) { - _deployEigenLayerContractsLocal(); - } else if (chainId == 5) { - _deployEigenLayerContractsGoerli(); - } - // If CHAIN_ID ENV is not set, assume local deployment on 31337 - } catch { - _deployEigenLayerContractsLocal(); - } - - fuzzedAddressMapping[address(0)] = true; - fuzzedAddressMapping[address(eigenLayerProxyAdmin)] = true; - fuzzedAddressMapping[address(strategyManager)] = true; - fuzzedAddressMapping[address(eigenPodManager)] = true; - fuzzedAddressMapping[address(delegation)] = true; - fuzzedAddressMapping[address(slasher)] = true; - } - - function _deployEigenLayerContractsGoerli() internal { - _setAddresses(goerliDeploymentConfig); - pauser = operationsMultisig; - unpauser = executorMultisig; - // deploy proxy admin for ability to upgrade proxy contracts - eigenLayerProxyAdmin = ProxyAdmin(eigenLayerProxyAdminAddress); - - emptyContract = new EmptyContract(); - - //deploy pauser registry - eigenLayerPauserReg = PauserRegistry(eigenLayerPauserRegAddress); - - delegation = DelegationManager(delegationAddress); - strategyManager = StrategyManager(strategyManagerAddress); - slasher = Slasher(slasherAddress); - eigenPodManager = EigenPodManager(eigenPodManagerAddress); - delayedWithdrawalRouter = DelayedWithdrawalRouter(delayedWithdrawalRouterAddress); - - beaconChainOracleAddress = address(new BeaconChainOracleMock()); - - ethPOSDeposit = new ETHPOSDepositMock(); - pod = new EigenPod(ethPOSDeposit, delayedWithdrawalRouter, eigenPodManager, MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR, 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) - ); - - // deploy StrategyBase contract implementation, then create upgradeable proxy that points to implementation and initialize it - baseStrategyImplementation = new StrategyBase(strategyManager); - wethStrat = StrategyBase( - address( - new TransparentUpgradeableProxy( - address(baseStrategyImplementation), - address(eigenLayerProxyAdmin), - abi.encodeWithSelector(StrategyBase.initialize.selector, weth, eigenLayerPauserReg) - ) - ) - ); - - eigenToken = new ERC20PresetFixedSupply( - "eigen", - "EIGEN", - wethInitialSupply, - address(this) - ); - - // deploy upgradeable proxy that points to StrategyBase implementation and initialize it - eigenStrat = StrategyBase( - address( - new TransparentUpgradeableProxy( - address(baseStrategyImplementation), - address(eigenLayerProxyAdmin), - abi.encodeWithSelector(StrategyBase.initialize.selector, eigenToken, eigenLayerPauserReg) - ) - ) - ); - - stakers = [acct_0, acct_1]; - } - - function _deployEigenLayerContractsLocal() internal { - pauser = address(69); - unpauser = address(489); - // deploy proxy admin for ability to upgrade proxy contracts - eigenLayerProxyAdmin = new ProxyAdmin(); - - //deploy pauser registry - address[] memory pausers = new address[](1); - pausers[0] = pauser; - eigenLayerPauserReg = new PauserRegistry(pausers, unpauser); - - /** - * 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), "")) - ); - - ethPOSDeposit = new ETHPOSDepositMock(); - pod = new EigenPod(ethPOSDeposit, delayedWithdrawalRouter, eigenPodManager, MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR, GOERLI_GENESIS_TIME); - - eigenPodBeacon = new UpgradeableBeacon(address(pod)); - - // Second, deploy the *implementation* contracts, using the *proxy contracts* as inputs - 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); - DelayedWithdrawalRouter 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, - eigenLayerReputedMultisig, - eigenLayerPauserReg, - 0/*initialPausedStatus*/ - ) - ); - eigenLayerProxyAdmin.upgradeAndCall( - TransparentUpgradeableProxy(payable(address(strategyManager))), - address(strategyManagerImplementation), - abi.encodeWithSelector( - StrategyManager.initialize.selector, - eigenLayerReputedMultisig, - eigenLayerReputedMultisig, - eigenLayerPauserReg, - 0/*initialPausedStatus*/ - ) - ); - eigenLayerProxyAdmin.upgradeAndCall( - TransparentUpgradeableProxy(payable(address(slasher))), - address(slasherImplementation), - abi.encodeWithSelector( - Slasher.initialize.selector, - eigenLayerReputedMultisig, - eigenLayerPauserReg, - 0/*initialPausedStatus*/ - ) - ); - eigenLayerProxyAdmin.upgradeAndCall( - TransparentUpgradeableProxy(payable(address(eigenPodManager))), - address(eigenPodManagerImplementation), - abi.encodeWithSelector( - EigenPodManager.initialize.selector, - type(uint256).max, // maxPods - beaconChainOracleAddress, - eigenLayerReputedMultisig, - eigenLayerPauserReg, - 0/*initialPausedStatus*/ - ) - ); - uint256 initPausedStatus = 0; - uint256 withdrawalDelayBlocks = PARTIAL_WITHDRAWAL_FRAUD_PROOF_PERIOD_BLOCKS; - eigenLayerProxyAdmin.upgradeAndCall( - TransparentUpgradeableProxy(payable(address(delayedWithdrawalRouter))), - address(delayedWithdrawalRouterImplementation), - 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) - ); - - // deploy StrategyBase contract implementation, then create upgradeable proxy that points to implementation and initialize it - baseStrategyImplementation = new StrategyBase(strategyManager); - wethStrat = StrategyBase( - address( - new TransparentUpgradeableProxy( - address(baseStrategyImplementation), - address(eigenLayerProxyAdmin), - abi.encodeWithSelector(StrategyBase.initialize.selector, weth, eigenLayerPauserReg) - ) - ) - ); - - eigenToken = new ERC20PresetFixedSupply( - "eigen", - "EIGEN", - wethInitialSupply, - address(this) - ); - - // deploy upgradeable proxy that points to StrategyBase implementation and initialize it - eigenStrat = StrategyBase( - address( - new TransparentUpgradeableProxy( - address(baseStrategyImplementation), - address(eigenLayerProxyAdmin), - abi.encodeWithSelector(StrategyBase.initialize.selector, eigenToken, eigenLayerPauserReg) - ) - ) - ); - - stakers = [acct_0, acct_1]; - } - - function _setAddresses(string memory config) internal { - 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"); - delayedWithdrawalRouterAddress = stdJson.readAddress(config, ".addresses.delayedWithdrawalRouter"); - emptyContractAddress = stdJson.readAddress(config, ".addresses.emptyContract"); - operationsMultisig = stdJson.readAddress(config, ".parameters.operationsMultisig"); - executorMultisig = stdJson.readAddress(config, ".parameters.executorMultisig"); - } - -} From f272bd41f4ae3b27fea40dfdc2328c258ac37519 Mon Sep 17 00:00:00 2001 From: steven Date: Mon, 18 Dec 2023 16:08:53 -0500 Subject: [PATCH 097/101] chore: additional relative imports --- src/BLSApkRegistry.sol | 6 ++-- src/BLSApkRegistryStorage.sol | 6 ++-- src/BLSSignatureChecker.sol | 12 +++---- src/IndexRegistry.sol | 4 +-- src/IndexRegistryStorage.sol | 4 +-- src/OperatorStateRetriever.sol | 10 +++--- src/RegistryCoordinator.sol | 18 +++++----- src/ServiceManagerBase.sol | 8 ++--- src/StakeRegistry.sol | 8 ++--- src/StakeRegistryStorage.sol | 4 +-- src/interfaces/IBLSApkRegistry.sol | 4 +-- src/interfaces/IBLSSignatureChecker.sol | 8 ++--- src/interfaces/IRegistryCoordinator.sol | 8 ++--- test/ffi/BLSPubKeyCompendiumFFI.t.sol | 6 ++-- test/ffi/BLSSignatureCheckerFFI.t.sol | 12 +++---- test/harnesses/BLSApkRegistryHarness.sol | 2 +- .../RegistryCoordinatorHarness.t.sol | 2 +- test/harnesses/StakeRegistryHarness.sol | 2 +- test/mocks/RegistryCoordinatorMock.sol | 2 +- test/unit/StakeRegistryUnit.t.sol | 18 +++++----- test/unit/VoteWeigherBaseUnit.t.sol | 2 +- test/utils/BLSMockAVSDeployer.sol | 10 +++--- test/utils/MockAVSDeployer.sol | 36 +++++++++---------- 23 files changed, 96 insertions(+), 96 deletions(-) diff --git a/src/BLSApkRegistry.sol b/src/BLSApkRegistry.sol index ea6d318f..bd140036 100644 --- a/src/BLSApkRegistry.sol +++ b/src/BLSApkRegistry.sol @@ -1,11 +1,11 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity =0.8.12; -import {BLSApkRegistryStorage} from "src/BLSApkRegistryStorage.sol"; +import {BLSApkRegistryStorage} from "./BLSApkRegistryStorage.sol"; -import {IRegistryCoordinator} from "src/interfaces/IRegistryCoordinator.sol"; +import {IRegistryCoordinator} from "./interfaces/IRegistryCoordinator.sol"; -import {BN254} from "src/libraries/BN254.sol"; +import {BN254} from "./libraries/BN254.sol"; contract BLSApkRegistry is BLSApkRegistryStorage { using BN254 for BN254.G1Point; diff --git a/src/BLSApkRegistryStorage.sol b/src/BLSApkRegistryStorage.sol index 878d8a10..e40a2957 100644 --- a/src/BLSApkRegistryStorage.sol +++ b/src/BLSApkRegistryStorage.sol @@ -1,12 +1,12 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity =0.8.12; -import {IBLSApkRegistry} from "src/interfaces/IBLSApkRegistry.sol"; -import {IRegistryCoordinator} from "src/interfaces/IRegistryCoordinator.sol"; +import {IBLSApkRegistry} from "./interfaces/IBLSApkRegistry.sol"; +import {IRegistryCoordinator} from "./interfaces/IRegistryCoordinator.sol"; import {Initializable} from "@openzeppelin-upgrades/contracts/proxy/utils/Initializable.sol"; -import {BN254} from "src/libraries/BN254.sol"; +import {BN254} from "./libraries/BN254.sol"; abstract contract BLSApkRegistryStorage is Initializable, IBLSApkRegistry { /// @notice the hash of the zero pubkey aka BN254.G1Point(0,0) diff --git a/src/BLSSignatureChecker.sol b/src/BLSSignatureChecker.sol index 03dbe42d..5aecde91 100644 --- a/src/BLSSignatureChecker.sol +++ b/src/BLSSignatureChecker.sol @@ -1,13 +1,13 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity =0.8.12; -import {IBLSSignatureChecker} from "src/interfaces/IBLSSignatureChecker.sol"; -import {IRegistryCoordinator} from "src/interfaces/IRegistryCoordinator.sol"; -import {IBLSApkRegistry} from "src/interfaces/IBLSApkRegistry.sol"; -import {IStakeRegistry, IDelegationManager} from "src/interfaces/IStakeRegistry.sol"; +import {IBLSSignatureChecker} from "./interfaces/IBLSSignatureChecker.sol"; +import {IRegistryCoordinator} from "./interfaces/IRegistryCoordinator.sol"; +import {IBLSApkRegistry} from "./interfaces/IBLSApkRegistry.sol"; +import {IStakeRegistry, IDelegationManager} from "./interfaces/IStakeRegistry.sol"; -import {BitmapUtils} from "src/libraries/BitmapUtils.sol"; -import {BN254} from "src/libraries/BN254.sol"; +import {BitmapUtils} from "./libraries/BitmapUtils.sol"; +import {BN254} from "./libraries/BN254.sol"; /** * @title Used for checking BLS aggregate signatures from the operators of a `BLSRegistry`. diff --git a/src/IndexRegistry.sol b/src/IndexRegistry.sol index 0d9a8340..05611deb 100644 --- a/src/IndexRegistry.sol +++ b/src/IndexRegistry.sol @@ -1,8 +1,8 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity =0.8.12; -import {IndexRegistryStorage} from "src/IndexRegistryStorage.sol"; -import {IRegistryCoordinator} from "src/interfaces/IRegistryCoordinator.sol"; +import {IndexRegistryStorage} from "./IndexRegistryStorage.sol"; +import {IRegistryCoordinator} from "./interfaces/IRegistryCoordinator.sol"; /** * @title A `Registry` that keeps track of an ordered list of operators for each quorum diff --git a/src/IndexRegistryStorage.sol b/src/IndexRegistryStorage.sol index 2b4a57eb..0c403779 100644 --- a/src/IndexRegistryStorage.sol +++ b/src/IndexRegistryStorage.sol @@ -3,8 +3,8 @@ pragma solidity =0.8.12; import {Initializable} from "@openzeppelin-upgrades/contracts/proxy/utils/Initializable.sol"; -import {IRegistryCoordinator} from "src/interfaces/IRegistryCoordinator.sol"; -import {IIndexRegistry} from "src/interfaces/IIndexRegistry.sol"; +import {IRegistryCoordinator} from "./interfaces/IRegistryCoordinator.sol"; +import {IIndexRegistry} from "./interfaces/IIndexRegistry.sol"; /** * @title Storage variables for the `IndexRegistry` contract. diff --git a/src/OperatorStateRetriever.sol b/src/OperatorStateRetriever.sol index f87439b3..d899679d 100644 --- a/src/OperatorStateRetriever.sol +++ b/src/OperatorStateRetriever.sol @@ -1,12 +1,12 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity =0.8.12; -import {IRegistryCoordinator} from "src/interfaces/IRegistryCoordinator.sol"; -import {IBLSApkRegistry} from "src/interfaces/IBLSApkRegistry.sol"; -import {IStakeRegistry} from "src/interfaces/IStakeRegistry.sol"; -import {IIndexRegistry} from "src/interfaces/IIndexRegistry.sol"; +import {IRegistryCoordinator} from "./interfaces/IRegistryCoordinator.sol"; +import {IBLSApkRegistry} from "./interfaces/IBLSApkRegistry.sol"; +import {IStakeRegistry} from "./interfaces/IStakeRegistry.sol"; +import {IIndexRegistry} from "./interfaces/IIndexRegistry.sol"; -import {BitmapUtils} from "src/libraries/BitmapUtils.sol"; +import {BitmapUtils} from "./libraries/BitmapUtils.sol"; /** * @title OperatorStateRetriever with view functions that allow to retrieve the state of an AVSs registry system. diff --git a/src/RegistryCoordinator.sol b/src/RegistryCoordinator.sol index 9345ac5b..b96a32f1 100644 --- a/src/RegistryCoordinator.sol +++ b/src/RegistryCoordinator.sol @@ -9,16 +9,16 @@ import {EIP1271SignatureUtils} from "eigenlayer-contracts/src/contracts/librarie import {IPauserRegistry} from "eigenlayer-contracts/src/contracts/interfaces/IPauserRegistry.sol"; import {Pausable} from "eigenlayer-contracts/src/contracts/permissions/Pausable.sol"; -import {IRegistryCoordinator} from "src/interfaces/IRegistryCoordinator.sol"; +import {IRegistryCoordinator} from "./interfaces/IRegistryCoordinator.sol"; import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol"; -import {IBLSApkRegistry} from "src/interfaces/IBLSApkRegistry.sol"; -import {ISocketUpdater} from "src/interfaces/ISocketUpdater.sol"; -import {IStakeRegistry} from "src/interfaces/IStakeRegistry.sol"; -import {IIndexRegistry} from "src/interfaces/IIndexRegistry.sol"; -import {IServiceManager} from "src/interfaces/IServiceManager.sol"; - -import {BitmapUtils} from "src/libraries/BitmapUtils.sol"; -import {BN254} from "src/libraries/BN254.sol"; +import {IBLSApkRegistry} from "./interfaces/IBLSApkRegistry.sol"; +import {ISocketUpdater} from "./interfaces/ISocketUpdater.sol"; +import {IStakeRegistry} from "./interfaces/IStakeRegistry.sol"; +import {IIndexRegistry} from "./interfaces/IIndexRegistry.sol"; +import {IServiceManager} from "./interfaces/IServiceManager.sol"; + +import {BitmapUtils} from "./libraries/BitmapUtils.sol"; +import {BN254} from "./libraries/BN254.sol"; /** * @title A `RegistryCoordinator` that has three registries: diff --git a/src/ServiceManagerBase.sol b/src/ServiceManagerBase.sol index 2bdf3e07..eb58ab40 100644 --- a/src/ServiceManagerBase.sol +++ b/src/ServiceManagerBase.sol @@ -3,13 +3,13 @@ pragma solidity =0.8.12; import {OwnableUpgradeable} from "@openzeppelin-upgrades/contracts/access/OwnableUpgradeable.sol"; -import {BitmapUtils} from "src/libraries/BitmapUtils.sol"; +import {BitmapUtils} from "./libraries/BitmapUtils.sol"; import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol"; import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; -import {IServiceManager} from "src/interfaces/IServiceManager.sol"; -import {IRegistryCoordinator} from "src/interfaces/IRegistryCoordinator.sol"; -import {IStakeRegistry} from "src/interfaces/IStakeRegistry.sol"; +import {IServiceManager} from "./interfaces/IServiceManager.sol"; +import {IRegistryCoordinator} from "./interfaces/IRegistryCoordinator.sol"; +import {IStakeRegistry} from "./interfaces/IStakeRegistry.sol"; /** * @title Minimal implementation of a ServiceManager-type contract. diff --git a/src/StakeRegistry.sol b/src/StakeRegistry.sol index e83b3278..964501a9 100644 --- a/src/StakeRegistry.sol +++ b/src/StakeRegistry.sol @@ -3,12 +3,12 @@ pragma solidity =0.8.12; import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; -import {StakeRegistryStorage} from "src/StakeRegistryStorage.sol"; +import {StakeRegistryStorage} from "./StakeRegistryStorage.sol"; -import {IRegistryCoordinator} from "src/interfaces/IRegistryCoordinator.sol"; -import {IStakeRegistry} from "src/interfaces/IStakeRegistry.sol"; +import {IRegistryCoordinator} from "./interfaces/IRegistryCoordinator.sol"; +import {IStakeRegistry} from "./interfaces/IStakeRegistry.sol"; -import {BitmapUtils} from "src/libraries/BitmapUtils.sol"; +import {BitmapUtils} from "./libraries/BitmapUtils.sol"; /** * @title A `Registry` that keeps track of stakes of operators for up to 256 quorums. diff --git a/src/StakeRegistryStorage.sol b/src/StakeRegistryStorage.sol index 30f8365e..768819a4 100644 --- a/src/StakeRegistryStorage.sol +++ b/src/StakeRegistryStorage.sol @@ -4,8 +4,8 @@ pragma solidity =0.8.12; import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; import {IStrategyManager} from "eigenlayer-contracts/src/contracts/interfaces/IStrategyManager.sol"; -import {IRegistryCoordinator} from "src/interfaces/IRegistryCoordinator.sol"; -import {IStakeRegistry} from "src/interfaces/IStakeRegistry.sol"; +import {IRegistryCoordinator} from "./interfaces/IRegistryCoordinator.sol"; +import {IStakeRegistry} from "./interfaces/IStakeRegistry.sol"; /** * @title Storage variables for the `StakeRegistry` contract. diff --git a/src/interfaces/IBLSApkRegistry.sol b/src/interfaces/IBLSApkRegistry.sol index 62a3e6b1..bec2ac7f 100644 --- a/src/interfaces/IBLSApkRegistry.sol +++ b/src/interfaces/IBLSApkRegistry.sol @@ -1,9 +1,9 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity =0.8.12; -import {IRegistry} from "src/interfaces/IRegistry.sol"; +import {IRegistry} from "./IRegistry.sol"; -import {BN254} from "src/libraries/BN254.sol"; +import {BN254} from "../libraries/BN254.sol"; /** * @title Minimal interface for a registry that keeps track of aggregate operator public keys for among many quorums. diff --git a/src/interfaces/IBLSSignatureChecker.sol b/src/interfaces/IBLSSignatureChecker.sol index ac63096c..18f3626e 100644 --- a/src/interfaces/IBLSSignatureChecker.sol +++ b/src/interfaces/IBLSSignatureChecker.sol @@ -1,11 +1,11 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity =0.8.12; -import {IRegistryCoordinator} from "src/interfaces/IRegistryCoordinator.sol"; -import {IBLSApkRegistry} from "src/interfaces/IBLSApkRegistry.sol"; -import {IStakeRegistry, IDelegationManager} from "src/interfaces/IStakeRegistry.sol"; +import {IRegistryCoordinator} from "./IRegistryCoordinator.sol"; +import {IBLSApkRegistry} from "./IBLSApkRegistry.sol"; +import {IStakeRegistry, IDelegationManager} from "./IStakeRegistry.sol"; -import {BN254} from "src/libraries/BN254.sol"; +import {BN254} from "../libraries/BN254.sol"; /** * @title Used for checking BLS aggregate signatures from the operators of a EigenLayer AVS with the RegistryCoordinator/BLSApkRegistry/StakeRegistry architechture. diff --git a/src/interfaces/IRegistryCoordinator.sol b/src/interfaces/IRegistryCoordinator.sol index 04976a10..b0530568 100644 --- a/src/interfaces/IRegistryCoordinator.sol +++ b/src/interfaces/IRegistryCoordinator.sol @@ -1,10 +1,10 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity =0.8.12; -import {IBLSApkRegistry} from "src/interfaces/IBLSApkRegistry.sol"; -import {IStakeRegistry} from "src/interfaces/IStakeRegistry.sol"; -import {IIndexRegistry} from "src/interfaces/IIndexRegistry.sol"; -import {BN254} from "src/libraries/BN254.sol"; +import {IBLSApkRegistry} from "./IBLSApkRegistry.sol"; +import {IStakeRegistry} from "./IStakeRegistry.sol"; +import {IIndexRegistry} from "./IIndexRegistry.sol"; +import {BN254} from "../libraries/BN254.sol"; /** * @title Interface for a contract that coordinates between various registries for an AVS. diff --git a/test/ffi/BLSPubKeyCompendiumFFI.t.sol b/test/ffi/BLSPubKeyCompendiumFFI.t.sol index 3bb75118..431e7b86 100644 --- a/test/ffi/BLSPubKeyCompendiumFFI.t.sol +++ b/test/ffi/BLSPubKeyCompendiumFFI.t.sol @@ -1,9 +1,9 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity =0.8.12; -import "src/BLSApkRegistry.sol"; -import "test/ffi/util/G2Operations.sol"; -import {IBLSApkRegistry} from "src/interfaces/IBLSApkRegistry.sol"; +import "../../src/BLSApkRegistry.sol"; +import "../ffi/util/G2Operations.sol"; +import {IBLSApkRegistry} from "../../src/interfaces/IBLSApkRegistry.sol"; contract BLSApkRegistryFFITests is G2Operations { using BN254 for BN254.G1Point; diff --git a/test/ffi/BLSSignatureCheckerFFI.t.sol b/test/ffi/BLSSignatureCheckerFFI.t.sol index 6161cdc1..153311f0 100644 --- a/test/ffi/BLSSignatureCheckerFFI.t.sol +++ b/test/ffi/BLSSignatureCheckerFFI.t.sol @@ -1,12 +1,12 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity =0.8.12; -import {G2Operations} from "test/ffi/util/G2Operations.sol"; -import {MockAVSDeployer} from "test/utils/MockAVSDeployer.sol"; -import {BLSSignatureChecker} from "src/BLSSignatureChecker.sol"; -import {OperatorStateRetriever} from "src/OperatorStateRetriever.sol"; -import {BN254} from "src/libraries/BN254.sol"; -import {BitmapUtils} from "src/libraries/BitmapUtils.sol"; +import {G2Operations} from "../ffi/util/G2Operations.sol"; +import {MockAVSDeployer} from "../utils/MockAVSDeployer.sol"; +import {BLSSignatureChecker} from "../../src/BLSSignatureChecker.sol"; +import {OperatorStateRetriever} from "../../src/OperatorStateRetriever.sol"; +import {BN254} from "../../src/libraries/BN254.sol"; +import {BitmapUtils} from "../../src/libraries/BitmapUtils.sol"; contract BLSSignatureCheckerFFITests is MockAVSDeployer, G2Operations { diff --git a/test/harnesses/BLSApkRegistryHarness.sol b/test/harnesses/BLSApkRegistryHarness.sol index a888986e..6cd6ed4e 100644 --- a/test/harnesses/BLSApkRegistryHarness.sol +++ b/test/harnesses/BLSApkRegistryHarness.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity =0.8.12; -import "src/BLSApkRegistry.sol"; +import "../../src/BLSApkRegistry.sol"; // wrapper around the BLSApkRegistry contract that exposes internal functionality, for unit testing _other functionality_. contract BLSApkRegistryHarness is BLSApkRegistry { diff --git a/test/harnesses/RegistryCoordinatorHarness.t.sol b/test/harnesses/RegistryCoordinatorHarness.t.sol index 0429b50f..70bd306b 100644 --- a/test/harnesses/RegistryCoordinatorHarness.t.sol +++ b/test/harnesses/RegistryCoordinatorHarness.t.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity =0.8.12; -import "src/RegistryCoordinator.sol"; +import "../../src/RegistryCoordinator.sol"; import "forge-std/Test.sol"; diff --git a/test/harnesses/StakeRegistryHarness.sol b/test/harnesses/StakeRegistryHarness.sol index 96f529ba..b24b54ff 100644 --- a/test/harnesses/StakeRegistryHarness.sol +++ b/test/harnesses/StakeRegistryHarness.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity =0.8.12; -import "src/StakeRegistry.sol"; +import "../../src/StakeRegistry.sol"; // wrapper around the StakeRegistry contract that exposes the internal functions for unit testing. contract StakeRegistryHarness is StakeRegistry { diff --git a/test/mocks/RegistryCoordinatorMock.sol b/test/mocks/RegistryCoordinatorMock.sol index fb68a38f..938427af 100644 --- a/test/mocks/RegistryCoordinatorMock.sol +++ b/test/mocks/RegistryCoordinatorMock.sol @@ -2,7 +2,7 @@ pragma solidity =0.8.12; -import "src/interfaces/IRegistryCoordinator.sol"; +import "../../src/interfaces/IRegistryCoordinator.sol"; contract RegistryCoordinatorMock is IRegistryCoordinator { diff --git a/test/unit/StakeRegistryUnit.t.sol b/test/unit/StakeRegistryUnit.t.sol index 4add656b..0560efed 100644 --- a/test/unit/StakeRegistryUnit.t.sol +++ b/test/unit/StakeRegistryUnit.t.sol @@ -8,21 +8,21 @@ import {Slasher} from "eigenlayer-contracts/src/contracts/core/Slasher.sol"; import {PauserRegistry} from "eigenlayer-contracts/src/contracts/permissions/PauserRegistry.sol"; import {ISlasher} from "eigenlayer-contracts/src/contracts/interfaces/ISlasher.sol"; import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; -import {IStakeRegistry} from "src/interfaces/IStakeRegistry.sol"; -import {IIndexRegistry} from "src/interfaces/IIndexRegistry.sol"; -import {IRegistryCoordinator} from "src/interfaces/IRegistryCoordinator.sol"; -import {IBLSApkRegistry} from "src/interfaces/IBLSApkRegistry.sol"; -import {IServiceManager} from "src/interfaces/IServiceManager.sol"; +import {IStakeRegistry} from "../../src/interfaces/IStakeRegistry.sol"; +import {IIndexRegistry} from "../../src/interfaces/IIndexRegistry.sol"; +import {IRegistryCoordinator} from "../../src/interfaces/IRegistryCoordinator.sol"; +import {IBLSApkRegistry} from "../../src/interfaces/IBLSApkRegistry.sol"; +import {IServiceManager} from "../../src/interfaces/IServiceManager.sol"; -import {BitmapUtils} from "src/libraries/BitmapUtils.sol"; +import {BitmapUtils} from "../../src/libraries/BitmapUtils.sol"; import {StrategyManagerMock} from "eigenlayer-contracts/src/test/mocks/StrategyManagerMock.sol"; import {EigenPodManagerMock} from "eigenlayer-contracts/src/test/mocks/EigenPodManagerMock.sol"; import {DelegationManagerMock} from "eigenlayer-contracts/src/test/mocks/DelegationManagerMock.sol"; -import {StakeRegistryHarness} from "test/harnesses/StakeRegistryHarness.sol"; -import {StakeRegistry} from "src/StakeRegistry.sol"; -import {RegistryCoordinatorHarness} from "test/harnesses/RegistryCoordinatorHarness.t.sol"; +import {StakeRegistryHarness} from "../harnesses/StakeRegistryHarness.sol"; +import {StakeRegistry} from "../../src/StakeRegistry.sol"; +import {RegistryCoordinatorHarness} from "../harnesses/RegistryCoordinatorHarness.t.sol"; import "forge-std/Test.sol"; diff --git a/test/unit/VoteWeigherBaseUnit.t.sol b/test/unit/VoteWeigherBaseUnit.t.sol index 5bff2245..3c41364b 100644 --- a/test/unit/VoteWeigherBaseUnit.t.sol +++ b/test/unit/VoteWeigherBaseUnit.t.sol @@ -10,7 +10,7 @@ import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy import {IEigenPodManager} from "eigenlayer-contracts/src/contracts/interfaces/IEigenPodManager.sol"; import {ISlasher} from "eigenlayer-contracts/src/contracts/interfaces/ISlasher.sol"; import {IServiceManager} from "../../src/interfaces/IServiceManager.sol"; -import {StakeRegistry} from "../../src/StakeRegistry.sol"; +import {StakeRegistry, IStakeRegistry} from "../../src/StakeRegistry.sol"; import {RegistryCoordinatorMock} from "../mocks/RegistryCoordinatorMock.sol"; diff --git a/test/utils/BLSMockAVSDeployer.sol b/test/utils/BLSMockAVSDeployer.sol index ab0122e2..a0231e1d 100644 --- a/test/utils/BLSMockAVSDeployer.sol +++ b/test/utils/BLSMockAVSDeployer.sol @@ -1,11 +1,11 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity =0.8.12; -import {BLSSignatureChecker} from "src/BLSSignatureChecker.sol"; -import {MockAVSDeployer} from "test/utils/MockAVSDeployer.sol"; -import {BN254} from "src/libraries/BN254.sol"; -import {OperatorStateRetriever} from "src/OperatorStateRetriever.sol"; -import {BitmapUtils} from "src/libraries/BitmapUtils.sol"; +import {BLSSignatureChecker} from "../../src/BLSSignatureChecker.sol"; +import {MockAVSDeployer} from "../utils/MockAVSDeployer.sol"; +import {BN254} from "../../src/libraries/BN254.sol"; +import {OperatorStateRetriever} from "../../src/OperatorStateRetriever.sol"; +import {BitmapUtils} from "../../src/libraries/BitmapUtils.sol"; contract BLSMockAVSDeployer is MockAVSDeployer { using BN254 for BN254.G1Point; diff --git a/test/utils/MockAVSDeployer.sol b/test/utils/MockAVSDeployer.sol index 85eea82e..236fed40 100644 --- a/test/utils/MockAVSDeployer.sol +++ b/test/utils/MockAVSDeployer.sol @@ -9,30 +9,30 @@ import {ISlasher} from "eigenlayer-contracts/src/contracts/interfaces/ISlasher.s import {PauserRegistry} from "eigenlayer-contracts/src/contracts/permissions/PauserRegistry.sol"; import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol"; -import {BitmapUtils} from "src/libraries/BitmapUtils.sol"; -import {BN254} from "src/libraries/BN254.sol"; - -import {OperatorStateRetriever} from "src/OperatorStateRetriever.sol"; -import {RegistryCoordinator} from "src/RegistryCoordinator.sol"; -import {RegistryCoordinatorHarness} from "test/harnesses/RegistryCoordinatorHarness.t.sol"; -import {BLSApkRegistry} from "src/BLSApkRegistry.sol"; -import {ServiceManagerBase} from "src/ServiceManagerBase.sol"; -import {StakeRegistry} from "src/StakeRegistry.sol"; -import {IndexRegistry} from "src/IndexRegistry.sol"; -import {IBLSApkRegistry} from "src/interfaces/IBLSApkRegistry.sol"; -import {IStakeRegistry} from "src/interfaces/IStakeRegistry.sol"; -import {IIndexRegistry} from "src/interfaces/IIndexRegistry.sol"; -import {IRegistryCoordinator} from "src/interfaces/IRegistryCoordinator.sol"; -import {IServiceManager} from "src/interfaces/IServiceManager.sol"; +import {BitmapUtils} from "../../src/libraries/BitmapUtils.sol"; +import {BN254} from "../../src/libraries/BN254.sol"; + +import {OperatorStateRetriever} from "../../src/OperatorStateRetriever.sol"; +import {RegistryCoordinator} from "../../src/RegistryCoordinator.sol"; +import {RegistryCoordinatorHarness} from "../harnesses/RegistryCoordinatorHarness.t.sol"; +import {BLSApkRegistry} from "../../src/BLSApkRegistry.sol"; +import {ServiceManagerBase} from "../../src/ServiceManagerBase.sol"; +import {StakeRegistry} from "../../src/StakeRegistry.sol"; +import {IndexRegistry} from "../../src/IndexRegistry.sol"; +import {IBLSApkRegistry} from "../../src/interfaces/IBLSApkRegistry.sol"; +import {IStakeRegistry} from "../../src/interfaces/IStakeRegistry.sol"; +import {IIndexRegistry} from "../../src/interfaces/IIndexRegistry.sol"; +import {IRegistryCoordinator} from "../../src/interfaces/IRegistryCoordinator.sol"; +import {IServiceManager} from "../../src/interfaces/IServiceManager.sol"; import {StrategyManagerMock} from "eigenlayer-contracts/src/test/mocks/StrategyManagerMock.sol"; import {EigenPodManagerMock} from "eigenlayer-contracts/src/test/mocks/EigenPodManagerMock.sol"; -import {DelegationMock} from "test/mocks/DelegationMock.sol"; -import {BLSApkRegistryHarness} from "test/harnesses/BLSApkRegistryHarness.sol"; +import {DelegationMock} from "../mocks/DelegationMock.sol"; +import {BLSApkRegistryHarness} from "../harnesses/BLSApkRegistryHarness.sol"; import {EmptyContract} from "eigenlayer-contracts/src/test/mocks/EmptyContract.sol"; -import {StakeRegistryHarness} from "test/harnesses/StakeRegistryHarness.sol"; +import {StakeRegistryHarness} from "../harnesses/StakeRegistryHarness.sol"; import "forge-std/Test.sol"; From 7cfa95adbabaa7a5daca00cb59a7c818f5e34dc3 Mon Sep 17 00:00:00 2001 From: steven Date: Mon, 18 Dec 2023 16:15:43 -0500 Subject: [PATCH 098/101] chore: remove whitespace diff --- script/AVSContractsDeploy.s.sol | 1 - 1 file changed, 1 deletion(-) diff --git a/script/AVSContractsDeploy.s.sol b/script/AVSContractsDeploy.s.sol index d16c3b2b..10802c75 100644 --- a/script/AVSContractsDeploy.s.sol +++ b/script/AVSContractsDeploy.s.sol @@ -17,7 +17,6 @@ import "eigenlayer-contracts/src/contracts/strategies/StrategyBaseTVLLimits.sol" import "eigenlayer-contracts/src/contracts/pods/EigenPod.sol"; import "eigenlayer-contracts/src/contracts/pods/EigenPodManager.sol"; import "eigenlayer-contracts/src/contracts/pods/DelayedWithdrawalRouter.sol"; - import "eigenlayer-contracts/src/contracts/permissions/PauserRegistry.sol"; import "eigenlayer-contracts/src/test/mocks/EmptyContract.sol"; import "eigenlayer-contracts/src/test/mocks/ERC20Mock.sol"; From 0839f944661dd39121a546f5bc3547f5ead92f8c Mon Sep 17 00:00:00 2001 From: steven Date: Mon, 18 Dec 2023 16:27:29 -0500 Subject: [PATCH 099/101] chore: clean up remappings --- remappings.txt | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/remappings.txt b/remappings.txt index a7cd70c1..8c001fd0 100644 --- a/remappings.txt +++ b/remappings.txt @@ -1,7 +1,5 @@ -@openzeppelin-upgrades/=lib/eigenlayer-contracts/lib/openzeppelin-contracts-upgradeable/ -@openzeppelin/=lib/eigenlayer-contracts/lib/openzeppelin-contracts/ +@openzeppelin-upgrades/=lib/openzeppelin-contracts-upgradeable/ +@openzeppelin/=lib/openzeppelin-contracts/ ds-test/=lib/ds-test/src/ eigenlayer-contracts/=lib/eigenlayer-contracts/ forge-std/=lib/forge-std/src/ -openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/ -openzeppelin-contracts/=lib/openzeppelin-contracts/ From 3d61d230b0ca108a41000ccb4680e2557d1e5e15 Mon Sep 17 00:00:00 2001 From: steven Date: Mon, 18 Dec 2023 16:30:23 -0500 Subject: [PATCH 100/101] chore: remove whitespace diff --- script/AVSContractsDeploy.s.sol | 2 ++ 1 file changed, 2 insertions(+) diff --git a/script/AVSContractsDeploy.s.sol b/script/AVSContractsDeploy.s.sol index 10802c75..8ec6c200 100644 --- a/script/AVSContractsDeploy.s.sol +++ b/script/AVSContractsDeploy.s.sol @@ -17,7 +17,9 @@ import "eigenlayer-contracts/src/contracts/strategies/StrategyBaseTVLLimits.sol" import "eigenlayer-contracts/src/contracts/pods/EigenPod.sol"; import "eigenlayer-contracts/src/contracts/pods/EigenPodManager.sol"; import "eigenlayer-contracts/src/contracts/pods/DelayedWithdrawalRouter.sol"; + import "eigenlayer-contracts/src/contracts/permissions/PauserRegistry.sol"; + import "eigenlayer-contracts/src/test/mocks/EmptyContract.sol"; import "eigenlayer-contracts/src/test/mocks/ERC20Mock.sol"; From 399919877d3237dcf7d7cae24265c84c314a6a8e Mon Sep 17 00:00:00 2001 From: steven Date: Mon, 18 Dec 2023 16:37:46 -0500 Subject: [PATCH 101/101] chore: stragglers --- test/integration/CoreRegistration.t.sol | 4 ++-- test/unit/BLSApkRegistryUnit.t.sol | 4 ++-- test/unit/RegistryCoordinatorUnit.t.sol | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/test/integration/CoreRegistration.t.sol b/test/integration/CoreRegistration.t.sol index 88254c10..66a9e31b 100644 --- a/test/integration/CoreRegistration.t.sol +++ b/test/integration/CoreRegistration.t.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity =0.8.12; -import "test/utils/MockAVSDeployer.sol"; +import "../utils/MockAVSDeployer.sol"; import { DelegationManager } from "eigenlayer-contracts/src/contracts/core/DelegationManager.sol"; import { IDelegationManager } from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; @@ -185,4 +185,4 @@ contract Test_CoreRegistration is MockAVSDeployer { return operatorSignature; } -} \ No newline at end of file +} diff --git a/test/unit/BLSApkRegistryUnit.t.sol b/test/unit/BLSApkRegistryUnit.t.sol index 3a055f13..6a4e0a2f 100644 --- a/test/unit/BLSApkRegistryUnit.t.sol +++ b/test/unit/BLSApkRegistryUnit.t.sol @@ -3,8 +3,8 @@ pragma solidity =0.8.12; import "forge-std/Test.sol"; -import "test/harnesses/BLSApkRegistryHarness.sol"; -import "test/mocks/RegistryCoordinatorMock.sol"; +import "../harnesses/BLSApkRegistryHarness.sol"; +import "../mocks/RegistryCoordinatorMock.sol"; contract BLSApkRegistryUnitTests is Test { diff --git a/test/unit/RegistryCoordinatorUnit.t.sol b/test/unit/RegistryCoordinatorUnit.t.sol index c2fc0057..0ab7584b 100644 --- a/test/unit/RegistryCoordinatorUnit.t.sol +++ b/test/unit/RegistryCoordinatorUnit.t.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity =0.8.12; -import "test/utils/MockAVSDeployer.sol"; +import "../utils/MockAVSDeployer.sol"; contract RegistryCoordinatorUnit is MockAVSDeployer { using BN254 for BN254.G1Point;