From 11b006137db429fed661da5ef01ac2a1927823b6 Mon Sep 17 00:00:00 2001 From: steven <12021290+stevennevins@users.noreply.github.com> Date: Mon, 27 Jan 2025 19:25:31 -0500 Subject: [PATCH] feat: slashing release (#311) * Update LICENSE (#285) * chore: upgrade core to target operator set release * chore: update mock contracts with latest interfaces (#293) * chore: update mock contracts with latest interfaces * chore: bump to latest operator set release commits * chore: fixes for depedency bump * feat: operator set migration-1-migration (#286) * chore: checkout migration branch * feat: implement migration function * chore: update to release branch * feat: operator set creation for each quorum number * feat: migration with merge sorted array of operators and their quorums * feat: operator set migration working * chore: revert change from testing * chore: revert change from testing * chore: remove extra logging and commented out asserts * chore: remove unused file * fix: remove console logs * refactor: to view functions * chore: nit and remove unneeded function * fix: remove duplication of looping for all the operators * chore: remove comment * feat: allow migrating in two transactions * feat: finalization of migration * chore: use string errors and fix migration issues * chore: rename * feat: use library for merge sort * test: fuzz view function * feat: operator set migration-2-create quorum (#287) * chore: checkout migration branch * feat: implement migration function * chore: update to release branch * feat: operator set creation for each quorum number * feat: migration with merge sorted array of operators and their quorums * feat: operator set migration working * chore: revert change from testing * chore: revert change from testing * chore: remove extra logging and commented out asserts * chore: remove unused file * fix: remove console logs * feat: create quorum post operator set migration * test(wip): create quorum test adds new operator set * test: migration create quorum * refactor: to view functions * chore: nit and remove unneeded function * fix: remove duplication of looping for all the operators * chore: remove comment * feat: allow migrating in two transactions * feat: finalization of migration * chore: use string errors and fix migration issues * chore: rename * feat: use library for merge sort * test: fuzz view function * fix: updates from merge * chore: use interface * chore: bump operator set release dependency in core * chore: updates from dependency bump * feat: add natspec * docs: add natspec * feat: add checks operator was registered for quorums when migrating * fix: resolve code size issue in RegistryCoordinator * chore: update to latest core (#299) * chore: update to latest eigenlayer-contracts feat/operator-set-release * chore: add method to mock * feat(op sets): update stakes when forceUnregister (#300) * feat: update stakes handle direct deregistration on AVSDirectory * test: update stake for quorum if operator directly unregistered from the AVSDirectory * chore: simplify setup * chore: simplify setup * chore: make service manager immutable on stakeRegistry * feat(op sets): register and deregister (#301) * feat: register and deregister to operator sets * fix: bytecode wrangling * chore: shorter errors to reduce bytecode * test: register after migration * chore: remove stale todos * chore: add back commented line * chore: remain consistent with m2 events * fix: side effect of merge from _deregister function * fix: bytecode massaging * chore: rename for clarity * chore: remove comments and whitespace * docs: add natspec to the library * feat(op sets): upgrade and migrate script (#303) * feat: upgrade and test pre prod upgrade and migration * fix: add param introduced in merge * chore: bump slashing core dependency (#312) * chore: bump to slashing branch * chore: bump compiler version * fix: dep interface changes * fix: compiler errors from interface changes and type changes * fix: compiler errors * chore: bump dependencies * chore: bump core dependency and resolve issues * chore: bump core dependency and fix compiler errors * feat: integrate AllocationManager * feat: add a slashing permission to the service manager * chore: remove unneeded casting * feat: implement a slasher permission and forward call to AllocationManager * feat: add simiple slasher starting point * chore: bump slashing magnitudes * chore: bump core slashing-magnitudes branch * feat: slasher templates / examples (#310) * chore: bump to slashing branch * chore: bump compiler version * fix: dep interface changes * fix: compiler errors from interface changes and type changes * fix: compiler errors * chore: bump dependencies * chore: bump core dependency and resolve issues * chore: bump core dependency and fix compiler errors * feat: integrate AllocationManager * feat: add a slashing permission to the service manager * chore: remove unneeded casting * feat: implement a slasher permission and forward call to AllocationManager * feat: add simiple slasher starting point * feat: slashers * chore: change around slashed event * fix: call dm * feat: add proposal mechanism for updating slasher * fix: set to completed instead of delete * chore: use struct instead of params directly * chore: clean up params more * chore: simplify and organize files * chore: cleanup logic and couple event with internal func * fix: pass correct params * chore: organize and add interface * chore: nits * chore: cleanup more nits * fix: storage gap * chore: nits refactor * chore: go back to fulfill being onlySlasher * test: fixes from core updates * fix: use delegated stake per operator set instead of per AVS * fix: update to 14 days * feat: configurable lookahead and stake type * chore: remove unused test util contracts (#319) * feat: remove both option * chore: remove unused test util contracts * chore: remove diff * feat: remove both option * fix: storage gap remove one slot (#320) * feat: track total slashable stake and total delegated stake per quorum (#317) * feat: remove both option * feat: total delegated stake and total slashable stake per quorum config * test: resolve some breaking changes to tests * chore: move stake type to file level definition * chore: refactor loop * test: add unit test for slashble stake quorum init * test: assert on state and event * test: delegated stake quorum and assertions * fix: use libraries with only internal vis (#324) * fix: revert making library function vis external * fix: signature checker internal * feat: avs registrar registration flow changes (#318) * feat: remove both option * feat: total delegated stake and total slashable stake per quorum config * test: resolve some breaking changes to tests * chore: bump core dependency * chore: bump dependency * chore: bump to latest slashing mags * fix: creation of registry coordinator * test: wip * feat: integrate registrar interfaces * test: add function to delegation mock to set operator status * test: additional test case * chore: bumping core dep and adding UAM (#325) * test: use permission controller mock * chore: label fuzz tests * test: various fixes from config changes * chore: remove comment * test: fix permission controlled functions * test: fix config issue in integration tests * test: fix avs directory initialize * feat: wip prevent m2 registration flows after migration * feat: registration changes part 2 * chore: add note * fix: remove handling of forceDeregistration * fix: fix total delegated stake usage * fix: integration tests * test: fix remaining integration tests * test: add back log check * test: add additional tests for transition to operator sets * test: add more test cases * feat: record m2 quorums on migration * chore: add note about churn support * fix: prevent operator set registration changes for m2 quorums * feat: require strings * chore: add dev note and add require string * chore: bump dependency for slashing mags updates (#329) * fix: internal slashing security review (#332) * docs: match file and library name, and add docstrings * docs: update error strings to match function * style: forge fmt + moving variables around + fixing error strings * fix: enforce max quorum count for EjectionManager * style: more error string fixes + formatting * fix: correctly set StakeRegistryStorage gap * style: more error string corrections + typos * fix: revert cases * chore: bump core dependency to pull in latest updates * fix: slashing review fixes (#333) * fix: withdrawal delay check * fix: add operator set strategies (#334) * fix: remove registerOperatorToOperatorSet interface function * fix: update interface functions with alm interface * fix: operator set strategies in ALM * chore: remove todo * fix: add strategies by stake registry * fix: params for deregister * chore: remove old migration functions * chore: check quorum exists before setting params * fix: deregister flow for operator set quorums * test: fix test for DM withdrawal delay blocks * chore: update all pragmas to 0.8.27 (#336) * feat: custom require errors for registry coordinator (#337) * feat: custom require errors for registry coordinator * ci: update the ci to use the correct compiler settings * chore: bump core dependency * chore: bump forge-std * docs: update readme with note on slashing (#341) * chore: bump core fix iface changes (#352) * chore: bump dep * fix: iface changes and param names * feat: ci storage reports (#347) * feat: storage reports * fix: storage report * fix: storage report * fix: ci * fix: ci * fix: ci * fix: ci * fix: ci * fix: prevent calling enable operator sets twice and use internal function for checkALM * chore: address review comments * refactor: custom errors in middleware (#355) * refactor: custom errors * fix: unit tests * chore: review remove setStakeType * fix: wire up stake registry to call add and rm strategies for op set * refactor: slashing UAM (#357) * refactor: uam * feat: add uam interfaces * fix: lookahead period to blocks * fix: tests * chore: storage report * feat: ci forge coverage (#349) * feat: ci forge coverage * fix: ci * feat: add forge fmt to ci (#363) * feat: ci forge fmt * chore: forge fmt * feat: add register with churn type (#360) * feat: add register with churn type * fix: tests for encoded registration type * feat: add ejector support for operator sets * refactor: handle operator set dereg * fix: dereg on churn from ALM if needed * refactor: slashing registry coordinator (#361) * refactor: slashing registry coordinator refactor: registry coordinator refactor: remove sm calls fix: tests wip * fix: integration tests * refactor: internal functions * fix: remove unused block * fix: comment out test for now * build: fmt check run only on src contracts * fix: call SM only based on m2 quorums (#369) * feat: update enableOperatorSets flow (#367) * feat: update enableOperatorSets flow * fix: dereg op check * chore: renamed m2 quorums * fix: msg.sender -> operator * chore: forge fmt * fix: tests --------- Co-authored-by: Yash Patil <40046473+ypatil12@users.noreply.github.com> * fix: commented out view (#373) * refactor: natspec + interface changes (#364) * refactor: natspec + interfaces refactor: natspec + interfaces refactor: natspec + interfaces docs: `RegistryCoordinator` natspec chore: forge fmt refactor: named mapping params docs: natspec `BLSApkRegistry` docs: natspec `BLSApkRegistry` docs: natspec `BLSApkRegistry` refactor: interface structure refactor: separate `BLSSignatureChecker` storage refactor: rename -> `IECDSAStakeRegistry` docs: natspec docs: natspec `ECDSAStakeRegistry` docs: natspec `EjectionManager` + separate storage docs: natspec docs: natspec `IndexRegistry` refactor: remove `IRegistry` chore: forge fmt chore: make storage-report docs: natspec refactor: remove `ISocketUpdater` refactor: use refactor: remove todo refactor: `///` -> `/**` refactor: improve comment refactor: rename var refactor: improve comment * fix: compiling - tests failing * refactor: test passing **mostly** * refactor: add missing gap * refactor: natspec * fix: test - state changes moved out of internal * chore: forge fmt * refactor: reorganize errors * refactor: remove override * refactor: note avsd * Yash/natspec - address comments (#372) * feat: natpsec with inheritance * feat: storage * chore: format * chore: fmt * chore: format * fix: test * fix: test --------- Co-authored-by: Michael Sun Co-authored-by: Yash Patil <40046473+ypatil12@users.noreply.github.com> * chore: forge fmt * feat: storage gap on socket registry * chore: remove unused storage; update bindings * chore: nuke storage report script for now on ci --------- Co-authored-by: Gautham Anant <32277907+gpsanant@users.noreply.github.com> Co-authored-by: Madhur Shrimal Co-authored-by: Nadir Akhtar Co-authored-by: clandestine.eth <96172957+0xClandestine@users.noreply.github.com> Co-authored-by: Michael Sun <35479365+8sunyuan@users.noreply.github.com> Co-authored-by: Yash Patil <40046473+ypatil12@users.noreply.github.com> Co-authored-by: Michael Sun --- .github/workflows/forge-coverage.yml | 72 + .github/workflows/forge-fmt.yml | 30 + .github/workflows/tests.yml | 3 - Makefile | 2 + README.md | 18 +- bin/storage-report.sh | 45 + docs/RegistryCoordinator.md | 6 +- docs/storage-report/AVSRegistrar.md | 6 + docs/storage-report/BLSApkRegistry.md | 0 docs/storage-report/BLSApkRegistryStorage.md | 0 docs/storage-report/BLSSignatureChecker.md | 0 .../BLSSignatureCheckerStorage.md | 0 .../storage-report/ECDSAServiceManagerBase.md | 0 docs/storage-report/ECDSAStakeRegistry.md | 0 .../ECDSAStakeRegistryEqualWeight.md | 0 .../ECDSAStakeRegistryPermissioned.md | 0 .../ECDSAStakeRegistryStorage.md | 0 docs/storage-report/EjectionManager.md | 0 docs/storage-report/EjectionManagerStorage.md | 0 docs/storage-report/IndexRegistry.md | 0 docs/storage-report/IndexRegistryStorage.md | 0 docs/storage-report/InstantSlasher.md | 0 docs/storage-report/OperatorStateRetriever.md | 0 docs/storage-report/RegistryCoordinator.md | 0 .../RegistryCoordinatorStorage.md | 33 + docs/storage-report/ServiceManagerBase.md | 0 .../ServiceManagerBaseStorage.md | 19 + docs/storage-report/ServiceManagerRouter.md | 0 docs/storage-report/SlasherBase.md | 0 docs/storage-report/SlasherStorage.md | 0 .../SlashingRegistryCoordinator.md | 0 .../SlashingRegistryCoordinatorStorage.md | 0 docs/storage-report/SocketRegistry.md | 0 docs/storage-report/SocketRegistryStorage.md | 0 docs/storage-report/StakeRegistry.md | 0 docs/storage-report/StakeRegistryStorage.md | 0 docs/storage-report/VetoableSlasher.md | 0 foundry.toml | 138 +- lib/eigenlayer-contracts | 2 +- lib/forge-std | 2 +- script/ServiceManagerRouterDeploy.s.sol | 2 +- script/utils/OperatorSetUpgradeLib.sol | 56 + .../utils/testdata/17000/core_testdata.json | 23 + .../testdata/17000/middlware_testdata.json | 18 + src/BLSApkRegistry.sol | 258 +- src/BLSApkRegistryStorage.sol | 50 +- src/BLSSignatureChecker.sol | 231 +- src/BLSSignatureCheckerStorage.sol | 38 + src/EjectionManager.sol | 173 +- src/EjectionManagerStorage.sol | 34 + src/IndexRegistry.sol | 283 +- src/IndexRegistryStorage.sol | 13 +- src/OperatorStateRetriever.sol | 118 +- src/RegistryCoordinator.sol | 953 +------ src/ServiceManagerBase.sol | 182 +- src/ServiceManagerBaseStorage.sol | 27 +- src/ServiceManagerRouter.sol | 33 +- src/SlashingRegistryCoordinator.sol | 1142 ++++++++ ...=> SlashingRegistryCoordinatorStorage.sol} | 72 +- src/SocketRegistry.sol | 36 +- src/SocketRegistryStorage.sol | 22 + src/StakeRegistry.sol | 594 ++-- src/StakeRegistryStorage.sol | 49 +- src/interfaces/IBLSApkRegistry.sol | 334 ++- src/interfaces/IBLSSignatureChecker.sol | 192 +- src/interfaces/IECDSAStakeRegistry.sol | 305 +++ .../IECDSAStakeRegistryEventsAndErrors.sol | 111 - src/interfaces/IEjectionManager.sol | 167 +- src/interfaces/IIndexRegistry.sol | 184 +- src/interfaces/IRegistry.sol | 13 - src/interfaces/IRegistryCoordinator.sol | 230 +- src/interfaces/IServiceManager.sol | 132 +- src/interfaces/IServiceManagerUI.sol | 58 +- src/interfaces/ISlasher.sol | 92 + .../ISlashingRegistryCoordinator.sol | 606 +++++ src/interfaces/ISocketRegistry.sol | 4 +- src/interfaces/IStakeRegistry.sol | 495 +++- src/libraries/BN254.sol | 106 +- src/libraries/BitmapUtils.sol | 55 +- src/libraries/LibMergeSort.sol | 67 + src/libraries/QuorumBitmapHistoryLib.sol | 154 ++ src/libraries/SignatureCheckerLib.sol | 30 + src/slashers/InstantSlasher.sol | 29 + src/slashers/VetoableSlasher.sol | 92 + src/slashers/base/SlasherBase.sol | 53 + src/slashers/base/SlasherStorage.sol | 38 + src/unaudited/ECDSAServiceManagerBase.sol | 144 +- src/unaudited/ECDSAStakeRegistry.sol | 541 ++-- src/unaudited/ECDSAStakeRegistryStorage.sol | 30 +- .../ECDSAStakeRegistryEqualWeight.sol | 20 +- .../ECDSAStakeRegistryPermissioned.sol | 35 +- test/events/IBLSApkRegistryEvents.sol | 18 +- test/events/IIndexRegistryEvents.sol | 6 +- test/events/IServiceManagerBaseEvents.sol | 83 +- test/events/IStakeRegistryEvents.sol | 12 +- test/ffi/BLSPubKeyCompendiumFFI.t.sol | 43 +- test/ffi/BLSSignatureCheckerFFI.t.sol | 111 +- test/ffi/UpdateOperators.t.sol | 1057 +++++++- test/ffi/util/G2Operations.sol | 9 +- test/harnesses/AVSDirectoryHarness.sol | 12 + test/harnesses/BLSApkRegistryHarness.sol | 8 +- test/harnesses/BitmapUtilsWrapper.sol | 22 +- .../RegistryCoordinatorHarness.t.sol | 45 +- test/harnesses/StakeRegistryHarness.sol | 17 +- test/integration/CoreRegistration.t.sol | 108 +- test/integration/IntegrationBase.t.sol | 470 ++-- test/integration/IntegrationChecks.t.sol | 509 ++-- test/integration/IntegrationConfig.t.sol | 298 +- test/integration/IntegrationDeployer.t.sol | 287 +- test/integration/TimeMachine.t.sol | 15 +- test/integration/User.t.sol | 173 +- .../mocks/BeaconChainOracleMock.t.sol | 23 + .../tests/Full_Register_Deregister.t.sol | 27 +- ...ll_Register_CoreBalanceChange_Update.t.sol | 37 +- .../tests/NonFull_Register_Deregister.t.sol | 17 +- test/integration/utils/BitmapStrings.t.sol | 20 +- test/integration/utils/Sort.t.sol | 6 +- test/mocks/AVSDirectoryMock.sol | 144 +- test/mocks/AVSRegistrarMock.sol | 17 + test/mocks/AllocationManagerMock.sol | 154 ++ test/mocks/DelegationMock.sol | 397 +-- test/mocks/ECDSAServiceManagerMock.sol | 36 +- test/mocks/ECDSAStakeRegistryMock.sol | 8 +- test/mocks/EigenPodManagerMock.sol | 112 + test/mocks/PermissionControllerMock.sol | 88 + test/mocks/RegistryCoordinatorMock.sol | 219 +- test/mocks/RewardsCoordinatorMock.sol | 177 +- test/mocks/ServiceManagerMock.sol | 17 +- test/mocks/StakeRegistryMock.sol | 148 +- test/unit/AVSRegistrar.t.sol | 120 + test/unit/BLSApkRegistryUnit.t.sol | 520 +--- test/unit/BLSSignatureCheckerUnit.t.sol | 458 ++-- test/unit/BitmapUtils.t.sol | 111 +- test/unit/ECDSAServiceManager.t.sol | 378 +-- .../ECDSAStakeRegistryEqualWeightUnit.t.sol | 87 +- .../ECDSAStakeRegistryPermissionedUnit.t.sol | 66 +- test/unit/ECDSAStakeRegistryUnit.t.sol | 356 +-- test/unit/EjectionManagerUnit.t.sol | 310 ++- test/unit/IndexRegistryUnit.t.sol | 290 +- test/unit/LibMergeSort.t.sol | 187 ++ test/unit/OperatorStateRetrieverUnit.t.sol | 74 +- test/unit/RegistryCoordinatorUnit.t.sol | 1973 ++++++++++---- test/unit/ServiceManagerBase.t.sol | 757 +----- test/unit/ServiceManagerRouter.t.sol | 15 +- .../SlashingRegistryCoordinatorUnit.t.sol | 2414 +++++++++++++++++ test/unit/SocketRegistryUnit.t.sol | 10 +- test/unit/StakeRegistryUnit.t.sol | 318 ++- test/unit/UpgradeableProxyLib.sol | 52 + test/unit/Utils.sol | 21 +- test/utils/BLSMockAVSDeployer.sol | 107 +- test/utils/CoreDeployLib.sol | 271 ++ test/utils/Greeter.sol | 11 - test/utils/GreeterProxiable.sol | 65 - test/utils/GreeterV2.sol | 14 - test/utils/GreeterV2Proxiable.sol | 17 - test/utils/MockAVSDeployer.sol | 226 +- test/utils/NoInitializer.sol | 13 - test/utils/Operators.sol | 52 +- test/utils/Owners.sol | 25 +- test/utils/ProofParsing.sol | 135 +- test/utils/ProxyTestContracts.sol | 9 - test/utils/SignatureCompaction.sol | 11 +- test/utils/UpgradeableProxyUtils.sol | 241 -- test/utils/UpgradeableProxyUtils.t.sol | 87 - test/utils/WithConstructor.sol | 19 - 165 files changed, 15997 insertions(+), 7873 deletions(-) create mode 100644 .github/workflows/forge-coverage.yml create mode 100644 .github/workflows/forge-fmt.yml create mode 100644 Makefile create mode 100644 bin/storage-report.sh create mode 100644 docs/storage-report/AVSRegistrar.md create mode 100644 docs/storage-report/BLSApkRegistry.md create mode 100644 docs/storage-report/BLSApkRegistryStorage.md create mode 100644 docs/storage-report/BLSSignatureChecker.md create mode 100644 docs/storage-report/BLSSignatureCheckerStorage.md create mode 100644 docs/storage-report/ECDSAServiceManagerBase.md create mode 100644 docs/storage-report/ECDSAStakeRegistry.md create mode 100644 docs/storage-report/ECDSAStakeRegistryEqualWeight.md create mode 100644 docs/storage-report/ECDSAStakeRegistryPermissioned.md create mode 100644 docs/storage-report/ECDSAStakeRegistryStorage.md create mode 100644 docs/storage-report/EjectionManager.md create mode 100644 docs/storage-report/EjectionManagerStorage.md create mode 100644 docs/storage-report/IndexRegistry.md create mode 100644 docs/storage-report/IndexRegistryStorage.md create mode 100644 docs/storage-report/InstantSlasher.md create mode 100644 docs/storage-report/OperatorStateRetriever.md create mode 100644 docs/storage-report/RegistryCoordinator.md create mode 100644 docs/storage-report/RegistryCoordinatorStorage.md create mode 100644 docs/storage-report/ServiceManagerBase.md create mode 100644 docs/storage-report/ServiceManagerBaseStorage.md create mode 100644 docs/storage-report/ServiceManagerRouter.md create mode 100644 docs/storage-report/SlasherBase.md create mode 100644 docs/storage-report/SlasherStorage.md create mode 100644 docs/storage-report/SlashingRegistryCoordinator.md create mode 100644 docs/storage-report/SlashingRegistryCoordinatorStorage.md create mode 100644 docs/storage-report/SocketRegistry.md create mode 100644 docs/storage-report/SocketRegistryStorage.md create mode 100644 docs/storage-report/StakeRegistry.md create mode 100644 docs/storage-report/StakeRegistryStorage.md create mode 100644 docs/storage-report/VetoableSlasher.md create mode 100644 script/utils/OperatorSetUpgradeLib.sol create mode 100644 script/utils/testdata/17000/core_testdata.json create mode 100644 script/utils/testdata/17000/middlware_testdata.json create mode 100644 src/BLSSignatureCheckerStorage.sol create mode 100644 src/EjectionManagerStorage.sol create mode 100644 src/SlashingRegistryCoordinator.sol rename src/{RegistryCoordinatorStorage.sol => SlashingRegistryCoordinatorStorage.sol} (61%) create mode 100644 src/SocketRegistryStorage.sol create mode 100644 src/interfaces/IECDSAStakeRegistry.sol delete mode 100644 src/interfaces/IECDSAStakeRegistryEventsAndErrors.sol delete mode 100644 src/interfaces/IRegistry.sol create mode 100644 src/interfaces/ISlasher.sol create mode 100644 src/interfaces/ISlashingRegistryCoordinator.sol create mode 100644 src/libraries/LibMergeSort.sol create mode 100644 src/libraries/QuorumBitmapHistoryLib.sol create mode 100644 src/libraries/SignatureCheckerLib.sol create mode 100644 src/slashers/InstantSlasher.sol create mode 100644 src/slashers/VetoableSlasher.sol create mode 100644 src/slashers/base/SlasherBase.sol create mode 100644 src/slashers/base/SlasherStorage.sol create mode 100644 test/harnesses/AVSDirectoryHarness.sol create mode 100644 test/integration/mocks/BeaconChainOracleMock.t.sol create mode 100644 test/mocks/AVSRegistrarMock.sol create mode 100644 test/mocks/AllocationManagerMock.sol create mode 100644 test/mocks/EigenPodManagerMock.sol create mode 100644 test/mocks/PermissionControllerMock.sol create mode 100644 test/unit/AVSRegistrar.t.sol create mode 100644 test/unit/LibMergeSort.t.sol create mode 100644 test/unit/SlashingRegistryCoordinatorUnit.t.sol create mode 100644 test/unit/UpgradeableProxyLib.sol create mode 100644 test/utils/CoreDeployLib.sol delete mode 100644 test/utils/Greeter.sol delete mode 100644 test/utils/GreeterProxiable.sol delete mode 100644 test/utils/GreeterV2.sol delete mode 100644 test/utils/GreeterV2Proxiable.sol delete mode 100644 test/utils/NoInitializer.sol delete mode 100644 test/utils/ProxyTestContracts.sol delete mode 100644 test/utils/UpgradeableProxyUtils.sol delete mode 100644 test/utils/UpgradeableProxyUtils.t.sol delete mode 100644 test/utils/WithConstructor.sol diff --git a/.github/workflows/forge-coverage.yml b/.github/workflows/forge-coverage.yml new file mode 100644 index 00000000..436a0c7a --- /dev/null +++ b/.github/workflows/forge-coverage.yml @@ -0,0 +1,72 @@ +name: Coverage + +on: + push: + workflow_dispatch: {} + +jobs: + run-coverage: + name: CI + runs-on: ubuntu-latest + strategy: + fail-fast: false + + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Load issue number + uses: actions/github-script@v6 + id: get_issue_number + with: + script: | + const pullRequests = await github.rest.repos.listPullRequestsAssociatedWithCommit({ + commit_sha: context.sha, + owner: context.repo.owner, + repo: context.repo.repo, + }); + + if (pullRequests.data.length > 0) { + return pullRequests.data[0].number; + } else { + throw new Error('No associated pull request found.'); + } + result-encoding: string + + - name: Install dependencies + run: | + sudo apt-get install lcov + id: lcov + + - name: Install Foundry + uses: foundry-rs/foundry-toolchain@v1 + with: + version: nightly + + - name: Run coverage + run: FOUNDRY_PROFILE=ci forge coverage --report lcov + env: + RPC_MAINNET: ${{ secrets.RPC_MAINNET }} + RPC_HOLESKY: ${{ secrets.RPC_HOLESKY }} + + - name: Prune coverage report + run: lcov --remove ./lcov.info -o ./lcov.info.pruned 'test/*' 'script/*' '*Storage.sol' --ignore-errors inconsistent + + - name: Generate reports + run: genhtml -o report ./lcov.info.pruned + + - name: Upload coverage results + uses: actions/upload-artifact@v4 + with: + name: code-coverage-report + path: report/* + + - name: View and log coverage + id: print_coverage + run: | + EOF=$(dd if=/dev/urandom bs=15 count=1 status=none | base64) + echo "comment_contents<<$EOF" >> $GITHUB_OUTPUT + echo "$(lcov --list ./lcov.info.pruned --ignore-errors inconsistent)" >> $GITHUB_OUTPUT + echo "$EOF" >> $GITHUB_OUTPUT + - name: Log Coverage Report + run: echo "${{ steps.print_coverage.outputs.comment_contents }}" \ No newline at end of file diff --git a/.github/workflows/forge-fmt.yml b/.github/workflows/forge-fmt.yml new file mode 100644 index 00000000..c70bfe53 --- /dev/null +++ b/.github/workflows/forge-fmt.yml @@ -0,0 +1,30 @@ +name: Forge Fmt + +on: + workflow_dispatch: + push: + branches: + - master + - mainnet + - testnet-goerli + - dev + pull_request: + +jobs: + check: + name: CI + strategy: + fail-fast: true + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + - name: Install Foundry + uses: foundry-rs/foundry-toolchain@v1 + with: + version: nightly + - name: Run forge fmt + run: | + forge fmt --check src + id: fmt \ No newline at end of file diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 9ce44c3e..660c8895 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -13,9 +13,6 @@ on: - dev pull_request: -env: - FOUNDRY_PROFILE: ci - jobs: check: strategy: diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..db84aea5 --- /dev/null +++ b/Makefile @@ -0,0 +1,2 @@ +storage-report: + bash ".github/bin/storage-report.sh" "docs/storage-report/" \ No newline at end of file diff --git a/README.md b/README.md index f33b6920..62b879a8 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,7 @@ EigenLayer is a set of smart contracts deployed on Ethereum that enable restakin ## Branching The main branches we use are: + * [`dev (default)`](https://github.com/Layr-Labs/eigenlayer-middleware/tree/dev): The most up-to-date branch, containing the work-in-progress code for upcoming releases * [`testnet-holesky`](https://github.com/Layr-Labs/eigenlayer-middleware/tree/testnet-holesky): Our current testnet deployment * [`mainnet`](https://github.com/Layr-Labs/eigenlayer-middleware/tree/mainnet): Our current mainnet deployment @@ -24,6 +25,7 @@ The main branches we use are: ### Basics 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) @@ -32,10 +34,25 @@ Most of this content is intro-level and describes user interactions with the Eig ### Deep Dive For shadowy super-coders: + * The most up-to-date technical documentation can be found in [/docs](/docs). * To get an idea of how users interact with these contracts, check out the integration tests: [/test/integration](./test/integration) * To explore the EigenLayer core contracts, check out the core repo technical docs [here][core-docs-dev]. +### Note On Slashing + +The middleware contracts are available for testnet integration and experimentation. As with the rest of the testnet code, these contracts have not yet been fully audited. Internal security reviews are in progress, and we're preparing to engage external auditors. We're providing these now so AVSs can begin testing slashing conditions, Operator Sets, and related functionality, and so Operators can simulate slashing and test allocations. For more details, see [ELIP-002](https://github.com/eigenfoundation/ELIPs/blob/main/ELIPs/ELIP-002.md). + +For Operators: + +We provide scripts to deploy a mock service manager and allocate some stake to a mock Operator Set in [these scripts](https://github.com/Layr-Labs/eigenlayer-middleware/pull/335). +We've reduced various safety delays on testnet to better simulate operational flows. Additional information on these delays is in [ELIP-002](https://github.com/eigenfoundation/ELIPs/blob/main/ELIPs/ELIP-002.md). +For AVSs: + +We provide a migration path for existing M2 environments (LINK). While this requires understanding the provided script, the Developer Experience will improve before Mainnet launch. Note that this is a gradual migration process to new Operator Sets, not a direct upgrade of existing Operator Sets. +We also provide scripts to spin up new service managers from scratch. +In all cases, you’ll need to register existing Operators or spin up new Operators and allocate funds to them. These testing flows will improve as we refine the scripts and contracts. Stay tuned for updates, and feel free to reach out with any questions. + ## Building and Running Tests 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: @@ -80,4 +97,3 @@ The current testnet deployment is on holesky, is from our M2 beta release. You c [`ServiceManagerRouter`](https://github.com/Layr-Labs/eigenlayer-middleware/blob/testnet-holesky/src/ServiceManagerRouter.sol) | - | [`0x4463...5a37`](https://holesky.etherscan.io/address/0x44632dfBdCb6D3E21EF613B0ca8A6A0c618F5a37#code) | | [`ProxyAdmin`](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v4.7.1/contracts/proxy/transparent/ProxyAdmin.sol) | - | [`0xB043...5c15`](https://holesky.etherscan.io/address/0xB043055dd967A382577c2f5261fA6428f2905c15) | | [`eigenda/EigenDAServiceManager`](https://github.com/Layr-Labs/eigenda/blob/a33b41561cc3fb4cd6d50a8738e4c5dca43ec0a5/contracts/src/core/EigenDAServiceManager.sol) | [`0xD4A7E1Bd8015057293f0D0A557088c286942e84b`](https://holesky.etherscan.io/address/0xD4A7E1Bd8015057293f0D0A557088c286942e84b) | [`0xa722...67f3`](https://holesky.etherscan.io/address/0xa7227485e6C693AC4566fe168C5E3647c5c267f3) | Proxy: [`TUP@4.7.1`](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v4.7.1/contracts/proxy/transparent/TransparentUpgradeableProxy.sol) | - diff --git a/bin/storage-report.sh b/bin/storage-report.sh new file mode 100644 index 00000000..4c34d5ac --- /dev/null +++ b/bin/storage-report.sh @@ -0,0 +1,45 @@ +#!/bin/sh + +# Default output directory +OUTPUT_DIR=${1:-docs/storage-report} + +# Function to print messages +log() { + echo "$(date '+%Y-%m-%d %H:%M:%S') [INFO] $1" +} + +# Function to print error messages +error() { + echo "$(date '+%Y-%m-%d %H:%M:%S') [ERROR] $1" >&2 +} + +log "Starting the storage report generation." + +# Create the output directory if it doesn't exist +if ! mkdir -p "$OUTPUT_DIR"; then + error "Failed to create output directory: $OUTPUT_DIR" + exit 1 +fi + +log "Output directory is set to: $OUTPUT_DIR" + +# Loop through Solidity files and generate storage report +# NOTE: Ignores `src/interfaces` & `src/libraries` since they "should" not contain storage logic. +for file in $(find src/ -name "*.sol" ! -path "*/interfaces/*" ! -path "*/libraries/*"); do + contract_name=$(basename "$file" .sol) + + # Check if the file exists and is readable + if [ ! -r "$file" ]; then + error "Cannot read file: $file" + continue + fi + + log "Processing contract: $contract_name" + + # Run forge inspect and capture errors + if ! forge inspect "$contract_name" storage > "$OUTPUT_DIR/$contract_name.md"; then + error "Failed to generate storage report for contract: $contract_name" + else + log "Storage report generated for contract: $contract_name" + fi +done \ No newline at end of file diff --git a/docs/RegistryCoordinator.md b/docs/RegistryCoordinator.md index 1f917638..4752c7f7 100644 --- a/docs/RegistryCoordinator.md +++ b/docs/RegistryCoordinator.md @@ -42,7 +42,7 @@ These methods allow operators to register for/deregister from one or more quorum function registerOperator( bytes calldata quorumNumbers, string calldata socket, - IBLSApkRegistry.PubkeyRegistrationParams calldata params, + IBLSApkRegistryTypes.PubkeyRegistrationParams calldata params, SignatureWithSaltAndExpiry memory operatorSignature ) external @@ -88,7 +88,7 @@ If the Operator was not currently registered for any quorums, this method will r function registerOperatorWithChurn( bytes calldata quorumNumbers, string calldata socket, - IBLSApkRegistry.PubkeyRegistrationParams calldata params, + IBLSApkRegistryTypes.PubkeyRegistrationParams calldata params, OperatorKickParam[] calldata operatorKickParams, SignatureWithSaltAndExpiry memory churnApproverSignature, SignatureWithSaltAndExpiry memory operatorSignature @@ -256,7 +256,7 @@ These methods are used by the Owner to configure the `RegistryCoordinator`: function createQuorum( OperatorSetParam memory operatorSetParams, uint96 minimumStake, - IStakeRegistry.StrategyParams[] memory strategyParams + IStakeRegistryTypes.StrategyParams[] memory strategyParams ) external virtual diff --git a/docs/storage-report/AVSRegistrar.md b/docs/storage-report/AVSRegistrar.md new file mode 100644 index 00000000..1ec5dc07 --- /dev/null +++ b/docs/storage-report/AVSRegistrar.md @@ -0,0 +1,6 @@ + +╭------+------+------+--------+-------+----------╮ +| Name | Type | Slot | Offset | Bytes | Contract | ++================================================+ +╰------+------+------+--------+-------+----------╯ + diff --git a/docs/storage-report/BLSApkRegistry.md b/docs/storage-report/BLSApkRegistry.md new file mode 100644 index 00000000..e69de29b diff --git a/docs/storage-report/BLSApkRegistryStorage.md b/docs/storage-report/BLSApkRegistryStorage.md new file mode 100644 index 00000000..e69de29b diff --git a/docs/storage-report/BLSSignatureChecker.md b/docs/storage-report/BLSSignatureChecker.md new file mode 100644 index 00000000..e69de29b diff --git a/docs/storage-report/BLSSignatureCheckerStorage.md b/docs/storage-report/BLSSignatureCheckerStorage.md new file mode 100644 index 00000000..e69de29b diff --git a/docs/storage-report/ECDSAServiceManagerBase.md b/docs/storage-report/ECDSAServiceManagerBase.md new file mode 100644 index 00000000..e69de29b diff --git a/docs/storage-report/ECDSAStakeRegistry.md b/docs/storage-report/ECDSAStakeRegistry.md new file mode 100644 index 00000000..e69de29b diff --git a/docs/storage-report/ECDSAStakeRegistryEqualWeight.md b/docs/storage-report/ECDSAStakeRegistryEqualWeight.md new file mode 100644 index 00000000..e69de29b diff --git a/docs/storage-report/ECDSAStakeRegistryPermissioned.md b/docs/storage-report/ECDSAStakeRegistryPermissioned.md new file mode 100644 index 00000000..e69de29b diff --git a/docs/storage-report/ECDSAStakeRegistryStorage.md b/docs/storage-report/ECDSAStakeRegistryStorage.md new file mode 100644 index 00000000..e69de29b diff --git a/docs/storage-report/EjectionManager.md b/docs/storage-report/EjectionManager.md new file mode 100644 index 00000000..e69de29b diff --git a/docs/storage-report/EjectionManagerStorage.md b/docs/storage-report/EjectionManagerStorage.md new file mode 100644 index 00000000..e69de29b diff --git a/docs/storage-report/IndexRegistry.md b/docs/storage-report/IndexRegistry.md new file mode 100644 index 00000000..e69de29b diff --git a/docs/storage-report/IndexRegistryStorage.md b/docs/storage-report/IndexRegistryStorage.md new file mode 100644 index 00000000..e69de29b diff --git a/docs/storage-report/InstantSlasher.md b/docs/storage-report/InstantSlasher.md new file mode 100644 index 00000000..e69de29b diff --git a/docs/storage-report/OperatorStateRetriever.md b/docs/storage-report/OperatorStateRetriever.md new file mode 100644 index 00000000..e69de29b diff --git a/docs/storage-report/RegistryCoordinator.md b/docs/storage-report/RegistryCoordinator.md new file mode 100644 index 00000000..e69de29b diff --git a/docs/storage-report/RegistryCoordinatorStorage.md b/docs/storage-report/RegistryCoordinatorStorage.md new file mode 100644 index 00000000..e28515ac --- /dev/null +++ b/docs/storage-report/RegistryCoordinatorStorage.md @@ -0,0 +1,33 @@ + +╭-------------------------+---------------------------------------------------------------------------+------+--------+-------+---------------------------------------------------------------╮ +| Name | Type | Slot | Offset | Bytes | Contract | ++=============================================================================================================================================================================================+ +| quorumCount | uint8 | 0 | 0 | 1 | src/RegistryCoordinatorStorage.sol:RegistryCoordinatorStorage | +|-------------------------+---------------------------------------------------------------------------+------+--------+-------+---------------------------------------------------------------| +| _quorumParams | mapping(uint8 => struct ISlashingRegistryCoordinatorTypes.OperatorSetParam) | 1 | 0 | 32 | src/RegistryCoordinatorStorage.sol:RegistryCoordinatorStorage | +|-------------------------+---------------------------------------------------------------------------+------+--------+-------+---------------------------------------------------------------| +| _operatorBitmapHistory | mapping(bytes32 => struct ISlashingRegistryCoordinatorTypes.QuorumBitmapUpdate[]) | 2 | 0 | 32 | src/RegistryCoordinatorStorage.sol:RegistryCoordinatorStorage | +|-------------------------+---------------------------------------------------------------------------+------+--------+-------+---------------------------------------------------------------| +| _operatorInfo | mapping(address => struct ISlashingRegistryCoordinatorTypes.OperatorInfo) | 3 | 0 | 32 | src/RegistryCoordinatorStorage.sol:RegistryCoordinatorStorage | +|-------------------------+---------------------------------------------------------------------------+------+--------+-------+---------------------------------------------------------------| +| isChurnApproverSaltUsed | mapping(bytes32 => bool) | 4 | 0 | 32 | src/RegistryCoordinatorStorage.sol:RegistryCoordinatorStorage | +|-------------------------+---------------------------------------------------------------------------+------+--------+-------+---------------------------------------------------------------| +| quorumUpdateBlockNumber | mapping(uint8 => uint256) | 5 | 0 | 32 | src/RegistryCoordinatorStorage.sol:RegistryCoordinatorStorage | +|-------------------------+---------------------------------------------------------------------------+------+--------+-------+---------------------------------------------------------------| +| registries | address[] | 6 | 0 | 32 | src/RegistryCoordinatorStorage.sol:RegistryCoordinatorStorage | +|-------------------------+---------------------------------------------------------------------------+------+--------+-------+---------------------------------------------------------------| +| churnApprover | address | 7 | 0 | 20 | src/RegistryCoordinatorStorage.sol:RegistryCoordinatorStorage | +|-------------------------+---------------------------------------------------------------------------+------+--------+-------+---------------------------------------------------------------| +| ejector | address | 8 | 0 | 20 | src/RegistryCoordinatorStorage.sol:RegistryCoordinatorStorage | +|-------------------------+---------------------------------------------------------------------------+------+--------+-------+---------------------------------------------------------------| +| lastEjectionTimestamp | mapping(address => uint256) | 9 | 0 | 32 | src/RegistryCoordinatorStorage.sol:RegistryCoordinatorStorage | +|-------------------------+---------------------------------------------------------------------------+------+--------+-------+---------------------------------------------------------------| +| ejectionCooldown | uint256 | 10 | 0 | 32 | src/RegistryCoordinatorStorage.sol:RegistryCoordinatorStorage | +|-------------------------+---------------------------------------------------------------------------+------+--------+-------+---------------------------------------------------------------| +| isOperatorSetAVS | bool | 11 | 0 | 1 | src/RegistryCoordinatorStorage.sol:RegistryCoordinatorStorage | +|-------------------------+---------------------------------------------------------------------------+------+--------+-------+---------------------------------------------------------------| +| isM2Quorum | mapping(uint8 => bool) | 12 | 0 | 32 | src/RegistryCoordinatorStorage.sol:RegistryCoordinatorStorage | +|-------------------------+---------------------------------------------------------------------------+------+--------+-------+---------------------------------------------------------------| +| __GAP | uint256[37] | 13 | 0 | 1184 | src/RegistryCoordinatorStorage.sol:RegistryCoordinatorStorage | +╰-------------------------+---------------------------------------------------------------------------+------+--------+-------+---------------------------------------------------------------╯ + diff --git a/docs/storage-report/ServiceManagerBase.md b/docs/storage-report/ServiceManagerBase.md new file mode 100644 index 00000000..e69de29b diff --git a/docs/storage-report/ServiceManagerBaseStorage.md b/docs/storage-report/ServiceManagerBaseStorage.md new file mode 100644 index 00000000..16e40d2d --- /dev/null +++ b/docs/storage-report/ServiceManagerBaseStorage.md @@ -0,0 +1,19 @@ + +╭------------------+-------------+------+--------+-------+-------------------------------------------------------------╮ +| Name | Type | Slot | Offset | Bytes | Contract | ++======================================================================================================================+ +| _initialized | uint8 | 0 | 0 | 1 | src/ServiceManagerBaseStorage.sol:ServiceManagerBaseStorage | +|------------------+-------------+------+--------+-------+-------------------------------------------------------------| +| _initializing | bool | 0 | 1 | 1 | src/ServiceManagerBaseStorage.sol:ServiceManagerBaseStorage | +|------------------+-------------+------+--------+-------+-------------------------------------------------------------| +| __gap | uint256[50] | 1 | 0 | 1600 | src/ServiceManagerBaseStorage.sol:ServiceManagerBaseStorage | +|------------------+-------------+------+--------+-------+-------------------------------------------------------------| +| _owner | address | 51 | 0 | 20 | src/ServiceManagerBaseStorage.sol:ServiceManagerBaseStorage | +|------------------+-------------+------+--------+-------+-------------------------------------------------------------| +| __gap | uint256[49] | 52 | 0 | 1568 | src/ServiceManagerBaseStorage.sol:ServiceManagerBaseStorage | +|------------------+-------------+------+--------+-------+-------------------------------------------------------------| +| rewardsInitiator | address | 101 | 0 | 20 | src/ServiceManagerBaseStorage.sol:ServiceManagerBaseStorage | +|------------------+-------------+------+--------+-------+-------------------------------------------------------------| +| __GAP | uint256[49] | 102 | 0 | 1568 | src/ServiceManagerBaseStorage.sol:ServiceManagerBaseStorage | +╰------------------+-------------+------+--------+-------+-------------------------------------------------------------╯ + diff --git a/docs/storage-report/ServiceManagerRouter.md b/docs/storage-report/ServiceManagerRouter.md new file mode 100644 index 00000000..e69de29b diff --git a/docs/storage-report/SlasherBase.md b/docs/storage-report/SlasherBase.md new file mode 100644 index 00000000..e69de29b diff --git a/docs/storage-report/SlasherStorage.md b/docs/storage-report/SlasherStorage.md new file mode 100644 index 00000000..e69de29b diff --git a/docs/storage-report/SlashingRegistryCoordinator.md b/docs/storage-report/SlashingRegistryCoordinator.md new file mode 100644 index 00000000..e69de29b diff --git a/docs/storage-report/SlashingRegistryCoordinatorStorage.md b/docs/storage-report/SlashingRegistryCoordinatorStorage.md new file mode 100644 index 00000000..e69de29b diff --git a/docs/storage-report/SocketRegistry.md b/docs/storage-report/SocketRegistry.md new file mode 100644 index 00000000..e69de29b diff --git a/docs/storage-report/SocketRegistryStorage.md b/docs/storage-report/SocketRegistryStorage.md new file mode 100644 index 00000000..e69de29b diff --git a/docs/storage-report/StakeRegistry.md b/docs/storage-report/StakeRegistry.md new file mode 100644 index 00000000..e69de29b diff --git a/docs/storage-report/StakeRegistryStorage.md b/docs/storage-report/StakeRegistryStorage.md new file mode 100644 index 00000000..e69de29b diff --git a/docs/storage-report/VetoableSlasher.md b/docs/storage-report/VetoableSlasher.md new file mode 100644 index 00000000..e69de29b diff --git a/foundry.toml b/foundry.toml index 4e13d2e8..ce96ecd1 100644 --- a/foundry.toml +++ b/foundry.toml @@ -1,29 +1,111 @@ [profile.default] -src = "src" -out = "out" -libs = ["lib"] -fs_permissions = [{ access = "read-write", path = "./" }] -gas_limit = 5000000000 - -ffi = true -no-match-contract = "FFI" - -# Enables or disables the optimizer -optimizer = true -# The number of optimizer runs -optimizer_runs = 200 -# Whether or not to use the Yul intermediate representation compilation pipeline -via_ir = false -# Override the Solidity version (this overrides `auto_detect_solc`) -solc_version = '0.8.12' - -[fmt] -bracket_spacing = false -int_types = "long" -line_length = 100 -multiline_func_header = "params_first" -number_underscore = "thousands" -quote_style = "double" -tab_width = 4 - -# See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options + # Project Configuration + + # Path to contract sources relative to the root of the project. + src = "src" + # Path to the test contract sources relative to the root of the project. + test = "test" + # Path to the script contract sources relative to the root of the project. + script = "script" + # Path to store contract artifacts relative to the root of the project. + out = "out" + # Array of paths that contain libraries, relative to the root of the project. + libs = ["lib"] + + # Solidity Compiler Configuration + + # Defines paths for Solidity imports. + remappings = [ + "@openzeppelin/=lib/openzeppelin-contracts-v4.9.0/", + "@openzeppelin-upgrades/=lib/openzeppelin-contracts-upgradeable-v4.9.0/", + "ds-test/=lib/ds-test/src/", + "forge-std/=lib/forge-std/src/" + ] + # Specifies the exact version of Solidity to use, overriding auto-detection. + solc_version = '0.8.27' + # If enabled, treats Solidity compiler warnings as errors, preventing artifact generation if warnings are present. + deny_warnings = false + # If set to true, changes compilation pipeline to go through the new IR optimizer. + via_ir = false + # Whether or not to enable the Solidity optimizer. + optimizer = true + # The number of runs specifies roughly how often each opcode of the deployed code will be executed + # across the life-time of the contract. This means it is a trade-off parameter between code size (deploy cost) + # and code execution cost (cost after deployment). + optimizer_runs = 200 + + # Test Configuration + + # Verbosity level during test execution. Higher levels provide more detailed information: + # - 2 (-vv): Logs emitted during tests are displayed. + # - 3 (-vvv): Stack traces for failing tests are displayed. + # - 4 (-vvvv): Stack traces for all tests and setup traces for failing tests are displayed. + # - 5 (-vvvvv): Stack and setup traces are always displayed. + verbosity = 0 + # Enables the Foreign Function Interface (FFI) cheatcode. + # WARNING: This allows arbitrary programs to run on your computer, which poses security risks. + ffi = true + # Contracts to include in gas reports. By default, all contracts are included. + gas_reports = ["./src/**/*"] + # Show test execution progress if set to true. + show_progress = true + # Sparse mode only compiles files that match certain criteria. + sparse_mode = true + + gas_limit = 5000000000 + no-match-contract = "FFI" + fs_permissions = [{ access = "read-write", path = "./" }] + +[profile.default.fmt] + # Single-line vs multi-line statement blocks + single_line_statement_blocks = "preserve" # Options: "single", "multi", "preserve" + # Formatting style for long function headers + multiline_func_header = "params_first" # Options: "attributes_first", "params_first", "all" + # Sort import statements alphabetically + sort_imports = false + # Maximum line length where formatter will wrap the line + line_length = 100 # Default: 120 + # Number of spaces per indentation level + tab_width = 4 # Default: 4 + # Whether to print spaces between brackets + bracket_spacing = false + # Style of uint/int256 types + int_types = "long" # Options: "long", "short", "preserve" + # Quotation mark style + quote_style = "double" # Options: "double", "single", "preserve" + # Style of underscores in number literals + number_underscore = "remove" # Options: "preserve", "thousands", "remove" + # Whether or not to wrap comments at line_length + wrap_comments = false + # List of files to ignore during formatting (can use glob patterns) + # ignore = [ + # "./script/**/*", + # "./test/**/*" + # ] + +# TODO: Decide if we want to enable this. +# [profile.test.fmt] +# int_types = "short" +# line_length = 140 +# ignore = [ +# "./src/**/*" +# ] + +[profile.ci.fuzz] + optimizer = false + runs = 32 + +[profile.intense.fuzz] + optimizer = false + runs = 15000 + +[profile.forktest.fuzz] + runs = 16 + +[rpc_endpoints] + mainnet = "${RPC_MAINNET}" + holesky = "${RPC_HOLESKY}" + +[etherscan] + mainnet = { key = "${ETHERSCAN_API_KEY}" } + holesky = { key = "${ETHERSCAN_API_KEY}" } \ No newline at end of file diff --git a/lib/eigenlayer-contracts b/lib/eigenlayer-contracts index ac57bc1b..22de8094 160000 --- a/lib/eigenlayer-contracts +++ b/lib/eigenlayer-contracts @@ -1 +1 @@ -Subproject commit ac57bc1b28c83d9d7143c0da19167c148c3596a3 +Subproject commit 22de809403924707ccce6998e62b868bfae0fc58 diff --git a/lib/forge-std b/lib/forge-std index bb4ceea9..1eea5bae 160000 --- a/lib/forge-std +++ b/lib/forge-std @@ -1 +1 @@ -Subproject commit bb4ceea94d6f10eeb5b41dc2391c6c8bf8e734ef +Subproject commit 1eea5bae12ae557d589f9f0f0edae2faa47cb262 diff --git a/script/ServiceManagerRouterDeploy.s.sol b/script/ServiceManagerRouterDeploy.s.sol index 6a61a796..d87315b3 100644 --- a/script/ServiceManagerRouterDeploy.s.sol +++ b/script/ServiceManagerRouterDeploy.s.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {ServiceManagerRouter} from "../src/ServiceManagerRouter.sol"; import "forge-std/Script.sol"; diff --git a/script/utils/OperatorSetUpgradeLib.sol b/script/utils/OperatorSetUpgradeLib.sol new file mode 100644 index 00000000..fe25839d --- /dev/null +++ b/script/utils/OperatorSetUpgradeLib.sol @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; +// Deploy L2AVS proxy + +import {TransparentUpgradeableProxy} from + "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +import {ProxyAdmin} from "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; + +import {Vm} from "forge-std/Vm.sol"; +import {stdJson} from "forge-std/StdJson.sol"; + +library OperatorSetUpgradeLib { + using stdJson for string; + + // address(uint160(uint256(keccak256("hevm cheat code")))) == 0x7109709ECfa91a80626fF3989D68f67F5b1DD12D + // solhint-disable-next-line const-name-snakecase + Vm private constant vm = Vm(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D); + + /** + * @dev Storage slot with the address of the current implementation. + * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1. + */ + bytes32 internal constant IMPLEMENTATION_SLOT = + 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; + + /** + * @dev Storage slot with the admin of the contract. + * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1. + */ + bytes32 internal constant ADMIN_SLOT = + 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103; + + function upgrade(address proxy, address implementation, bytes memory data) internal { + ProxyAdmin admin = ProxyAdmin(getAdmin(proxy)); + admin.upgradeAndCall(TransparentUpgradeableProxy(payable(proxy)), implementation, data); + } + + function upgrade(address proxy, address implementation) internal { + ProxyAdmin admin = ProxyAdmin(getAdmin(proxy)); + admin.upgrade(TransparentUpgradeableProxy(payable(proxy)), implementation); + } + + function getAdmin( + address proxy + ) internal view returns (address) { + bytes32 value = vm.load(proxy, ADMIN_SLOT); + return address(uint160(uint256(value))); + } + + function getImplementation( + address proxy + ) internal view returns (address) { + bytes32 value = vm.load(proxy, IMPLEMENTATION_SLOT); + return address(uint160(uint256(value))); + } +} diff --git a/script/utils/testdata/17000/core_testdata.json b/script/utils/testdata/17000/core_testdata.json new file mode 100644 index 00000000..2ebdc8fb --- /dev/null +++ b/script/utils/testdata/17000/core_testdata.json @@ -0,0 +1,23 @@ +{ + "addresses":{ + "avsDirectory": "0x141d6995556135D4997b2ff72EB443Be300353bC", + "avsDirectoryImplementation": "0x357978adC03375BD6a3605DE055fABb84695d79A", + "baseStrategyImplementation": "0x62450517EfA1CE60d79801daf8f95973865e8D40", + "beaconOracle": "0x4C116BB629bff7A8373c2378bBd919f8349B8f25", + "delayedWithdrawalRouter": "0xC4BC46a87A67a531eCF7f74338E1FA79533334Fa", + "delayedWithdrawalRouterImplementation": "0x0011FA2c512063C495f77296Af8d195F33A8Dd38", + "delegationManager": "0x75dfE5B44C2E530568001400D3f704bC8AE350CC", + "delegationManagerImplementation": "0x56E88cb4f0136fC27D95499dE4BE2acf47946Fa1", + "eigenLayerPauserReg": "0x9Ab2FEAf0465f0eD51Fc2b663eF228B418c9Dad1", + "eigenLayerProxyAdmin": "0x1BEF05C7303d44e0E2FCD2A19d993eDEd4c51b5B", + "eigenPodBeacon": "0x92Cc4a800A1513E85C481dDDf3A06C6921211eaC", + "eigenPodImplementation": "0x2D6c7f9862BD80Cf0d9d93FC6b513D69E7Db7869", + "eigenPodManager": "0xB8d8952f572e67B11e43bC21250967772fa883Ff", + "eigenPodManagerImplementation": "0xc5B857A92245f64e9D90cCc5b096Db82eB77eB5c", + "emptyContract": "0x9690d52B1Ce155DB2ec5eCbF5a262ccCc7B3A6D2", + "rewardsCoordinator": "0xb22Ef643e1E067c994019A4C19e403253C05c2B0", + "rewardsCoordinatorImplementation": "0x76d4D84c90a2AFf213F7D859d2a288685A1a2Ede", + "slasher": "0x12699471dF8dca329C76D72823B1b79d55709384", + "slasherImplementation": "0x9460fCe11E1e0365419fa860599903B4E5097cf0" + } +} \ No newline at end of file diff --git a/script/utils/testdata/17000/middlware_testdata.json b/script/utils/testdata/17000/middlware_testdata.json new file mode 100644 index 00000000..6c25eeee --- /dev/null +++ b/script/utils/testdata/17000/middlware_testdata.json @@ -0,0 +1,18 @@ +{ + "addresses":{ + "blsApkRegistry": "0xAd7f9e558170a149Ca8E90f41Ab2444A5d3bd6aD", + "blsApkRegistryImplementation": "0x482a96D5879e32347d8df125f038D7eC8Ab358dd", + "eigenDAProxyAdmin": "0x9Fd7E279f5bD692Dc04792151E14Ad814FC60eC1", + "eigenDAServiceManager": "0x54A03db2784E3D0aCC08344D05385d0b62d4F432", + "eigenDAServiceManagerImplementation": "0xEB11a0f320E39d3371Fec4Bf5C76944DfBA8ee10", + "indexRegistry": "0x8cE5F2a53cBd29710eb94A04e40C07A4DdF15d10", + "indexRegistryImplementation": "0x1D4d6054BD11A5711ad7c5d3E376C987a603e17C", + "mockRollup": "0x0433646AdCeE95fbF89b3BFDb8157e75c19b6C2e", + "operatorStateRetriever": "0x17cA8C41a59466710443143b2ECF08CaA35d80ad", + "registryCoordinator": "0x2c61EA360D6500b58E7f481541A36B443Bc858c6", + "registryCoordinatorImplementation": "0x6f21A84E7f185cCBA248B436e3b583E609d1dE1D", + "serviceManagerRouter": "0xDb028E067fe81e9f406C2DE382Ba82e9cD7cBD03", + "stakeRegistry": "0x53668EBf2e28180e38B122c641BC51Ca81088871", + "stakeRegistryImplementation": "0x854dc9e5d011B060bf77B1a492302C349f2f00b5" + } +} \ No newline at end of file diff --git a/src/BLSApkRegistry.sol b/src/BLSApkRegistry.sol index 2bad724b..831210ea 100644 --- a/src/BLSApkRegistry.sol +++ b/src/BLSApkRegistry.sol @@ -1,9 +1,9 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; -import {BLSApkRegistryStorage} from "./BLSApkRegistryStorage.sol"; +import {BLSApkRegistryStorage, IBLSApkRegistry} from "./BLSApkRegistryStorage.sol"; -import {IRegistryCoordinator} from "./interfaces/IRegistryCoordinator.sol"; +import {ISlashingRegistryCoordinator} from "./interfaces/ISlashingRegistryCoordinator.sol"; import {BN254} from "./libraries/BN254.sol"; @@ -18,30 +18,22 @@ contract BLSApkRegistry is BLSApkRegistryStorage { /// @notice Sets the (immutable) `registryCoordinator` address constructor( - IRegistryCoordinator _registryCoordinator - ) BLSApkRegistryStorage(_registryCoordinator) {} - - /******************************************************************************* - EXTERNAL FUNCTIONS - REGISTRY COORDINATOR - *******************************************************************************/ + ISlashingRegistryCoordinator _slashingRegistryCoordinator + ) BLSApkRegistryStorage(_slashingRegistryCoordinator) {} /** - * @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 + * + * EXTERNAL FUNCTIONS - REGISTRY COORDINATOR + * */ + + /// @inheritdoc IBLSApkRegistry function registerOperator( address operator, bytes memory quorumNumbers ) public virtual onlyRegistryCoordinator { // Get the operator's pubkey. Reverts if they have not registered a key - (BN254.G1Point memory pubkey, ) = getRegisteredPubkey(operator); + (BN254.G1Point memory pubkey,) = getRegisteredPubkey(operator); // Update each quorum's aggregate pubkey _processQuorumApkUpdate(quorumNumbers, pubkey); @@ -50,87 +42,71 @@ contract BLSApkRegistry is BLSApkRegistryStorage { emit OperatorAddedToQuorums(operator, getOperatorId(operator), quorumNumbers); } - /** - * @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 - */ + /// @inheritdoc IBLSApkRegistry function deregisterOperator( address operator, bytes memory quorumNumbers ) public virtual onlyRegistryCoordinator { // Get the operator's pubkey. Reverts if they have not registered a key - (BN254.G1Point memory pubkey, ) = getRegisteredPubkey(operator); + (BN254.G1Point memory pubkey,) = getRegisteredPubkey(operator); // Update each quorum's aggregate pubkey _processQuorumApkUpdate(quorumNumbers, pubkey.negate()); emit OperatorRemovedFromQuorums(operator, getOperatorId(operator), quorumNumbers); } - /** - * @notice Initializes a new quorum by pushing its first apk update - * @param quorumNumber The number of the new quorum - */ - function initializeQuorum(uint8 quorumNumber) public virtual onlyRegistryCoordinator { - require(apkHistory[quorumNumber].length == 0, "BLSApkRegistry.initializeQuorum: quorum already exists"); - - apkHistory[quorumNumber].push(ApkUpdate({ - apkHash: bytes24(0), - updateBlockNumber: uint32(block.number), - nextUpdateBlockNumber: 0 - })); + /// @inheritdoc IBLSApkRegistry + function initializeQuorum( + uint8 quorumNumber + ) public virtual onlyRegistryCoordinator { + require(apkHistory[quorumNumber].length == 0, QuorumAlreadyExists()); + + apkHistory[quorumNumber].push( + ApkUpdate({ + apkHash: bytes24(0), + updateBlockNumber: uint32(block.number), + nextUpdateBlockNumber: 0 + }) + ); } - /** - * @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 - */ + /// @inheritdoc IBLSApkRegistry function registerBLSPublicKey( address operator, PubkeyRegistrationParams calldata params, BN254.G1Point calldata pubkeyRegistrationMessageHash ) external onlyRegistryCoordinator returns (bytes32 operatorId) { bytes32 pubkeyHash = BN254.hashG1Point(params.pubkeyG1); - require( - pubkeyHash != ZERO_PK_HASH, "BLSApkRegistry.registerBLSPublicKey: cannot register zero pubkey" - ); - require( - operatorToPubkeyHash[operator] == bytes32(0), - "BLSApkRegistry.registerBLSPublicKey: operator already registered pubkey" - ); - require( - pubkeyHashToOperator[pubkeyHash] == address(0), - "BLSApkRegistry.registerBLSPublicKey: public key already registered" - ); + require(pubkeyHash != ZERO_PK_HASH, ZeroPubKey()); + require(getOperatorId(operator) == bytes32(0), OperatorAlreadyRegistered()); + require(pubkeyHashToOperator[pubkeyHash] == address(0), BLSPubkeyAlreadyRegistered()); // gamma = h(sigma, P, P', H(m)) - uint256 gamma = uint256(keccak256(abi.encodePacked( - params.pubkeyRegistrationSignature.X, - params.pubkeyRegistrationSignature.Y, - params.pubkeyG1.X, - params.pubkeyG1.Y, - params.pubkeyG2.X, - params.pubkeyG2.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(), - 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"); + uint256 gamma = uint256( + keccak256( + abi.encodePacked( + params.pubkeyRegistrationSignature.X, + params.pubkeyRegistrationSignature.Y, + params.pubkeyG1.X, + params.pubkeyG1.Y, + params.pubkeyG2.X, + params.pubkeyG2.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(), + pubkeyRegistrationMessageHash.plus(BN254.generatorG1().scalar_mul(gamma)), + params.pubkeyG2 + ), + InvalidBLSSignatureOrPrivateKey() + ); operatorToPubkey[operator] = params.pubkeyG1; operatorToPubkeyHash[operator] = pubkeyHash; @@ -140,18 +116,22 @@ contract BLSApkRegistry is BLSApkRegistryStorage { return pubkeyHash; } - /******************************************************************************* - INTERNAL FUNCTIONS - *******************************************************************************/ - - function _processQuorumApkUpdate(bytes memory quorumNumbers, BN254.G1Point memory point) internal { + /** + * + * INTERNAL FUNCTIONS + * + */ + function _processQuorumApkUpdate( + bytes memory quorumNumbers, + BN254.G1Point memory point + ) internal { BN254.G1Point memory newApk; for (uint256 i = 0; i < quorumNumbers.length; i++) { // Validate quorum exists and get history length uint8 quorumNumber = uint8(quorumNumbers[i]); uint256 historyLength = apkHistory[quorumNumber].length; - require(historyLength != 0, "BLSApkRegistry._processQuorumApkUpdate: quorum does not exist"); + require(historyLength != 0, QuorumDoesNotExist()); // Update aggregate public key for this quorum newApk = currentApk[quorumNumber].plus(point); @@ -165,50 +145,53 @@ contract BLSApkRegistry is BLSApkRegistryStorage { lastUpdate.apkHash = newApkHash; } else { lastUpdate.nextUpdateBlockNumber = uint32(block.number); - apkHistory[quorumNumber].push(ApkUpdate({ - apkHash: newApkHash, - updateBlockNumber: uint32(block.number), - nextUpdateBlockNumber: 0 - })); + apkHistory[quorumNumber].push( + ApkUpdate({ + apkHash: newApkHash, + updateBlockNumber: uint32(block.number), + nextUpdateBlockNumber: 0 + }) + ); } } } - /******************************************************************************* - VIEW FUNCTIONS - *******************************************************************************/ /** - * @notice Returns the pubkey and pubkey hash of an operator - * @dev Reverts if the operator has not registered a valid pubkey + * + * VIEW FUNCTIONS + * */ - function getRegisteredPubkey(address operator) public view returns (BN254.G1Point memory, bytes32) { + + /// @inheritdoc IBLSApkRegistry + function getRegisteredPubkey( + address operator + ) public view returns (BN254.G1Point memory, bytes32) { BN254.G1Point memory pubkey = operatorToPubkey[operator]; - bytes32 pubkeyHash = operatorToPubkeyHash[operator]; + bytes32 pubkeyHash = getOperatorId(operator); + + require(pubkeyHash != bytes32(0), OperatorNotRegistered()); - require( - pubkeyHash != bytes32(0), - "BLSApkRegistry.getRegisteredPubkey: operator is not registered" - ); - return (pubkey, pubkeyHash); } - /** - * @notice Returns the indices of the quorumApks index at `blockNumber` for the provided `quorumNumbers` - * @dev Returns the current indices if `blockNumber >= block.number` - */ + /// @inheritdoc IBLSApkRegistry function getApkIndicesAtBlockNumber( bytes calldata quorumNumbers, uint256 blockNumber ) 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]); - + uint256 quorumApkUpdatesLength = apkHistory[quorumNumber].length; - if (quorumApkUpdatesLength == 0 || blockNumber < apkHistory[quorumNumber][0].updateBlockNumber) { - revert("BLSApkRegistry.getApkIndicesAtBlockNumber: blockNumber is before the first update"); + if ( + quorumApkUpdatesLength == 0 + || blockNumber < apkHistory[quorumNumber][0].updateBlockNumber + ) { + revert( + "BLSApkRegistry.getApkIndicesAtBlockNumber: blockNumber is before the first update" + ); } // Loop backward through apkHistory until we find an entry that preceeds `blockNumber` @@ -222,23 +205,22 @@ contract BLSApkRegistry is BLSApkRegistryStorage { return indices; } - /// @notice Returns the current APK for the provided `quorumNumber ` - function getApk(uint8 quorumNumber) external view returns (BN254.G1Point memory) { + /// @inheritdoc IBLSApkRegistry + 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 getApkUpdateAtIndex(uint8 quorumNumber, uint256 index) external view returns (ApkUpdate memory) { + /// @inheritdoc IBLSApkRegistry + function getApkUpdateAtIndex( + uint8 quorumNumber, + uint256 index + ) external view returns (ApkUpdate memory) { return apkHistory[quorumNumber][index]; } - /** - * @notice get hash of the apk of `quorumNumber` at `blockNumber` using the provided `index`; - * called by checkSignatures in BLSSignatureChecker.sol. - * @param quorumNumber is the quorum whose ApkHash is being retrieved - * @param blockNumber is the number of the block for which the latest ApkHash will be retrieved - * @param index is the index of the apkUpdate being retrieved from the list of quorum apkUpdates in storage - */ + /// @inheritdoc IBLSApkRegistry function getApkHashAtBlockNumberAndIndex( uint8 quorumNumber, uint32 blockNumber, @@ -251,38 +233,38 @@ contract BLSApkRegistry is BLSApkRegistryStorage { * - 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, BlockNumberTooRecent()); require( - blockNumber >= quorumApkUpdate.updateBlockNumber, - "BLSApkRegistry._validateApkHashAtBlockNumber: index too recent" - ); - require( - quorumApkUpdate.nextUpdateBlockNumber == 0 || blockNumber < quorumApkUpdate.nextUpdateBlockNumber, - "BLSApkRegistry._validateApkHashAtBlockNumber: not latest apk update" + quorumApkUpdate.nextUpdateBlockNumber == 0 + || blockNumber < quorumApkUpdate.nextUpdateBlockNumber, + BlockNumberNotLatest() ); return quorumApkUpdate.apkHash; } - /// @notice Returns the length of ApkUpdates for the provided `quorumNumber` - function getApkHistoryLength(uint8 quorumNumber) external view returns (uint32) { + /// @inheritdoc IBLSApkRegistry + function getApkHistoryLength( + uint8 quorumNumber + ) external view returns (uint32) { return uint32(apkHistory[quorumNumber].length); } - /// @notice Returns the operator address for the given `pubkeyHash` - function getOperatorFromPubkeyHash(bytes32 pubkeyHash) public view returns (address) { + /// @inheritdoc IBLSApkRegistry + function getOperatorFromPubkeyHash( + bytes32 pubkeyHash + ) public view returns (address) { 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) { + /// @inheritdoc IBLSApkRegistry + function getOperatorId( + address operator + ) public view returns (bytes32) { return operatorToPubkeyHash[operator]; } function _checkRegistryCoordinator() internal view { - require( - msg.sender == address(registryCoordinator), - "BLSApkRegistry.onlyRegistryCoordinator: caller is not the registry coordinator" - ); + require(msg.sender == address(registryCoordinator), OnlyRegistryCoordinatorOwner()); } } diff --git a/src/BLSApkRegistryStorage.sol b/src/BLSApkRegistryStorage.sol index 9597d5ac..2d07a1a9 100644 --- a/src/BLSApkRegistryStorage.sol +++ b/src/BLSApkRegistryStorage.sol @@ -1,40 +1,44 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; -import {IBLSApkRegistry} from "./interfaces/IBLSApkRegistry.sol"; -import {IRegistryCoordinator} from "./interfaces/IRegistryCoordinator.sol"; +import {IBLSApkRegistry, IBLSApkRegistryTypes} from "./interfaces/IBLSApkRegistry.sol"; +import {ISlashingRegistryCoordinator} from "./interfaces/ISlashingRegistryCoordinator.sol"; import {Initializable} from "@openzeppelin-upgrades/contracts/proxy/utils/Initializable.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) - bytes32 internal constant ZERO_PK_HASH = hex"ad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5"; + /// @dev Returns the hash of the zero pubkey aka BN254.G1Point(0,0) + bytes32 internal constant ZERO_PK_HASH = + hex"ad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5"; - /// @notice the registry coordinator contract + /// @inheritdoc IBLSApkRegistry address public immutable registryCoordinator; - // 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 - mapping(uint8 => BN254.G1Point) public currentApk; - - constructor(IRegistryCoordinator _registryCoordinator) { - registryCoordinator = address(_registryCoordinator); + /// INDIVIDUAL PUBLIC KEY STORAGE + + /// @inheritdoc IBLSApkRegistry + mapping(address operator => bytes32 operatorId) public operatorToPubkeyHash; + /// @inheritdoc IBLSApkRegistry + mapping(bytes32 pubkeyHash => address operator) public pubkeyHashToOperator; + /// @inheritdoc IBLSApkRegistry + mapping(address operator => BN254.G1Point pubkeyG1) public operatorToPubkey; + + /// AGGREGATE PUBLIC KEY STORAGE + + /// @inheritdoc IBLSApkRegistry + mapping(uint8 quorumNumber => IBLSApkRegistryTypes.ApkUpdate[]) public apkHistory; + /// @inheritdoc IBLSApkRegistry + mapping(uint8 quorumNumber => BN254.G1Point) public currentApk; + + constructor( + ISlashingRegistryCoordinator _slashingRegistryCoordinator + ) { + registryCoordinator = address(_slashingRegistryCoordinator); // disable initializers so that the implementation contract cannot be initialized _disableInitializers(); } - // storage gap for upgradeability uint256[45] private __GAP; } diff --git a/src/BLSSignatureChecker.sol b/src/BLSSignatureChecker.sol index 5392289c..5743be64 100644 --- a/src/BLSSignatureChecker.sol +++ b/src/BLSSignatureChecker.sol @@ -1,119 +1,67 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; - -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"; +pragma solidity ^0.8.27; import {BitmapUtils} from "./libraries/BitmapUtils.sol"; import {BN254} from "./libraries/BN254.sol"; +import "./BLSSignatureCheckerStorage.sol"; + /** * @title Used for checking BLS aggregate signatures from the operators of a `BLSRegistry`. * @author Layr Labs, Inc. * @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service * @notice This is the contract for checking the validity of aggregate operator signatures. */ -contract BLSSignatureChecker is IBLSSignatureChecker { +contract BLSSignatureChecker is BLSSignatureCheckerStorage { using BN254 for BN254.G1Point; - // CONSTANTS & IMMUTABLES - - // gas cost of multiplying 2 pairings - uint256 internal constant PAIRING_EQUALITY_CHECK_GAS = 120_000; - - IRegistryCoordinator public immutable registryCoordinator; - IStakeRegistry public immutable stakeRegistry; - IBLSApkRegistry public immutable blsApkRegistry; - IDelegationManager public immutable delegation; - /// @notice If true, check the staleness of the operator stakes and that its within the delegation withdrawalDelayBlocks window. - bool public staleStakesForbidden; + /// MODIFIERS modifier onlyCoordinatorOwner() { - require( - msg.sender == registryCoordinator.owner(), - "BLSSignatureChecker.onlyCoordinatorOwner: caller is not the owner of the registryCoordinator" - ); + require(msg.sender == registryCoordinator.owner(), OnlyRegistryCoordinatorOwner()); _; } - constructor(IRegistryCoordinator _registryCoordinator) { - registryCoordinator = _registryCoordinator; - stakeRegistry = _registryCoordinator.stakeRegistry(); - blsApkRegistry = _registryCoordinator.blsApkRegistry(); - delegation = stakeRegistry.delegation(); - } + /// CONSTRUCTION + + constructor( + ISlashingRegistryCoordinator _registryCoordinator + ) BLSSignatureCheckerStorage(_registryCoordinator) {} + + /// ACTIONS - /** - * /** - * RegistryCoordinator owner can either enforce or not that operator stakes are staler - * than the delegation.minWithdrawalDelayBlocks() window. - * @param value to toggle staleStakesForbidden - */ - function setStaleStakesForbidden(bool value) external onlyCoordinatorOwner { + /// @inheritdoc IBLSSignatureChecker + function setStaleStakesForbidden( + bool value + ) external onlyCoordinatorOwner { _setStaleStakesForbidden(value); } - struct NonSignerInfo { - uint256[] quorumBitmaps; - bytes32[] pubkeyHashes; - } + /// VIEW - /** - * @notice This function is called by disperser when it has aggregated all the signatures of the operators - * that are part of the quorum for a particular taskNumber and is asserting them into onchain. The function - * checks that the claim for aggregated signatures are valid. - * - * The thesis of this procedure entails: - * - getting the aggregated pubkey of all registered nodes at the time of pre-commit by the - * disperser (represented by apk in the parameters), - * - subtracting the pubkeys of all the signers not in the quorum (nonSignerPubkeys) and storing - * the output in apk to get aggregated pubkey of all operators that are part of quorum. - * - use this aggregated pubkey to verify the aggregated signature under BLS scheme. - * - * @dev Before signature verification, the function verifies operator stake information. This includes ensuring that the provided `referenceBlockNumber` - * is correct, i.e., ensure that the stake returned from the specified block number is recent enough and that the stake is either the most recent update - * for the total stake (of the operator) or latest before the referenceBlockNumber. - * @param msgHash is the hash being signed - * @dev NOTE: Be careful to ensure `msgHash` is collision-resistant! This method does not hash - * `msgHash` in any way, so if an attacker is able to pass in an arbitrary value, they may be able - * to tamper with signature verification. - * @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 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 - */ + /// @inheritdoc IBLSSignatureChecker function checkSignatures( bytes32 msgHash, bytes calldata quorumNumbers, uint32 referenceBlockNumber, NonSignerStakesAndSignature memory params ) public view returns (QuorumStakeTotals memory, bytes32) { - require( - quorumNumbers.length != 0, - "BLSSignatureChecker.checkSignatures: empty quorum input" - ); + require(quorumNumbers.length != 0, InputEmptyQuorumNumbers()); 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" + (quorumNumbers.length == params.quorumApks.length) + && (quorumNumbers.length == params.quorumApkIndices.length) + && (quorumNumbers.length == params.totalStakeIndices.length) + && (quorumNumbers.length == params.nonSignerStakeIndices.length), + InputArrayLengthMismatch() ); require( - params.nonSignerPubkeys.length == - params.nonSignerQuorumBitmapIndices.length, - "BLSSignatureChecker.checkSignatures: input nonsigner length mismatch" + params.nonSignerPubkeys.length == params.nonSignerQuorumBitmapIndices.length, + InputNonSignerLengthMismatch() ); - require( - referenceBlockNumber < uint32(block.number), - "BLSSignatureChecker.checkSignatures: invalid reference block" - ); + require(referenceBlockNumber < uint32(block.number), InvalidReferenceBlocknumber()); // 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 @@ -131,50 +79,43 @@ contract BLSSignatureChecker is IBLSSignatureChecker { stakeTotals.signedStakeForQuorum = new uint96[](quorumNumbers.length); NonSignerInfo memory nonSigners; - nonSigners.quorumBitmaps = new uint256[]( - params.nonSignerPubkeys.length - ); + 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() + 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(); + 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" + uint256(nonSigners.pubkeyHashes[j]) + > uint256(nonSigners.pubkeyHashes[j - 1]), + NonSignerPubkeysNotSorted() ); } // 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] - }); + 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 - ) + BitmapUtils.countNumOnes(nonSigners.quorumBitmaps[j] & signingQuorumBitmap) ) ); } @@ -193,46 +134,41 @@ contract BLSSignatureChecker is IBLSSignatureChecker { */ { bool _staleStakesForbidden = staleStakesForbidden; - uint256 withdrawalDelayBlocks = _staleStakesForbidden - ? delegation.minWithdrawalDelayBlocks() - : 0; + uint256 withdrawalDelayBlocks = + _staleStakesForbidden ? delegation.minWithdrawalDelayBlocks() : 0; 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" + registryCoordinator.quorumUpdateBlockNumber(uint8(quorumNumbers[i])) + + withdrawalDelayBlocks > referenceBlockNumber, + StaleStakesForbidden() ); } // 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({ + 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" + InvalidQuorumApkHash() ); 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]; + 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; @@ -241,21 +177,14 @@ 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.isSet( - nonSigners.quorumBitmaps[j], - uint8(quorumNumbers[i]) - ) - ) { + if (BitmapUtils.isSet(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 - ] - }); + quorumNumber: uint8(quorumNumbers[i]), + blockNumber: referenceBlockNumber, + operatorId: nonSigners.pubkeyHashes[j], + index: params.nonSignerStakeIndices[i][nonSignerForQuorumIndex] + }); unchecked { ++nonSignerForQuorumIndex; } @@ -265,42 +194,20 @@ contract BLSSignatureChecker is IBLSSignatureChecker { } { // verify the signature - ( - bool pairingSuccessful, - bool signatureIsValid - ) = trySignatureAndApkVerification( - msgHash, - apk, - params.apkG2, - params.sigma - ); - require( - pairingSuccessful, - "BLSSignatureChecker.checkSignatures: pairing precompile call failed" - ); - require( - signatureIsValid, - "BLSSignatureChecker.checkSignatures: signature is invalid" - ); + (bool pairingSuccessful, bool signatureIsValid) = + trySignatureAndApkVerification(msgHash, apk, params.apkG2, params.sigma); + require(pairingSuccessful, InvalidBLSPairingKey()); + require(signatureIsValid, InvalidBLSSignature()); } // set signatoryRecordHash variable used for fraudproofs - bytes32 signatoryRecordHash = keccak256( - abi.encodePacked(referenceBlockNumber, nonSigners.pubkeyHashes) - ); + 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 (stakeTotals, signatoryRecordHash); } - /** - * trySignatureAndApkVerification verifies a BLS aggregate signature and the veracity of a calculated G1 Public key - * @param msgHash is the hash being signed - * @param apk is the claimed G1 public key - * @param apkG2 is provided G2 public key - * @param sigma is the G1 point signature - * @return pairingSuccessful is true if the pairing precompile call was successful - * @return siganatureIsValid is true if the signature is valid - */ + /// @inheritdoc IBLSSignatureChecker function trySignatureAndApkVerification( bytes32 msgHash, BN254.G1Point memory apk, @@ -333,12 +240,10 @@ contract BLSSignatureChecker is IBLSSignatureChecker { ); } - function _setStaleStakesForbidden(bool value) internal { + function _setStaleStakesForbidden( + bool value + ) internal { staleStakesForbidden = value; emit StaleStakesForbiddenUpdate(value); } - - // storage gap for upgradeability - // slither-disable-next-line shadowing-state - uint256[49] private __GAP; } diff --git a/src/BLSSignatureCheckerStorage.sol b/src/BLSSignatureCheckerStorage.sol new file mode 100644 index 00000000..59bd073a --- /dev/null +++ b/src/BLSSignatureCheckerStorage.sol @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.27; + +import {IBLSSignatureChecker} from "./interfaces/IBLSSignatureChecker.sol"; +import {ISlashingRegistryCoordinator} from "./interfaces/ISlashingRegistryCoordinator.sol"; +import {IBLSApkRegistry} from "./interfaces/IBLSApkRegistry.sol"; +import {IStakeRegistry, IDelegationManager} from "./interfaces/IStakeRegistry.sol"; + +abstract contract BLSSignatureCheckerStorage is IBLSSignatureChecker { + /// @dev Returns the assumed gas cost of multiplying 2 pairings. + uint256 internal constant PAIRING_EQUALITY_CHECK_GAS = 120000; + + /// @inheritdoc IBLSSignatureChecker + ISlashingRegistryCoordinator public immutable registryCoordinator; + /// @inheritdoc IBLSSignatureChecker + IStakeRegistry public immutable stakeRegistry; + /// @inheritdoc IBLSSignatureChecker + IBLSApkRegistry public immutable blsApkRegistry; + /// @inheritdoc IBLSSignatureChecker + IDelegationManager public immutable delegation; + + /// STATE + + /// @inheritdoc IBLSSignatureChecker + bool public staleStakesForbidden; + + constructor( + ISlashingRegistryCoordinator _registryCoordinator + ) { + registryCoordinator = _registryCoordinator; + stakeRegistry = _registryCoordinator.stakeRegistry(); + blsApkRegistry = _registryCoordinator.blsApkRegistry(); + delegation = stakeRegistry.delegation(); + } + + // slither-disable-next-line shadowing-state + uint256[49] private __GAP; +} diff --git a/src/EjectionManager.sol b/src/EjectionManager.sol index ca81676b..2d695f27 100644 --- a/src/EjectionManager.sol +++ b/src/EjectionManager.sol @@ -1,72 +1,49 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {OwnableUpgradeable} from "@openzeppelin-upgrades/contracts/access/OwnableUpgradeable.sol"; -import {IEjectionManager} from "./interfaces/IEjectionManager.sol"; -import {IRegistryCoordinator} from "./interfaces/IRegistryCoordinator.sol"; -import {IStakeRegistry} from "./interfaces/IStakeRegistry.sol"; +import { + EjectionManagerStorage, + IEjectionManager, + IRegistryCoordinator, + IStakeRegistry +} from "./EjectionManagerStorage.sol"; + +// TODO: double check order of inheritance since we separated storage from logic... /** * @title Used for automated ejection of operators from the RegistryCoordinator under a ratelimit * @author Layr Labs, Inc. */ -contract EjectionManager is IEjectionManager, OwnableUpgradeable{ - - /// @notice The basis point denominator for the ejectable stake percent - uint16 internal constant BIPS_DENOMINATOR = 10000; - - /// @notice the RegistryCoordinator contract that is the entry point for ejection - IRegistryCoordinator public immutable registryCoordinator; - /// @notice the StakeRegistry contract that keeps track of quorum stake - IStakeRegistry public immutable stakeRegistry; - - /// @notice Addresses permissioned to eject operators under a ratelimit - mapping(address => bool) public isEjector; - - /// @notice Keeps track of the total stake ejected for a quorum - mapping(uint8 => StakeEjection[]) public stakeEjectedForQuorum; - /// @notice Ratelimit parameters for each quorum - mapping(uint8 => QuorumEjectionParams) public quorumEjectionParams; - +contract EjectionManager is OwnableUpgradeable, EjectionManagerStorage { constructor( IRegistryCoordinator _registryCoordinator, IStakeRegistry _stakeRegistry - ) { - registryCoordinator = _registryCoordinator; - stakeRegistry = _stakeRegistry; - + ) EjectionManagerStorage(_registryCoordinator, _stakeRegistry) { _disableInitializers(); } - /** - * @param _owner will hold the owner role - * @param _ejectors will hold the ejector role - * @param _quorumEjectionParams are the ratelimit parameters for the quorum at each index - */ function initialize( address _owner, address[] memory _ejectors, QuorumEjectionParams[] memory _quorumEjectionParams ) external initializer { _transferOwnership(_owner); - for(uint8 i = 0; i < _ejectors.length; i++) { + for (uint8 i = 0; i < _ejectors.length; i++) { _setEjector(_ejectors[i], true); } - for(uint8 i = 0; i < _quorumEjectionParams.length; i++) { + for (uint8 i = 0; i < _quorumEjectionParams.length; i++) { _setQuorumEjectionParams(i, _quorumEjectionParams[i]); } } - /** - * @notice Ejects operators from the AVSs RegistryCoordinator under a ratelimit - * @param _operatorIds The ids of the operators 'j' to eject for each quorum 'i' - * @dev This function will eject as many operators as possible prioritizing operators at the lower index - * @dev The owner can eject operators without recording of stake ejection - */ - function ejectOperators(bytes32[][] memory _operatorIds) external { - require(isEjector[msg.sender] || msg.sender == owner(), "Ejector: Only owner or ejector can eject"); + /// @inheritdoc IEjectionManager + function ejectOperators( + bytes32[][] memory operatorIds + ) external { + require(isEjector[msg.sender] || msg.sender == owner(), OnlyOwnerOrEjector()); - for(uint i = 0; i < _operatorIds.length; ++i) { + for (uint256 i = 0; i < operatorIds.length; ++i) { uint8 quorumNumber = uint8(i); uint256 amountEjectable = amountEjectableForQuorum(quorumNumber); @@ -74,27 +51,28 @@ contract EjectionManager is IEjectionManager, OwnableUpgradeable{ uint32 ejectedOperators; bool ratelimitHit; - if(amountEjectable > 0 || msg.sender == owner()){ - for(uint8 j = 0; j < _operatorIds[i].length; ++j) { - uint256 operatorStake = stakeRegistry.getCurrentStake(_operatorIds[i][j], quorumNumber); + if (amountEjectable > 0 || msg.sender == owner()) { + for (uint8 j = 0; j < operatorIds[i].length; ++j) { + uint256 operatorStake = + stakeRegistry.getCurrentStake(operatorIds[i][j], quorumNumber); //if caller is ejector enforce ratelimit - if( - isEjector[msg.sender] && - quorumEjectionParams[quorumNumber].rateLimitWindow > 0 && - stakeForEjection + operatorStake > amountEjectable - ){ + if ( + isEjector[msg.sender] + && quorumEjectionParams[quorumNumber].rateLimitWindow > 0 + && stakeForEjection + operatorStake > amountEjectable + ) { ratelimitHit = true; stakeForEjection += operatorStake; ++ejectedOperators; registryCoordinator.ejectOperator( - registryCoordinator.getOperatorFromId(_operatorIds[i][j]), + registryCoordinator.getOperatorFromId(operatorIds[i][j]), abi.encodePacked(quorumNumber) ); - emit OperatorEjected(_operatorIds[i][j], quorumNumber); + emit OperatorEjected(operatorIds[i][j], quorumNumber); break; } @@ -103,82 +81,85 @@ contract EjectionManager is IEjectionManager, OwnableUpgradeable{ ++ejectedOperators; registryCoordinator.ejectOperator( - registryCoordinator.getOperatorFromId(_operatorIds[i][j]), + registryCoordinator.getOperatorFromId(operatorIds[i][j]), abi.encodePacked(quorumNumber) ); - - emit OperatorEjected(_operatorIds[i][j], quorumNumber); + + emit OperatorEjected(operatorIds[i][j], quorumNumber); } } //record the stake ejected if ejector and ratelimit enforced - if(isEjector[msg.sender] && stakeForEjection > 0){ - stakeEjectedForQuorum[quorumNumber].push(StakeEjection({ - timestamp: block.timestamp, - stakeEjected: stakeForEjection - })); + if (isEjector[msg.sender] && stakeForEjection > 0) { + stakeEjectedForQuorum[quorumNumber].push( + StakeEjection({timestamp: block.timestamp, stakeEjected: stakeForEjection}) + ); } emit QuorumEjection(ejectedOperators, ratelimitHit); } } - /** - * @notice Sets the ratelimit parameters for a quorum - * @param _quorumNumber The quorum number to set the ratelimit parameters for - * @param _quorumEjectionParams The quorum ratelimit parameters to set for the given quorum - */ - function setQuorumEjectionParams(uint8 _quorumNumber, QuorumEjectionParams memory _quorumEjectionParams) external onlyOwner() { - _setQuorumEjectionParams(_quorumNumber, _quorumEjectionParams); + /// @inheritdoc IEjectionManager + function setQuorumEjectionParams( + uint8 quorumNumber, + QuorumEjectionParams memory _quorumEjectionParams + ) external onlyOwner { + _setQuorumEjectionParams(quorumNumber, _quorumEjectionParams); } - /** - * @notice Sets the address permissioned to eject operators under a ratelimit - * @param _ejector The address to permission - * @param _status The status to set for the given address - */ - function setEjector(address _ejector, bool _status) external onlyOwner() { - _setEjector(_ejector, _status); + /// @inheritdoc IEjectionManager + function setEjector(address ejector, bool status) external onlyOwner { + _setEjector(ejector, status); } ///@dev internal function to set the quorum ejection params - function _setQuorumEjectionParams(uint8 _quorumNumber, QuorumEjectionParams memory _quorumEjectionParams) internal { - quorumEjectionParams[_quorumNumber] = _quorumEjectionParams; - emit QuorumEjectionParamsSet(_quorumNumber, _quorumEjectionParams.rateLimitWindow, _quorumEjectionParams.ejectableStakePercent); + function _setQuorumEjectionParams( + uint8 quorumNumber, + QuorumEjectionParams memory _quorumEjectionParams + ) internal { + require(quorumNumber < MAX_QUORUM_COUNT, MaxQuorumCount()); + quorumEjectionParams[quorumNumber] = _quorumEjectionParams; + emit QuorumEjectionParamsSet( + quorumNumber, + _quorumEjectionParams.rateLimitWindow, + _quorumEjectionParams.ejectableStakePercent + ); } ///@dev internal function to set the ejector - function _setEjector(address _ejector, bool _status) internal { - isEjector[_ejector] = _status; - emit EjectorUpdated(_ejector, _status); + function _setEjector(address ejector, bool status) internal { + isEjector[ejector] = status; + emit EjectorUpdated(ejector, status); } - /** - * @notice Returns the amount of stake that can be ejected for a quorum at the current block.timestamp - * @param _quorumNumber The quorum number to view ejectable stake for - */ - function amountEjectableForQuorum(uint8 _quorumNumber) public view returns (uint256) { - uint256 cutoffTime = block.timestamp - quorumEjectionParams[_quorumNumber].rateLimitWindow; - uint256 totalEjectable = uint256(quorumEjectionParams[_quorumNumber].ejectableStakePercent) * uint256(stakeRegistry.getCurrentTotalStake(_quorumNumber)) / uint256(BIPS_DENOMINATOR); - uint256 totalEjected; - uint256 i; - if (stakeEjectedForQuorum[_quorumNumber].length == 0) { + /// @inheritdoc IEjectionManager + function amountEjectableForQuorum( + uint8 quorumNumber + ) public view returns (uint256) { + uint256 totalEjectable = uint256(quorumEjectionParams[quorumNumber].ejectableStakePercent) + * uint256(stakeRegistry.getCurrentTotalStake(quorumNumber)) / uint256(BIPS_DENOMINATOR); + + if (stakeEjectedForQuorum[quorumNumber].length == 0) { return totalEjectable; } - i = stakeEjectedForQuorum[_quorumNumber].length - 1; - while(stakeEjectedForQuorum[_quorumNumber][i].timestamp > cutoffTime) { - totalEjected += stakeEjectedForQuorum[_quorumNumber][i].stakeEjected; - if(i == 0){ + uint256 cutoffTime = block.timestamp - quorumEjectionParams[quorumNumber].rateLimitWindow; + uint256 totalEjected = 0; + uint256 i = stakeEjectedForQuorum[quorumNumber].length - 1; + + while (stakeEjectedForQuorum[quorumNumber][i].timestamp > cutoffTime) { + totalEjected += stakeEjectedForQuorum[quorumNumber][i].stakeEjected; + if (i == 0) { break; } else { --i; } } - if(totalEjected >= totalEjectable){ + if (totalEjected >= totalEjectable) { return 0; } return totalEjectable - totalEjected; } -} \ No newline at end of file +} diff --git a/src/EjectionManagerStorage.sol b/src/EjectionManagerStorage.sol new file mode 100644 index 00000000..e8996cd6 --- /dev/null +++ b/src/EjectionManagerStorage.sol @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.27; + +import {IRegistryCoordinator} from "./interfaces/IRegistryCoordinator.sol"; +import {IStakeRegistry} from "./interfaces/IStakeRegistry.sol"; +import {IEjectionManager} from "./interfaces/IEjectionManager.sol"; + +abstract contract EjectionManagerStorage is IEjectionManager { + /// @notice The basis point denominator for the ejectable stake percent + uint16 internal constant BIPS_DENOMINATOR = 10000; + /// @notice The max number of quorums + uint8 internal constant MAX_QUORUM_COUNT = 192; + + /// @inheritdoc IEjectionManager + IRegistryCoordinator public immutable registryCoordinator; + /// @inheritdoc IEjectionManager + IStakeRegistry public immutable stakeRegistry; + + /// @inheritdoc IEjectionManager + mapping(address => bool) public isEjector; + /// @inheritdoc IEjectionManager + mapping(uint8 => StakeEjection[]) public stakeEjectedForQuorum; + /// @inheritdoc IEjectionManager + mapping(uint8 => QuorumEjectionParams) public quorumEjectionParams; + + constructor(IRegistryCoordinator _registryCoordinator, IStakeRegistry _stakeRegistry) { + registryCoordinator = _registryCoordinator; + stakeRegistry = _stakeRegistry; + } + + /// @dev This was missing before the slashing release, if your contract + /// was deployed pre-slashing, you should double check your storage layout. + uint256[47] private __gap; +} diff --git a/src/IndexRegistry.sol b/src/IndexRegistry.sol index 8df2c0a1..032b1839 100644 --- a/src/IndexRegistry.sol +++ b/src/IndexRegistry.sol @@ -1,53 +1,41 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; -import {IndexRegistryStorage} from "./IndexRegistryStorage.sol"; -import {IRegistryCoordinator} from "./interfaces/IRegistryCoordinator.sol"; +import {IIndexRegistry, IndexRegistryStorage} from "./IndexRegistryStorage.sol"; +import {ISlashingRegistryCoordinator} from "./interfaces/ISlashingRegistryCoordinator.sol"; /** * @title A `Registry` that keeps track of an ordered list of operators for each quorum * @author Layr Labs, Inc. */ contract IndexRegistry is IndexRegistryStorage { - - /// @notice when applied to a function, only allows the RegistryCoordinator to call it modifier onlyRegistryCoordinator() { _checkRegistryCoordinator(); _; } - /// @notice sets the (immutable) `registryCoordinator` address constructor( - IRegistryCoordinator _registryCoordinator - ) IndexRegistryStorage(_registryCoordinator) {} - - /******************************************************************************* - EXTERNAL FUNCTIONS - REGISTRY COORDINATOR - *******************************************************************************/ + ISlashingRegistryCoordinator _slashingRegistryCoordinator + ) IndexRegistryStorage(_slashingRegistryCoordinator) {} /** - * @notice Registers the operator with the specified `operatorId` for the quorums specified by `quorumNumbers`. - * @param operatorId is the id of the operator that is being registered - * @param quorumNumbers is the quorum numbers the operator is registered for - * @return numOperatorsPerQuorum is a list of the number of operators (including the registering operator) in each of the quorums the operator is registered for - * @dev access restricted to the RegistryCoordinator - * @dev Preconditions (these are assumed, not validated in this contract): - * 1) `quorumNumbers` has no duplicates - * 2) `quorumNumbers.length` != 0 - * 3) `quorumNumbers` is ordered in ascending order - * 4) the operator is not already registered + * + * EXTERNAL FUNCTIONS - REGISTRY COORDINATOR + * */ + + /// @inheritdoc IIndexRegistry function registerOperator( - bytes32 operatorId, + bytes32 operatorId, bytes calldata quorumNumbers - ) public virtual onlyRegistryCoordinator returns(uint32[] memory) { + ) public virtual onlyRegistryCoordinator returns (uint32[] memory) { 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]); uint256 historyLength = _operatorCountHistory[quorumNumber].length; - require(historyLength != 0, "IndexRegistry.registerOperator: quorum does not exist"); + require(historyLength != 0, QuorumDoesNotExist()); /** * Increase the number of operators currently active for this quorum, @@ -67,27 +55,16 @@ contract IndexRegistry is IndexRegistryStorage { return numOperatorsPerQuorum; } - /** - * @notice Deregisters the operator with the specified `operatorId` for the quorums specified by `quorumNumbers`. - * @param operatorId is the id of the operator that is being deregistered - * @param quorumNumbers is the quorum numbers the operator is deregistered for - * @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 - */ + /// @inheritdoc IIndexRegistry function deregisterOperator( - bytes32 operatorId, + bytes32 operatorId, bytes calldata quorumNumbers ) public virtual onlyRegistryCoordinator { for (uint256 i = 0; i < quorumNumbers.length; i++) { // Validate quorum exists and get the operatorIndex of the operator being deregistered uint8 quorumNumber = uint8(quorumNumbers[i]); uint256 historyLength = _operatorCountHistory[quorumNumber].length; - require(historyLength != 0, "IndexRegistry.registerOperator: quorum does not exist"); + require(historyLength != 0, QuorumDoesNotExist()); uint32 operatorIndexToRemove = currentOperatorIndex[quorumNumber][operatorId]; /** @@ -108,61 +85,61 @@ contract IndexRegistry is IndexRegistryStorage { } } - /** - * @notice Initialize a quorum by pushing its first quorum update - * @param quorumNumber The number of the new quorum - */ - function initializeQuorum(uint8 quorumNumber) public virtual onlyRegistryCoordinator { - require(_operatorCountHistory[quorumNumber].length == 0, "IndexRegistry.createQuorum: quorum already exists"); + /// @inheritdoc IIndexRegistry + function initializeQuorum( + uint8 quorumNumber + ) public virtual onlyRegistryCoordinator { + require(_operatorCountHistory[quorumNumber].length == 0, QuorumDoesNotExist()); - _operatorCountHistory[quorumNumber].push(QuorumUpdate({ - numOperators: 0, - fromBlockNumber: uint32(block.number) - })); + _operatorCountHistory[quorumNumber].push( + QuorumUpdate({numOperators: 0, fromBlockNumber: uint32(block.number)}) + ); } - /******************************************************************************* - INTERNAL FUNCTIONS - *******************************************************************************/ - /** - * @notice Increases the historical operator count by 1 and returns the new count + * + * INTERNAL FUNCTIONS + * */ - function _increaseOperatorCount(uint8 quorumNumber) internal returns (uint32) { + + /// @notice Increases the historical operator count by 1 and returns the new count. + function _increaseOperatorCount( + uint8 quorumNumber + ) internal returns (uint32) { QuorumUpdate storage lastUpdate = _latestQuorumUpdate(quorumNumber); uint32 newOperatorCount = lastUpdate.numOperators + 1; - + _updateOperatorCountHistory(quorumNumber, lastUpdate, newOperatorCount); // If this is the first time we're using this operatorIndex, push its first update // This maintains an invariant: existing indices have nonzero history if (_operatorIndexHistory[quorumNumber][newOperatorCount - 1].length == 0) { - _operatorIndexHistory[quorumNumber][newOperatorCount - 1].push(OperatorUpdate({ - operatorId: OPERATOR_DOES_NOT_EXIST_ID, - fromBlockNumber: uint32(block.number) - })); + _operatorIndexHistory[quorumNumber][newOperatorCount - 1].push( + OperatorUpdate({ + operatorId: OPERATOR_DOES_NOT_EXIST_ID, + 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) { + /// @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; - + _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. - */ + /// @notice Updates `_operatorCountHistory` with a new operator count. + /// @dev If the lastUpdate was made in this block, update the entry. + /// Otherwise, push a new historical entry. function _updateOperatorCountHistory( uint8 quorumNumber, QuorumUpdate storage lastUpdate, @@ -171,35 +148,39 @@ contract IndexRegistry is IndexRegistryStorage { if (lastUpdate.fromBlockNumber == uint32(block.number)) { lastUpdate.numOperators = newOperatorCount; } else { - _operatorCountHistory[quorumNumber].push(QuorumUpdate({ - numOperators: newOperatorCount, - fromBlockNumber: uint32(block.number) - })); + _operatorCountHistory[quorumNumber].push( + QuorumUpdate({numOperators: newOperatorCount, fromBlockNumber: uint32(block.number)}) + ); } } - /** - * @notice For a given quorum and operatorIndex, 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 operatorIndex) internal returns (bytes32) { + /// @notice For a given quorum and operatorIndex, 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 operatorIndex + ) internal returns (bytes32) { OperatorUpdate storage lastUpdate = _latestOperatorIndexUpdate(quorumNumber, operatorIndex); bytes32 removedOperatorId = lastUpdate.operatorId; // Set the current operator id for this operatorIndex to 0 - _updateOperatorIndexHistory(quorumNumber, operatorIndex, lastUpdate, OPERATOR_DOES_NOT_EXIST_ID); + _updateOperatorIndexHistory( + quorumNumber, operatorIndex, lastUpdate, OPERATOR_DOES_NOT_EXIST_ID + ); 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 operatorIndex the latest index of that operator in the list of operators registered for this quorum - */ - function _assignOperatorToIndex(bytes32 operatorId, uint8 quorumNumber, uint32 operatorIndex) internal { + /// @notice Assigns an operator to an index and updates the index history. + /// @param operatorId operatorId of the operator to update. + /// @param quorumNumber quorumNumber of the operator to update. + /// @param operatorIndex the latest index of that operator in the list of operators registered for this quorum. + function _assignOperatorToIndex( + bytes32 operatorId, + uint8 quorumNumber, + uint32 operatorIndex + ) internal { OperatorUpdate storage lastUpdate = _latestOperatorIndexUpdate(quorumNumber, operatorIndex); _updateOperatorIndexHistory(quorumNumber, operatorIndex, lastUpdate, operatorId); @@ -209,11 +190,9 @@ contract IndexRegistry is IndexRegistryStorage { emit QuorumIndexUpdate(operatorId, quorumNumber, operatorIndex); } - /** - * @notice Update `_operatorIndexHistory` 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. - */ + /// @notice Updates `_operatorIndexHistory` with a new operator id for the current block. + /// @dev If the lastUpdate was made in this block, update the entry. + /// Otherwise, push a new historical entry. function _updateOperatorIndexHistory( uint8 quorumNumber, uint32 operatorIndex, @@ -223,35 +202,37 @@ contract IndexRegistry is IndexRegistryStorage { if (lastUpdate.fromBlockNumber == uint32(block.number)) { lastUpdate.operatorId = newOperatorId; } else { - _operatorIndexHistory[quorumNumber][operatorIndex].push(OperatorUpdate({ - operatorId: newOperatorId, - fromBlockNumber: uint32(block.number) - })); + _operatorIndexHistory[quorumNumber][operatorIndex].push( + OperatorUpdate({operatorId: newOperatorId, fromBlockNumber: uint32(block.number)}) + ); } } - /// @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) { + /// @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 _latestOperatorIndexUpdate(uint8 quorumNumber, uint32 operatorIndex) internal view returns (OperatorUpdate storage) { + /// @notice Returns the most recent operator id update for an index. + /// @dev Reverts if the index has never been used (history length == 0). + function _latestOperatorIndexUpdate( + uint8 quorumNumber, + uint32 operatorIndex + ) internal view returns (OperatorUpdate storage) { uint256 historyLength = _operatorIndexHistory[quorumNumber][operatorIndex].length; return _operatorIndexHistory[quorumNumber][operatorIndex][historyLength - 1]; } - /** - * @notice Returns the total number of operators of the service for the given `quorumNumber` at the given `blockNumber` - * @dev Reverts if the quorum does not exist, or if the blockNumber is from before the quorum existed - */ + /// @notice Returns the total number of operators of the service for the given `quorumNumber` at the given `blockNumber`. + /// @dev Reverts if the quorum does not exist, or if the blockNumber is from before the quorum existed. function _operatorCountAtBlockNumber( - uint8 quorumNumber, + uint8 quorumNumber, uint32 blockNumber - ) internal view returns (uint32){ + ) internal view returns (uint32) { uint256 historyLength = _operatorCountHistory[quorumNumber].length; // Loop backwards through _operatorCountHistory until we find an entry that preceeds `blockNumber` @@ -262,24 +243,25 @@ contract IndexRegistry is IndexRegistryStorage { return quorumUpdate.numOperators; } } - - revert("IndexRegistry._operatorCountAtBlockNumber: quorum did not exist at given block number"); + + revert( + "IndexRegistry._operatorCountAtBlockNumber: quorum did not exist at given block number" + ); } - - /** - * @return operatorId at the given `operatorIndex` at the given `blockNumber` for the given `quorumNumber` - * Precondition: requires that the operatorIndex was used active at the given block number for quorum - */ + + /// @notice Returns the operatorId at the given `operatorIndex` at the given `blockNumber` for the given `quorumNumber`. + /// @dev Requires that the operatorIndex was active at the given block number for quorum. function _operatorIdForIndexAtBlockNumber( - uint8 quorumNumber, - uint32 operatorIndex, + uint8 quorumNumber, + uint32 operatorIndex, uint32 blockNumber - ) internal view returns(bytes32) { + ) internal view returns (bytes32) { uint256 historyLength = _operatorIndexHistory[quorumNumber][operatorIndex].length; // Loop backward through _operatorIndexHistory until we find an entry that preceeds `blockNumber` for (uint256 i = historyLength; i > 0; i--) { - OperatorUpdate memory operatorIndexUpdate = _operatorIndexHistory[quorumNumber][operatorIndex][i - 1]; + OperatorUpdate memory operatorIndexUpdate = + _operatorIndexHistory[quorumNumber][operatorIndex][i - 1]; if (operatorIndexUpdate.fromBlockNumber <= blockNumber) { // Special case: this will be OPERATOR_DOES_NOT_EXIST_ID if this operatorIndex was not used at the block number @@ -291,57 +273,66 @@ contract IndexRegistry is IndexRegistryStorage { return OPERATOR_DOES_NOT_EXIST_ID; } - /******************************************************************************* - VIEW FUNCTIONS - *******************************************************************************/ + /** + * + * VIEW FUNCTIONS + * + */ - /// @notice Returns the _operatorIndexHistory entry for the specified `operatorIndex` and `quorumNumber` - /// at the specified `arrayIndex` - function getOperatorUpdateAtIndex(uint8 quorumNumber, uint32 operatorIndex, uint32 arrayIndex) external view returns (OperatorUpdate memory) { + /// @inheritdoc IIndexRegistry + function getOperatorUpdateAtIndex( + uint8 quorumNumber, + uint32 operatorIndex, + uint32 arrayIndex + ) external view returns (OperatorUpdate memory) { return _operatorIndexHistory[quorumNumber][operatorIndex][arrayIndex]; } - /// @notice Returns the _operatorCountHistory entry for the specified `quorumNumber` at the specified `quorumIndex` - function getQuorumUpdateAtIndex(uint8 quorumNumber, uint32 quorumIndex) external view returns (QuorumUpdate memory) { + /// @inheritdoc IIndexRegistry + function getQuorumUpdateAtIndex( + uint8 quorumNumber, + uint32 quorumIndex + ) external view returns (QuorumUpdate memory) { return _operatorCountHistory[quorumNumber][quorumIndex]; } - /// @notice Returns the most recent QuorumUpdate entry for the specified quorumNumber - /// @dev Reverts if the quorum does not exist - function getLatestQuorumUpdate(uint8 quorumNumber) external view returns (QuorumUpdate memory) { + /// @inheritdoc IIndexRegistry + function getLatestQuorumUpdate( + uint8 quorumNumber + ) external view returns (QuorumUpdate memory) { return _latestQuorumUpdate(quorumNumber); } - /// @notice Returns the most recent OperatorUpdate entry for the specified quorumNumber and operatorIndex - /// @dev Reverts if there is no update for the given operatorIndex - function getLatestOperatorUpdate(uint8 quorumNumber, uint32 operatorIndex) external view returns (OperatorUpdate memory) { + /// @inheritdoc IIndexRegistry + function getLatestOperatorUpdate( + uint8 quorumNumber, + uint32 operatorIndex + ) external view returns (OperatorUpdate memory) { return _latestOperatorIndexUpdate(quorumNumber, operatorIndex); } - /// @notice Returns an ordered list of operators of the services for the given `quorumNumber` at the given `blockNumber` + /// @inheritdoc IIndexRegistry function getOperatorListAtBlockNumber( - uint8 quorumNumber, + uint8 quorumNumber, uint32 blockNumber - ) external view returns (bytes32[] memory){ + ) external view returns (bytes32[] memory) { uint32 operatorCount = _operatorCountAtBlockNumber(quorumNumber, blockNumber); bytes32[] memory operatorList = new bytes32[](operatorCount); for (uint256 i = 0; i < operatorCount; i++) { 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" - ); + require(operatorList[i] != OPERATOR_DOES_NOT_EXIST_ID, OperatorIdDoesNotExist()); } 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){ + /// @inheritdoc IIndexRegistry + function totalOperatorsForQuorum( + uint8 quorumNumber + ) external view returns (uint32) { return _latestQuorumUpdate(quorumNumber).numOperators; } function _checkRegistryCoordinator() internal view { - require(msg.sender == address(registryCoordinator), "IndexRegistry.onlyRegistryCoordinator: caller is not the registry coordinator"); + require(msg.sender == address(registryCoordinator), OnlyRegistryCoordinator()); } } diff --git a/src/IndexRegistryStorage.sol b/src/IndexRegistryStorage.sol index b28a4510..f2c1e3e6 100644 --- a/src/IndexRegistryStorage.sol +++ b/src/IndexRegistryStorage.sol @@ -1,9 +1,9 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {Initializable} from "@openzeppelin-upgrades/contracts/proxy/utils/Initializable.sol"; -import {IRegistryCoordinator} from "./interfaces/IRegistryCoordinator.sol"; +import {ISlashingRegistryCoordinator} from "./interfaces/ISlashingRegistryCoordinator.sol"; import {IIndexRegistry} from "./interfaces/IIndexRegistry.sol"; /** @@ -12,7 +12,6 @@ import {IIndexRegistry} from "./interfaces/IIndexRegistry.sol"; * @notice This storage contract is separate from the logic to simplify the upgrade process. */ abstract contract IndexRegistryStorage is Initializable, IIndexRegistry { - /// @notice The value that is returned when an operator does not exist at an index at a certain block bytes32 public constant OPERATOR_DOES_NOT_EXIST_ID = bytes32(0); @@ -22,7 +21,7 @@ abstract contract IndexRegistryStorage is Initializable, IIndexRegistry { /// @notice maps quorumNumber => operator id => current operatorIndex /// NOTE: This mapping is NOT updated when an operator is deregistered, /// so it's possible that an index retrieved from this mapping is inaccurate. - /// If you're querying for an operator that might be deregistered, ALWAYS + /// If you're querying for an operator that might be deregistered, ALWAYS /// check this index against the latest `_operatorIndexHistory` entry mapping(uint8 => mapping(bytes32 => uint32)) public currentOperatorIndex; /// @notice maps quorumNumber => operatorIndex => historical operator ids at that index @@ -31,9 +30,9 @@ abstract contract IndexRegistryStorage is Initializable, IIndexRegistry { mapping(uint8 => QuorumUpdate[]) internal _operatorCountHistory; constructor( - IRegistryCoordinator _registryCoordinator - ){ - registryCoordinator = address(_registryCoordinator); + ISlashingRegistryCoordinator _slashingRegistryCoordinator + ) { + registryCoordinator = address(_slashingRegistryCoordinator); // disable initializers so that the implementation contract cannot be initialized _disableInitializers(); } diff --git a/src/OperatorStateRetriever.sol b/src/OperatorStateRetriever.sol index f0547a2d..d3e06490 100644 --- a/src/OperatorStateRetriever.sol +++ b/src/OperatorStateRetriever.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; -import {IRegistryCoordinator} from "./interfaces/IRegistryCoordinator.sol"; +import {ISlashingRegistryCoordinator} from "./interfaces/ISlashingRegistryCoordinator.sol"; import {IBLSApkRegistry} from "./interfaces/IBLSApkRegistry.sol"; import {IStakeRegistry} from "./interfaces/IStakeRegistry.sol"; import {IIndexRegistry} from "./interfaces/IIndexRegistry.sol"; @@ -22,31 +22,35 @@ contract OperatorStateRetriever { struct CheckSignaturesIndices { uint32[] nonSignerQuorumBitmapIndices; uint32[] quorumApkIndices; - uint32[] totalStakeIndices; + uint32[] totalStakeIndices; uint32[][] nonSignerStakeIndices; // nonSignerStakeIndices[quorumNumberIndex][nonSignerIndex] } + error OperatorNotRegistered(); + /** * @notice This function is intended to to be called by AVS operators every time a new task is created (i.e.) - * the AVS coordinator makes a request to AVS operators. Since all of the crucial information is kept onchain, + * the AVS coordinator makes a request to AVS operators. Since all of the crucial information is kept onchain, * operators don't need to run indexers to fetch the data. * @param registryCoordinator is the registry coordinator to fetch the AVS registry information from - * @param operatorId the id of the operator to fetch the quorums lists + * @param operatorId the id of the operator to fetch the quorums lists * @param blockNumber is the block number to get the operator state for * @return 1) the quorumBitmap of the operator at the given blockNumber - * 2) 2d array of Operator structs. For each quorum the provided operator + * 2) 2d array of Operator structs. For each quorum the provided operator * was a part of at `blockNumber`, an ordered list of operators. */ function getOperatorState( - IRegistryCoordinator registryCoordinator, - bytes32 operatorId, + ISlashingRegistryCoordinator registryCoordinator, + bytes32 operatorId, uint32 blockNumber ) external view returns (uint256, Operator[][] memory) { bytes32[] memory operatorIds = new bytes32[](1); operatorIds[0] = operatorId; - uint256 index = registryCoordinator.getQuorumBitmapIndicesAtBlockNumber(blockNumber, operatorIds)[0]; - - uint256 quorumBitmap = registryCoordinator.getQuorumBitmapAtBlockNumberByIndex(operatorId, blockNumber, index); + uint256 index = + registryCoordinator.getQuorumBitmapIndicesAtBlockNumber(blockNumber, operatorIds)[0]; + + uint256 quorumBitmap = + registryCoordinator.getQuorumBitmapAtBlockNumberByIndex(operatorId, blockNumber, index); bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); @@ -54,7 +58,7 @@ contract OperatorStateRetriever { } /** - * @notice returns the ordered list of operators (id and stake) for each quorum. The AVS coordinator + * @notice returns the ordered list of operators (id and stake) for each quorum. The AVS coordinator * may call this function directly to get the operator state for a given block number * @param registryCoordinator is the registry coordinator to fetch the AVS registry information from * @param quorumNumbers are the ids of the quorums to get the operator state for @@ -62,10 +66,10 @@ contract OperatorStateRetriever { * @return 2d array of Operators. For each quorum, an ordered list of Operators */ function getOperatorState( - IRegistryCoordinator registryCoordinator, - bytes memory quorumNumbers, + ISlashingRegistryCoordinator registryCoordinator, + bytes memory quorumNumbers, uint32 blockNumber - ) public view returns(Operator[][] memory) { + ) public view returns (Operator[][] memory) { IStakeRegistry stakeRegistry = registryCoordinator.stakeRegistry(); IIndexRegistry indexRegistry = registryCoordinator.indexRegistry(); IBLSApkRegistry blsApkRegistry = registryCoordinator.blsApkRegistry(); @@ -73,70 +77,81 @@ contract OperatorStateRetriever { Operator[][] memory operators = new Operator[][](quorumNumbers.length); for (uint256 i = 0; i < quorumNumbers.length; i++) { uint8 quorumNumber = uint8(quorumNumbers[i]); - bytes32[] memory operatorIds = indexRegistry.getOperatorListAtBlockNumber(quorumNumber, blockNumber); + bytes32[] memory operatorIds = + indexRegistry.getOperatorListAtBlockNumber(quorumNumber, blockNumber); operators[i] = new Operator[](operatorIds.length); for (uint256 j = 0; j < operatorIds.length; j++) { operators[i][j] = Operator({ operator: blsApkRegistry.getOperatorFromPubkeyHash(operatorIds[j]), operatorId: bytes32(operatorIds[j]), - stake: stakeRegistry.getStakeAtBlockNumber(bytes32(operatorIds[j]), quorumNumber, blockNumber) + stake: stakeRegistry.getStakeAtBlockNumber( + bytes32(operatorIds[j]), quorumNumber, blockNumber + ) }); } } - + return operators; } /** * @notice this is called by the AVS operator to get the relevant indices for the checkSignatures function - * if they are not running an indexer + * if they are not running an indexer * @param registryCoordinator is the registry coordinator to fetch the AVS registry information from * @param referenceBlockNumber is the block number to get the indices for * @param quorumNumbers are the ids of the quorums to get the operator state for * @param nonSignerOperatorIds are the ids of the nonsigning operators * @return 1) the indices of the quorumBitmaps for each of the operators in the @param nonSignerOperatorIds array at the given blocknumber * 2) the indices of the total stakes entries for the given quorums at the given blocknumber - * 3) the indices of the stakes of each of the nonsigners in each of the quorums they were a + * 3) the indices of the stakes of each of the nonsigners in each of the quorums they were a * part of (for each nonsigner, an array of length the number of quorums they were a part of * that are also part of the provided quorumNumbers) at the given blocknumber * 4) the indices of the quorum apks for each of the provided quorums at the given blocknumber */ function getCheckSignaturesIndices( - IRegistryCoordinator registryCoordinator, - uint32 referenceBlockNumber, - bytes calldata quorumNumbers, + ISlashingRegistryCoordinator registryCoordinator, + uint32 referenceBlockNumber, + bytes calldata quorumNumbers, bytes32[] calldata nonSignerOperatorIds ) external view returns (CheckSignaturesIndices memory) { IStakeRegistry stakeRegistry = registryCoordinator.stakeRegistry(); CheckSignaturesIndices memory checkSignaturesIndices; // get the indices of the quorumBitmap updates for each of the operators in the nonSignerOperatorIds array - checkSignaturesIndices.nonSignerQuorumBitmapIndices = registryCoordinator.getQuorumBitmapIndicesAtBlockNumber(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.getTotalStakeIndicesAtBlockNumber(referenceBlockNumber, quorumNumbers); - + checkSignaturesIndices.totalStakeIndices = + stakeRegistry.getTotalStakeIndicesAtBlockNumber(referenceBlockNumber, quorumNumbers); + checkSignaturesIndices.nonSignerStakeIndices = new uint32[][](quorumNumbers.length); - for (uint8 quorumNumberIndex = 0; quorumNumberIndex < quorumNumbers.length; quorumNumberIndex++) { + for ( + uint8 quorumNumberIndex = 0; + quorumNumberIndex < quorumNumbers.length; + quorumNumberIndex++ + ) { uint256 numNonSignersForQuorum = 0; // this array's length will be at most the number of nonSignerOperatorIds, this will be trimmed after it is filled - checkSignaturesIndices.nonSignerStakeIndices[quorumNumberIndex] = new uint32[](nonSignerOperatorIds.length); + checkSignaturesIndices.nonSignerStakeIndices[quorumNumberIndex] = + new uint32[](nonSignerOperatorIds.length); - for (uint i = 0; i < nonSignerOperatorIds.length; i++) { + for (uint256 i = 0; i < nonSignerOperatorIds.length; i++) { // get the quorumBitmap for the operator at the given blocknumber and index - uint192 nonSignerQuorumBitmap = - registryCoordinator.getQuorumBitmapAtBlockNumberByIndex( - nonSignerOperatorIds[i], - referenceBlockNumber, - checkSignaturesIndices.nonSignerQuorumBitmapIndices[i] - ); - - require(nonSignerQuorumBitmap != 0, "OperatorStateRetriever.getCheckSignaturesIndices: operator must be registered at blocknumber"); - + uint192 nonSignerQuorumBitmap = registryCoordinator + .getQuorumBitmapAtBlockNumberByIndex( + nonSignerOperatorIds[i], + referenceBlockNumber, + checkSignaturesIndices.nonSignerQuorumBitmapIndices[i] + ); + + require(nonSignerQuorumBitmap != 0, OperatorNotRegistered()); + // 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.getStakeUpdateIndexAtBlockNumber( + checkSignaturesIndices.nonSignerStakeIndices[quorumNumberIndex][numNonSignersForQuorum] + = stakeRegistry.getStakeUpdateIndexAtBlockNumber( nonSignerOperatorIds[i], uint8(quorumNumbers[quorumNumberIndex]), referenceBlockNumber @@ -147,15 +162,18 @@ contract OperatorStateRetriever { // resize the array to the number of nonSigners for this quorum uint32[] memory nonSignerStakeIndicesForQuorum = new uint32[](numNonSignersForQuorum); - for (uint i = 0; i < numNonSignersForQuorum; i++) { - nonSignerStakeIndicesForQuorum[i] = checkSignaturesIndices.nonSignerStakeIndices[quorumNumberIndex][i]; + for (uint256 i = 0; i < numNonSignersForQuorum; i++) { + nonSignerStakeIndicesForQuorum[i] = + checkSignaturesIndices.nonSignerStakeIndices[quorumNumberIndex][i]; } - checkSignaturesIndices.nonSignerStakeIndices[quorumNumberIndex] = nonSignerStakeIndicesForQuorum; + checkSignaturesIndices.nonSignerStakeIndices[quorumNumberIndex] = + nonSignerStakeIndicesForQuorum; } IBLSApkRegistry blsApkRegistry = registryCoordinator.blsApkRegistry(); // get the indices of the quorum apks for each of the provided quorums at the given blocknumber - checkSignaturesIndices.quorumApkIndices = blsApkRegistry.getApkIndicesAtBlockNumber(quorumNumbers, referenceBlockNumber); + checkSignaturesIndices.quorumApkIndices = + blsApkRegistry.getApkIndicesAtBlockNumber(quorumNumbers, referenceBlockNumber); return checkSignaturesIndices; } @@ -167,14 +185,17 @@ contract OperatorStateRetriever { * @param blockNumber is the block number to get the quorumBitmaps for */ function getQuorumBitmapsAtBlockNumber( - IRegistryCoordinator registryCoordinator, + ISlashingRegistryCoordinator registryCoordinator, bytes32[] memory operatorIds, uint32 blockNumber ) external view returns (uint256[] memory) { - uint32[] memory quorumBitmapIndices = registryCoordinator.getQuorumBitmapIndicesAtBlockNumber(blockNumber, operatorIds); + uint32[] memory quorumBitmapIndices = + registryCoordinator.getQuorumBitmapIndicesAtBlockNumber(blockNumber, operatorIds); uint256[] memory quorumBitmaps = new uint256[](operatorIds.length); for (uint256 i = 0; i < operatorIds.length; i++) { - quorumBitmaps[i] = registryCoordinator.getQuorumBitmapAtBlockNumberByIndex(operatorIds[i], blockNumber, quorumBitmapIndices[i]); + quorumBitmaps[i] = registryCoordinator.getQuorumBitmapAtBlockNumberByIndex( + operatorIds[i], blockNumber, quorumBitmapIndices[i] + ); } return quorumBitmaps; } @@ -186,7 +207,7 @@ contract OperatorStateRetriever { * @dev if an operator is not registered, the operatorId will be 0 */ function getBatchOperatorId( - IRegistryCoordinator registryCoordinator, + ISlashingRegistryCoordinator registryCoordinator, address[] memory operators ) external view returns (bytes32[] memory operatorIds) { operatorIds = new bytes32[](operators.length); @@ -202,7 +223,7 @@ contract OperatorStateRetriever { * @dev if an operator is not registered, the operator address will be 0 */ function getBatchOperatorFromId( - IRegistryCoordinator registryCoordinator, + ISlashingRegistryCoordinator registryCoordinator, bytes32[] memory operatorIds ) external view returns (address[] memory operators) { operators = new address[](operatorIds.length); @@ -210,5 +231,4 @@ contract OperatorStateRetriever { operators[i] = registryCoordinator.getOperatorFromId(operatorIds[i]); } } - } diff --git a/src/RegistryCoordinator.sol b/src/RegistryCoordinator.sol index a774fb19..325722ad 100644 --- a/src/RegistryCoordinator.sol +++ b/src/RegistryCoordinator.sol @@ -1,133 +1,70 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {IPauserRegistry} from "eigenlayer-contracts/src/contracts/interfaces/IPauserRegistry.sol"; -import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol"; -import {IBLSApkRegistry} from "./interfaces/IBLSApkRegistry.sol"; +import {IAllocationManager} from + "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; +import {IBLSApkRegistry, IBLSApkRegistryTypes} from "./interfaces/IBLSApkRegistry.sol"; import {IStakeRegistry} from "./interfaces/IStakeRegistry.sol"; import {IIndexRegistry} from "./interfaces/IIndexRegistry.sol"; import {IServiceManager} from "./interfaces/IServiceManager.sol"; import {IRegistryCoordinator} from "./interfaces/IRegistryCoordinator.sol"; import {ISocketRegistry} from "./interfaces/ISocketRegistry.sol"; -import {EIP1271SignatureUtils} from "eigenlayer-contracts/src/contracts/libraries/EIP1271SignatureUtils.sol"; import {BitmapUtils} from "./libraries/BitmapUtils.sol"; -import {BN254} from "./libraries/BN254.sol"; - +import {SlashingRegistryCoordinator} from "./SlashingRegistryCoordinator.sol"; +import {ISlashingRegistryCoordinator} from "./interfaces/ISlashingRegistryCoordinator.sol"; 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"; - -import {Pausable} from "eigenlayer-contracts/src/contracts/permissions/Pausable.sol"; -import {RegistryCoordinatorStorage} from "./RegistryCoordinatorStorage.sol"; /** * @title A `RegistryCoordinator` that has three registries: * 1) a `StakeRegistry` that keeps track of operators' stakes * 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 RegistryCoordinator is - EIP712, - Initializable, - Pausable, - OwnableUpgradeable, - RegistryCoordinatorStorage, - ISignatureUtils -{ +contract RegistryCoordinator is IRegistryCoordinator, SlashingRegistryCoordinator { using BitmapUtils for *; - using BN254 for BN254.G1Point; - - modifier onlyEjector { - _checkEjector(); - _; - } - /// @dev Checks that `quorumNumber` corresponds to a quorum that has been created - /// via `initialize` or `createQuorum` - modifier quorumExists(uint8 quorumNumber) { - _checkQuorumExists(quorumNumber); - _; - } + /// @notice the ServiceManager for this AVS, which forwards calls onto EigenLayer's core contracts + IServiceManager public immutable serviceManager; constructor( IServiceManager _serviceManager, IStakeRegistry _stakeRegistry, IBLSApkRegistry _blsApkRegistry, IIndexRegistry _indexRegistry, - ISocketRegistry _socketRegistry - ) - RegistryCoordinatorStorage(_serviceManager, _stakeRegistry, _blsApkRegistry, _indexRegistry, _socketRegistry) - EIP712("AVSRegistryCoordinator", "v0.0.1") + ISocketRegistry _socketRegistry, + IAllocationManager _allocationManager, + IPauserRegistry _pauserRegistry + ) + SlashingRegistryCoordinator( + _stakeRegistry, + _blsApkRegistry, + _indexRegistry, + _socketRegistry, + _allocationManager, + _pauserRegistry + ) { - _disableInitializers(); + serviceManager = _serviceManager; } /** - * @param _initialOwner will hold the owner role - * @param _churnApprover will hold the churnApprover role, which authorizes registering with churn - * @param _ejector will hold the ejector role, which can force-eject operators from quorums - * @param _pauserRegistry a registry of addresses that can pause the contract - * @param _initialPausedStatus pause status after calling initialize - * Config for initial quorums (see `createQuorum`): - * @param _operatorSetParams max operator count and operator churn parameters - * @param _minimumStakes minimum stake weight to allow an operator to register - * @param _strategyParams which Strategies/multipliers a quorum considers when calculating stake weight + * + * EXTERNAL FUNCTIONS + * */ - function initialize( - address _initialOwner, - address _churnApprover, - address _ejector, - IPauserRegistry _pauserRegistry, - uint256 _initialPausedStatus, - OperatorSetParam[] memory _operatorSetParams, - uint96[] memory _minimumStakes, - IStakeRegistry.StrategyParams[][] memory _strategyParams - ) external initializer { - require( - _operatorSetParams.length == _minimumStakes.length && _minimumStakes.length == _strategyParams.length, - "RegCoord.initialize: input length mismatch" - ); - - // Initialize roles - _transferOwnership(_initialOwner); - _initializePauser(_pauserRegistry, _initialPausedStatus); - _setChurnApprover(_churnApprover); - _setEjector(_ejector); - - // Add registry contracts to the registries array - registries.push(address(stakeRegistry)); - registries.push(address(blsApkRegistry)); - registries.push(address(indexRegistry)); - // Create quorums - for (uint256 i = 0; i < _operatorSetParams.length; i++) { - _createQuorum(_operatorSetParams[i], _minimumStakes[i], _strategyParams[i]); - } - } - - /******************************************************************************* - EXTERNAL FUNCTIONS - *******************************************************************************/ - - /** - * @notice Registers msg.sender as an operator for one or more quorums. If any quorum exceeds its maximum - * operator capacity after the operator is registered, 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 (typically an IP address) - * @param params contains the G1 & G2 public keys of the operator, and a signature proving their ownership - * @param operatorSignature is the signature of the operator used by the AVS to register the operator in the delegation manager - * @dev `params` is ignored if the caller has previously registered a public key - * @dev `operatorSignature` is ignored if the operator's status is already REGISTERED - */ + /// @inheritdoc IRegistryCoordinator function registerOperator( - bytes calldata quorumNumbers, - string calldata socket, - IBLSApkRegistry.PubkeyRegistrationParams calldata params, + bytes memory quorumNumbers, + string memory socket, + IBLSApkRegistryTypes.PubkeyRegistrationParams memory params, SignatureWithSaltAndExpiry memory operatorSignature ) external onlyWhenNotPaused(PAUSED_REGISTER_OPERATOR) { + require(!m2QuorumsDisabled, M2QuorumsAlreadyDisabled()); /** * If the operator has NEVER registered a pubkey before, use `params` to register * their pubkey in blsApkRegistry @@ -140,11 +77,10 @@ contract RegistryCoordinator is // Register the operator in each of the registry contracts and update the operator's // quorum bitmap and registration status uint32[] memory numOperatorsPerQuorum = _registerOperator({ - operator: msg.sender, + operator: msg.sender, operatorId: operatorId, - quorumNumbers: quorumNumbers, - socket: socket, - operatorSignature: operatorSignature + quorumNumbers: quorumNumbers, + socket: socket }).numOperatorsPerQuorum; // For each quorum, validate that the new operator count does not exceed the maximum @@ -154,33 +90,32 @@ contract RegistryCoordinator is require( numOperatorsPerQuorum[i] <= _quorumParams[quorumNumber].maxOperatorCount, - "RegCoord.registerOperator: operator count exceeds maximum" + MaxQuorumsReached() ); } + + // If the operator wasn't registered for any quorums, update their status + // and register them with this AVS in EigenLayer core (DelegationManager) + if (_operatorInfo[msg.sender].status != OperatorStatus.REGISTERED) { + _operatorInfo[msg.sender] = + OperatorInfo({operatorId: operatorId, status: OperatorStatus.REGISTERED}); + + serviceManager.registerOperatorToAVS(msg.sender, operatorSignature); + emit OperatorRegistered(msg.sender, operatorId); + } } - /** - * @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 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 over the `operatorKickParams` - * @param operatorSignature is the signature of the operator used by the AVS to register the operator in the delegation manager - * @dev `params` is ignored if the caller has previously registered a public key - * @dev `operatorSignature` is ignored if the operator's status is already REGISTERED - */ + /// @inheritdoc IRegistryCoordinator function registerOperatorWithChurn( - bytes calldata quorumNumbers, - string calldata socket, - IBLSApkRegistry.PubkeyRegistrationParams calldata params, - OperatorKickParam[] calldata operatorKickParams, + bytes calldata quorumNumbers, + string memory socket, + IBLSApkRegistryTypes.PubkeyRegistrationParams memory params, + OperatorKickParam[] memory operatorKickParams, SignatureWithSaltAndExpiry memory churnApproverSignature, SignatureWithSaltAndExpiry memory operatorSignature ) external onlyWhenNotPaused(PAUSED_REGISTER_OPERATOR) { - require(operatorKickParams.length == quorumNumbers.length, "RegCoord.registerOperatorWithChurn: input length mismatch"); - + require(!m2QuorumsDisabled, M2QuorumsAlreadyDisabled()); + /** * If the operator has NEVER registered a pubkey before, use `params` to register * their pubkey in blsApkRegistry @@ -190,776 +125,94 @@ contract RegistryCoordinator is */ bytes32 operatorId = _getOrCreateOperatorId(msg.sender, params); - // Verify the churn approver's signature for the registering operator and kick params - _verifyChurnApproverSignature({ - registeringOperator: msg.sender, - registeringOperatorId: operatorId, - operatorKickParams: operatorKickParams, - churnApproverSignature: churnApproverSignature - }); - - // Register the operator in each of the registry contracts and update the operator's - // quorum bitmap and registration status - RegisterResults memory results = _registerOperator({ + _registerOperatorWithChurn({ operator: msg.sender, operatorId: operatorId, quorumNumbers: quorumNumbers, socket: socket, - operatorSignature: operatorSignature + operatorKickParams: operatorKickParams, + churnApproverSignature: churnApproverSignature }); - // Check that each quorum's operator count is below the configured maximum. If the max - // is exceeded, use `operatorKickParams` to deregister an existing operator to make space - for (uint256 i = 0; i < quorumNumbers.length; i++) { - OperatorSetParam memory operatorSetParams = _quorumParams[uint8(quorumNumbers[i])]; - - /** - * 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: uint8(quorumNumbers[i]), - totalQuorumStake: results.totalStakes[i], - newOperator: msg.sender, - newOperatorStake: results.operatorStakes[i], - kickParams: operatorKickParams[i], - setParams: operatorSetParams - }); + // If the operator wasn't registered for any quorums, update their status + // and register them with this AVS in EigenLayer core (DelegationManager) + if (_operatorInfo[msg.sender].status != OperatorStatus.REGISTERED) { + _operatorInfo[msg.sender] = + OperatorInfo({operatorId: operatorId, status: OperatorStatus.REGISTERED}); - _deregisterOperator(operatorKickParams[i].operator, quorumNumbers[i:i+1]); - } + serviceManager.registerOperatorToAVS(msg.sender, operatorSignature); + emit OperatorRegistered(msg.sender, operatorId); } } - /** - * @notice Deregisters the caller from one or more quorums - * @param quorumNumbers is an ordered byte array containing the quorum numbers being deregistered from - */ + /// @inheritdoc IRegistryCoordinator function deregisterOperator( - bytes calldata quorumNumbers - ) external onlyWhenNotPaused(PAUSED_DEREGISTER_OPERATOR) { - _deregisterOperator({ - operator: msg.sender, - quorumNumbers: quorumNumbers - }); - } - - /** - * @notice Updates the StakeRegistry's view of one or more operators' stakes. If any operator - * is found to be below the minimum stake for the quorum, they are deregistered. - * @dev stakes are queried from the Eigenlayer core DelegationManager contract - * @param operators a list of operator addresses to update - */ - function updateOperators(address[] calldata operators) external onlyWhenNotPaused(PAUSED_UPDATE_OPERATOR) { - for (uint256 i = 0; i < operators.length; i++) { - address operator = operators[i]; - OperatorInfo memory operatorInfo = _operatorInfo[operator]; - bytes32 operatorId = operatorInfo.operatorId; - - // Update the operator's stake for their active quorums - uint192 currentBitmap = _currentOperatorBitmap(operatorId); - bytes memory quorumsToUpdate = BitmapUtils.bitmapToBytesArray(currentBitmap); - _updateOperator(operator, operatorInfo, quorumsToUpdate); - } - } - - /** - * @notice For each quorum in `quorumNumbers`, updates the StakeRegistry's view of ALL its registered operators' stakes. - * Each quorum's `quorumUpdateBlockNumber` is also updated, which tracks the most recent block number when ALL registered - * operators were updated. - * @dev stakes are queried from the Eigenlayer core DelegationManager contract - * @param operatorsPerQuorum for each quorum in `quorumNumbers`, this has a corresponding list of operators to update. - * @dev Each list of operator addresses MUST be sorted in ascending order - * @dev Each list of operator addresses MUST represent the entire list of registered operators for the corresponding quorum - * @param quorumNumbers is an ordered byte array containing the quorum numbers being updated - * @dev invariant: Each list of `operatorsPerQuorum` MUST be a sorted version of `IndexRegistry.getOperatorListAtBlockNumber` - * for the corresponding quorum. - * @dev note on race condition: if an operator registers/deregisters for any quorum in `quorumNumbers` after a txn to - * this method is broadcast (but before it is executed), the method will fail - */ - function updateOperatorsForQuorum( - address[][] calldata operatorsPerQuorum, - bytes calldata quorumNumbers - ) external onlyWhenNotPaused(PAUSED_UPDATE_OPERATOR) { - // Input validation - // - all quorums should exist (checked against `quorumCount` in orderedBytesArrayToBitmap) - // - there should be no duplicates in `quorumNumbers` - // - there should be one list of operators per quorum - uint192 quorumBitmap = uint192(BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers, quorumCount)); - require( - operatorsPerQuorum.length == quorumNumbers.length, - "RegCoord.updateOperatorsForQuorum: input length mismatch" - ); - - // For each quorum, update ALL registered operators - for (uint256 i = 0; i < quorumNumbers.length; ++i) { - uint8 quorumNumber = uint8(quorumNumbers[i]); - - // Ensure we've passed in the correct number of operators for this quorum - address[] calldata currQuorumOperators = operatorsPerQuorum[i]; + bytes memory quorumNumbers + ) external override onlyWhenNotPaused(PAUSED_DEREGISTER_OPERATOR) { + // Check that the quorum numbers are M2 quorums + for (uint256 i = 0; i < quorumNumbers.length; i++) { require( - currQuorumOperators.length == indexRegistry.totalOperatorsForQuorum(quorumNumber), - "RegCoord.updateOperatorsForQuorum: number of updated operators does not match quorum total" + !operatorSetsEnabled || _isM2Quorum(uint8(quorumNumbers[i])), OperatorSetQuorum() ); - - address prevOperatorAddress = address(0); - // For each operator: - // - check that they are registered for this quorum - // - check that their address is strictly greater than the last operator - // ... then, update their stakes - 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); - // Check that the operator is registered - require( - BitmapUtils.isSet(currentBitmap, quorumNumber), - "RegCoord.updateOperatorsForQuorum: operator not in quorum" - ); - // Prevent duplicate operators - require( - operator > prevOperatorAddress, - "RegCoord.updateOperatorsForQuorum: operators array must be sorted in ascending address order" - ); - } - - // Update the operator - _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); - } - } - - /** - * @notice Updates the socket of the msg.sender given they are a registered operator - * @param socket is the new socket of the operator - */ - function updateSocket(string memory socket) external { - require(_operatorInfo[msg.sender].status == OperatorStatus.REGISTERED, "RegCoord.updateSocket: operator not registered"); - _setOperatorSocket(_operatorInfo[msg.sender].operatorId, socket); - } - - /******************************************************************************* - EXTERNAL FUNCTIONS - EJECTOR - *******************************************************************************/ - - /** - * @notice Forcibly deregisters an operator from one or more quorums - * @param operator the operator to eject - * @param quorumNumbers the quorum numbers to eject the operator from - * @dev possible race condition if prior to being ejected for a set of quorums the operator self deregisters from a subset - */ - function ejectOperator( - address operator, - bytes calldata quorumNumbers - ) external onlyEjector { - lastEjectionTimestamp[operator] = block.timestamp; - - OperatorInfo storage operatorInfo = _operatorInfo[operator]; - bytes32 operatorId = operatorInfo.operatorId; - uint192 quorumsToRemove = uint192(BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers, quorumCount)); - uint192 currentBitmap = _currentOperatorBitmap(operatorId); - if( - operatorInfo.status == OperatorStatus.REGISTERED && - !quorumsToRemove.isEmpty() && - quorumsToRemove.isSubsetOf(currentBitmap) - ){ - _deregisterOperator({ - operator: operator, - quorumNumbers: quorumNumbers - }); } + _deregisterOperator({operator: msg.sender, quorumNumbers: quorumNumbers}); } - /******************************************************************************* - EXTERNAL FUNCTIONS - OWNER - *******************************************************************************/ - - /** - * @notice Creates a quorum and initializes it in each registry contract - * @param operatorSetParams configures the quorum's max operator count and churn parameters - * @param minimumStake sets the minimum stake required for an operator to register or remain - * registered - * @param strategyParams a list of strategies and multipliers used by the StakeRegistry to - * calculate an operator's stake weight for the quorum - */ - function createQuorum( - OperatorSetParam memory operatorSetParams, - uint96 minimumStake, - IStakeRegistry.StrategyParams[] memory strategyParams - ) external virtual onlyOwner { - _createQuorum(operatorSetParams, minimumStake, strategyParams); - } - - /** - * @notice Updates an existing quorum's configuration with a new max operator count - * and operator churn parameters - * @param quorumNumber the quorum number to update - * @param operatorSetParams the new config - * @dev only callable by the owner - */ - function setOperatorSetParams( - uint8 quorumNumber, - OperatorSetParam memory operatorSetParams - ) external onlyOwner quorumExists(quorumNumber) { - _setOperatorSetParams(quorumNumber, operatorSetParams); - } - - /** - * @notice Sets the churnApprover, which approves operator registration with churn - * (see `registerOperatorWithChurn`) - * @param _churnApprover the new churn approver - * @dev only callable by the owner - */ - function setChurnApprover(address _churnApprover) external onlyOwner { - _setChurnApprover(_churnApprover); - } - - /** - * @notice Sets the ejector, which can force-deregister operators from quorums - * @param _ejector the new ejector - * @dev only callable by the owner - */ - function setEjector(address _ejector) external onlyOwner { - _setEjector(_ejector); - } + /// @inheritdoc IRegistryCoordinator + function enableOperatorSets() external onlyOwner { + require(!operatorSetsEnabled, OperatorSetsAlreadyEnabled()); - /** - * @notice Sets the ejection cooldown, which is the time an operator must wait in - * seconds afer ejection before registering for any quorum - * @param _ejectionCooldown the new ejection cooldown in seconds - * @dev only callable by the owner - */ - function setEjectionCooldown(uint256 _ejectionCooldown) external onlyOwner { - ejectionCooldown = _ejectionCooldown; - } + // Set the bitmap for M2 quorums + M2quorumBitmap = _getQuorumBitmap(quorumCount); - /******************************************************************************* - INTERNAL FUNCTIONS - *******************************************************************************/ + // Enable operator sets mode + operatorSetsEnabled = true; - struct RegisterResults { - uint32[] numOperatorsPerQuorum; - uint96[] operatorStakes; - uint96[] totalStakes; + emit OperatorSetsEnabled(); } - /** - * @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, - bytes32 operatorId, - bytes calldata quorumNumbers, - string memory socket, - SignatureWithSaltAndExpiry memory operatorSignature - ) 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 - * - the quorums we're registering for exist (checked against `quorumCount` in orderedBytesArrayToBitmap) - * - the operator is not currently registered for any quorums we're registering for - * Then, calculate the operator's new bitmap after registration - */ - uint192 quorumsToAdd = uint192(BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers, quorumCount)); - uint192 currentBitmap = _currentOperatorBitmap(operatorId); - require(!quorumsToAdd.isEmpty(), "RegCoord._registerOperator: bitmap cannot be 0"); - require(quorumsToAdd.noBitsInCommon(currentBitmap), "RegCoord._registerOperator: operator already registered for some quorums"); - uint192 newBitmap = uint192(currentBitmap.plus(quorumsToAdd)); - - // Check that the operator can reregister if ejected - require(lastEjectionTimestamp[operator] + ejectionCooldown < block.timestamp, "RegCoord._registerOperator: operator cannot reregister yet"); - - /** - * Update operator's bitmap, socket, and status. Only update operatorInfo if needed: - * if we're `REGISTERED`, the operatorId and status are already correct. - */ - _updateOperatorBitmap({ - operatorId: operatorId, - newBitmap: newBitmap - }); - - // If the operator wasn't registered for any quorums, update their status - // and register them with this AVS in EigenLayer core (DelegationManager) - if (_operatorInfo[operator].status != OperatorStatus.REGISTERED) { - _operatorInfo[operator] = OperatorInfo({ - operatorId: operatorId, - status: OperatorStatus.REGISTERED - }); - - // Register the operator with the EigenLayer core contracts via this AVS's ServiceManager - serviceManager.registerOperatorToAVS(operator, operatorSignature); + /// @inheritdoc IRegistryCoordinator + function disableM2QuorumRegistration() external onlyOwner { + require(operatorSetsEnabled, OperatorSetsNotEnabled()); - _setOperatorSocket(operatorId, socket); - - emit OperatorRegistered(operator, operatorId); - } - - // Register the operator with the BLSApkRegistry, StakeRegistry, and IndexRegistry - blsApkRegistry.registerOperator(operator, quorumNumbers); - (results.operatorStakes, results.totalStakes) = - stakeRegistry.registerOperator(operator, operatorId, quorumNumbers); - results.numOperatorsPerQuorum = indexRegistry.registerOperator(operatorId, quorumNumbers); - - return results; - } - - /** - * @notice Checks if the caller is the ejector - * @dev Reverts if the caller is not the ejector - */ - function _checkEjector() internal view { - require(msg.sender == ejector, "RegCoord.onlyEjector: caller is not the ejector"); - } + m2QuorumsDisabled = true; - /** - * @notice Checks if a quorum exists - * @param quorumNumber The quorum number to check - * @dev Reverts if the quorum does not exist - */ - function _checkQuorumExists(uint8 quorumNumber) internal view { - require( - quorumNumber < quorumCount, - "RegCoord.quorumExists: quorum does not exist" - ); + emit M2QuorumsDisabled(); } - /** - * @notice Fetches an operator's pubkey hash from the BLSApkRegistry. If the - * operator has not registered a pubkey, attempts to register a pubkey using - * `params` - * @param operator the operator whose pubkey to query from the BLSApkRegistry - * @param params contains the G1 & G2 public keys of the operator, and a signature proving their ownership - * @dev `params` can be empty if the operator has already registered a pubkey in the BLSApkRegistry - */ - function _getOrCreateOperatorId( + /// @dev Hook to allow for any post-deregister logic + function _afterDeregisterOperator( address operator, - IBLSApkRegistry.PubkeyRegistrationParams calldata params - ) internal returns (bytes32 operatorId) { - operatorId = blsApkRegistry.getOperatorId(operator); - if (operatorId == 0) { - operatorId = blsApkRegistry.registerBLSPublicKey(operator, params, pubkeyRegistrationMessageHash(operator)); - } - return operatorId; - } - - /** - * @notice Validates that an incoming operator is eligible to replace an existing - * operator based on the stake of both - * @dev In order to churn, the incoming operator needs to have more stake than the - * existing operator by a proportion given by `kickBIPsOfOperatorStake` - * @dev In order to be churned out, the existing operator needs to have a proportion - * of the total quorum stake less than `kickBIPsOfTotalStake` - * @param quorumNumber `newOperator` is trying to replace an operator in this quorum - * @param totalQuorumStake the total stake of all operators in the quorum, after the - * `newOperator` registers - * @param newOperator the incoming operator - * @param newOperatorStake the incoming operator's stake - * @param kickParams the quorum number and existing operator to replace - * @dev the existing operator's registration to this quorum isn't checked here, but - * if we attempt to deregister them, this will be checked in `_deregisterOperator` - * @param setParams config for this quorum containing `kickBIPsX` stake proportions - * mentioned above - */ - 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(newOperator != operatorToKick, "RegCoord._validateChurn: cannot churn self"); - require(kickParams.quorumNumber == quorumNumber, "RegCoord._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), - "RegCoord._validateChurn: incoming operator has insufficient stake for churn" - ); - require( - operatorToKickStake < _totalKickThreshold(totalQuorumStake, setParams), - "RegCoord._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 BLSApkRegistry, IndexRegistry, and StakeRegistry - */ - function _deregisterOperator( - address operator, - bytes memory quorumNumbers - ) internal virtual { - // Fetch the operator's info and ensure they are registered - OperatorInfo storage operatorInfo = _operatorInfo[operator]; - bytes32 operatorId = operatorInfo.operatorId; - require(operatorInfo.status == OperatorStatus.REGISTERED, "RegCoord._deregisterOperator: operator is not registered"); - - /** - * 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 quorums we're deregistering from exist (checked against `quorumCount` in orderedBytesArrayToBitmap) - * - the operator is currently registered for any quorums we're trying to deregister from - * Then, calculate the operator's new bitmap after deregistration - */ - uint192 quorumsToRemove = uint192(BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers, quorumCount)); - uint192 currentBitmap = _currentOperatorBitmap(operatorId); - require(!quorumsToRemove.isEmpty(), "RegCoord._deregisterOperator: bitmap cannot be 0"); - require(quorumsToRemove.isSubsetOf(currentBitmap), "RegCoord._deregisterOperator: operator is not registered for quorums"); - uint192 newBitmap = uint192(currentBitmap.minus(quorumsToRemove)); - - // Update operator's bitmap and status - _updateOperatorBitmap({ - operatorId: operatorId, - newBitmap: newBitmap - }); - - // If the operator is no longer registered for any quorums, update their status and deregister + bytes32 operatorId, + bytes memory quorumNumbers, + uint192 newBitmap + ) internal virtual override { + uint256 operatorM2QuorumBitmap = newBitmap.minus(M2quorumBitmap); + // If the operator is no longer registered for any M2 quorums, update their status and deregister // them from the AVS via the EigenLayer core contracts - if (newBitmap.isEmpty()) { - operatorInfo.status = OperatorStatus.DEREGISTERED; + if (operatorM2QuorumBitmap.isEmpty()) { serviceManager.deregisterOperatorFromAVS(operator); - emit OperatorDeregistered(operator, operatorId); } - - // Deregister operator with each of the registry contracts - blsApkRegistry.deregisterOperator(operator, quorumNumbers); - stakeRegistry.deregisterOperator(operatorId, quorumNumbers); - indexRegistry.deregisterOperator(operatorId, quorumNumbers); } - /** - * @notice Updates the StakeRegistry's view of the operator's stake in one or more quorums. - * For any quorums where the StakeRegistry finds the operator is under the configured minimum - * stake, `quorumsToRemove` is returned and used to deregister the operator from those quorums - * @dev 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. - */ - 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 - function _verifyChurnApproverSignature( - address registeringOperator, - bytes32 registeringOperatorId, - OperatorKickParam[] memory operatorKickParams, - SignatureWithSaltAndExpiry memory churnApproverSignature - ) internal { - // make sure the salt hasn't been used already - require(!isChurnApproverSaltUsed[churnApproverSignature.salt], "RegCoord._verifyChurnApproverSignature: churnApprover salt already used"); - require(churnApproverSignature.expiry >= block.timestamp, "RegCoord._verifyChurnApproverSignature: churnApprover signature expired"); - - // set salt used to true - isChurnApproverSaltUsed[churnApproverSignature.salt] = true; - - // check the churnApprover's signature - EIP1271SignatureUtils.checkSignature_EIP1271( - churnApprover, - calculateOperatorChurnApprovalDigestHash(registeringOperator, registeringOperatorId, operatorKickParams, churnApproverSignature.salt, churnApproverSignature.expiry), - churnApproverSignature.signature - ); - } - - /** - * @notice Creates a quorum and initializes it in each registry contract - * @param operatorSetParams configures the quorum's max operator count and churn parameters - * @param minimumStake sets the minimum stake required for an operator to register or remain - * registered - * @param strategyParams a list of strategies and multipliers used by the StakeRegistry to - * calculate an operator's stake weight for the quorum - */ - function _createQuorum( - OperatorSetParam memory operatorSetParams, - uint96 minimumStake, - IStakeRegistry.StrategyParams[] memory strategyParams - ) internal { - // Increment the total quorum count. Fails if we're already at the max - uint8 prevQuorumCount = quorumCount; - require(prevQuorumCount < MAX_QUORUM_COUNT, "RegCoord.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.initializeQuorum(quorumNumber, minimumStake, strategyParams); - indexRegistry.initializeQuorum(quorumNumber); - blsApkRegistry.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; - } - } - - /** - * @notice Returns the index of the quorumBitmap for the provided `operatorId` at the given `blockNumber` - * @dev Reverts if the operator had not yet (ever) registered at `blockNumber` - * @dev This function is designed to find proper inputs to the `getQuorumBitmapAtBlockNumberByIndex` function - */ - function _getQuorumBitmapIndexAtBlockNumber( - uint32 blockNumber, - bytes32 operatorId - ) internal view returns (uint32 index) { - uint256 length = _operatorBitmapHistory[operatorId].length; - - // Traverse the operator's bitmap history in reverse, returning the first index - // corresponding to an update made before or at `blockNumber` - for (uint256 i = 0; i < length; i++) { - index = uint32(length - i - 1); - - if (_operatorBitmapHistory[operatorId][index].updateBlockNumber <= blockNumber) { - return index; - } - } - - revert( - "RegCoord.getQuorumBitmapIndexAtBlockNumber: no bitmap update found for operator at blockNumber" - ); - } - - function _setOperatorSetParams(uint8 quorumNumber, OperatorSetParam memory operatorSetParams) internal { - _quorumParams[quorumNumber] = operatorSetParams; - emit OperatorSetParamsUpdated(quorumNumber, operatorSetParams); - } - - function _setChurnApprover(address newChurnApprover) internal { - emit ChurnApproverUpdated(churnApprover, newChurnApprover); - churnApprover = newChurnApprover; - } - - function _setEjector(address newEjector) internal { - emit EjectorUpdated(ejector, newEjector); - ejector = newEjector; - } - - function _setOperatorSocket(bytes32 operatorId, string memory socket) internal { - socketRegistry.setOperatorSocket(operatorId, socket); - emit OperatorSocketUpdate(operatorId, socket); - } - - /******************************************************************************* - VIEW FUNCTIONS - *******************************************************************************/ - - /// @notice Returns the operator set params for the given `quorumNumber` - function getOperatorSetParams(uint8 quorumNumber) external view returns (OperatorSetParam memory) { - return _quorumParams[quorumNumber]; - } - - /// @notice Returns the operator struct for the given `operator` - function getOperator(address operator) external view returns (OperatorInfo memory) { - return _operatorInfo[operator]; - } - - /// @notice Returns the operatorId for the given `operator` - function getOperatorId(address operator) external view returns (bytes32) { - return _operatorInfo[operator].operatorId; - } - - /// @notice Returns the operator address for the given `operatorId` - function getOperatorFromId(bytes32 operatorId) external view returns (address) { - return blsApkRegistry.getOperatorFromPubkeyHash(operatorId); - } - - /// @notice Returns the status for the given `operator` - function getOperatorStatus(address operator) external view returns (IRegistryCoordinator.OperatorStatus) { - return _operatorInfo[operator].status; - } - - /** - * @notice Returns the indices of the quorumBitmaps for the provided `operatorIds` at the given `blockNumber` - * @dev Reverts if any of the `operatorIds` was not (yet) registered at `blockNumber` - * @dev This function is designed to find proper inputs to the `getQuorumBitmapAtBlockNumberByIndex` function - */ - function getQuorumBitmapIndicesAtBlockNumber( - uint32 blockNumber, - bytes32[] memory operatorIds - ) external view returns (uint32[] memory) { - uint32[] memory indices = new uint32[](operatorIds.length); - for (uint256 i = 0; i < operatorIds.length; i++) { - indices[i] = _getQuorumBitmapIndexAtBlockNumber(blockNumber, operatorIds[i]); - } - return indices; - } - - /** - * @notice Returns the quorum bitmap for the given `operatorId` at the given `blockNumber` via the `index`, - * reverting if `index` is incorrect - * @dev This function is meant to be used in concert with `getQuorumBitmapIndicesAtBlockNumber`, which - * helps off-chain processes to fetch the correct `index` input - */ - function getQuorumBitmapAtBlockNumberByIndex( - bytes32 operatorId, - uint32 blockNumber, - 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( - blockNumber >= quorumBitmapUpdate.updateBlockNumber, - "RegCoord.getQuorumBitmapAtBlockNumberByIndex: quorumBitmapUpdate is from after blockNumber" - ); - require( - quorumBitmapUpdate.nextUpdateBlockNumber == 0 || blockNumber < quorumBitmapUpdate.nextUpdateBlockNumber, - "RegCoord.getQuorumBitmapAtBlockNumberByIndex: quorumBitmapUpdate is from before blockNumber" - ); - - return quorumBitmapUpdate.quorumBitmap; - } - - /// @notice Returns the `index`th entry in the operator with `operatorId`'s bitmap history - function getQuorumBitmapUpdateByIndex( - bytes32 operatorId, - uint256 index - ) external view returns (QuorumBitmapUpdate memory) { - 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 getCurrentQuorumBitmap(bytes32 operatorId) external view returns (uint192) { - return _currentOperatorBitmap(operatorId); - } - - /// @notice Returns the length of the quorum bitmap history for the given `operatorId` - function getQuorumBitmapHistoryLength(bytes32 operatorId) external view returns (uint256) { - return _operatorBitmapHistory[operatorId].length; - } - - /// @notice Returns the number of registries - function numRegistries() external view returns (uint256) { - return registries.length; - } - - /** - * @notice Public function for the the churnApprover signature hash calculation when operators are being kicked from quorums - * @param registeringOperatorId The id of the registering operator - * @param operatorKickParams The parameters needed to kick the operator from the quorums that have reached their caps - * @param salt The salt to use for the churnApprover's signature - * @param expiry The desired expiry time of the churnApprover's signature - */ - function calculateOperatorChurnApprovalDigestHash( - address registeringOperator, - bytes32 registeringOperatorId, - OperatorKickParam[] memory operatorKickParams, - bytes32 salt, - uint256 expiry - ) public view returns (bytes32) { - // calculate the digest hash - return _hashTypedDataV4(keccak256(abi.encode(OPERATOR_CHURN_APPROVAL_TYPEHASH, registeringOperator, 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 Returns a bitmap with all bits set up to `quorumCount`. Used for bit-masking quorum numbers + /// and differentiating between operator sets and M2 quorums + function _getQuorumBitmap( + uint256 quorumCount + ) internal pure returns (uint256) { + // This creates a number where all bits up to quorumCount are set to 1 + // For example: + // quorumCount = 3 -> 0111 (7 in decimal) + // quorumCount = 5 -> 011111 (31 in decimal) + // This is a safe operation since we limit MAX_QUORUM_COUNT to 192 + return (1 << quorumCount) - 1; } /// @dev need to override function here since its defined in both these contracts function owner() public view - override(OwnableUpgradeable, IRegistryCoordinator) + override(SlashingRegistryCoordinator, ISlashingRegistryCoordinator) returns (address) { return OwnableUpgradeable.owner(); diff --git a/src/ServiceManagerBase.sol b/src/ServiceManagerBase.sol index 65a5a152..d11e7197 100644 --- a/src/ServiceManagerBase.sol +++ b/src/ServiceManagerBase.sol @@ -1,18 +1,30 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {Initializable} from "@openzeppelin-upgrades/contracts/proxy/utils/Initializable.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol"; import {IAVSDirectory} from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol"; -import {IRewardsCoordinator} from "eigenlayer-contracts/src/contracts/interfaces/IRewardsCoordinator.sol"; +import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; +import {IRewardsCoordinator} from + "eigenlayer-contracts/src/contracts/interfaces/IRewardsCoordinator.sol"; +import { + IAllocationManager, + IAllocationManagerTypes +} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; +import {IPermissionController} from + "eigenlayer-contracts/src/contracts/interfaces/IPermissionController.sol"; +import {IPermissionController} from + "eigenlayer-contracts/src/contracts/interfaces/IPermissionController.sol"; import {ServiceManagerBaseStorage} from "./ServiceManagerBaseStorage.sol"; import {IServiceManager} from "./interfaces/IServiceManager.sol"; -import {IRegistryCoordinator} from "./interfaces/IRegistryCoordinator.sol"; +import {ISlashingRegistryCoordinator} from "./interfaces/ISlashingRegistryCoordinator.sol"; import {IStakeRegistry} from "./interfaces/IStakeRegistry.sol"; + import {BitmapUtils} from "./libraries/BitmapUtils.sol"; +import {LibMergeSort} from "./libraries/LibMergeSort.sol"; /** * @title Minimal implementation of a ServiceManager-type contract. @@ -25,10 +37,7 @@ abstract contract ServiceManagerBase is ServiceManagerBaseStorage { /// @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" - ); + require(msg.sender == address(_registryCoordinator), OnlyRegistryCoordinator()); _; } @@ -38,25 +47,22 @@ abstract contract ServiceManagerBase is ServiceManagerBaseStorage { _; } - function _checkRewardsInitiator() internal view { - require( - msg.sender == rewardsInitiator, - "ServiceManagerBase.onlyRewardsInitiator: caller is not the rewards initiator" - ); - } - /// @notice Sets the (immutable) `_registryCoordinator` address constructor( IAVSDirectory __avsDirectory, IRewardsCoordinator __rewardsCoordinator, - IRegistryCoordinator __registryCoordinator, - IStakeRegistry __stakeRegistry + ISlashingRegistryCoordinator __registryCoordinator, + IStakeRegistry __stakeRegistry, + IPermissionController __permissionController, + IAllocationManager __allocationManager ) ServiceManagerBaseStorage( __avsDirectory, __rewardsCoordinator, __registryCoordinator, - __stakeRegistry + __stakeRegistry, + __permissionController, + __allocationManager ) { _disableInitializers(); @@ -70,6 +76,51 @@ abstract contract ServiceManagerBase is ServiceManagerBaseStorage { _setRewardsInitiator(_rewardsInitiator); } + /// @inheritdoc IServiceManager + function addPendingAdmin( + address admin + ) external onlyOwner { + _permissionController.addPendingAdmin({account: address(this), admin: admin}); + } + + /// @inheritdoc IServiceManager + function removePendingAdmin( + address pendingAdmin + ) external onlyOwner { + _permissionController.removePendingAdmin({account: address(this), admin: pendingAdmin}); + } + + /// @inheritdoc IServiceManager + function removeAdmin( + address admin + ) external onlyOwner { + _permissionController.removeAdmin({account: address(this), admin: admin}); + } + + /// @inheritdoc IServiceManager + function setAppointee(address appointee, address target, bytes4 selector) external onlyOwner { + _permissionController.setAppointee({ + account: address(this), + appointee: appointee, + target: target, + selector: selector + }); + } + + /// @inheritdoc IServiceManager + function removeAppointee( + address appointee, + address target, + bytes4 selector + ) external onlyOwner { + _permissionController.removeAppointee({ + account: address(this), + appointee: appointee, + target: target, + selector: selector + }); + } + /** * @notice Updates the metadata URI for the AVS * @param _metadataURI is the metadata URI for the AVS @@ -101,13 +152,10 @@ abstract contract ServiceManagerBase is ServiceManagerBaseStorage { // transfer token to ServiceManager and approve RewardsCoordinator to transfer again // in createAVSRewardsSubmission() call rewardsSubmissions[i].token.safeTransferFrom( - msg.sender, - address(this), - rewardsSubmissions[i].amount + msg.sender, address(this), rewardsSubmissions[i].amount ); rewardsSubmissions[i].token.safeIncreaseAllowance( - address(_rewardsCoordinator), - rewardsSubmissions[i].amount + address(_rewardsCoordinator), rewardsSubmissions[i].amount ); } @@ -129,43 +177,30 @@ abstract contract ServiceManagerBase is ServiceManagerBaseStorage { * smaller array of submissions if necessary. */ function createOperatorDirectedAVSRewardsSubmission( - IRewardsCoordinator.OperatorDirectedRewardsSubmission[] - calldata operatorDirectedRewardsSubmissions + IRewardsCoordinator.OperatorDirectedRewardsSubmission[] calldata + operatorDirectedRewardsSubmissions ) public virtual onlyRewardsInitiator { - for ( - uint256 i = 0; - i < operatorDirectedRewardsSubmissions.length; - ++i - ) { + for (uint256 i = 0; i < operatorDirectedRewardsSubmissions.length; ++i) { // Calculate total amount of token to transfer uint256 totalAmount = 0; for ( - uint256 j = 0; - j < - operatorDirectedRewardsSubmissions[i].operatorRewards.length; - ++j + uint256 j = 0; j < operatorDirectedRewardsSubmissions[i].operatorRewards.length; ++j ) { - totalAmount += operatorDirectedRewardsSubmissions[i] - .operatorRewards[j] - .amount; + totalAmount += operatorDirectedRewardsSubmissions[i].operatorRewards[j].amount; } // Transfer token to ServiceManager and approve RewardsCoordinator to transfer again // in createOperatorDirectedAVSRewardsSubmission() call operatorDirectedRewardsSubmissions[i].token.safeTransferFrom( - msg.sender, - address(this), - totalAmount + msg.sender, address(this), totalAmount ); operatorDirectedRewardsSubmissions[i].token.safeIncreaseAllowance( - address(_rewardsCoordinator), - totalAmount + address(_rewardsCoordinator), totalAmount ); } _rewardsCoordinator.createOperatorDirectedAVSRewardsSubmission( - address(this), - operatorDirectedRewardsSubmissions + address(this), operatorDirectedRewardsSubmissions ); } @@ -174,7 +209,9 @@ abstract contract ServiceManagerBase is ServiceManagerBaseStorage { * @param claimer The address of the entity that can call `processClaim` on behalf of the earner * @dev Only callable by the owner. */ - function setClaimerFor(address claimer) public virtual onlyOwner { + function setClaimerFor( + address claimer + ) public virtual onlyOwner { _rewardsCoordinator.setClaimerFor(claimer); } @@ -200,6 +237,18 @@ abstract contract ServiceManagerBase is ServiceManagerBaseStorage { _avsDirectory.deregisterOperatorFromAVS(operator); } + function deregisterOperatorFromOperatorSets( + address operator, + uint32[] memory operatorSetIds + ) public virtual onlyRegistryCoordinator { + IAllocationManager.DeregisterParams memory params = IAllocationManagerTypes.DeregisterParams({ + operator: operator, + avs: address(this), + operatorSetIds: operatorSetIds + }); + _allocationManager.deregisterFromOperatorSets(params); + } + /** * @notice Sets the rewards initiator address * @param newRewardsInitiator The new rewards initiator address @@ -211,7 +260,9 @@ abstract contract ServiceManagerBase is ServiceManagerBaseStorage { _setRewardsInitiator(newRewardsInitiator); } - function _setRewardsInitiator(address newRewardsInitiator) internal { + function _setRewardsInitiator( + address newRewardsInitiator + ) internal { emit RewardsInitiatorUpdated(rewardsInitiator, newRewardsInitiator); rewardsInitiator = newRewardsInitiator; } @@ -222,12 +273,7 @@ abstract contract ServiceManagerBase is ServiceManagerBaseStorage { * @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 - virtual - returns (address[] memory) - { + function getRestakeableStrategies() external view virtual returns (address[] memory) { uint256 quorumCount = _registryCoordinator.quorumCount(); if (quorumCount == 0) { @@ -242,13 +288,10 @@ abstract contract ServiceManagerBase is ServiceManagerBaseStorage { address[] memory restakedStrategies = new address[](strategyCount); uint256 index = 0; for (uint256 i = 0; i < _registryCoordinator.quorumCount(); i++) { - uint256 strategyParamsLength = _stakeRegistry.strategyParamsLength( - uint8(i) - ); + uint256 strategyParamsLength = _stakeRegistry.strategyParamsLength(uint8(i)); for (uint256 j = 0; j < strategyParamsLength; j++) { - restakedStrategies[index] = address( - _stakeRegistry.strategyParamsByIndex(uint8(i), j).strategy - ); + restakedStrategies[index] = + address(_stakeRegistry.strategyParamsByIndex(uint8(i), j).strategy); index++; } } @@ -266,23 +309,17 @@ abstract contract ServiceManagerBase is ServiceManagerBaseStorage { address operator ) external view virtual returns (address[] memory) { bytes32 operatorId = _registryCoordinator.getOperatorId(operator); - uint192 operatorBitmap = _registryCoordinator.getCurrentQuorumBitmap( - operatorId - ); + 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 - ); + bytes memory operatorRestakedQuorums = BitmapUtils.bitmapToBytesArray(operatorBitmap); uint256 strategyCount; for (uint256 i = 0; i < operatorRestakedQuorums.length; i++) { - strategyCount += _stakeRegistry.strategyParamsLength( - uint8(operatorRestakedQuorums[i]) - ); + strategyCount += _stakeRegistry.strategyParamsLength(uint8(operatorRestakedQuorums[i])); } // Get strategies for each quorum in operator bitmap @@ -290,13 +327,10 @@ abstract contract ServiceManagerBase is ServiceManagerBaseStorage { uint256 index = 0; for (uint256 i = 0; i < operatorRestakedQuorums.length; i++) { uint8 quorum = uint8(operatorRestakedQuorums[i]); - uint256 strategyParamsLength = _stakeRegistry.strategyParamsLength( - quorum - ); + uint256 strategyParamsLength = _stakeRegistry.strategyParamsLength(quorum); for (uint256 j = 0; j < strategyParamsLength; j++) { - restakedStrategies[index] = address( - _stakeRegistry.strategyParamsByIndex(quorum, j).strategy - ); + restakedStrategies[index] = + address(_stakeRegistry.strategyParamsByIndex(quorum, j).strategy); index++; } } @@ -307,4 +341,8 @@ abstract contract ServiceManagerBase is ServiceManagerBaseStorage { function avsDirectory() external view override returns (address) { return address(_avsDirectory); } + + function _checkRewardsInitiator() internal view { + require(msg.sender == rewardsInitiator, OnlyRewardsInitiator()); + } } diff --git a/src/ServiceManagerBaseStorage.sol b/src/ServiceManagerBaseStorage.sol index e0c1d86a..4fdd0039 100644 --- a/src/ServiceManagerBaseStorage.sol +++ b/src/ServiceManagerBaseStorage.sol @@ -1,14 +1,21 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {OwnableUpgradeable} from "@openzeppelin-upgrades/contracts/access/OwnableUpgradeable.sol"; import {IServiceManager} from "./interfaces/IServiceManager.sol"; -import {IRegistryCoordinator} from "./interfaces/IRegistryCoordinator.sol"; +import {ISlashingRegistryCoordinator} from "./interfaces/ISlashingRegistryCoordinator.sol"; import {IStakeRegistry} from "./interfaces/IStakeRegistry.sol"; import {IAVSDirectory} from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol"; -import {IRewardsCoordinator} from "eigenlayer-contracts/src/contracts/interfaces/IRewardsCoordinator.sol"; +import {IAllocationManager} from + "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; +import {IRewardsCoordinator} from + "eigenlayer-contracts/src/contracts/interfaces/IRewardsCoordinator.sol"; +import {IAllocationManager} from + "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; +import {IPermissionController} from + "eigenlayer-contracts/src/contracts/interfaces/IPermissionController.sol"; /** * @title Storage variables for the `ServiceManagerBase` contract. @@ -22,9 +29,11 @@ abstract contract ServiceManagerBaseStorage is IServiceManager, OwnableUpgradeab * */ IAVSDirectory internal immutable _avsDirectory; + IAllocationManager internal immutable _allocationManager; IRewardsCoordinator internal immutable _rewardsCoordinator; - IRegistryCoordinator internal immutable _registryCoordinator; + ISlashingRegistryCoordinator internal immutable _registryCoordinator; IStakeRegistry internal immutable _stakeRegistry; + IPermissionController internal immutable _permissionController; /** * @@ -35,17 +44,21 @@ abstract contract ServiceManagerBaseStorage is IServiceManager, OwnableUpgradeab /// @notice The address of the entity that can initiate rewards address public rewardsInitiator; - /// @notice Sets the (immutable) `_avsDirectory`, `_rewardsCoordinator`, `_registryCoordinator`, and `_stakeRegistry` addresses + /// @notice Sets the (immutable) `_avsDirectory`, `_rewardsCoordinator`, `_registryCoordinator`, `_stakeRegistry`, and `_allocationManager` addresses constructor( IAVSDirectory __avsDirectory, IRewardsCoordinator __rewardsCoordinator, - IRegistryCoordinator __registryCoordinator, - IStakeRegistry __stakeRegistry + ISlashingRegistryCoordinator __registryCoordinator, + IStakeRegistry __stakeRegistry, + IPermissionController __permissionController, + IAllocationManager __allocationManager ) { _avsDirectory = __avsDirectory; _rewardsCoordinator = __rewardsCoordinator; _registryCoordinator = __registryCoordinator; _stakeRegistry = __stakeRegistry; + _permissionController = __permissionController; + _allocationManager = __allocationManager; } // storage gap for upgradeability diff --git a/src/ServiceManagerRouter.sol b/src/ServiceManagerRouter.sol index e2259cfb..2589ea74 100644 --- a/src/ServiceManagerRouter.sol +++ b/src/ServiceManagerRouter.sol @@ -1,27 +1,27 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {IServiceManagerUI} from "./interfaces/IServiceManagerUI.sol"; /** * @title Contract that proxies calls to a ServiceManager contract. * This contract is designed to be used by off-chain services which need - * errors to be handled gracefully. + * errors to be handled gracefully. * @author Layr Labs, Inc. */ - contract ServiceManagerRouter { - - address public constant FAILED_CALL_ADDRESS = address(0x000000000000000000000000000000000000dEaD); + address public constant FAILED_CALL_ADDRESS = + address(0x000000000000000000000000000000000000dEaD); /** * @notice Returns the list of strategies that the AVS supports for restaking * @param serviceManager Address of AVS's ServiceManager contract */ - function getRestakeableStrategies(address serviceManager) external view returns (address[] memory) { - bytes memory data = abi.encodeWithSelector( - IServiceManagerUI.getRestakeableStrategies.selector - ); + function getRestakeableStrategies( + address serviceManager + ) external view returns (address[] memory) { + bytes memory data = + abi.encodeWithSelector(IServiceManagerUI.getRestakeableStrategies.selector); return _makeCall(serviceManager, data); } @@ -30,20 +30,25 @@ contract ServiceManagerRouter { * @param serviceManager Address of AVS's ServiceManager contract * @param operator Address of the operator to get restaked strategies for */ - function getOperatorRestakedStrategies(address serviceManager, address operator) external view returns (address[] memory) { + function getOperatorRestakedStrategies( + address serviceManager, + address operator + ) external view returns (address[] memory) { bytes memory data = abi.encodeWithSelector( - IServiceManagerUI.getOperatorRestakedStrategies.selector, - operator + IServiceManagerUI.getOperatorRestakedStrategies.selector, operator ); return _makeCall(serviceManager, data); } /** * @notice Internal helper function to make static calls - * @dev Handles calls to contracts that don't implement the given function and to EOAs by + * @dev Handles calls to contracts that don't implement the given function and to EOAs by * returning a failed call address */ - function _makeCall(address serviceManager, bytes memory data) internal view returns (address[] memory) { + function _makeCall( + address serviceManager, + bytes memory data + ) internal view returns (address[] memory) { (bool success, bytes memory strategiesBytes) = serviceManager.staticcall(data); if (success && strategiesBytes.length > 0) { return abi.decode(strategiesBytes, (address[])); diff --git a/src/SlashingRegistryCoordinator.sol b/src/SlashingRegistryCoordinator.sol new file mode 100644 index 00000000..101fad15 --- /dev/null +++ b/src/SlashingRegistryCoordinator.sol @@ -0,0 +1,1142 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.27; + +import {IPauserRegistry} from "eigenlayer-contracts/src/contracts/interfaces/IPauserRegistry.sol"; +import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol"; +import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; +import { + IAllocationManager, + OperatorSet, + IAllocationManagerTypes +} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; + +import {IBLSApkRegistry, IBLSApkRegistryTypes} from "./interfaces/IBLSApkRegistry.sol"; +import {IStakeRegistry, IStakeRegistryTypes} from "./interfaces/IStakeRegistry.sol"; +import {IIndexRegistry} from "./interfaces/IIndexRegistry.sol"; +import {ISlashingRegistryCoordinator} from "./interfaces/ISlashingRegistryCoordinator.sol"; +import {ISocketRegistry} from "./interfaces/ISocketRegistry.sol"; + +import {BitmapUtils} from "./libraries/BitmapUtils.sol"; +import {BN254} from "./libraries/BN254.sol"; +import {SignatureCheckerLib} from "./libraries/SignatureCheckerLib.sol"; +import {QuorumBitmapHistoryLib} from "./libraries/QuorumBitmapHistoryLib.sol"; + +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"; + +import {Pausable} from "eigenlayer-contracts/src/contracts/permissions/Pausable.sol"; +import {SlashingRegistryCoordinatorStorage} from "./SlashingRegistryCoordinatorStorage.sol"; + +/** + * @title A `RegistryCoordinator` that has three registries: + * 1) a `StakeRegistry` that keeps track of operators' stakes + * 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 SlashingRegistryCoordinator is + EIP712, + Initializable, + Pausable, + OwnableUpgradeable, + SlashingRegistryCoordinatorStorage, + ISignatureUtils +{ + using BitmapUtils for *; + using BN254 for BN254.G1Point; + + modifier onlyAllocationManager() { + _checkAllocationManager(); + _; + } + + modifier onlyEjector() { + _checkEjector(); + _; + } + + /// @dev Checks that `quorumNumber` corresponds to a quorum that has been created + /// via `initialize` or `createQuorum` + modifier quorumExists( + uint8 quorumNumber + ) { + _checkQuorumExists(quorumNumber); + _; + } + + constructor( + IStakeRegistry _stakeRegistry, + IBLSApkRegistry _blsApkRegistry, + IIndexRegistry _indexRegistry, + ISocketRegistry _socketRegistry, + IAllocationManager _allocationManager, + IPauserRegistry _pauserRegistry + ) + SlashingRegistryCoordinatorStorage( + _stakeRegistry, + _blsApkRegistry, + _indexRegistry, + _socketRegistry, + _allocationManager + ) + EIP712("AVSRegistryCoordinator", "v0.0.1") + Pausable(_pauserRegistry) + { + _disableInitializers(); + } + + /** + * + * EXTERNAL FUNCTIONS + * + */ + function initialize( + address _initialOwner, + address _churnApprover, + address _ejector, + uint256 _initialPausedStatus, + address _accountIdentifier + ) external initializer { + _transferOwnership(_initialOwner); + _setChurnApprover(_churnApprover); + _setPausedStatus(_initialPausedStatus); + _setEjector(_ejector); + _setAccountIdentifier(_accountIdentifier); + // Add registry contracts to the registries array + registries.push(address(stakeRegistry)); + registries.push(address(blsApkRegistry)); + registries.push(address(indexRegistry)); + + // Set the AVS to be OperatorSets compatible + operatorSetsEnabled = true; + + // Set the AVS to not accept M2 quorums + m2QuorumsDisabled = true; + } + + /** + * @notice Creates a quorum and initializes it in each registry contract + * @param operatorSetParams configures the quorum's max operator count and churn parameters + * @param minimumStake sets the minimum stake required for an operator to register or remain + * registered + * @param strategyParams a list of strategies and multipliers used by the StakeRegistry to + * calculate an operator's stake weight for the quorum + * @dev For m2 AVS this function has the same behavior as createQuorum before + * For migrated AVS that enable operator sets this will create a quorum that measures total delegated stake for operator set + * + */ + function createTotalDelegatedStakeQuorum( + OperatorSetParam memory operatorSetParams, + uint96 minimumStake, + IStakeRegistryTypes.StrategyParams[] memory strategyParams + ) external virtual onlyOwner { + _createQuorum( + operatorSetParams, + minimumStake, + strategyParams, + IStakeRegistryTypes.StakeType.TOTAL_DELEGATED, + 0 + ); + } + + function createSlashableStakeQuorum( + OperatorSetParam memory operatorSetParams, + uint96 minimumStake, + IStakeRegistryTypes.StrategyParams[] memory strategyParams, + uint32 lookAheadPeriod + ) external virtual onlyOwner { + require(operatorSetsEnabled, OperatorSetsNotEnabled()); + _createQuorum( + operatorSetParams, + minimumStake, + strategyParams, + IStakeRegistryTypes.StakeType.TOTAL_SLASHABLE, + lookAheadPeriod + ); + } + + function registerOperator( + address operator, + uint32[] memory operatorSetIds, + bytes calldata data + ) external override onlyAllocationManager onlyWhenNotPaused(PAUSED_REGISTER_OPERATOR) { + require(operatorSetsEnabled, OperatorSetsNotEnabled()); + bytes memory quorumNumbers = _getQuorumNumbers(operatorSetIds); + + ( + RegistrationType registrationType, + string memory socket, + IBLSApkRegistryTypes.PubkeyRegistrationParams memory params + ) = abi.decode( + data, (RegistrationType, string, IBLSApkRegistryTypes.PubkeyRegistrationParams) + ); + + /** + * If the operator has NEVER registered a pubkey before, use `params` to register + * their pubkey in blsApkRegistry + * + * If the operator HAS registered a pubkey, `params` is ignored and the pubkey hash + * (operatorId) is fetched instead + */ + bytes32 operatorId = _getOrCreateOperatorId(operator, params); + + if (registrationType == RegistrationType.NORMAL) { + uint32[] memory numOperatorsPerQuorum = _registerOperator({ + operator: operator, + operatorId: operatorId, + quorumNumbers: quorumNumbers, + socket: socket + }).numOperatorsPerQuorum; + + // For each quorum, validate that the new operator count does not exceed the maximum + // (If it does, an operator needs to be replaced -- see `registerOperatorWithChurn`) + for (uint256 i = 0; i < quorumNumbers.length; i++) { + uint8 quorumNumber = uint8(quorumNumbers[i]); + + require( + numOperatorsPerQuorum[i] <= _quorumParams[quorumNumber].maxOperatorCount, + MaxQuorumsReached() + ); + } + } else if (registrationType == RegistrationType.CHURN) { + // Decode registration data from bytes + ( + , + , + , + OperatorKickParam[] memory operatorKickParams, + SignatureWithSaltAndExpiry memory churnApproverSignature + ) = abi.decode( + data, + ( + RegistrationType, + string, + IBLSApkRegistryTypes.PubkeyRegistrationParams, + OperatorKickParam[], + SignatureWithSaltAndExpiry + ) + ); + _registerOperatorWithChurn({ + operator: operator, + operatorId: operatorId, + quorumNumbers: quorumNumbers, + socket: socket, + operatorKickParams: operatorKickParams, + churnApproverSignature: churnApproverSignature + }); + } else { + revert InvalidRegistrationType(); + } + + // If the operator wasn't registered for any quorums, update their status + // and register them with this AVS in EigenLayer core (DelegationManager) + if (_operatorInfo[operator].status != OperatorStatus.REGISTERED) { + _operatorInfo[operator] = OperatorInfo(operatorId, OperatorStatus.REGISTERED); + emit OperatorRegistered(operator, operatorId); + } + } + + function deregisterOperator( + address operator, + uint32[] memory operatorSetIds + ) external override onlyAllocationManager onlyWhenNotPaused(PAUSED_DEREGISTER_OPERATOR) { + require(operatorSetsEnabled, OperatorSetsNotEnabled()); + bytes memory quorumNumbers = _getQuorumNumbers(operatorSetIds); + _deregisterOperator(operator, quorumNumbers); + } + + /** + * @notice Updates the StakeRegistry's view of one or more operators' stakes. If any operator + * is found to be below the minimum stake for the quorum, they are deregistered. + * @dev stakes are queried from the Eigenlayer core DelegationManager contract + * @param operators a list of operator addresses to update + */ + function updateOperators( + address[] memory operators + ) external onlyWhenNotPaused(PAUSED_UPDATE_OPERATOR) { + for (uint256 i = 0; i < operators.length; i++) { + address operator = operators[i]; + OperatorInfo memory operatorInfo = _operatorInfo[operator]; + bytes32 operatorId = operatorInfo.operatorId; + + // Update the operator's stake for their active quorums + uint192 currentBitmap = _currentOperatorBitmap(operatorId); + bytes memory quorumsToUpdate = BitmapUtils.bitmapToBytesArray(currentBitmap); + _updateOperator(operator, operatorInfo, quorumsToUpdate); + } + } + + /** + * @notice For each quorum in `quorumNumbers`, updates the StakeRegistry's view of ALL its registered operators' stakes. + * Each quorum's `quorumUpdateBlockNumber` is also updated, which tracks the most recent block number when ALL registered + * operators were updated. + * @dev stakes are queried from the Eigenlayer core DelegationManager contract + * @param operatorsPerQuorum for each quorum in `quorumNumbers`, this has a corresponding list of operators to update. + * @dev Each list of operator addresses MUST be sorted in ascending order + * @dev Each list of operator addresses MUST represent the entire list of registered operators for the corresponding quorum + * @param quorumNumbers is an ordered byte array containing the quorum numbers being updated + * @dev invariant: Each list of `operatorsPerQuorum` MUST be a sorted version of `IndexRegistry.getOperatorListAtBlockNumber` + * for the corresponding quorum. + * @dev note on race condition: if an operator registers/deregisters for any quorum in `quorumNumbers` after a txn to + * this method is broadcast (but before it is executed), the method will fail + */ + function updateOperatorsForQuorum( + address[][] memory operatorsPerQuorum, + bytes calldata quorumNumbers + ) external onlyWhenNotPaused(PAUSED_UPDATE_OPERATOR) { + // Input validation + // - all quorums should exist (checked against `quorumCount` in orderedBytesArrayToBitmap) + // - there should be no duplicates in `quorumNumbers` + // - there should be one list of operators per quorum + BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers, quorumCount); + require(operatorsPerQuorum.length == quorumNumbers.length, InputLengthMismatch()); + + // For each quorum, update ALL registered operators + for (uint256 i = 0; i < quorumNumbers.length; ++i) { + uint8 quorumNumber = uint8(quorumNumbers[i]); + + // Ensure we've passed in the correct number of operators for this quorum + address[] memory currQuorumOperators = operatorsPerQuorum[i]; + require( + currQuorumOperators.length == indexRegistry.totalOperatorsForQuorum(quorumNumber), + QuorumOperatorCountMismatch() + ); + + address prevOperatorAddress = address(0); + // For each operator: + // - check that they are registered for this quorum + // - check that their address is strictly greater than the last operator + // ... then, update their stakes + 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); + // Check that the operator is registered + require( + BitmapUtils.isSet(currentBitmap, quorumNumber), NotRegisteredForQuorum() + ); + // Prevent duplicate operators + require(operator > prevOperatorAddress, NotSorted()); + } + + // Update the operator + _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); + } + } + + /** + * @notice Updates the socket of the msg.sender given they are a registered operator + * @param socket is the new socket of the operator + */ + function updateSocket( + string memory socket + ) external { + require(_operatorInfo[msg.sender].status == OperatorStatus.REGISTERED, NotRegistered()); + _setOperatorSocket(_operatorInfo[msg.sender].operatorId, socket); + } + + /** + * + * EXTERNAL FUNCTIONS - EJECTOR + * + */ + + /** + * @notice Forcibly deregisters an operator from one or more quorums + * @param operator the operator to eject + * @param quorumNumbers the quorum numbers to eject the operator from + * @dev possible race condition if prior to being ejected for a set of quorums the operator self deregisters from a subset + */ + function ejectOperator(address operator, bytes memory quorumNumbers) external onlyEjector { + lastEjectionTimestamp[operator] = block.timestamp; + + OperatorInfo storage operatorInfo = _operatorInfo[operator]; + bytes32 operatorId = operatorInfo.operatorId; + uint192 quorumsToRemove = + uint192(BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers, quorumCount)); + uint192 currentBitmap = _currentOperatorBitmap(operatorId); + if ( + operatorInfo.status == OperatorStatus.REGISTERED && !quorumsToRemove.isEmpty() + && quorumsToRemove.isSubsetOf(currentBitmap) + ) { + _deregisterOperator({operator: operator, quorumNumbers: quorumNumbers}); + + if (operatorSetsEnabled) { + _forceDeregisterOperator(operator, quorumNumbers); + } + } + } + + /** + * + * EXTERNAL FUNCTIONS - OWNER + * + */ + + /** + * @notice Updates an existing quorum's configuration with a new max operator count + * and operator churn parameters + * @param quorumNumber the quorum number to update + * @param operatorSetParams the new config + * @dev only callable by the owner + */ + function setOperatorSetParams( + uint8 quorumNumber, + OperatorSetParam memory operatorSetParams + ) external onlyOwner quorumExists(quorumNumber) { + _setOperatorSetParams(quorumNumber, operatorSetParams); + } + + /** + * @notice Sets the churnApprover, which approves operator registration with churn + * (see `registerOperatorWithChurn`) + * @param _churnApprover the new churn approver + * @dev only callable by the owner + */ + function setChurnApprover( + address _churnApprover + ) external onlyOwner { + _setChurnApprover(_churnApprover); + } + + /** + * @notice Sets the ejector, which can force-deregister operators from quorums + * @param _ejector the new ejector + * @dev only callable by the owner + */ + function setEjector( + address _ejector + ) external onlyOwner { + _setEjector(_ejector); + } + + /** + * @notice Sets the account identifier for this AVS (used for UAM integration in EigenLayer) + * @param _accountIdentifier the new account identifier + * @dev only callable by the owner + */ + function setAccountIdentifier( + address _accountIdentifier + ) external onlyOwner { + _setAccountIdentifier(_accountIdentifier); + } + + /** + * @notice Sets the ejection cooldown, which is the time an operator must wait in + * seconds afer ejection before registering for any quorum + * @param _ejectionCooldown the new ejection cooldown in seconds + * @dev only callable by the owner + */ + function setEjectionCooldown( + uint256 _ejectionCooldown + ) external onlyOwner { + ejectionCooldown = _ejectionCooldown; + } + + /** + * + * INTERNAL FUNCTIONS + * + */ + + /** + * @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, + bytes32 operatorId, + bytes memory quorumNumbers, + string memory socket + ) 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 + * - the quorums we're registering for exist (checked against `quorumCount` in orderedBytesArrayToBitmap) + * - the operator is not currently registered for any quorums we're registering for + * Then, calculate the operator's new bitmap after registration + */ + uint192 quorumsToAdd = + uint192(BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers, quorumCount)); + uint192 currentBitmap = _currentOperatorBitmap(operatorId); + + // call hook to allow for any pre-register logic + _beforeRegisterOperator(operator, operatorId, quorumNumbers, currentBitmap); + + require(!quorumsToAdd.isEmpty(), BitmapEmpty()); + require(quorumsToAdd.noBitsInCommon(currentBitmap), AlreadyRegisteredForQuorums()); + uint192 newBitmap = uint192(currentBitmap.plus(quorumsToAdd)); + + // Check that the operator can reregister if ejected + require( + lastEjectionTimestamp[operator] + ejectionCooldown < block.timestamp, + CannotReregisterYet() + ); + + /** + * Update operator's bitmap, socket, and status. Only update operatorInfo if needed: + * if we're `REGISTERED`, the operatorId and status are already correct. + */ + _updateOperatorBitmap({operatorId: operatorId, newBitmap: newBitmap}); + + emit OperatorSocketUpdate(operatorId, socket); + + // Register the operator with the BLSApkRegistry, StakeRegistry, and IndexRegistry + blsApkRegistry.registerOperator(operator, quorumNumbers); + (results.operatorStakes, results.totalStakes) = + stakeRegistry.registerOperator(operator, operatorId, quorumNumbers); + results.numOperatorsPerQuorum = indexRegistry.registerOperator(operatorId, quorumNumbers); + + // call hook to allow for any post-register logic + _afterRegisterOperator(operator, operatorId, quorumNumbers, newBitmap); + + return results; + } + + function _registerOperatorWithChurn( + address operator, + bytes32 operatorId, + bytes memory quorumNumbers, + string memory socket, + OperatorKickParam[] memory operatorKickParams, + SignatureWithSaltAndExpiry memory churnApproverSignature + ) internal virtual { + require(operatorKickParams.length == quorumNumbers.length, InputLengthMismatch()); + + // Verify the churn approver's signature for the registering operator and kick params + _verifyChurnApproverSignature({ + registeringOperator: operator, + registeringOperatorId: operatorId, + operatorKickParams: operatorKickParams, + churnApproverSignature: churnApproverSignature + }); + + // Register the operator in each of the registry contracts and update the operator's + // quorum bitmap and registration status + RegisterResults memory results = + _registerOperator(operator, operatorId, quorumNumbers, socket); + + // Check that each quorum's operator count is below the configured maximum. If the max + // is exceeded, use `operatorKickParams` to deregister an existing operator to make space + for (uint256 i = 0; i < quorumNumbers.length; i++) { + OperatorSetParam memory operatorSetParams = _quorumParams[uint8(quorumNumbers[i])]; + + /** + * 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: uint8(quorumNumbers[i]), + totalQuorumStake: results.totalStakes[i], + newOperator: operator, + newOperatorStake: results.operatorStakes[i], + kickParams: operatorKickParams[i], + setParams: operatorSetParams + }); + + bytes memory singleQuorumNumber = new bytes(1); + singleQuorumNumber[0] = quorumNumbers[i]; + _deregisterOperator(operatorKickParams[i].operator, singleQuorumNumber); + + if (operatorSetsEnabled) { + _forceDeregisterOperator(operatorKickParams[i].operator, singleQuorumNumber); + } + } + } + } + + /** + * @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 BLSApkRegistry, IndexRegistry, and StakeRegistry + */ + function _deregisterOperator(address operator, bytes memory quorumNumbers) internal virtual { + // Fetch the operator's info and ensure they are registered + OperatorInfo storage operatorInfo = _operatorInfo[operator]; + bytes32 operatorId = operatorInfo.operatorId; + uint192 currentBitmap = _currentOperatorBitmap(operatorId); + + // call hook to allow for any pre-deregister logic + _beforeDeregisterOperator(operator, operatorId, quorumNumbers, currentBitmap); + + require(operatorInfo.status == OperatorStatus.REGISTERED, NotRegistered()); + + /** + * 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 quorums we're deregistering from exist (checked against `quorumCount` in orderedBytesArrayToBitmap) + * - the operator is currently registered for any quorums we're trying to deregister from + * Then, calculate the operator's new bitmap after deregistration + */ + uint192 quorumsToRemove = + uint192(BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers, quorumCount)); + require(!quorumsToRemove.isEmpty(), BitmapCannotBeZero()); + require(quorumsToRemove.isSubsetOf(currentBitmap), NotRegisteredForQuorum()); + uint192 newBitmap = uint192(currentBitmap.minus(quorumsToRemove)); + + // Update operator's bitmap and status + _updateOperatorBitmap({operatorId: operatorId, newBitmap: newBitmap}); + + // If the operator is no longer registered for any quorums, update their status and deregister + // them from the AVS via the EigenLayer core contracts + if (newBitmap.isEmpty()) { + _operatorInfo[operator].status = OperatorStatus.DEREGISTERED; + emit OperatorDeregistered(operator, operatorId); + } + + // Deregister operator with each of the registry contracts + blsApkRegistry.deregisterOperator(operator, quorumNumbers); + stakeRegistry.deregisterOperator(operatorId, quorumNumbers); + indexRegistry.deregisterOperator(operatorId, quorumNumbers); + + // call hook to allow for any post-deregister logic + _afterDeregisterOperator(operator, operatorId, quorumNumbers, newBitmap); + } + + /** + * @notice Helper function to handle operator set deregistration for OperatorSets quorums. This is used + * when an operator is force-deregistered from a set of quorums. For any of the quorums that are + * OperatorSets quorums, we need to deregister the operator in the AllocationManager. + * @param operator The operator to deregister + * @param quorumNumbers The quorum numbers the operator is force-deregistered from + */ + function _forceDeregisterOperator(address operator, bytes memory quorumNumbers) internal { + uint32[] memory operatorSetIds = new uint32[](quorumNumbers.length); + uint256 numOperatorSetQuorums; + + // Check each quorum's stake type + for (uint256 i = 0; i < quorumNumbers.length; i++) { + uint8 quorumNumber = uint8(quorumNumbers[i]); + if (_isM2Quorum(quorumNumber)) { + operatorSetIds[numOperatorSetQuorums++] = quorumNumber; + } + } + + // If any OperatorSet quorums found, deregister from AVS in the AllocationManager + if (numOperatorSetQuorums > 0) { + // Resize array to exact size needed + assembly { + mstore(operatorSetIds, numOperatorSetQuorums) + } + allocationManager.deregisterFromOperatorSets( + IAllocationManagerTypes.DeregisterParams({ + operator: operator, + avs: accountIdentifier, + operatorSetIds: operatorSetIds + }) + ); + } + } + + /** + * @notice Checks if the caller is the ejector + * @dev Reverts if the caller is not the ejector + */ + function _checkEjector() internal view { + require(msg.sender == ejector, OnlyEjector()); + } + + function _checkAllocationManager() internal view { + require(msg.sender == address(allocationManager), OnlyAllocationManager()); + } + + /** + * @notice Checks if a quorum exists + * @param quorumNumber The quorum number to check + * @dev Reverts if the quorum does not exist + */ + function _checkQuorumExists( + uint8 quorumNumber + ) internal view { + require(quorumNumber < quorumCount, QuorumDoesNotExist()); + } + + /** + * @notice Fetches an operator's pubkey hash from the BLSApkRegistry. If the + * operator has not registered a pubkey, attempts to register a pubkey using + * `params` + * @param operator the operator whose pubkey to query from the BLSApkRegistry + * @param params contains the G1 & G2 public keys of the operator, and a signature proving their ownership + * @dev `params` can be empty if the operator has already registered a pubkey in the BLSApkRegistry + */ + function _getOrCreateOperatorId( + address operator, + IBLSApkRegistryTypes.PubkeyRegistrationParams memory params + ) internal returns (bytes32 operatorId) { + operatorId = blsApkRegistry.getOperatorId(operator); + if (operatorId == 0) { + operatorId = blsApkRegistry.registerBLSPublicKey( + operator, params, pubkeyRegistrationMessageHash(operator) + ); + } + return operatorId; + } + + /** + * @notice Validates that an incoming operator is eligible to replace an existing + * operator based on the stake of both + * @dev In order to churn, the incoming operator needs to have more stake than the + * existing operator by a proportion given by `kickBIPsOfOperatorStake` + * @dev In order to be churned out, the existing operator needs to have a proportion + * of the total quorum stake less than `kickBIPsOfTotalStake` + * @param quorumNumber `newOperator` is trying to replace an operator in this quorum + * @param totalQuorumStake the total stake of all operators in the quorum, after the + * `newOperator` registers + * @param newOperator the incoming operator + * @param newOperatorStake the incoming operator's stake + * @param kickParams the quorum number and existing operator to replace + * @dev the existing operator's registration to this quorum isn't checked here, but + * if we attempt to deregister them, this will be checked in `_deregisterOperator` + * @param setParams config for this quorum containing `kickBIPsX` stake proportions + * mentioned above + */ + 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(newOperator != operatorToKick, CannotChurnSelf()); + require(kickParams.quorumNumber == quorumNumber, QuorumOperatorCountMismatch()); + + // 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), + InsufficientStakeForChurn() + ); + require( + operatorToKickStake < _totalKickThreshold(totalQuorumStake, setParams), + CannotKickOperatorAboveThreshold() + ); + } + + /** + * @notice Updates the StakeRegistry's view of the operator's stake in one or more quorums. + * For any quorums where the StakeRegistry finds the operator is under the configured minimum + * stake, `quorumsToRemove` is returned and used to deregister the operator from those quorums + * @dev 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. + */ + 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 Updates an operator's socket address in the SocketRegistry + * @param operatorId The unique identifier of the operator + * @param socket The new socket address to set for the operator + * @dev Emits an OperatorSocketUpdate event after updating + */ + function _setOperatorSocket(bytes32 operatorId, string memory socket) internal { + socketRegistry.setOperatorSocket(operatorId, socket); + emit OperatorSocketUpdate(operatorId, socket); + } + + /// @notice verifies churnApprover's signature on operator churn approval and increments the churnApprover nonce + function _verifyChurnApproverSignature( + address registeringOperator, + bytes32 registeringOperatorId, + OperatorKickParam[] memory operatorKickParams, + SignatureWithSaltAndExpiry memory churnApproverSignature + ) internal { + // make sure the salt hasn't been used already + require(!isChurnApproverSaltUsed[churnApproverSignature.salt], ChurnApproverSaltUsed()); + require(churnApproverSignature.expiry >= block.timestamp, SignatureExpired()); + + // set salt used to true + isChurnApproverSaltUsed[churnApproverSignature.salt] = true; + + // check the churnApprover's signature + SignatureCheckerLib.isValidSignature( + churnApprover, + calculateOperatorChurnApprovalDigestHash( + registeringOperator, + registeringOperatorId, + operatorKickParams, + churnApproverSignature.salt, + churnApproverSignature.expiry + ), + churnApproverSignature.signature + ); + } + + /** + * @notice Creates a quorum and initializes it in each registry contract + * @param operatorSetParams configures the quorum's max operator count and churn parameters + * @param minimumStake sets the minimum stake required for an operator to register or remain + * registered + * @param strategyParams a list of strategies and multipliers used by the StakeRegistry to + * calculate an operator's stake weight for the quorum + */ + function _createQuorum( + OperatorSetParam memory operatorSetParams, + uint96 minimumStake, + IStakeRegistryTypes.StrategyParams[] memory strategyParams, + IStakeRegistryTypes.StakeType stakeType, + uint32 lookAheadPeriod + ) internal { + // Increment the total quorum count. Fails if we're already at the max + uint8 prevQuorumCount = quorumCount; + require(prevQuorumCount < MAX_QUORUM_COUNT, MaxQuorumsReached()); + 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); + + /// Update the AllocationManager if operatorSetQuorum + if (operatorSetsEnabled && !_isM2Quorum(quorumNumber)) { + // Create array of CreateSetParams for the new quorum + IAllocationManagerTypes.CreateSetParams[] memory createSetParams = + new IAllocationManagerTypes.CreateSetParams[](1); + + // Extract strategies from strategyParams + IStrategy[] memory strategies = new IStrategy[](strategyParams.length); + for (uint256 i = 0; i < strategyParams.length; i++) { + strategies[i] = strategyParams[i].strategy; + } + + // Initialize CreateSetParams with quorumNumber as operatorSetId + createSetParams[0] = IAllocationManagerTypes.CreateSetParams({ + operatorSetId: quorumNumber, + strategies: strategies + }); + allocationManager.createOperatorSets({avs: accountIdentifier, params: createSetParams}); + } + // Initialize stake registry based on stake type + if (stakeType == IStakeRegistryTypes.StakeType.TOTAL_DELEGATED) { + stakeRegistry.initializeDelegatedStakeQuorum(quorumNumber, minimumStake, strategyParams); + } else if (stakeType == IStakeRegistryTypes.StakeType.TOTAL_SLASHABLE) { + stakeRegistry.initializeSlashableStakeQuorum( + quorumNumber, minimumStake, lookAheadPeriod, strategyParams + ); + } + + indexRegistry.initializeQuorum(quorumNumber); + blsApkRegistry.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 { + QuorumBitmapHistoryLib.updateOperatorBitmap(_operatorBitmapHistory, operatorId, 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) { + return QuorumBitmapHistoryLib.currentOperatorBitmap(_operatorBitmapHistory, operatorId); + } + + /** + * @notice Returns the index of the quorumBitmap for the provided `operatorId` at the given `blockNumber` + * @dev Reverts if the operator had not yet (ever) registered at `blockNumber` + * @dev This function is designed to find proper inputs to the `getQuorumBitmapAtBlockNumberByIndex` function + */ + function _getQuorumBitmapIndexAtBlockNumber( + uint32 blockNumber, + bytes32 operatorId + ) internal view returns (uint32 index) { + return QuorumBitmapHistoryLib.getQuorumBitmapIndexAtBlockNumber( + _operatorBitmapHistory, blockNumber, operatorId + ); + } + + /// @notice Returns the quorum numbers for the provided `OperatorSetIds` + /// OperatorSetIds are used in the AllocationManager to identify operator sets for a given AVS + function _getQuorumNumbers( + uint32[] memory operatorSetIds + ) internal pure returns (bytes memory) { + bytes memory quorumNumbers = new bytes(operatorSetIds.length); + for (uint256 i = 0; i < operatorSetIds.length; i++) { + quorumNumbers[i] = bytes1(uint8(operatorSetIds[i])); + } + return quorumNumbers; + } + + /// @notice Returns true if the quorum number is an M2 quorum + /// @dev We use bitwise and to check if the quorum number is an M2 quorum + function _isM2Quorum( + uint8 quorumNumber + ) internal view returns (bool) { + return M2quorumBitmap.isSet(quorumNumber); + } + + function _setOperatorSetParams( + uint8 quorumNumber, + OperatorSetParam memory operatorSetParams + ) internal { + _quorumParams[quorumNumber] = operatorSetParams; + emit OperatorSetParamsUpdated(quorumNumber, operatorSetParams); + } + + function _setChurnApprover( + address newChurnApprover + ) internal { + emit ChurnApproverUpdated(churnApprover, newChurnApprover); + churnApprover = newChurnApprover; + } + + function _setEjector( + address newEjector + ) internal { + emit EjectorUpdated(ejector, newEjector); + ejector = newEjector; + } + + function _setAccountIdentifier( + address _accountIdentifier + ) internal { + accountIdentifier = _accountIdentifier; + } + + /// @dev Hook to allow for any pre-register logic in `_registerOperator` + function _beforeRegisterOperator( + address operator, + bytes32 operatorId, + bytes memory quorumNumbers, + uint192 currentBitmap + ) internal virtual {} + + /// @dev Hook to allow for any post-register logic in `_registerOperator` + function _afterRegisterOperator( + address operator, + bytes32 operatorId, + bytes memory quorumNumbers, + uint192 newBitmap + ) internal virtual {} + + /// @dev Hook to allow for any pre-deregister logic in `_deregisterOperator` + function _beforeDeregisterOperator( + address operator, + bytes32 operatorId, + bytes memory quorumNumbers, + uint192 currentBitmap + ) internal virtual {} + + /// @dev Hook to allow for any post-deregister logic in `_deregisterOperator` + function _afterDeregisterOperator( + address operator, + bytes32 operatorId, + bytes memory quorumNumbers, + uint192 newBitmap + ) internal virtual {} + + /** + * + * VIEW FUNCTIONS + * + */ + + /// @notice Returns the operator set params for the given `quorumNumber` + function getOperatorSetParams( + uint8 quorumNumber + ) external view returns (OperatorSetParam memory) { + return _quorumParams[quorumNumber]; + } + + /// @notice Returns the operator struct for the given `operator` + function getOperator( + address operator + ) external view returns (OperatorInfo memory) { + return _operatorInfo[operator]; + } + + /// @notice Returns the operatorId for the given `operator` + function getOperatorId( + address operator + ) external view returns (bytes32) { + return _operatorInfo[operator].operatorId; + } + + /// @notice Returns the operator address for the given `operatorId` + function getOperatorFromId( + bytes32 operatorId + ) external view returns (address) { + return blsApkRegistry.getOperatorFromPubkeyHash(operatorId); + } + + /// @notice Returns the status for the given `operator` + function getOperatorStatus( + address operator + ) external view returns (ISlashingRegistryCoordinator.OperatorStatus) { + return _operatorInfo[operator].status; + } + + /// @notice Returns true if the quorum number is an M2 quorum + function isM2Quorum( + uint8 quorumNumber + ) external view returns (bool) { + return _isM2Quorum(quorumNumber); + } + + /** + * @notice Returns the indices of the quorumBitmaps for the provided `operatorIds` at the given `blockNumber` + * @dev Reverts if any of the `operatorIds` was not (yet) registered at `blockNumber` + * @dev This function is designed to find proper inputs to the `getQuorumBitmapAtBlockNumberByIndex` function + */ + function getQuorumBitmapIndicesAtBlockNumber( + uint32 blockNumber, + bytes32[] memory operatorIds + ) external view returns (uint32[] memory) { + return QuorumBitmapHistoryLib.getQuorumBitmapIndicesAtBlockNumber( + _operatorBitmapHistory, blockNumber, operatorIds + ); + } + + /** + * @notice Returns the quorum bitmap for the given `operatorId` at the given `blockNumber` via the `index`, + * reverting if `index` is incorrect + * @dev This function is meant to be used in concert with `getQuorumBitmapIndicesAtBlockNumber`, which + * helps off-chain processes to fetch the correct `index` input + */ + function getQuorumBitmapAtBlockNumberByIndex( + bytes32 operatorId, + uint32 blockNumber, + uint256 index + ) external view returns (uint192) { + return QuorumBitmapHistoryLib.getQuorumBitmapAtBlockNumberByIndex( + _operatorBitmapHistory, operatorId, blockNumber, index + ); + } + + /// @notice Returns the `index`th entry in the operator with `operatorId`'s bitmap history + function getQuorumBitmapUpdateByIndex( + bytes32 operatorId, + uint256 index + ) external view returns (QuorumBitmapUpdate memory) { + 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 getCurrentQuorumBitmap( + bytes32 operatorId + ) external view returns (uint192) { + return _currentOperatorBitmap(operatorId); + } + + /// @notice Returns the length of the quorum bitmap history for the given `operatorId` + function getQuorumBitmapHistoryLength( + bytes32 operatorId + ) external view returns (uint256) { + return _operatorBitmapHistory[operatorId].length; + } + + /// @notice Returns the number of registries + function numRegistries() external view returns (uint256) { + return registries.length; + } + + /** + * @notice Public function for the the churnApprover signature hash calculation when operators are being kicked from quorums + * @param registeringOperatorId The id of the registering operator + * @param operatorKickParams The parameters needed to kick the operator from the quorums that have reached their caps + * @param salt The salt to use for the churnApprover's signature + * @param expiry The desired expiry time of the churnApprover's signature + */ + function calculateOperatorChurnApprovalDigestHash( + address registeringOperator, + bytes32 registeringOperatorId, + OperatorKickParam[] memory operatorKickParams, + bytes32 salt, + uint256 expiry + ) public view returns (bytes32) { + // calculate the digest hash + return _hashTypedDataV4( + keccak256( + abi.encode( + OPERATOR_CHURN_APPROVAL_TYPEHASH, + registeringOperator, + 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 + view + virtual + override(OwnableUpgradeable, ISlashingRegistryCoordinator) + returns (address) + { + return OwnableUpgradeable.owner(); + } +} diff --git a/src/RegistryCoordinatorStorage.sol b/src/SlashingRegistryCoordinatorStorage.sol similarity index 61% rename from src/RegistryCoordinatorStorage.sol rename to src/SlashingRegistryCoordinatorStorage.sol index a8b4fced..14946dd9 100644 --- a/src/RegistryCoordinatorStorage.sol +++ b/src/SlashingRegistryCoordinatorStorage.sol @@ -1,24 +1,33 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {IBLSApkRegistry} from "./interfaces/IBLSApkRegistry.sol"; import {IStakeRegistry} from "./interfaces/IStakeRegistry.sol"; import {IIndexRegistry} from "./interfaces/IIndexRegistry.sol"; import {IServiceManager} from "./interfaces/IServiceManager.sol"; -import {IRegistryCoordinator} from "./interfaces/IRegistryCoordinator.sol"; +import {IAVSDirectory} from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol"; +import { + IAllocationManager, + OperatorSet, + IAllocationManagerTypes +} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; +import {ISlashingRegistryCoordinator} from "./interfaces/ISlashingRegistryCoordinator.sol"; import {ISocketRegistry} from "./interfaces/ISocketRegistry.sol"; -abstract contract RegistryCoordinatorStorage is IRegistryCoordinator { - - /******************************************************************************* - CONSTANTS AND IMMUTABLES - *******************************************************************************/ +abstract contract SlashingRegistryCoordinatorStorage is ISlashingRegistryCoordinator { + /** + * + * CONSTANTS AND IMMUTABLES + * + */ /// @notice The EIP-712 typehash for the `DelegationApproval` struct used by the contract - bytes32 public constant OPERATOR_CHURN_APPROVAL_TYPEHASH = - keccak256("OperatorChurnApproval(address registeringOperator,bytes32 registeringOperatorId,OperatorKickParam[] operatorKickParams,bytes32 salt,uint256 expiry)OperatorKickParam(uint8 quorumNumber,address operator)"); + bytes32 public constant OPERATOR_CHURN_APPROVAL_TYPEHASH = keccak256( + "OperatorChurnApproval(address registeringOperator,bytes32 registeringOperatorId,OperatorKickParam[] operatorKickParams,bytes32 salt,uint256 expiry)OperatorKickParam(uint8 quorumNumber,address operator)" + ); /// @notice The EIP-712 typehash used for registering BLS public keys - bytes32 public constant PUBKEY_REGISTRATION_TYPEHASH = keccak256("BN254PubkeyRegistration(address operator)"); + 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 @@ -32,20 +41,24 @@ abstract contract RegistryCoordinatorStorage is IRegistryCoordinator { /// @notice The maximum number of quorums this contract supports uint8 internal constant MAX_QUORUM_COUNT = 192; - /// @notice the ServiceManager for this AVS, which forwards calls onto EigenLayer's core contracts - IServiceManager public immutable serviceManager; + /// @notice + ISocketRegistry public immutable socketRegistry; /// @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 Socket Registry contract that will keep track of operators' sockets - ISocketRegistry public immutable socketRegistry; - /******************************************************************************* - STATE - *******************************************************************************/ + /// EigenLayer contracts + /// @notice the AllocationManager that tracks OperatorSets and Slashing in EigenLayer + IAllocationManager public immutable allocationManager; + + /** + * + * STATE + * + */ /// @notice the current number of quorums supported by the registry coordinator uint8 public quorumCount; @@ -72,21 +85,38 @@ abstract contract RegistryCoordinatorStorage is IRegistryCoordinator { /// @notice the delay in seconds before an operator can reregister after being ejected uint256 public ejectionCooldown; + /// @notice Whether this AVS allows operator sets for registration + /// @dev If true, operators may register to operator sets via the AllocationManager + bool public operatorSetsEnabled; + + /// @notice Whether this AVS allows M2 quorums for registration + /// @dev If true, operators may **not** register to M2 quorums. Deregistration is still allowed. + bool public m2QuorumsDisabled; + + /// @notice The account identifier for this AVS (used for UAM integration in EigenLayer) + /// @dev NOTE: Updating this value will break existing OperatorSets and UAM integration. + /// This value should only be set once. + address public accountIdentifier; + + /// @notice The bitmap containing all M2 quorums. This is only used for existing AVS middlewares that have M2 quorums + /// and need to call `enableOperatorSets()` to enable operator sets mode. + uint256 internal M2quorumBitmap; + constructor( - IServiceManager _serviceManager, IStakeRegistry _stakeRegistry, IBLSApkRegistry _blsApkRegistry, IIndexRegistry _indexRegistry, - ISocketRegistry _socketRegistry + ISocketRegistry _socketRegistry, + IAllocationManager _allocationManager ) { - serviceManager = _serviceManager; stakeRegistry = _stakeRegistry; blsApkRegistry = _blsApkRegistry; indexRegistry = _indexRegistry; socketRegistry = _socketRegistry; + allocationManager = _allocationManager; } // storage gap for upgradeability // slither-disable-next-line shadowing-state - uint256[39] private __GAP; + uint256[37] private __GAP; } diff --git a/src/SocketRegistry.sol b/src/SocketRegistry.sol index 8ede8dda..691af84b 100644 --- a/src/SocketRegistry.sol +++ b/src/SocketRegistry.sol @@ -3,43 +3,47 @@ pragma solidity ^0.8.12; import {IRegistryCoordinator} from "./interfaces/IRegistryCoordinator.sol"; import {ISocketRegistry} from "./interfaces/ISocketRegistry.sol"; +import {SocketRegistryStorage} from "./SocketRegistryStorage.sol"; /** * @title A `Registry` that keeps track of operator sockets. * @author Layr Labs, Inc. */ -contract SocketRegistry is ISocketRegistry { - - /// @notice The address of the RegistryCoordinator - address public immutable registryCoordinator; - - /// @notice A mapping from operator IDs to their sockets - mapping(bytes32 => string) public operatorIdToSocket; - +contract SocketRegistry is ISocketRegistry, SocketRegistryStorage { /// @notice A modifier that only allows the RegistryCoordinator to call a function modifier onlyRegistryCoordinator() { - require(msg.sender == address(registryCoordinator), "SocketRegistry.onlyRegistryCoordinator: caller is not the RegistryCoordinator"); + require( + msg.sender == address(registryCoordinator), + "SocketRegistry.onlyRegistryCoordinator: caller is not the RegistryCoordinator" + ); _; } /// @notice A modifier that only allows the owner of the RegistryCoordinator to call a function modifier onlyCoordinatorOwner() { - require(msg.sender == IRegistryCoordinator(registryCoordinator).owner(), "SocketRegistry.onlyCoordinatorOwner: caller is not the owner of the registryCoordinator"); + require( + msg.sender == IRegistryCoordinator(registryCoordinator).owner(), + "SocketRegistry.onlyCoordinatorOwner: caller is not the owner of the registryCoordinator" + ); _; } - constructor(IRegistryCoordinator _registryCoordinator) { - registryCoordinator = address(_registryCoordinator); - } + constructor( + IRegistryCoordinator _registryCoordinator + ) SocketRegistryStorage(address(_registryCoordinator)) {} /// @notice sets the socket for an operator only callable by the RegistryCoordinator - function setOperatorSocket(bytes32 _operatorId, string memory _socket) external onlyRegistryCoordinator { + function setOperatorSocket( + bytes32 _operatorId, + string memory _socket + ) external onlyRegistryCoordinator { operatorIdToSocket[_operatorId] = _socket; } /// @notice gets the stored socket for an operator - function getOperatorSocket(bytes32 _operatorId) external view returns (string memory) { + function getOperatorSocket( + bytes32 _operatorId + ) external view returns (string memory) { return operatorIdToSocket[_operatorId]; } - } diff --git a/src/SocketRegistryStorage.sol b/src/SocketRegistryStorage.sol new file mode 100644 index 00000000..4f700173 --- /dev/null +++ b/src/SocketRegistryStorage.sol @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.12; + +/** + * @title Storage contract for SocketRegistry + * @author Layr Labs, Inc. + */ +contract SocketRegistryStorage { + /// @notice The address of the RegistryCoordinator + address public immutable registryCoordinator; + + /// @notice A mapping from operator IDs to their sockets + mapping(bytes32 => string) public operatorIdToSocket; + + constructor( + address _registryCoordinator + ) { + registryCoordinator = _registryCoordinator; + } + + uint256[48] private __GAP; +} diff --git a/src/StakeRegistry.sol b/src/StakeRegistry.sol index 929b67d9..4cf3a286 100644 --- a/src/StakeRegistry.sol +++ b/src/StakeRegistry.sol @@ -1,12 +1,17 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; -import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; +import {IDelegationManager} from + "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; +import {IAVSDirectory} from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol"; +import {OperatorSet} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; +import {IAllocationManager} from + "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; import {StakeRegistryStorage, IStrategy} from "./StakeRegistryStorage.sol"; -import {IRegistryCoordinator} from "./interfaces/IRegistryCoordinator.sol"; -import {IStakeRegistry} from "./interfaces/IStakeRegistry.sol"; +import {ISlashingRegistryCoordinator} from "./interfaces/ISlashingRegistryCoordinator.sol"; +import {IStakeRegistry, IStakeRegistryTypes} from "./interfaces/IStakeRegistry.sol"; import {BitmapUtils} from "./libraries/BitmapUtils.sol"; @@ -20,70 +25,66 @@ import {BitmapUtils} from "./libraries/BitmapUtils.sol"; * @author Layr Labs, Inc. */ contract StakeRegistry is StakeRegistryStorage { - using BitmapUtils for *; - - modifier onlyRegistryCoordinator() { - _checkRegistryCoordinator(); + + modifier onlySlashingRegistryCoordinator() { + _checkSlashingRegistryCoordinator(); _; } modifier onlyCoordinatorOwner() { - _checkRegistryCoordinatorOwner(); + _checkSlashingRegistryCoordinatorOwner(); _; } - modifier quorumExists(uint8 quorumNumber) { + modifier quorumExists( + uint8 quorumNumber + ) { _checkQuorumExists(quorumNumber); _; } constructor( - IRegistryCoordinator _registryCoordinator, - IDelegationManager _delegationManager - ) StakeRegistryStorage(_registryCoordinator, _delegationManager) {} - - /******************************************************************************* - EXTERNAL FUNCTIONS - REGISTRY COORDINATOR - *******************************************************************************/ + ISlashingRegistryCoordinator _slashingRegistryCoordinator, + IDelegationManager _delegationManager, + IAVSDirectory _avsDirectory, + IAllocationManager _allocationManager + ) + StakeRegistryStorage( + _slashingRegistryCoordinator, + _delegationManager, + _avsDirectory, + _allocationManager + ) + {} /** - * @notice Registers the `operator` with `operatorId` for the specified `quorumNumbers`. - * @param operator The address of the operator to register. - * @param operatorId The id of the operator to register. - * @param quorumNumbers The quorum numbers the operator is registering for, where each byte is an 8 bit integer quorumNumber. - * @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 - * 2) `quorumNumbers.length` != 0 - * 3) `quorumNumbers` is ordered in ascending order - * 4) the operator is not already registered + * + * EXTERNAL FUNCTIONS - REGISTRY COORDINATOR + * */ + + /// @inheritdoc IStakeRegistry function registerOperator( address operator, bytes32 operatorId, bytes calldata quorumNumbers - ) public virtual onlyRegistryCoordinator returns (uint96[] memory, uint96[] memory) { - + ) public virtual onlySlashingRegistryCoordinator 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++) { - + for (uint256 i = 0; i < quorumNumbers.length; i++) { uint8 quorumNumber = uint8(quorumNumbers[i]); _checkQuorumExists(quorumNumber); // 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" - ); + (uint96 currentStake, bool hasMinimumStake) = + _weightOfOperatorForQuorum(quorumNumber, operator); + require(hasMinimumStake, BelowMinimumStakeRequirement()); // Update the operator's stake int256 stakeDelta = _recordOperatorStakeUpdate({ - operatorId: operatorId, + operatorId: operatorId, quorumNumber: quorumNumber, newStake: currentStake }); @@ -96,22 +97,11 @@ contract StakeRegistry is StakeRegistryStorage { return (currentStakes, totalStakes); } - /** - * @notice Deregisters the operator with `operatorId` for the specified `quorumNumbers`. - * @param operatorId The id of the operator to deregister. - * @param quorumNumbers The quorum numbers the operator is deregistering from, where each byte is an 8 bit integer quorumNumber. - * @dev access restricted to the RegistryCoordinator - * @dev Preconditions (these are assumed, not validated in this contract): - * 1) `quorumNumbers` has no duplicates - * 2) `quorumNumbers.length` != 0 - * 3) `quorumNumbers` is ordered in ascending order - * 4) the operator is not already deregistered - * 5) `quorumNumbers` is a subset of the quorumNumbers that the operator is registered for - */ + /// @inheritdoc IStakeRegistry function deregisterOperator( bytes32 operatorId, bytes calldata quorumNumbers - ) public virtual onlyRegistryCoordinator { + ) public virtual onlySlashingRegistryCoordinator { /** * For each quorum, remove the operator's stake for the quorum and update * the quorum's total stake to account for the removal @@ -122,8 +112,8 @@ contract StakeRegistry is StakeRegistryStorage { // Update the operator's stake for the quorum and retrieve the shares removed int256 stakeDelta = _recordOperatorStakeUpdate({ - operatorId: operatorId, - quorumNumber: quorumNumber, + operatorId: operatorId, + quorumNumber: quorumNumber, newStake: 0 }); @@ -132,20 +122,12 @@ 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 `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. - */ + /// @inheritdoc IStakeRegistry function updateOperatorStake( - address operator, - bytes32 operatorId, + address operator, + bytes32 operatorId, bytes calldata quorumNumbers - ) external onlyRegistryCoordinator returns (uint192) { + ) external onlySlashingRegistryCoordinator returns (uint192) { uint192 quorumsToRemove; /** @@ -162,9 +144,10 @@ contract StakeRegistry is StakeRegistryStorage { // 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); - + (uint96 stakeWeight, bool hasMinimumStake) = + _weightOfOperatorForQuorum(quorumNumber, operator); // If the operator no longer meets the minimum stake, set their stake to zero and mark them for removal + /// also handle setting the operator's stake to 0 and remove them from the quorum if (!hasMinimumStake) { stakeWeight = 0; quorumsToRemove = uint192(quorumsToRemove.setBit(quorumNumber)); @@ -185,98 +168,150 @@ contract StakeRegistry is StakeRegistryStorage { return quorumsToRemove; } - /// @notice Initialize a new quorum and push its first history update - function initializeQuorum( + /// @inheritdoc IStakeRegistry + function initializeDelegatedStakeQuorum( + uint8 quorumNumber, + uint96 minimumStake, + StrategyParams[] memory _strategyParams + ) public virtual onlySlashingRegistryCoordinator { + require(!_quorumExists(quorumNumber), QuorumAlreadyExists()); + _addStrategyParams(quorumNumber, _strategyParams); + _setMinimumStakeForQuorum(quorumNumber, minimumStake); + _setStakeType(quorumNumber, IStakeRegistryTypes.StakeType.TOTAL_DELEGATED); + + _totalStakeHistory[quorumNumber].push( + StakeUpdate({ + updateBlockNumber: uint32(block.number), + nextUpdateBlockNumber: 0, + stake: 0 + }) + ); + } + + /// @inheritdoc IStakeRegistry + function initializeSlashableStakeQuorum( uint8 quorumNumber, uint96 minimumStake, + uint32 lookAheadPeriod, StrategyParams[] memory _strategyParams - ) public virtual onlyRegistryCoordinator { - require(!_quorumExists(quorumNumber), "StakeRegistry.initializeQuorum: quorum already exists"); + ) public virtual onlySlashingRegistryCoordinator { + require(!_quorumExists(quorumNumber), QuorumAlreadyExists()); _addStrategyParams(quorumNumber, _strategyParams); _setMinimumStakeForQuorum(quorumNumber, minimumStake); + _setStakeType(quorumNumber, IStakeRegistryTypes.StakeType.TOTAL_SLASHABLE); + _setLookAheadPeriod(quorumNumber, lookAheadPeriod); - _totalStakeHistory[quorumNumber].push(StakeUpdate({ - updateBlockNumber: uint32(block.number), - nextUpdateBlockNumber: 0, - stake: 0 - })); + _totalStakeHistory[quorumNumber].push( + StakeUpdate({ + updateBlockNumber: uint32(block.number), + nextUpdateBlockNumber: 0, + stake: 0 + }) + ); } + /// @inheritdoc IStakeRegistry function setMinimumStakeForQuorum( - uint8 quorumNumber, + uint8 quorumNumber, uint96 minimumStake ) public virtual onlyCoordinatorOwner 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". - */ + /// @inheritdoc IStakeRegistry + function setSlashableStakeLookahead( + uint8 quorumNumber, + uint32 _lookAheadBlocks + ) external onlyCoordinatorOwner quorumExists(quorumNumber) { + _setLookAheadPeriod(quorumNumber, _lookAheadBlocks); + } + + /// @inheritdoc IStakeRegistry function addStrategies( - uint8 quorumNumber, + uint8 quorumNumber, StrategyParams[] memory _strategyParams ) public virtual onlyCoordinatorOwner quorumExists(quorumNumber) { _addStrategyParams(quorumNumber, _strategyParams); + + uint256 numStratsToAdd = _strategyParams.length; + + if (isOperatorSetQuorum(quorumNumber)) { + IStrategy[] memory strategiesToAdd = new IStrategy[](numStratsToAdd); + for (uint256 i = 0; i < numStratsToAdd; i++) { + strategiesToAdd[i] = _strategyParams[i].strategy; + } + allocationManager.addStrategiesToOperatorSet({ + avs: ISlashingRegistryCoordinator(registryCoordinator).accountIdentifier(), + operatorSetId: quorumNumber, + strategies: strategiesToAdd + }); + } } - /** - * @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 - */ + /// @inheritdoc IStakeRegistry function removeStrategies( uint8 quorumNumber, uint256[] memory indicesToRemove ) public virtual onlyCoordinatorOwner quorumExists(quorumNumber) { uint256 toRemoveLength = indicesToRemove.length; - require(toRemoveLength > 0, "StakeRegistry.removeStrategies: no indices to remove provided"); + require(toRemoveLength > 0, InputArrayLengthZero()); StrategyParams[] storage _strategyParams = strategyParams[quorumNumber]; IStrategy[] storage _strategiesPerQuorum = strategiesPerQuorum[quorumNumber]; + IStrategy[] memory _strategiesToRemove = new IStrategy[](toRemoveLength); for (uint256 i = 0; i < toRemoveLength; i++) { - emit StrategyRemovedFromQuorum(quorumNumber, _strategyParams[indicesToRemove[i]].strategy); - emit StrategyMultiplierUpdated(quorumNumber, _strategyParams[indicesToRemove[i]].strategy, 0); + _strategiesToRemove[i] = _strategyParams[indicesToRemove[i]].strategy; + 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(); - _strategiesPerQuorum[indicesToRemove[i]] = _strategiesPerQuorum[_strategiesPerQuorum.length - 1]; + _strategiesPerQuorum[indicesToRemove[i]] = + _strategiesPerQuorum[_strategiesPerQuorum.length - 1]; _strategiesPerQuorum.pop(); } + + if (isOperatorSetQuorum(quorumNumber)) { + allocationManager.removeStrategiesFromOperatorSet({ + avs: ISlashingRegistryCoordinator(registryCoordinator).accountIdentifier(), + operatorSetId: quorumNumber, + strategies: _strategiesToRemove + }); + } } - /** - * @notice Modifies 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 - */ + /// @inheritdoc IStakeRegistry function modifyStrategyParams( uint8 quorumNumber, uint256[] calldata strategyIndices, uint96[] calldata newMultipliers ) 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"); + require(numStrats > 0, InputArrayLengthZero()); + require(newMultipliers.length == numStrats, InputArrayLengthMismatch()); 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]); + emit StrategyMultiplierUpdated( + quorumNumber, _strategyParams[strategyIndices[i]].strategy, newMultipliers[i] + ); } } - /******************************************************************************* - INTERNAL FUNCTIONS - *******************************************************************************/ - + /** + * + * INTERNAL FUNCTIONS + * + */ function _getStakeUpdateIndexForOperatorAtBlockNumber( bytes32 operatorId, uint8 quorumNumber, @@ -286,7 +321,10 @@ contract StakeRegistry is StakeRegistryStorage { // Iterate backwards through operatorStakeHistory until we find an update that preceeds blockNumber for (uint256 i = length; i > 0; i--) { - if (operatorStakeHistory[operatorId][quorumNumber][i - 1].updateBlockNumber <= blockNumber) { + if ( + operatorStakeHistory[operatorId][quorumNumber][i - 1].updateBlockNumber + <= blockNumber + ) { return uint32(i - 1); } } @@ -311,20 +349,22 @@ contract StakeRegistry is StakeRegistryStorage { uint8 quorumNumber, uint96 newStake ) internal returns (int256) { - uint96 prevStake; uint256 historyLength = operatorStakeHistory[operatorId][quorumNumber].length; if (historyLength == 0) { // No prior stake history - push our first entry - operatorStakeHistory[operatorId][quorumNumber].push(StakeUpdate({ - updateBlockNumber: uint32(block.number), - nextUpdateBlockNumber: 0, - stake: newStake - })); + operatorStakeHistory[operatorId][quorumNumber].push( + StakeUpdate({ + updateBlockNumber: uint32(block.number), + nextUpdateBlockNumber: 0, + stake: newStake + }) + ); } else { // We have prior stake history - fetch our last-recorded stake - StakeUpdate 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 @@ -335,27 +375,32 @@ contract StakeRegistry is StakeRegistryStorage { /** * 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 (lastUpdate.updateBlockNumber == uint32(block.number)) { lastUpdate.stake = newStake; } else { lastUpdate.nextUpdateBlockNumber = uint32(block.number); - operatorStakeHistory[operatorId][quorumNumber].push(StakeUpdate({ - updateBlockNumber: uint32(block.number), - nextUpdateBlockNumber: 0, - stake: newStake - })); + operatorStakeHistory[operatorId][quorumNumber].push( + StakeUpdate({ + updateBlockNumber: uint32(block.number), + nextUpdateBlockNumber: 0, + stake: newStake + }) + ); } } // Log update and return stake delta emit OperatorStakeUpdate(operatorId, quorumNumber, newStake); - return _calculateDelta({ prev: prevStake, cur: newStake }); + return _calculateDelta({prev: prevStake, cur: newStake}); } /// @notice Applies a delta to the total stake recorded for `quorumNumber` /// @return Returns the new total stake for the quorum - function _recordTotalStakeUpdate(uint8 quorumNumber, int256 stakeDelta) internal returns (uint96) { + function _recordTotalStakeUpdate( + uint8 quorumNumber, + int256 stakeDelta + ) internal returns (uint96) { // Get our last-recorded stake update uint256 historyLength = _totalStakeHistory[quorumNumber].length; StakeUpdate storage lastStakeUpdate = _totalStakeHistory[quorumNumber][historyLength - 1]; @@ -364,7 +409,7 @@ contract StakeRegistry is StakeRegistryStorage { 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); @@ -376,17 +421,19 @@ contract StakeRegistry is StakeRegistryStorage { lastStakeUpdate.stake = newStake; } else { lastStakeUpdate.nextUpdateBlockNumber = uint32(block.number); - _totalStakeHistory[quorumNumber].push(StakeUpdate({ - updateBlockNumber: uint32(block.number), - nextUpdateBlockNumber: 0, - stake: newStake - })); + _totalStakeHistory[quorumNumber].push( + StakeUpdate({ + updateBlockNumber: uint32(block.number), + nextUpdateBlockNumber: 0, + stake: newStake + }) + ); } return 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 conscious choice, @@ -396,32 +443,27 @@ contract StakeRegistry is StakeRegistryStorage { uint8 quorumNumber, StrategyParams[] memory _strategyParams ) internal { - require(_strategyParams.length > 0, "StakeRegistry._addStrategyParams: no strategies provided"); + require(_strategyParams.length > 0, InputArrayLengthZero()); uint256 numStratsToAdd = _strategyParams.length; uint256 numStratsExisting = strategyParams[quorumNumber].length; require( numStratsExisting + numStratsToAdd <= MAX_WEIGHING_FUNCTION_LENGTH, - "StakeRegistry._addStrategyParams: exceed MAX_WEIGHING_FUNCTION_LENGTH" + InputArrayLengthMismatch() ); 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); j++) { require( strategyParams[quorumNumber][j].strategy != _strategyParams[i].strategy, - "StakeRegistry._addStrategyParams: cannot add same strategy 2x" + InputDuplicateStrategy() ); } - require( - _strategyParams[i].multiplier > 0, - "StakeRegistry._addStrategyParams: cannot add strategy with zero weight" - ); + require(_strategyParams[i].multiplier > 0, InputMultiplierZero()); strategyParams[quorumNumber].push(_strategyParams[i]); strategiesPerQuorum[quorumNumber].push(_strategyParams[i].strategy); emit StrategyAddedToQuorum(quorumNumber, _strategyParams[i].strategy); emit StrategyMultiplierUpdated( - quorumNumber, - _strategyParams[i].strategy, - _strategyParams[i].multiplier + quorumNumber, _strategyParams[i].strategy, _strategyParams[i].multiplier ); } } @@ -450,14 +492,34 @@ contract StakeRegistry is StakeRegistryStorage { * - blockNumber should be >= the update block number * - the next update block number should be either 0 or strictly greater than blockNumber */ + require(blockNumber >= stakeUpdate.updateBlockNumber, InvalidBlockNumber()); require( - blockNumber >= stakeUpdate.updateBlockNumber, - "StakeRegistry._validateStakeUpdateAtBlockNumber: stakeUpdate is from after blockNumber" + stakeUpdate.nextUpdateBlockNumber == 0 + || blockNumber < stakeUpdate.nextUpdateBlockNumber, + InvalidBlockNumber() ); - require( - stakeUpdate.nextUpdateBlockNumber == 0 || blockNumber < stakeUpdate.nextUpdateBlockNumber, - "StakeRegistry._validateStakeUpdateAtBlockNumber: there is a newer stakeUpdate available before blockNumber" + } + + /// Returns total Slashable stake for an operator per strategy that can have the weights applied based on strategy multipliers + function _getSlashableStakePerStrategy( + uint8 quorumNumber, + address operator + ) internal view returns (uint256[] memory) { + address[] memory operators = new address[](1); + operators[0] = operator; + uint32 beforeTimestamp = + uint32(block.number + slashableStakeLookAheadPerQuorum[quorumNumber]); + + uint256[][] memory slashableShares = allocationManager.getMinimumSlashableStake( + OperatorSet( + ISlashingRegistryCoordinator(registryCoordinator).accountIdentifier(), quorumNumber + ), + operators, + strategiesPerQuorum[quorumNumber], + beforeTimestamp ); + + return slashableShares[0]; } /** @@ -466,19 +528,39 @@ contract StakeRegistry is StakeRegistryStorage { * @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, bool) { + function _weightOfOperatorForQuorum( + uint8 quorumNumber, + address operator + ) internal view virtual returns (uint96, bool) { uint96 weight; uint256 stratsLength = strategyParamsLength(quorumNumber); StrategyParams memory strategyAndMultiplier; - - uint256[] memory strategyShares = delegation.getOperatorShares(operator, strategiesPerQuorum[quorumNumber]); - for (uint256 i = 0; i < stratsLength; i++) { - // accessing i^th StrategyParams struct for the quorumNumber - strategyAndMultiplier = strategyParams[quorumNumber][i]; - - // add the weight from the shares for this strategy to the total weight - if (strategyShares[i] > 0) { - weight += uint96(strategyShares[i] * strategyAndMultiplier.multiplier / WEIGHTING_DIVISOR); + uint256[] memory strategyShares; + + if (stakeTypePerQuorum[quorumNumber] == IStakeRegistryTypes.StakeType.TOTAL_SLASHABLE) { + strategyShares = _getSlashableStakePerStrategy(quorumNumber, operator); + for (uint256 i = 0; i < stratsLength; i++) { + strategyAndMultiplier = strategyParams[quorumNumber][i]; + if (strategyShares[i] > 0) { + weight += uint96( + strategyShares[i] * strategyAndMultiplier.multiplier / WEIGHTING_DIVISOR + ); + } + } + } else { + /// M2 Concept of delegated stake + strategyShares = + delegation.getOperatorShares(operator, strategiesPerQuorum[quorumNumber]); + for (uint256 i = 0; i < stratsLength; i++) { + // accessing i^th StrategyParams struct for the quorumNumber + strategyAndMultiplier = strategyParams[quorumNumber][i]; + + // add the weight from the shares for this strategy to the total weight + if (strategyShares[i] > 0) { + weight += uint96( + strategyShares[i] * strategyAndMultiplier.multiplier / WEIGHTING_DIVISOR + ); + } } } @@ -488,47 +570,58 @@ contract StakeRegistry is StakeRegistryStorage { } /// @notice Returns `true` if the quorum has been initialized - function _quorumExists(uint8 quorumNumber) internal view returns (bool) { + function _quorumExists( + uint8 quorumNumber + ) internal view returns (bool) { return _totalStakeHistory[quorumNumber].length != 0; } - /******************************************************************************* - 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 + * + * VIEW FUNCTIONS + * */ + + /// @inheritdoc IStakeRegistry + function isOperatorSetQuorum( + uint8 quorumNumber + ) public view returns (bool) { + bool isM2 = ISlashingRegistryCoordinator(registryCoordinator).isM2Quorum(quorumNumber); + bool isOperatorSet = ISlashingRegistryCoordinator(registryCoordinator).operatorSetsEnabled(); + return isOperatorSet && !isM2; + } + + /// @inheritdoc IStakeRegistry function weightOfOperatorForQuorum( - uint8 quorumNumber, + uint8 quorumNumber, address operator - ) public virtual view quorumExists(quorumNumber) returns (uint96) { - (uint96 stake, ) = _weightOfOperatorForQuorum(quorumNumber, operator); + ) public view virtual quorumExists(quorumNumber) returns (uint96) { + (uint96 stake,) = _weightOfOperatorForQuorum(quorumNumber, operator); return stake; } - /// @notice Returns the length of the dynamic array stored in `strategyParams[quorumNumber]`. - function strategyParamsLength(uint8 quorumNumber) public view returns (uint256) { + /// @inheritdoc IStakeRegistry + 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` + /// @inheritdoc IStakeRegistry function strategyParamsByIndex( - uint8 quorumNumber, + uint8 quorumNumber, uint256 index - ) public view returns (StrategyParams memory) - { + ) 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 + * + * VIEW FUNCTIONS - Operator Stake History + * */ + + /// @inheritdoc IStakeRegistry function getStakeHistoryLength( bytes32 operatorId, uint8 quorumNumber @@ -536,31 +629,24 @@ contract StakeRegistry is StakeRegistryStorage { return operatorStakeHistory[operatorId][quorumNumber].length; } - /** - * @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. - */ + /// @inheritdoc IStakeRegistry function getStakeHistory( - bytes32 operatorId, + bytes32 operatorId, uint8 quorumNumber ) external view returns (StakeUpdate[] 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) { + /// @inheritdoc IStakeRegistry + function getCurrentStake( + bytes32 operatorId, + uint8 quorumNumber + ) external view returns (uint96) { 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 StakeUpdate struct with **every entry equal to 0** in the event that the operator has no stake history - */ + /// @inheritdoc IStakeRegistry function getLatestStakeUpdate( bytes32 operatorId, uint8 quorumNumber @@ -575,13 +661,7 @@ contract StakeRegistry is StakeRegistryStorage { } } - /** - * @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 `operatorStakeHistory[operatorId][quorumNumber]`. - * @dev Function will revert if `index` is out-of-bounds. - */ + /// @inheritdoc IStakeRegistry function getStakeUpdateAtIndex( uint8 quorumNumber, bytes32 operatorId, @@ -590,19 +670,18 @@ contract StakeRegistry is StakeRegistryStorage { return operatorStakeHistory[operatorId][quorumNumber][index]; } - /// @notice Returns the stake of the operator for the provided `quorumNumber` at the given `blockNumber` + /// @inheritdoc IStakeRegistry function getStakeAtBlockNumber( bytes32 operatorId, uint8 quorumNumber, uint32 blockNumber ) external view returns (uint96) { - return - operatorStakeHistory[operatorId][quorumNumber][ - _getStakeUpdateIndexForOperatorAtBlockNumber(operatorId, quorumNumber, blockNumber) - ].stake; + 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` + /// @inheritdoc IStakeRegistry function getStakeUpdateIndexAtBlockNumber( bytes32 operatorId, uint8 quorumNumber, @@ -611,66 +690,48 @@ contract StakeRegistry is StakeRegistryStorage { return _getStakeUpdateIndexForOperatorAtBlockNumber(operatorId, quorumNumber, blockNumber); } - /** - * @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 - * 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 `operatorStakeHistory[operatorId][quorumNumber]`. - * @param blockNumber Block number to make sure the stake is from. - * @dev Function will revert if `index` is out-of-bounds. - */ + /// @inheritdoc IStakeRegistry function getStakeAtBlockNumberAndIndex( uint8 quorumNumber, uint32 blockNumber, bytes32 operatorId, uint256 index ) external view returns (uint96) { - StakeUpdate memory operatorStakeUpdate = operatorStakeHistory[operatorId][quorumNumber][index]; + StakeUpdate memory operatorStakeUpdate = + operatorStakeHistory[operatorId][quorumNumber][index]; _validateStakeUpdateAtBlockNumber(operatorStakeUpdate, blockNumber); return operatorStakeUpdate.stake; } - /******************************************************************************* - VIEW FUNCTIONS - Total Stake History - *******************************************************************************/ - /** - * @notice Returns the length of the total stake history for the given quorum + * + * VIEW FUNCTIONS - Total Stake History + * */ - function getTotalStakeHistoryLength(uint8 quorumNumber) external view returns (uint256) { + + /// @inheritdoc IStakeRegistry + 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) { + /// @inheritdoc IStakeRegistry + 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]`. - */ + /// @inheritdoc IStakeRegistry function getTotalStakeUpdateAtIndex( uint8 quorumNumber, uint256 index ) external view returns (StakeUpdate 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. - * @param quorumNumber The quorum number to get the stake for. - * @param index Array index for lookup, within the dynamic array `_totalStakeHistory[quorumNumber]`. - * @param blockNumber Block number to make sure the stake is from. - * @dev Function will revert if `index` is out-of-bounds. - */ + /// @inheritdoc IStakeRegistry function getTotalStakeAtBlockNumberFromIndex( uint8 quorumNumber, uint32 blockNumber, @@ -681,12 +742,7 @@ contract StakeRegistry is StakeRegistryStorage { return totalStakeUpdate.stake; } - /** - * @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` - */ + /// @inheritdoc IStakeRegistry function getTotalStakeIndicesAtBlockNumber( uint32 blockNumber, bytes calldata quorumNumbers @@ -697,11 +753,14 @@ contract StakeRegistry is StakeRegistryStorage { _checkQuorumExists(quorumNumber); require( _totalStakeHistory[quorumNumber][0].updateBlockNumber <= blockNumber, - "StakeRegistry.getTotalStakeIndicesAtBlockNumber: quorum has no stake history at blockNumber" + EmptyStakeHistory() ); uint256 length = _totalStakeHistory[quorumNumber].length; for (uint256 j = 0; j < length; j++) { - if (_totalStakeHistory[quorumNumber][length - j - 1].updateBlockNumber <= blockNumber) { + if ( + _totalStakeHistory[quorumNumber][length - j - 1].updateBlockNumber + <= blockNumber + ) { indices[i] = uint32(length - j - 1); break; } @@ -710,18 +769,41 @@ contract StakeRegistry is StakeRegistryStorage { return indices; } - function _checkRegistryCoordinator() internal view { - require( - msg.sender == address(registryCoordinator), - "StakeRegistry.onlyRegistryCoordinator: caller is not the RegistryCoordinator" - ); + /** + * @notice Sets the stake type for the registry for a specific quorum + * @param quorumNumber The quorum number to set the stake type for + * @param _stakeType The type of stake to track (TOTAL_DELEGATED, TOTAL_SLASHABLE, or BOTH) + */ + function _setStakeType(uint8 quorumNumber, IStakeRegistryTypes.StakeType _stakeType) internal { + stakeTypePerQuorum[quorumNumber] = _stakeType; + emit StakeTypeSet(_stakeType); + } + + /** + * @notice Sets the look ahead time for checking operator shares for a specific quorum + * @param quorumNumber The quorum number to set the look ahead period for + * @param _lookAheadBlocks The number of blocks to look ahead when checking shares + */ + function _setLookAheadPeriod(uint8 quorumNumber, uint32 _lookAheadBlocks) internal { + uint32 oldLookAheadDays = slashableStakeLookAheadPerQuorum[quorumNumber]; + slashableStakeLookAheadPerQuorum[quorumNumber] = _lookAheadBlocks; + emit LookAheadPeriodChanged(oldLookAheadDays, _lookAheadBlocks); } - function _checkRegistryCoordinatorOwner() internal view { - require(msg.sender == IRegistryCoordinator(registryCoordinator).owner(), "StakeRegistry.onlyCoordinatorOwner: caller is not the owner of the registryCoordinator"); + function _checkSlashingRegistryCoordinator() internal view { + require(msg.sender == registryCoordinator, OnlySlashingRegistryCoordinator()); } - function _checkQuorumExists(uint8 quorumNumber) internal view { - require(_quorumExists(quorumNumber), "StakeRegistry.quorumExists: quorum does not exist"); + function _checkSlashingRegistryCoordinatorOwner() internal view { + require( + msg.sender == ISlashingRegistryCoordinator(registryCoordinator).owner(), + OnlySlashingRegistryCoordinatorOwner() + ); + } + + function _checkQuorumExists( + uint8 quorumNumber + ) internal view { + require(_quorumExists(quorumNumber), QuorumDoesNotExist()); } } diff --git a/src/StakeRegistryStorage.sol b/src/StakeRegistryStorage.sol index 6ef74e3e..2c00aec2 100644 --- a/src/StakeRegistryStorage.sol +++ b/src/StakeRegistryStorage.sol @@ -1,11 +1,18 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; -import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; -import {IStrategyManager, IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategyManager.sol"; +import {IDelegationManager} from + "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; +import {IAVSDirectory} from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol"; +import {IAllocationManager} from + "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; +import { + IStrategyManager, + IStrategy +} from "eigenlayer-contracts/src/contracts/interfaces/IStrategyManager.sol"; -import {IRegistryCoordinator} from "./interfaces/IRegistryCoordinator.sol"; -import {IStakeRegistry} from "./interfaces/IStakeRegistry.sol"; +import {ISlashingRegistryCoordinator} from "./interfaces/ISlashingRegistryCoordinator.sol"; +import {IStakeRegistry, IStakeRegistryTypes} from "./interfaces/IStakeRegistry.sol"; /** * @title Storage variables for the `StakeRegistry` contract. @@ -13,7 +20,6 @@ import {IStakeRegistry} from "./interfaces/IStakeRegistry.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 `strategyParams` mapping. @@ -24,6 +30,12 @@ abstract contract StakeRegistryStorage is IStakeRegistry { /// @notice The address of the Delegation contract for EigenLayer. IDelegationManager public immutable delegation; + /// @notice The address of the Delegation contract for EigenLayer. + IAVSDirectory public immutable avsDirectory; + + /// @notice the address of the AllocationManager for EigenLayer. + IAllocationManager public immutable allocationManager; + /// @notice the coordinator contract that this registry is associated with address public immutable registryCoordinator; @@ -35,25 +47,36 @@ abstract contract StakeRegistryStorage is IStakeRegistry { mapping(uint8 => StakeUpdate[]) internal _totalStakeHistory; /// @notice mapping from operator's operatorId to the history of their stake updates - mapping(bytes32 => mapping(uint8 => StakeUpdate[])) internal operatorStakeHistory; + mapping(bytes32 operatorId => mapping(uint8 => StakeUpdate[])) internal operatorStakeHistory; /** * @notice mapping from quorum number to the list of strategies considered and their * corresponding multipliers for that specific quorum */ - mapping(uint8 => StrategyParams[]) public strategyParams; - mapping(uint8 => IStrategy[]) public strategiesPerQuorum; + mapping(uint8 quorumNumber => StrategyParams[]) public strategyParams; + + /// @notice mapping from quorum number to the list of strategies considered for that specific quorum + mapping(uint8 quorumNumber => IStrategy[]) public strategiesPerQuorum; + + /// @notice mapping from quorum number to the IStakeRegistryTypes.StakeType for that specific quorum + mapping(uint8 quorumNumber => IStakeRegistryTypes.StakeType) public stakeTypePerQuorum; + /// @notice mapping from quorum number to the slashable stake look ahead time (in blocks) + mapping(uint8 quorumNumber => uint32) public slashableStakeLookAheadPerQuorum; constructor( - IRegistryCoordinator _registryCoordinator, - IDelegationManager _delegationManager + ISlashingRegistryCoordinator _slashingRegistryCoordinator, + IDelegationManager _delegationManager, + IAVSDirectory _avsDirectory, + IAllocationManager _allocationManager ) { - registryCoordinator = address(_registryCoordinator); + registryCoordinator = address(_slashingRegistryCoordinator); delegation = _delegationManager; + avsDirectory = _avsDirectory; + allocationManager = _allocationManager; } // storage gap for upgradeability // slither-disable-next-line shadowing-state - uint256[45] private __GAP; + uint256[43] private __GAP; } diff --git a/src/interfaces/IBLSApkRegistry.sol b/src/interfaces/IBLSApkRegistry.sol index 2812a0ce..05a5c229 100644 --- a/src/interfaces/IBLSApkRegistry.sol +++ b/src/interfaces/IBLSApkRegistry.sol @@ -1,142 +1,278 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; - -import {IRegistry} from "./IRegistry.sol"; +pragma solidity ^0.8.27; import {BN254} from "../libraries/BN254.sol"; -/** - * @title Minimal interface for a registry that keeps track of aggregate operator public keys across many quorums. - * @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 +interface IBLSApkRegistryErrors { + /// @notice Thrown when a non-RegistryCoordinator address calls a restricted function. + error OnlyRegistryCoordinatorOwner(); + /// @notice Thrown when attempting to initialize a quorum that already exists. + error QuorumAlreadyExists(); + /// @notice Thrown when a quorum does not exist. + error QuorumDoesNotExist(); + /// @notice Thrown when a BLS pubkey provided is zero pubkey + error ZeroPubKey(); + /// @notice Thrown when an operator has already registered a BLS pubkey. + error OperatorAlreadyRegistered(); + /// @notice Thrown when the operator is not registered. + error OperatorNotRegistered(); + /// @notice Thrown when a BLS pubkey has already been registered for an operator. + error BLSPubkeyAlreadyRegistered(); + /// @notice Thrown when either the G1 signature is wrong, or G1 and G2 private key do not match. + error InvalidBLSSignatureOrPrivateKey(); + /// @notice Thrown when the quorum apk update block number is too recent. + error BlockNumberTooRecent(); + /// @notice Thrown when blocknumber and index provided is not the latest apk update. + error BlockNumberNotLatest(); +} + +interface IBLSApkRegistryTypes { + /// @notice Tracks the history of aggregate public key updates for a quorum. + /// @dev Each update contains a hash of the aggregate public key and block numbers for timing. + /// @param apkHash First 24 bytes of keccak256(apk_x0, apk_x1, apk_y0, apk_y1) representing the aggregate public key. + /// @param updateBlockNumber Block number when this update occurred (inclusive). + /// @param nextUpdateBlockNumber Block number when the next update occurred (exclusive), or 0 if this is the latest update. 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 pubkeyRegistrationSignature is the registration message 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 - */ + /// @notice Parameters required when registering a new BLS public key. + /// @dev Contains the registration signature and both G1/G2 public key components. + /// @param pubkeyRegistrationSignature Registration message signed by operator's private key to prove ownership. + /// @param pubkeyG1 The operator's public key in G1 group format. + /// @param pubkeyG2 The operator's public key in G2 group format, must correspond to the same private key as pubkeyG1. 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); - - // @notice Emitted when a new operator pubkey is registered for a set of quorums - event OperatorAddedToQuorums( - address operator, - bytes32 operatorId, - bytes quorumNumbers +interface IBLSApkRegistryEvents is IBLSApkRegistryTypes { + /* + * @notice Emitted when `operator` registers their BLS public key pair (`pubkeyG1` and `pubkeyG2`). + * @param operator The address of the operator registering the keys. + * @param pubkeyG1 The operator's G1 public key. + * @param pubkeyG2 The operator's G2 public key. + */ + event NewPubkeyRegistration( + address indexed operator, BN254.G1Point pubkeyG1, BN254.G2Point pubkeyG2 ); - // @notice Emitted when an operator pubkey is removed from a set of quorums - event OperatorRemovedFromQuorums( - address operator, - bytes32 operatorId, - bytes quorumNumbers - ); + /* + * @notice Emitted when `operator`'s pubkey is registered for `quorumNumbers`. + * @param operator The address of the operator being registered. + * @param operatorId The unique identifier for this operator (pubkey hash). + * @param quorumNumbers The quorum numbers the operator is being registered for. + */ + event OperatorAddedToQuorums(address operator, bytes32 operatorId, bytes quorumNumbers); + + /* + * @notice Emitted when `operator`'s pubkey is deregistered from `quorumNumbers`. + * @param operator The address of the operator being deregistered. + * @param operatorId The unique identifier for this operator (pubkey hash). + * @param quorumNumbers The quorum numbers the operator is being deregistered from. + */ + event OperatorRemovedFromQuorums(address operator, bytes32 operatorId, bytes quorumNumbers); +} + +interface IBLSApkRegistry is IBLSApkRegistryErrors, IBLSApkRegistryEvents { + /* STORAGE */ + + /* + * @notice Returns the address of the registry coordinator contract. + * @return The address of the registry coordinator. + * @dev This value is immutable and set during contract construction. + */ + function registryCoordinator() external view returns (address); + + /* + * @notice Maps `operator` to their BLS public key hash (`operatorId`). + * @param operator The address of the operator. + * @return operatorId The hash of the operator's BLS public key. + */ + function operatorToPubkeyHash( + address operator + ) external view returns (bytes32 operatorId); + + /* + * @notice Maps `pubkeyHash` to their corresponding `operator` address. + * @param pubkeyHash The hash of a BLS public key. + * @return operator The address of the operator who registered this public key. + */ + function pubkeyHashToOperator( + bytes32 pubkeyHash + ) external view returns (address operator); - /** - * @notice Registers the `operator`'s pubkey for the specified `quorumNumbers`. + /* + * @notice Maps `operator` to their BLS public key in G1. + * @dev Returns a non-encoded BN254.G1Point. + * @param operator The address of the operator. + * @return The operator's BLS public key in G1. + */ + function operatorToPubkey( + address operator + ) external view returns (uint256, uint256); + + /* + * @notice Stores the history of aggregate public key updates for `quorumNumber` at `index`. + * @dev Returns a non-encoded IBLSApkRegistryTypes.ApkUpdate. + * @param quorumNumber The identifier of the quorum. + * @param index The index in the history array. + * @return The APK update entry at the specified index for the given quorum. + * @dev Each entry contains the APK hash, update block number, and next update block number. + */ + function apkHistory( + uint8 quorumNumber, + uint256 index + ) external view returns (bytes24, uint32, uint32); + + /* + * @notice Maps `quorumNumber` to their current aggregate public key. + * @dev Returns a non-encoded BN254.G1Point. + * @param quorumNumber The identifier of the quorum. + * @return The current APK as a G1 point. + */ + function currentApk( + uint8 quorumNumber + ) external view returns (uint256, uint256); + + /* ACTIONS */ + + /* + * @notice Registers `operator`'s pubkey for `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 + * @param quorumNumbers The quorum numbers to register for, where each byte is an 8-bit integer. + * @dev Access restricted to the RegistryCoordinator. + * @dev Preconditions (assumed, not validated): + * 1. `quorumNumbers` has no duplicatesd + * 2. `quorumNumbers.length` != 0 + * 3. `quorumNumbers` is ordered ascending + * 4. The operator is not already registered */ function registerOperator(address operator, bytes calldata quorumNumbers) external; - /** - * @notice Deregisters the `operator`'s pubkey for the specified `quorumNumbers`. + /* + * @notice Deregisters `operator`'s pubkey from `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 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. + * @param quorumNumbers The quorum numbers to deregister from, where each byte is an 8-bit integer. + * @dev Access restricted to the RegistryCoordinator. + * @dev Preconditions (assumed, not validated): + * 1. `quorumNumbers` has no duplicates + * 2. `quorumNumbers.length` != 0 + * 3. `quorumNumbers` is ordered ascending + * 4. The operator is not already deregistered + * 5. `quorumNumbers` is a subset of the operator's registered quorums */ - function operatorToPubkeyHash(address operator) external view returns (bytes32); + function deregisterOperator(address operator, bytes calldata quorumNumbers) external; - /** - * @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`. + /* + * @notice Initializes `quorumNumber` by pushing its first APK update. + * @param quorumNumber The number of the new quorum. */ - function pubkeyHashToOperator(bytes32 pubkeyHash) external view returns (address); + function initializeQuorum( + uint8 quorumNumber + ) external; - /** - * @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 + /* + * @notice Registers `operator` as the owner of a BLS public key using `params` and `pubkeyRegistrationMessageHash`. + * @param operator The operator for whom the key is being registered. + * @param params Contains the G1 & G2 public keys and ownership proof signature. + * @param pubkeyRegistrationMessageHash The hash that must be signed to prove key ownership. + * @return operatorId The unique identifier (pubkey hash) for this operator. + * @dev Called by the RegistryCoordinator. */ function registerBLSPublicKey( address operator, - PubkeyRegistrationParams calldata params, + IBLSApkRegistryTypes.PubkeyRegistrationParams calldata params, BN254.G1Point calldata pubkeyRegistrationMessageHash ) external returns (bytes32 operatorId); - /** - * @notice Returns the pubkey and pubkey hash of an operator - * @dev Reverts if the operator has not registered a valid pubkey + /* VIEW */ + + /* + * @notice Returns the pubkey and pubkey hash of `operator`. + * @param operator The address of the operator. + * @return The operator's G1 public key and its hash. + * @dev Reverts if the operator has not registered a valid pubkey. */ - function getRegisteredPubkey(address operator) external view returns (BN254.G1Point memory, bytes32); + function getRegisteredPubkey( + address operator + ) external view returns (BN254.G1Point memory, bytes32); - /// @notice Returns the current APK for the provided `quorumNumber ` - function getApk(uint8 quorumNumber) external view returns (BN254.G1Point memory); + /* + * @notice Returns the APK indices at `blockNumber` for `quorumNumbers`. + * @param quorumNumbers The quorum numbers to get indices for. + * @param blockNumber The block number to query at. + * @return Array of indices corresponding to each quorum number. + */ + function getApkIndicesAtBlockNumber( + bytes calldata quorumNumbers, + uint256 blockNumber + ) external view returns (uint32[] 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 current aggregate public key for `quorumNumber`. + * @param quorumNumber The quorum to query. + * @return The current APK as a G1 point. + */ + function getApk( + uint8 quorumNumber + ) external view returns (BN254.G1Point 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 an APK update entry for `quorumNumber` at `index`. + * @param quorumNumber The quorum to query. + * @param index The index in the APK history. + * @return The APK update entry. + */ + function getApkUpdateAtIndex( + uint8 quorumNumber, + uint256 index + ) external view returns (IBLSApkRegistryTypes.ApkUpdate memory); - /// @notice Returns the operator address for the given `pubkeyHash` - function getOperatorFromPubkeyHash(bytes32 pubkeyHash) external view returns (address); + /* + * @notice Gets the 24-byte hash of `quorumNumber`'s APK at `blockNumber` and `index`. + * @param quorumNumber The quorum to query. + * @param blockNumber The block number to get the APK hash for. + * @param index The index in the APK history. + * @return The 24-byte APK hash. + * @dev Called by checkSignatures in BLSSignatureChecker.sol. + */ + function getApkHashAtBlockNumberAndIndex( + uint8 quorumNumber, + uint32 blockNumber, + uint256 index + ) external view returns (bytes24); + + /* + * @notice Returns the number of APK updates for `quorumNumber`. + * @param quorumNumber The quorum to query. + * @return The length of the APK history. + */ + function getApkHistoryLength( + uint8 quorumNumber + ) external view returns (uint32); - /** - * @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 + /* + * @notice Maps `operator` to their corresponding public key hash. + * @param operator The address of the operator. + * @return operatorId The hash of the operator's BLS public key. + * @dev Returns bytes32(0) if the operator hasn't registered a key. */ - function getApkHashAtBlockNumberAndIndex(uint8 quorumNumber, uint32 blockNumber, uint256 index) external view returns (bytes24); + function getOperatorId( + address operator + ) external view returns (bytes32 operatorId); - /// @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); + /* + * @notice Maps `pubkeyHash` to their corresponding operator address. + * @param pubkeyHash The hash of a BLS public key. + * @return operator The address of the operator who registered this public key. + * @dev Returns address(0) if the public key hash hasn't been registered. + */ + function getOperatorFromPubkeyHash( + bytes32 pubkeyHash + ) external view returns (address operator); } diff --git a/src/interfaces/IBLSSignatureChecker.sol b/src/interfaces/IBLSSignatureChecker.sol index 844c7217..e7b78a04 100644 --- a/src/interfaces/IBLSSignatureChecker.sol +++ b/src/interfaces/IBLSSignatureChecker.sol @@ -1,82 +1,174 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; -import {IRegistryCoordinator} from "./IRegistryCoordinator.sol"; +import {ISlashingRegistryCoordinator} from "./ISlashingRegistryCoordinator.sol"; import {IBLSApkRegistry} from "./IBLSApkRegistry.sol"; import {IStakeRegistry, IDelegationManager} 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/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. - */ -interface IBLSSignatureChecker { - // DATA STRUCTURES +interface IBLSSignatureCheckerErrors { + /// @notice Thrown when the caller is not the registry coordinator owner. + error OnlyRegistryCoordinatorOwner(); + /// @notice Thrown when the quorum numbers input in is empty. + error InputEmptyQuorumNumbers(); + /// @notice Thrown when two array parameters have mismatching lengths. + error InputArrayLengthMismatch(); + /// @notice Thrown when the non-signer pubkey length does not match non-signer bitmap indices length. + error InputNonSignerLengthMismatch(); + /// @notice Thrown when the reference block number is invalid. + error InvalidReferenceBlocknumber(); + /// @notice Thrown when the non signer pubkeys are not sorted. + error NonSignerPubkeysNotSorted(); + /// @notice Thrown when StakeRegistry updates have not been updated within withdrawalDelayBlocks window + error StaleStakesForbidden(); + /// @notice Thrown when the quorum apk hash in storage does not match provided quorum apk. + error InvalidQuorumApkHash(); + /// @notice Thrown when BLS pairing precompile call fails. + error InvalidBLSPairingKey(); + /// @notice Thrown when BLS signature is invalid. + error InvalidBLSSignature(); +} - struct NonSignerStakesAndSignature { - uint32[] nonSignerQuorumBitmapIndices; // is the indices of all nonsigner quorum bitmaps - BN254.G1Point[] nonSignerPubkeys; // is the G1 pubkeys of all nonsigners - BN254.G1Point[] quorumApks; // is the aggregate G1 pubkey of each quorum - BN254.G2Point apkG2; // is the aggregate G2 pubkey of all signers - BN254.G1Point sigma; // is the aggregate G1 signature of all signers - uint32[] quorumApkIndices; // is the indices of each quorum aggregate pubkey - uint32[] totalStakeIndices; // is the indices of each quorums total stake - uint32[][] nonSignerStakeIndices; // is the indices of each non signers stake within a quorum +interface IBLSSignatureCheckerTypes { + /// @notice Contains bitmap and pubkey hash information for non-signing operators. + /// @param quorumBitmaps Array of bitmaps indicating which quorums each non-signer was registered for. + /// @param pubkeyHashes Array of BLS public key hashes for each non-signer. + struct NonSignerInfo { + uint256[] quorumBitmaps; + bytes32[] pubkeyHashes; } - /** - * @notice this data structure is used for recording the details on the total stake of the registered - * operators and those operators who are part of the quorum for a particular taskNumber - */ + /// @notice Contains non-signer information and aggregated signature data for BLS verification. + /// @param nonSignerQuorumBitmapIndices The indices of all non-signer quorum bitmaps. + /// @param nonSignerPubkeys The G1 public keys of all non-signers. + /// @param quorumApks The aggregate G1 public key of each quorum. + /// @param apkG2 The aggregate G2 public key of all signers. + /// @param sigma The aggregate G1 signature of all signers. + /// @param quorumApkIndices The indices of each quorum's aggregate public key in the APK registry. + /// @param totalStakeIndices The indices of each quorum's total stake in the stake registry. + /// @param nonSignerStakeIndices The indices of each non-signer's stake within each quorum. + /// @dev Used as input to checkSignatures() to verify BLS signatures. + struct NonSignerStakesAndSignature { + uint32[] nonSignerQuorumBitmapIndices; + BN254.G1Point[] nonSignerPubkeys; + BN254.G1Point[] quorumApks; + BN254.G2Point apkG2; + BN254.G1Point sigma; + uint32[] quorumApkIndices; + uint32[] totalStakeIndices; + uint32[][] nonSignerStakeIndices; + } + /// @notice Records the total stake amounts for operators in each quorum. + /// @param signedStakeForQuorum Array of total stake amounts from operators who signed, per quorum. + /// @param totalStakeForQuorum Array of total stake amounts from all operators, per quorum. + /// @dev Used to track stake distribution and calculate quorum thresholds. Array indices correspond to quorum numbers. struct QuorumStakeTotals { - // total stake of the operators in each quorum uint96[] signedStakeForQuorum; - // total amount staked by all operators in each quorum uint96[] totalStakeForQuorum; } +} - // EVENTS +interface IBLSSignatureCheckerEvents is IBLSSignatureCheckerTypes { + /// @notice Emitted when `staleStakesForbiddenUpdate` is set. + event StaleStakesForbiddenUpdate(bool value); +} + +interface IBLSSignatureChecker is IBLSSignatureCheckerErrors, IBLSSignatureCheckerEvents { + /* STATE */ - /// @notice Emitted when `staleStakesForbiddenUpdate` is set - event StaleStakesForbiddenUpdate(bool value); - - // CONSTANTS & IMMUTABLES + /* + * @notice Returns the address of the registry coordinator contract. + * @return The address of the registry coordinator. + * @dev This value is immutable and set during contract construction. + */ + function registryCoordinator() external view returns (ISlashingRegistryCoordinator); - function registryCoordinator() external view returns (IRegistryCoordinator); + /* + * @notice Returns the address of the stake registry contract. + * @return The address of the stake registry. + * @dev This value is immutable and set during contract construction. + */ function stakeRegistry() external view returns (IStakeRegistry); + + /* + * @notice Returns the address of the BLS APK registry contract. + * @return The address of the BLS APK registry. + * @dev This value is immutable and set during contract construction. + */ function blsApkRegistry() external view returns (IBLSApkRegistry); + + /* + * @notice Returns the address of the delegation manager contract. + * @return The address of the delegation manager. + * @dev This value is immutable and set during contract construction. + */ function delegation() external view returns (IDelegationManager); - /** + /* + * @notice Returns whether stale stakes are forbidden in signature verification. + * @return True if stale stakes are forbidden, false otherwise. + */ + function staleStakesForbidden() external view returns (bool); + + /* ACTIONS */ + + /* + * @notice Sets `value` as the new staleStakesForbidden flag. + * @param value True to forbid stale stakes, false to allow them. + * @dev Access restricted to the registry coordinator owner. + */ + function setStaleStakesForbidden( + bool value + ) external; + + /* VIEW */ + + /* * @notice This function is called by disperser when it has aggregated all the signatures of the operators * that are part of the quorum for a particular taskNumber and is asserting them into onchain. The function * checks that the claim for aggregated signatures are valid. * * The thesis of this procedure entails: - * - getting the aggregated pubkey of all registered nodes at the time of pre-commit by the - * disperser (represented by apk in the parameters), - * - subtracting the pubkeys of all the signers not in the quorum (nonSignerPubkeys) and storing - * the output in apk to get aggregated pubkey of all operators that are part of quorum. - * - use this aggregated pubkey to verify the aggregated signature under BLS scheme. - * - * @dev Before signature verification, the function verifies operator stake information. This includes ensuring that the provided `referenceBlockNumber` - * is correct, i.e., ensure that the stake returned from the specified block number is recent enough and that the stake is either the most recent update - * for the total stake (or the operator) or latest before the referenceBlockNumber. + * 1. Getting the aggregated pubkey of all registered nodes at the time of pre-commit by the + * disperser (represented by apk in the parameters) + * 2. Subtracting the pubkeys of all non-signers (nonSignerPubkeys) and storing + * the output in apk to get aggregated pubkey of all operators that are part of quorum + * 3. Using this aggregated pubkey to verify the aggregated signature under BLS scheme + * + * @param msgHash The hash of the message that was signed. NOTE: Be careful to ensure msgHash is + * collision-resistant! This method does not hash msgHash in any way, so if an attacker is able + * to pass in an arbitrary value, they may be able to tamper with signature verification. + * @param quorumNumbers The quorum numbers to verify signatures for, where each byte is an 8-bit integer. + * @param referenceBlockNumber The block number at which the stake information is being verified + * @param nonSignerStakesAndSignature Contains non-signer information and aggregated signature data. + * @return quorumStakeTotals The struct containing the total and signed stake for each quorum + * @return signatoryRecordHash The hash of the signatory record, which is used for fraud proofs + * @dev Before signature verification, the function verifies operator stake information. This includes + * ensuring that the provided referenceBlockNumber is valid and recent enough, and that the stake is + * either the most recent update for the total stake (of the operator) or latest before the referenceBlockNumber. */ function checkSignatures( - bytes32 msgHash, + bytes32 msgHash, bytes calldata quorumNumbers, - uint32 referenceBlockNumber, + uint32 referenceBlockNumber, NonSignerStakesAndSignature memory nonSignerStakesAndSignature - ) - external - view - returns ( - QuorumStakeTotals memory, - bytes32 - ); + ) external view returns (QuorumStakeTotals memory, bytes32); + + /* + * @notice Attempts to verify signature `sigma` against message hash `msgHash` using aggregate public keys `apk` and `apkG2`. + * @param msgHash The hash of the message that was signed. + * @param apk The aggregate public key in G1. + * @param apkG2 The aggregate public key in G2. + * @param sigma The signature to verify. + * @return pairingSuccessful True if the pairing check succeeded. + * @return siganatureIsValid True if the signature is valid. + */ + function trySignatureAndApkVerification( + bytes32 msgHash, + BN254.G1Point memory apk, + BN254.G2Point memory apkG2, + BN254.G1Point memory sigma + ) external view returns (bool pairingSuccessful, bool siganatureIsValid); } diff --git a/src/interfaces/IECDSAStakeRegistry.sol b/src/interfaces/IECDSAStakeRegistry.sol new file mode 100644 index 00000000..e17086f8 --- /dev/null +++ b/src/interfaces/IECDSAStakeRegistry.sol @@ -0,0 +1,305 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.27; + +import {IERC1271Upgradeable} from + "@openzeppelin-upgrades/contracts/interfaces/IERC1271Upgradeable.sol"; +import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; +import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol"; +import {IDelegationManager} from + "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; + +// TODO: many of these errors do not have test coverage. + +interface IECDSAStakeRegistryErrors { + /// @notice Thrown when the lengths of the signers array and signatures array do not match. + error LengthMismatch(); + /// @notice Thrown when encountering an invalid length for the signers or signatures array. + error InvalidLength(); + /// @notice Thrown when encountering an invalid signature. + error InvalidSignature(); + /// @notice Thrown when the threshold update is greater than BPS. + error InvalidThreshold(); + /// @notice Thrown when missing operators in an update. + error MustUpdateAllOperators(); + /// @notice Thrown when reference blocks must be for blocks that have already been confirmed. + error InvalidReferenceBlock(); + /// @notice Thrown when operator weights were out of sync and the signed weight exceed the total. + error InvalidSignedWeight(); + /// @notice Thrown when the total signed stake fails to meet the required threshold. + error InsufficientSignedStake(); + /// @notice Thrown when an individual signer's weight fails to meet the required threshold. + error InsufficientWeight(); + /// @notice Thrown when the quorum is invalid. + error InvalidQuorum(); + /// @notice Thrown when the system finds a list of items unsorted. + error NotSorted(); + /// @notice Thrown when registering an already registered operator. + error OperatorAlreadyRegistered(); + /// @notice Thrown when de-registering or updating the stake for an unregisted operator. + error OperatorNotRegistered(); +} + +interface IECDSAStakeRegistryTypes { + /// @notice Parameters for a strategy and its weight multiplier. + /// @param strategy The strategy contract reference. + /// @param multiplier The multiplier applied to the strategy. + struct StrategyParams { + IStrategy strategy; + uint96 multiplier; + } + + /// @notice Configuration for a quorum's strategies. + /// @param strategies An array of strategy parameters defining the quorum. + struct Quorum { + StrategyParams[] strategies; + } +} + +interface IECDSAStakeRegistryEvents is IECDSAStakeRegistryTypes { + /* + * @notice Emitted when the system registers an operator. + * @param operator The address of the registered operator. + * @param avs The address of the associated AVS. + */ + event OperatorRegistered(address indexed operator, address indexed avs); + + /* + * @notice Emitted when the system deregisters an operator. + * @param operator The address of the deregistered operator. + * @param avs The address of the associated AVS. + */ + event OperatorDeregistered(address indexed operator, address indexed avs); + + /* + * @notice Emitted when the system updates the quorum. + * @param previous The previous quorum configuration. + * @param current The new quorum configuration. + */ + event QuorumUpdated(Quorum previous, Quorum current); + + /* + * @notice Emitted when the weight to join the operator set updates. + * @param previous The previous minimum weight. + * @param current The new minimumWeight. + */ + event MinimumWeightUpdated(uint256 previous, uint256 current); + + /* + * @notice Emitted when the weight required to be an operator changes. + * @param oldMinimumWeight The previous weight. + * @param newMinimumWeight The updated weight. + */ + event UpdateMinimumWeight(uint256 oldMinimumWeight, uint256 newMinimumWeight); + + /* + * @notice Emitted when the system updates an operator's weight. + * @param operator The address of the operator updated. + * @param oldWeight The operator's weight before the update. + * @param newWeight The operator's weight after the update. + */ + event OperatorWeightUpdated(address indexed operator, uint256 oldWeight, uint256 newWeight); + + /* + * @notice Emitted when the system updates the total weight. + * @param oldTotalWeight The total weight before the update. + * @param newTotalWeight The total weight after the update. + */ + event TotalWeightUpdated(uint256 oldTotalWeight, uint256 newTotalWeight); + + /* + * @notice Emits when setting a new threshold weight. + */ + event ThresholdWeightUpdated(uint256 thresholdWeight); + + /* + * @notice Emitted when an operator's signing key is updated. + * @param operator The address of the operator whose signing key was updated. + * @param updateBlock The block number at which the signing key was updated. + * @param newSigningKey The operator's signing key after the update. + * @param oldSigningKey The operator's signing key before the update. + */ + event SigningKeyUpdate( + address indexed operator, + uint256 indexed updateBlock, + address indexed newSigningKey, + address oldSigningKey + ); +} + +interface IECDSAStakeRegistry is + IECDSAStakeRegistryErrors, + IECDSAStakeRegistryEvents, + IERC1271Upgradeable +{ + /* ACTIONS */ + + /* + * @notice Registers a new operator using a provided operators signature and signing key. + * @param operatorSignature Contains the operator's signature, salt, and expiry. + * @param signingKey The signing key to add to the operator's history. + */ + function registerOperatorWithSignature( + ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature, + address signingKey + ) external; + + /* + * @notice Deregisters an existing operator. + */ + function deregisterOperator() external; + + /* + * @notice Updates the signing key for an operator. + * @param newSigningKey The new signing key to set for the operator. + * @dev Only callable by the operator themselves. + */ + function updateOperatorSigningKey( + address newSigningKey + ) external; + + /* + * @notice Updates the StakeRegistry's view of operators' stakes. + * @param operators A list of operator addresses to update. + * @dev Queries stakes from the Eigenlayer core DelegationManager contract. + */ + function updateOperators( + address[] memory operators + ) external; + + /* + * @notice Updates the quorum configuration and the set of operators. + * @param quorum The new quorum configuration, including strategies and their new weights. + * @param operators The list of operator addresses to update stakes for. + */ + function updateQuorumConfig( + IECDSAStakeRegistryTypes.Quorum memory quorum, + address[] memory operators + ) external; + + /* + * @notice Updates the weight an operator must have to join the operator set. + * @param newMinimumWeight The new weight an operator must have to join the operator set. + * @param operators The list of operators to update after changing the minimum weight. + */ + function updateMinimumWeight(uint256 newMinimumWeight, address[] memory operators) external; + + /* + * @notice Sets a new cumulative threshold weight for message validation. + * @param thresholdWeight The updated threshold weight required to validate a message. + */ + function updateStakeThreshold( + uint256 thresholdWeight + ) external; + + /* VIEW */ + + /* + * @notice Retrieves the current stake quorum details. + * @return The current quorum of strategies and weights. + */ + function quorum() external view returns (IECDSAStakeRegistryTypes.Quorum memory); + + /* + * @notice Retrieves the latest signing key for a given operator. + * @param operator The address of the operator. + * @return The latest signing key of the operator. + */ + function getLatestOperatorSigningKey( + address operator + ) external view returns (address); + + /* + * @notice Retrieves the signing key for an operator at a specific block. + * @param operator The address of the operator. + * @param blockNumber The block number to query at. + * @return The signing key of the operator at the given block. + */ + function getOperatorSigningKeyAtBlock( + address operator, + uint256 blockNumber + ) external view returns (address); + + /* + * @notice Retrieves the last recorded weight for a given operator. + * @param operator The address of the operator. + * @return The latest weight of the operator. + */ + function getLastCheckpointOperatorWeight( + address operator + ) external view returns (uint256); + + /* + * @notice Retrieves the last recorded total weight across all operators. + * @return The latest total weight. + */ + function getLastCheckpointTotalWeight() external view returns (uint256); + + /* + * @notice Retrieves the last recorded threshold weight. + * @return The latest threshold weight. + */ + function getLastCheckpointThresholdWeight() external view returns (uint256); + + /* + * @notice Returns whether an operator is currently registered. + * @param operator The operator address to check. + * @return Whether the operator is registered. + */ + function operatorRegistered( + address operator + ) external view returns (bool); + + /* + * @notice Returns the minimum weight required for operator participation. + * @return The minimum weight threshold. + */ + function minimumWeight() external view returns (uint256); + + /* + * @notice Retrieves the operator's weight at a specific block number. + * @param operator The address of the operator. + * @param blockNumber The block number to query at. + * @return The weight of the operator at the given block. + */ + function getOperatorWeightAtBlock( + address operator, + uint32 blockNumber + ) external view returns (uint256); + + /* + * @notice Retrieves the operator's weight. + * @param operator The address of the operator. + * @return The current weight of the operator. + */ + function getOperatorWeight( + address operator + ) external view returns (uint256); + + /* + * @notice Updates operators for a specific quorum. + * @param operatorsPerQuorum Array of operator addresses per quorum. + * @param data Additional data (unused but kept for interface compatibility). + */ + function updateOperatorsForQuorum( + address[][] memory operatorsPerQuorum, + bytes memory data + ) external; + + /* + * @notice Retrieves the total weight at a specific block number. + * @param blockNumber The block number to query at. + * @return The total weight at the given block. + */ + function getLastCheckpointTotalWeightAtBlock( + uint32 blockNumber + ) external view returns (uint256); + + /* + * @notice Retrieves the threshold weight at a specific block number. + * @param blockNumber The block number to query at. + * @return The threshold weight at the given block. + */ + function getLastCheckpointThresholdWeightAtBlock( + uint32 blockNumber + ) external view returns (uint256); +} diff --git a/src/interfaces/IECDSAStakeRegistryEventsAndErrors.sol b/src/interfaces/IECDSAStakeRegistryEventsAndErrors.sol deleted file mode 100644 index 445db814..00000000 --- a/src/interfaces/IECDSAStakeRegistryEventsAndErrors.sol +++ /dev/null @@ -1,111 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.12; - -import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; - -struct StrategyParams { - IStrategy strategy; // The strategy contract reference - uint96 multiplier; // The multiplier applied to the strategy -} - -struct Quorum { - StrategyParams[] strategies; // An array of strategy parameters to define the quorum -} - -interface ECDSAStakeRegistryEventsAndErrors { - /// @notice Emitted when the system registers an operator - /// @param _operator The address of the registered operator - /// @param _avs The address of the associated AVS - event OperatorRegistered(address indexed _operator, address indexed _avs); - - /// @notice Emitted when the system deregisters an operator - /// @param _operator The address of the deregistered operator - /// @param _avs The address of the associated AVS - event OperatorDeregistered(address indexed _operator, address indexed _avs); - - /// @notice Emitted when the system updates the quorum - /// @param _old The previous quorum configuration - /// @param _new The new quorum configuration - event QuorumUpdated(Quorum _old, Quorum _new); - - /// @notice Emitted when the weight to join the operator set updates - /// @param _old The previous minimum weight - /// @param _new The new minimumWeight - event MinimumWeightUpdated(uint256 _old, uint256 _new); - - /// @notice Emitted when the weight required to be an operator changes - /// @param oldMinimumWeight The previous weight - /// @param newMinimumWeight The updated weight - event UpdateMinimumWeight( - uint256 oldMinimumWeight, - uint256 newMinimumWeight - ); - - /// @notice Emitted when the system updates an operator's weight - /// @param _operator The address of the operator updated - /// @param oldWeight The operator's weight before the update - /// @param newWeight The operator's weight after the update - event OperatorWeightUpdated( - address indexed _operator, - uint256 oldWeight, - uint256 newWeight - ); - - /// @notice Emitted when the system updates the total weight - /// @param oldTotalWeight The total weight before the update - /// @param newTotalWeight The total weight after the update - event TotalWeightUpdated(uint256 oldTotalWeight, uint256 newTotalWeight); - - /// @notice Emits when setting a new threshold weight. - event ThresholdWeightUpdated(uint256 _thresholdWeight); - - /// @notice Emitted when an operator's signing key is updated - /// @param operator The address of the operator whose signing key was updated - /// @param updateBlock The block number at which the signing key was updated - /// @param newSigningKey The operator's signing key after the update - /// @param oldSigningKey The operator's signing key before the update - event SigningKeyUpdate( - address indexed operator, - uint256 indexed updateBlock, - address indexed newSigningKey, - address oldSigningKey - ); - /// @notice Indicates when the lengths of the signers array and signatures array do not match. - error LengthMismatch(); - - /// @notice Indicates encountering an invalid length for the signers or signatures array. - error InvalidLength(); - - /// @notice Indicates encountering an invalid signature. - error InvalidSignature(); - - /// @notice Thrown when the threshold update is greater than BPS - error InvalidThreshold(); - - /// @notice Thrown when missing operators in an update - error MustUpdateAllOperators(); - - /// @notice Reference blocks must be for blocks that have already been confirmed - error InvalidReferenceBlock(); - - /// @notice Indicates operator weights were out of sync and the signed weight exceed the total - error InvalidSignedWeight(); - - /// @notice Indicates the total signed stake fails to meet the required threshold. - error InsufficientSignedStake(); - - /// @notice Indicates an individual signer's weight fails to meet the required threshold. - error InsufficientWeight(); - - /// @notice Indicates the quorum is invalid - error InvalidQuorum(); - - /// @notice Indicates the system finds a list of items unsorted - error NotSorted(); - - /// @notice Thrown when registering an already registered operator - error OperatorAlreadyRegistered(); - - /// @notice Thrown when de-registering or updating the stake for an unregisted operator - error OperatorNotRegistered(); -} diff --git a/src/interfaces/IEjectionManager.sol b/src/interfaces/IEjectionManager.sol index 6649e75b..5ead07cb 100644 --- a/src/interfaces/IEjectionManager.sol +++ b/src/interfaces/IEjectionManager.sol @@ -1,55 +1,152 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; -/** - * @title Interface for a contract that ejects operators from an AVSs RegistryCoordinator - * @author Layr Labs, Inc. - */ -interface IEjectionManager { +import {IRegistryCoordinator} from "./IRegistryCoordinator.sol"; +import {IStakeRegistry} from "./IStakeRegistry.sol"; - /// @notice A quorum's ratelimit parameters +interface IEjectionManagerErrors { + /// @notice Thrown when the caller is not the owner or ejector. + error OnlyOwnerOrEjector(); + /// @notice Thrown when quorum number exceeds MAX_QUORUM_COUNT. + error MaxQuorumCount(); +} + +interface IEjectionManagerTypes { + /// @notice Parameters for controlling ejection rate limits per quorum. + /// @param rateLimitWindow Time window to track ejection rate (in seconds). + /// @param ejectableStakePercent Maximum percentage of stake that can be ejected per window (in BIPS). struct QuorumEjectionParams { - uint32 rateLimitWindow; // Time delta to track ejection over - uint16 ejectableStakePercent; // Max stake to be ejectable per time delta + uint32 rateLimitWindow; + uint16 ejectableStakePercent; } - /// @notice A stake ejection event + /// @notice Records a stake ejection event with timing and amount. + /// @param timestamp Time when the ejection occurred. + /// @param stakeEjected Amount of stake that was ejected. struct StakeEjection { - uint256 timestamp; // Timestamp of the ejection - uint256 stakeEjected; // Amount of stake ejected at the timestamp + uint256 timestamp; + uint256 stakeEjected; } +} - ///@notice Emitted when the ejector address is set +interface IEjectionManagerEvents is IEjectionManagerTypes { + /* + * @notice Emitted when the ejector address is set. + * @param ejector The address being configured as ejector. + * @param status The new status for the ejector address. + */ event EjectorUpdated(address ejector, bool status); - ///@notice Emitted when the ratelimit parameters for a quorum are set - event QuorumEjectionParamsSet(uint8 quorumNumber, uint32 rateLimitWindow, uint16 ejectableStakePercent); - ///@notice Emitted when an operator is ejected + + /* + * @notice Emitted when the rate limit parameters for a quorum are set. + * @param quorumNumber The quorum number being configured. + * @param rateLimitWindow The new time window for rate limiting. + * @param ejectableStakePercent The new percentage of stake that can be ejected. + */ + event QuorumEjectionParamsSet( + uint8 quorumNumber, uint32 rateLimitWindow, uint16 ejectableStakePercent + ); + + /* + * @notice Emitted when an operator is ejected. + * @param operatorId The unique identifier of the ejected operator. + * @param quorumNumber The quorum number the operator was ejected from. + */ event OperatorEjected(bytes32 operatorId, uint8 quorumNumber); - ///@notice Emitted when operators are ejected for a quroum + + /* + * @notice Emitted when operators are ejected for a quorum. + * @param ejectedOperators Number of operators that were ejected. + * @param ratelimitHit Whether the ejection rate limit was reached. + */ event QuorumEjection(uint32 ejectedOperators, bool ratelimitHit); +} + +interface IEjectionManager is IEjectionManagerErrors, IEjectionManagerEvents { + /* STATE */ + + /* + * @notice Returns the address of the registry coordinator contract. + * @return The address of the registry coordinator. + * @dev This value is immutable and set during contract construction. + */ + function registryCoordinator() external view returns (IRegistryCoordinator); - /** - * @notice Ejects operators from the AVSs registryCoordinator under a ratelimit - * @param _operatorIds The ids of the operators to eject for each quorum + /* + * @notice Returns the address of the stake registry contract. + * @return The address of the stake registry. + * @dev This value is immutable and set during contract construction. */ - function ejectOperators(bytes32[][] memory _operatorIds) external; + function stakeRegistry() external view returns (IStakeRegistry); - /** - * @notice Sets the ratelimit parameters for a quorum - * @param _quorumNumber The quorum number to set the ratelimit parameters for - * @param _quorumEjectionParams The quorum ratelimit parameters to set for the given quorum + /* + * @notice Returns whether `ejector` is authorized to eject operators under a rate limit. + * @param ejector The address to check. + * @return Whether the address is authorized to eject operators. */ - function setQuorumEjectionParams(uint8 _quorumNumber, QuorumEjectionParams memory _quorumEjectionParams) external; + function isEjector( + address ejector + ) external view returns (bool); - /** - * @notice Sets the address permissioned to eject operators under a ratelimit - * @param _ejector The address to permission + /* + * @notice Returns the stake ejected for a quorum `quorumNumber` at array offset `index`. + * @param quorumNumber The quorum number to query. + * @param index The index in the ejection history. + * @return timestamp The timestamp of the ejection. + * @return stakeEjected The amount of stake ejected. */ - function setEjector(address _ejector, bool _status) external; + function stakeEjectedForQuorum( + uint8 quorumNumber, + uint256 index + ) external view returns (uint256 timestamp, uint256 stakeEjected); + + /* + * @notice Returns the rate limit parameters for quorum `quorumNumber`. + * @param quorumNumber The quorum number to query. + * @return rateLimitWindow The time window to track ejection rate (in seconds). + * @return ejectableStakePercent The maximum percentage of stake that can be ejected per window (in BIPS). + */ + function quorumEjectionParams( + uint8 quorumNumber + ) external view returns (uint32 rateLimitWindow, uint16 ejectableStakePercent); + + /* ACTIONS */ + + /* + * @notice Ejects operators specified in `operatorIds` from the AVS's RegistryCoordinator under a rate limit. + * @param operatorIds The ids of the operators to eject for each quorum. + * @dev This function will eject as many operators as possible prioritizing operators at the lower index. + * @dev The owner can eject operators without recording of stake ejection. + */ + function ejectOperators( + bytes32[][] memory operatorIds + ) external; + + /* + * @notice Sets the rate limit parameters for quorum `quorumNumber` to `quorumEjectionParams`. + * @param quorumNumber The quorum number to set the rate limit parameters for. + * @param quorumEjectionParams The quorum rate limit parameters to set. + */ + function setQuorumEjectionParams( + uint8 quorumNumber, + QuorumEjectionParams memory quorumEjectionParams + ) external; + + /* + * @notice Sets whether address `ejector` is permissioned to eject operators under a rate limit to `status`. + * @param ejector The address to permission. + * @param status The status to set for the given address. + */ + function setEjector(address ejector, bool status) external; + + /* VIEW */ - /** - * @notice Returns the amount of stake that can be ejected for a quorum at the current block.timestamp - * @param _quorumNumber The quorum number to view ejectable stake for + /* + * @notice Returns the amount of stake that can be ejected for quorum `quorumNumber` at the current block.timestamp. + * @param quorumNumber The quorum number to view ejectable stake for. + * @return The amount of stake that can be ejected. */ - function amountEjectableForQuorum(uint8 _quorumNumber) external view returns (uint256); -} \ No newline at end of file + function amountEjectableForQuorum( + uint8 quorumNumber + ) external view returns (uint256); +} diff --git a/src/interfaces/IIndexRegistry.sol b/src/interfaces/IIndexRegistry.sol index 72a702d8..9cd26302 100644 --- a/src/interfaces/IIndexRegistry.sol +++ b/src/interfaces/IIndexRegistry.sol @@ -1,57 +1,99 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; -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. - * @author Layr Labs, Inc. - */ -interface IIndexRegistry is IRegistry { - // EVENTS - - // emitted when an operator's index in the ordered operator list for the quorum with number `quorumNumber` is updated - event QuorumIndexUpdate(bytes32 indexed operatorId, uint8 quorumNumber, uint32 newOperatorIndex); - - // DATA STRUCTURES +interface IIndexRegistryErrors { + /// @notice Thrown when a function is called by an address that is not the RegistryCoordinator. + error OnlyRegistryCoordinator(); + /// @notice Thrown when attempting to query a quorum that has no history. + error QuorumDoesNotExist(); + /// @notice Thrown when attempting to look up an operator that does not exist at the specified block number. + error OperatorIdDoesNotExist(); +} - // struct used to give definitive ordering to operators at each blockNumber. +interface IIndexRegistryTypes { + /// @notice Represents an update to an operator's status at a specific index. + /// @param fromBlockNumber The block number from which this update takes effect. + /// @param operatorId The unique identifier of the operator. struct OperatorUpdate { - // blockNumber number from which `operatorIndex` was the operators index - // the operator's index is the first entry such that `blockNumber >= entry.fromBlockNumber` uint32 fromBlockNumber; - // the operator at this index bytes32 operatorId; } - // struct used to denote the number of operators in a quorum at a given blockNumber + /// @notice Represents an update to the total number of operators in a quorum. + /// @param fromBlockNumber The block number from which this update takes effect. + /// @param numOperators The total number of operators after the update. struct QuorumUpdate { - // The total number of operators at a `blockNumber` is the first entry such that `blockNumber >= entry.fromBlockNumber` uint32 fromBlockNumber; - // The number of operators at `fromBlockNumber` uint32 numOperators; } +} + +interface IIndexRegistryEvents is IIndexRegistryTypes { + /* + * @notice Emitted when an operator's index in a quorum is updated. + * @param operatorId The unique identifier of the operator. + * @param quorumNumber The identifier of the quorum. + * @param newOperatorIndex The new index assigned to the operator. + */ + event QuorumIndexUpdate( + bytes32 indexed operatorId, uint8 quorumNumber, uint32 newOperatorIndex + ); +} + +interface IIndexRegistry is IIndexRegistryErrors, IIndexRegistryEvents { + /* + * @notice Returns the special identifier used to indicate a non-existent operator. + * @return The bytes32 constant OPERATOR_DOES_NOT_EXIST_ID. + */ + function OPERATOR_DOES_NOT_EXIST_ID() external pure returns (bytes32); + + /* + * @notice Returns the address of the RegistryCoordinator contract. + * @return The address of the RegistryCoordinator. + */ + function registryCoordinator() external view returns (address); + + /* + * @notice Returns the current index of an operator with ID `operatorId` in quorum `quorumNumber`. + * @dev This mapping is NOT updated when an operator is deregistered, + * so it's possible that an index retrieved from this mapping is inaccurate. + * If you're querying for an operator that might be deregistered, ALWAYS + * check this index against the latest `_operatorIndexHistory` entry. + * @param quorumNumber The identifier of the quorum. + * @param operatorId The unique identifier of the operator. + * @return The current index of the operator. + */ + function currentOperatorIndex( + uint8 quorumNumber, + bytes32 operatorId + ) external view returns (uint32); + + // ACTIONS - /** + /* * @notice Registers the operator with the specified `operatorId` for the quorums specified by `quorumNumbers`. - * @param operatorId is the id of the operator that is being registered - * @param quorumNumbers is the quorum numbers the operator is registered for - * @return numOperatorsPerQuorum is a list of the number of operators (including the registering operator) in each of the quorums the operator is registered for - * @dev access restricted to the RegistryCoordinator - * @dev Preconditions (these are assumed, not validated in this contract): + * @param operatorId The unique identifier of the operator. + * @param quorumNumbers The quorum numbers to register for. + * @return An array containing a list of the number of operators (including the registering operator) + * in each of the quorums the operator is registered for. + * @dev Access restricted to the RegistryCoordinator. + * @dev Preconditions: * 1) `quorumNumbers` has no duplicates * 2) `quorumNumbers.length` != 0 * 3) `quorumNumbers` is ordered in ascending order * 4) the operator is not already registered */ - function registerOperator(bytes32 operatorId, bytes calldata quorumNumbers) external returns(uint32[] memory); + function registerOperator( + bytes32 operatorId, + bytes calldata quorumNumbers + ) external returns (uint32[] memory); - /** + /* * @notice Deregisters the operator with the specified `operatorId` for the quorums specified by `quorumNumbers`. - * @param operatorId is the id of the operator that is being deregistered - * @param quorumNumbers is the quorum numbers the operator is deregistered for - * @dev access restricted to the RegistryCoordinator - * @dev Preconditions (these are assumed, not validated in this contract): + * @param operatorId The unique identifier of the operator. + * @param quorumNumbers The quorum numbers to deregister from. + * @dev Access restricted to the RegistryCoordinator. + * @dev Preconditions: * 1) `quorumNumbers` has no duplicates * 2) `quorumNumbers.length` != 0 * 3) `quorumNumbers` is ordered in ascending order @@ -60,31 +102,77 @@ 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 + /* + * @notice Initializes a new quorum `quorumNumber`. + * @param quorumNumber The identifier of the quorum to initialize. */ - function initializeQuorum(uint8 quorumNumber) external; + function initializeQuorum( + uint8 quorumNumber + ) external; - /// @notice Returns the OperatorUpdate entry for the specified `operatorIndex` and `quorumNumber` at the specified `arrayIndex` + // VIEW + + /* + * @notice Returns the operator update at index `arrayIndex` for operator at index `operatorIndex` in quorum `quorumNumber`. + * @param quorumNumber The identifier of the quorum. + * @param operatorIndex The index of the operator. + * @param arrayIndex The index in the update history. + * @return The operator update entry. + */ function getOperatorUpdateAtIndex( uint8 quorumNumber, uint32 operatorIndex, uint32 arrayIndex ) external view returns (OperatorUpdate memory); - /// @notice Returns the QuorumUpdate entry for the specified `quorumNumber` at the specified `quorumIndex` - function getQuorumUpdateAtIndex(uint8 quorumNumber, uint32 quorumIndex) external view returns (QuorumUpdate memory); + /* + * @notice Returns the quorum update at index `quorumIndex` for quorum `quorumNumber`. + * @param quorumNumber The identifier of the quorum. + * @param quorumIndex The index in the quorum's update history. + * @return The quorum update entry. + */ + function getQuorumUpdateAtIndex( + uint8 quorumNumber, + uint32 quorumIndex + ) external view returns (QuorumUpdate memory); - /// @notice Returns the most recent OperatorUpdate entry for the specified quorumNumber and operatorIndex - function getLatestOperatorUpdate(uint8 quorumNumber, uint32 operatorIndex) external view returns (OperatorUpdate memory); + /* + * @notice Returns the latest quorum update for quorum `quorumNumber`. + * @param quorumNumber The identifier of the quorum. + * @return The most recent quorum update. + */ + function getLatestQuorumUpdate( + uint8 quorumNumber + ) external view returns (QuorumUpdate memory); - /// @notice Returns the most recent QuorumUpdate entry for the specified quorumNumber - function getLatestQuorumUpdate(uint8 quorumNumber) external view returns (QuorumUpdate memory); + /* + * @notice Returns the latest operator update for operator at index `operatorIndex` in quorum `quorumNumber`. + * @param quorumNumber The identifier of the quorum. + * @param operatorIndex The index of the operator. + * @return The most recent operator update. + */ + function getLatestOperatorUpdate( + uint8 quorumNumber, + uint32 operatorIndex + ) external view returns (OperatorUpdate memory); - /// @notice Returns the current number of operators of this service for `quorumNumber`. - function totalOperatorsForQuorum(uint8 quorumNumber) external view returns (uint32); + /* + * @notice Returns the list of operators in quorum `quorumNumber` at block `blockNumber`. + * @param quorumNumber The identifier of the quorum. + * @param blockNumber The block number to query. + * @return An array of operator IDs. + */ + function getOperatorListAtBlockNumber( + uint8 quorumNumber, + uint32 blockNumber + ) external view returns (bytes32[] memory); - /// @notice Returns an ordered list of operators of the services for the given `quorumNumber` at the given `blockNumber` - function getOperatorListAtBlockNumber(uint8 quorumNumber, uint32 blockNumber) external view returns (bytes32[] memory); + /* + * @notice Returns the total number of operators in quorum `quorumNumber`. + * @param quorumNumber The identifier of the quorum. + * @return The total number of operators. + */ + function totalOperatorsForQuorum( + uint8 quorumNumber + ) external view returns (uint32); } diff --git a/src/interfaces/IRegistry.sol b/src/interfaces/IRegistry.sol deleted file mode 100644 index 8a483cba..00000000 --- a/src/interfaces/IRegistry.sol +++ /dev/null @@ -1,13 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity >=0.5.0; - -/** - * @title Minimal interface for a `Registry`-type contract. - * @author Layr Labs, Inc. - * @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service - * @notice Functions related to the registration process itself have been intentionally excluded - * because their function signatures may vary significantly. - */ -interface IRegistry { - function registryCoordinator() external view returns (address); -} diff --git a/src/interfaces/IRegistryCoordinator.sol b/src/interfaces/IRegistryCoordinator.sol index 631ca3a3..4c9397dc 100644 --- a/src/interfaces/IRegistryCoordinator.sol +++ b/src/interfaces/IRegistryCoordinator.sol @@ -1,161 +1,121 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; - -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. - * @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); - - event OperatorSocketUpdate(bytes32 indexed operatorId, string socket); - - /// @notice emitted when all the operators for a quorum are updated at once - event QuorumBlockNumberUpdated(uint8 indexed quorumNumber, uint256 blocknumber); - - // DATA STRUCTURES - enum OperatorStatus - { - // default is NEVER_REGISTERED - NEVER_REGISTERED, - REGISTERED, - DEREGISTERED - } +pragma solidity ^0.8.27; + +import { + ISlashingRegistryCoordinator, + ISlashingRegistryCoordinatorErrors, + ISlashingRegistryCoordinatorEvents, + ISlashingRegistryCoordinatorTypes +} from "./ISlashingRegistryCoordinator.sol"; +import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol"; +import {IBLSApkRegistryTypes} from "./IBLSApkRegistry.sol"; +import {IServiceManager} from "./IServiceManager.sol"; + +interface IRegistryCoordinatorErrors is ISlashingRegistryCoordinatorErrors { + /// @notice Thrown when operator sets mode is already enabled. + error OperatorSetsAlreadyEnabled(); + /// @notice Thrown when a quorum is an operator set quorum. + error OperatorSetQuorum(); + /// @notice Thrown when M2 quorums are already disabled. + error M2QuorumsAlreadyDisabled(); +} - // STRUCTS +interface IRegistryCoordinatorTypes is ISlashingRegistryCoordinatorTypes {} +interface IRegistryCoordinatorEvents is + ISlashingRegistryCoordinatorEvents, + IRegistryCoordinatorTypes +{ /** - * @notice Data structure for storing info on operators + * @notice Emitted when operator sets mode is enabled. + * @dev Emitted in enableOperatorSets(). */ - struct OperatorInfo { - // 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; - } + event OperatorSetsEnabled(); /** - * @notice Data structure for storing info on quorum bitmap updates where the `quorumBitmap` is the bitmap of the - * quorums the operator is registered for starting at (inclusive)`updateBlockNumber` and ending at (exclusive) `nextUpdateBlockNumber` - * @dev nextUpdateBlockNumber is initialized to 0 for the latest update + * @notice Emitted when M2 quorums are disabled. + * @dev Emitted in disableM2QuorumRegistration(). */ - struct QuorumBitmapUpdate { - uint32 updateBlockNumber; - uint32 nextUpdateBlockNumber; - 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; - } + event M2QuorumsDisabled(); +} +interface IRegistryCoordinator is + IRegistryCoordinatorErrors, + IRegistryCoordinatorEvents, + ISlashingRegistryCoordinator +{ /** - * @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 + * @notice Reference to the ServiceManager contract. + * @return The ServiceManager contract interface. + * @dev This is only relevant for Pre-Slashing AVSs */ - struct OperatorKickParam { - uint8 quorumNumber; - address operator; - } + function serviceManager() external view returns (IServiceManager); - /// @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); + /// ACTIONS /** - * @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 + * @notice Registers an operator for service in specified quorums. If any quorum exceeds its maximum + * operator capacity after the operator is registered, this method will fail. + * @param quorumNumbers is an ordered byte array containing the quorum numbers being registered for AVSDirectory. + * @param socket is the socket of the operator (typically an IP address). + * @param params contains the G1 & G2 public keys of the operator, and a signature proving their ownership. + * @param operatorSignature is the signature of the operator used by the AVS to register the operator in the delegation manager. + * @dev `params` is ignored if the caller has previously registered a public key. + * @dev `operatorSignature` is ignored if the operator's status is already REGISTERED. + * @dev This function registers operators to the AVSDirectory using the M2-registration pathway. */ - function ejectOperator( - address operator, - bytes calldata quorumNumbers + function registerOperator( + bytes memory quorumNumbers, + string memory socket, + IBLSApkRegistryTypes.PubkeyRegistrationParams memory params, + ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature ) external; - /// @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 (OperatorInfo memory); - - /// @notice Returns the operatorId for the given `operator` - function getOperatorId(address operator) external view returns (bytes32); - - /// @notice Returns the operator address for the given `operatorId` - function getOperatorFromId(bytes32 operatorId) external view returns (address operator); - - /// @notice Returns the status for the given `operator` - 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 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 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 getQuorumBitmapUpdateByIndex(bytes32 operatorId, uint256 index) external view returns (QuorumBitmapUpdate memory); - - /// @notice Returns the current quorum bitmap for the given `operatorId` - function getCurrentQuorumBitmap(bytes32 operatorId) external view returns (uint192); - - /// @notice Returns the length of the quorum bitmap history for the given `operatorId` - function getQuorumBitmapHistoryLength(bytes32 operatorId) external view returns (uint256); - - /// @notice Returns the registry at the desired index - function registries(uint256) external view returns (address); - - /// @notice Returns the number of registries - function numRegistries() external view returns (uint256); + * @notice Registers an operator while replacing existing operators in full 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 AVSDirectory. + * @param socket is the socket of the operator (typically an IP address). + * @param params contains the G1 & G2 public keys of the operator, and a signature proving their ownership. + * @param operatorKickParams 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 over the `operatorKickParams`. + * @param operatorSignature is the signature of the operator used by the AVS to register the operator in the delegation manager. + * @dev `params` is ignored if the caller has previously registered a public key. + * @dev `operatorSignature` is ignored if the operator's status is already REGISTERED. + * @dev This function registers operators to the AVSDirectory using the M2-registration pathway. + */ + function registerOperatorWithChurn( + bytes calldata quorumNumbers, + string memory socket, + IBLSApkRegistryTypes.PubkeyRegistrationParams memory params, + OperatorKickParam[] memory operatorKickParams, + ISignatureUtils.SignatureWithSaltAndExpiry memory churnApproverSignature, + ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature + ) 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 + * @notice Deregisters the caller from one or more quorums. The operator will be removed from all registry contracts + * and their quorum bitmap will be updated accordingly. If the operator is deregistered from all quorums, their status + * will be updated to DEREGISTERED. + * @param quorumNumbers is an ordered byte array containing the quorum numbers being deregistered from. + * @dev Will revert if operator is not currently registered for any of the specified quorums. + * @dev This function deregisters operators from the AVSDirectory using the M2-registration pathway. */ - function pubkeyRegistrationMessageHash(address operator) external view returns (BN254.G1Point memory); - - /// @notice returns the blocknumber the quorum was last updated all at once for all operators - function quorumUpdateBlockNumber(uint8 quorumNumber) external view returns (uint256); + function deregisterOperator( + bytes memory quorumNumbers + ) external; - /// @notice The owner of the registry coordinator - function owner() external view returns (address); + /** + * @notice Enables operator sets mode for the AVS. Once enabled, this cannot be disabled. + * @dev When enabled, all existing quorums are marked as M2 quorums and future quorums must be explicitly + * created as either M2 or operator set quorums. + */ + function enableOperatorSets() external; /** - * @notice Updates the socket of the msg.sender given they are a registered operator - * @param socket is the new socket of the operator + * @notice Disables M2 quorum registration for the AVS. Once disabled, this cannot be enabled. + * @dev When disabled, all registrations to M2 quorums will revert. Deregistrations are still possible. */ - function updateSocket(string memory socket) external; + function disableM2QuorumRegistration() external; } diff --git a/src/interfaces/IServiceManager.sol b/src/interfaces/IServiceManager.sol index c8f49166..a93f03f0 100644 --- a/src/interfaces/IServiceManager.sol +++ b/src/interfaces/IServiceManager.sol @@ -1,56 +1,112 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity >=0.5.0; -import {IRewardsCoordinator} from "eigenlayer-contracts/src/contracts/interfaces/IRewardsCoordinator.sol"; +import {IRewardsCoordinator} from + "eigenlayer-contracts/src/contracts/interfaces/IRewardsCoordinator.sol"; import {IServiceManagerUI} from "./IServiceManagerUI.sol"; +import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol"; +import {IAllocationManagerTypes} from + "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; +import {IAllocationManager} from + "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; +import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; +import {IAVSRegistrar} from "eigenlayer-contracts/src/contracts/interfaces/IAVSRegistrar.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 is IServiceManagerUI { - /** - * @notice Creates a new rewards submission to the EigenLayer RewardsCoordinator contract, to be split amongst the - * set of stakers delegated to operators who are registered to this `avs` - * @param rewardsSubmissions The rewards submissions being created - * @dev Only callable by the permissioned rewardsInitiator address - * @dev The duration of the `rewardsSubmission` cannot exceed `MAX_REWARDS_DURATION` - * @dev The tokens are sent to the `RewardsCoordinator` contract - * @dev Strategies must be in ascending order of addresses to check for duplicates +interface IServiceManagerErrors { + /// @notice Thrown when a function is called by an address that is not the RegistryCoordinator. + error OnlyRegistryCoordinator(); + /// @notice Thrown when a function is called by an address that is not the RewardsInitiator. + error OnlyRewardsInitiator(); + /// @notice Thrown when a function is called by an address that is not the StakeRegistry. + error OnlyStakeRegistry(); + /// @notice Thrown when a function is called by an address that is not the Slasher. + error OnlySlasher(); + /// @notice Thrown when a slashing proposal delay has not been met yet. + error DelayPeriodNotPassed(); +} + +interface IServiceManagerEvents { + /** + * @notice Emitted when the rewards initiator address is updated. + * @param prevRewardsInitiator The previous rewards initiator address. + * @param newRewardsInitiator The new rewards initiator address. + */ + event RewardsInitiatorUpdated(address prevRewardsInitiator, address newRewardsInitiator); +} + +interface IServiceManager is IServiceManagerUI, IServiceManagerErrors, IServiceManagerEvents { + /** + * @notice Creates a new rewards submission to the EigenLayer RewardsCoordinator contract. + * @dev Only callable by the permissioned rewardsInitiator address. + * @dev The duration of the `rewardsSubmission` cannot exceed `MAX_REWARDS_DURATION`. + * @dev The tokens are sent to the `RewardsCoordinator` contract. + * @dev Strategies must be in ascending order of addresses to check for duplicates. * @dev This function will revert if the `rewardsSubmission` is malformed, - * e.g. if the `strategies` and `weights` arrays are of non-equal lengths + * e.g. if the `strategies` and `weights` arrays are of non-equal lengths. + * @param rewardsSubmissions The rewards submissions to be split amongst the set of stakers + * delegated to operators who are registered to this `avs`. */ function createAVSRewardsSubmission( IRewardsCoordinator.RewardsSubmission[] calldata rewardsSubmissions ) external; /** - * @notice Creates a new operator-directed rewards submission on behalf of an AVS, to be split amongst the operators and - * set of stakers delegated to operators who are registered to the `avs`. - * @param operatorDirectedRewardsSubmissions The operator-directed rewards submissions being created - * @dev Only callable by the permissioned rewardsInitiator address - * @dev The duration of the `rewardsSubmission` cannot exceed `MAX_REWARDS_DURATION` - * @dev The tokens are sent to the `RewardsCoordinator` contract - * @dev This contract needs a token approval of sum of all `operatorRewards` in the `operatorDirectedRewardsSubmissions`, before calling this function. - * @dev Strategies must be in ascending order of addresses to check for duplicates - * @dev Operators must be in ascending order of addresses to check for duplicates. - * @dev This function will revert if the `operatorDirectedRewardsSubmissions` is malformed. - */ - function createOperatorDirectedAVSRewardsSubmission( - IRewardsCoordinator.OperatorDirectedRewardsSubmission[] - calldata operatorDirectedRewardsSubmissions + * @notice PERMISSIONCONTROLLER FUNCTIONS + */ + + /** + * @notice Calls `addPendingAdmin` on the `PermissionController` contract. + * @dev Only callable by the owner of the contract. + * @param admin The address of the admin to add. + */ + function addPendingAdmin( + address admin ) external; /** - * @notice Forwards a call to Eigenlayer's RewardsCoordinator contract to set the address of the entity that can call `processClaim` on behalf of this contract. - * @param claimer The address of the entity that can call `processClaim` on behalf of the earner - * @dev Only callable by the owner. + * @notice Calls `removePendingAdmin` on the `PermissionController` contract. + * @dev Only callable by the owner of the contract. + * @param pendingAdmin The address of the pending admin to remove. */ - function setClaimerFor(address claimer) external; + function removePendingAdmin( + address pendingAdmin + ) external; - // EVENTS - event RewardsInitiatorUpdated( - address prevRewardsInitiator, - address newRewardsInitiator - ); + /** + * @notice Calls `removeAdmin` on the `PermissionController` contract. + * @dev Only callable by the owner of the contract. + * @param admin The address of the admin to remove. + */ + function removeAdmin( + address admin + ) external; + + /** + * @notice Calls `setAppointee` on the `PermissionController` contract. + * @dev Only callable by the owner of the contract. + * @param appointee The address of the appointee to set. + * @param target The address of the target to set the appointee for. + * @param selector The function selector to set the appointee for. + */ + function setAppointee(address appointee, address target, bytes4 selector) external; + + /** + * @notice Calls `removeAppointee` on the `PermissionController` contract. + * @dev Only callable by the owner of the contract. + * @param appointee The address of the appointee to remove. + * @param target The address of the target to remove the appointee for. + * @param selector The function selector to remove the appointee for. + */ + function removeAppointee(address appointee, address target, bytes4 selector) external; + + /** + * @notice Deregisters an operator from specified operator sets + * @param operator The address of the operator to deregister + * @param operatorSetIds The IDs of the operator sets to deregister from + * @dev Only callable by the RegistryCoordinator + */ + function deregisterOperatorFromOperatorSets( + address operator, + uint32[] memory operatorSetIds + ) external; } diff --git a/src/interfaces/IServiceManagerUI.sol b/src/interfaces/IServiceManagerUI.sol index 92cdce9c..a506256b 100644 --- a/src/interfaces/IServiceManagerUI.sol +++ b/src/interfaces/IServiceManagerUI.sol @@ -10,21 +10,23 @@ import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISi */ interface IServiceManagerUI { /** - * Metadata should follow the format outlined by this example. - { - "name": "EigenLabs AVS 1", - "website": "https://www.eigenlayer.xyz/", - "description": "This is my 1st AVS", - "logo": "https://holesky-operator-metadata.s3.amazonaws.com/eigenlayer.png", - "twitter": "https://twitter.com/eigenlayer" - } - * @notice Updates the metadata URI for the AVS - * @param _metadataURI is the metadata URI for the AVS + * @notice Updates the metadata URI for the AVS, + * @param metadataURI is the metadata URI for the AVS. + * @dev Metadata should follow the format outlined by this example. + * { + * "name": "EigenLabs AVS 1", + * "website": "https://www.eigenlayer.xyz/", + * "description": "This is my 1st AVS", + * "logo": "https://holesky-operator-metadata.s3.amazonaws.com/eigenlayer.png", + * "twitter": "https://twitter.com/eigenlayer" + * } */ - function updateAVSMetadataURI(string memory _metadataURI) external; + function updateAVSMetadataURI( + string memory metadataURI + ) external; /** - * @notice Forwards a call to EigenLayer's AVSDirectory contract to confirm operator registration with the AVS + * @notice Forwards a call to EigenLayer's AVSDirectory 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. */ @@ -34,28 +36,34 @@ interface IServiceManagerUI { ) external; /** - * @notice Forwards a call to EigenLayer's AVSDirectory contract to confirm operator deregistration from the AVS + * @notice Forwards a call to EigenLayer's AVSDirectory contract to confirm operator deregistration from the AVS. * @param operator The address of the operator to deregister. */ - function deregisterOperatorFromAVS(address operator) external; + 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 + * @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); + 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 + * @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); - /// @notice Returns the EigenLayer AVSDirectory contract. + /** + * @notice Returns the EigenLayer AVSDirectory contract. + */ function avsDirectory() external view returns (address); } diff --git a/src/interfaces/ISlasher.sol b/src/interfaces/ISlasher.sol new file mode 100644 index 00000000..fe388afb --- /dev/null +++ b/src/interfaces/ISlasher.sol @@ -0,0 +1,92 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.27; + +import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; +import {IAllocationManager} from + "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; + +interface ISlasherErrors { + /// @notice Thrown when a caller without veto committee privileges attempts a restricted operation. + error OnlyVetoCommittee(); + /// @notice Thrown when a caller without slasher privileges attempts a restricted operation. + error OnlySlasher(); + /// @notice Thrown when attempting to veto a slashing request after the veto period has expired. + error VetoPeriodPassed(); + /// @notice Thrown when attempting to execute a slashing request before the veto period has ended. + error VetoPeriodNotPassed(); + /// @notice Thrown when attempting to interact with a slashing request that has been cancelled. + error SlashingRequestIsCancelled(); + /// @notice Thrown when attempting to modify a slashing request that does not exist. + error SlashingRequestNotRequested(); +} + +interface ISlasherTypes { + /** + * @notice Represents the current status of a slashing request. + * @dev The status of a slashing request can be one of the following: + * - Null: Default state, no request exists. + * - Requested: Slashing has been requested but not yet executed. + * - Completed: Slashing has been successfully executed. + * - Cancelled: Slashing request was cancelled by veto committee. + */ + enum SlashingStatus { + Null, + Requested, + Completed, + Cancelled + } + + /** + * @notice Contains all information related to a slashing request. + * @param params The slashing parameters from the allocation manager. + * @param requestTimestamp The timestamp when the slashing request was created. + * @param status The current status of the slashing request. + */ + struct SlashingRequest { + IAllocationManager.SlashingParams params; + uint256 requestTimestamp; + SlashingStatus status; + } +} + +interface ISlasherEvents is ISlasherTypes { + /** + * @notice Emitted when a new slashing request is created. + * @param requestId The unique identifier for the slashing request (indexed). + * @param operator The address of the operator to be slashed (indexed). + * @param operatorSetId The ID of the operator set involved (indexed). + * @param wadsToSlash The amounts to slash from each strategy. + * @param description A human-readable description of the slashing reason. + */ + event SlashingRequested( + uint256 indexed requestId, + address indexed operator, + uint32 indexed operatorSetId, + uint256[] wadsToSlash, + string description + ); + + /** + * @notice Emitted when a slashing request is cancelled by the veto committee. + * @param requestId The unique identifier of the cancelled request (indexed). + */ + event SlashingRequestCancelled(uint256 indexed requestId); + + /** + * @notice Emitted when an operator is successfully slashed. + * @param slashingRequestId The ID of the executed slashing request (indexed). + * @param operator The address of the slashed operator (indexed). + * @param operatorSetId The ID of the operator set involved (indexed). + * @param wadsToSlash The amounts slashed from each strategy. + * @param description A human-readable description of why the operator was slashed. + */ + event OperatorSlashed( + uint256 indexed slashingRequestId, + address indexed operator, + uint32 indexed operatorSetId, + uint256[] wadsToSlash, + string description + ); +} + +interface ISlasher is ISlasherErrors, ISlasherEvents {} diff --git a/src/interfaces/ISlashingRegistryCoordinator.sol b/src/interfaces/ISlashingRegistryCoordinator.sol new file mode 100644 index 00000000..70b74af8 --- /dev/null +++ b/src/interfaces/ISlashingRegistryCoordinator.sol @@ -0,0 +1,606 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.27; + +import {IBLSApkRegistry} from "./IBLSApkRegistry.sol"; +import {IStakeRegistry} from "./IStakeRegistry.sol"; +import {IIndexRegistry} from "./IIndexRegistry.sol"; +import {BN254} from "../libraries/BN254.sol"; +import {IAllocationManager} from + "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; +import {IBLSApkRegistry} from "./IBLSApkRegistry.sol"; +import {IStakeRegistry, IStakeRegistryTypes} from "./IStakeRegistry.sol"; +import {IIndexRegistry} from "./IIndexRegistry.sol"; +import {BN254} from "../libraries/BN254.sol"; +import {IAVSRegistrar} from "eigenlayer-contracts/src/contracts/interfaces/IAVSRegistrar.sol"; + +interface ISlashingRegistryCoordinatorErrors { + /// @notice Thrown when array lengths in input parameters don't match. + error InputLengthMismatch(); + /// @notice Thrown when an invalid registration type is provided. + error InvalidRegistrationType(); + /// @notice Thrown when non-allocation manager calls restricted function. + error OnlyAllocationManager(); + /// @notice Thrown when non-ejector calls restricted function. + error OnlyEjector(); + /// @notice Thrown when operating on a non-existent quorum. + error QuorumDoesNotExist(); + /// @notice Thrown when registering/deregistering with empty bitmap. + error BitmapEmpty(); + /// @notice Thrown when registering for already registered quorums. + error AlreadyRegisteredForQuorums(); + /// @notice Thrown when registering before ejection cooldown expires. + error CannotReregisterYet(); + /// @notice Thrown when unregistered operator attempts restricted operation. + error NotRegistered(); + /// @notice Thrown when operator attempts self-churn. + error CannotChurnSelf(); + /// @notice Thrown when operator count doesn't match quorum requirements. + error QuorumOperatorCountMismatch(); + /// @notice Thrown when operator has insufficient stake for churn. + error InsufficientStakeForChurn(); + /// @notice Thrown when attempting to kick operator above stake threshold. + error CannotKickOperatorAboveThreshold(); + /// @notice Thrown when updating to zero bitmap. + error BitmapCannotBeZero(); + /// @notice Thrown when deregistering from unregistered quorum. + error NotRegisteredForQuorum(); + /// @notice Thrown when churn approver salt is already used. + error ChurnApproverSaltUsed(); + /// @notice Thrown when operators or quorums list is not sorted ascending. + error NotSorted(); + /// @notice Thrown when maximum quorum count is reached. + error MaxQuorumsReached(); + /// @notice Thrown when operator set operations are attempted while not enabled. + error OperatorSetsNotEnabled(); +} + +interface ISlashingRegistryCoordinatorTypes { + /// @notice Core data structure for tracking operator information. + /// @dev Links an operator's unique identifier with their current registration status. + /// @param operatorId Unique identifier for the operator, typically derived from their BLS public key. + /// @param status Current registration state of the operator in the system. + struct OperatorInfo { + bytes32 operatorId; + OperatorStatus status; + } + + /// @notice Records historical changes to an operator's quorum registrations. + /// @dev Used for querying an operator's quorum memberships at specific block numbers. + /// @param updateBlockNumber Block number when this update occurred (inclusive). + /// @param nextUpdateBlockNumber Block number when the next update occurred (exclusive), or 0 if this is the latest update. + /// @param quorumBitmap Bitmap where each bit represents registration in a specific quorum (1 = registered, 0 = not registered). + struct QuorumBitmapUpdate { + uint32 updateBlockNumber; + uint32 nextUpdateBlockNumber; + uint192 quorumBitmap; + } + + /// @notice Configuration parameters for operator management within a quorum. + /// @dev All BIPs (Basis Points) values are in relation to BIPS_DENOMINATOR (10000). + /// @param maxOperatorCount Maximum number of operators allowed in the quorum. + /// @param kickBIPsOfOperatorStake Required stake ratio (in BIPs) between new and existing operator for churn. + /// Example: 10500 means new operator needs 105% of existing operator's stake. + /// @param kickBIPsOfTotalStake Minimum stake ratio (in BIPs) of total quorum stake an operator must maintain. + /// Example: 100 means operator needs 1% of total quorum stake to avoid being churned. + struct OperatorSetParam { + uint32 maxOperatorCount; + uint16 kickBIPsOfOperatorStake; + uint16 kickBIPsOfTotalStake; + } + + /// @notice Parameters for removing an operator during churn. + /// @dev Used in registerOperatorWithChurn to specify which operator to replace. + /// @param quorumNumber The quorum from which to remove the operator. + /// @param operator Address of the operator to be removed. + struct OperatorKickParam { + uint8 quorumNumber; + address operator; + } + + /// @notice Represents the registration state of an operator. + /// @dev Used to track an operator's lifecycle in the system. + /// @custom:enum NEVER_REGISTERED The operator has never registered with the system. + /// @custom:enum REGISTERED The operator is currently registered and active. + /// @custom:enum DEREGISTERED The operator was previously registered but has since deregistered. + enum OperatorStatus { + NEVER_REGISTERED, + REGISTERED, + DEREGISTERED + } + + /** + * @notice Enum representing the type of operator registration. + * @custom:enum NORMAL Represents a normal operator registration. + * @custom:enum CHURN Represents an operator registration during a churn event. + */ + enum RegistrationType { + NORMAL, + CHURN + } + + /** + * @notice Data structure for storing the results of a registerOperator call. + * @dev Contains arrays storing per-quorum information about operator counts and stakes. + * @param numOperatorsPerQuorum For each quorum the operator registered for, stores the number of operators registered. + * @param operatorStakes For each quorum the operator registered for, stores the stake of the operator in the quorum. + * @param totalStakes For each quorum the operator registered for, stores the total stake of the quorum. + */ + struct RegisterResults { + uint32[] numOperatorsPerQuorum; + uint96[] operatorStakes; + uint96[] totalStakes; + } +} + +interface ISlashingRegistryCoordinatorEvents is ISlashingRegistryCoordinatorTypes { + /** + * @notice Emitted when an operator registers for service in one or more quorums. + * @dev Emitted in _registerOperator() and _registerOperatorToOperatorSet(). + * @param operator The address of the registered operator. + * @param operatorId The unique identifier of the operator (BLS public key hash). + */ + event OperatorRegistered(address indexed operator, bytes32 indexed operatorId); + + /** + * @notice Emitted when an operator deregisters from service in one or more quorums. + * @dev Emitted in _deregisterOperator(). + * @param operator The address of the deregistered operator. + * @param operatorId The unique identifier of the operator (BLS public key hash). + */ + event OperatorDeregistered(address indexed operator, bytes32 indexed operatorId); + + /** + * @notice Emitted when a quorum's operator set parameters are updated. + * @dev Emitted in _setOperatorSetParams(). + * @param quorumNumber The identifier of the quorum being updated. + * @param operatorSetParams The new operator set parameters for the quorum. + */ + event OperatorSetParamsUpdated(uint8 indexed quorumNumber, OperatorSetParam operatorSetParams); + + /** + * @notice Emitted when the churn approver address is updated. + * @dev Emitted in _setChurnApprover(). + * @param prevChurnApprover The previous churn approver address. + * @param newChurnApprover The new churn approver address. + */ + event ChurnApproverUpdated(address prevChurnApprover, address newChurnApprover); + + /** + * @notice Emitted when the ejector address is updated. + * @dev Emitted in _setEjector(). + * @param prevEjector The previous ejector address. + * @param newEjector The new ejector address. + */ + event EjectorUpdated(address prevEjector, address newEjector); + + /** + * @notice Emitted when all operators in a quorum are updated simultaneously. + * @dev Emitted in updateOperatorsForQuorum(). + * @param quorumNumber The identifier of the quorum being updated. + * @param blocknumber The block number at which the quorum update occurred. + */ + event QuorumBlockNumberUpdated(uint8 indexed quorumNumber, uint256 blocknumber); + + /** + * @notice Emitted when an operator's socket is updated. + * @dev Emitted in updateSocket(). + * @param operatorId The unique identifier of the operator (BLS public key hash). + * @param socket The new socket address for the operator (typically an IP address). + */ + event OperatorSocketUpdate(bytes32 indexed operatorId, string socket); +} + +interface ISlashingRegistryCoordinator is + IAVSRegistrar, + ISlashingRegistryCoordinatorErrors, + ISlashingRegistryCoordinatorEvents +{ + /// IMMUTABLES & CONSTANTS + + /** + * @notice EIP-712 typehash for operator churn approval signatures. + * @return The typehash constant. + */ + function OPERATOR_CHURN_APPROVAL_TYPEHASH() external view returns (bytes32); + + /** + * @notice EIP-712 typehash for pubkey registration signatures. + * @return The typehash constant. + */ + function PUBKEY_REGISTRATION_TYPEHASH() external view returns (bytes32); + + /** + * @notice Reference to the BLSApkRegistry contract. + * @return The BLSApkRegistry contract interface. + */ + function blsApkRegistry() external view returns (IBLSApkRegistry); + + /** + * @notice Reference to the StakeRegistry contract. + * @return The StakeRegistry contract interface. + */ + function stakeRegistry() external view returns (IStakeRegistry); + + /** + * @notice Reference to the IndexRegistry contract. + * @return The IndexRegistry contract interface. + */ + function indexRegistry() external view returns (IIndexRegistry); + + /** + * @notice Reference to the AllocationManager contract. + * @return The AllocationManager contract interface. + * @dev This is only relevant for Slashing AVSs + */ + function allocationManager() external view returns (IAllocationManager); + + /// STORAGE + + /** + * @notice The total number of quorums that have been created. + * @return The count of quorums. + */ + function quorumCount() external view returns (uint8); + + /** + * @notice Checks if a churn approver salt has been used. + * @param salt The salt to check. + * @return True if the salt has been used, false otherwise. + */ + function isChurnApproverSaltUsed( + bytes32 salt + ) external view returns (bool); + + /** + * @notice Gets the last block number when all operators in a quorum were updated. + * @param quorumNumber The quorum identifier. + * @return The block number of the last update. + */ + function quorumUpdateBlockNumber( + uint8 quorumNumber + ) external view returns (uint256); + + /** + * @notice Gets the registry contract address at a specific index. + * @param index The index in the registries array. + * @return The registry contract address. + */ + function registries( + uint256 index + ) external view returns (address); + + /** + * @notice The address authorized to approve operator churn operations. + * @return The churn approver address. + */ + function churnApprover() external view returns (address); + + /** + * @notice The address authorized to forcibly eject operators. + * @return The ejector address. + */ + function ejector() external view returns (address); + + /** + * @notice Gets the timestamp of an operator's last ejection. + * @param operator The operator address. + * @return The timestamp of the last ejection. + */ + function lastEjectionTimestamp( + address operator + ) external view returns (uint256); + + /** + * @notice The cooldown period after ejection before an operator can re-register. + * @return The cooldown duration in seconds. + */ + function ejectionCooldown() external view returns (uint256); + + /** + * @notice Checks if a quorum is an M2 quorum. + * @param quorumNumber The quorum identifier. + * @return True if the quorum is M2, false otherwise. + */ + function isM2Quorum( + uint8 quorumNumber + ) external view returns (bool); + + /** + * @notice Whether operator sets mode is enabled. + * @return True if operator sets mode is enabled, false otherwise. + */ + function operatorSetsEnabled() external view returns (bool); + + /// ACTIONS + + /** + * @notice Registers an operator through the allocation manager for operator set quorums. + * @param operator The operator address to register. + * @param operatorSetIds The operator set IDs to register for (corresponds to quorum numbers). + * @param data Additional registration data containing the operator's socket and BLS public key parameters. + * @dev Can only be called by the allocation manager. + * @dev Will revert if operator sets are not enabled or if registering for M2 quorums. + * @dev This function implements the Slashing registration pathway specified by the IAVSRegistrar interface. + */ + function registerOperator( + address operator, + uint32[] memory operatorSetIds, + bytes memory data + ) external; + + /** + * @notice Deregisters an operator through the allocation manager from operator set quorums. + * @param operator The operator address to deregister. + * @param operatorSetIds The operator set IDs to deregister from (corresponds to quorum numbers). + * @dev Can only be called by the allocation manager. + * @dev Will revert if operator sets are not enabled or if deregistering from M2 quorums. + * @dev This function implements the Slashing deregistration pathway specified by the IAVSRegistrar interface. + */ + function deregisterOperator(address operator, uint32[] memory operatorSetIds) external; + + /** + * @notice Updates stake weights for specified operators. If any operator is found to be below + * the minimum stake for their registered quorums, they are deregistered from those quorums. + * @param operators The operators whose stakes should be updated. + * @dev Stakes are queried from the Eigenlayer core DelegationManager contract. + */ + function updateOperators( + address[] memory operators + ) external; + + /** + * @notice For each quorum in `quorumNumbers`, updates the StakeRegistry's view of ALL its registered operators' stakes. + * Each quorum's `quorumUpdateBlockNumber` is also updated, which tracks the most recent block number when ALL registered + * operators were updated. + * @param operatorsPerQuorum for each quorum in `quorumNumbers`, this has a corresponding list of operators to update. + * @param quorumNumbers is an ordered byte array containing the quorum numbers being updated. + * @dev Each list of operator addresses MUST be sorted in ascending order. + * @dev Each list of operator addresses MUST represent the entire list of registered operators for the corresponding quorum. + * @dev Stakes are queried from the Eigenlayer core DelegationManager contract. + * @dev Will revert if an operator registers/deregisters for any quorum in `quorumNumbers` after transaction broadcast but before execution. + */ + function updateOperatorsForQuorum( + address[][] memory operatorsPerQuorum, + bytes calldata quorumNumbers + ) external; + + /** + * @notice Updates the socket of the msg.sender given they are a registered operator. + * @param socket The new socket address for the operator (typically an IP address). + * @dev Will revert if msg.sender is not a registered operator. + */ + function updateSocket( + string memory socket + ) external; + + /** + * @notice Forcibly removes an operator from specified quorums and sets their ejection timestamp. + * @param operator The operator address to eject. + * @param quorumNumbers The quorum numbers to eject the operator from. + * @dev Can only be called by the ejector address. + * @dev The operator cannot re-register until ejectionCooldown period has passed. + */ + function ejectOperator(address operator, bytes memory quorumNumbers) external; + + /** + * @notice Creates a new quorum that tracks total delegated stake for operators. + * @param operatorSetParams Configures the quorum's max operator count and churn parameters. + * @param minimumStake Sets the minimum stake required for an operator to register or remain registered. + * @param strategyParams A list of strategies and multipliers used by the StakeRegistry to calculate + * an operator's stake weight for the quorum. + * @dev For m2 AVS this function has the same behavior as createQuorum before. + * @dev For migrated AVS that enable operator sets this will create a quorum that measures total delegated stake for operator set. + */ + function createTotalDelegatedStakeQuorum( + OperatorSetParam memory operatorSetParams, + uint96 minimumStake, + IStakeRegistryTypes.StrategyParams[] memory strategyParams + ) external; + + /** + * @notice Creates a new quorum that tracks slashable stake for operators. + * @param operatorSetParams Configures the quorum's max operator count and churn parameters. + * @param minimumStake Sets the minimum stake required for an operator to register or remain registered. + * @param strategyParams A list of strategies and multipliers used by the StakeRegistry to calculate + * an operator's stake weight for the quorum. + * @param lookAheadPeriod The number of blocks to look ahead when calculating slashable stake. + * @dev Can only be called when operator sets are enabled. + */ + function createSlashableStakeQuorum( + OperatorSetParam memory operatorSetParams, + uint96 minimumStake, + IStakeRegistryTypes.StrategyParams[] memory strategyParams, + uint32 lookAheadPeriod + ) external; + + /** + * @notice Updates the configuration parameters for an existing operator set quorum. + * @param quorumNumber The identifier of the quorum to update. + * @param operatorSetParams The new operator set parameters to apply. + * @dev Can only be called by the contract owner. + */ + function setOperatorSetParams( + uint8 quorumNumber, + OperatorSetParam memory operatorSetParams + ) external; + + /** + * @notice Updates the address authorized to approve operator churn operations. + * @param _churnApprover The new churn approver address. + * @dev Can only be called by the contract owner. + * @dev The churn approver is responsible for signing off on operator replacements in full quorums. + */ + function setChurnApprover( + address _churnApprover + ) external; + + /** + * @notice Updates the address authorized to forcibly eject operators. + * @param _ejector The new ejector address. + * @dev Can only be called by the contract owner. + * @dev The ejector can force-remove operators from quorums regardless of their stake. + */ + function setEjector( + address _ejector + ) external; + + /** + * @notice Updates the duration operators must wait after ejection before re-registering. + * @param _ejectionCooldown The new cooldown duration in seconds. + * @dev Can only be called by the contract owner. + */ + function setEjectionCooldown( + uint256 _ejectionCooldown + ) external; + + /// VIEW + + /** + * @notice Returns the operator set parameters for a given quorum. + * @param quorumNumber The identifier of the quorum to query. + * @return The OperatorSetParam struct containing max operator count and churn thresholds. + */ + function getOperatorSetParams( + uint8 quorumNumber + ) external view returns (OperatorSetParam memory); + + /** + * @notice Returns the complete operator information for a given address. + * @param operator The operator address to query. + * @return An OperatorInfo struct containing the operator's ID and registration status. + */ + function getOperator( + address operator + ) external view returns (OperatorInfo memory); + + /** + * @notice Returns the unique identifier for a given operator address. + * @param operator The operator address to query. + * @return The operator's ID (derived from their BLS public key hash). + */ + function getOperatorId( + address operator + ) external view returns (bytes32); + + /** + * @notice Returns the operator address associated with a given operator ID. + * @param operatorId The unique identifier to look up. + * @return The operator's address. + * @dev Returns address(0) if the ID is not registered. + */ + function getOperatorFromId( + bytes32 operatorId + ) external view returns (address); + + /** + * @notice Returns the current registration status for a given operator. + * @param operator The operator address to query. + * @return The operator's status (NEVER_REGISTERED, REGISTERED, or DEREGISTERED). + */ + function getOperatorStatus( + address operator + ) external view returns (OperatorStatus); + + /** + * @notice Returns the indices needed to look up quorum bitmaps for operators at a specific block. + * @param blockNumber The historical block number to query. + * @param operatorIds Array of operator IDs to get indices for. + * @return Array of indices corresponding to each operator ID. + * @dev Reverts if any operator had not yet registered at the specified block. + * @dev This function is designed to find proper inputs for getQuorumBitmapAtBlockNumberByIndex. + */ + function getQuorumBitmapIndicesAtBlockNumber( + uint32 blockNumber, + bytes32[] memory operatorIds + ) external view returns (uint32[] memory); + + /** + * @notice Returns the quorum bitmap for an operator at a specific historical block. + * @param operatorId The operator's unique identifier. + * @param blockNumber The historical block number to query. + * @param index The index in the operator's bitmap history (from getQuorumBitmapIndicesAtBlockNumber). + * @return The quorum bitmap showing which quorums the operator was registered for. + * @dev Reverts if the index is incorrect for the specified block number. + */ + function getQuorumBitmapAtBlockNumberByIndex( + bytes32 operatorId, + uint32 blockNumber, + uint256 index + ) external view returns (uint192); + + /** + * @notice Returns a specific update from an operator's quorum bitmap history. + * @param operatorId The operator's unique identifier. + * @param index The index in the bitmap history to query. + * @return The QuorumBitmapUpdate struct at that index. + */ + function getQuorumBitmapUpdateByIndex( + bytes32 operatorId, + uint256 index + ) external view returns (QuorumBitmapUpdate memory); + + /** + * @notice Returns the current quorum bitmap for an operator. + * @param operatorId The operator's unique identifier. + * @return A bitmap where each bit represents registration in a specific quorum. + * @dev Returns 0 if the operator is not registered for any quorums. + */ + function getCurrentQuorumBitmap( + bytes32 operatorId + ) external view returns (uint192); + + /** + * @notice Returns the number of updates in an operator's bitmap history. + * @param operatorId The operator's unique identifier. + * @return The length of the bitmap history array. + */ + function getQuorumBitmapHistoryLength( + bytes32 operatorId + ) external view returns (uint256); + + /** + * @notice Returns the number of registry contracts managed by this coordinator. + * @return The count of registry contracts (typically 3: stake, BLS, and index). + */ + function numRegistries() external view returns (uint256); + + /** + * @notice Calculates the digest hash that must be signed by the churn approver. + * @param registeringOperator The address of the operator attempting to register. + * @param registeringOperatorId The unique ID of the registering operator. + * @param operatorKickParams Parameters specifying which operators to replace in full quorums. + * @param salt Random value to ensure signature uniqueness. + * @param expiry Timestamp after which the signature becomes invalid. + * @return The EIP-712 typed data hash to be signed. + */ + function calculateOperatorChurnApprovalDigestHash( + address registeringOperator, + bytes32 registeringOperatorId, + OperatorKickParam[] memory operatorKickParams, + bytes32 salt, + uint256 expiry + ) external view returns (bytes32); + + /** + * @notice Returns the message hash that an operator must sign to register their BLS public key. + * @param operator The address of the operator registering their key. + * @return A point on the G1 curve representing the message hash. + */ + function pubkeyRegistrationMessageHash( + address operator + ) external view returns (BN254.G1Point memory); + + /** + * @notice Returns the address of the contract owner. + * @return The owner's address. + * @dev The owner can update contract configuration and create new quorums. + */ + function owner() external view returns (address); + + /** + * @notice Returns the account identifier for this AVS (used for UAM integration in EigenLayer) + * @dev NOTE: Updating this value will break existing OperatorSets and UAM integration. This value should only be set once. + * @return The account identifier address + */ + function accountIdentifier() external view returns (address); +} diff --git a/src/interfaces/ISocketRegistry.sol b/src/interfaces/ISocketRegistry.sol index 136a345b..c456a4f9 100644 --- a/src/interfaces/ISocketRegistry.sol +++ b/src/interfaces/ISocketRegistry.sol @@ -6,5 +6,7 @@ interface ISocketRegistry { function setOperatorSocket(bytes32 _operatorId, string memory _socket) external; /// @notice gets the stored socket for an operator - function getOperatorSocket(bytes32 _operatorId) external view returns (string memory); + function getOperatorSocket( + bytes32 _operatorId + ) external view returns (string memory); } diff --git a/src/interfaces/IStakeRegistry.sol b/src/interfaces/IStakeRegistry.sol index 044ccd58..b7e74644 100644 --- a/src/interfaces/IStakeRegistry.sol +++ b/src/interfaces/IStakeRegistry.sol @@ -1,116 +1,245 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; -import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.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"; +/// @notice Interface containing all error definitions for the StakeRegistry contract. +interface IStakeRegistryErrors { + /// @dev Thrown when the caller is not the registry coordinator + error OnlySlashingRegistryCoordinator(); + /// @dev Thrown when the caller is not the owner of the registry coordinator + error OnlySlashingRegistryCoordinatorOwner(); + /// @dev Thrown when the stake is below the minimum required for a quorum + error BelowMinimumStakeRequirement(); + /// @notice Thrown when attempting to create a quorum that already exists. + error QuorumAlreadyExists(); + /// @notice Thrown when attempting to interact with a quorum that does not exist. + error QuorumDoesNotExist(); + /// @notice Thrown when two array parameters have mismatching lengths. + error InputArrayLengthMismatch(); + /// @notice Thrown when an input array has zero length. + error InputArrayLengthZero(); + /// @notice Thrown when a duplicate strategy is provided in an input array. + error InputDuplicateStrategy(); + /// @notice Thrown when a multiplier input is zero. + error InputMultiplierZero(); + /// @notice Thrown when the provided block number is invalid for the stake update. + error InvalidBlockNumber(); + /// @notice Thrown when attempting to access stake history that doesn't exist for a quorum. + error EmptyStakeHistory(); +} -/** - * @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 { - - // DATA STRUCTURES +interface IStakeRegistryTypes { + /// @notice Defines the type of stake being tracked. + /// @param TOTAL_DELEGATED Represents the total delegated stake. + /// @param TOTAL_SLASHABLE Represents the total slashable stake. + enum StakeType { + TOTAL_DELEGATED, + TOTAL_SLASHABLE + } - /// @notice struct used to store the stakes of an individual operator or the sum of all operators' stakes, for storage + /// @notice Stores stake information for an operator or total stakes at a specific block. + /// @param updateBlockNumber The block number at which the stake amounts were updated. + /// @param nextUpdateBlockNumber The block number at which the next update occurred (0 if no next update). + /// @param stake The stake weight for the quorum. 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. - /// @notice This entry has the value **0** until another update takes place. uint32 nextUpdateBlockNumber; - // stake weight for the quorum 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 - */ + /// @notice Parameters for weighing a particular strategy's stake. + /// @param strategy The strategy contract address. + /// @param multiplier The weight multiplier applied to the strategy's stake. struct StrategyParams { IStrategy strategy; uint96 multiplier; } +} - // EVENTS +interface IStakeRegistryEvents is IStakeRegistryTypes { + /** + * @notice Emitted when an operator's stake is updated. + * @param operatorId The unique identifier of the operator (indexed). + * @param quorumNumber The quorum number for which the stake was updated. + * @param stake The new stake amount. + */ + event OperatorStakeUpdate(bytes32 indexed operatorId, uint8 quorumNumber, uint96 stake); - /// @notice emitted whenever the stake of `operator` is updated - event OperatorStakeUpdate( - bytes32 indexed operatorId, - uint8 quorumNumber, - uint96 stake - ); - /// @notice emitted when the minimum stake for a quorum is updated + /** + * @notice Emitted when the look ahead period for checking operator shares is updated. + * @param oldLookAheadBlocks The previous look ahead period. + * @param newLookAheadBlocks The new look ahead period. + */ + event LookAheadPeriodChanged(uint32 oldLookAheadBlocks, uint32 newLookAheadBlocks); + + /** + * @notice Emitted when the stake type is updated. + * @param newStakeType The new stake type being set. + */ + event StakeTypeSet(StakeType newStakeType); + + /** + * @notice Emitted when the minimum stake for a quorum is updated. + * @param quorumNumber The quorum number being updated (indexed). + * @param minimumStake The new minimum stake requirement. + */ event MinimumStakeForQuorumUpdated(uint8 indexed quorumNumber, uint96 minimumStake); - /// @notice emitted when a new quorum is created + + /** + * @notice Emitted when a new quorum is created. + * @param quorumNumber The number of the newly created quorum (indexed). + */ event QuorumCreated(uint8 indexed quorumNumber); - /// @notice emitted when `strategy` has been added to the array at `strategyParams[quorumNumber]` + + /** + * @notice Emitted when a strategy is added to a quorum. + * @param quorumNumber The quorum number the strategy was added to (indexed). + * @param strategy The strategy contract that was added. + */ event StrategyAddedToQuorum(uint8 indexed quorumNumber, IStrategy strategy); - /// @notice emitted when `strategy` has removed from the array at `strategyParams[quorumNumber]` + + /** + * @notice Emitted when a strategy is removed from a quorum. + * @param quorumNumber The quorum number the strategy was removed from (indexed). + * @param strategy The strategy contract that was removed. + */ event StrategyRemovedFromQuorum(uint8 indexed quorumNumber, IStrategy strategy); - /// @notice emitted when `strategy` has its `multiplier` updated in the array at `strategyParams[quorumNumber]` - event StrategyMultiplierUpdated(uint8 indexed quorumNumber, IStrategy strategy, uint256 multiplier); + + /** + * @notice Emitted when a strategy's multiplier is updated. + * @param quorumNumber The quorum number for the strategy update (indexed). + * @param strategy The strategy contract being updated. + * @param multiplier The new multiplier value. + */ + event StrategyMultiplierUpdated( + uint8 indexed quorumNumber, IStrategy strategy, uint256 multiplier + ); +} + +interface IStakeRegistry is IStakeRegistryErrors, IStakeRegistryEvents { + /// STATE + + /** + * @notice Returns the EigenLayer delegation manager contract. + */ + function delegation() external view returns (IDelegationManager); + + /// ACTIONS /** * @notice Registers the `operator` with `operatorId` for the specified `quorumNumbers`. * @param operator The address of the operator to register. * @param operatorId The id of the operator to register. * @param quorumNumbers The quorum numbers the operator is registering for, where each byte is an 8 bit integer quorumNumber. - * @return The operator's current stake for each quorum, and the total stake for each quorum - * @dev access restricted to the RegistryCoordinator + * @return operatorStakes The operator's current stake for each quorum. + * @return totalStakes 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 - * 2) `quorumNumbers.length` != 0 - * 3) `quorumNumbers` is ordered in ascending order - * 4) the operator is not already registered + * 1) `quorumNumbers` has no duplicates. + * 2) `quorumNumbers.length` != 0. + * 3) `quorumNumbers` is ordered in ascending order. + * 4) The operator is not already registered. */ function registerOperator( - address operator, - bytes32 operatorId, + address operator, + bytes32 operatorId, bytes memory quorumNumbers - ) external returns (uint96[] memory, uint96[] memory); + ) external returns (uint96[] memory operatorStakes, uint96[] memory totalStakes); /** * @notice Deregisters the operator with `operatorId` for the specified `quorumNumbers`. * @param operatorId The id of the operator to deregister. * @param quorumNumbers The quorum numbers the operator is deregistering from, where each byte is an 8 bit integer quorumNumber. - * @dev access restricted to the RegistryCoordinator + * @dev 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 + * 1) `quorumNumbers` has no duplicates. + * 2) `quorumNumbers.length` != 0. + * 3) `quorumNumbers` is ordered in ascending order. + * 4) The operator is not already deregistered. + * 5) `quorumNumbers` is a subset of the quorumNumbers that the operator is registered for. */ function deregisterOperator(bytes32 operatorId, bytes memory quorumNumbers) external; /** - * @notice Initialize a new quorum created by the registry coordinator by setting strategies, weights, and minimum stake + * @notice Called by the registry coordinator to update an operator's stake for one or more quorums. + * @param operator The address of the operator to update. + * @param operatorId The id of the operator to update. + * @param quorumNumbers The quorum numbers to update the stake for. + * @return A bitmap of quorums where the operator no longer meets the minimum stake and should be deregistered. */ - function initializeQuorum(uint8 quorumNumber, uint96 minimumStake, StrategyParams[] memory strategyParams) external; + function updateOperatorStake( + address operator, + bytes32 operatorId, + bytes calldata quorumNumbers + ) external returns (uint192); + + /** + * @notice Initialize a new quorum created by the registry coordinator by setting strategies, weights, and minimum stake. + * @param quorumNumber The number of the quorum to initialize. + * @param minimumStake The minimum stake required for the quorum. + * @param strategyParams The initial strategy parameters for the quorum. + */ + function initializeDelegatedStakeQuorum( + uint8 quorumNumber, + uint96 minimumStake, + StrategyParams[] memory strategyParams + ) external; - /// @notice Adds new strategies and the associated multipliers to the @param quorumNumber. - function addStrategies( + /** + * @notice Initialize a new quorum and push its first history update. + * @param quorumNumber The number of the quorum to initialize. + * @param minimumStake The minimum stake required for the quorum. + * @param lookAheadPeriod The look ahead period for checking operator shares. + * @param strategyParams The initial strategy parameters for the quorum. + */ + function initializeSlashableStakeQuorum( uint8 quorumNumber, + uint96 minimumStake, + uint32 lookAheadPeriod, StrategyParams[] memory strategyParams ) external; /** - * @notice This function is used for removing strategies and their associated weights from the - * 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 + * @notice Sets the minimum stake requirement for a quorum `quorumNumber`. + * @param quorumNumber The quorum number to set the minimum stake for. + * @param minimumStake The new minimum stake requirement. + */ + function setMinimumStakeForQuorum(uint8 quorumNumber, uint96 minimumStake) external; + + /** + * @notice Sets the look ahead time to `lookAheadBlocks` for checking operator shares for a specific quorum. + * @param quorumNumber The quorum number to set the look ahead period for. + * @param lookAheadBlocks The number of blocks to look ahead when checking shares. + */ + function setSlashableStakeLookahead(uint8 quorumNumber, uint32 lookAheadBlocks) external; + + /** + * @notice Adds new strategies and their associated multipliers to the specified 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". + * @param quorumNumber The quorum number to add strategies to. + * @param strategyParams The strategy parameters to add. + */ + function addStrategies(uint8 quorumNumber, StrategyParams[] memory strategyParams) external; + + /** + * @notice Removes strategies and their associated weights from the specified quorum. + * @param quorumNumber The quorum number to remove strategies from. + * @param indicesToRemove The indices of strategies to remove. + * @dev Higher indices should be *first* in the list of `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 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 + * @notice Modifies the weights of strategies that are already in the mapping strategyParams. + * @param quorumNumber The quorum number to change the strategy for. + * @param strategyIndices The indices of the strategies to change. + * @param newMultipliers The new multipliers for the strategies. */ function modifyStrategyParams( uint8 quorumNumber, @@ -118,131 +247,215 @@ interface IStakeRegistry is IRegistry { uint96[] calldata newMultipliers ) external; - /// @notice Constant used as a divisor in calculating weights. - function WEIGHTING_DIVISOR() external pure returns (uint256); + /// VIEW - /// @notice Returns the EigenLayer delegation manager contract. - function delegation() external view returns (IDelegationManager); + /** + * @notice Returns whether a quorum is an operator set quorum. + * @param quorumNumber The quorum number to query. + * @return Whether the quorum is an operator set quorum. + */ + function isOperatorSetQuorum( + uint8 quorumNumber + ) external view returns (bool); - /// @notice In order to register for a quorum i, an operator must have at least `minimumStakeForQuorum[i]` - function minimumStakeForQuorum(uint8 quorumNumber) external view returns (uint96); + /** + * @notice Returns the minimum stake requirement for a quorum `quorumNumber`. + * @dev In order to register for a quorum i, an operator must have at least `minimumStakeForQuorum[i]`. + * @param quorumNumber The quorum number to query. + * @return The minimum stake requirement. + */ + function minimumStakeForQuorum( + uint8 quorumNumber + ) external view returns (uint96); - /// @notice Returns the length of the dynamic array stored in `strategyParams[quorumNumber]`. - function strategyParamsLength(uint8 quorumNumber) external view returns (uint256); + /** + * @notice Returns the length of the dynamic array stored in `strategyParams[quorumNumber]`. + * @param quorumNumber The quorum number to query. + * @return The number of strategies for the quorum. + */ + function strategyParamsLength( + uint8 quorumNumber + ) external view returns (uint256); - /// @notice Returns the strategy and weight multiplier for the `index`'th strategy in the quorum `quorumNumber` + /** + * @notice Returns the strategy and weight multiplier for the `index`'th strategy in the quorum. + * @param quorumNumber The quorum number to query. + * @param index The index of the strategy to query. + * @return The strategy parameters. + */ function strategyParamsByIndex( uint8 quorumNumber, uint256 index ) external view returns (StrategyParams 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` + * @notice Returns the length of the stake history for an operator in a quorum. + * @param operatorId The id of the operator to query. + * @param quorumNumber The quorum number to query. + * @return The length of the stake history array. + */ + function getStakeHistoryLength( + bytes32 operatorId, + uint8 quorumNumber + ) external view returns (uint256); + + /** + * @notice Computes the total weight of the operator in the specified quorum. + * @param quorumNumber The quorum number to query. + * @param operator The operator address to query. + * @return The total weight of the operator. + * @dev Reverts if `quorumNumber` is greater than or equal to `quorumCount`. */ - function weightOfOperatorForQuorum(uint8 quorumNumber, address operator) external view returns (uint96); + function weightOfOperatorForQuorum( + uint8 quorumNumber, + address operator + ) external view returns (uint96); /** - * @notice Returns the entire `operatorIdToStakeHistory[operatorId][quorumNumber]` array. + * @notice Returns the entire stake history array for an operator in a quorum. * @param operatorId The id of the operator of interest. * @param quorumNumber The quorum number to get the stake for. + * @return The array of stake updates. */ - function getStakeHistory(bytes32 operatorId, uint8 quorumNumber) external view returns (StakeUpdate[] memory); + function getStakeHistory( + bytes32 operatorId, + uint8 quorumNumber + ) external view returns (StakeUpdate[] memory); - function getTotalStakeHistoryLength(uint8 quorumNumber) external view returns (uint256); + /** + * @notice Returns the length of the total stake history for a quorum. + * @param quorumNumber The quorum number to query. + * @return The length of the total stake history array. + */ + 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]`. + * @notice Returns the stake update at the specified index in the total stake history. + * @param quorumNumber The quorum number to query. + * @param index The index to query. + * @return The stake update at the specified index. */ - function getTotalStakeUpdateAtIndex(uint8 quorumNumber, uint256 index) external view returns (StakeUpdate 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) - external - view - returns (uint32); + /** + * @notice Returns the index of the operator's stake update at the specified block number. + * @param operatorId The id of the operator to query. + * @param quorumNumber The quorum number to query. + * @param blockNumber The block number to query. + * @return The index of the stake update. + */ + function getStakeUpdateIndexAtBlockNumber( + 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 getTotalStakeIndicesAtBlockNumber(uint32 blockNumber, bytes calldata quorumNumbers) external view returns(uint32[] memory) ; + /** + * @notice Returns the indices of total stakes for the provided quorums at the given block number. + * @param blockNumber The block number to query. + * @param quorumNumbers The quorum numbers to query. + * @return The array of stake update indices. + */ + function getTotalStakeIndicesAtBlockNumber( + uint32 blockNumber, + bytes calldata quorumNumbers + ) external view returns (uint32[] memory); /** - * @notice Returns the `index`-th entry in the `operatorIdToStakeHistory[operatorId][quorumNumber]` array. - * @param quorumNumber The quorum number to get the stake for. - * @param operatorId The id of the operator of interest. - * @param index Array index for lookup, within the dynamic array `operatorIdToStakeHistory[operatorId][quorumNumber]`. + * @notice Returns the stake update at the specified index for an operator in a quorum. + * @param quorumNumber The quorum number to query. + * @param operatorId The id of the operator to query. + * @param index The index to query. + * @return The stake update at the specified index. * @dev Function will revert if `index` is out-of-bounds. */ - function getStakeUpdateAtIndex(uint8 quorumNumber, bytes32 operatorId, uint256 index) - external - view - returns (StakeUpdate memory); + function getStakeUpdateAtIndex( + uint8 quorumNumber, + bytes32 operatorId, + uint256 index + ) external view returns (StakeUpdate memory); /** - * @notice Returns the most recent stake weight for the `operatorId` for a certain quorum - * @dev Function returns an StakeUpdate struct with **every entry equal to 0** in the event that the operator has no stake history + * @notice Returns the most recent stake update for an operator in a quorum. + * @param operatorId The id of the operator to query. + * @param quorumNumber The quorum number to query. + * @return The most recent stake update. + * @dev Returns a StakeUpdate struct with all entries equal to 0 if the operator has no stake history. */ - function getLatestStakeUpdate(bytes32 operatorId, uint8 quorumNumber) external view returns (StakeUpdate 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 - * `index`-th entry in the `operatorIdToStakeHistory[operatorId][quorumNumber]` array if the entry - * corresponds to the operator's stake at `blockNumber`. Reverts otherwise. - * @param quorumNumber The quorum number to get the stake for. - * @param operatorId The id of the operator of interest. - * @param index Array index for lookup, within the dynamic array `operatorIdToStakeHistory[operatorId][quorumNumber]`. - * @param blockNumber Block number to make sure the stake is from. + * @notice Returns the stake at the specified block number and index for an operator in a quorum. + * @param quorumNumber The quorum number to query. + * @param blockNumber The block number to query. + * @param operatorId The id of the operator to query. + * @param index The index to query. + * @return The stake amount. * @dev Function will revert if `index` is out-of-bounds. - * @dev used the BLSSignatureChecker to get past stakes of signing operators + * @dev Used by the BLSSignatureChecker to get past stakes of signing operators. */ - function getStakeAtBlockNumberAndIndex(uint8 quorumNumber, uint32 blockNumber, bytes32 operatorId, uint256 index) - external - view - returns (uint96); + function getStakeAtBlockNumberAndIndex( + uint8 quorumNumber, + uint32 blockNumber, + bytes32 operatorId, + uint256 index + ) external view returns (uint96); /** - * @notice Returns the total stake weight for quorum `quorumNumber`, at the `index`-th entry in the - * `totalStakeHistory[quorumNumber]` array if the entry corresponds to the total stake at `blockNumber`. - * Reverts otherwise. - * @param quorumNumber The quorum number to get the stake for. - * @param index Array index for lookup, within the dynamic array `totalStakeHistory[quorumNumber]`. - * @param blockNumber Block number to make sure the stake is from. + * @notice Returns the total stake at the specified block number and index for a quorum. + * @param quorumNumber The quorum number to query. + * @param blockNumber The block number to query. + * @param index The index to query. + * @return The total stake amount. * @dev Function will revert if `index` is out-of-bounds. - * @dev used the BLSSignatureChecker to get past stakes of signing operators + * @dev Used by the BLSSignatureChecker to get past stakes of signing operators. */ - function getTotalStakeAtBlockNumberFromIndex(uint8 quorumNumber, uint32 blockNumber, uint256 index) external view returns (uint96); + function getTotalStakeAtBlockNumberFromIndex( + uint8 quorumNumber, + uint32 blockNumber, + uint256 index + ) external view returns (uint96); /** - * @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 + * @notice Returns the current stake for an operator in a quorum. + * @param operatorId The id of the operator to query. + * @param quorumNumber The quorum number to query. + * @return The current stake amount. + * @dev Returns 0 if the operator has no stake history. */ - 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 getStakeAtBlockNumber(bytes32 operatorId, uint8 quorumNumber, uint32 blockNumber) - external - view - returns (uint96); + function getCurrentStake( + bytes32 operatorId, + uint8 quorumNumber + ) external view returns (uint96); /** - * @notice Returns the stake weight from the latest entry in `_totalStakeHistory` for quorum `quorumNumber`. - * @dev Will revert if `_totalStakeHistory[quorumNumber]` is empty. + * @notice Returns the stake of an operator at a specific block number. + * @param operatorId The id of the operator to query. + * @param quorumNumber The quorum number to query. + * @param blockNumber The block number to query. + * @return The stake amount at the specified block. */ - function getCurrentTotalStake(uint8 quorumNumber) external view returns (uint96); + function getStakeAtBlockNumber( + bytes32 operatorId, + uint8 quorumNumber, + uint32 blockNumber + ) external view returns (uint96); /** - * @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 - * @return A bitmap of quorums where the operator no longer meets the minimum stake - * and should be deregistered. + * @notice Returns the current total stake for a quorum. + * @param quorumNumber The quorum number to query. + * @return The current total stake amount. + * @dev Will revert if `_totalStakeHistory[quorumNumber]` is empty. */ - function updateOperatorStake( - address operator, - bytes32 operatorId, - bytes calldata quorumNumbers - ) external returns (uint192); + function getCurrentTotalStake( + uint8 quorumNumber + ) external view returns (uint96); } diff --git a/src/libraries/BN254.sol b/src/libraries/BN254.sol index 0d8adb92..df4b8248 100644 --- a/src/libraries/BN254.sol +++ b/src/libraries/BN254.sol @@ -19,7 +19,7 @@ // The remainder of the code in this library is written by LayrLabs Inc. and is also under an MIT license -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; /** * @title Library for operations on the BN254 elliptic curve. @@ -46,16 +46,31 @@ library BN254 { uint256[2] Y; } + /// @dev Thrown when the sum of two points of G1 fails + error ECAddFailed(); + /// @dev Thrown when the scalar multiplication of a point of G1 fails + error ECMulFailed(); + /// @dev Thrown when the scalar is too large. + error ScalarTooLarge(); + /// @dev Thrown when the pairing check fails + error ECPairingFailed(); + /// @dev Thrown when the exponentiation mod fails + error ExpModFailed(); + function generatorG1() internal pure returns (G1Point memory) { return G1Point(1, 2); } // generator of group G2 /// @dev Generator point in F_q2 is of the form: (x0 + ix1, y0 + iy1). - uint256 internal constant G2x1 = 11559732032986387107991004021392285783925812861821192530917403151452391805634; - uint256 internal constant G2x0 = 10857046999023057135944570762232829481370756359578518086990519993285655852781; - uint256 internal constant G2y1 = 4082367875863433681332203403145435568316851327593401208105741076214120093531; - uint256 internal constant G2y0 = 8495653923123431417604973247489272438418190587263600148770280649306958101930; + uint256 internal constant G2x1 = + 11559732032986387107991004021392285783925812861821192530917403151452391805634; + uint256 internal constant G2x0 = + 10857046999023057135944570762232829481370756359578518086990519993285655852781; + uint256 internal constant G2y1 = + 4082367875863433681332203403145435568316851327593401208105741076214120093531; + uint256 internal constant G2y0 = + 8495653923123431417604973247489272438418190587263600148770280649306958101930; /// @notice returns the G2 generator /// @dev mind the ordering of the 1s and 0s! @@ -68,10 +83,14 @@ library BN254 { // negation of the generator of group G2 /// @dev Generator point in F_q2 is of the form: (x0 + ix1, y0 + iy1). - uint256 internal constant nG2x1 = 11559732032986387107991004021392285783925812861821192530917403151452391805634; - uint256 internal constant nG2x0 = 10857046999023057135944570762232829481370756359578518086990519993285655852781; - uint256 internal constant nG2y1 = 17805874995975841540914202342111839520379459829704422454583296818431106115052; - uint256 internal constant nG2y0 = 13392588948715843804641432497768002650278120570034223513918757245338268106653; + uint256 internal constant nG2x1 = + 11559732032986387107991004021392285783925812861821192530917403151452391805634; + uint256 internal constant nG2x0 = + 10857046999023057135944570762232829481370756359578518086990519993285655852781; + uint256 internal constant nG2y1 = + 17805874995975841540914202342111839520379459829704422454583296818431106115052; + uint256 internal constant nG2y0 = + 13392588948715843804641432497768002650278120570034223513918757245338268106653; function negGeneratorG2() internal pure returns (G2Point memory) { return G2Point([nG2x1, nG2x0], [nG2y1, nG2y0]); @@ -84,7 +103,9 @@ library BN254 { * @param p Some point in G1. * @return The negation of `p`, i.e. p.plus(p.negate()) should be zero. */ - function negate(G1Point memory p) internal pure returns (G1Point memory) { + function negate( + G1Point memory p + ) internal pure returns (G1Point memory) { // The prime q in the base field F_q for G1 if (p.X == 0 && p.Y == 0) { return G1Point(0, 0); @@ -109,12 +130,10 @@ library BN254 { success := staticcall(sub(gas(), 2000), 6, input, 0x80, r, 0x40) // Use "invalid" to make gas estimation work switch success - case 0 { - invalid() - } + case 0 { invalid() } } - require(success, "ec-add-failed"); + require(success, ECAddFailed()); } /** @@ -122,12 +141,15 @@ library BN254 { * @param p the point to multiply * @param s the scalar to multiply by * @dev this function is only safe to use if the scalar is 9 bits or less - */ - function scalar_mul_tiny(BN254.G1Point memory p, uint16 s) internal view returns (BN254.G1Point memory) { - require(s < 2**9, "scalar-too-large"); + */ + function scalar_mul_tiny( + BN254.G1Point memory p, + uint16 s + ) internal view returns (BN254.G1Point memory) { + require(s < 2 ** 9, ScalarTooLarge()); // if s is 1 return p - if(s == 1) { + if (s == 1) { return p; } @@ -141,7 +163,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) { @@ -155,7 +177,7 @@ library BN254 { ++i; } } - + // return the accumulated product return acc; } @@ -176,11 +198,9 @@ library BN254 { success := staticcall(sub(gas(), 2000), 7, input, 0x60, r, 0x40) // Use "invalid" to make gas estimation work switch success - case 0 { - invalid() - } + case 0 { invalid() } } - require(success, "ec-mul-failed"); + require(success, ECMulFailed()); } /** @@ -218,12 +238,10 @@ library BN254 { success := staticcall(sub(gas(), 2000), 8, input, mul(12, 0x20), out, 0x20) // Use "invalid" to make gas estimation work switch success - case 0 { - invalid() - } + case 0 { invalid() } } - require(success, "pairing-opcode-failed"); + require(success, ECPairingFailed()); return out[0] != 0; } @@ -270,7 +288,9 @@ library BN254 { /// @return hashedG1 the keccak256 hash of the G1 Point /// @dev used for BLS signatures - function hashG1Point(BN254.G1Point memory pk) internal pure returns (bytes32 hashedG1) { + function hashG1Point( + BN254.G1Point memory pk + ) internal pure returns (bytes32 hashedG1) { assembly { mstore(0, mload(pk)) mstore(0x20, mload(add(0x20, pk))) @@ -289,7 +309,9 @@ library BN254 { /** * @notice adapted from https://github.com/HarryR/solcrypto/blob/master/contracts/altbn128.sol */ - function hashToG1(bytes32 _x) internal view returns (G1Point memory) { + function hashToG1( + bytes32 _x + ) internal view returns (G1Point memory) { uint256 beta = 0; uint256 y = 0; @@ -299,7 +321,7 @@ library BN254 { (beta, y) = findYFromX(x); // y^2 == beta - if( beta == mulmod(y, y, FP_MODULUS) ) { + if (beta == mulmod(y, y, FP_MODULUS)) { return G1Point(x, y); } @@ -315,21 +337,29 @@ library BN254 { * * Returns: (x^3 + b), y */ - function findYFromX(uint256 x) internal view returns (uint256, uint256) { + function findYFromX( + uint256 x + ) internal view returns (uint256, uint256) { // beta = (x^3 + b) % p uint256 beta = addmod(mulmod(mulmod(x, x, FP_MODULUS), x, FP_MODULUS), 3, FP_MODULUS); // y^2 = x^3 + b // this acts like: y = sqrt(beta) = beta^((p+1) / 4) - uint256 y = expMod(beta, 0xc19139cb84c680a6e14116da060561765e05aa45a1c72a34f082305b61f3f52, FP_MODULUS); + uint256 y = expMod( + beta, 0xc19139cb84c680a6e14116da060561765e05aa45a1c72a34f082305b61f3f52, FP_MODULUS + ); return (beta, y); } - function expMod(uint256 _base, uint256 _exponent, uint256 _modulus) internal view returns (uint256 retval) { + function expMod( + uint256 _base, + uint256 _exponent, + uint256 _modulus + ) internal view returns (uint256 retval) { bool success; uint256[1] memory output; - uint[6] memory input; + uint256[6] memory input; input[0] = 0x20; // baseLen = new(big.Int).SetBytes(getData(input, 0, 32)) input[1] = 0x20; // expLen = new(big.Int).SetBytes(getData(input, 32, 32)) input[2] = 0x20; // modLen = new(big.Int).SetBytes(getData(input, 64, 32)) @@ -340,11 +370,9 @@ library BN254 { success := staticcall(sub(gas(), 2000), 5, input, 0xc0, output, 0x20) // Use "invalid" to make gas estimation work switch success - case 0 { - invalid() - } + case 0 { invalid() } } - require(success, "BN254.expMod: call failure"); + require(success, ExpModFailed()); return output[0]; } } diff --git a/src/libraries/BitmapUtils.sol b/src/libraries/BitmapUtils.sol index 41f9ef48..4e4c9373 100644 --- a/src/libraries/BitmapUtils.sol +++ b/src/libraries/BitmapUtils.sol @@ -1,12 +1,19 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; /** * @title Library for Bitmap utilities such as converting between an array of bytes and a bitmap and finding the number of 1s in a bitmap. * @author Layr Labs, Inc. */ library BitmapUtils { + /// @dev Thrown when the input byte array is too long to be converted to a bitmap + error BytesArrayLengthTooLong(); + /// @dev Thrown when the input byte array is not strictly ordered + error BytesArrayNotOrdered(); + /// @dev Thrown when the bitmap value is too large + error BitmapValueTooLarge(); + /** * @notice Byte arrays are meant to contain unique bytes. * If the array length exceeds 256, then it's impossible for all entries to be unique. @@ -22,10 +29,11 @@ library BitmapUtils { * @dev This function will eventually revert in the event that the `orderedBytesArray` is not properly ordered (in ascending order). * @dev This function will also revert if the `orderedBytesArray` input contains any duplicate entries (i.e. duplicate bytes). */ - function orderedBytesArrayToBitmap(bytes memory orderedBytesArray) internal pure returns (uint256) { + function orderedBytesArrayToBitmap( + bytes memory orderedBytesArray + ) internal pure returns (uint256) { // sanity-check on input. a too-long input would fail later on due to having duplicate entry(s) - require(orderedBytesArray.length <= MAX_BYTE_ARRAY_LENGTH, - "BitmapUtils.orderedBytesArrayToBitmap: orderedBytesArray is too long"); + require(orderedBytesArray.length <= MAX_BYTE_ARRAY_LENGTH, BytesArrayLengthTooLong()); // return empty bitmap early if length of array is 0 if (orderedBytesArray.length == 0) { @@ -46,7 +54,7 @@ library BitmapUtils { // construct a single-bit mask from the numerical value of the next byte of the array bitMask = uint256(1 << uint8(orderedBytesArray[i])); // check strictly ascending array ordering by comparing the mask to the bitmap so far (revert if mask isn't greater than bitmap) - require(bitMask > bitmap, "BitmapUtils.orderedBytesArrayToBitmap: orderedBytesArray is not ordered"); + require(bitMask > bitmap, BytesArrayNotOrdered()); // add the entry to the bitmap bitmap = (bitmap | bitMask); } @@ -59,12 +67,13 @@ library BitmapUtils { * @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 bitUpperBound) internal pure returns (uint256) { + function orderedBytesArrayToBitmap( + bytes memory orderedBytesArray, + uint8 bitUpperBound + ) internal pure returns (uint256) { uint256 bitmap = orderedBytesArrayToBitmap(orderedBytesArray); - require((1 << bitUpperBound) > bitmap, - "BitmapUtils.orderedBytesArrayToBitmap: bitmap exceeds max value" - ); + require((1 << bitUpperBound) > bitmap, BitmapValueTooLarge()); return bitmap; } @@ -76,7 +85,9 @@ library BitmapUtils { * @dev This function returns 'true' for the edge case of the `bytesArray` having zero length. * It also returns 'false' early for arrays with length in excess of MAX_BYTE_ARRAY_LENGTH (i.e. so long that they cannot be strictly ordered) */ - function isArrayStrictlyAscendingOrdered(bytes calldata bytesArray) internal pure returns (bool) { + function isArrayStrictlyAscendingOrdered( + bytes calldata bytesArray + ) internal pure returns (bool) { // Return early if the array is too long, or has a length of 0 if (bytesArray.length > MAX_BYTE_ARRAY_LENGTH) { return false; @@ -95,11 +106,11 @@ library BitmapUtils { if (uint256(uint8(bytesArray[i])) <= uint256(uint8(singleByte))) { return false; } - + // Pull the next byte out of the array singleByte = bytesArray[i]; } - + return true; } @@ -109,7 +120,9 @@ library BitmapUtils { * @return bytesArray The resulting bitmap array of bytes. * @dev Each byte in the input is processed as indicating a single bit to flip in the bitmap */ - function bitmapToBytesArray(uint256 bitmap) internal pure returns (bytes memory /*bytesArray*/) { + function bitmapToBytesArray( + uint256 bitmap + ) internal pure returns (bytes memory /*bytesArray*/ ) { // initialize an empty uint256 to be used as a bitmask inside the loop uint256 bitMask; // allocate only the needed amount of memory @@ -129,14 +142,18 @@ library BitmapUtils { // if the i-th bit is flipped, then add a byte encoding the value 'i' to the `bytesArray` bytesArray[arrayIndex] = bytes1(uint8(i)); // increment the bytesArray slot since we've assigned one more byte of memory - unchecked{ ++arrayIndex; } + unchecked { + ++arrayIndex; + } } } return bytesArray; } /// @return count number of ones in binary representation of `n` - function countNumOnes(uint256 n) internal pure returns (uint16) { + function countNumOnes( + uint256 n + ) internal pure returns (uint16) { uint16 count = 0; while (n > 0) { n &= (n - 1); // Clear the least significant bit (turn off the rightmost set bit). @@ -149,9 +166,9 @@ library BitmapUtils { function isSet(uint256 bitmap, uint8 bit) internal pure returns (bool) { return 1 == ((bitmap >> bit) & 1); } - + /** - * @notice Returns a copy of `bitmap` with `bit` set. + * @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);` @@ -163,7 +180,9 @@ library BitmapUtils { /** * @notice Returns true if `bitmap` has no set bits */ - function isEmpty(uint256 bitmap) internal pure returns (bool) { + function isEmpty( + uint256 bitmap + ) internal pure returns (bool) { return bitmap == 0; } diff --git a/src/libraries/LibMergeSort.sol b/src/libraries/LibMergeSort.sol new file mode 100644 index 00000000..e8d05da2 --- /dev/null +++ b/src/libraries/LibMergeSort.sol @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.27; + +library LibMergeSort { + function sort( + address[] memory array + ) internal pure returns (address[] memory) { + if (array.length <= 1) { + return array; + } + + uint256 mid = array.length / 2; + address[] memory left = new address[](mid); + address[] memory right = new address[](array.length - mid); + + for (uint256 i = 0; i < mid; i++) { + left[i] = array[i]; + } + for (uint256 i = mid; i < array.length; i++) { + right[i - mid] = array[i]; + } + + return mergeSortArrays(sort(left), sort(right)); + } + + function mergeSortArrays( + address[] memory left, + address[] memory right + ) internal pure returns (address[] memory) { + uint256 leftLength = left.length; + uint256 rightLength = right.length; + address[] memory merged = new address[](leftLength + rightLength); + + uint256 i = 0; // Index for left array + uint256 j = 0; // Index for right array + uint256 k = 0; // Index for merged array + + // Merge the two arrays into the merged array + while (i < leftLength && j < rightLength) { + if (left[i] < right[j]) { + merged[k++] = left[i++]; + } else if (left[i] > right[j]) { + merged[k++] = right[j++]; + } else { + merged[k++] = left[i++]; + j++; + } + } + + // Copy remaining elements of left, if any + while (i < leftLength) { + merged[k++] = left[i++]; + } + + // Copy remaining elements of right, if any + while (j < rightLength) { + merged[k++] = right[j++]; + } + + // Resize the merged array to remove unused space + assembly { + mstore(merged, k) + } + + return merged; + } +} diff --git a/src/libraries/QuorumBitmapHistoryLib.sol b/src/libraries/QuorumBitmapHistoryLib.sol new file mode 100644 index 00000000..d709b035 --- /dev/null +++ b/src/libraries/QuorumBitmapHistoryLib.sol @@ -0,0 +1,154 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.27; + +import { + ISlashingRegistryCoordinator, + ISlashingRegistryCoordinatorTypes +} from "../interfaces/ISlashingRegistryCoordinator.sol"; + +/// @title QuorumBitmapHistoryLib +/// @notice This library operates on the _operatorBitmapHistory in the RegistryCoordinator +library QuorumBitmapHistoryLib { + /// @dev Thrown when the quorum bitmap update is not found + error BitmapUpdateNotFound(); + /// @dev Thrown when quorum bitmap update is after the block number + error BitmapUpdateIsAfterBlockNumber(); + /// @dev Thrown when the next update block number is not 0 and strictly greater than blockNumber + error NextBitmapUpdateIsBeforeBlockNumber(); + + /// @notice Retrieves the index of the quorum bitmap update at or before the specified block number + /// @param self The mapping of operator IDs to their quorum bitmap update history + /// @param blockNumber The block number to search for + /// @param operatorId The ID of the operator + /// @return index The index of the quorum bitmap update + function getQuorumBitmapIndexAtBlockNumber( + mapping(bytes32 => ISlashingRegistryCoordinator.QuorumBitmapUpdate[]) storage self, + uint32 blockNumber, + bytes32 operatorId + ) internal view returns (uint32 index) { + uint256 length = self[operatorId].length; + + // Traverse the operator's bitmap history in reverse, returning the first index + // corresponding to an update made before or at `blockNumber` + for (uint256 i = 0; i < length; i++) { + index = uint32(length - i - 1); + + if (self[operatorId][index].updateBlockNumber <= blockNumber) { + return index; + } + } + + revert( + "RegistryCoordinator.getQuorumBitmapIndexAtBlockNumber: no bitmap update found for operatorId" + ); + } + + /// @notice Retrieves the current quorum bitmap for the given operator ID + /// @param self The mapping of operator IDs to their quorum bitmap update history + /// @param operatorId The ID of the operator + /// @return The current quorum bitmap + function currentOperatorBitmap( + mapping(bytes32 => ISlashingRegistryCoordinator.QuorumBitmapUpdate[]) storage self, + bytes32 operatorId + ) internal view returns (uint192) { + uint256 historyLength = self[operatorId].length; + if (historyLength == 0) { + return 0; + } else { + return self[operatorId][historyLength - 1].quorumBitmap; + } + } + + /// @notice Retrieves the indices of the quorum bitmap updates for the given operator IDs at the specified block number + /// @param self The mapping of operator IDs to their quorum bitmap update history + /// @param blockNumber The block number to search for + /// @param operatorIds The array of operator IDs + /// @return An array of indices corresponding to the quorum bitmap updates + function getQuorumBitmapIndicesAtBlockNumber( + mapping(bytes32 => ISlashingRegistryCoordinator.QuorumBitmapUpdate[]) storage self, + uint32 blockNumber, + bytes32[] memory operatorIds + ) internal view returns (uint32[] memory) { + uint32[] memory indices = new uint32[](operatorIds.length); + for (uint256 i = 0; i < operatorIds.length; i++) { + indices[i] = getQuorumBitmapIndexAtBlockNumber(self, blockNumber, operatorIds[i]); + } + return indices; + } + + /// @notice Retrieves the quorum bitmap for the given operator ID at the specified block number and index + /// @param self The mapping of operator IDs to their quorum bitmap update history + /// @param operatorId The ID of the operator + /// @param blockNumber The block number to validate against + /// @param index The index of the quorum bitmap update + /// @return The quorum bitmap at the specified index + function getQuorumBitmapAtBlockNumberByIndex( + mapping(bytes32 => ISlashingRegistryCoordinator.QuorumBitmapUpdate[]) storage self, + bytes32 operatorId, + uint32 blockNumber, + uint256 index + ) internal view returns (uint192) { + ISlashingRegistryCoordinator.QuorumBitmapUpdate memory quorumBitmapUpdate = + self[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( + blockNumber >= quorumBitmapUpdate.updateBlockNumber, BitmapUpdateIsAfterBlockNumber() + ); + require( + quorumBitmapUpdate.nextUpdateBlockNumber == 0 + || blockNumber < quorumBitmapUpdate.nextUpdateBlockNumber, + NextBitmapUpdateIsBeforeBlockNumber() + ); + + return quorumBitmapUpdate.quorumBitmap; + } + + /// @notice Updates the quorum bitmap for the given operator ID + /// @param self The mapping of operator IDs to their quorum bitmap update history + /// @param operatorId The ID of the operator + /// @param newBitmap The new quorum bitmap to set + function updateOperatorBitmap( + mapping(bytes32 => ISlashingRegistryCoordinator.QuorumBitmapUpdate[]) storage self, + bytes32 operatorId, + uint192 newBitmap + ) internal { + uint256 historyLength = self[operatorId].length; + + if (historyLength == 0) { + // No prior bitmap history - push our first entry + self[operatorId].push( + ISlashingRegistryCoordinatorTypes.QuorumBitmapUpdate({ + updateBlockNumber: uint32(block.number), + nextUpdateBlockNumber: 0, + quorumBitmap: newBitmap + }) + ); + } else { + // We have prior history - fetch our last-recorded update + ISlashingRegistryCoordinator.QuorumBitmapUpdate storage lastUpdate = + self[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); + self[operatorId].push( + ISlashingRegistryCoordinatorTypes.QuorumBitmapUpdate({ + updateBlockNumber: uint32(block.number), + nextUpdateBlockNumber: 0, + quorumBitmap: newBitmap + }) + ); + } + } + } +} diff --git a/src/libraries/SignatureCheckerLib.sol b/src/libraries/SignatureCheckerLib.sol new file mode 100644 index 00000000..864e4afb --- /dev/null +++ b/src/libraries/SignatureCheckerLib.sol @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.27; + +import "@openzeppelin-upgrades/contracts/utils/cryptography/SignatureCheckerUpgradeable.sol"; + +/** + * @title SignatureCheckerLib + * @dev This library wraps the EIP1271SignatureUtils library to provide an external function for signature validation. + * This approach helps in reducing the code size of the RegistryCoordinator contract by offloading the signature + * validation logic to this external library. + */ +library SignatureCheckerLib { + error InvalidSignature(); + + /** + * @notice Validates a signature using EIP-1271 standard. + * @param signer The address of the signer. + * @param digestHash The hash of the data that was signed. + * @param signature The signature to be validated. + */ + function isValidSignature( + address signer, + bytes32 digestHash, + bytes memory signature + ) internal view { + if (!SignatureCheckerUpgradeable.isValidSignatureNow(signer, digestHash, signature)) { + revert InvalidSignature(); + } + } +} diff --git a/src/slashers/InstantSlasher.sol b/src/slashers/InstantSlasher.sol new file mode 100644 index 00000000..10276bff --- /dev/null +++ b/src/slashers/InstantSlasher.sol @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.27; + +import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; +import {IAllocationManager} from + "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; +import {ISlashingRegistryCoordinator} from "../interfaces/ISlashingRegistryCoordinator.sol"; +import {SlasherBase} from "./base/SlasherBase.sol"; + +contract InstantSlasher is SlasherBase { + constructor( + IAllocationManager _allocationManager, + ISlashingRegistryCoordinator _slashingRegistryCoordinator, + address _slasher + ) SlasherBase(_allocationManager, _slashingRegistryCoordinator) {} + + function initialize( + address _slasher + ) external initializer { + __SlasherBase_init(_slasher); + } + + function fulfillSlashingRequest( + IAllocationManager.SlashingParams memory _slashingParams + ) external virtual onlySlasher { + uint256 requestId = nextRequestId++; + _fulfillSlashingRequest(requestId, _slashingParams); + } +} diff --git a/src/slashers/VetoableSlasher.sol b/src/slashers/VetoableSlasher.sol new file mode 100644 index 00000000..949c46c2 --- /dev/null +++ b/src/slashers/VetoableSlasher.sol @@ -0,0 +1,92 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.27; + +import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; +import {IAllocationManager} from + "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; +import {SlasherBase} from "./base/SlasherBase.sol"; +import {ISlashingRegistryCoordinator} from "../interfaces/ISlashingRegistryCoordinator.sol"; + +contract VetoableSlasher is SlasherBase { + uint256 public constant VETO_PERIOD = 3 days; + address public vetoCommittee; + + mapping(uint256 => SlashingRequest) public slashingRequests; + + modifier onlyVetoCommittee() { + _checkVetoCommittee(msg.sender); + _; + } + + constructor( + IAllocationManager _allocationManager, + ISlashingRegistryCoordinator _slashingRegistryCoordinator, + address _slasher + ) SlasherBase(_allocationManager, _slashingRegistryCoordinator) {} + + function initialize(address _vetoCommittee, address _slasher) external virtual initializer { + __SlasherBase_init(_slasher); + vetoCommittee = _vetoCommittee; + } + + function queueSlashingRequest( + IAllocationManager.SlashingParams memory params + ) external virtual onlySlasher { + _queueSlashingRequest(params); + } + + function cancelSlashingRequest( + uint256 requestId + ) external virtual onlyVetoCommittee { + require( + block.timestamp < slashingRequests[requestId].requestTimestamp + VETO_PERIOD, + VetoPeriodPassed() + ); + require( + slashingRequests[requestId].status == SlashingStatus.Requested, + SlashingRequestNotRequested() + ); + + _cancelSlashingRequest(requestId); + } + + function fulfillSlashingRequest( + uint256 requestId + ) external virtual onlySlasher { + SlashingRequest storage request = slashingRequests[requestId]; + require(block.timestamp >= request.requestTimestamp + VETO_PERIOD, VetoPeriodNotPassed()); + require(request.status == SlashingStatus.Requested, SlashingRequestIsCancelled()); + + request.status = SlashingStatus.Completed; + + _fulfillSlashingRequest(requestId, request.params); + } + + function _queueSlashingRequest( + IAllocationManager.SlashingParams memory params + ) internal virtual { + uint256 requestId = nextRequestId++; + slashingRequests[requestId] = SlashingRequest({ + params: params, + requestTimestamp: block.timestamp, + status: SlashingStatus.Requested + }); + + emit SlashingRequested( + requestId, params.operator, params.operatorSetId, params.wadsToSlash, params.description + ); + } + + function _cancelSlashingRequest( + uint256 requestId + ) internal virtual { + slashingRequests[requestId].status = SlashingStatus.Cancelled; + emit SlashingRequestCancelled(requestId); + } + + function _checkVetoCommittee( + address account + ) internal view virtual { + require(account == vetoCommittee, OnlyVetoCommittee()); + } +} diff --git a/src/slashers/base/SlasherBase.sol b/src/slashers/base/SlasherBase.sol new file mode 100644 index 00000000..a6917d39 --- /dev/null +++ b/src/slashers/base/SlasherBase.sol @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.27; + +import {Initializable} from "@openzeppelin-upgrades/contracts/proxy/utils/Initializable.sol"; +import {SlasherStorage, ISlashingRegistryCoordinator} from "./SlasherStorage.sol"; +import { + IAllocationManagerTypes, + IAllocationManager +} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; +import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; + +abstract contract SlasherBase is Initializable, SlasherStorage { + modifier onlySlasher() { + _checkSlasher(msg.sender); + _; + } + + constructor( + IAllocationManager _allocationManager, + ISlashingRegistryCoordinator _registryCoordinator + ) SlasherStorage(_allocationManager, _registryCoordinator) { + _disableInitializers(); + } + + function __SlasherBase_init( + address _slasher + ) internal onlyInitializing { + slasher = _slasher; + } + + function _fulfillSlashingRequest( + uint256 _requestId, + IAllocationManager.SlashingParams memory _params + ) internal virtual { + allocationManager.slashOperator({ + avs: slashingRegistryCoordinator.accountIdentifier(), + params: _params + }); + emit OperatorSlashed( + _requestId, + _params.operator, + _params.operatorSetId, + _params.wadsToSlash, + _params.description + ); + } + + function _checkSlasher( + address account + ) internal view virtual { + require(account == slasher, OnlySlasher()); + } +} diff --git a/src/slashers/base/SlasherStorage.sol b/src/slashers/base/SlasherStorage.sol new file mode 100644 index 00000000..54627326 --- /dev/null +++ b/src/slashers/base/SlasherStorage.sol @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.27; + +import {IAllocationManager} from + "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; +import {ISlasher} from "../../interfaces/ISlasher.sol"; +import {ISlashingRegistryCoordinator} from "../../interfaces/ISlashingRegistryCoordinator.sol"; + +contract SlasherStorage is ISlasher { + /** + * + * CONSTANTS AND IMMUTABLES + * + */ + + /// @notice the AllocationManager that tracks OperatorSets and Slashing in EigenLayer + IAllocationManager public immutable allocationManager; + /// @notice the SlashingRegistryCoordinator for this AVS + ISlashingRegistryCoordinator public immutable slashingRegistryCoordinator; + /** + * + * STATE + * + */ + address public slasher; + + uint256 public nextRequestId; + + constructor( + IAllocationManager _allocationManager, + ISlashingRegistryCoordinator _slashingRegistryCoordinator + ) { + allocationManager = _allocationManager; + slashingRegistryCoordinator = _slashingRegistryCoordinator; + } + + uint256[48] private __gap; +} diff --git a/src/unaudited/ECDSAServiceManagerBase.sol b/src/unaudited/ECDSAServiceManagerBase.sol index 91738da5..d5bf2c55 100644 --- a/src/unaudited/ECDSAServiceManagerBase.sol +++ b/src/unaudited/ECDSAServiceManagerBase.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {OwnableUpgradeable} from "@openzeppelin-upgrades/contracts/access/OwnableUpgradeable.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; @@ -8,17 +8,19 @@ import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISi import {IAVSDirectory} from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol"; import {IServiceManager} from "../interfaces/IServiceManager.sol"; import {IServiceManagerUI} from "../interfaces/IServiceManagerUI.sol"; -import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; +import {IDelegationManager} from + "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; import {IStakeRegistry} from "../interfaces/IStakeRegistry.sol"; -import {IRewardsCoordinator} from "eigenlayer-contracts/src/contracts/interfaces/IRewardsCoordinator.sol"; -import {Quorum} from "../interfaces/IECDSAStakeRegistryEventsAndErrors.sol"; +import {IRewardsCoordinator} from + "eigenlayer-contracts/src/contracts/interfaces/IRewardsCoordinator.sol"; +import {IECDSAStakeRegistryTypes} from "../interfaces/IECDSAStakeRegistry.sol"; import {ECDSAStakeRegistry} from "../unaudited/ECDSAStakeRegistry.sol"; +import {IAVSRegistrar} from "eigenlayer-contracts/src/contracts/interfaces/IAVSRegistrar.sol"; +import {IAllocationManager} from + "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; -abstract contract ECDSAServiceManagerBase is - IServiceManager, - OwnableUpgradeable -{ +abstract contract ECDSAServiceManagerBase is IServiceManager, OwnableUpgradeable { using SafeERC20 for IERC20; /// @notice Address of the stake registry contract, which manages registration and stake recording. @@ -27,6 +29,9 @@ abstract contract ECDSAServiceManagerBase is /// @notice Address of the AVS directory contract, which manages AVS-related data for registered operators. address public immutable avsDirectory; + /// @notice Address of the AllocationManager contract + address public immutable allocationManager; + /// @notice Address of the rewards coordinator contract, which handles rewards distributions. address internal immutable rewardsCoordinator; @@ -41,10 +46,7 @@ abstract contract ECDSAServiceManagerBase is * This is used to restrict certain registration and deregistration functionality to the `stakeRegistry` */ modifier onlyStakeRegistry() { - require( - msg.sender == stakeRegistry, - "ECDSAServiceManagerBase.onlyStakeRegistry: caller is not the stakeRegistry" - ); + require(msg.sender == stakeRegistry, OnlyStakeRegistry()); _; } @@ -57,10 +59,7 @@ abstract contract ECDSAServiceManagerBase is } function _checkRewardsInitiator() internal view { - require( - msg.sender == rewardsInitiator, - "ECDSAServiceManagerBase.onlyRewardsInitiator: caller is not the rewards initiator" - ); + require(msg.sender == rewardsInitiator, OnlyRewardsInitiator()); } /** @@ -74,12 +73,14 @@ abstract contract ECDSAServiceManagerBase is address _avsDirectory, address _stakeRegistry, address _rewardsCoordinator, - address _delegationManager + address _delegationManager, + address _allocationManager ) { avsDirectory = _avsDirectory; stakeRegistry = _stakeRegistry; rewardsCoordinator = _rewardsCoordinator; delegationManager = _delegationManager; + allocationManager = _allocationManager; _disableInitializers(); } @@ -110,18 +111,16 @@ abstract contract ECDSAServiceManagerBase is _createAVSRewardsSubmission(rewardsSubmissions); } - /// @inheritdoc IServiceManager function createOperatorDirectedAVSRewardsSubmission( - IRewardsCoordinator.OperatorDirectedRewardsSubmission[] - calldata operatorDirectedRewardsSubmissions - ) external virtual onlyRewardsInitiator { - _createOperatorDirectedAVSRewardsSubmission( + IRewardsCoordinator.OperatorDirectedRewardsSubmission[] calldata operatorDirectedRewardsSubmissions - ); + ) external virtual onlyRewardsInitiator { + _createOperatorDirectedAVSRewardsSubmission(operatorDirectedRewardsSubmissions); } - /// @inheritdoc IServiceManager - function setClaimerFor(address claimer) external virtual onlyOwner { + function setClaimerFor( + address claimer + ) external virtual onlyOwner { _setClaimerFor(claimer); } @@ -141,12 +140,7 @@ abstract contract ECDSAServiceManagerBase is } /// @inheritdoc IServiceManagerUI - function getRestakeableStrategies() - external - view - virtual - returns (address[] memory) - { + function getRestakeableStrategies() external view virtual returns (address[] memory) { return _getRestakeableStrategies(); } @@ -178,10 +172,7 @@ abstract contract ECDSAServiceManagerBase is address operator, ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature ) internal virtual { - IAVSDirectory(avsDirectory).registerOperatorToAVS( - operator, - operatorSignature - ); + IAVSDirectory(avsDirectory).registerOperatorToAVS(operator, operatorSignature); } /** @@ -189,7 +180,9 @@ abstract contract ECDSAServiceManagerBase is * @dev This internal function is a proxy to the `deregisterOperatorFromAVS` function of the AVSDirectory contract. * @param operator The address of the operator to deregister. */ - function _deregisterOperatorFromAVS(address operator) internal virtual { + function _deregisterOperatorFromAVS( + address operator + ) internal virtual { IAVSDirectory(avsDirectory).deregisterOperatorFromAVS(operator); } @@ -203,19 +196,14 @@ abstract contract ECDSAServiceManagerBase is ) internal virtual { for (uint256 i = 0; i < rewardsSubmissions.length; ++i) { rewardsSubmissions[i].token.safeTransferFrom( - msg.sender, - address(this), - rewardsSubmissions[i].amount + msg.sender, address(this), rewardsSubmissions[i].amount ); rewardsSubmissions[i].token.safeIncreaseAllowance( - rewardsCoordinator, - rewardsSubmissions[i].amount + rewardsCoordinator, rewardsSubmissions[i].amount ); } - IRewardsCoordinator(rewardsCoordinator).createAVSRewardsSubmission( - rewardsSubmissions - ); + IRewardsCoordinator(rewardsCoordinator).createAVSRewardsSubmission(rewardsSubmissions); } /** @@ -224,52 +212,40 @@ abstract contract ECDSAServiceManagerBase is * @param operatorDirectedRewardsSubmissions The operator-directed rewards submissions being created. */ function _createOperatorDirectedAVSRewardsSubmission( - IRewardsCoordinator.OperatorDirectedRewardsSubmission[] - calldata operatorDirectedRewardsSubmissions + IRewardsCoordinator.OperatorDirectedRewardsSubmission[] calldata + operatorDirectedRewardsSubmissions ) internal virtual { - for ( - uint256 i = 0; - i < operatorDirectedRewardsSubmissions.length; - ++i - ) { + for (uint256 i = 0; i < operatorDirectedRewardsSubmissions.length; ++i) { // Calculate total amount of token to transfer uint256 totalAmount = 0; for ( - uint256 j = 0; - j < - operatorDirectedRewardsSubmissions[i].operatorRewards.length; - ++j + uint256 j = 0; j < operatorDirectedRewardsSubmissions[i].operatorRewards.length; ++j ) { - totalAmount += operatorDirectedRewardsSubmissions[i] - .operatorRewards[j] - .amount; + totalAmount += operatorDirectedRewardsSubmissions[i].operatorRewards[j].amount; } // Transfer token to ServiceManager and approve RewardsCoordinator to transfer again // in createOperatorDirectedAVSRewardsSubmission() call operatorDirectedRewardsSubmissions[i].token.safeTransferFrom( - msg.sender, - address(this), - totalAmount + msg.sender, address(this), totalAmount ); operatorDirectedRewardsSubmissions[i].token.safeIncreaseAllowance( - rewardsCoordinator, - totalAmount + rewardsCoordinator, totalAmount ); } - IRewardsCoordinator(rewardsCoordinator) - .createOperatorDirectedAVSRewardsSubmission( - address(this), - operatorDirectedRewardsSubmissions - ); + IRewardsCoordinator(rewardsCoordinator).createOperatorDirectedAVSRewardsSubmission( + address(this), operatorDirectedRewardsSubmissions + ); } /** * @notice Forwards a call to Eigenlayer's RewardsCoordinator contract to set the address of the entity that can call `processClaim` on behalf of this contract. * @param claimer The address of the entity that can call `processClaim` on behalf of the earner. */ - function _setClaimerFor(address claimer) internal virtual { + function _setClaimerFor( + address claimer + ) internal virtual { IRewardsCoordinator(rewardsCoordinator).setClaimerFor(claimer); } @@ -278,13 +254,8 @@ abstract contract ECDSAServiceManagerBase is * @dev Fetches the quorum configuration from the ECDSAStakeRegistry and extracts the strategy addresses. * @return strategies An array of addresses representing the strategies in the current quorum. */ - function _getRestakeableStrategies() - internal - view - virtual - returns (address[] memory) - { - Quorum memory quorum = ECDSAStakeRegistry(stakeRegistry).quorum(); + function _getRestakeableStrategies() internal view virtual returns (address[] memory) { + IECDSAStakeRegistryTypes.Quorum memory quorum = ECDSAStakeRegistry(stakeRegistry).quorum(); address[] memory strategies = new address[](quorum.strategies.length); for (uint256 i = 0; i < quorum.strategies.length; i++) { strategies[i] = address(quorum.strategies[i].strategy); @@ -292,6 +263,17 @@ abstract contract ECDSAServiceManagerBase is return strategies; } + /** + * @notice Sets the AVS registrar address in the AllocationManager + * @param registrar The new AVS registrar address + * @dev Only callable by the registry coordinator + */ + function setAVSRegistrar( + IAVSRegistrar registrar + ) external onlyOwner { + IAllocationManager(allocationManager).setAVSRegistrar(address(this), registrar); + } + /** * @notice Retrieves the addresses of strategies where the operator has restaked. * @dev This function fetches the quorum details from the ECDSAStakeRegistry, retrieves the operator's shares for each strategy, @@ -302,14 +284,14 @@ abstract contract ECDSAServiceManagerBase is function _getOperatorRestakedStrategies( address _operator ) internal view virtual returns (address[] memory) { - Quorum memory quorum = ECDSAStakeRegistry(stakeRegistry).quorum(); + IECDSAStakeRegistryTypes.Quorum memory quorum = ECDSAStakeRegistry(stakeRegistry).quorum(); uint256 count = quorum.strategies.length; IStrategy[] memory strategies = new IStrategy[](count); for (uint256 i; i < count; i++) { strategies[i] = quorum.strategies[i].strategy; } - uint256[] memory shares = IDelegationManager(delegationManager) - .getOperatorShares(_operator, strategies); + uint256[] memory shares = + IDelegationManager(delegationManager).getOperatorShares(_operator, strategies); uint256 activeCount; for (uint256 i; i < count; i++) { @@ -342,7 +324,9 @@ abstract contract ECDSAServiceManagerBase is _setRewardsInitiator(newRewardsInitiator); } - function _setRewardsInitiator(address newRewardsInitiator) internal { + function _setRewardsInitiator( + address newRewardsInitiator + ) internal { emit RewardsInitiatorUpdated(rewardsInitiator, newRewardsInitiator); rewardsInitiator = newRewardsInitiator; } diff --git a/src/unaudited/ECDSAStakeRegistry.sol b/src/unaudited/ECDSAStakeRegistry.sol index ab4bdbeb..c961c83e 100644 --- a/src/unaudited/ECDSAStakeRegistry.sol +++ b/src/unaudited/ECDSAStakeRegistry.sol @@ -1,16 +1,24 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; -import {ECDSAStakeRegistryStorage, Quorum, StrategyParams} from "./ECDSAStakeRegistryStorage.sol"; +import { + IECDSAStakeRegistry, + ECDSAStakeRegistryStorage, + IECDSAStakeRegistryTypes +} from "./ECDSAStakeRegistryStorage.sol"; import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; -import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; +import {IDelegationManager} from + "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol"; import {IServiceManager} from "../interfaces/IServiceManager.sol"; import {OwnableUpgradeable} from "@openzeppelin-upgrades/contracts/access/OwnableUpgradeable.sol"; -import {CheckpointsUpgradeable} from "@openzeppelin-upgrades/contracts/utils/CheckpointsUpgradeable.sol"; -import {SignatureCheckerUpgradeable} from "@openzeppelin-upgrades/contracts/utils/cryptography/SignatureCheckerUpgradeable.sol"; -import {IERC1271Upgradeable} from "@openzeppelin-upgrades/contracts/interfaces/IERC1271Upgradeable.sol"; +import {CheckpointsUpgradeable} from + "@openzeppelin-upgrades/contracts/utils/CheckpointsUpgradeable.sol"; +import {SignatureCheckerUpgradeable} from + "@openzeppelin-upgrades/contracts/utils/cryptography/SignatureCheckerUpgradeable.sol"; +import {IERC1271Upgradeable} from + "@openzeppelin-upgrades/contracts/interfaces/IERC1271Upgradeable.sol"; /// @title ECDSA Stake Registry /// @dev THIS CONTRACT IS NOT AUDITED @@ -33,214 +41,168 @@ contract ECDSAStakeRegistry is /// @notice Initializes the contract with the given parameters. /// @param _serviceManager The address of the service manager. - /// @param _thresholdWeight The threshold weight in basis points. - /// @param _quorum The quorum struct containing the details of the quorum thresholds. + /// @param thresholdWeight The threshold weight in basis points. + /// @param quorum The quorum struct containing the details of the quorum thresholds. function initialize( address _serviceManager, - uint256 _thresholdWeight, - Quorum memory _quorum + uint256 thresholdWeight, + IECDSAStakeRegistryTypes.Quorum memory quorum ) external initializer { - __ECDSAStakeRegistry_init(_serviceManager, _thresholdWeight, _quorum); + __ECDSAStakeRegistry_init(_serviceManager, thresholdWeight, quorum); } - /// @notice Registers a new operator using a provided signature and signing key - /// @param _operatorSignature Contains the operator's signature, salt, and expiry - /// @param _signingKey The signing key to add to the operator's history + /// @notice Initializes state for the StakeRegistry + /// @param _serviceManagerAddr The AVS' ServiceManager contract's address + function __ECDSAStakeRegistry_init( + address _serviceManagerAddr, + uint256 thresholdWeight, + IECDSAStakeRegistryTypes.Quorum memory quorum + ) internal onlyInitializing { + _serviceManager = _serviceManagerAddr; + _updateStakeThreshold(thresholdWeight); + _updateQuorumConfig(quorum); + __Ownable_init(); + } + + /// @inheritdoc IECDSAStakeRegistry function registerOperatorWithSignature( - ISignatureUtils.SignatureWithSaltAndExpiry memory _operatorSignature, - address _signingKey + ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature, + address signingKey ) external { - _registerOperatorWithSig(msg.sender, _operatorSignature, _signingKey); + _registerOperatorWithSig(msg.sender, operatorSignature, signingKey); } - /// @notice Deregisters an existing operator + /// @inheritdoc IECDSAStakeRegistry function deregisterOperator() external { _deregisterOperator(msg.sender); } - /** - * @notice Updates the signing key for an operator - * @dev Only callable by the operator themselves - * @param _newSigningKey The new signing key to set for the operator - */ - function updateOperatorSigningKey(address _newSigningKey) external { + /// @inheritdoc IECDSAStakeRegistry + function updateOperatorSigningKey( + address newSigningKey + ) external { if (!_operatorRegistered[msg.sender]) { revert OperatorNotRegistered(); } - _updateOperatorSigningKey(msg.sender, _newSigningKey); + _updateOperatorSigningKey(msg.sender, newSigningKey); } - /** - * @notice Updates the StakeRegistry's view of one or more operators' stakes adding a new entry in their history of stake checkpoints, - * @dev Queries stakes from the Eigenlayer core DelegationManager contract - * @param _operators A list of operator addresses to update - */ - function updateOperators(address[] memory _operators) external { - _updateOperators(_operators); + /// @inheritdoc IECDSAStakeRegistry + function updateOperators( + address[] memory operators + ) external { + _updateOperators(operators); } - /** - * @notice Updates the quorum configuration and the set of operators - * @dev Only callable by the contract owner. - * It first updates the quorum configuration and then updates the list of operators. - * @param _quorum The new quorum configuration, including strategies and their new weights - * @param _operators The list of operator addresses to update stakes for - */ + /// @inheritdoc IECDSAStakeRegistry function updateQuorumConfig( - Quorum memory _quorum, - address[] memory _operators + IECDSAStakeRegistryTypes.Quorum memory quorum, + address[] memory operators ) external onlyOwner { - _updateQuorumConfig(_quorum); - _updateOperators(_operators); + _updateQuorumConfig(quorum); + _updateOperators(operators); } - /// @notice Updates the weight an operator must have to join the operator set - /// @dev Access controlled to the contract owner - /// @param _newMinimumWeight The new weight an operator must have to join the operator set + /// @inheritdoc IECDSAStakeRegistry function updateMinimumWeight( - uint256 _newMinimumWeight, - address[] memory _operators + uint256 newMinimumWeight, + address[] memory operators ) external onlyOwner { - _updateMinimumWeight(_newMinimumWeight); - _updateOperators(_operators); + _updateMinimumWeight(newMinimumWeight); + _updateOperators(operators); } - /** - * @notice Sets a new cumulative threshold weight for message validation by operator set signatures. - * @dev This function can only be invoked by the owner of the contract. It delegates the update to - * an internal function `_updateStakeThreshold`. - * @param _thresholdWeight The updated threshold weight required to validate a message. This is the - * cumulative weight that must be met or exceeded by the sum of the stakes of the signatories for - * a message to be deemed valid. - */ - function updateStakeThreshold(uint256 _thresholdWeight) external onlyOwner { - _updateStakeThreshold(_thresholdWeight); + /// @inheritdoc IECDSAStakeRegistry + function updateStakeThreshold( + uint256 thresholdWeight + ) external onlyOwner { + _updateStakeThreshold(thresholdWeight); } - /// @notice Verifies if the provided signature data is valid for the given data hash. - /// @param _dataHash The hash of the data that was signed. - /// @param _signatureData Encoded signature data consisting of an array of operators, an array of signatures, and a reference block number. - /// @return The function selector that indicates the signature is valid according to ERC1271 standard. function isValidSignature( - bytes32 _dataHash, + bytes32 digest, bytes memory _signatureData ) external view returns (bytes4) { - ( - address[] memory operators, - bytes[] memory signatures, - uint32 referenceBlock - ) = abi.decode(_signatureData, (address[], bytes[], uint32)); - _checkSignatures(_dataHash, operators, signatures, referenceBlock); + (address[] memory operators, bytes[] memory signatures, uint32 referenceBlock) = + abi.decode(_signatureData, (address[], bytes[], uint32)); + _checkSignatures(digest, operators, signatures, referenceBlock); return IERC1271Upgradeable.isValidSignature.selector; } - /// @notice Retrieves the current stake quorum details. - /// @return Quorum - The current quorum of strategies and weights - function quorum() external view returns (Quorum memory) { + /// @inheritdoc IECDSAStakeRegistry + function quorum() external view returns (IECDSAStakeRegistryTypes.Quorum memory) { return _quorum; } - /** - * @notice Retrieves the latest signing key for a given operator. - * @param _operator The address of the operator. - * @return The latest signing key of the operator. - */ - function getLastestOperatorSigningKey( - address _operator + /// @inheritdoc IECDSAStakeRegistry + function getLatestOperatorSigningKey( + address operator ) external view returns (address) { - return address(uint160(_operatorSigningKeyHistory[_operator].latest())); + return address(uint160(_operatorSigningKeyHistory[operator].latest())); } - /** - * @notice Retrieves the latest signing key for a given operator at a specific block number. - * @param _operator The address of the operator. - * @param _blockNumber The block number to get the operator's signing key. - * @return The signing key of the operator at the given block. - */ + /// @inheritdoc IECDSAStakeRegistry function getOperatorSigningKeyAtBlock( - address _operator, - uint256 _blockNumber + address operator, + uint256 blockNumber ) external view returns (address) { - return - address( - uint160( - _operatorSigningKeyHistory[_operator].getAtBlock( - _blockNumber - ) - ) - ); - } - - /// @notice Retrieves the last recorded weight for a given operator. - /// @param _operator The address of the operator. - /// @return uint256 - The latest weight of the operator. + return address(uint160(_operatorSigningKeyHistory[operator].getAtBlock(blockNumber))); + } + + /// @inheritdoc IECDSAStakeRegistry function getLastCheckpointOperatorWeight( - address _operator + address operator ) external view returns (uint256) { - return _operatorWeightHistory[_operator].latest(); + return _operatorWeightHistory[operator].latest(); } - /// @notice Retrieves the last recorded total weight across all operators. - /// @return uint256 - The latest total weight. + /// @inheritdoc IECDSAStakeRegistry function getLastCheckpointTotalWeight() external view returns (uint256) { return _totalWeightHistory.latest(); } - /// @notice Retrieves the last recorded threshold weight - /// @return uint256 - The latest threshold weight. - function getLastCheckpointThresholdWeight() - external - view - returns (uint256) - { + /// @inheritdoc IECDSAStakeRegistry + function getLastCheckpointThresholdWeight() external view returns (uint256) { return _thresholdWeightHistory.latest(); } - /// @notice Retrieves the operator's weight at a specific block number. - /// @param _operator The address of the operator. - /// @param _blockNumber The block number to get the operator weight for the quorum - /// @return uint256 - The weight of the operator at the given block. + /// @inheritdoc IECDSAStakeRegistry function getOperatorWeightAtBlock( - address _operator, - uint32 _blockNumber + address operator, + uint32 blockNumber ) external view returns (uint256) { - return _operatorWeightHistory[_operator].getAtBlock(_blockNumber); + return _operatorWeightHistory[operator].getAtBlock(blockNumber); } - /// @notice Retrieves the total weight at a specific block number. - /// @param _blockNumber The block number to get the total weight for the quorum - /// @return uint256 - The total weight at the given block. + /// @inheritdoc IECDSAStakeRegistry function getLastCheckpointTotalWeightAtBlock( - uint32 _blockNumber + uint32 blockNumber ) external view returns (uint256) { - return _totalWeightHistory.getAtBlock(_blockNumber); + return _totalWeightHistory.getAtBlock(blockNumber); } - /// @notice Retrieves the threshold weight at a specific block number. - /// @param _blockNumber The block number to get the threshold weight for the quorum - /// @return uint256 - The threshold weight the given block. + /// @inheritdoc IECDSAStakeRegistry function getLastCheckpointThresholdWeightAtBlock( - uint32 _blockNumber + uint32 blockNumber ) external view returns (uint256) { - return _thresholdWeightHistory.getAtBlock(_blockNumber); + return _thresholdWeightHistory.getAtBlock(blockNumber); } + /// @inheritdoc IECDSAStakeRegistry function operatorRegistered( - address _operator + address operator ) external view returns (bool) { - return _operatorRegistered[_operator]; + return _operatorRegistered[operator]; } - /// @notice Returns the weight an operator must have to contribute to validating an AVS + /// @inheritdoc IECDSAStakeRegistry function minimumWeight() external view returns (uint256) { return _minimumWeight; } - /// @notice Calculates the current weight of an operator based on their delegated stake in the strategies considered in the quorum - /// @param _operator The address of the operator. - /// @return uint256 - The current weight of the operator; returns 0 if below the threshold. + /// @inheritdoc IECDSAStakeRegistry function getOperatorWeight( - address _operator + address operator ) public view returns (uint256) { StrategyParams[] memory strategyParams = _quorum.strategies; uint256 weight; @@ -248,10 +210,7 @@ contract ECDSAStakeRegistry is for (uint256 i; i < strategyParams.length; i++) { strategies[i] = strategyParams[i].strategy; } - uint256[] memory shares = DELEGATION_MANAGER.getOperatorShares( - _operator, - strategies - ); + uint256[] memory shares = DELEGATION_MANAGER.getOperatorShares(operator, strategies); for (uint256 i; i < strategyParams.length; i++) { weight += shares[i] * strategyParams[i].multiplier; } @@ -264,22 +223,7 @@ contract ECDSAStakeRegistry is } } - /// @notice Initializes state for the StakeRegistry - /// @param _serviceManagerAddr The AVS' ServiceManager contract's address - function __ECDSAStakeRegistry_init( - address _serviceManagerAddr, - uint256 _thresholdWeight, - Quorum memory _quorum - ) internal onlyInitializing { - _serviceManager = _serviceManagerAddr; - _updateStakeThreshold(_thresholdWeight); - _updateQuorumConfig(_quorum); - __Ownable_init(); - } - - /// @notice Updates the set of operators for the first quorum. - /// @param operatorsPerQuorum An array of operator address arrays, one for each quorum. - /// @dev This interface maintains compatibility with avs-sync which handles multiquorums while this registry has a single quorum + /// @inheritdoc IECDSAStakeRegistry function updateOperatorsForQuorum( address[][] memory operatorsPerQuorum, bytes memory @@ -289,139 +233,138 @@ contract ECDSAStakeRegistry is /// @dev Updates the list of operators if the provided list has the correct number of operators. /// Reverts if the provided list of operators does not match the expected total count of operators. - /// @param _operators The list of operator addresses to update. - function _updateAllOperators(address[] memory _operators) internal { - if (_operators.length != _totalOperators) { + /// @param operators The list of operator addresses to update. + function _updateAllOperators( + address[] memory operators + ) internal { + if (operators.length != _totalOperators) { revert MustUpdateAllOperators(); } - _updateOperators(_operators); + _updateOperators(operators); } /// @dev Updates the weights for a given list of operator addresses. /// When passing an operator that isn't registered, then 0 is added to their history - /// @param _operators An array of addresses for which to update the weights. - function _updateOperators(address[] memory _operators) internal { + /// @param operators An array of addresses for which to update the weights. + function _updateOperators( + address[] memory operators + ) internal { int256 delta; - for (uint256 i; i < _operators.length; i++) { - delta += _updateOperatorWeight(_operators[i]); + for (uint256 i; i < operators.length; i++) { + delta += _updateOperatorWeight(operators[i]); } _updateTotalWeight(delta); } /// @dev Updates the stake threshold weight and records the history. - /// @param _thresholdWeight The new threshold weight to set and record in the history. - function _updateStakeThreshold(uint256 _thresholdWeight) internal { - _thresholdWeightHistory.push(_thresholdWeight); - emit ThresholdWeightUpdated(_thresholdWeight); + /// @param thresholdWeight The new threshold weight to set and record in the history. + function _updateStakeThreshold( + uint256 thresholdWeight + ) internal { + _thresholdWeightHistory.push(thresholdWeight); + emit ThresholdWeightUpdated(thresholdWeight); } /// @dev Updates the weight an operator must have to join the operator set - /// @param _newMinimumWeight The new weight an operator must have to join the operator set - function _updateMinimumWeight(uint256 _newMinimumWeight) internal { + /// @param newMinimumWeight The new weight an operator must have to join the operator set + function _updateMinimumWeight( + uint256 newMinimumWeight + ) internal { uint256 oldMinimumWeight = _minimumWeight; - _minimumWeight = _newMinimumWeight; - emit MinimumWeightUpdated(oldMinimumWeight, _newMinimumWeight); + _minimumWeight = newMinimumWeight; + emit MinimumWeightUpdated(oldMinimumWeight, newMinimumWeight); } /// @notice Updates the quorum configuration - /// @dev Replaces the current quorum configuration with `_newQuorum` if valid. + /// @dev Replaces the current quorum configuration with `newQuorum` if valid. /// Reverts with `InvalidQuorum` if the new quorum configuration is not valid. /// Emits `QuorumUpdated` event with the old and new quorum configurations. - /// @param _newQuorum The new quorum configuration to set. - function _updateQuorumConfig(Quorum memory _newQuorum) internal { - if (!_isValidQuorum(_newQuorum)) { + /// @param newQuorum The new quorum configuration to set. + function _updateQuorumConfig( + IECDSAStakeRegistryTypes.Quorum memory newQuorum + ) internal { + if (!_isValidQuorum(newQuorum)) { revert InvalidQuorum(); } - Quorum memory oldQuorum = _quorum; + IECDSAStakeRegistryTypes.Quorum memory oldQuorum = _quorum; delete _quorum; - for (uint256 i; i < _newQuorum.strategies.length; i++) { - _quorum.strategies.push(_newQuorum.strategies[i]); + for (uint256 i; i < newQuorum.strategies.length; i++) { + _quorum.strategies.push(newQuorum.strategies[i]); } - emit QuorumUpdated(oldQuorum, _newQuorum); + emit QuorumUpdated(oldQuorum, newQuorum); } /// @dev Internal function to deregister an operator - /// @param _operator The operator's address to deregister - function _deregisterOperator(address _operator) internal { - if (!_operatorRegistered[_operator]) { + /// @param operator The operator's address to deregister + function _deregisterOperator( + address operator + ) internal { + if (!_operatorRegistered[operator]) { revert OperatorNotRegistered(); } _totalOperators--; - delete _operatorRegistered[_operator]; - int256 delta = _updateOperatorWeight(_operator); + delete _operatorRegistered[operator]; + int256 delta = _updateOperatorWeight(operator); _updateTotalWeight(delta); - IServiceManager(_serviceManager).deregisterOperatorFromAVS(_operator); - emit OperatorDeregistered(_operator, address(_serviceManager)); + IServiceManager(_serviceManager).deregisterOperatorFromAVS(operator); + emit OperatorDeregistered(operator, address(_serviceManager)); } /// @dev registers an operator through a provided signature - /// @param _operatorSignature Contains the operator's signature, salt, and expiry - /// @param _signingKey The signing key to add to the operator's history + /// @param operatorSignature Contains the operator's signature, salt, and expiry + /// @param signingKey The signing key to add to the operator's history function _registerOperatorWithSig( - address _operator, - ISignatureUtils.SignatureWithSaltAndExpiry memory _operatorSignature, - address _signingKey + address operator, + ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature, + address signingKey ) internal virtual { - if (_operatorRegistered[_operator]) { + if (_operatorRegistered[operator]) { revert OperatorAlreadyRegistered(); } _totalOperators++; - _operatorRegistered[_operator] = true; - int256 delta = _updateOperatorWeight(_operator); + _operatorRegistered[operator] = true; + int256 delta = _updateOperatorWeight(operator); _updateTotalWeight(delta); - _updateOperatorSigningKey(_operator, _signingKey); - IServiceManager(_serviceManager).registerOperatorToAVS( - _operator, - _operatorSignature - ); - emit OperatorRegistered(_operator, _serviceManager); + _updateOperatorSigningKey(operator, signingKey); + IServiceManager(_serviceManager).registerOperatorToAVS(operator, operatorSignature); + emit OperatorRegistered(operator, _serviceManager); } /// @dev Internal function to update an operator's signing key - /// @param _operator The address of the operator to update the signing key for - /// @param _newSigningKey The new signing key to set for the operator - function _updateOperatorSigningKey( - address _operator, - address _newSigningKey - ) internal { - address oldSigningKey = address( - uint160(_operatorSigningKeyHistory[_operator].latest()) - ); - if (_newSigningKey == oldSigningKey) { + /// @param operator The address of the operator to update the signing key for + /// @param newSigningKey The new signing key to set for the operator + function _updateOperatorSigningKey(address operator, address newSigningKey) internal { + address oldSigningKey = address(uint160(_operatorSigningKeyHistory[operator].latest())); + if (newSigningKey == oldSigningKey) { return; } - _operatorSigningKeyHistory[_operator].push(uint160(_newSigningKey)); - emit SigningKeyUpdate( - _operator, - block.number, - _newSigningKey, - oldSigningKey - ); + _operatorSigningKeyHistory[operator].push(uint160(newSigningKey)); + emit SigningKeyUpdate(operator, block.number, newSigningKey, oldSigningKey); } /// @notice Updates the weight of an operator and returns the previous and current weights. - /// @param _operator The address of the operator to update the weight of. + /// @param operator The address of the operator to update the weight of. function _updateOperatorWeight( - address _operator + address operator ) internal virtual returns (int256) { int256 delta; uint256 newWeight; - uint256 oldWeight = _operatorWeightHistory[_operator].latest(); - if (!_operatorRegistered[_operator]) { + uint256 oldWeight = _operatorWeightHistory[operator].latest(); + if (!_operatorRegistered[operator]) { delta -= int256(oldWeight); if (delta == 0) { return delta; } - _operatorWeightHistory[_operator].push(0); + _operatorWeightHistory[operator].push(0); } else { - newWeight = getOperatorWeight(_operator); + newWeight = getOperatorWeight(operator); delta = int256(newWeight) - int256(oldWeight); if (delta == 0) { return delta; } - _operatorWeightHistory[_operator].push(newWeight); + _operatorWeightHistory[operator].push(newWeight); } - emit OperatorWeightUpdated(_operator, oldWeight, newWeight); + emit OperatorWeightUpdated(operator, oldWeight, newWeight); return delta; } @@ -443,13 +386,13 @@ contract ECDSAStakeRegistry is * @dev Verifies that a specified quorum configuration is valid. A valid quorum has: * 1. Weights that sum to exactly 10,000 basis points, ensuring proportional representation. * 2. Unique strategies without duplicates to maintain quorum integrity. - * @param _quorum The quorum configuration to be validated. + * @param quorum The quorum configuration to be validated. * @return bool True if the quorum configuration is valid, otherwise false. */ function _isValidQuorum( - Quorum memory _quorum + IECDSAStakeRegistryTypes.Quorum memory quorum ) internal pure returns (bool) { - StrategyParams[] memory strategies = _quorum.strategies; + StrategyParams[] memory strategies = quorum.strategies; address lastStrategy; address currentStrategy; uint256 totalMultiplier; @@ -468,157 +411,141 @@ contract ECDSAStakeRegistry is /** * @notice Common logic to verify a batch of ECDSA signatures against a hash, using either last stake weight or at a specific block. - * @param _dataHash The hash of the data the signers endorsed. - * @param _operators A collection of addresses that endorsed the data hash. - * @param _signatures A collection of signatures matching the signers. - * @param _referenceBlock The block number for evaluating stake weight; use max uint32 for latest weight. + * @param digest The hash of the data the signers endorsed. + * @param operators A collection of addresses that endorsed the data hash. + * @param signatures A collection of signatures matching the signers. + * @param referenceBlock The block number for evaluating stake weight; use max uint32 for latest weight. */ function _checkSignatures( - bytes32 _dataHash, - address[] memory _operators, - bytes[] memory _signatures, - uint32 _referenceBlock + bytes32 digest, + address[] memory operators, + bytes[] memory signatures, + uint32 referenceBlock ) internal view { - uint256 signersLength = _operators.length; + uint256 signersLength = operators.length; address currentOperator; address lastOperator; address signer; uint256 signedWeight; - _validateSignaturesLength(signersLength, _signatures.length); + _validateSignaturesLength(signersLength, signatures.length); for (uint256 i; i < signersLength; i++) { - currentOperator = _operators[i]; - signer = _getOperatorSigningKey(currentOperator, _referenceBlock); + currentOperator = operators[i]; + signer = _getOperatorSigningKey(currentOperator, referenceBlock); _validateSortedSigners(lastOperator, currentOperator); - _validateSignature(signer, _dataHash, _signatures[i]); + _validateSignature(signer, digest, signatures[i]); lastOperator = currentOperator; - uint256 operatorWeight = _getOperatorWeight( - currentOperator, - _referenceBlock - ); + uint256 operatorWeight = _getOperatorWeight(currentOperator, referenceBlock); signedWeight += operatorWeight; } - _validateThresholdStake(signedWeight, _referenceBlock); + _validateThresholdStake(signedWeight, referenceBlock); } /// @notice Validates that the number of signers equals the number of signatures, and neither is zero. - /// @param _signersLength The number of signers. - /// @param _signaturesLength The number of signatures. + /// @param signersLength The number of signers. + /// @param signaturesLength The number of signatures. function _validateSignaturesLength( - uint256 _signersLength, - uint256 _signaturesLength + uint256 signersLength, + uint256 signaturesLength ) internal pure { - if (_signersLength != _signaturesLength) { + if (signersLength != signaturesLength) { revert LengthMismatch(); } - if (_signersLength == 0) { + if (signersLength == 0) { revert InvalidLength(); } } /// @notice Ensures that signers are sorted in ascending order by address. - /// @param _lastSigner The address of the last signer. - /// @param _currentSigner The address of the current signer. - function _validateSortedSigners( - address _lastSigner, - address _currentSigner - ) internal pure { - if (_lastSigner >= _currentSigner) { + /// @param lastSigner The address of the last signer. + /// @param currentSigner The address of the current signer. + function _validateSortedSigners(address lastSigner, address currentSigner) internal pure { + if (lastSigner >= currentSigner) { revert NotSorted(); } } /// @notice Validates a given signature against the signer's address and data hash. - /// @param _signer The address of the signer to validate. - /// @param _dataHash The hash of the data that is signed. - /// @param _signature The signature to validate. + /// @param signer The address of the signer to validate. + /// @param digest The hash of the data that is signed. + /// @param signature The signature to validate. function _validateSignature( - address _signer, - bytes32 _dataHash, - bytes memory _signature + address signer, + bytes32 digest, + bytes memory signature ) internal view { - if (!_signer.isValidSignatureNow(_dataHash, _signature)) { + if (!signer.isValidSignatureNow(digest, signature)) { revert InvalidSignature(); } } /// @notice Retrieves the operator weight for a signer, either at the last checkpoint or a specified block. - /// @param _operator The operator to query their signing key history for - /// @param _referenceBlock The block number to query the operator's weight at, or the maximum uint32 value for the last checkpoint. + /// @param operator The operator to query their signing key history for + /// @param referenceBlock The block number to query the operator's weight at, or the maximum uint32 value for the last checkpoint. /// @return The weight of the operator. function _getOperatorSigningKey( - address _operator, - uint32 _referenceBlock + address operator, + uint32 referenceBlock ) internal view returns (address) { - if (_referenceBlock >= block.number) { + if (referenceBlock >= block.number) { revert InvalidReferenceBlock(); } - return - address( - uint160( - _operatorSigningKeyHistory[_operator].getAtBlock( - _referenceBlock - ) - ) - ); + return address(uint160(_operatorSigningKeyHistory[operator].getAtBlock(referenceBlock))); } /// @notice Retrieves the operator weight for a signer, either at the last checkpoint or a specified block. - /// @param _signer The address of the signer whose weight is returned. - /// @param _referenceBlock The block number to query the operator's weight at, or the maximum uint32 value for the last checkpoint. + /// @param signer The address of the signer whose weight is returned. + /// @param referenceBlock The block number to query the operator's weight at, or the maximum uint32 value for the last checkpoint. /// @return The weight of the operator. function _getOperatorWeight( - address _signer, - uint32 _referenceBlock + address signer, + uint32 referenceBlock ) internal view returns (uint256) { - if (_referenceBlock >= block.number) { + if (referenceBlock >= block.number) { revert InvalidReferenceBlock(); } - return _operatorWeightHistory[_signer].getAtBlock(_referenceBlock); + return _operatorWeightHistory[signer].getAtBlock(referenceBlock); } /// @notice Retrieve the total stake weight at a specific block or the latest if not specified. - /// @dev If the `_referenceBlock` is the maximum value for uint32, the latest total weight is returned. - /// @param _referenceBlock The block number to retrieve the total stake weight from. + /// @dev If the `referenceBlock` is the maximum value for uint32, the latest total weight is returned. + /// @param referenceBlock The block number to retrieve the total stake weight from. /// @return The total stake weight at the given block or the latest if the given block is the max uint32 value. function _getTotalWeight( - uint32 _referenceBlock + uint32 referenceBlock ) internal view returns (uint256) { - if (_referenceBlock >= block.number) { + if (referenceBlock >= block.number) { revert InvalidReferenceBlock(); } - return _totalWeightHistory.getAtBlock(_referenceBlock); + return _totalWeightHistory.getAtBlock(referenceBlock); } /// @notice Retrieves the threshold stake for a given reference block. - /// @param _referenceBlock The block number to query the threshold stake for. + /// @param referenceBlock The block number to query the threshold stake for. /// If set to the maximum uint32 value, it retrieves the latest threshold stake. /// @return The threshold stake in basis points for the reference block. function _getThresholdStake( - uint32 _referenceBlock + uint32 referenceBlock ) internal view returns (uint256) { - if (_referenceBlock >= block.number) { + if (referenceBlock >= block.number) { revert InvalidReferenceBlock(); } - return _thresholdWeightHistory.getAtBlock(_referenceBlock); + return _thresholdWeightHistory.getAtBlock(referenceBlock); } /// @notice Validates that the cumulative stake of signed messages meets or exceeds the required threshold. - /// @param _signedWeight The cumulative weight of the signers that have signed the message. - /// @param _referenceBlock The block number to verify the stake threshold for - function _validateThresholdStake( - uint256 _signedWeight, - uint32 _referenceBlock - ) internal view { - uint256 totalWeight = _getTotalWeight(_referenceBlock); - if (_signedWeight > totalWeight) { + /// @param signedWeight The cumulative weight of the signers that have signed the message. + /// @param referenceBlock The block number to verify the stake threshold for + function _validateThresholdStake(uint256 signedWeight, uint32 referenceBlock) internal view { + uint256 totalWeight = _getTotalWeight(referenceBlock); + if (signedWeight > totalWeight) { revert InvalidSignedWeight(); } - uint256 thresholdStake = _getThresholdStake(_referenceBlock); - if (thresholdStake > _signedWeight) { + uint256 thresholdStake = _getThresholdStake(referenceBlock); + if (thresholdStake > signedWeight) { revert InsufficientSignedStake(); } } diff --git a/src/unaudited/ECDSAStakeRegistryStorage.sol b/src/unaudited/ECDSAStakeRegistryStorage.sol index 0742157a..8d9d69d8 100644 --- a/src/unaudited/ECDSAStakeRegistryStorage.sol +++ b/src/unaudited/ECDSAStakeRegistryStorage.sol @@ -1,24 +1,26 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; -import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; -import {CheckpointsUpgradeable} from "@openzeppelin-upgrades/contracts/utils/CheckpointsUpgradeable.sol"; -import {ECDSAStakeRegistryEventsAndErrors, Quorum, StrategyParams} from "../interfaces/IECDSAStakeRegistryEventsAndErrors.sol"; +import {IDelegationManager} from + "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; +import {CheckpointsUpgradeable} from + "@openzeppelin-upgrades/contracts/utils/CheckpointsUpgradeable.sol"; +import { + IECDSAStakeRegistry, IECDSAStakeRegistryTypes +} from "../interfaces/IECDSAStakeRegistry.sol"; -abstract contract ECDSAStakeRegistryStorage is - ECDSAStakeRegistryEventsAndErrors -{ +abstract contract ECDSAStakeRegistryStorage is IECDSAStakeRegistry { /// @notice Manages staking delegations through the DelegationManager interface IDelegationManager internal immutable DELEGATION_MANAGER; /// @dev The total amount of multipliers to weigh stakes - uint256 internal constant BPS = 10_000; + uint256 internal constant BPS = 10000; /// @notice The size of the current operator set uint256 internal _totalOperators; /// @notice Stores the current quorum configuration - Quorum internal _quorum; + IECDSAStakeRegistryTypes.Quorum internal _quorum; /// @notice Specifies the weight required to become an operator uint256 internal _minimumWeight; @@ -30,8 +32,7 @@ abstract contract ECDSAStakeRegistryStorage is uint256 internal _stakeExpiry; /// @notice Maps an operator to their signing key history using checkpoints - mapping(address => CheckpointsUpgradeable.History) - internal _operatorSigningKeyHistory; + mapping(address => CheckpointsUpgradeable.History) internal _operatorSigningKeyHistory; /// @notice Tracks the total stake history over time using checkpoints CheckpointsUpgradeable.History internal _totalWeightHistory; @@ -40,14 +41,15 @@ abstract contract ECDSAStakeRegistryStorage is CheckpointsUpgradeable.History internal _thresholdWeightHistory; /// @notice Maps operator addresses to their respective stake histories using checkpoints - mapping(address => CheckpointsUpgradeable.History) - internal _operatorWeightHistory; + mapping(address => CheckpointsUpgradeable.History) internal _operatorWeightHistory; /// @notice Maps an operator to their registration status mapping(address => bool) internal _operatorRegistered; /// @param _delegationManager Connects this registry with the DelegationManager - constructor(IDelegationManager _delegationManager) { + constructor( + IDelegationManager _delegationManager + ) { DELEGATION_MANAGER = _delegationManager; } diff --git a/src/unaudited/examples/ECDSAStakeRegistryEqualWeight.sol b/src/unaudited/examples/ECDSAStakeRegistryEqualWeight.sol index 23b8be2a..3edd6278 100644 --- a/src/unaudited/examples/ECDSAStakeRegistryEqualWeight.sol +++ b/src/unaudited/examples/ECDSAStakeRegistryEqualWeight.sol @@ -1,10 +1,12 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol"; import {ECDSAStakeRegistryPermissioned} from "./ECDSAStakeRegistryPermissioned.sol"; -import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; -import {CheckpointsUpgradeable} from "@openzeppelin-upgrades/contracts/utils/CheckpointsUpgradeable.sol"; +import {IDelegationManager} from + "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; +import {CheckpointsUpgradeable} from + "@openzeppelin-upgrades/contracts/utils/CheckpointsUpgradeable.sol"; /// @title ECDSA Stake Registry with Equal Weight /// @dev THIS CONTRACT IS NOT AUDITED @@ -25,16 +27,18 @@ contract ECDSAStakeRegistryEqualWeight is ECDSAStakeRegistryPermissioned { /// @dev Overrides the _updateOperatorWeight function from the parent class to implement equal weighting. /// Emits an OperatorWeightUpdated event upon successful update. /// @param _operator The address of the operator whose weight is being updated. - function _updateOperatorWeight(address _operator) internal override returns (int256){ + function _updateOperatorWeight( + address _operator + ) internal override returns (int256) { uint256 oldWeight; uint256 newWeight; int256 delta; if (_operatorRegistered[_operator]) { - (oldWeight, ) = _operatorWeightHistory[_operator].push(1); - delta = int256(1) - int(oldWeight); // handles if they were already registered + (oldWeight,) = _operatorWeightHistory[_operator].push(1); + delta = int256(1) - int256(oldWeight); // handles if they were already registered } else { - (oldWeight, ) = _operatorWeightHistory[_operator].push(0); - delta = int256(0) - int(oldWeight); + (oldWeight,) = _operatorWeightHistory[_operator].push(0); + delta = int256(0) - int256(oldWeight); } emit OperatorWeightUpdated(_operator, oldWeight, newWeight); return delta; diff --git a/src/unaudited/examples/ECDSAStakeRegistryPermissioned.sol b/src/unaudited/examples/ECDSAStakeRegistryPermissioned.sol index ef2e691c..f6afffdf 100644 --- a/src/unaudited/examples/ECDSAStakeRegistryPermissioned.sol +++ b/src/unaudited/examples/ECDSAStakeRegistryPermissioned.sol @@ -1,9 +1,10 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol"; import {ECDSAStakeRegistry} from "../ECDSAStakeRegistry.sol"; -import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; +import {IDelegationManager} from + "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; /// @title ECDSA Stake Registry with an Operator Allowlist /// @dev THIS CONTRACT IS NOT AUDITED @@ -36,27 +37,35 @@ contract ECDSAStakeRegistryPermissioned is ECDSAStakeRegistry { /// @notice Adds an operator to the allowlisted operator set /// @dev An allowlisted operator isn't a part of the operator set. They must subsequently register themselves /// @param _operator The address of the operator to be allowlisted - function permitOperator(address _operator) external onlyOwner { + function permitOperator( + address _operator + ) external onlyOwner { _permitOperator(_operator); } /// @notice Revokes an operator's permission and deregisters them /// @dev Emits the OperatorRevoked event if the operator was previously allowlisted. /// @param _operator The address of the operator to remove from the allowlist and deregistered. - function revokeOperator(address _operator) external onlyOwner { + function revokeOperator( + address _operator + ) external onlyOwner { _revokeOperator(_operator); } /// @notice Directly deregisters an operator without removing from the allowlist /// @dev Does not emit an event because it does not modify the allowlist. /// @param _operator The address of the operator to deregister - function ejectOperator(address _operator) external onlyOwner { + function ejectOperator( + address _operator + ) external onlyOwner { _ejectOperator(_operator); } /// @dev Deregisters and operator from the active operator set /// @param _operator The address of the operator to remove. - function _ejectOperator(address _operator) internal { + function _ejectOperator( + address _operator + ) internal { _deregisterOperator(_operator); emit OperatorEjected(_operator); } @@ -64,7 +73,9 @@ contract ECDSAStakeRegistryPermissioned is ECDSAStakeRegistry { /// @dev Adds an operator to the allowlisted operator set /// Doesn't register the operator into the operator set /// @param _operator The address of the operator to allowlist. - function _permitOperator(address _operator) internal { + function _permitOperator( + address _operator + ) internal { if (allowlistedOperators[_operator]) { revert OperatorAlreadyAllowlisted(); } @@ -75,7 +86,9 @@ contract ECDSAStakeRegistryPermissioned is ECDSAStakeRegistry { /// @dev Removes an operator from the allowlist. /// If the operator is registered, also deregisters the operator. /// @param _operator The address of the operator to be revoked. - function _revokeOperator(address _operator) internal { + function _revokeOperator( + address _operator + ) internal { if (!allowlistedOperators[_operator]) { revert OperatorNotAllowlisted(); } @@ -95,10 +108,6 @@ contract ECDSAStakeRegistryPermissioned is ECDSAStakeRegistry { if (allowlistedOperators[_operator] != true) { revert OperatorNotAllowlisted(); } - super._registerOperatorWithSig( - _operator, - _operatorSignature, - _operatorSigningKey - ); + super._registerOperatorWithSig(_operator, _operatorSignature, _operatorSigningKey); } } diff --git a/test/events/IBLSApkRegistryEvents.sol b/test/events/IBLSApkRegistryEvents.sol index 1ed588de..4b5e299f 100644 --- a/test/events/IBLSApkRegistryEvents.sol +++ b/test/events/IBLSApkRegistryEvents.sol @@ -1,24 +1,18 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {BN254} from "../../src/libraries/BN254.sol"; interface IBLSApkRegistryEvents { // EVENTS /// @notice Emitted when `operator` registers with the public keys `pubkeyG1` and `pubkeyG2`. - event NewPubkeyRegistration(address indexed operator, BN254.G1Point pubkeyG1, BN254.G2Point 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, - bytes32 operatorId, - bytes quorumNumbers - ); + event OperatorAddedToQuorums(address operator, bytes32 operatorId, bytes quorumNumbers); // @notice Emitted when an operator pubkey is removed from a set of quorums - event OperatorRemovedFromQuorums( - address operator, - bytes32 operatorId, - bytes quorumNumbers - ); + event OperatorRemovedFromQuorums(address operator, bytes32 operatorId, bytes quorumNumbers); } diff --git a/test/events/IIndexRegistryEvents.sol b/test/events/IIndexRegistryEvents.sol index 79494de0..6e13714b 100644 --- a/test/events/IIndexRegistryEvents.sol +++ b/test/events/IIndexRegistryEvents.sol @@ -1,7 +1,9 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; interface IIndexRegistryEvents { // emitted when an operator's index in the ordered operator list for the quorum with number `quorumNumber` is updated - event QuorumIndexUpdate(bytes32 indexed operatorId, uint8 quorumNumber, uint32 newOperatorIndex); + event QuorumIndexUpdate( + bytes32 indexed operatorId, uint8 quorumNumber, uint32 newOperatorIndex + ); } diff --git a/test/events/IServiceManagerBaseEvents.sol b/test/events/IServiceManagerBaseEvents.sol index 4ae9b92e..45b12cf5 100644 --- a/test/events/IServiceManagerBaseEvents.sol +++ b/test/events/IServiceManagerBaseEvents.sol @@ -1,7 +1,10 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; -import {IRewardsCoordinator, IERC20} from "eigenlayer-contracts/src/contracts/interfaces/IRewardsCoordinator.sol"; +import { + IRewardsCoordinator, + IERC20 +} from "eigenlayer-contracts/src/contracts/interfaces/IRewardsCoordinator.sol"; interface IServiceManagerBaseEvents { /// RewardsCoordinator EVENTS /// @@ -21,27 +24,14 @@ interface IServiceManagerBaseEvents { IRewardsCoordinator.RewardsSubmission rewardsSubmission ); /// @notice rewardsUpdater is responsible for submiting DistributionRoots, only owner can set rewardsUpdater - event RewardsUpdaterSet( - address indexed oldRewardsUpdater, - address indexed newRewardsUpdater - ); + event RewardsUpdaterSet(address indexed oldRewardsUpdater, address indexed newRewardsUpdater); event RewardsForAllSubmitterSet( - address indexed rewardsForAllSubmitter, - bool indexed oldValue, - bool indexed newValue - ); - event ActivationDelaySet( - uint32 oldActivationDelay, - uint32 newActivationDelay - ); - event DefaultOperatorSplitBipsSet( - uint16 oldDefaultOperatorSplitBips, - uint16 newDefaultOperatorSplitBips + address indexed rewardsForAllSubmitter, bool indexed oldValue, bool indexed newValue ); + event ActivationDelaySet(uint32 oldActivationDelay, uint32 newActivationDelay); + event GlobalCommissionBipsSet(uint16 oldGlobalCommissionBips, uint16 newGlobalCommissionBips); event ClaimerForSet( - address indexed earner, - address indexed oldClaimer, - address indexed claimer + address indexed earner, address indexed oldClaimer, address indexed claimer ); /// @notice rootIndex is the specific array index of the newly created root in the storage array event DistributionRootSubmitted( @@ -59,53 +49,6 @@ interface IServiceManagerBaseEvents { IERC20 token, uint256 claimedAmount ); - /** - * @notice Emitted when an AVS creates a valid `OperatorDirectedRewardsSubmission` - * @param caller The address calling `createOperatorDirectedAVSRewardsSubmission`. - * @param avs The avs on behalf of which the operator-directed rewards are being submitted. - * @param operatorDirectedRewardsSubmissionHash Keccak256 hash of (`avs`, `submissionNonce` and `operatorDirectedRewardsSubmission`). - * @param submissionNonce Current nonce of the avs. Used to generate a unique submission hash. - * @param operatorDirectedRewardsSubmission The Operator-Directed Rewards Submission. Contains the token, start timestamp, duration, operator rewards, description and, strategy and multipliers. - */ - event OperatorDirectedAVSRewardsSubmissionCreated( - address indexed caller, - address indexed avs, - bytes32 indexed operatorDirectedRewardsSubmissionHash, - uint256 submissionNonce, - IRewardsCoordinator.OperatorDirectedRewardsSubmission operatorDirectedRewardsSubmission - ); - /** - * @notice Emitted when the operator split for an AVS is set. - * @param caller The address calling `setOperatorAVSSplit`. - * @param operator The operator on behalf of which the split is being set. - * @param avs The avs for which the split is being set by the operator. - * @param activatedAt The timestamp at which the split will be activated. - * @param oldOperatorAVSSplitBips The old split for the operator for the AVS. - * @param newOperatorAVSSplitBips The new split for the operator for the AVS. - */ - event OperatorAVSSplitBipsSet( - address indexed caller, - address indexed operator, - address indexed avs, - uint32 activatedAt, - uint16 oldOperatorAVSSplitBips, - uint16 newOperatorAVSSplitBips - ); - /** - * @notice Emitted when the operator split for Programmatic Incentives is set. - * @param caller The address calling `setOperatorPISplit`. - * @param operator The operator on behalf of which the split is being set. - * @param activatedAt The timestamp at which the split will be activated. - * @param oldOperatorPISplitBips The old split for the operator for Programmatic Incentives. - * @param newOperatorPISplitBips The new split for the operator for Programmatic Incentives. - */ - event OperatorPISplitBipsSet( - address indexed caller, - address indexed operator, - uint32 activatedAt, - uint16 oldOperatorPISplitBips, - uint16 newOperatorPISplitBips - ); /// TOKEN EVENTS FOR TESTING /// /** @@ -120,9 +63,5 @@ interface IServiceManagerBaseEvents { * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ - event Approval( - address indexed owner, - address indexed spender, - uint256 value - ); + event Approval(address indexed owner, address indexed spender, uint256 value); } diff --git a/test/events/IStakeRegistryEvents.sol b/test/events/IStakeRegistryEvents.sol index 3e6f7e24..81426dfa 100644 --- a/test/events/IStakeRegistryEvents.sol +++ b/test/events/IStakeRegistryEvents.sol @@ -1,15 +1,11 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {IStakeRegistry, IStrategy} from "src/interfaces/IStakeRegistry.sol"; interface IStakeRegistryEvents { /// @notice emitted whenever the stake of `operator` is updated - event OperatorStakeUpdate( - bytes32 indexed operatorId, - uint8 quorumNumber, - uint96 stake - ); + event OperatorStakeUpdate(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 @@ -19,5 +15,7 @@ interface IStakeRegistryEvents { /// @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 `strategyParams[quorumNumber]` - event StrategyMultiplierUpdated(uint8 indexed quorumNumber, IStrategy strategy, uint256 multiplier); + event StrategyMultiplierUpdated( + uint8 indexed quorumNumber, IStrategy strategy, uint256 multiplier + ); } diff --git a/test/ffi/BLSPubKeyCompendiumFFI.t.sol b/test/ffi/BLSPubKeyCompendiumFFI.t.sol index 18d49a17..44283d9c 100644 --- a/test/ffi/BLSPubKeyCompendiumFFI.t.sol +++ b/test/ffi/BLSPubKeyCompendiumFFI.t.sol @@ -1,9 +1,10 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "../../src/BLSApkRegistry.sol"; import "../ffi/util/G2Operations.sol"; -import {IBLSApkRegistry} from "../../src/interfaces/IBLSApkRegistry.sol"; +import {IBLSApkRegistry, IBLSApkRegistryTypes} from "../../src/interfaces/IBLSApkRegistry.sol"; +import {ISlashingRegistryCoordinator} from "../../src/interfaces/ISlashingRegistryCoordinator.sol"; contract BLSApkRegistryFFITests is G2Operations { using BN254 for BN254.G1Point; @@ -12,10 +13,10 @@ contract BLSApkRegistryFFITests is G2Operations { Vm cheats = Vm(VM_ADDRESS); BLSApkRegistry blsApkRegistry; - IRegistryCoordinator registryCoordinator; + ISlashingRegistryCoordinator registryCoordinator; uint256 privKey; - IBLSApkRegistry.PubkeyRegistrationParams pubkeyRegistrationParams; + IBLSApkRegistryTypes.PubkeyRegistrationParams pubkeyRegistrationParams; address alice = address(0x69); @@ -23,28 +24,46 @@ contract BLSApkRegistryFFITests is G2Operations { blsApkRegistry = new BLSApkRegistry(registryCoordinator); } - function testRegisterBLSPublicKey(uint256 _privKey) public { + function testRegisterBLSPublicKey( + uint256 _privKey + ) public { cheats.assume(_privKey != 0); _setKeys(_privKey); pubkeyRegistrationParams.pubkeyRegistrationSignature = _signMessage(alice); vm.prank(address(registryCoordinator)); - blsApkRegistry.registerBLSPublicKey(alice, pubkeyRegistrationParams, registryCoordinator.pubkeyRegistrationMessageHash(alice)); + blsApkRegistry.registerBLSPublicKey( + alice, + pubkeyRegistrationParams, + registryCoordinator.pubkeyRegistrationMessageHash(alice) + ); - 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"); + 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 { + function _setKeys( + uint256 _privKey + ) internal { privKey = _privKey; pubkeyRegistrationParams.pubkeyG1 = BN254.generatorG1().scalar_mul(_privKey); pubkeyRegistrationParams.pubkeyG2 = G2Operations.mul(_privKey); } - function _signMessage(address signer) internal view returns(BN254.G1Point memory) { + function _signMessage( + address signer + ) internal view returns (BN254.G1Point memory) { BN254.G1Point memory messageHash = registryCoordinator.pubkeyRegistrationMessageHash(signer); return BN254.scalar_mul(messageHash, privKey); } diff --git a/test/ffi/BLSSignatureCheckerFFI.t.sol b/test/ffi/BLSSignatureCheckerFFI.t.sol index 16f72b4e..55e6c0c4 100644 --- a/test/ffi/BLSSignatureCheckerFFI.t.sol +++ b/test/ffi/BLSSignatureCheckerFFI.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {G2Operations} from "../ffi/util/G2Operations.sol"; import {MockAVSDeployer} from "../utils/MockAVSDeployer.sol"; @@ -8,9 +8,7 @@ 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 { - using BN254 for BN254.G1Point; bytes32 msgHash = keccak256(abi.encodePacked("hello world")); @@ -21,7 +19,7 @@ contract BLSSignatureCheckerFFITests is MockAVSDeployer, G2Operations { BLSSignatureChecker blsSignatureChecker; - function setUp() virtual public { + function setUp() public virtual { _deployMockEigenLayerAndAVS(); blsSignatureChecker = new BLSSignatureChecker(registryCoordinator); @@ -30,24 +28,27 @@ contract BLSSignatureCheckerFFITests is MockAVSDeployer, G2Operations { // this test checks that a valid signature from maxOperatorsToRegister with a random number of nonsigners is checked // correctly on the BLSSignatureChecker contract when all operators are only regsitered for a single quorum and // the signature is only checked for stakes on that quorum - function testBLSSignatureChecker_SingleQuorum_Valid(uint256 pseudoRandomNumber) public { + function testBLSSignatureChecker_SingleQuorum_Valid( + uint256 pseudoRandomNumber + ) public { uint256 numNonSigners = pseudoRandomNumber % (maxOperatorsToRegister - 1); uint256 quorumBitmap = 1; bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); - (uint32 referenceBlockNumber, BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature) = - _registerSignatoriesAndGetNonSignerStakeAndSignatureRandom(pseudoRandomNumber, numNonSigners, quorumBitmap); + ( + uint32 referenceBlockNumber, + BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature + ) = _registerSignatoriesAndGetNonSignerStakeAndSignatureRandom( + pseudoRandomNumber, numNonSigners, quorumBitmap + ); uint256 gasBefore = gasleft(); ( BLSSignatureChecker.QuorumStakeTotals memory quorumStakeTotals, /* bytes32 signatoryRecordHash */ ) = blsSignatureChecker.checkSignatures( - msgHash, - quorumNumbers, - referenceBlockNumber, - nonSignerStakesAndSignature + msgHash, quorumNumbers, referenceBlockNumber, nonSignerStakesAndSignature ); uint256 gasAfter = gasleft(); emit log_named_uint("gasUsed", gasBefore - gasAfter); @@ -61,32 +62,37 @@ contract BLSSignatureCheckerFFITests is MockAVSDeployer, G2Operations { // this test checks that a valid signature from maxOperatorsToRegister with a random number of nonsigners is checked // correctly on the BLSSignatureChecker contract when all operators are registered for the first 100 quorums // and the signature is only checked for stakes on those quorums - function testBLSSignatureChecker_100Quorums_Valid(uint256 pseudoRandomNumber) public { + function testBLSSignatureChecker_100Quorums_Valid( + uint256 pseudoRandomNumber + ) public { uint256 numNonSigners = pseudoRandomNumber % (maxOperatorsToRegister - 1); // 100 set bits uint256 quorumBitmap = (1 << 100) - 1; bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); - (uint32 referenceBlockNumber, BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature) = - _registerSignatoriesAndGetNonSignerStakeAndSignatureRandom(pseudoRandomNumber, numNonSigners, quorumBitmap); + ( + uint32 referenceBlockNumber, + BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature + ) = _registerSignatoriesAndGetNonSignerStakeAndSignatureRandom( + pseudoRandomNumber, numNonSigners, quorumBitmap + ); nonSignerStakesAndSignature.sigma = sigma.scalar_mul(quorumNumbers.length); nonSignerStakesAndSignature.apkG2 = oneHundredQuorumApkG2; uint256 gasBefore = gasleft(); blsSignatureChecker.checkSignatures( - msgHash, - quorumNumbers, - referenceBlockNumber, - nonSignerStakesAndSignature + msgHash, quorumNumbers, referenceBlockNumber, nonSignerStakesAndSignature ); uint256 gasAfter = gasleft(); emit log_named_uint("gasUsed", gasBefore - gasAfter); } - function _setAggregatePublicKeysAndSignature(uint256 pseudoRandomNumber) internal { - if(pseudoRandomNumber > type(uint256).max / 100) { + function _setAggregatePublicKeysAndSignature( + uint256 pseudoRandomNumber + ) internal { + if (pseudoRandomNumber > type(uint256).max / 100) { pseudoRandomNumber = type(uint256).max / 100; } aggSignerPrivKey = pseudoRandomNumber; @@ -95,29 +101,45 @@ contract BLSSignatureCheckerFFITests is MockAVSDeployer, G2Operations { sigma = BN254.hashToG1(msgHash).scalar_mul(aggSignerPrivKey); } - function _generateSignerAndNonSignerPrivateKeys(uint256 pseudoRandomNumber, uint256 numSigners, uint256 numNonSigners) internal returns (uint256[] memory, uint256[] memory) { + function _generateSignerAndNonSignerPrivateKeys( + uint256 pseudoRandomNumber, + uint256 numSigners, + uint256 numNonSigners + ) internal returns (uint256[] memory, uint256[] memory) { _setAggregatePublicKeysAndSignature(pseudoRandomNumber); uint256[] memory signerPrivateKeys = new uint256[](numSigners); // generate numSigners numbers that add up to aggSignerPrivKey mod BN254.FR_MODULUS uint256 sum = 0; - for (uint i = 0; i < numSigners - 1; i++) { - signerPrivateKeys[i] = uint256(keccak256(abi.encodePacked("signerPrivateKey", pseudoRandomNumber, i))) % BN254.FR_MODULUS; + for (uint256 i = 0; i < numSigners - 1; i++) { + signerPrivateKeys[i] = uint256( + keccak256(abi.encodePacked("signerPrivateKey", pseudoRandomNumber, i)) + ) % BN254.FR_MODULUS; sum = addmod(sum, signerPrivateKeys[i], BN254.FR_MODULUS); } // signer private keys need to add to aggSignerPrivKey - signerPrivateKeys[numSigners - 1] = addmod(aggSignerPrivKey, BN254.FR_MODULUS - sum % BN254.FR_MODULUS, BN254.FR_MODULUS); + signerPrivateKeys[numSigners - 1] = + addmod(aggSignerPrivKey, BN254.FR_MODULUS - sum % BN254.FR_MODULUS, BN254.FR_MODULUS); uint256[] memory nonSignerPrivateKeys = new uint256[](numNonSigners); - for (uint i = 0; i < numNonSigners; i++) { - nonSignerPrivateKeys[i] = uint256(keccak256(abi.encodePacked("nonSignerPrivateKey", pseudoRandomNumber, i))) % BN254.FR_MODULUS; + for (uint256 i = 0; i < numNonSigners; i++) { + nonSignerPrivateKeys[i] = uint256( + keccak256(abi.encodePacked("nonSignerPrivateKey", pseudoRandomNumber, i)) + ) % BN254.FR_MODULUS; } return (signerPrivateKeys, nonSignerPrivateKeys); } - function _registerSignatoriesAndGetNonSignerStakeAndSignatureRandom(uint256 pseudoRandomNumber, uint256 numNonSigners, uint256 quorumBitmap) internal returns(uint32, BLSSignatureChecker.NonSignerStakesAndSignature memory) { - (uint256[] memory signerPrivateKeys, uint256[] memory nonSignerPrivateKeys) = _generateSignerAndNonSignerPrivateKeys(pseudoRandomNumber, maxOperatorsToRegister - numNonSigners, numNonSigners); + function _registerSignatoriesAndGetNonSignerStakeAndSignatureRandom( + uint256 pseudoRandomNumber, + uint256 numNonSigners, + uint256 quorumBitmap + ) internal returns (uint32, BLSSignatureChecker.NonSignerStakesAndSignature memory) { + (uint256[] memory signerPrivateKeys, uint256[] memory nonSignerPrivateKeys) = + _generateSignerAndNonSignerPrivateKeys( + pseudoRandomNumber, maxOperatorsToRegister - numNonSigners, numNonSigners + ); bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); // randomly combine signer and non-signer private keys @@ -132,15 +154,17 @@ contract BLSSignatureCheckerFFITests is MockAVSDeployer, G2Operations { { uint256 signerIndex = 0; uint256 nonSignerIndex = 0; - for (uint i = 0; i < maxOperatorsToRegister; i++) { + for (uint256 i = 0; i < maxOperatorsToRegister; i++) { uint256 randomSeed = uint256(keccak256(abi.encodePacked("privKeyCombination", i))); if (randomSeed % 2 == 0 && signerIndex < signerPrivateKeys.length) { privateKeys[i] = signerPrivateKeys[signerIndex]; signerIndex++; } else if (nonSignerIndex < nonSignerPrivateKeys.length) { privateKeys[i] = nonSignerPrivateKeys[nonSignerIndex]; - nonSignerStakesAndSignature.nonSignerPubkeys[nonSignerIndex] = BN254.generatorG1().scalar_mul(privateKeys[i]); - nonSignerOperatorIds[nonSignerIndex] = nonSignerStakesAndSignature.nonSignerPubkeys[nonSignerIndex].hashG1Point(); + nonSignerStakesAndSignature.nonSignerPubkeys[nonSignerIndex] = + BN254.generatorG1().scalar_mul(privateKeys[i]); + nonSignerOperatorIds[nonSignerIndex] = + nonSignerStakesAndSignature.nonSignerPubkeys[nonSignerIndex].hashG1Point(); nonSignerIndex++; } else { privateKeys[i] = signerPrivateKeys[signerIndex]; @@ -151,36 +175,37 @@ contract BLSSignatureCheckerFFITests is MockAVSDeployer, G2Operations { pubkeys[i] = BN254.generatorG1().scalar_mul(privateKeys[i]); // add the public key to each quorum - for (uint j = 0; j < nonSignerStakesAndSignature.quorumApks.length; j++) { - nonSignerStakesAndSignature.quorumApks[j] = nonSignerStakesAndSignature.quorumApks[j].plus(pubkeys[i]); + for (uint256 j = 0; j < nonSignerStakesAndSignature.quorumApks.length; j++) { + nonSignerStakesAndSignature.quorumApks[j] = + nonSignerStakesAndSignature.quorumApks[j].plus(pubkeys[i]); } } } // register all operators for the first quorum - for (uint i = 0; i < maxOperatorsToRegister; i++) { + for (uint256 i = 0; i < maxOperatorsToRegister; i++) { cheats.roll(registrationBlockNumber + blocksBetweenRegistrations * i); _registerOperatorWithCoordinator(operators[i], quorumBitmap, pubkeys[i], defaultStake); } - uint32 referenceBlockNumber = registrationBlockNumber + blocksBetweenRegistrations * uint32(maxOperatorsToRegister) + 1; + uint32 referenceBlockNumber = registrationBlockNumber + + blocksBetweenRegistrations * uint32(maxOperatorsToRegister) + 1; cheats.roll(referenceBlockNumber + 100); - OperatorStateRetriever.CheckSignaturesIndices memory checkSignaturesIndices = operatorStateRetriever.getCheckSignaturesIndices( - registryCoordinator, - referenceBlockNumber, - quorumNumbers, - nonSignerOperatorIds + OperatorStateRetriever.CheckSignaturesIndices memory checkSignaturesIndices = + operatorStateRetriever.getCheckSignaturesIndices( + registryCoordinator, referenceBlockNumber, quorumNumbers, nonSignerOperatorIds ); - nonSignerStakesAndSignature.nonSignerQuorumBitmapIndices = checkSignaturesIndices.nonSignerQuorumBitmapIndices; + nonSignerStakesAndSignature.nonSignerQuorumBitmapIndices = + checkSignaturesIndices.nonSignerQuorumBitmapIndices; nonSignerStakesAndSignature.apkG2 = aggSignerApkG2; nonSignerStakesAndSignature.sigma = sigma; nonSignerStakesAndSignature.quorumApkIndices = checkSignaturesIndices.quorumApkIndices; nonSignerStakesAndSignature.totalStakeIndices = checkSignaturesIndices.totalStakeIndices; - nonSignerStakesAndSignature.nonSignerStakeIndices = checkSignaturesIndices.nonSignerStakeIndices; + nonSignerStakesAndSignature.nonSignerStakeIndices = + checkSignaturesIndices.nonSignerStakeIndices; return (referenceBlockNumber, nonSignerStakesAndSignature); } - } diff --git a/test/ffi/UpdateOperators.t.sol b/test/ffi/UpdateOperators.t.sol index 26fad27d..349bb8d1 100644 --- a/test/ffi/UpdateOperators.t.sol +++ b/test/ffi/UpdateOperators.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "test/integration/User.t.sol"; @@ -10,7 +10,1008 @@ contract Integration_AVS_Sync_GasCosts_FFI is IntegrationChecks { using BitmapUtils for *; // Private keys sorted by operatorIds. - uint256[] public privateKeys = [853, 690, 815, 398, 987, 432, 946, 717, 760, 840, 719, 714, 11, 554, 528, 368, 160, 22, 562, 266, 827, 488, 335, 566, 365, 54, 6, 733, 835, 656, 496, 472, 126, 50, 643, 632, 421, 797, 610, 737, 154, 918, 819, 694, 556, 608, 203, 521, 188, 908, 400, 349, 290, 463, 680, 973, 204, 439, 822, 799, 795, 251, 482, 326, 411, 839, 851, 652, 458, 108, 92, 278, 8, 773, 302, 699, 936, 427, 321, 700, 683, 36, 828, 732, 963, 664, 776, 161, 460, 426, 878, 96, 572, 678, 898, 372, 764, 579, 215, 507, 533, 965, 72, 708, 706, 334, 722, 665, 446, 397, 151, 802, 224, 753, 206, 190, 569, 253, 735, 578, 859, 711, 135, 944, 344, 655, 202, 743, 292, 176, 262, 961, 270, 117, 502, 546, 247, 31, 663, 515, 850, 509, 728, 424, 197, 239, 905, 545, 121, 438, 513, 881, 233, 221, 593, 831, 491, 282, 979, 410, 873, 316, 210, 371, 40, 255, 329, 483, 975, 742, 214, 813, 691, 467, 830, 808, 951, 924, 147, 705, 772, 30, 486, 576, 469, 331, 27, 313, 849, 805, 499, 404, 178, 10, 399, 485, 627, 60, 709, 570, 97, 894, 88, 264, 245, 129, 818, 218, 395, 387, 110, 455, 695, 199, 648, 444, 435, 230, 84, 489, 649, 385, 274, 95, 442, 899, 999, 651, 310, 227, 823, 538, 345, 229, 551, 24, 686, 877, 707, 671, 585, 530, 952, 28, 692, 336, 673, 777, 789, 366, 781, 872, 386, 64, 342, 244, 445, 816, 332, 436, 596, 148, 425, 863, 967, 611, 153, 749, 940, 150, 280, 634, 631, 954, 891, 666, 319, 93, 807, 82, 79, 91, 146, 291, 78, 923, 910, 320, 529, 857, 945, 205, 602, 974, 41, 503, 868, 783, 303, 536, 523, 357, 409, 832, 474, 862, 516, 140, 617, 543, 356, 77, 328, 976, 46, 834, 750, 99, 633, 949, 568, 636, 766, 363, 174, 138, 112, 574, 541, 703, 81, 412, 98, 477, 452, 755, 598, 464, 880, 884, 418, 829, 645, 607, 279, 820, 66, 416, 517, 384, 29, 192, 59, 15, 573, 94, 383, 981, 889, 914, 172, 322, 248, 845, 775, 1000, 854, 817, 668, 724, 786, 575, 710, 825, 407, 592, 890, 911, 641, 544, 989, 347, 196, 125, 370, 459, 803, 454, 564, 939, 658, 624, 996, 142, 514, 758, 848, 980, 955, 855, 298, 119, 391, 341, 130, 577, 798, 323, 986, 58, 171, 14, 959, 234, 838, 811, 958, 715, 902, 846, 571, 456, 268, 882, 257, 591, 497, 630, 20, 136, 672, 621, 744, 791, 314, 252, 367, 833, 23, 693, 726, 595, 276, 620, 167, 325, 401, 481, 63, 730, 771, 613, 614, 943, 526, 604, 104, 932, 856, 263, 650, 590, 232, 462, 931, 236, 915, 402, 644, 315, 213, 249, 869, 179, 312, 718, 675, 493, 903, 542, 287, 752, 285, 487, 661, 616, 182, 888, 929, 842, 364, 720, 935, 396, 763, 235, 226, 879, 346, 916, 615, 800, 972, 906, 547, 997, 198, 500, 39, 193, 864, 837, 301, 484, 362, 836, 293, 913, 324, 269, 520, 25, 169, 745, 960, 883, 61, 741, 382, 115, 220, 953, 612, 970, 207, 796, 901, 865, 53, 275, 408, 639, 875, 265, 756, 887, 133, 586, 296, 450, 433, 200, 49, 662, 2, 4, 183, 697, 222, 479, 429, 747, 143, 713, 784, 982, 539, 380, 75, 667, 601, 992, 457, 70, 103, 587, 785, 107, 186, 679, 804, 821, 567, 201, 928, 461, 413, 120, 67, 164, 441, 89, 962, 738, 801, 242, 740, 373, 759, 68, 490, 43, 358, 677, 937, 778, 589, 947, 565, 414, 861, 712, 299, 83, 904, 978, 886, 969, 701, 453, 933, 757, 76, 721, 286, 311, 968, 156, 184, 998, 994, 921, 159, 790, 934, 12, 128, 519, 307, 809, 687, 228, 393, 52, 912, 217, 113, 736, 195, 858, 925, 21, 170, 305, 892, 977, 360, 139, 810, 71, 727, 669, 622, 919, 32, 173, 208, 34, 535, 957, 782, 792, 874, 48, 132, 177, 57, 13, 475, 243, 647, 352, 5, 494, 277, 681, 580, 338, 158, 920, 350, 300, 284, 626, 297, 369, 806, 470, 895, 603, 420, 768, 761, 33, 191, 449, 359, 375, 844, 561, 86, 355, 606, 676, 734, 271, 381, 716, 767, 337, 688, 938, 746, 540, 942, 209, 451, 267, 907, 731, 674, 180, 619, 166, 893, 100, 704, 993, 294, 583, 770, 739, 256, 508, 495, 440, 289, 145, 237, 557, 109, 187, 102, 80, 843, 273, 37, 38, 354, 584, 794, 134, 194, 927, 922, 885, 118, 65, 684, 45, 431, 419, 281, 582, 531, 504, 471, 900, 51, 896, 124, 216, 466, 26, 378, 9, 137, 116, 588, 988, 423, 729, 157, 600, 152, 698, 106, 774, 448, 950, 56, 876, 379, 956, 163, 702, 511, 69, 3, 473, 990, 181, 555, 563, 165, 549, 447, 560, 510, 552, 304, 629, 930, 518, 826, 991, 17, 16, 35, 505, 723, 480, 478, 841, 254, 640, 405, 867, 522, 175, 468, 926, 852, 241, 860, 74, 443, 501, 769, 394, 246, 225, 966, 388, 111, 272, 259, 376, 985, 765, 984, 231, 597, 548, 374, 295, 870, 917, 897, 725, 642, 780, 422, 283, 971, 415, 240, 787, 238, 340, 149, 527, 550, 377, 964, 558, 144, 553, 788, 618, 351, 330, 525, 685, 625, 85, 42, 465, 941, 762, 47, 476, 751, 871, 189, 212, 754, 824, 223, 748, 7, 532, 670, 162, 403, 1, 659, 306, 657, 317, 623, 318, 605, 537, 131, 689, 308, 430, 168, 309, 599, 682, 654, 866, 90, 327, 581, 909, 635, 114, 559, 123, 127, 660, 646, 288, 19, 389, 793, 406, 638, 812, 343, 62, 498, 437, 44, 260, 122, 101, 628, 779, 417, 847, 696, 492, 211, 434, 339, 594, 333, 219, 348, 18, 250, 105, 512, 185, 524, 506, 155, 948, 141, 55, 428, 637, 392, 609, 995, 361, 983, 261, 653, 258, 87, 353, 390, 73, 534, 814]; + uint256[] public privateKeys = [ + 853, + 690, + 815, + 398, + 987, + 432, + 946, + 717, + 760, + 840, + 719, + 714, + 11, + 554, + 528, + 368, + 160, + 22, + 562, + 266, + 827, + 488, + 335, + 566, + 365, + 54, + 6, + 733, + 835, + 656, + 496, + 472, + 126, + 50, + 643, + 632, + 421, + 797, + 610, + 737, + 154, + 918, + 819, + 694, + 556, + 608, + 203, + 521, + 188, + 908, + 400, + 349, + 290, + 463, + 680, + 973, + 204, + 439, + 822, + 799, + 795, + 251, + 482, + 326, + 411, + 839, + 851, + 652, + 458, + 108, + 92, + 278, + 8, + 773, + 302, + 699, + 936, + 427, + 321, + 700, + 683, + 36, + 828, + 732, + 963, + 664, + 776, + 161, + 460, + 426, + 878, + 96, + 572, + 678, + 898, + 372, + 764, + 579, + 215, + 507, + 533, + 965, + 72, + 708, + 706, + 334, + 722, + 665, + 446, + 397, + 151, + 802, + 224, + 753, + 206, + 190, + 569, + 253, + 735, + 578, + 859, + 711, + 135, + 944, + 344, + 655, + 202, + 743, + 292, + 176, + 262, + 961, + 270, + 117, + 502, + 546, + 247, + 31, + 663, + 515, + 850, + 509, + 728, + 424, + 197, + 239, + 905, + 545, + 121, + 438, + 513, + 881, + 233, + 221, + 593, + 831, + 491, + 282, + 979, + 410, + 873, + 316, + 210, + 371, + 40, + 255, + 329, + 483, + 975, + 742, + 214, + 813, + 691, + 467, + 830, + 808, + 951, + 924, + 147, + 705, + 772, + 30, + 486, + 576, + 469, + 331, + 27, + 313, + 849, + 805, + 499, + 404, + 178, + 10, + 399, + 485, + 627, + 60, + 709, + 570, + 97, + 894, + 88, + 264, + 245, + 129, + 818, + 218, + 395, + 387, + 110, + 455, + 695, + 199, + 648, + 444, + 435, + 230, + 84, + 489, + 649, + 385, + 274, + 95, + 442, + 899, + 999, + 651, + 310, + 227, + 823, + 538, + 345, + 229, + 551, + 24, + 686, + 877, + 707, + 671, + 585, + 530, + 952, + 28, + 692, + 336, + 673, + 777, + 789, + 366, + 781, + 872, + 386, + 64, + 342, + 244, + 445, + 816, + 332, + 436, + 596, + 148, + 425, + 863, + 967, + 611, + 153, + 749, + 940, + 150, + 280, + 634, + 631, + 954, + 891, + 666, + 319, + 93, + 807, + 82, + 79, + 91, + 146, + 291, + 78, + 923, + 910, + 320, + 529, + 857, + 945, + 205, + 602, + 974, + 41, + 503, + 868, + 783, + 303, + 536, + 523, + 357, + 409, + 832, + 474, + 862, + 516, + 140, + 617, + 543, + 356, + 77, + 328, + 976, + 46, + 834, + 750, + 99, + 633, + 949, + 568, + 636, + 766, + 363, + 174, + 138, + 112, + 574, + 541, + 703, + 81, + 412, + 98, + 477, + 452, + 755, + 598, + 464, + 880, + 884, + 418, + 829, + 645, + 607, + 279, + 820, + 66, + 416, + 517, + 384, + 29, + 192, + 59, + 15, + 573, + 94, + 383, + 981, + 889, + 914, + 172, + 322, + 248, + 845, + 775, + 1000, + 854, + 817, + 668, + 724, + 786, + 575, + 710, + 825, + 407, + 592, + 890, + 911, + 641, + 544, + 989, + 347, + 196, + 125, + 370, + 459, + 803, + 454, + 564, + 939, + 658, + 624, + 996, + 142, + 514, + 758, + 848, + 980, + 955, + 855, + 298, + 119, + 391, + 341, + 130, + 577, + 798, + 323, + 986, + 58, + 171, + 14, + 959, + 234, + 838, + 811, + 958, + 715, + 902, + 846, + 571, + 456, + 268, + 882, + 257, + 591, + 497, + 630, + 20, + 136, + 672, + 621, + 744, + 791, + 314, + 252, + 367, + 833, + 23, + 693, + 726, + 595, + 276, + 620, + 167, + 325, + 401, + 481, + 63, + 730, + 771, + 613, + 614, + 943, + 526, + 604, + 104, + 932, + 856, + 263, + 650, + 590, + 232, + 462, + 931, + 236, + 915, + 402, + 644, + 315, + 213, + 249, + 869, + 179, + 312, + 718, + 675, + 493, + 903, + 542, + 287, + 752, + 285, + 487, + 661, + 616, + 182, + 888, + 929, + 842, + 364, + 720, + 935, + 396, + 763, + 235, + 226, + 879, + 346, + 916, + 615, + 800, + 972, + 906, + 547, + 997, + 198, + 500, + 39, + 193, + 864, + 837, + 301, + 484, + 362, + 836, + 293, + 913, + 324, + 269, + 520, + 25, + 169, + 745, + 960, + 883, + 61, + 741, + 382, + 115, + 220, + 953, + 612, + 970, + 207, + 796, + 901, + 865, + 53, + 275, + 408, + 639, + 875, + 265, + 756, + 887, + 133, + 586, + 296, + 450, + 433, + 200, + 49, + 662, + 2, + 4, + 183, + 697, + 222, + 479, + 429, + 747, + 143, + 713, + 784, + 982, + 539, + 380, + 75, + 667, + 601, + 992, + 457, + 70, + 103, + 587, + 785, + 107, + 186, + 679, + 804, + 821, + 567, + 201, + 928, + 461, + 413, + 120, + 67, + 164, + 441, + 89, + 962, + 738, + 801, + 242, + 740, + 373, + 759, + 68, + 490, + 43, + 358, + 677, + 937, + 778, + 589, + 947, + 565, + 414, + 861, + 712, + 299, + 83, + 904, + 978, + 886, + 969, + 701, + 453, + 933, + 757, + 76, + 721, + 286, + 311, + 968, + 156, + 184, + 998, + 994, + 921, + 159, + 790, + 934, + 12, + 128, + 519, + 307, + 809, + 687, + 228, + 393, + 52, + 912, + 217, + 113, + 736, + 195, + 858, + 925, + 21, + 170, + 305, + 892, + 977, + 360, + 139, + 810, + 71, + 727, + 669, + 622, + 919, + 32, + 173, + 208, + 34, + 535, + 957, + 782, + 792, + 874, + 48, + 132, + 177, + 57, + 13, + 475, + 243, + 647, + 352, + 5, + 494, + 277, + 681, + 580, + 338, + 158, + 920, + 350, + 300, + 284, + 626, + 297, + 369, + 806, + 470, + 895, + 603, + 420, + 768, + 761, + 33, + 191, + 449, + 359, + 375, + 844, + 561, + 86, + 355, + 606, + 676, + 734, + 271, + 381, + 716, + 767, + 337, + 688, + 938, + 746, + 540, + 942, + 209, + 451, + 267, + 907, + 731, + 674, + 180, + 619, + 166, + 893, + 100, + 704, + 993, + 294, + 583, + 770, + 739, + 256, + 508, + 495, + 440, + 289, + 145, + 237, + 557, + 109, + 187, + 102, + 80, + 843, + 273, + 37, + 38, + 354, + 584, + 794, + 134, + 194, + 927, + 922, + 885, + 118, + 65, + 684, + 45, + 431, + 419, + 281, + 582, + 531, + 504, + 471, + 900, + 51, + 896, + 124, + 216, + 466, + 26, + 378, + 9, + 137, + 116, + 588, + 988, + 423, + 729, + 157, + 600, + 152, + 698, + 106, + 774, + 448, + 950, + 56, + 876, + 379, + 956, + 163, + 702, + 511, + 69, + 3, + 473, + 990, + 181, + 555, + 563, + 165, + 549, + 447, + 560, + 510, + 552, + 304, + 629, + 930, + 518, + 826, + 991, + 17, + 16, + 35, + 505, + 723, + 480, + 478, + 841, + 254, + 640, + 405, + 867, + 522, + 175, + 468, + 926, + 852, + 241, + 860, + 74, + 443, + 501, + 769, + 394, + 246, + 225, + 966, + 388, + 111, + 272, + 259, + 376, + 985, + 765, + 984, + 231, + 597, + 548, + 374, + 295, + 870, + 917, + 897, + 725, + 642, + 780, + 422, + 283, + 971, + 415, + 240, + 787, + 238, + 340, + 149, + 527, + 550, + 377, + 964, + 558, + 144, + 553, + 788, + 618, + 351, + 330, + 525, + 685, + 625, + 85, + 42, + 465, + 941, + 762, + 47, + 476, + 751, + 871, + 189, + 212, + 754, + 824, + 223, + 748, + 7, + 532, + 670, + 162, + 403, + 1, + 659, + 306, + 657, + 317, + 623, + 318, + 605, + 537, + 131, + 689, + 308, + 430, + 168, + 309, + 599, + 682, + 654, + 866, + 90, + 327, + 581, + 909, + 635, + 114, + 559, + 123, + 127, + 660, + 646, + 288, + 19, + 389, + 793, + 406, + 638, + 812, + 343, + 62, + 498, + 437, + 44, + 260, + 122, + 101, + 628, + 779, + 417, + 847, + 696, + 492, + 211, + 434, + 339, + 594, + 333, + 219, + 348, + 18, + 250, + 105, + 512, + 185, + 524, + 506, + 155, + 948, + 141, + 55, + 428, + 637, + 392, + 609, + 995, + 361, + 983, + 261, + 653, + 258, + 87, + 353, + 390, + 73, + 534, + 814 + ]; bytes32[] public operatorIds; address[] public operatorAddresses; @@ -26,34 +1027,22 @@ contract Integration_AVS_Sync_GasCosts_FFI is IntegrationChecks { // READ JSON CONFIG DATA string memory config_data = vm.readFile(keysConfigPath); for (uint256 i = 0; i < MAX_OPERATOR_COUNT; i++) { - IBLSApkRegistry.PubkeyRegistrationParams memory pubkey; + IBLSApkRegistryTypes.PubkeyRegistrationParams memory pubkey; uint256 privateKey = privateKeys[i]; // G1 - pubkey.pubkeyG1.X = stdJson.readUint( - config_data, - string.concat(".G1x[", vm.toString(i), "]") - ); - pubkey.pubkeyG1.Y = stdJson.readUint( - config_data, - string.concat(".G1y[", vm.toString(i), "]") - ); - // G2 - pubkey.pubkeyG2.X[1] = stdJson.readUint( - config_data, - string.concat(".G2x1[", vm.toString(i), "]") - ); - pubkey.pubkeyG2.Y[1] = stdJson.readUint( - config_data, - string.concat(".G2y1[", vm.toString(i), "]") - ); - pubkey.pubkeyG2.X[0] = stdJson.readUint( - config_data, - string.concat(".G2x0[", vm.toString(i), "]") - ); - pubkey.pubkeyG2.Y[0] = stdJson.readUint( - config_data, - string.concat(".G2y0[", vm.toString(i), "]") - ); + pubkey.pubkeyG1.X = + stdJson.readUint(config_data, string.concat(".G1x[", vm.toString(i), "]")); + pubkey.pubkeyG1.Y = + stdJson.readUint(config_data, string.concat(".G1y[", vm.toString(i), "]")); + // G2 + pubkey.pubkeyG2.X[1] = + stdJson.readUint(config_data, string.concat(".G2x1[", vm.toString(i), "]")); + pubkey.pubkeyG2.Y[1] = + stdJson.readUint(config_data, string.concat(".G2y1[", vm.toString(i), "]")); + pubkey.pubkeyG2.X[0] = + stdJson.readUint(config_data, string.concat(".G2x0[", vm.toString(i), "]")); + pubkey.pubkeyG2.Y[0] = + stdJson.readUint(config_data, string.concat(".G2y0[", vm.toString(i), "]")); privKeys.push(privateKey); pubkeys.push(pubkey); } @@ -61,7 +1050,7 @@ contract Integration_AVS_Sync_GasCosts_FFI is IntegrationChecks { function _generateOperatorKeys() internal { for (uint256 i = 0; i < 200; i++) { - IBLSApkRegistry.PubkeyRegistrationParams memory pubkey; + IBLSApkRegistryTypes.PubkeyRegistrationParams memory pubkey; uint256 privateKey = privateKeys[i]; pubkey.pubkeyG1 = BN254.generatorG1().scalar_mul(privateKey); pubkey.pubkeyG2 = G2Operations.mul(privateKey); @@ -146,11 +1135,13 @@ contract Integration_AVS_Sync_GasCosts_FFI is IntegrationChecks { console.log("Gas used for updateOperatorsForQuorum: ", gasBefore - gasAfter); } - function _sortArray(address[] memory arr) internal pure returns (address[] memory) { + function _sortArray( + address[] memory arr + ) internal pure returns (address[] memory) { uint256 l = arr.length; - for(uint i = 0; i < l; i++) { - for(uint j = i+1; j < l ;j++) { - if(arr[i] > arr[j]) { + for (uint256 i = 0; i < l; i++) { + for (uint256 j = i + 1; j < l; j++) { + if (arr[i] > arr[j]) { address temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; diff --git a/test/ffi/util/G2Operations.sol b/test/ffi/util/G2Operations.sol index f25cbbb7..52127f9a 100644 --- a/test/ffi/util/G2Operations.sol +++ b/test/ffi/util/G2Operations.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "forge-std/Test.sol"; import "openzeppelin-contracts/contracts/utils/Strings.sol"; @@ -8,12 +8,14 @@ import "../../../src/libraries/BN254.sol"; contract G2Operations is Test { using Strings for uint256; - function mul(uint256 x) public returns (BN254.G2Point memory g2Point) { + function mul( + uint256 x + ) public returns (BN254.G2Point memory g2Point) { string[] memory inputs = new string[](5); inputs[0] = "go"; inputs[1] = "run"; inputs[2] = "test/ffi/go/g2mul.go"; - inputs[3] = x.toString(); + inputs[3] = x.toString(); inputs[4] = "1"; bytes memory res = vm.ffi(inputs); @@ -31,5 +33,4 @@ contract G2Operations is Test { res = vm.ffi(inputs); g2Point.Y[0] = abi.decode(res, (uint256)); } - } diff --git a/test/harnesses/AVSDirectoryHarness.sol b/test/harnesses/AVSDirectoryHarness.sol new file mode 100644 index 00000000..d452da82 --- /dev/null +++ b/test/harnesses/AVSDirectoryHarness.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.27; + +import {IDelegationManager} from + "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; +import {AVSDirectory} from "eigenlayer-contracts/src/contracts/core/AVSDirectory.sol"; +import {IPauserRegistry} from "eigenlayer-contracts/src/contracts/interfaces/IPauserRegistry.sol"; + +// wrapper around the AVSDirectory contract that exposes internal functionality, for unit testing +contract AVSDirectoryHarness is AVSDirectory { + constructor(IDelegationManager _dm, IPauserRegistry _pauser) AVSDirectory(_dm, _pauser) {} +} diff --git a/test/harnesses/BLSApkRegistryHarness.sol b/test/harnesses/BLSApkRegistryHarness.sol index 546d355c..b97ba955 100644 --- a/test/harnesses/BLSApkRegistryHarness.sol +++ b/test/harnesses/BLSApkRegistryHarness.sol @@ -1,17 +1,15 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; 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) {} + ISlashingRegistryCoordinator _slashingRegistryCoordinator + ) BLSApkRegistry(_slashingRegistryCoordinator) {} function setBLSPublicKey(address account, BN254.G1Point memory pk) external { - bytes32 pubkeyHash = BN254.hashG1Point(pk); // store updates operatorToPubkeyHash[account] = pubkeyHash; diff --git a/test/harnesses/BitmapUtilsWrapper.sol b/test/harnesses/BitmapUtilsWrapper.sol index 19e1135e..982d2e1a 100644 --- a/test/harnesses/BitmapUtilsWrapper.sol +++ b/test/harnesses/BitmapUtilsWrapper.sol @@ -1,23 +1,31 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "../../src/libraries/BitmapUtils.sol"; // wrapper around the BitmapUtils library that exposes the internal functions contract BitmapUtilsWrapper { - function orderedBytesArrayToBitmap(bytes calldata orderedBytesArray) external pure returns (uint256) { + function orderedBytesArrayToBitmap( + bytes calldata orderedBytesArray + ) external pure returns (uint256) { return BitmapUtils.orderedBytesArrayToBitmap(orderedBytesArray); } - function isArrayStrictlyAscendingOrdered(bytes calldata bytesArray) external pure returns (bool) { + function isArrayStrictlyAscendingOrdered( + bytes calldata bytesArray + ) external pure returns (bool) { return BitmapUtils.isArrayStrictlyAscendingOrdered(bytesArray); } - function bitmapToBytesArray(uint256 bitmap) external pure returns (bytes memory bytesArray) { + function bitmapToBytesArray( + uint256 bitmap + ) external pure returns (bytes memory bytesArray) { return BitmapUtils.bitmapToBytesArray(bitmap); } - function countNumOnes(uint256 n) external pure returns (uint16) { + function countNumOnes( + uint256 n + ) external pure returns (uint16) { return BitmapUtils.countNumOnes(n); } @@ -29,7 +37,9 @@ contract BitmapUtilsWrapper { return BitmapUtils.setBit(bitmap, bit); } - function isEmpty(uint256 bitmap) external pure returns (bool) { + function isEmpty( + uint256 bitmap + ) external pure returns (bool) { return BitmapUtils.isEmpty(bitmap); } diff --git a/test/harnesses/RegistryCoordinatorHarness.t.sol b/test/harnesses/RegistryCoordinatorHarness.t.sol index 80b38ca2..050fae43 100644 --- a/test/harnesses/RegistryCoordinatorHarness.t.sol +++ b/test/harnesses/RegistryCoordinatorHarness.t.sol @@ -1,8 +1,10 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "../../src/RegistryCoordinator.sol"; +import {ISocketRegistry} from "../../src/interfaces/ISocketRegistry.sol"; + import "forge-std/Test.sol"; // wrapper around the RegistryCoordinator contract that exposes the internal functions for unit testing. @@ -12,12 +14,26 @@ contract RegistryCoordinatorHarness is RegistryCoordinator, Test { IStakeRegistry _stakeRegistry, IBLSApkRegistry _blsApkRegistry, IIndexRegistry _indexRegistry, - ISocketRegistry _socketRegistry - ) RegistryCoordinator(_serviceManager, _stakeRegistry, _blsApkRegistry, _indexRegistry, _socketRegistry) { + ISocketRegistry _socketRegistry, + IAllocationManager _allocationManager, + IPauserRegistry _pauserRegistry + ) + RegistryCoordinator( + _serviceManager, + _stakeRegistry, + _blsApkRegistry, + _indexRegistry, + _socketRegistry, + _allocationManager, + _pauserRegistry + ) + { _transferOwnership(msg.sender); } - function setQuorumCount(uint8 count) external { + function setQuorumCount( + uint8 count + ) external { quorumCount = count; } @@ -27,20 +43,17 @@ contract RegistryCoordinatorHarness is RegistryCoordinator, Test { // @notice exposes the internal `_registerOperator` function, overriding all access controls function _registerOperatorExternal( - address operator, + address operator, bytes32 operatorId, bytes calldata quorumNumbers, string memory socket, SignatureWithSaltAndExpiry memory operatorSignature ) external returns (RegisterResults memory results) { - return _registerOperator(operator, operatorId, quorumNumbers, socket, operatorSignature); + return _registerOperator(operator, operatorId, quorumNumbers, socket); } // @notice exposes the internal `_deregisterOperator` function, overriding all access controls - function _deregisterOperatorExternal( - address operator, - bytes calldata quorumNumbers - ) external { + function _deregisterOperatorExternal(address operator, bytes calldata quorumNumbers) external { _deregisterOperator(operator, quorumNumbers); } @@ -57,4 +70,16 @@ contract RegistryCoordinatorHarness is RegistryCoordinator, Test { function _updateOperatorBitmapExternal(bytes32 operatorId, uint192 quorumBitmap) external { _updateOperatorBitmap(operatorId, quorumBitmap); } + + function setOperatorSetsEnabled( + bool enabled + ) external { + operatorSetsEnabled = enabled; + } + + function setM2QuorumsDisabled( + bool disabled + ) external { + m2QuorumsDisabled = disabled; + } } diff --git a/test/harnesses/StakeRegistryHarness.sol b/test/harnesses/StakeRegistryHarness.sol index 386508d9..2a205541 100644 --- a/test/harnesses/StakeRegistryHarness.sol +++ b/test/harnesses/StakeRegistryHarness.sol @@ -1,17 +1,22 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "../../src/StakeRegistry.sol"; // wrapper around the StakeRegistry contract that exposes the internal functions for unit testing. contract StakeRegistryHarness is StakeRegistry { constructor( - IRegistryCoordinator _registryCoordinator, - IDelegationManager _delegationManager - ) StakeRegistry(_registryCoordinator, _delegationManager) { - } + ISlashingRegistryCoordinator _registryCoordinator, + IDelegationManager _delegationManager, + IAVSDirectory _avsDirectory, + IAllocationManager _allocationManager + ) StakeRegistry(_registryCoordinator, _delegationManager, _avsDirectory, _allocationManager) {} - function recordOperatorStakeUpdate(bytes32 operatorId, uint8 quorumNumber, uint96 newStake) external returns(int256) { + function recordOperatorStakeUpdate( + bytes32 operatorId, + uint8 quorumNumber, + uint96 newStake + ) external returns (int256) { return _recordOperatorStakeUpdate(operatorId, quorumNumber, newStake); } diff --git a/test/integration/CoreRegistration.t.sol b/test/integration/CoreRegistration.t.sol index 64730b17..42359be0 100644 --- a/test/integration/CoreRegistration.t.sol +++ b/test/integration/CoreRegistration.t.sol @@ -1,13 +1,23 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "../utils/MockAVSDeployer.sol"; -import { AVSDirectory } from "eigenlayer-contracts/src/contracts/core/AVSDirectory.sol"; -import { IAVSDirectory } from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol"; -import { DelegationManager } from "eigenlayer-contracts/src/contracts/core/DelegationManager.sol"; -import { IDelegationManager } from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; -import { RewardsCoordinator } from "eigenlayer-contracts/src/contracts/core/RewardsCoordinator.sol"; -import { IRewardsCoordinator } from "eigenlayer-contracts/src/contracts/interfaces/IRewardsCoordinator.sol"; +import {AVSDirectory} from "eigenlayer-contracts/src/contracts/core/AVSDirectory.sol"; +import { + IAVSDirectory, + IAVSDirectoryTypes +} from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol"; +import {IStrategyManager} from "eigenlayer-contracts/src/contracts/interfaces/IStrategyManager.sol"; +import {DelegationManager} from "eigenlayer-contracts/src/contracts/core/DelegationManager.sol"; +import { + IDelegationManager, + IDelegationManagerTypes +} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; +import {RewardsCoordinator} from "eigenlayer-contracts/src/contracts/core/RewardsCoordinator.sol"; +import {IRewardsCoordinator} from + "eigenlayer-contracts/src/contracts/interfaces/IRewardsCoordinator.sol"; +import {PermissionController} from + "eigenlayer-contracts/src/contracts/permissions/PermissionController.sol"; contract Test_CoreRegistration is MockAVSDeployer { // Contracts @@ -26,7 +36,15 @@ contract Test_CoreRegistration is MockAVSDeployer { _deployMockEigenLayerAndAVS(); // Deploy New DelegationManager - DelegationManager delegationManagerImplementation = new DelegationManager(strategyManagerMock, slasher, eigenPodManagerMock); + PermissionController permissionController; // TODO: Fix + DelegationManager delegationManagerImplementation = new DelegationManager( + IStrategyManager(address(strategyManagerMock)), + eigenPodManagerMock, + allocationManagerMock, + pauserRegistry, + permissionController, + 0 + ); IStrategy[] memory initializeStrategiesToSetDelayBlocks = new IStrategy[](0); uint256[] memory initializeWithdrawalDelayBlocks = new uint256[](0); delegationManager = DelegationManager( @@ -48,7 +66,8 @@ contract Test_CoreRegistration is MockAVSDeployer { ); // Deploy New AVS Directory - AVSDirectory avsDirectoryImplementation = new AVSDirectory(delegationManager); + AVSDirectory avsDirectoryImplementation = + new AVSDirectory(delegationManager, pauserRegistry); // TODO: Fix Config avsDirectory = AVSDirectory( address( new TransparentUpgradeableProxy( @@ -72,7 +91,9 @@ contract Test_CoreRegistration is MockAVSDeployer { avsDirectory, rewardsCoordinatorMock, registryCoordinator, - stakeRegistry + stakeRegistry, + permissionController, + allocationManager ); registryCoordinatorImplementation = new RegistryCoordinatorHarness( @@ -80,7 +101,9 @@ contract Test_CoreRegistration is MockAVSDeployer { stakeRegistry, blsApkRegistry, indexRegistry, - socketRegistry + socketRegistry, + allocationManager, + pauserRegistry ); // Upgrade Registry Coordinator & ServiceManager @@ -102,17 +125,15 @@ contract Test_CoreRegistration is MockAVSDeployer { // Register operator to EigenLayer cheats.prank(operator); delegationManager.registerAsOperator( - IDelegationManager.OperatorDetails({ - __deprecated_earningsReceiver: operator, - delegationApprover: address(0), - stakerOptOutWindowBlocks: 0 - }), + operator, + // TODO: fix or parameterize + 0, emptyStringForMetadataURI ); // Set operator weight in single quorum bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(MAX_QUORUM_BITMAP); - for (uint i = 0; i < quorumNumbers.length; i++) { + for (uint256 i = 0; i < quorumNumbers.length; i++) { _setOperatorWeight(operator, uint8(quorumNumbers[i]), defaultStake); } } @@ -122,11 +143,7 @@ contract Test_CoreRegistration is MockAVSDeployer { // Get operator signature ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature = _getOperatorSignature( - operatorPrivateKey, - operator, - address(serviceManager), - emptySalt, - maxExpiry + operatorPrivateKey, operator, address(serviceManager), emptySalt, maxExpiry ); // set operator as registered in Eigenlayer @@ -134,11 +151,17 @@ contract Test_CoreRegistration is MockAVSDeployer { // Register operator cheats.prank(operator); - registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, operatorSignature); + registryCoordinator.registerOperator( + quorumNumbers, defaultSocket, pubkeyRegistrationParams, operatorSignature + ); // Check operator is registered - IAVSDirectory.OperatorAVSRegistrationStatus operatorStatus = avsDirectory.avsOperatorStatus(address(serviceManager), operator); - assertEq(uint8(operatorStatus), uint8(IAVSDirectory.OperatorAVSRegistrationStatus.REGISTERED)); + IAVSDirectoryTypes.OperatorAVSRegistrationStatus operatorStatus = + avsDirectory.avsOperatorStatus(address(serviceManager), operator); + assertEq( + uint8(operatorStatus), + uint8(IAVSDirectoryTypes.OperatorAVSRegistrationStatus.REGISTERED) + ); } function test_deregisterOperator_coreStateChanges() public { @@ -151,8 +174,12 @@ contract Test_CoreRegistration is MockAVSDeployer { registryCoordinator.deregisterOperator(quorumNumbers); // Check operator is deregistered - IAVSDirectory.OperatorAVSRegistrationStatus operatorStatus = avsDirectory.avsOperatorStatus(address(serviceManager), operator); - assertEq(uint8(operatorStatus), uint8(IAVSDirectory.OperatorAVSRegistrationStatus.UNREGISTERED)); + IAVSDirectoryTypes.OperatorAVSRegistrationStatus operatorStatus = + avsDirectory.avsOperatorStatus(address(serviceManager), operator); + assertEq( + uint8(operatorStatus), + uint8(IAVSDirectoryTypes.OperatorAVSRegistrationStatus.UNREGISTERED) + ); } function test_deregisterOperator_notGloballyDeregistered() public { @@ -167,8 +194,12 @@ contract Test_CoreRegistration is MockAVSDeployer { registryCoordinator.deregisterOperator(quorumNumbers); // Check operator is still registered - IAVSDirectory.OperatorAVSRegistrationStatus operatorStatus = avsDirectory.avsOperatorStatus(address(serviceManager), operator); - assertEq(uint8(operatorStatus), uint8(IAVSDirectory.OperatorAVSRegistrationStatus.REGISTERED)); + IAVSDirectoryTypes.OperatorAVSRegistrationStatus operatorStatus = + avsDirectory.avsOperatorStatus(address(serviceManager), operator); + assertEq( + uint8(operatorStatus), + uint8(IAVSDirectoryTypes.OperatorAVSRegistrationStatus.REGISTERED) + ); } function test_setMetadataURI_fail_notServiceManagerOwner() public { @@ -189,14 +220,12 @@ contract Test_CoreRegistration is MockAVSDeployer { } // Utils - function _registerOperator(bytes memory quorumNumbers) internal { + function _registerOperator( + bytes memory quorumNumbers + ) internal { // Get operator signature ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature = _getOperatorSignature( - operatorPrivateKey, - operator, - address(serviceManager), - emptySalt, - maxExpiry + operatorPrivateKey, operator, address(serviceManager), emptySalt, maxExpiry ); // set operator as registered in Eigenlayer @@ -204,7 +233,9 @@ contract Test_CoreRegistration is MockAVSDeployer { // Register operator cheats.prank(operator); - registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, operatorSignature); + registryCoordinator.registerOperator( + quorumNumbers, defaultSocket, pubkeyRegistrationParams, operatorSignature + ); } function _getOperatorSignature( @@ -217,11 +248,12 @@ contract Test_CoreRegistration is MockAVSDeployer { operatorSignature.salt = salt; operatorSignature.expiry = expiry; { - bytes32 digestHash = avsDirectory.calculateOperatorAVSRegistrationDigestHash(operatorToSign, avs, salt, expiry); + bytes32 digestHash = avsDirectory.calculateOperatorAVSRegistrationDigestHash( + operatorToSign, avs, salt, expiry + ); (uint8 v, bytes32 r, bytes32 s) = cheats.sign(_operatorPrivateKey, digestHash); operatorSignature.signature = abi.encodePacked(r, s, v); } return operatorSignature; } - } diff --git a/test/integration/IntegrationBase.t.sol b/test/integration/IntegrationBase.t.sol index c9695d70..180edd11 100644 --- a/test/integration/IntegrationBase.t.sol +++ b/test/integration/IntegrationBase.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "forge-std/Test.sol"; @@ -14,7 +14,6 @@ import "test/integration/TimeMachine.t.sol"; import "test/integration/User.t.sol"; abstract contract IntegrationBase is IntegrationConfig { - using Strings for *; using BitmapUtils for *; using BN254 for *; @@ -30,22 +29,26 @@ abstract contract IntegrationBase is IntegrationConfig { /// @dev Also checks that the user has NEVER_REGISTERED status function assert_HasNoOperatorInfo(User user, string memory err) internal { - IRegistryCoordinator.OperatorInfo memory info = _getOperatorInfo(user); + ISlashingRegistryCoordinatorTypes.OperatorInfo memory info = _getOperatorInfo(user); assertEq(info.operatorId, bytes32(0), err); - assertTrue(info.status == IRegistryCoordinator.OperatorStatus.NEVER_REGISTERED, err); + assertTrue( + info.status == ISlashingRegistryCoordinatorTypes.OperatorStatus.NEVER_REGISTERED, err + ); } function assert_HasRegisteredStatus(User user, string memory err) internal { - IRegistryCoordinator.OperatorStatus status = registryCoordinator.getOperatorStatus(address(user)); + ISlashingRegistryCoordinatorTypes.OperatorStatus status = + registryCoordinator.getOperatorStatus(address(user)); - assertTrue(status == IRegistryCoordinator.OperatorStatus.REGISTERED, err); + assertTrue(status == ISlashingRegistryCoordinatorTypes.OperatorStatus.REGISTERED, err); } function assert_HasDeregisteredStatus(User user, string memory err) internal { - IRegistryCoordinator.OperatorStatus status = registryCoordinator.getOperatorStatus(address(user)); + ISlashingRegistryCoordinatorTypes.OperatorStatus status = + registryCoordinator.getOperatorStatus(address(user)); - assertTrue(status == IRegistryCoordinator.OperatorStatus.DEREGISTERED, err); + assertTrue(status == ISlashingRegistryCoordinatorTypes.OperatorStatus.DEREGISTERED, err); } function assert_EmptyQuorumBitmap(User user, string memory err) internal { @@ -54,10 +57,14 @@ abstract contract IntegrationBase is IntegrationConfig { assertTrue(bitmap == 0, err); } - function assert_NotRegisteredForQuorums(User user, bytes memory quorums, string memory err) internal { + function assert_NotRegisteredForQuorums( + User user, + bytes memory quorums, + string memory err + ) internal { uint192 bitmap = registryCoordinator.getCurrentQuorumBitmap(user.operatorId()); - for (uint i = 0; i < quorums.length; i++) { + for (uint256 i = 0; i < quorums.length; i++) { uint8 quorum = uint8(quorums[i]); assertFalse(bitmap.isSet(quorum), err); @@ -65,7 +72,11 @@ abstract contract IntegrationBase is IntegrationConfig { } /// @dev Checks that the user's current bitmap includes ALL of these quorums - function assert_IsRegisteredForQuorums(User user, bytes memory quorums, string memory err) internal { + function assert_IsRegisteredForQuorums( + User user, + bytes memory quorums, + string memory err + ) internal { uint192 currentBitmap = registryCoordinator.getCurrentQuorumBitmap(user.operatorId()); uint192 subsetBitmap = uint192(quorums.orderedBytesArrayToBitmap()); @@ -75,7 +86,7 @@ abstract contract IntegrationBase is IntegrationConfig { /// @dev Checks whether each of the quorums has been initialized in the RegistryCoordinator function assert_QuorumsExist(bytes memory quorums, string memory err) internal { uint8 count = registryCoordinator.quorumCount(); - for (uint i = 0; i < quorums.length; i++) { + for (uint256 i = 0; i < quorums.length; i++) { uint8 quorum = uint8(quorums[i]); assertTrue(quorum < count, err); @@ -85,7 +96,7 @@ abstract contract IntegrationBase is IntegrationConfig { /// BLSApkRegistry: function assert_NoRegisteredPubkey(User user, string memory err) internal { - (uint pubkeyX, uint pubkeyY) = blsApkRegistry.operatorToPubkey(address(user)); + (uint256 pubkeyX, uint256 pubkeyY) = blsApkRegistry.operatorToPubkey(address(user)); bytes32 pubkeyHash = blsApkRegistry.operatorToPubkeyHash(address(user)); assertEq(pubkeyX, 0, err); @@ -95,7 +106,7 @@ abstract contract IntegrationBase is IntegrationConfig { function assert_HasRegisteredPubkey(User user, string memory err) internal { BN254.G1Point memory expectedPubkey = user.pubkeyG1(); - (uint actualPkX, uint actualPkY) = blsApkRegistry.operatorToPubkey(address(user)); + (uint256 actualPkX, uint256 actualPkY) = blsApkRegistry.operatorToPubkey(address(user)); bytes32 expectedHash = expectedPubkey.hashG1Point(); bytes32 actualHash = blsApkRegistry.operatorToPubkeyHash(address(user)); @@ -113,7 +124,7 @@ abstract contract IntegrationBase is IntegrationConfig { function assert_NoExistingStake(User user, bytes memory quorums, string memory err) internal { bytes32 operatorId = user.operatorId(); - for (uint i = 0; i < quorums.length; i++) { + for (uint256 i = 0; i < quorums.length; i++) { uint8 quorum = uint8(quorums[i]); uint96 curStake = stakeRegistry.getCurrentStake(operatorId, quorum); @@ -123,8 +134,12 @@ abstract contract IntegrationBase is IntegrationConfig { } /// @dev Checks that the user meets the minimum weight required for each quorum - function assert_MeetsMinimumWeight(User user, bytes memory quorums, string memory err) internal { - for (uint i = 0; i < quorums.length; i++) { + function assert_MeetsMinimumWeight( + User user, + bytes memory quorums, + string memory err + ) internal { + for (uint256 i = 0; i < quorums.length; i++) { uint8 quorum = uint8(quorums[i]); uint96 minimum = stakeRegistry.minimumStakeForQuorum(quorum); @@ -135,10 +150,14 @@ abstract contract IntegrationBase is IntegrationConfig { } /// @dev Checks that the user meets the minimum stake required for each quorum - function assert_HasAtLeastMinimumStake(User user, bytes memory quorums, string memory err) internal { + function assert_HasAtLeastMinimumStake( + User user, + bytes memory quorums, + string memory err + ) internal { bytes32 operatorId = user.operatorId(); - for (uint i = 0; i < quorums.length; i++) { + for (uint256 i = 0; i < quorums.length; i++) { uint8 quorum = uint8(quorums[i]); uint96 minimum = stakeRegistry.minimumStakeForQuorum(quorum); @@ -153,10 +172,11 @@ abstract contract IntegrationBase is IntegrationConfig { /// @dev Checks that we're specifically UNDER the max operator count, i.e. we are allowing /// at least one more operator to register function assert_BelowMaxOperators(bytes memory quorums, string memory err) internal { - for (uint i = 0; i < quorums.length; i++) { + for (uint256 i = 0; i < quorums.length; i++) { uint8 quorum = uint8(quorums[i]); - uint32 maxOperatorCount = registryCoordinator.getOperatorSetParams(quorum).maxOperatorCount; + uint32 maxOperatorCount = + registryCoordinator.getOperatorSetParams(quorum).maxOperatorCount; uint32 curOperatorCount = indexRegistry.totalOperatorsForQuorum(quorum); assertTrue(curOperatorCount < maxOperatorCount, err); @@ -164,29 +184,37 @@ abstract contract IntegrationBase is IntegrationConfig { } /// AVSDirectory: - + function assert_NotRegisteredToAVS(User operator, string memory err) internal { - IAVSDirectory.OperatorAVSRegistrationStatus status = avsDirectory.avsOperatorStatus(address(serviceManager), address(operator)); + IAVSDirectoryTypes.OperatorAVSRegistrationStatus status = + avsDirectory.avsOperatorStatus(address(serviceManager), address(operator)); - assertTrue(status == IAVSDirectory.OperatorAVSRegistrationStatus.UNREGISTERED, err); + assertTrue(status == IAVSDirectoryTypes.OperatorAVSRegistrationStatus.UNREGISTERED, err); } function assert_IsRegisteredToAVS(User operator, string memory err) internal { - IAVSDirectory.OperatorAVSRegistrationStatus status = avsDirectory.avsOperatorStatus(address(serviceManager), address(operator)); + IAVSDirectory.OperatorAVSRegistrationStatus status = + avsDirectory.avsOperatorStatus(address(serviceManager), address(operator)); - assertTrue(status == IAVSDirectory.OperatorAVSRegistrationStatus.REGISTERED, err); + assertTrue(status == IAVSDirectoryTypes.OperatorAVSRegistrationStatus.REGISTERED, err); } - /******************************************************************************* - SNAPSHOT ASSERTIONS (MIDDLEWARE) - TIME TRAVELERS ONLY BEYOND THIS POINT - *******************************************************************************/ + /** + * + * SNAPSHOT ASSERTIONS (MIDDLEWARE) + * TIME TRAVELERS ONLY BEYOND THIS POINT + * + */ /// @dev Checks that `quorums` were added to the user's registered quorums /// NOTE: This means curBitmap - prevBitmap = quorums - function assert_Snap_Registered_ForQuorums(User user, bytes memory quorums, string memory err) internal { + function assert_Snap_Registered_ForQuorums( + User user, + bytes memory quorums, + string memory err + ) internal { bytes32 operatorId = user.operatorId(); - uint quorumsAdded = quorums.orderedBytesArrayToBitmap(); + uint256 quorumsAdded = quorums.orderedBytesArrayToBitmap(); uint192 curBitmap = _getQuorumBitmap(operatorId); uint192 prevBitmap = _getPrevQuorumBitmap(operatorId); @@ -195,9 +223,13 @@ abstract contract IntegrationBase is IntegrationConfig { assertTrue(curBitmap == prevBitmap.plus(quorumsAdded), err); } - function assert_Snap_Deregistered_FromQuorums(User user, bytes memory quorums, string memory err) internal { + function assert_Snap_Deregistered_FromQuorums( + User user, + bytes memory quorums, + string memory err + ) internal { bytes32 operatorId = user.operatorId(); - uint quorumsRemoved = quorums.orderedBytesArrayToBitmap(); + uint256 quorumsRemoved = quorums.orderedBytesArrayToBitmap(); uint192 curBitmap = _getQuorumBitmap(operatorId); uint192 prevBitmap = _getPrevQuorumBitmap(operatorId); @@ -213,8 +245,8 @@ abstract contract IntegrationBase is IntegrationConfig { } function assert_Snap_Unchanged_OperatorInfo(User user, string memory err) internal { - IRegistryCoordinator.OperatorInfo memory curInfo = _getOperatorInfo(user); - IRegistryCoordinator.OperatorInfo memory prevInfo = _getPrevOperatorInfo(user); + ISlashingRegistryCoordinatorTypes.OperatorInfo memory curInfo = _getOperatorInfo(user); + ISlashingRegistryCoordinatorTypes.OperatorInfo memory prevInfo = _getPrevOperatorInfo(user); assertEq(prevInfo.operatorId, curInfo.operatorId, err); assertTrue(prevInfo.status == curInfo.status, err); @@ -222,7 +254,7 @@ abstract contract IntegrationBase is IntegrationConfig { function assert_Snap_Unchanged_QuorumBitmap(User user, string memory err) internal { bytes32 operatorId = user.operatorId(); - + uint192 curBitmap = _getQuorumBitmap(operatorId); uint192 prevBitmap = _getPrevQuorumBitmap(operatorId); @@ -230,26 +262,34 @@ abstract contract IntegrationBase is IntegrationConfig { } /// @dev Check that the user's pubkey was added to each quorum's apk - function assert_Snap_Added_QuorumApk(User user, bytes memory quorums, string memory err) internal { + function assert_Snap_Added_QuorumApk( + User user, + bytes memory quorums, + string memory err + ) internal { BN254.G1Point memory userPubkey = user.pubkeyG1(); BN254.G1Point[] memory curApks = _getQuorumApks(quorums); BN254.G1Point[] memory prevApks = _getPrevQuorumApks(quorums); - for (uint i = 0; i < quorums.length; i++) { + for (uint256 i = 0; i < quorums.length; i++) { BN254.G1Point memory expectedApk = prevApks[i].plus(userPubkey); assertEq(expectedApk.X, curApks[i].X, err); assertEq(expectedApk.Y, curApks[i].Y, err); } } - function assert_Snap_Removed_QuorumApk(User user, bytes memory quorums, string memory err) internal { + function assert_Snap_Removed_QuorumApk( + User user, + bytes memory quorums, + string memory err + ) internal { BN254.G1Point memory userPubkey = user.pubkeyG1(); BN254.G1Point[] memory curApks = _getQuorumApks(quorums); BN254.G1Point[] memory prevApks = _getPrevQuorumApks(quorums); - for (uint i = 0; i < quorums.length; i++) { + for (uint256 i = 0; i < quorums.length; i++) { BN254.G1Point memory expectedApk = prevApks[i].plus(userPubkey.negate()); assertEq(expectedApk.X, curApks[i].X, err); assertEq(expectedApk.Y, curApks[i].Y, err); @@ -260,7 +300,7 @@ abstract contract IntegrationBase is IntegrationConfig { BN254.G1Point[] memory curApks = _getQuorumApks(quorums); BN254.G1Point[] memory prevApks = _getPrevQuorumApks(quorums); - for (uint i = 0; i < quorums.length; i++) { + for (uint256 i = 0; i < quorums.length; i++) { assertEq(curApks[i].X, prevApks[i].X, err); assertEq(curApks[i].Y, prevApks[i].Y, err); } @@ -275,7 +315,11 @@ abstract contract IntegrationBase is IntegrationConfig { string memory err ) internal { // Sanity check input lengths - assertEq(churnedOperators.length, churnedQuorums.length, "assert_Snap_Churned_QuorumApk: input length mismatch"); + assertEq( + churnedOperators.length, + churnedQuorums.length, + "assert_Snap_Churned_QuorumApk: input length mismatch" + ); BN254.G1Point memory incomingPubkey = incomingOperator.pubkeyG1(); @@ -285,14 +329,12 @@ abstract contract IntegrationBase is IntegrationConfig { // For each churned quorum, check: // - that the corresponding churned operator pubkey was removed // - ... AND that the incomingOperator pubkey was added - for (uint i = 0; i < churnedQuorums.length; i++) { + for (uint256 i = 0; i < churnedQuorums.length; i++) { BN254.G1Point memory churnedPubkey = churnedOperators[i].pubkeyG1(); - BN254.G1Point memory expectedApk - = prevApks[i] - .plus(churnedPubkey.negate()) - .plus(incomingPubkey); - + BN254.G1Point memory expectedApk = + prevApks[i].plus(churnedPubkey.negate()).plus(incomingPubkey); + assertEq(expectedApk.X, curApks[i].X, err); assertEq(expectedApk.Y, curApks[i].Y, err); } @@ -300,7 +342,7 @@ abstract contract IntegrationBase is IntegrationConfig { /// @dev Check that specific weights were added to the operator and total stakes for each quorum function assert_Snap_AddedWeightToStakes( - User user, + User user, bytes memory quorums, uint96[] memory addedWeights, string memory err @@ -311,7 +353,7 @@ abstract contract IntegrationBase is IntegrationConfig { uint96[] memory curTotalStakes = _getTotalStakes(quorums); uint96[] memory prevTotalStakes = _getPrevTotalStakes(quorums); - for (uint i = 0; i < quorums.length; i++) { + for (uint256 i = 0; i < quorums.length; i++) { assertEq(curOperatorStakes[i], prevOperatorStakes[i] + addedWeights[i], err); assertEq(curTotalStakes[i], prevTotalStakes[i] + addedWeights[i], err); } @@ -320,7 +362,7 @@ abstract contract IntegrationBase is IntegrationConfig { /// @dev Check that the operator's stake weight was added to the operator and total /// stakes for each quorum function assert_Snap_Added_OperatorWeight( - User user, + User user, bytes memory quorums, string memory err ) internal { @@ -338,12 +380,16 @@ abstract contract IntegrationBase is IntegrationConfig { string memory err ) internal { // Sanity check input lengths - assertEq(churnedOperators.length, churnedQuorums.length, "assert_Snap_Churned_OperatorWeight: input length mismatch"); + assertEq( + churnedOperators.length, + churnedQuorums.length, + "assert_Snap_Churned_OperatorWeight: input length mismatch" + ); // Get weights added and removed for each quorum uint96[] memory addedWeights = _getWeights(incomingOperator, churnedQuorums); uint96[] memory removedWeights = new uint96[](churnedOperators.length); - for (uint i = 0; i < churnedOperators.length; i++) { + for (uint256 i = 0; i < churnedOperators.length; i++) { removedWeights[i] = _getWeight(uint8(churnedQuorums[i]), churnedOperators[i]); } @@ -355,21 +401,23 @@ abstract contract IntegrationBase is IntegrationConfig { // For each quorum, check that the incoming operator's individual stake was increased by addedWeights // and that the total stake is plus addedWeights and minus removedWeights - for (uint i = 0; i < churnedQuorums.length; i++) { + for (uint256 i = 0; i < churnedQuorums.length; i++) { assertEq(curIncomingOpStakes[i], prevIncomingOpStakes[i] + addedWeights[i], err); - assertEq(curTotalStakes[i], prevTotalStakes[i] + addedWeights[i] - removedWeights[i], err); + assertEq( + curTotalStakes[i], prevTotalStakes[i] + addedWeights[i] - removedWeights[i], err + ); } } function assert_Snap_Unchanged_OperatorStake( - User user, + User user, bytes memory quorums, string memory err ) internal { uint96[] memory curStakes = _getStakes(user, quorums); uint96[] memory prevStakes = _getPrevStakes(user, quorums); - for (uint i = 0; i < quorums.length; i++) { + for (uint256 i = 0; i < quorums.length; i++) { assertEq(curStakes[i], prevStakes[i], err); } } @@ -383,7 +431,7 @@ abstract contract IntegrationBase is IntegrationConfig { uint96[] memory curWeights = _getWeights(user, quorums); uint96[] memory prevWeights = _getPrevWeights(user, quorums); - for (uint i = 0; i < quorums.length; i++) { + for (uint256 i = 0; i < quorums.length; i++) { assertTrue(curWeights[i] >= prevWeights[i], err); } } @@ -397,7 +445,7 @@ abstract contract IntegrationBase is IntegrationConfig { uint96[] memory curWeights = _getWeights(user, quorums); uint96[] memory prevWeights = _getPrevWeights(user, quorums); - for (uint i = 0; i < quorums.length; i++) { + for (uint256 i = 0; i < quorums.length; i++) { assertTrue(curWeights[i] <= prevWeights[i], err); } } @@ -410,7 +458,7 @@ abstract contract IntegrationBase is IntegrationConfig { uint96[] memory curWeights = _getWeights(user, quorums); uint96[] memory prevWeights = _getPrevWeights(user, quorums); - for (uint i = 0; i < quorums.length; i++) { + for (uint256 i = 0; i < quorums.length; i++) { assertEq(curWeights[i], prevWeights[i], err); } } @@ -425,13 +473,13 @@ abstract contract IntegrationBase is IntegrationConfig { uint96[] memory curTotalStakes = _getTotalStakes(quorums); uint96[] memory prevTotalStakes = _getPrevTotalStakes(quorums); - for (uint i = 0; i < quorums.length; i++) { + for (uint256 i = 0; i < quorums.length; i++) { assertEq(curTotalStakes[i], prevTotalStakes[i] + addedWeights[i], err); } } function assert_Snap_Removed_TotalStake( - User user, + User user, bytes memory quorums, string memory err ) internal { @@ -441,19 +489,16 @@ abstract contract IntegrationBase is IntegrationConfig { uint96[] memory curTotalStakes = _getTotalStakes(quorums); uint96[] memory prevTotalStakes = _getPrevTotalStakes(quorums); - for (uint i = 0; i < quorums.length; i++) { + for (uint256 i = 0; i < quorums.length; i++) { assertEq(curTotalStakes[i], prevTotalStakes[i] - prevOperatorStakes[i], err); } } - function assert_Snap_Unchanged_TotalStake( - bytes memory quorums, - string memory err - ) internal { + function assert_Snap_Unchanged_TotalStake(bytes memory quorums, string memory err) internal { uint96[] memory curTotalStakes = _getTotalStakes(quorums); uint96[] memory prevTotalStakes = _getPrevTotalStakes(quorums); - for (uint i = 0; i < quorums.length; i++) { + for (uint256 i = 0; i < quorums.length; i++) { assertEq(curTotalStakes[i], prevTotalStakes[i], err); } } @@ -462,8 +507,8 @@ abstract contract IntegrationBase is IntegrationConfig { function assert_Snap_Added_OperatorCount(bytes memory quorums, string memory err) internal { uint32[] memory curOperatorCounts = _getOperatorCounts(quorums); uint32[] memory prevOperatorCounts = _getPrevOperatorCounts(quorums); - - for (uint i = 0; i < quorums.length; i++) { + + for (uint256 i = 0; i < quorums.length; i++) { assertEq(curOperatorCounts[i], prevOperatorCounts[i] + 1, err); } } @@ -471,17 +516,20 @@ abstract contract IntegrationBase is IntegrationConfig { function assert_Snap_Reduced_OperatorCount(bytes memory quorums, string memory err) internal { uint32[] memory curOperatorCounts = _getOperatorCounts(quorums); uint32[] memory prevOperatorCounts = _getPrevOperatorCounts(quorums); - - for (uint i = 0; i < quorums.length; i++) { + + for (uint256 i = 0; i < quorums.length; i++) { assertEq(curOperatorCounts[i], prevOperatorCounts[i] - 1, err); } } - function assert_Snap_Unchanged_OperatorCount(bytes memory quorums, string memory err) internal { + function assert_Snap_Unchanged_OperatorCount( + bytes memory quorums, + string memory err + ) internal { uint32[] memory curOperatorCounts = _getOperatorCounts(quorums); uint32[] memory prevOperatorCounts = _getPrevOperatorCounts(quorums); - - for (uint i = 0; i < quorums.length; i++) { + + for (uint256 i = 0; i < quorums.length; i++) { assertEq(curOperatorCounts[i], prevOperatorCounts[i], err); } } @@ -491,13 +539,13 @@ abstract contract IntegrationBase is IntegrationConfig { /// - that the operator is in the current list, but not the previous list function assert_Snap_Added_OperatorListEntry( User operator, - bytes memory quorums, + bytes memory quorums, string memory err ) internal { bytes32[][] memory curOperatorLists = _getOperatorLists(quorums); bytes32[][] memory prevOperatorLists = _getPrevOperatorLists(quorums); - for (uint i = 0; i < quorums.length; i++) { + for (uint256 i = 0; i < quorums.length; i++) { assertEq(curOperatorLists[i].length, prevOperatorLists[i].length + 1, err); assertTrue(_contains(curOperatorLists[i], operator), err); @@ -510,13 +558,13 @@ abstract contract IntegrationBase is IntegrationConfig { /// - that the operator is in the previous list, but not the current list function assert_Snap_Removed_OperatorListEntry( User operator, - bytes memory quorums, + bytes memory quorums, string memory err ) internal { bytes32[][] memory curOperatorLists = _getOperatorLists(quorums); bytes32[][] memory prevOperatorLists = _getPrevOperatorLists(quorums); - for (uint i = 0; i < quorums.length; i++) { + for (uint256 i = 0; i < quorums.length; i++) { assertEq(curOperatorLists[i].length, prevOperatorLists[i].length - 1, err); assertFalse(_contains(curOperatorLists[i], operator), err); @@ -524,7 +572,10 @@ abstract contract IntegrationBase is IntegrationConfig { } } - function assert_Snap_Unchanged_OperatorListEntry(bytes memory quorums, string memory err) internal { + function assert_Snap_Unchanged_OperatorListEntry( + bytes memory quorums, + string memory err + ) internal { bytes32[][] memory curOperatorLists = _getOperatorLists(quorums); bytes32[][] memory prevOperatorLists = _getPrevOperatorLists(quorums); @@ -541,16 +592,20 @@ abstract contract IntegrationBase is IntegrationConfig { function assert_Snap_Replaced_OperatorListEntries( User incomingOperator, User[] memory churnedOperators, - bytes memory churnedQuorums, + bytes memory churnedQuorums, string memory err ) internal { // Sanity check input lengths - assertEq(churnedOperators.length, churnedQuorums.length, "assert_Snap_Replaced_OperatorListEntries: input length mismatch"); - + assertEq( + churnedOperators.length, + churnedQuorums.length, + "assert_Snap_Replaced_OperatorListEntries: input length mismatch" + ); + bytes32[][] memory curOperatorLists = _getOperatorLists(churnedQuorums); bytes32[][] memory prevOperatorLists = _getPrevOperatorLists(churnedQuorums); - for (uint i = 0; i < churnedQuorums.length; i++) { + for (uint256 i = 0; i < churnedQuorums.length; i++) { assertEq(curOperatorLists[i].length, prevOperatorLists[i].length, err); // check incomingOperator was added @@ -563,25 +618,27 @@ abstract contract IntegrationBase is IntegrationConfig { } } - /******************************************************************************* - SNAPSHOT ASSERTIONS (CORE) - TIME TRAVELERS ONLY BEYOND THIS POINT - *******************************************************************************/ + /** + * + * SNAPSHOT ASSERTIONS (CORE) + * TIME TRAVELERS ONLY BEYOND THIS POINT + * + */ - /// @dev Check that the operator has `addedShares` additional operator shares + /// @dev Check that the operator has `addedShares` additional operator shares // for each strategy since the last snapshot function assert_Snap_Added_OperatorShares( - User operator, - IStrategy[] memory strategies, - uint[] memory addedShares, + User operator, + IStrategy[] memory strategies, + uint256[] memory addedShares, string memory err ) internal { - uint[] memory curShares = _getOperatorShares(operator, strategies); + uint256[] memory curShares = _getOperatorShares(operator, strategies); // Use timewarp to get previous operator shares - uint[] memory prevShares = _getPrevOperatorShares(operator, strategies); + uint256[] memory prevShares = _getPrevOperatorShares(operator, strategies); // For each strategy, check (prev + added == cur) - for (uint i = 0; i < strategies.length; i++) { + for (uint256 i = 0; i < strategies.length; i++) { assertEq(prevShares[i] + addedShares[i], curShares[i], err); } } @@ -589,17 +646,17 @@ abstract contract IntegrationBase is IntegrationConfig { /// @dev Check that the operator has `removedShares` fewer operator shares /// for each strategy since the last snapshot function assert_Snap_Removed_OperatorShares( - User operator, - IStrategy[] memory strategies, - uint[] memory removedShares, + User operator, + IStrategy[] memory strategies, + uint256[] memory removedShares, string memory err ) internal { - uint[] memory curShares = _getOperatorShares(operator, strategies); + uint256[] memory curShares = _getOperatorShares(operator, strategies); // Use timewarp to get previous operator shares - uint[] memory prevShares = _getPrevOperatorShares(operator, strategies); + uint256[] memory prevShares = _getPrevOperatorShares(operator, strategies); // For each strategy, check (prev - removed == cur) - for (uint i = 0; i < strategies.length; i++) { + for (uint256 i = 0; i < strategies.length; i++) { assertEq(prevShares[i] - removedShares[i], curShares[i], err); } } @@ -607,17 +664,17 @@ abstract contract IntegrationBase is IntegrationConfig { /// @dev Check that the staker has `addedShares` additional delegatable shares /// for each strategy since the last snapshot function assert_Snap_Added_StakerShares( - User staker, - IStrategy[] memory strategies, - uint[] memory addedShares, + User staker, + IStrategy[] memory strategies, + uint256[] memory addedShares, string memory err ) internal { - uint[] memory curShares = _getStakerShares(staker, strategies); + uint256[] memory curShares = _getStakerShares(staker, strategies); // Use timewarp to get previous staker shares - uint[] memory prevShares = _getPrevStakerShares(staker, strategies); + uint256[] memory prevShares = _getPrevStakerShares(staker, strategies); // For each strategy, check (prev + added == cur) - for (uint i = 0; i < strategies.length; i++) { + for (uint256 i = 0; i < strategies.length; i++) { assertEq(prevShares[i] + addedShares[i], curShares[i], err); } } @@ -625,61 +682,65 @@ abstract contract IntegrationBase is IntegrationConfig { /// @dev Check that the staker has `removedShares` fewer delegatable shares /// for each strategy since the last snapshot function assert_Snap_Removed_StakerShares( - User staker, - IStrategy[] memory strategies, - uint[] memory removedShares, + User staker, + IStrategy[] memory strategies, + uint256[] memory removedShares, string memory err ) internal { - uint[] memory curShares = _getStakerShares(staker, strategies); + uint256[] memory curShares = _getStakerShares(staker, strategies); // Use timewarp to get previous staker shares - uint[] memory prevShares = _getPrevStakerShares(staker, strategies); + uint256[] memory prevShares = _getPrevStakerShares(staker, strategies); // For each strategy, check (prev - removed == cur) - for (uint i = 0; i < strategies.length; i++) { + for (uint256 i = 0; i < strategies.length; i++) { assertEq(prevShares[i] - removedShares[i], curShares[i], err); } } function assert_Snap_Added_QueuedWithdrawals( - User staker, + User staker, IDelegationManager.Withdrawal[] memory withdrawals, string memory err ) internal { - uint curQueuedWithdrawals = _getCumulativeWithdrawals(staker); + uint256 curQueuedWithdrawals = _getCumulativeWithdrawals(staker); // Use timewarp to get previous cumulative withdrawals - uint prevQueuedWithdrawals = _getPrevCumulativeWithdrawals(staker); + uint256 prevQueuedWithdrawals = _getPrevCumulativeWithdrawals(staker); assertEq(prevQueuedWithdrawals + withdrawals.length, curQueuedWithdrawals, err); } - function assert_Snap_Added_QueuedWithdrawal( - User staker, - string memory err - ) internal { - uint curQueuedWithdrawal = _getCumulativeWithdrawals(staker); + function assert_Snap_Added_QueuedWithdrawal(User staker, string memory err) internal { + uint256 curQueuedWithdrawal = _getCumulativeWithdrawals(staker); // Use timewarp to get previous cumulative withdrawals - uint prevQueuedWithdrawal = _getPrevCumulativeWithdrawals(staker); + uint256 prevQueuedWithdrawal = _getPrevCumulativeWithdrawals(staker); assertEq(prevQueuedWithdrawal + 1, curQueuedWithdrawal, err); } - /******************************************************************************* - UTILITY METHODS - *******************************************************************************/ - - function _calcRemaining(bytes memory start, bytes memory removed) internal pure returns (bytes memory) { - uint startBM = start.orderedBytesArrayToBitmap(); - uint removeBM = removed.orderedBytesArrayToBitmap(); + /** + * + * UTILITY METHODS + * + */ + function _calcRemaining( + bytes memory start, + bytes memory removed + ) internal pure returns (bytes memory) { + uint256 startBM = start.orderedBytesArrayToBitmap(); + uint256 removeBM = removed.orderedBytesArrayToBitmap(); return startBM.minus(removeBM).bitmapToBytesArray(); } /// @dev For some strategies/underlying token balances, calculate the expected shares received /// from depositing all tokens - function _calculateExpectedShares(IStrategy[] memory strategies, uint[] memory tokenBalances) internal returns (uint[] memory) { - uint[] memory expectedShares = new uint[](strategies.length); + function _calculateExpectedShares( + IStrategy[] memory strategies, + uint256[] memory tokenBalances + ) internal returns (uint256[] memory) { + uint256[] memory expectedShares = new uint256[](strategies.length); - for (uint i = 0; i < strategies.length; i++) { + for (uint256 i = 0; i < strategies.length; i++) { IStrategy strat = strategies[i]; expectedShares[i] = strat.underlyingToShares(tokenBalances[i]); @@ -690,10 +751,13 @@ abstract contract IntegrationBase is IntegrationConfig { /// @dev For some strategies/underlying token balances, calculate the expected shares received /// from depositing all tokens - function _calculateExpectedTokens(IStrategy[] memory strategies, uint[] memory shares) internal returns (uint[] memory) { - uint[] memory expectedTokens = new uint[](strategies.length); + function _calculateExpectedTokens( + IStrategy[] memory strategies, + uint256[] memory shares + ) internal returns (uint256[] memory) { + uint256[] memory expectedTokens = new uint256[](strategies.length); - for (uint i = 0; i < strategies.length; i++) { + for (uint256 i = 0; i < strategies.length; i++) { IStrategy strat = strategies[i]; expectedTokens[i] = strat.sharesToUnderlying(shares[i]); @@ -703,10 +767,12 @@ abstract contract IntegrationBase is IntegrationConfig { } /// @dev Converts a list of strategies to underlying tokens - function _getUnderlyingTokens(IStrategy[] memory strategies) internal view returns (IERC20[] memory) { + function _getUnderlyingTokens( + IStrategy[] memory strategies + ) internal view returns (IERC20[] memory) { IERC20[] memory tokens = new IERC20[](strategies.length); - for (uint i = 0; i < tokens.length; i++) { + for (uint256 i = 0; i < tokens.length; i++) { IStrategy strat = strategies[i]; tokens[i] = strat.underlyingToken(); @@ -718,7 +784,7 @@ abstract contract IntegrationBase is IntegrationConfig { function _contains(bytes32[] memory operatorIds, User operator) internal view returns (bool) { bytes32 checkId = operator.operatorId(); - for (uint i = 0; i < operatorIds.length; i++) { + for (uint256 i = 0; i < operatorIds.length; i++) { if (operatorIds[i] == checkId) { return true; } @@ -727,12 +793,13 @@ abstract contract IntegrationBase is IntegrationConfig { return false; } - /******************************************************************************* - TIMEWARP GETTERS - *******************************************************************************/ - + /** + * + * TIMEWARP GETTERS + * + */ modifier timewarp() { - uint curState = timeMachine.warpToLast(); + uint256 curState = timeMachine.warpToLast(); _; timeMachine.warpToPresent(curState); } @@ -741,17 +808,20 @@ abstract contract IntegrationBase is IntegrationConfig { /// @dev Uses timewarp modifier to get operator shares at the last snapshot function _getPrevOperatorShares( - User operator, + User operator, IStrategy[] memory strategies - ) internal timewarp() returns (uint[] memory) { + ) internal timewarp returns (uint256[] memory) { return _getOperatorShares(operator, strategies); } /// @dev Looks up each strategy and returns a list of the operator's shares - function _getOperatorShares(User operator, IStrategy[] memory strategies) internal view returns (uint[] memory) { - uint[] memory curShares = new uint[](strategies.length); + function _getOperatorShares( + User operator, + IStrategy[] memory strategies + ) internal view returns (uint256[] memory) { + uint256[] memory curShares = new uint256[](strategies.length); - for (uint i = 0; i < strategies.length; i++) { + for (uint256 i = 0; i < strategies.length; i++) { curShares[i] = delegationManager.operatorShares(address(operator), strategies[i]); } @@ -760,64 +830,83 @@ abstract contract IntegrationBase is IntegrationConfig { /// @dev Uses timewarp modifier to get staker shares at the last snapshot function _getPrevStakerShares( - User staker, + User staker, IStrategy[] memory strategies - ) internal timewarp() returns (uint[] memory) { + ) internal timewarp returns (uint256[] memory) { return _getStakerShares(staker, strategies); } /// @dev Looks up each strategy and returns a list of the staker's shares - function _getStakerShares(User staker, IStrategy[] memory strategies) internal view returns (uint[] memory) { - uint[] memory curShares = new uint[](strategies.length); + function _getStakerShares( + User staker, + IStrategy[] memory strategies + ) internal view returns (uint256[] memory) { + uint256[] memory curShares = new uint256[](strategies.length); - for (uint i = 0; i < strategies.length; i++) { + for (uint256 i = 0; i < strategies.length; i++) { IStrategy strat = strategies[i]; - curShares[i] = strategyManager.stakerStrategyShares(address(staker), strat); + curShares[i] = strategyManager.stakerDepositShares(address(staker), strat); } return curShares; } - function _getPrevCumulativeWithdrawals(User staker) internal timewarp() returns (uint) { + function _getPrevCumulativeWithdrawals( + User staker + ) internal timewarp returns (uint256) { return _getCumulativeWithdrawals(staker); } - function _getCumulativeWithdrawals(User staker) internal view returns (uint) { + function _getCumulativeWithdrawals( + User staker + ) internal view returns (uint256) { return delegationManager.cumulativeWithdrawalsQueued(address(staker)); } - + /// RegistryCoordinator: - function _getOperatorInfo(User user) internal view returns (IRegistryCoordinator.OperatorInfo memory) { + function _getOperatorInfo( + User user + ) internal view returns (ISlashingRegistryCoordinatorTypes.OperatorInfo memory) { return registryCoordinator.getOperator(address(user)); } - function _getPrevOperatorInfo(User user) internal timewarp() returns (IRegistryCoordinator.OperatorInfo memory) { + function _getPrevOperatorInfo( + User user + ) internal timewarp returns (ISlashingRegistryCoordinatorTypes.OperatorInfo memory) { return _getOperatorInfo(user); } - function _getQuorumBitmap(bytes32 operatorId) internal view returns (uint192) { + function _getQuorumBitmap( + bytes32 operatorId + ) internal view returns (uint192) { return registryCoordinator.getCurrentQuorumBitmap(operatorId); } - function _getPrevQuorumBitmap(bytes32 operatorId) internal timewarp() returns (uint192) { + function _getPrevQuorumBitmap( + bytes32 operatorId + ) internal timewarp returns (uint192) { return _getQuorumBitmap(operatorId); } /// BLSApkRegistry: - function _getQuorumApks(bytes memory quorums) internal view returns (BN254.G1Point[] memory) { + function _getQuorumApks( + bytes memory quorums + ) internal view returns (BN254.G1Point[] memory) { BN254.G1Point[] memory apks = new BN254.G1Point[](quorums.length); - for (uint i = 0; i < quorums.length; i++) { + for (uint256 i = 0; i < quorums.length; i++) { apks[i] = blsApkRegistry.getApk(uint8(quorums[i])); } return apks; } - function _getPrevQuorumApks(bytes memory quorums) internal timewarp() returns (BN254.G1Point[] memory) { + function _getPrevQuorumApks( + bytes memory quorums + ) internal timewarp returns (BN254.G1Point[] memory) { return _getQuorumApks(quorums); } @@ -827,28 +916,34 @@ abstract contract IntegrationBase is IntegrationConfig { bytes32 operatorId = user.operatorId(); uint96[] memory stakes = new uint96[](quorums.length); - for (uint i = 0; i < quorums.length; i++) { + for (uint256 i = 0; i < quorums.length; i++) { stakes[i] = stakeRegistry.getCurrentStake(operatorId, uint8(quorums[i])); } return stakes; } - function _getPrevStakes(User user, bytes memory quorums) internal timewarp() returns (uint96[] memory) { + function _getPrevStakes( + User user, + bytes memory quorums + ) internal timewarp returns (uint96[] memory) { return _getStakes(user, quorums); } function _getWeights(User user, bytes memory quorums) internal view returns (uint96[] memory) { uint96[] memory weights = new uint96[](quorums.length); - for (uint i = 0; i < quorums.length; i++) { + for (uint256 i = 0; i < quorums.length; i++) { weights[i] = stakeRegistry.weightOfOperatorForQuorum(uint8(quorums[i]), address(user)); } return weights; } - function _getPrevWeights(User user, bytes memory quorums) internal timewarp() returns (uint96[] memory) { + function _getPrevWeights( + User user, + bytes memory quorums + ) internal timewarp returns (uint96[] memory) { return _getWeights(user, quorums); } @@ -860,7 +955,7 @@ abstract contract IntegrationBase is IntegrationConfig { uint96[] memory addedWeights = new uint96[](quorums.length); - for (uint i = 0; i < quorums.length; i++) { + for (uint256 i = 0; i < quorums.length; i++) { uint96 curWeight = curWeights[i]; uint96 prevWeight = prevWeights[i]; @@ -874,47 +969,60 @@ abstract contract IntegrationBase is IntegrationConfig { return addedWeights; } - function _getTotalStakes(bytes memory quorums) internal view returns (uint96[] memory) { + function _getTotalStakes( + bytes memory quorums + ) internal view returns (uint96[] memory) { uint96[] memory stakes = new uint96[](quorums.length); - for (uint i = 0; i < quorums.length; i++) { + for (uint256 i = 0; i < quorums.length; i++) { stakes[i] = stakeRegistry.getCurrentTotalStake(uint8(quorums[i])); } - + return stakes; } - function _getPrevTotalStakes(bytes memory quorums) internal timewarp() returns (uint96[] memory) { + function _getPrevTotalStakes( + bytes memory quorums + ) internal timewarp returns (uint96[] memory) { return _getTotalStakes(quorums); } /// IndexRegistry: - function _getOperatorCounts(bytes memory quorums) internal view returns (uint32[] memory) { + function _getOperatorCounts( + bytes memory quorums + ) internal view returns (uint32[] memory) { uint32[] memory operatorCounts = new uint32[](quorums.length); - for (uint i = 0; i < quorums.length; i++) { + for (uint256 i = 0; i < quorums.length; i++) { operatorCounts[i] = indexRegistry.totalOperatorsForQuorum(uint8(quorums[i])); } return operatorCounts; } - function _getPrevOperatorCounts(bytes memory quorums) internal timewarp() returns (uint32[] memory) { + function _getPrevOperatorCounts( + bytes memory quorums + ) internal timewarp returns (uint32[] memory) { return _getOperatorCounts(quorums); } - function _getOperatorLists(bytes memory quorums) internal view returns (bytes32[][] memory) { + function _getOperatorLists( + bytes memory quorums + ) internal view returns (bytes32[][] memory) { bytes32[][] memory operatorLists = new bytes32[][](quorums.length); - for (uint i = 0; i < quorums.length; i++) { - operatorLists[i] = indexRegistry.getOperatorListAtBlockNumber(uint8(quorums[i]), uint32(block.number)); + for (uint256 i = 0; i < quorums.length; i++) { + operatorLists[i] = + indexRegistry.getOperatorListAtBlockNumber(uint8(quorums[i]), uint32(block.number)); } return operatorLists; } - function _getPrevOperatorLists(bytes memory quorums) internal timewarp() returns (bytes32[][] memory) { + function _getPrevOperatorLists( + bytes memory quorums + ) internal timewarp returns (bytes32[][] memory) { return _getOperatorLists(quorums); } } diff --git a/test/integration/IntegrationChecks.t.sol b/test/integration/IntegrationChecks.t.sol index b54f8568..ba9c3d23 100644 --- a/test/integration/IntegrationChecks.t.sol +++ b/test/integration/IntegrationChecks.t.sol @@ -1,79 +1,80 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "test/integration/IntegrationBase.t.sol"; import "test/integration/User.t.sol"; /// @notice Contract that provides utility functions to reuse common test blocks & checks contract IntegrationChecks is IntegrationBase { - using BitmapUtils for *; - /******************************************************************************* - PRE-REGISTER CHECKS - *******************************************************************************/ - + /** + * + * PRE-REGISTER CHECKS + * + */ function check_Never_Registered( User operator ) internal { - _log("check_Never_Registered", operator); + _log("check_Never_Registered", operator); // RegistryCoordinator - assert_HasNoOperatorInfo(operator, - "operator should have empty id and NEVER_REGISTERED status"); - assert_EmptyQuorumBitmap(operator, - "operator already has bits in quorum bitmap"); + assert_HasNoOperatorInfo( + operator, "operator should have empty id and NEVER_REGISTERED status" + ); + assert_EmptyQuorumBitmap(operator, "operator already has bits in quorum bitmap"); // BLSApkRegistry - assert_NoRegisteredPubkey(operator, - "operator already has a registered pubkey"); + assert_NoRegisteredPubkey(operator, "operator already has a registered pubkey"); // DelegationManager - assert_NotRegisteredToAVS(operator, - "operator should not be registered to the AVS"); + assert_NotRegisteredToAVS(operator, "operator should not be registered to the AVS"); } - /******************************************************************************* - POST-REGISTER CHECKS - *******************************************************************************/ - - function check_Register_State( - User operator, - bytes memory quorums - ) internal { + /** + * + * POST-REGISTER CHECKS + * + */ + function check_Register_State(User operator, bytes memory quorums) internal { _log("check_Register_State", operator); // RegistryCoordinator - assert_HasOperatorInfoWithId(operator, - "operatorInfo should have operatorId"); - assert_HasRegisteredStatus(operator, - "operatorInfo status should be REGISTERED"); - assert_IsRegisteredForQuorums(operator, quorums, - "current operator bitmap should include quorums"); - assert_Snap_Registered_ForQuorums(operator, quorums, - "operator did not register for all quorums"); + assert_HasOperatorInfoWithId(operator, "operatorInfo should have operatorId"); + assert_HasRegisteredStatus(operator, "operatorInfo status should be REGISTERED"); + assert_IsRegisteredForQuorums( + operator, quorums, "current operator bitmap should include quorums" + ); + assert_Snap_Registered_ForQuorums( + operator, quorums, "operator did not register for all quorums" + ); // BLSApkRegistry - assert_HasRegisteredPubkey(operator, - "operator should have registered a pubkey"); - assert_Snap_Added_QuorumApk(operator, quorums, - "operator pubkey should have been added to each quorum apk"); - + assert_HasRegisteredPubkey(operator, "operator should have registered a pubkey"); + assert_Snap_Added_QuorumApk( + operator, quorums, "operator pubkey should have been added to each quorum apk" + ); + // StakeRegistry - assert_HasAtLeastMinimumStake(operator, quorums, - "operator should have at least the minimum stake in each quorum"); - assert_Snap_Added_OperatorWeight(operator, quorums, - "failed to add operator weight to operator and total stake in each quorum"); + assert_HasAtLeastMinimumStake( + operator, quorums, "operator should have at least the minimum stake in each quorum" + ); + assert_Snap_Added_OperatorWeight( + operator, + quorums, + "failed to add operator weight to operator and total stake in each quorum" + ); // IndexRegistry - assert_Snap_Added_OperatorCount(quorums, - "total operator count should have increased for each quorum"); - assert_Snap_Added_OperatorListEntry(operator, quorums, - "operator list should have one more entry"); + assert_Snap_Added_OperatorCount( + quorums, "total operator count should have increased for each quorum" + ); + assert_Snap_Added_OperatorListEntry( + operator, quorums, "operator list should have one more entry" + ); // AVSDirectory - assert_IsRegisteredToAVS(operator, - "operator should be registered to AVS"); + assert_IsRegisteredToAVS(operator, "operator should be registered to AVS"); } /// @dev Combines many checks of check_Register_State and check_Deregister_State @@ -86,80 +87,115 @@ contract IntegrationChecks is IntegrationBase { ) internal { _log("check_Churned_State", incomingOperator); - bytes memory combinedQuorums = - churnedQuorums - .orderedBytesArrayToBitmap() - .plus(standardQuorums.orderedBytesArrayToBitmap()) - .bitmapToBytesArray(); + bytes memory combinedQuorums = churnedQuorums.orderedBytesArrayToBitmap().plus( + standardQuorums.orderedBytesArrayToBitmap() + ).bitmapToBytesArray(); // RegistryCoordinator - assert_HasOperatorInfoWithId(incomingOperator, - "operatorInfo should have operatorId"); - assert_HasRegisteredStatus(incomingOperator, - "operatorInfo status should be REGISTERED"); - assert_IsRegisteredForQuorums(incomingOperator, combinedQuorums, - "current operator bitmap should include quorums"); - assert_Snap_Registered_ForQuorums(incomingOperator, combinedQuorums, - "operator did not register for all quorums"); + assert_HasOperatorInfoWithId(incomingOperator, "operatorInfo should have operatorId"); + assert_HasRegisteredStatus(incomingOperator, "operatorInfo status should be REGISTERED"); + assert_IsRegisteredForQuorums( + incomingOperator, combinedQuorums, "current operator bitmap should include quorums" + ); + assert_Snap_Registered_ForQuorums( + incomingOperator, combinedQuorums, "operator did not register for all quorums" + ); // BLSApkRegistry - assert_HasRegisteredPubkey(incomingOperator, - "operator should have registered a pubkey"); - assert_Snap_Added_QuorumApk(incomingOperator, standardQuorums, - "operator pubkey should have been added to standardQuorums apks"); - assert_Snap_Churned_QuorumApk(incomingOperator, churnedOperators, churnedQuorums, - "operator pubkey should have been added and churned operator pubkeys should have been removed from apks"); - + assert_HasRegisteredPubkey(incomingOperator, "operator should have registered a pubkey"); + assert_Snap_Added_QuorumApk( + incomingOperator, + standardQuorums, + "operator pubkey should have been added to standardQuorums apks" + ); + assert_Snap_Churned_QuorumApk( + incomingOperator, + churnedOperators, + churnedQuorums, + "operator pubkey should have been added and churned operator pubkeys should have been removed from apks" + ); + // StakeRegistry - assert_HasAtLeastMinimumStake(incomingOperator, combinedQuorums, - "operator should have at least the minimum stake in each quorum"); - assert_Snap_Added_OperatorWeight(incomingOperator, standardQuorums, - "failed to add operator weight to operator and total stake in standardQuorums"); - assert_Snap_Churned_OperatorWeight(incomingOperator, churnedOperators, churnedQuorums, - "failed to add operator weight and remove churned weight from each quorum"); + assert_HasAtLeastMinimumStake( + incomingOperator, + combinedQuorums, + "operator should have at least the minimum stake in each quorum" + ); + assert_Snap_Added_OperatorWeight( + incomingOperator, + standardQuorums, + "failed to add operator weight to operator and total stake in standardQuorums" + ); + assert_Snap_Churned_OperatorWeight( + incomingOperator, + churnedOperators, + churnedQuorums, + "failed to add operator weight and remove churned weight from each quorum" + ); // IndexRegistry - assert_Snap_Added_OperatorCount(standardQuorums, - "total operator count should have increased for standardQuorums"); - assert_Snap_Unchanged_OperatorCount(churnedQuorums, - "total operator count should be the same for churnedQuorums"); - assert_Snap_Added_OperatorListEntry(incomingOperator, standardQuorums, - "operator list should have one more entry in standardQuorums"); - assert_Snap_Replaced_OperatorListEntries(incomingOperator, churnedOperators, churnedQuorums, - "operator list should contain incoming operator and should not contain churned operators"); + assert_Snap_Added_OperatorCount( + standardQuorums, "total operator count should have increased for standardQuorums" + ); + assert_Snap_Unchanged_OperatorCount( + churnedQuorums, "total operator count should be the same for churnedQuorums" + ); + assert_Snap_Added_OperatorListEntry( + incomingOperator, + standardQuorums, + "operator list should have one more entry in standardQuorums" + ); + assert_Snap_Replaced_OperatorListEntries( + incomingOperator, + churnedOperators, + churnedQuorums, + "operator list should contain incoming operator and should not contain churned operators" + ); // AVSDirectory - assert_IsRegisteredToAVS(incomingOperator, - "operator should be registered to AVS"); + assert_IsRegisteredToAVS(incomingOperator, "operator should be registered to AVS"); // Check that churnedOperators are deregistered from churnedQuorums - for (uint i = 0; i < churnedOperators.length; i++) { + for (uint256 i = 0; i < churnedOperators.length; i++) { User churnedOperator = churnedOperators[i]; bytes memory churnedQuorum = new bytes(1); churnedQuorum[0] = churnedQuorums[i]; // RegistryCoordinator - assert_HasOperatorInfoWithId(churnedOperator, - "churned operatorInfo should still have operatorId"); - assert_NotRegisteredForQuorums(churnedOperator, churnedQuorum, - "churned operator bitmap should not include churned quorums"); - assert_Snap_Deregistered_FromQuorums(churnedOperator, churnedQuorum, - "churned operator did not deregister from churned quorum"); + assert_HasOperatorInfoWithId( + churnedOperator, "churned operatorInfo should still have operatorId" + ); + assert_NotRegisteredForQuorums( + churnedOperator, + churnedQuorum, + "churned operator bitmap should not include churned quorums" + ); + assert_Snap_Deregistered_FromQuorums( + churnedOperator, + churnedQuorum, + "churned operator did not deregister from churned quorum" + ); // BLSApkRegistry - assert_HasRegisteredPubkey(churnedOperator, - "churned operator should still have a registered pubkey"); + assert_HasRegisteredPubkey( + churnedOperator, "churned operator should still have a registered pubkey" + ); // StakeRegistry - assert_NoExistingStake(churnedOperator, churnedQuorum, - "operator should no longer have stake in any quorums"); + assert_NoExistingStake( + churnedOperator, + churnedQuorum, + "operator should no longer have stake in any quorums" + ); } } - /******************************************************************************* - BALANCE UPDATE CHECKS - *******************************************************************************/ + /** + * + * BALANCE UPDATE CHECKS + * + */ /// @dev Validate state directly after the operator deposits into Eigenlayer core /// We're mostly checking that nothing in the middleware contracts has changed, @@ -168,76 +204,79 @@ contract IntegrationChecks is IntegrationBase { User operator, bytes memory quorums, IStrategy[] memory strategies, - uint[] memory tokenBalances + uint256[] memory tokenBalances ) internal { _log("check_Deposit_State", operator); // RegistryCoordinator - assert_Snap_Unchanged_OperatorInfo(operator, - "operator info should not have changed"); - assert_Snap_Unchanged_QuorumBitmap(operator, - "operators quorum bitmap should not have changed"); - + assert_Snap_Unchanged_OperatorInfo(operator, "operator info should not have changed"); + assert_Snap_Unchanged_QuorumBitmap( + operator, "operators quorum bitmap should not have changed" + ); + // BLSApkRegistry - assert_Snap_Unchanged_QuorumApk(quorums, - "quorum apks should not have changed"); + assert_Snap_Unchanged_QuorumApk(quorums, "quorum apks should not have changed"); // StakeRegistry - assert_Snap_Increased_OperatorWeight(operator, quorums, - "operator weight should not have decreased after deposit"); - assert_Snap_Unchanged_OperatorStake(operator, quorums, - "operator stake should be unchanged"); - assert_Snap_Unchanged_TotalStake(quorums, - "total stake should be unchanged"); + assert_Snap_Increased_OperatorWeight( + operator, quorums, "operator weight should not have decreased after deposit" + ); + assert_Snap_Unchanged_OperatorStake(operator, quorums, "operator stake should be unchanged"); + assert_Snap_Unchanged_TotalStake(quorums, "total stake should be unchanged"); // IndexRegistry - assert_Snap_Unchanged_OperatorCount(quorums, - "operator counts should not have changed"); - assert_Snap_Unchanged_OperatorListEntry(quorums, - "operator list should not have changed"); + assert_Snap_Unchanged_OperatorCount(quorums, "operator counts should not have changed"); + assert_Snap_Unchanged_OperatorListEntry(quorums, "operator list should not have changed"); // Core - assert_Snap_Added_OperatorShares(operator, strategies, tokenBalances, - "operator should have additional stake"); + assert_Snap_Added_OperatorShares( + operator, strategies, tokenBalances, "operator should have additional stake" + ); } /// @dev Checks that an operator's stake was successfully increased /// NOTE: This method assumes (and checks) that the operator already /// met the minimum stake before stake was added. function check_DepositUpdate_State( - User operator, - bytes memory quorums, + User operator, + bytes memory quorums, uint96[] memory addedWeights ) internal { _log("check_DepositUpdate_State", operator); // RegistryCoordinator - assert_Snap_Unchanged_OperatorInfo(operator, - "operator info should not have changed"); - assert_Snap_Unchanged_QuorumBitmap(operator, - "operators quorum bitmap should not have changed"); + assert_Snap_Unchanged_OperatorInfo(operator, "operator info should not have changed"); + assert_Snap_Unchanged_QuorumBitmap( + operator, "operators quorum bitmap should not have changed" + ); // BLSApkRegistry - assert_Snap_Unchanged_QuorumApk(quorums, - "quorum apks should not have changed"); + assert_Snap_Unchanged_QuorumApk(quorums, "quorum apks should not have changed"); // StakeRegistry - assert_HasAtLeastMinimumStake(operator, quorums, - "operator should have at least the minimum stake in each quorum"); - assert_Snap_Unchanged_OperatorWeight(operator, quorums, - "updateOperators should not effect operator weight calculation"); - assert_Snap_AddedWeightToStakes(operator, quorums, addedWeights, - "weights should have been added to operator and total stakes"); + assert_HasAtLeastMinimumStake( + operator, quorums, "operator should have at least the minimum stake in each quorum" + ); + assert_Snap_Unchanged_OperatorWeight( + operator, quorums, "updateOperators should not effect operator weight calculation" + ); + assert_Snap_AddedWeightToStakes( + operator, + quorums, + addedWeights, + "weights should have been added to operator and total stakes" + ); // IndexRegistry - assert_Snap_Unchanged_OperatorCount(quorums, - "total operator count should be unchanged for each quorum"); - assert_Snap_Unchanged_OperatorListEntry(quorums, - "operator list should be unchanged for each quorum"); + assert_Snap_Unchanged_OperatorCount( + quorums, "total operator count should be unchanged for each quorum" + ); + assert_Snap_Unchanged_OperatorListEntry( + quorums, "operator list should be unchanged for each quorum" + ); // AVSDirectory - assert_IsRegisteredToAVS(operator, - "operator should be registered to AVS"); + assert_IsRegisteredToAVS(operator, "operator should be registered to AVS"); } /// @dev Validate state directly after the operator exits from Eigenlayer core (by queuing withdrawals) @@ -245,149 +284,145 @@ contract IntegrationChecks is IntegrationBase { User operator, bytes memory quorums, IStrategy[] memory strategies, - uint[] memory shares + uint256[] memory shares ) internal { _log("check_Withdraw_State", operator); // RegistryCoordinator - assert_Snap_Unchanged_OperatorInfo(operator, - "operator info should not have changed"); - assert_Snap_Unchanged_QuorumBitmap(operator, - "operators quorum bitmap should not have changed"); - + assert_Snap_Unchanged_OperatorInfo(operator, "operator info should not have changed"); + assert_Snap_Unchanged_QuorumBitmap( + operator, "operators quorum bitmap should not have changed" + ); + // BLSApkRegistry - assert_Snap_Unchanged_QuorumApk(quorums, - "quorum apks should not have changed"); + assert_Snap_Unchanged_QuorumApk(quorums, "quorum apks should not have changed"); // StakeRegistry - assert_Snap_Decreased_OperatorWeight(operator, quorums, - "operator weight should not have increased after deposit"); - assert_Snap_Unchanged_OperatorStake(operator, quorums, - "operator stake should be unchanged"); - assert_Snap_Unchanged_TotalStake(quorums, - "total stake should be unchanged"); + assert_Snap_Decreased_OperatorWeight( + operator, quorums, "operator weight should not have increased after deposit" + ); + assert_Snap_Unchanged_OperatorStake(operator, quorums, "operator stake should be unchanged"); + assert_Snap_Unchanged_TotalStake(quorums, "total stake should be unchanged"); // IndexRegistry - assert_Snap_Unchanged_OperatorCount(quorums, - "operator counts should not have changed"); - assert_Snap_Unchanged_OperatorListEntry(quorums, - "operator list should not have changed"); + assert_Snap_Unchanged_OperatorCount(quorums, "operator counts should not have changed"); + assert_Snap_Unchanged_OperatorListEntry(quorums, "operator list should not have changed"); // Core - assert_Snap_Removed_OperatorShares(operator, strategies, shares, - "operator should have reduced stake"); + assert_Snap_Removed_OperatorShares( + operator, strategies, shares, "operator should have reduced stake" + ); } /// @dev Validate state when, after exiting from the core contracts, updateOperators is called /// We expect that the operator is completely deregistered. /// NOTE: This is a combination of check_Deregister_State and check_CompleteDeregister_State - function check_WithdrawUpdate_State( - User operator, - bytes memory quorums - ) internal { + function check_WithdrawUpdate_State(User operator, bytes memory quorums) internal { _log("check_WithdrawUpdate_State", operator); // RegistryCoordinator - assert_HasOperatorInfoWithId(operator, - "operatorInfo should still have operatorId"); - assert_EmptyQuorumBitmap(operator, - "operator should not have any bits in bitmap"); - assert_HasDeregisteredStatus(operator, - "operatorInfo status should be DEREGISTERED"); - assert_Snap_Deregistered_FromQuorums(operator, quorums, - "operator did not deregister from all quorums"); + assert_HasOperatorInfoWithId(operator, "operatorInfo should still have operatorId"); + assert_EmptyQuorumBitmap(operator, "operator should not have any bits in bitmap"); + assert_HasDeregisteredStatus(operator, "operatorInfo status should be DEREGISTERED"); + assert_Snap_Deregistered_FromQuorums( + operator, quorums, "operator did not deregister from all quorums" + ); // BLSApkRegistry - assert_HasRegisteredPubkey(operator, - "operator should still have a registered pubkey"); - assert_Snap_Removed_QuorumApk(operator, quorums, - "operator pubkey should have been subtracted from each quorum apk"); + assert_HasRegisteredPubkey(operator, "operator should still have a registered pubkey"); + assert_Snap_Removed_QuorumApk( + operator, quorums, "operator pubkey should have been subtracted from each quorum apk" + ); // StakeRegistry - assert_NoExistingStake(operator, quorums, - "operator should no longer have stake in any quorums"); - assert_Snap_Removed_TotalStake(operator, quorums, - "failed to remove operator weight from total stake for each quorum"); + assert_NoExistingStake( + operator, quorums, "operator should no longer have stake in any quorums" + ); + assert_Snap_Removed_TotalStake( + operator, quorums, "failed to remove operator weight from total stake for each quorum" + ); // IndexRegistry - assert_Snap_Reduced_OperatorCount(quorums, - "total operator count should have decreased for each quorum"); - assert_Snap_Removed_OperatorListEntry(operator, quorums, - "operator list should have one fewer entry"); + assert_Snap_Reduced_OperatorCount( + quorums, "total operator count should have decreased for each quorum" + ); + assert_Snap_Removed_OperatorListEntry( + operator, quorums, "operator list should have one fewer entry" + ); // AVSDirectory - assert_NotRegisteredToAVS(operator, - "operator should not be registered to the AVS"); + assert_NotRegisteredToAVS(operator, "operator should not be registered to the AVS"); } /// @dev Used to validate a stake update after NO core balance changes occured - function check_NoUpdate_State( - User operator, - bytes memory quorums - ) internal { + function check_NoUpdate_State(User operator, bytes memory quorums) internal { _log("check_NoChangeUpdate_State", operator); // RegistryCoordinator - assert_Snap_Unchanged_OperatorInfo(operator, - "operator info should not have changed"); - assert_Snap_Unchanged_QuorumBitmap(operator, - "operators quorum bitmap should not have changed"); + assert_Snap_Unchanged_OperatorInfo(operator, "operator info should not have changed"); + assert_Snap_Unchanged_QuorumBitmap( + operator, "operators quorum bitmap should not have changed" + ); // BLSApkRegistry - assert_Snap_Unchanged_QuorumApk(quorums, - "quorum apks should not have changed"); + assert_Snap_Unchanged_QuorumApk(quorums, "quorum apks should not have changed"); // StakeRegistry - assert_Snap_Unchanged_OperatorWeight(operator, quorums, - "operator weight should be unchanged"); - assert_Snap_Unchanged_OperatorStake(operator, quorums, - "operator stake should be unchanged"); - assert_Snap_Unchanged_TotalStake(quorums, - "total stake should be unchanged"); + assert_Snap_Unchanged_OperatorWeight( + operator, quorums, "operator weight should be unchanged" + ); + assert_Snap_Unchanged_OperatorStake(operator, quorums, "operator stake should be unchanged"); + assert_Snap_Unchanged_TotalStake(quorums, "total stake should be unchanged"); // IndexRegistry - assert_Snap_Unchanged_OperatorCount(quorums, - "total operator count should be unchanged for each quorum"); - assert_Snap_Unchanged_OperatorListEntry(quorums, - "operator list should be unchanged for each quorum"); + assert_Snap_Unchanged_OperatorCount( + quorums, "total operator count should be unchanged for each quorum" + ); + assert_Snap_Unchanged_OperatorListEntry( + quorums, "operator list should be unchanged for each quorum" + ); } - /******************************************************************************* - POST-DEREGISTER CHECKS - *******************************************************************************/ + /** + * + * POST-DEREGISTER CHECKS + * + */ /// @dev Check that the operator correctly deregistered from some quorums - function check_Deregister_State( - User operator, - bytes memory quorums - ) internal { + function check_Deregister_State(User operator, bytes memory quorums) internal { _log("check_Deregister_State", operator); // RegistryCoordinator - assert_HasOperatorInfoWithId(operator, - "operatorInfo should still have operatorId"); - assert_NotRegisteredForQuorums(operator, quorums, - "current operator bitmap should not include quorums"); - assert_Snap_Deregistered_FromQuorums(operator, quorums, - "operator did not deregister from all quorums"); + assert_HasOperatorInfoWithId(operator, "operatorInfo should still have operatorId"); + assert_NotRegisteredForQuorums( + operator, quorums, "current operator bitmap should not include quorums" + ); + assert_Snap_Deregistered_FromQuorums( + operator, quorums, "operator did not deregister from all quorums" + ); // BLSApkRegistry - assert_HasRegisteredPubkey(operator, - "operator should still have a registered pubkey"); - assert_Snap_Removed_QuorumApk(operator, quorums, - "operator pubkey should have been subtracted from each quorum apk"); + assert_HasRegisteredPubkey(operator, "operator should still have a registered pubkey"); + assert_Snap_Removed_QuorumApk( + operator, quorums, "operator pubkey should have been subtracted from each quorum apk" + ); // StakeRegistry - assert_NoExistingStake(operator, quorums, - "operator should no longer have stake in any quorums"); - assert_Snap_Removed_TotalStake(operator, quorums, - "failed to remove operator weight from total stake for each quorum"); + assert_NoExistingStake( + operator, quorums, "operator should no longer have stake in any quorums" + ); + assert_Snap_Removed_TotalStake( + operator, quorums, "failed to remove operator weight from total stake for each quorum" + ); // IndexRegistry - assert_Snap_Reduced_OperatorCount(quorums, - "total operator count should have decreased for each quorum"); - assert_Snap_Removed_OperatorListEntry(operator, quorums, - "operator list should have one fewer entry"); + assert_Snap_Reduced_OperatorCount( + quorums, "total operator count should have decreased for each quorum" + ); + assert_Snap_Removed_OperatorListEntry( + operator, quorums, "operator list should have one fewer entry" + ); } /// @dev Check that the operator correctly deregistered from ALL their quorums @@ -397,21 +432,19 @@ contract IntegrationChecks is IntegrationBase { _log("check_CompleteDeregister_State", operator); // RegistryCoordinator - assert_EmptyQuorumBitmap(operator, - "operator should not have any bits in bitmap"); - assert_HasOperatorInfoWithId(operator, - "operatorInfo should still have operatorId"); - assert_HasDeregisteredStatus(operator, - "operatorInfo status should be DEREGISTERED"); + assert_EmptyQuorumBitmap(operator, "operator should not have any bits in bitmap"); + assert_HasOperatorInfoWithId(operator, "operatorInfo should still have operatorId"); + assert_HasDeregisteredStatus(operator, "operatorInfo status should be DEREGISTERED"); // AVSDirectory - assert_NotRegisteredToAVS(operator, - "operator should not be registered to the AVS"); + assert_NotRegisteredToAVS(operator, "operator should not be registered to the AVS"); } - /******************************************************************************* - UTIL METHODS - *******************************************************************************/ + /** + * + * UTIL METHODS + * + */ /// example output: /// - check_Register_State(Operator0) diff --git a/test/integration/IntegrationConfig.t.sol b/test/integration/IntegrationConfig.t.sol index d1cd2ac6..92c394bb 100644 --- a/test/integration/IntegrationConfig.t.sol +++ b/test/integration/IntegrationConfig.t.sol @@ -1,15 +1,16 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "forge-std/Test.sol"; import "test/integration/IntegrationDeployer.t.sol"; import "test/ffi/util/G2Operations.sol"; import "test/integration/utils/BitmapStrings.t.sol"; +import {ISlashingRegistryCoordinatorTypes} from + "../../src/interfaces/ISlashingRegistryCoordinator.sol"; contract Constants { - - /// Quorum Config: + /// IECDSAStakeRegistryTypes.Quorum Config: /// @dev Default OperatorSetParam values used to initialize quorums /// NOTE: This means each quorum has an operator limit of MAX_OPERATOR_COUNT by default @@ -23,15 +24,14 @@ contract Constants { /// @dev Number of BLS keypairs to pregenerate. This is a slow operation, /// so I've set this to a low number. - uint constant NUM_GENERATED_OPERATORS = MAX_OPERATOR_COUNT + 5; + uint256 constant NUM_GENERATED_OPERATORS = MAX_OPERATOR_COUNT + 5; - uint constant MAX_QUORUM_COUNT = 192; // From RegistryCoordinator.MAX_QUORUM_COUNT + uint256 constant MAX_QUORUM_COUNT = 192; // From RegistryCoordinator.MAX_QUORUM_COUNT uint16 internal constant BIPS_DENOMINATOR = 10000; } contract IntegrationConfig is IntegrationDeployer, G2Operations, Constants { - using BitmapStrings for *; using Strings for *; using BN254 for *; @@ -48,42 +48,42 @@ contract IntegrationConfig is IntegrationDeployer, G2Operations, Constants { bytes minStakeFlags; bytes fillTypeFlags; - uint constant FLAG = 1; + uint256 constant FLAG = 1; /// @dev Flags for userTypes - uint constant DEFAULT = (FLAG << 0); - uint constant ALT_METHODS = (FLAG << 1); + uint256 constant DEFAULT = (FLAG << 0); + uint256 constant ALT_METHODS = (FLAG << 1); /// @dev Flags for numQuorums and numStrategies - uint constant ONE = (FLAG << 0); - uint constant TWO = (FLAG << 1); - uint constant MANY = (FLAG << 2); - uint constant FIFTEEN = (FLAG << 3); - uint constant TWENTY = (FLAG << 4); - uint constant TWENTYFIVE = (FLAG << 5); + uint256 constant ONE = (FLAG << 0); + uint256 constant TWO = (FLAG << 1); + uint256 constant MANY = (FLAG << 2); + uint256 constant FIFTEEN = (FLAG << 3); + uint256 constant TWENTY = (FLAG << 4); + uint256 constant TWENTYFIVE = (FLAG << 5); /// @dev Flags for minimumStake - uint constant NO_MINIMUM = (FLAG << 0); - uint constant HAS_MINIMUM = (FLAG << 1); + uint256 constant NO_MINIMUM = (FLAG << 0); + uint256 constant HAS_MINIMUM = (FLAG << 1); /// @dev Flags for fillTypes - uint constant EMPTY = (FLAG << 0); - uint constant SOME_FILL = (FLAG << 1); - uint constant FULL = (FLAG << 2); + uint256 constant EMPTY = (FLAG << 0); + uint256 constant SOME_FILL = (FLAG << 1); + uint256 constant FULL = (FLAG << 2); /// @dev Tracking variables for pregenerated BLS keypairs: /// (See _fetchKeypair) - uint fetchIdx = 0; - uint[] privKeys; - IBLSApkRegistry.PubkeyRegistrationParams[] pubkeys; + uint256 fetchIdx = 0; + uint256[] privKeys; + IBLSApkRegistryTypes.PubkeyRegistrationParams[] pubkeys; /// @dev Current initialized quorums are tracked here: - uint quorumCount; + uint256 quorumCount; uint192 quorumBitmap; bytes quorumArray; /// @dev Number of operators generated so far - uint numOperators = 0; + uint256 numOperators = 0; /// @dev current array of operatorIds registered so far per quorum. /// does not update and remove if an operator is deregistered however, used for testing updateOperatorsForQuorum mapping(uint8 => address[]) operatorsForQuorum; @@ -94,10 +94,10 @@ contract IntegrationConfig is IntegrationDeployer, G2Operations, Constants { * so this is the best way to speed things up when running multiple tests. */ constructor() { - for (uint i = 0; i < NUM_GENERATED_OPERATORS; i++) { - IBLSApkRegistry.PubkeyRegistrationParams memory pubkey; - uint privKey = uint(keccak256(abi.encodePacked(i + 1))); - + for (uint256 i = 0; i < NUM_GENERATED_OPERATORS; i++) { + IBLSApkRegistryTypes.PubkeyRegistrationParams memory pubkey; + uint256 privKey = uint256(keccak256(abi.encodePacked(i + 1))); + pubkey.pubkeyG1 = BN254.generatorG1().scalar_mul(privKey); pubkey.pubkeyG2 = G2Operations.mul(privKey); @@ -112,26 +112,26 @@ contract IntegrationConfig is IntegrationDeployer, G2Operations, Constants { */ struct QuorumConfig { /// @dev The number of quorums created during setup - uint numQuorums; // ONE | TWO | MANY + uint256 numQuorums; // ONE | TWO | MANY /// @dev The number of strategies a quorum will consider - uint numStrategies; // ONE | TWO | MANY + uint256 numStrategies; // ONE | TWO | MANY /// @dev Whether each quorum has a minimum stake /// NOTE: Minimum stake is currently MIN_BALANCE by default - uint minimumStake; // NO_MINIMUM | HAS_MINIMUM + uint256 minimumStake; // NO_MINIMUM | HAS_MINIMUM /// @dev Whether each quorum created is pre-populated with operators /// NOTE: Default - uint fillTypes; // EMPTY | SOME_FILL | FULL + uint256 fillTypes; // EMPTY | SOME_FILL | FULL } /** * @param _randomSeed Fuzz tests supply a random u24 as input - * @param _userTypes [DEFAULT | ALT_METHODS] - every time a user is generated, it will use these values + * @param _userTypes [DEFAULT | ALT_METHODS] - every time a user is generated, it will use these values * @param _quorumConfig Quorums that are created/initialized in this method will be configured according * to this struct. See `QuorumConfig` above for details on each parameter. */ function _configRand( uint24 _randomSeed, - uint _userTypes, + uint256 _userTypes, QuorumConfig memory _quorumConfig ) internal { emit log_named_uint("_configRand: set random seed to", _randomSeed); @@ -147,7 +147,9 @@ contract IntegrationConfig is IntegrationDeployer, G2Operations, Constants { // Sanity check config assertTrue(userFlags.length != 0, "_configRand: invalid _userTypes, no flags passed"); assertTrue(numQuorumFlags.length != 0, "_configRand: invalid numQuorums, no flags passed"); - assertTrue(numStrategyFlags.length != 0, "_configRand: invalid numStrategies, no flags passed"); + assertTrue( + numStrategyFlags.length != 0, "_configRand: invalid numStrategies, no flags passed" + ); assertTrue(minStakeFlags.length != 0, "_configRand: invalid minimumStake, no flags passed"); assertTrue(fillTypeFlags.length != 0, "_configRand: invalid fillTypes, no flags passed"); @@ -158,15 +160,16 @@ contract IntegrationConfig is IntegrationDeployer, G2Operations, Constants { emit log_named_uint("_configRand: number of quorums being initialized", quorumCount); // Default OperatorSetParams for all quorums - IRegistryCoordinator.OperatorSetParam memory operatorSet = IRegistryCoordinator.OperatorSetParam({ + ISlashingRegistryCoordinatorTypes.OperatorSetParam memory operatorSet = + ISlashingRegistryCoordinatorTypes.OperatorSetParam({ maxOperatorCount: MAX_OPERATOR_COUNT, kickBIPsOfOperatorStake: KICK_BIPS_OPERATOR_STAKE, kickBIPsOfTotalStake: KICK_BIPS_TOTAL_STAKE }); // Initialize each quorum - for (uint i = 0; i < quorumCount; i++) { - IStakeRegistry.StrategyParams[] memory strategyParams = _randStrategyParams(); + for (uint256 i = 0; i < quorumCount; i++) { + IStakeRegistryTypes.StrategyParams[] memory strategyParams = _randStrategyParams(); uint96 minimumStake = _randMinStake(); emit log_named_uint("_configRand: creating quorum", i); @@ -175,7 +178,7 @@ contract IntegrationConfig is IntegrationDeployer, G2Operations, Constants { emit log_named_uint("- Minimum stake", minimumStake); cheats.prank(registryCoordinatorOwner); - registryCoordinator.createQuorum({ + registryCoordinator.createTotalDelegatedStakeQuorum({ operatorSetParams: operatorSet, minimumStake: minimumStake, strategyParams: strategyParams @@ -183,15 +186,19 @@ contract IntegrationConfig is IntegrationDeployer, G2Operations, Constants { } // Decide how many operators to register for each quorum initially - uint initialOperators = _randInitialOperators(operatorSet); - emit log(string.concat("Registering ", initialOperators.toString(), " initial operators in each quorum")); + uint256 initialOperators = _randInitialOperators(operatorSet); + emit log( + string.concat( + "Registering ", initialOperators.toString(), " initial operators in each quorum" + ) + ); // For each initial operator, register for all quorums - for (uint j = 0; j < initialOperators; j++) { + for (uint256 j = 0; j < initialOperators; j++) { User operator = _newRandomOperator(); operator.registerOperator(quorumArray); - for (uint k = 0; k < quorumArray.length; k++) { + for (uint256 k = 0; k < quorumArray.length; k++) { uint8 quorum = uint8(quorumArray[k]); operatorsForQuorum[quorum].push(address(operator)); } @@ -205,29 +212,35 @@ contract IntegrationConfig is IntegrationDeployer, G2Operations, Constants { /** * Gen/Init methods: */ - function _newRandomOperator() internal returns (User) { string memory operatorName = string.concat("Operator", numOperators.toString()); numOperators++; - (User operator, IStrategy[] memory strategies, uint[] memory tokenBalances) = _randUser(operatorName); - + (User operator, IStrategy[] memory strategies, uint256[] memory tokenBalances) = + _randUser(operatorName); + operator.registerAsOperator(); operator.depositIntoEigenlayer(strategies, tokenBalances); - assertTrue(delegationManager.isOperator(address(operator)), "_newRandomOperator: operator should be registered"); + assertTrue( + delegationManager.isOperator(address(operator)), + "_newRandomOperator: operator should be registered" + ); return operator; } /// @dev Create a new user with token balances in ALL core-whitelisted strategies - function _randUser(string memory name) internal returns (User, IStrategy[] memory, uint[] memory) { + function _randUser( + string memory name + ) internal returns (User, IStrategy[] memory, uint256[] memory) { // Create User contract and give it a unique BLS keypair - (uint privKey, IBLSApkRegistry.PubkeyRegistrationParams memory pubkey) = _fetchKeypair(); + (uint256 privKey, IBLSApkRegistryTypes.PubkeyRegistrationParams memory pubkey) = + _fetchKeypair(); // Use userFlags to pick the kind of user to generate User user; - uint userType = _randValue(userFlags); + uint256 userType = _randValue(userFlags); if (userType == DEFAULT) { user = new User(name, privKey, pubkey); @@ -238,21 +251,23 @@ contract IntegrationConfig is IntegrationDeployer, G2Operations, Constants { emit log_named_string("_randUser: Created user", user.NAME()); - (IStrategy[] memory strategies, uint[] memory balances) = _dealRandTokens(user); + (IStrategy[] memory strategies, uint256[] memory balances) = _dealRandTokens(user); return (user, strategies, balances); } - function _dealRandTokens(User user) internal returns (IStrategy[] memory, uint[] memory) { + function _dealRandTokens( + User user + ) internal returns (IStrategy[] memory, uint256[] memory) { IStrategy[] memory strategies = new IStrategy[](allStrats.length); - uint[] memory balances = new uint[](allStrats.length); + uint256[] memory balances = new uint256[](allStrats.length); emit log_named_string("_dealRandTokens: dealing assets to", user.NAME()); - + // Deal the user a random balance between [MIN_BALANCE, MAX_BALANCE] for each existing strategy - for (uint i = 0; i < allStrats.length; i++) { + for (uint256 i = 0; i < allStrats.length; i++) { IStrategy strat = allStrats[i]; IERC20 underlyingToken = strat.underlyingToken(); - uint balance = _randUint({ min: MIN_BALANCE, max: MAX_BALANCE }); + uint256 balance = _randUint({min: MIN_BALANCE, max: MAX_BALANCE}); StdCheats.deal(address(underlyingToken), address(user), balance); strategies[i] = strat; @@ -262,17 +277,19 @@ contract IntegrationConfig is IntegrationDeployer, G2Operations, Constants { return (strategies, balances); } - function _dealMaxTokens(User user) internal returns (IStrategy[] memory, uint[] memory) { + function _dealMaxTokens( + User user + ) internal returns (IStrategy[] memory, uint256[] memory) { IStrategy[] memory strategies = new IStrategy[](allStrats.length); - uint[] memory balances = new uint[](allStrats.length); + uint256[] memory balances = new uint256[](allStrats.length); emit log_named_string("_dealMaxTokens: dealing assets to", user.NAME()); - + // Deal the user the 100 * MAX_BALANCE for each existing strategy - for (uint i = 0; i < allStrats.length; i++) { + for (uint256 i = 0; i < allStrats.length; i++) { IStrategy strat = allStrats[i]; IERC20 underlyingToken = strat.underlyingToken(); - uint balance = 100 * MAX_BALANCE; + uint256 balance = 100 * MAX_BALANCE; StdCheats.deal(address(underlyingToken), address(user), balance); strategies[i] = strat; @@ -287,7 +304,7 @@ contract IntegrationConfig is IntegrationDeployer, G2Operations, Constants { /// @param standardQuorums the quorums that we want to register for WITHOUT churn /// @return churnTargets: one churnable operator for each churnQuorum function _getChurnTargets( - User incomingOperator, + User incomingOperator, bytes memory churnQuorums, bytes memory standardQuorums ) internal returns (User[] memory) { @@ -301,32 +318,45 @@ contract IntegrationConfig is IntegrationDeployer, G2Operations, Constants { // For each churn quorum, select operators as churn targets User[] memory churnTargets = new User[](churnQuorums.length); - for (uint i = 0; i < churnQuorums.length; i++) { + for (uint256 i = 0; i < churnQuorums.length; i++) { uint8 quorum = uint8(churnQuorums[i]); - IRegistryCoordinator.OperatorSetParam memory params - = registryCoordinator.getOperatorSetParams(quorum); + ISlashingRegistryCoordinatorTypes.OperatorSetParam memory params = + registryCoordinator.getOperatorSetParams(quorum); // Sanity check - make sure we're at the operator cap uint32 curNumOperators = indexRegistry.totalOperatorsForQuorum(quorum); - assertTrue(curNumOperators >= params.maxOperatorCount, "_getChurnTargets: non-full quorum cannot be churned"); + assertTrue( + curNumOperators >= params.maxOperatorCount, + "_getChurnTargets: non-full quorum cannot be churned" + ); // Get a random registered operator churnTargets[i] = _selectRandRegisteredOperator(quorum); emit log_named_string( - string.concat("_getChurnTargets: selected churn target for quorum ", uint(quorum).toString()), - churnTargets[i].NAME()); + string.concat( + "_getChurnTargets: selected churn target for quorum ", + uint256(quorum).toString() + ), + churnTargets[i].NAME() + ); uint96 currentTotalStake = stakeRegistry.getCurrentTotalStake(quorum); - uint96 operatorToChurnStake = stakeRegistry.getCurrentStake(churnTargets[i].operatorId(), quorum); + uint96 operatorToChurnStake = + stakeRegistry.getCurrentStake(churnTargets[i].operatorId(), quorum); // Ensure the incoming operator exceeds the individual stake threshold -- // more stake than the outgoing operator by kickBIPsOfOperatorStake while ( - _getWeight(quorum, incomingOperator) <= _individualKickThreshold(operatorToChurnStake, params) || - operatorToChurnStake >= _totalKickThreshold(currentTotalStake + _getWeight(quorum, incomingOperator), params) + _getWeight(quorum, incomingOperator) + <= _individualKickThreshold(operatorToChurnStake, params) + || operatorToChurnStake + >= _totalKickThreshold( + currentTotalStake + _getWeight(quorum, incomingOperator), params + ) ) { - (IStrategy[] memory strategies, uint[] memory balances) = _dealMaxTokens(incomingOperator); + (IStrategy[] memory strategies, uint256[] memory balances) = + _dealMaxTokens(incomingOperator); incomingOperator.depositIntoEigenlayer(strategies, balances); } } @@ -337,33 +367,35 @@ contract IntegrationConfig is IntegrationDeployer, G2Operations, Constants { /// From RegistryCoordinator._individualKickThreshold function _individualKickThreshold( - uint96 operatorStake, - IRegistryCoordinator.OperatorSetParam memory setParams + uint96 operatorStake, + ISlashingRegistryCoordinatorTypes.OperatorSetParam memory setParams ) internal pure returns (uint96) { return operatorStake * setParams.kickBIPsOfOperatorStake / BIPS_DENOMINATOR; } /// From RegistryCoordinator._totalKickThreshold function _totalKickThreshold( - uint96 totalStake, - IRegistryCoordinator.OperatorSetParam memory setParams + uint96 totalStake, + ISlashingRegistryCoordinatorTypes.OperatorSetParam memory setParams ) internal pure returns (uint96) { return totalStake * setParams.kickBIPsOfTotalStake / BIPS_DENOMINATOR; } - function _getWeight( - uint8 quorum, - User operator - ) internal view returns (uint96) { + function _getWeight(uint8 quorum, User operator) internal view returns (uint96) { return stakeRegistry.weightOfOperatorForQuorum(quorum, address(operator)); } - function _makeRoom(bytes memory quorums) private { - emit log_named_string("_getChurnTargets: making room by removing operators from quorums", quorums.toString()); + function _makeRoom( + bytes memory quorums + ) private { + emit log_named_string( + "_getChurnTargets: making room by removing operators from quorums", quorums.toString() + ); - for (uint i = 0; i < quorums.length; i++) { + for (uint256 i = 0; i < quorums.length; i++) { uint8 quorum = uint8(quorums[i]); - uint32 maxOperatorCount = registryCoordinator.getOperatorSetParams(quorum).maxOperatorCount; + uint32 maxOperatorCount = + registryCoordinator.getOperatorSetParams(quorum).maxOperatorCount; // Continue deregistering until we're under the cap // This uses while in case we tested a config change that lowered the max count @@ -378,25 +410,32 @@ contract IntegrationConfig is IntegrationDeployer, G2Operations, Constants { } } - function _selectRandRegisteredOperator(uint8 quorum) internal returns (User) { + function _selectRandRegisteredOperator( + uint8 quorum + ) internal returns (User) { uint32 curNumOperators = indexRegistry.totalOperatorsForQuorum(quorum); - + bytes32 randId = indexRegistry.getLatestOperatorUpdate({ quorumNumber: quorum, - operatorIndex: uint32(_randUint({ min: 0, max: curNumOperators - 1 })) + operatorIndex: uint32(_randUint({min: 0, max: curNumOperators - 1})) }).operatorId; return User(blsApkRegistry.getOperatorFromPubkeyHash(randId)); } - function _fetchKeypair() internal returns (uint, IBLSApkRegistry.PubkeyRegistrationParams memory) { + function _fetchKeypair() + internal + returns (uint256, IBLSApkRegistryTypes.PubkeyRegistrationParams memory) + { // should probably just generate another keypair at this point if (fetchIdx == privKeys.length) { - revert("_fetchKeypair: not enough generated keys. Check IntegrationDeployer.constructor"); + revert( + "_fetchKeypair: not enough generated keys. Check IntegrationDeployer.constructor" + ); } - uint privKey = privKeys[fetchIdx]; - IBLSApkRegistry.PubkeyRegistrationParams memory pubkey = pubkeys[fetchIdx]; + uint256 privKey = privKeys[fetchIdx]; + IBLSApkRegistryTypes.PubkeyRegistrationParams memory pubkey = pubkeys[fetchIdx]; fetchIdx++; return (privKey, pubkey); @@ -404,12 +443,12 @@ contract IntegrationConfig is IntegrationDeployer, G2Operations, Constants { /// @dev Uses `random` to return a random uint, with a range given by `min` and `max` (inclusive) /// @return `min` <= result <= `max` - function _randUint(uint min, uint max) internal returns (uint) { - uint range = max - min + 1; + function _randUint(uint256 min, uint256 max) internal returns (uint256) { + uint256 range = max - min + 1; // calculate the number of bits needed for the range - uint bitsNeeded = 0; - uint tempRange = range; + uint256 bitsNeeded = 0; + uint256 tempRange = range; while (tempRange > 0) { bitsNeeded++; tempRange >>= 1; @@ -417,8 +456,8 @@ contract IntegrationConfig is IntegrationDeployer, G2Operations, Constants { // create a mask for the required number of bits // and extract the value from the hash - uint mask = (1 << bitsNeeded) - 1; - uint value = uint(random) & mask; + uint256 mask = (1 << bitsNeeded) - 1; + uint256 value = uint256(random) & mask; // in case value is out of range, wrap around or retry while (value >= range) { @@ -431,14 +470,16 @@ contract IntegrationConfig is IntegrationDeployer, G2Operations, Constants { } function _randBool() internal returns (bool) { - return _randUint({ min: 0, max: 1 }) == 0; + return _randUint({min: 0, max: 1}) == 0; } - function _selectRand(bytes memory quorums) internal returns (bytes memory) { + function _selectRand( + bytes memory quorums + ) internal returns (bytes memory) { assertTrue(quorums.length != 0, "_selectRand: tried to select from empty quorum list"); uint192 result; - for (uint i = 0; i < quorums.length; i++) { + for (uint256 i = 0; i < quorums.length; i++) { if (_randBool()) { result = uint192(result.setBit(uint8(quorums[i]))); } @@ -457,20 +498,22 @@ contract IntegrationConfig is IntegrationDeployer, G2Operations, Constants { } /// @dev Select a random value from `arr` and return it. Reverts if arr is empty - function _randValue(bytes memory arr) internal returns (uint) { + function _randValue( + bytes memory arr + ) internal returns (uint256) { assertTrue(arr.length > 0, "_randValue: tried to select value from empty array"); - - uint idx = _randUint({ min: 0, max: arr.length - 1 }); - return uint(uint8(arr[idx])); + + uint256 idx = _randUint({min: 0, max: arr.length - 1}); + return uint256(uint8(arr[idx])); } /// Private _randX methods used by _configRand: /// @dev Select a random number of quorums to initialize /// NOTE: This should only be used when initializing quorums for the first time (in _configRand) - function _randQuorumCount() private returns (uint) { - uint quorumFlag = _randValue(numQuorumFlags); - + function _randQuorumCount() private returns (uint256) { + uint256 quorumFlag = _randValue(numQuorumFlags); + if (quorumFlag == ONE) { return 1; } else if (quorumFlag == TWO) { @@ -478,7 +521,7 @@ contract IntegrationConfig is IntegrationDeployer, G2Operations, Constants { } else if (quorumFlag == MANY) { // Ideally this would be MAX_QUORUM_COUNT, but that really slows tests // that have users register for all quorums - return _randUint({ min: 3, max: 10 }); + return _randUint({min: 3, max: 10}); } else { revert("_randQuorumCount: flag not recognized"); } @@ -488,16 +531,16 @@ contract IntegrationConfig is IntegrationDeployer, G2Operations, Constants { /// NOTE: This should only be used when creating a quorum for the first time. If you're /// selecting strategies to add after the quorum has been initialized, this is likely to /// return duplicates. - function _randStrategyParams() private returns (IStakeRegistry.StrategyParams[] memory) { - uint strategyFlag = _randValue(numStrategyFlags); - uint strategyCount; + function _randStrategyParams() private returns (IStakeRegistryTypes.StrategyParams[] memory) { + uint256 strategyFlag = _randValue(numStrategyFlags); + uint256 strategyCount; if (strategyFlag == ONE) { strategyCount = 1; } else if (strategyFlag == TWO) { strategyCount = 2; } else if (strategyFlag == MANY) { - strategyCount = _randUint({ min: 3, max: allStrats.length - 1 }); + strategyCount = _randUint({min: 3, max: allStrats.length - 1}); } else if (strategyFlag == FIFTEEN) { strategyCount = 15; } else if (strategyFlag == TWENTY) { @@ -508,10 +551,11 @@ contract IntegrationConfig is IntegrationDeployer, G2Operations, Constants { revert("_randStrategyCount: flag not recognized"); } - IStakeRegistry.StrategyParams[] memory params = new IStakeRegistry.StrategyParams[](strategyCount); + IStakeRegistryTypes.StrategyParams[] memory params = + new IStakeRegistryTypes.StrategyParams[](strategyCount); - for (uint i = 0; i < params.length; i++) { - params[i] = IStakeRegistry.StrategyParams({ + for (uint256 i = 0; i < params.length; i++) { + params[i] = IStakeRegistryTypes.StrategyParams({ strategy: allStrats[i], multiplier: DEFAULT_STRATEGY_MULTIPLIER }); @@ -520,17 +564,19 @@ contract IntegrationConfig is IntegrationDeployer, G2Operations, Constants { return params; } - /** + /** * @dev Uses _randFillType to determine how many operators to register for a quorum initially * @return The number of operators to register */ - function _randInitialOperators(IRegistryCoordinator.OperatorSetParam memory operatorSet) private returns (uint) { - uint fillTypeFlag = _randValue(fillTypeFlags); + function _randInitialOperators( + ISlashingRegistryCoordinatorTypes.OperatorSetParam memory operatorSet + ) private returns (uint256) { + uint256 fillTypeFlag = _randValue(fillTypeFlags); if (fillTypeFlag == EMPTY) { return 0; } else if (fillTypeFlag == SOME_FILL) { - return _randUint({ min: 1, max: operatorSet.maxOperatorCount - 1 }); + return _randUint({min: 1, max: operatorSet.maxOperatorCount - 1}); } else if (fillTypeFlag == FULL) { return operatorSet.maxOperatorCount; } else { @@ -540,8 +586,8 @@ contract IntegrationConfig is IntegrationDeployer, G2Operations, Constants { /// @dev Select a random number of quorums to initialize function _randMinStake() private returns (uint96) { - uint minStakeFlag = _randValue(minStakeFlags); - + uint256 minStakeFlag = _randValue(minStakeFlags); + if (minStakeFlag == NO_MINIMUM) { return 0; } else if (minStakeFlag == HAS_MINIMUM) { @@ -555,10 +601,12 @@ contract IntegrationConfig is IntegrationDeployer, G2Operations, Constants { * @dev Converts a bitmap into an array of bytes * @dev Each byte in the input is processed as indicating a single bit to flip in the bitmap */ - function _bitmapToBytes(uint bitmap) internal pure returns (bytes memory bytesArray) { - for (uint i = 0; i < 256; ++i) { + function _bitmapToBytes( + uint256 bitmap + ) internal pure returns (bytes memory bytesArray) { + for (uint256 i = 0; i < 256; ++i) { // Mask for i-th bit - uint mask = uint(1 << i); + uint256 mask = uint256(1 << i); // emit log_named_uint("mask: ", mask); diff --git a/test/integration/IntegrationDeployer.t.sol b/test/integration/IntegrationDeployer.t.sol index d69a908b..3e38d697 100644 --- a/test/integration/IntegrationDeployer.t.sol +++ b/test/integration/IntegrationDeployer.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "forge-std/Test.sol"; @@ -14,13 +14,14 @@ import "@openzeppelin/contracts/utils/Strings.sol"; // Core contracts import "eigenlayer-contracts/src/contracts/core/DelegationManager.sol"; import "eigenlayer-contracts/src/contracts/core/StrategyManager.sol"; -import "eigenlayer-contracts/src/contracts/core/Slasher.sol"; import "eigenlayer-contracts/src/contracts/core/AVSDirectory.sol"; import "eigenlayer-contracts/src/contracts/core/RewardsCoordinator.sol"; +import "eigenlayer-contracts/src/contracts/core/AllocationManager.sol"; import "eigenlayer-contracts/src/contracts/strategies/StrategyBase.sol"; import "eigenlayer-contracts/src/contracts/pods/EigenPodManager.sol"; import "eigenlayer-contracts/src/contracts/pods/EigenPod.sol"; import "eigenlayer-contracts/src/contracts/permissions/PauserRegistry.sol"; +import "eigenlayer-contracts/src/contracts/permissions/PermissionController.sol"; import "eigenlayer-contracts/src/test/mocks/ETHDepositMock.sol"; // Middleware contracts @@ -52,10 +53,11 @@ abstract contract IntegrationDeployer is Test, IUserDeployer { EigenPodManager eigenPodManager; RewardsCoordinator rewardsCoordinator; PauserRegistry pauserRegistry; - Slasher slasher; IBeacon eigenPodBeacon; EigenPod pod; ETHPOSDepositMock ethPOSDeposit; + AllocationManager allocationManager; + PermissionController permissionController; // Base strategy implementation in case we want to create more strategies later StrategyBase baseStrategyImplementation; @@ -95,10 +97,23 @@ abstract contract IntegrationDeployer is Test, IUserDeployer { uint256 constant MAX_STRATEGY_COUNT = 32; // From StakeRegistry.MAX_WEIGHING_FUNCTION_LENGTH uint96 constant DEFAULT_STRATEGY_MULTIPLIER = 1e18; // RewardsCoordinator + // Config Variables + /// @notice intervals(epochs) are 1 weeks + uint32 CALCULATION_INTERVAL_SECONDS = 7 days; + + /// @notice Max duration is 5 epochs (2 weeks * 5 = 10 weeks in seconds) uint32 MAX_REWARDS_DURATION = 70 days; + + /// @notice Lower bound start range is ~3 months into the past, multiple of CALCULATION_INTERVAL_SECONDS uint32 MAX_RETROACTIVE_LENGTH = 84 days; + /// @notice Upper bound start range is ~1 month into the future, multiple of CALCULATION_INTERVAL_SECONDS uint32 MAX_FUTURE_LENGTH = 28 days; - uint32 GENESIS_REWARDS_TIMESTAMP = 1_712_092_632; + /// @notice absolute min timestamp that a rewards can start at + uint32 GENESIS_REWARDS_TIMESTAMP = 1712188800; + /// @notice Equivalent to 100%, but in basis points. + uint16 internal constant ONE_HUNDRED_IN_BIPS = 10000; + + uint32 defaultOperatorSplitBips = 1000; /// @notice Delay in timestamp before a posted root can be claimed against uint32 activationDelay = 7 days; /// @notice intervals(epochs) are 2 weeks @@ -133,52 +148,78 @@ abstract contract IntegrationDeployer is Test, IUserDeployer { new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), "") ) ); - slasher = Slasher( + eigenPodManager = EigenPodManager( address( new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), "") ) ); - eigenPodManager = EigenPodManager( + avsDirectory = AVSDirectory( address( new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), "") ) ); - avsDirectory = AVSDirectory( + + allocationManager = AllocationManager( address( new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), "") ) ); - // RewardsCoordinator = RewardsCoordinator( - // address(new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), "")) - // ); - // Deploy EigenPod Contracts - pod = new EigenPod( - ethPOSDeposit, - eigenPodManager, - GENESIS_TIME_LOCAL + permissionController = PermissionController( + address( + new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), "") + ) + ); + + rewardsCoordinator = RewardsCoordinator( + address( + new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), "") + ) ); + // Deploy EigenPod Contracts + pod = new EigenPod(ethPOSDeposit, eigenPodManager, GENESIS_TIME_LOCAL); + eigenPodBeacon = new UpgradeableBeacon(address(pod)); + PermissionController permissionControllerImplementation = new PermissionController(); + // Second, deploy the *implementation* contracts, using the *proxy contracts* as inputs - DelegationManager delegationImplementation = - new DelegationManager(strategyManager, slasher, eigenPodManager); + DelegationManager delegationImplementation = new DelegationManager( + strategyManager, + eigenPodManager, + allocationManager, + pauserRegistry, + permissionController, + 0 + ); StrategyManager strategyManagerImplementation = - new StrategyManager(delegationManager, eigenPodManager, slasher); - Slasher slasherImplementation = new Slasher(strategyManager, delegationManager); - EigenPodManager eigenPodManagerImplementation = new EigenPodManager( - ethPOSDeposit, eigenPodBeacon, strategyManager, slasher, delegationManager - ); - AVSDirectory avsDirectoryImplemntation = new AVSDirectory(delegationManager); - // RewardsCoordinator rewardsCoordinatorImplementation = new RewardsCoordinator( - // delegationManager, - // IStrategyManager(address(strategyManager)), - // MAX_REWARDS_DURATION, - // MAX_RETROACTIVE_LENGTH, - // MAX_FUTURE_LENGTH, - // GENESIS_REWARDS_TIMESTAMP - // ); + new StrategyManager(delegationManager, pauserRegistry); + EigenPodManager eigenPodManagerImplementation = + new EigenPodManager(ethPOSDeposit, eigenPodBeacon, delegationManager, pauserRegistry); + AVSDirectory avsDirectoryImplementation = + new AVSDirectory(delegationManager, pauserRegistry); + + RewardsCoordinator rewardsCoordinatorImplementation = new RewardsCoordinator( + delegationManager, + IStrategyManager(address(strategyManager)), + allocationManager, + pauserRegistry, + permissionController, + CALCULATION_INTERVAL_SECONDS, + MAX_REWARDS_DURATION, + MAX_RETROACTIVE_LENGTH, + MAX_FUTURE_LENGTH, + GENESIS_REWARDS_TIMESTAMP + ); + + AllocationManager allocationManagerImplementation = new AllocationManager( + delegationManager, + pauserRegistry, + permissionController, + uint32(7 days), // DEALLOCATION_DELAY + uint32(1 days) // ALLOCATION_CONFIGURATION_DELAY + ); // Third, upgrade the proxy contracts to point to the implementations uint256 minWithdrawalDelayBlocks = 7 days / 12 seconds; @@ -191,11 +232,7 @@ abstract contract IntegrationDeployer is Test, IUserDeployer { abi.encodeWithSelector( DelegationManager.initialize.selector, eigenLayerReputedMultisig, // initialOwner - pauserRegistry, - 0, /* initialPausedStatus */ - minWithdrawalDelayBlocks, - initializeStrategiesToSetDelayBlocks, - initializeWithdrawalDelayBlocks + 0 /* initialPausedStatus */ ) ); // StrategyManager @@ -206,18 +243,6 @@ abstract contract IntegrationDeployer is Test, IUserDeployer { StrategyManager.initialize.selector, eigenLayerReputedMultisig, //initialOwner eigenLayerReputedMultisig, //initial whitelister - pauserRegistry, - 0 // initialPausedStatus - ) - ); - // Slasher - proxyAdmin.upgradeAndCall( - TransparentUpgradeableProxy(payable(address(slasher))), - address(slasherImplementation), - abi.encodeWithSelector( - Slasher.initialize.selector, - eigenLayerReputedMultisig, - pauserRegistry, 0 // initialPausedStatus ) ); @@ -228,39 +253,51 @@ abstract contract IntegrationDeployer is Test, IUserDeployer { abi.encodeWithSelector( EigenPodManager.initialize.selector, eigenLayerReputedMultisig, // initialOwner - pauserRegistry, 0 // initialPausedStatus ) ); // AVSDirectory proxyAdmin.upgradeAndCall( TransparentUpgradeableProxy(payable(address(avsDirectory))), - address(avsDirectoryImplemntation), + address(avsDirectoryImplementation), abi.encodeWithSelector( AVSDirectory.initialize.selector, eigenLayerReputedMultisig, // initialOwner - pauserRegistry, + // pauserRegistry, + 0 // initialPausedStatus + ) + ); + + proxyAdmin.upgrade( + TransparentUpgradeableProxy(payable(address(permissionController))), + address(permissionControllerImplementation) + ); + + proxyAdmin.upgradeAndCall( + TransparentUpgradeableProxy(payable(address(rewardsCoordinator))), + address(rewardsCoordinatorImplementation), + abi.encodeWithSelector( + RewardsCoordinator.initialize.selector, + eigenLayerReputedMultisig, // initialOwner + 0, // initialPausedStatus + rewardsUpdater, + activationDelay, + defaultOperatorSplitBips // defaultSplitBips + ) + ); + + proxyAdmin.upgradeAndCall( + TransparentUpgradeableProxy(payable(address(allocationManager))), + address(allocationManagerImplementation), + abi.encodeWithSelector( + AllocationManager.initialize.selector, + eigenLayerReputedMultisig, // initialOwner 0 // initialPausedStatus ) ); - // // RewardsCoordinator - // proxyAdmin.upgradeAndCall( - // TransparentUpgradeableProxy(payable(address(rewardsCoordinator))), - // address(rewardsCoordinatorImplementation), - // abi.encodeWithSelector( - // RewardsCoordinator.initialize.selector, - // eigenLayerReputedMultisig, // initialOwner - // pauserRegistry, - // 0, // initialPausedStatus - // rewardsUpdater, - // activationDelay, - // calculationIntervalSeconds, - // globalCommissionBips - // ) - // ); // Deploy and whitelist strategies - baseStrategyImplementation = new StrategyBase(strategyManager); + baseStrategyImplementation = new StrategyBase(strategyManager, pauserRegistry); for (uint256 i = 0; i < MAX_STRATEGY_COUNT; i++) { string memory number = uint256(i).toString(); string memory stratName = string.concat("StrategyToken", number); @@ -310,21 +347,25 @@ abstract contract IntegrationDeployer is Test, IUserDeployer { cheats.stopPrank(); StakeRegistry stakeRegistryImplementation = new StakeRegistry( - IRegistryCoordinator(registryCoordinator), IDelegationManager(delegationManager) + ISlashingRegistryCoordinator(registryCoordinator), + IDelegationManager(delegationManager), + IAVSDirectory(avsDirectory), + allocationManager ); BLSApkRegistry blsApkRegistryImplementation = - new BLSApkRegistry(IRegistryCoordinator(registryCoordinator)); + new BLSApkRegistry(ISlashingRegistryCoordinator(registryCoordinator)); IndexRegistry indexRegistryImplementation = - new IndexRegistry(IRegistryCoordinator(registryCoordinator)); + new IndexRegistry(ISlashingRegistryCoordinator(registryCoordinator)); ServiceManagerMock serviceManagerImplementation = new ServiceManagerMock( IAVSDirectory(avsDirectory), rewardsCoordinator, - IRegistryCoordinator(registryCoordinator), - stakeRegistry - ); - SocketRegistry socketRegistryImplementation = new SocketRegistry( - IRegistryCoordinator(registryCoordinator) + ISlashingRegistryCoordinator(registryCoordinator), + stakeRegistry, + permissionController, + allocationManager ); + SocketRegistry socketRegistryImplementation = + new SocketRegistry(IRegistryCoordinator(registryCoordinator)); proxyAdmin.upgrade( TransparentUpgradeableProxy(payable(address(stakeRegistry))), @@ -356,25 +397,107 @@ abstract contract IntegrationDeployer is Test, IUserDeployer { rewardsInitiator: address(msg.sender) }); - RegistryCoordinator registryCoordinatorImplementation = - new RegistryCoordinator(serviceManager, stakeRegistry, blsApkRegistry, indexRegistry, socketRegistry); + IStakeRegistryTypes.StakeType[] memory quorumStakeTypes = + new IStakeRegistryTypes.StakeType[](0); + uint32[] memory slashableStakeQuorumLookAheadPeriods = new uint32[](0); + + RegistryCoordinator registryCoordinatorImplementation = new RegistryCoordinator( + serviceManager, + stakeRegistry, + blsApkRegistry, + indexRegistry, + socketRegistry, + allocationManager, + pauserRegistry + ); proxyAdmin.upgradeAndCall( TransparentUpgradeableProxy(payable(address(registryCoordinator))), address(registryCoordinatorImplementation), abi.encodeWithSelector( - RegistryCoordinator.initialize.selector, + SlashingRegistryCoordinator.initialize.selector, registryCoordinatorOwner, churnApprover, ejector, - pauserRegistry, 0, /*initialPausedStatus*/ new IRegistryCoordinator.OperatorSetParam[](0), new uint96[](0), - new IStakeRegistry.StrategyParams[][](0) + new IStakeRegistryTypes.StrategyParams[][](0), + quorumStakeTypes, + slashableStakeQuorumLookAheadPeriods ) ); operatorStateRetriever = new OperatorStateRetriever(); + + // Setup UAM Permissions + cheats.startPrank(serviceManager.owner()); + // 1. set AVS registrar + serviceManager.setAppointee({ + appointee: serviceManager.owner(), + target: address(allocationManager), + selector: IAllocationManager.setAVSRegistrar.selector + }); + // 2. create operator sets + serviceManager.setAppointee({ + appointee: address(registryCoordinator), + target: address(allocationManager), + selector: IAllocationManager.createOperatorSets.selector + }); + // 3. deregister operator from operator sets + serviceManager.setAppointee({ + appointee: address(registryCoordinator), + target: address(allocationManager), + selector: IAllocationManager.deregisterFromOperatorSets.selector + }); + // 4. add strategies to operator sets + serviceManager.setAppointee({ + appointee: address(registryCoordinator), + target: address(stakeRegistry), + selector: IAllocationManager.addStrategiesToOperatorSet.selector + }); + // 5. remove strategies from operator sets + serviceManager.setAppointee({ + appointee: address(registryCoordinator), + target: address(stakeRegistry), + selector: IAllocationManager.removeStrategiesFromOperatorSet.selector + }); + cheats.stopPrank(); + + _setOperatorSetsEnabled(false); + _setM2QuorumsDisabled(false); + } + + /// @notice Overwrite RegistryCoordinator.operatorSetsEnabled to the specified value. + /// This is to enable testing of RegistryCoordinator in non-operator set mode. + function _setOperatorSetsEnabled( + bool operatorSetsEnabled + ) internal { + // 1. First read the current value of the entire slot + // which holds operatorSetsEnabled, m2QuorumsDisabled, and accountIdentifier + bytes32 currentSlot = cheats.load(address(registryCoordinator), bytes32(uint256(161))); + + // 2. Clear only the first byte (operatorSetsEnabled) while keeping the rest + bytes32 newSlot = (currentSlot & ~bytes32(uint256(0xff))) + | bytes32(uint256(operatorSetsEnabled ? 0x01 : 0x00)); + + // 3. Store the modified slot + cheats.store(address(registryCoordinator), bytes32(uint256(161)), newSlot); + } + + /// @notice Overwrite RegistryCoordinator.m2QuorumsDisabled to the specified value. + function _setM2QuorumsDisabled( + bool m2QuorumsDisabled + ) internal { + // 1. First read the current value of the entire slot + // which holds operatorSetsEnabled, m2QuorumsDisabled, and accountIdentifier + bytes32 currentSlot = cheats.load(address(registryCoordinator), bytes32(uint256(161))); + + // 2. Clear only the second byte (m2QuorumsDisabled) while keeping the rest + bytes32 newSlot = (currentSlot & ~bytes32(uint256(0xff) << 8)) + | bytes32(uint256(m2QuorumsDisabled ? 0x01 : 0x00) << 8); + + // 3. Store the modified slot + cheats.store(address(registryCoordinator), bytes32(uint256(161)), newSlot); } /// @dev Deploy a strategy and its underlying token, push to global lists of tokens/strategies, and whitelist @@ -404,9 +527,7 @@ abstract contract IntegrationDeployer is Test, IUserDeployer { bool[] memory thirdPartyTransfersForbiddenValues = new bool[](1); strategies[0] = strategy; cheats.prank(strategyManager.strategyWhitelister()); - strategyManager.addStrategiesToDepositWhitelist( - strategies, thirdPartyTransfersForbiddenValues - ); + strategyManager.addStrategiesToDepositWhitelist(strategies); // Add to allStrats allStrats.push(strategy); diff --git a/test/integration/TimeMachine.t.sol b/test/integration/TimeMachine.t.sol index b1df82d5..d16e1ac4 100644 --- a/test/integration/TimeMachine.t.sol +++ b/test/integration/TimeMachine.t.sol @@ -1,23 +1,22 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "forge-std/Test.sol"; contract TimeMachine is Test { - Vm cheats = Vm(VM_ADDRESS); bool pastExists = false; - uint lastSnapshot; + uint256 lastSnapshot; - function createSnapshot() public returns (uint) { - uint snapshot = cheats.snapshot(); + function createSnapshot() public returns (uint256) { + uint256 snapshot = cheats.snapshot(); lastSnapshot = snapshot; pastExists = true; return snapshot; } - function warpToLast() public returns (uint curState) { + function warpToLast() public returns (uint256 curState) { // Safety check to make sure createSnapshot is called before attempting to warp // so we don't accidentally prevent our own births assertTrue(pastExists, "TimeMachine.warpToLast: invalid usage, past does not exist"); @@ -27,7 +26,9 @@ contract TimeMachine is Test { return curState; } - function warpToPresent(uint curState) public { + function warpToPresent( + uint256 curState + ) public { cheats.revertTo(curState); } } diff --git a/test/integration/User.t.sol b/test/integration/User.t.sol index 9e5e5990..8a1a9b61 100644 --- a/test/integration/User.t.sol +++ b/test/integration/User.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "forge-std/Test.sol"; import "@openzeppelin/contracts/utils/Strings.sol"; @@ -11,10 +11,12 @@ import "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; // Core import "eigenlayer-contracts/src/contracts/core/DelegationManager.sol"; +import "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; import "eigenlayer-contracts/src/contracts/core/StrategyManager.sol"; import "eigenlayer-contracts/src/contracts/core/AVSDirectory.sol"; // Middleware +import "src/interfaces/IRegistryCoordinator.sol"; import "src/RegistryCoordinator.sol"; import "src/BLSApkRegistry.sol"; import "src/IndexRegistry.sol"; @@ -27,17 +29,15 @@ import "test/integration/TimeMachine.t.sol"; import "test/integration/utils/Sort.t.sol"; import "test/integration/utils/BitmapStrings.t.sol"; - interface IUserDeployer { function registryCoordinator() external view returns (RegistryCoordinator); function avsDirectory() external view returns (AVSDirectory); function timeMachine() external view returns (TimeMachine); - function churnApproverPrivateKey() external view returns (uint); + function churnApproverPrivateKey() external view returns (uint256); function churnApprover() external view returns (address); } contract User is Test { - using BN254 for *; using Strings for *; using BitmapStrings for *; @@ -56,24 +56,28 @@ contract User is Test { BLSApkRegistry blsApkRegistry; StakeRegistry stakeRegistry; IndexRegistry indexRegistry; - + TimeMachine timeMachine; - uint churnApproverPrivateKey; + uint256 churnApproverPrivateKey; address churnApprover; string public NAME; bytes32 public operatorId; // BLS keypair: - uint privKey; - IBLSApkRegistry.PubkeyRegistrationParams pubkeyParams; + uint256 privKey; + IBLSApkRegistryTypes.PubkeyRegistrationParams pubkeyParams; // EIP1271 sigs: mapping(bytes32 => bool) digests; - uint salt = 0; + uint256 salt = 0; - constructor(string memory name, uint _privKey, IBLSApkRegistry.PubkeyRegistrationParams memory _pubkeyParams) { + constructor( + string memory name, + uint256 _privKey, + IBLSApkRegistryTypes.PubkeyRegistrationParams memory _pubkeyParams + ) { IUserDeployer deployer = IUserDeployer(msg.sender); registryCoordinator = deployer.registryCoordinator(); @@ -99,7 +103,8 @@ contract User is Test { privKey = _privKey; pubkeyParams = _pubkeyParams; - BN254.G1Point memory registrationMessageHash = registryCoordinator.pubkeyRegistrationMessageHash(address(this)); + BN254.G1Point memory registrationMessageHash = + registryCoordinator.pubkeyRegistrationMessageHash(address(this)); pubkeyParams.pubkeyRegistrationSignature = registrationMessageHash.scalar_mul(privKey); operatorId = pubkeyParams.pubkeyG1.hashG1Point(); @@ -113,8 +118,9 @@ contract User is Test { /** * Middleware contracts: */ - - function registerOperator(bytes calldata quorums) public createSnapshot virtual returns (bytes32) { + function registerOperator( + bytes calldata quorums + ) public virtual createSnapshot returns (bytes32) { _log("registerOperator", quorums); vm.warp(block.timestamp + 1); @@ -135,7 +141,7 @@ contract User is Test { bytes calldata churnQuorums, User[] calldata churnTargets, bytes calldata standardQuorums - ) public createSnapshot virtual { + ) public virtual createSnapshot { _logChurn("registerOperatorWithChurn", churnQuorums, churnTargets, standardQuorums); // Sanity check input: @@ -143,36 +149,42 @@ contract User is Test { // - churnQuorums and standardQuorums should not have any bits in common uint192 churnBitmap = uint192(churnQuorums.orderedBytesArrayToBitmap()); uint192 standardBitmap = uint192(standardQuorums.orderedBytesArrayToBitmap()); - assertEq(churnQuorums.length, churnTargets.length, "User.registerOperatorWithChurn: input length mismatch"); - assertTrue(churnBitmap.noBitsInCommon(standardBitmap), "User.registerOperatorWithChurn: input quorums have common bits"); + assertEq( + churnQuorums.length, + churnTargets.length, + "User.registerOperatorWithChurn: input length mismatch" + ); + assertTrue( + churnBitmap.noBitsInCommon(standardBitmap), + "User.registerOperatorWithChurn: input quorums have common bits" + ); - bytes memory allQuorums = - churnBitmap - .plus(standardBitmap) - .bitmapToBytesArray(); + bytes memory allQuorums = churnBitmap.plus(standardBitmap).bitmapToBytesArray(); - IRegistryCoordinator.OperatorKickParam[] memory kickParams - = new IRegistryCoordinator.OperatorKickParam[](allQuorums.length); + ISlashingRegistryCoordinator.OperatorKickParam[] memory kickParams = + new ISlashingRegistryCoordinator.OperatorKickParam[](allQuorums.length); // this constructs OperatorKickParam[] in ascending quorum order // (yikes) - uint churnIdx; - uint stdIdx; + uint256 churnIdx; + uint256 stdIdx; while (churnIdx + stdIdx < allQuorums.length) { if (churnIdx == churnQuorums.length) { - kickParams[churnIdx + stdIdx] = IRegistryCoordinator.OperatorKickParam({ + kickParams[churnIdx + stdIdx] = ISlashingRegistryCoordinatorTypes.OperatorKickParam({ quorumNumber: 0, operator: address(0) }); stdIdx++; - } else if (stdIdx == standardQuorums.length || churnQuorums[churnIdx] < standardQuorums[stdIdx]) { - kickParams[churnIdx + stdIdx] = IRegistryCoordinator.OperatorKickParam({ + } else if ( + stdIdx == standardQuorums.length || churnQuorums[churnIdx] < standardQuorums[stdIdx] + ) { + kickParams[churnIdx + stdIdx] = ISlashingRegistryCoordinatorTypes.OperatorKickParam({ quorumNumber: uint8(churnQuorums[churnIdx]), operator: address(churnTargets[churnIdx]) }); churnIdx++; } else if (standardQuorums[stdIdx] < churnQuorums[churnIdx]) { - kickParams[churnIdx + stdIdx] = IRegistryCoordinator.OperatorKickParam({ + kickParams[churnIdx + stdIdx] = ISlashingRegistryCoordinatorTypes.OperatorKickParam({ quorumNumber: 0, operator: address(0) }); @@ -184,7 +196,7 @@ contract User is Test { // Generate churn approver signature bytes32 _salt = keccak256(abi.encodePacked(++salt, address(this))); - uint expiry = type(uint).max; + uint256 expiry = type(uint256).max; bytes32 digest = registryCoordinator.calculateOperatorChurnApprovalDigestHash({ registeringOperator: address(this), registeringOperatorId: operatorId, @@ -202,12 +214,8 @@ contract User is Test { } signature[signature.length - 1] = bytes1(v); - ISignatureUtils.SignatureWithSaltAndExpiry memory churnApproverSignature - = ISignatureUtils.SignatureWithSaltAndExpiry({ - signature: signature, - salt: _salt, - expiry: expiry - }); + ISignatureUtils.SignatureWithSaltAndExpiry memory churnApproverSignature = ISignatureUtils + .SignatureWithSaltAndExpiry({signature: signature, salt: _salt, expiry: expiry}); vm.warp(block.timestamp + 1); registryCoordinator.registerOperatorWithChurn({ @@ -220,14 +228,16 @@ contract User is Test { }); } - function deregisterOperator(bytes calldata quorums) public createSnapshot virtual { + function deregisterOperator( + bytes calldata quorums + ) public virtual createSnapshot { _log("deregisterOperator", quorums); registryCoordinator.deregisterOperator(quorums); } /// @dev Uses updateOperators to update this user's stake - function updateStakes() public createSnapshot virtual { + function updateStakes() public virtual createSnapshot { _log("updateStakes (updateOperators)"); address[] memory addrs = new address[](1); @@ -239,26 +249,23 @@ contract User is Test { /** * Core contracts: */ - - function registerAsOperator() public createSnapshot virtual { + function registerAsOperator() public virtual createSnapshot { _log("registerAsOperator (core)"); - IDelegationManager.OperatorDetails memory details = IDelegationManager.OperatorDetails({ - __deprecated_earningsReceiver: address(this), - delegationApprover: address(0), - stakerOptOutWindowBlocks: 0 - }); - - delegationManager.registerAsOperator(details, NAME); + /// TODO: check + delegationManager.registerAsOperator(msg.sender, 0, NAME); } // Deposit LSTs into the StrategyManager. This setup does not use the EPMgr or native ETH. - function depositIntoEigenlayer(IStrategy[] memory strategies, uint[] memory tokenBalances) public createSnapshot virtual { + function depositIntoEigenlayer( + IStrategy[] memory strategies, + uint256[] memory tokenBalances + ) public virtual createSnapshot { _log("depositIntoEigenLayer (core)"); - for (uint i = 0; i < strategies.length; i++) { + for (uint256 i = 0; i < strategies.length; i++) { IStrategy strat = strategies[i]; - uint tokenBalance = tokenBalances[i]; + uint256 tokenBalance = tokenBalances[i]; IERC20 underlyingToken = strat.underlyingToken(); underlyingToken.approve(address(strategyManager), tokenBalance); @@ -266,16 +273,23 @@ contract User is Test { } } - function exitEigenlayer() public createSnapshot virtual returns (IStrategy[] memory, uint[] memory) { + function exitEigenlayer() + public + virtual + createSnapshot + returns (IStrategy[] memory, uint256[] memory) + { _log("exitEigenlayer (core)"); - (IStrategy[] memory strategies, uint[] memory shares) = delegationManager.getDelegatableShares(address(this)); + (IStrategy[] memory strategies, uint256[] memory shares) = + delegationManager.getDepositedShares(address(this)); - IDelegationManager.QueuedWithdrawalParams[] memory params = new IDelegationManager.QueuedWithdrawalParams[](1); - params[0] = IDelegationManager.QueuedWithdrawalParams({ + IDelegationManagerTypes.QueuedWithdrawalParams[] memory params = + new IDelegationManager.QueuedWithdrawalParams[](1); + params[0] = IDelegationManagerTypes.QueuedWithdrawalParams({ strategies: strategies, - shares: shares, - withdrawer: address(this) + depositShares: shares, + __deprecated_withdrawer: address(this) }); delegationManager.queueWithdrawals(params); @@ -286,7 +300,6 @@ contract User is Test { /** * EIP1271 Signatures: */ - bytes4 internal constant EIP1271_MAGICVALUE = 0x1626ba7e; function isValidSignature(bytes32 digestHash, bytes memory) public view returns (bytes4) { @@ -301,8 +314,12 @@ contract User is Test { return pubkeyParams.pubkeyG1; } - function _genAVSRegistrationSig() internal returns (ISignatureUtils.SignatureWithSaltAndExpiry memory) { - ISignatureUtils.SignatureWithSaltAndExpiry memory signature = ISignatureUtils.SignatureWithSaltAndExpiry({ + function _genAVSRegistrationSig() + internal + returns (ISignatureUtils.SignatureWithSaltAndExpiry memory) + { + ISignatureUtils.SignatureWithSaltAndExpiry memory signature = ISignatureUtils + .SignatureWithSaltAndExpiry({ signature: new bytes(0), salt: bytes32(salt++), expiry: type(uint256).max @@ -320,7 +337,9 @@ contract User is Test { } // Operator0.registerOperator - function _log(string memory s) internal virtual { + function _log( + string memory s + ) internal virtual { emit log(string.concat(NAME, ".", s)); } @@ -329,14 +348,14 @@ contract User is Test { emit log_named_string(string.concat(NAME, ".", s), quorums.toString()); } - // Operator0.registerOperatorWithChurn + // Operator0.registerOperatorWithChurn // - standardQuorums: 0x00010203... // - churnQuorums: 0x0405... // - churnTargets: Operator1, Operator2, ... function _logChurn( - string memory s, - bytes memory churnQuorums, - User[] memory churnTargets, + string memory s, + bytes memory churnQuorums, + User[] memory churnTargets, bytes memory standardQuorums ) internal virtual { emit log(string.concat(NAME, ".", s)); @@ -345,7 +364,7 @@ contract User is Test { emit log_named_string("- churnQuorums", churnQuorums.toString()); string memory targetString = "["; - for (uint i = 0; i < churnTargets.length; i++) { + for (uint256 i = 0; i < churnTargets.length; i++) { if (i == churnTargets.length - 1) { targetString = string.concat(targetString, churnTargets[i].NAME()); } else { @@ -359,7 +378,6 @@ contract User is Test { } contract User_AltMethods is User { - using BitmapUtils for *; modifier createSnapshot() virtual override { @@ -368,12 +386,17 @@ contract User_AltMethods is User { _; } - constructor(string memory name, uint _privKey, IBLSApkRegistry.PubkeyRegistrationParams memory _pubkeyParams) - User(name, _privKey, _pubkeyParams) {} + constructor( + string memory name, + uint256 _privKey, + IBLSApkRegistryTypes.PubkeyRegistrationParams memory _pubkeyParams + ) User(name, _privKey, _pubkeyParams) {} /// @dev Rather than calling deregisterOperator, this pranks the ejector and calls /// ejectOperator - function deregisterOperator(bytes calldata quorums) public createSnapshot virtual override { + function deregisterOperator( + bytes calldata quorums + ) public virtual override createSnapshot { _log("deregisterOperator (eject)", quorums); address ejector = registryCoordinator.ejector(); @@ -383,25 +406,27 @@ contract User_AltMethods is User { } /// @dev Uses updateOperatorsForQuorum to update stakes of all operators in all quorums - function updateStakes() public createSnapshot virtual override { + function updateStakes() public virtual override createSnapshot { _log("updateStakes (updateOperatorsForQuorum)"); - bytes memory allQuorums = ((1 << registryCoordinator.quorumCount()) - 1).bitmapToBytesArray(); + bytes memory allQuorums = + ((1 << registryCoordinator.quorumCount()) - 1).bitmapToBytesArray(); address[][] memory operatorsPerQuorum = new address[][](allQuorums.length); - for (uint i = 0; i < allQuorums.length; i++) { + for (uint256 i = 0; i < allQuorums.length; i++) { uint8 quorum = uint8(allQuorums[i]); - bytes32[] memory operatorIds = indexRegistry.getOperatorListAtBlockNumber(quorum, uint32(block.number)); + bytes32[] memory operatorIds = + indexRegistry.getOperatorListAtBlockNumber(quorum, uint32(block.number)); operatorsPerQuorum[i] = new address[](operatorIds.length); - for (uint j = 0; j < operatorIds.length; j++) { + for (uint256 j = 0; j < operatorIds.length; j++) { operatorsPerQuorum[i][j] = blsApkRegistry.getOperatorFromPubkeyHash(operatorIds[j]); } operatorsPerQuorum[i] = Sort.sortAddresses(operatorsPerQuorum[i]); } - registryCoordinator.updateOperatorsForQuorum(operatorsPerQuorum, allQuorums); + registryCoordinator.updateOperatorsForQuorum(operatorsPerQuorum, allQuorums); } } diff --git a/test/integration/mocks/BeaconChainOracleMock.t.sol b/test/integration/mocks/BeaconChainOracleMock.t.sol new file mode 100644 index 00000000..766bb8e7 --- /dev/null +++ b/test/integration/mocks/BeaconChainOracleMock.t.sol @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.27; + +// import "eigenlayer-contracts/src/contracts/interfaces/IBeaconChainOracle.sol"; + +// NOTE: There's a copy of this file in the core repo, but importing that was causing +// the compiler to complain for an unfathomable reason. Apparently reimplementing it +// here fixes the issue. +contract BeaconChainOracleMock { + // contract BeaconChainOracleMock is IBeaconChainOracle { + + mapping(uint64 => bytes32) blockRoots; + + function timestampToBlockRoot( + uint256 timestamp + ) public view returns (bytes32) { + return blockRoots[uint64(timestamp)]; + } + + function setBlockRoot(uint64 timestamp, bytes32 blockRoot) public { + blockRoots[timestamp] = blockRoot; + } +} diff --git a/test/integration/tests/Full_Register_Deregister.t.sol b/test/integration/tests/Full_Register_Deregister.t.sol index 48bb150a..be7f672b 100644 --- a/test/integration/tests/Full_Register_Deregister.t.sol +++ b/test/integration/tests/Full_Register_Deregister.t.sol @@ -1,18 +1,19 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "test/integration/User.t.sol"; import "test/integration/IntegrationChecks.t.sol"; contract Integration_Full_Register_Deregister is IntegrationChecks { - using BitmapUtils for *; // 1. Register for all quorums by churning old operators // 2. Deregister from all quorums // 3. Re-register for all quorums without needing churn - function testFuzz_churnAll_deregisterAll_reregisterAll(uint24 _random) public { + function testFuzz_churnAll_deregisterAll_reregisterAll( + uint24 _random + ) public { _configRand({ _randomSeed: _random, _userTypes: DEFAULT | ALT_METHODS, @@ -60,7 +61,9 @@ contract Integration_Full_Register_Deregister is IntegrationChecks { // 2. Deregister from all quorums // 3. Old operators re-register for quorums // 4. Original operator re-registers for all quorums by churning old operators again - function testFuzz_churnAll_deregisterAll_oldReregisterAll(uint24 _random) public { + function testFuzz_churnAll_deregisterAll_oldReregisterAll( + uint24 _random + ) public { _configRand({ _randomSeed: _random, _userTypes: DEFAULT | ALT_METHODS, @@ -101,7 +104,7 @@ contract Integration_Full_Register_Deregister is IntegrationChecks { // 3. Old operators re-register for quorums // Note: churnTargets.length == quorums.length, so we do these one at a time - for (uint i = 0; i < churnTargets.length; i++) { + for (uint256 i = 0; i < churnTargets.length; i++) { User churnTarget = churnTargets[i]; bytes memory quorum = new bytes(1); quorum[0] = quorums[i]; @@ -109,7 +112,7 @@ contract Integration_Full_Register_Deregister is IntegrationChecks { churnTarget.registerOperator(quorum); check_Register_State(churnTarget, quorum); } - + // 4. Original operator re-registers for all quorums by churning old operators again operator.registerOperatorWithChurn(quorums, churnTargets, new bytes(0)); check_Churned_State({ @@ -123,7 +126,9 @@ contract Integration_Full_Register_Deregister is IntegrationChecks { // 1. Register for *some* quorums with churn, and the rest without churn // 2. Deregister from all quorums // 3. Re-register for all quorums without needing churn - function testFuzz_churnSome_deregisterSome_deregisterRemaining(uint24 _random) public { + function testFuzz_churnSome_deregisterSome_deregisterRemaining( + uint24 _random + ) public { _configRand({ _randomSeed: _random, _userTypes: DEFAULT | ALT_METHODS, @@ -140,11 +145,9 @@ contract Integration_Full_Register_Deregister is IntegrationChecks { // Select some quorums to register using churn, and the rest without churn bytes memory churnQuorums = _selectRand(quorums); - bytes memory standardQuorums = - quorums - .orderedBytesArrayToBitmap() - .minus(churnQuorums.orderedBytesArrayToBitmap()) - .bitmapToBytesArray(); + bytes memory standardQuorums = quorums.orderedBytesArrayToBitmap().minus( + churnQuorums.orderedBytesArrayToBitmap() + ).bitmapToBytesArray(); // Select churnable operators in each quorum. If needed, deals/deposits assets // for the operator, and deregisters operators from standardQuorums to make room diff --git a/test/integration/tests/NonFull_Register_CoreBalanceChange_Update.t.sol b/test/integration/tests/NonFull_Register_CoreBalanceChange_Update.t.sol index 43daf518..679ea1b5 100644 --- a/test/integration/tests/NonFull_Register_CoreBalanceChange_Update.t.sol +++ b/test/integration/tests/NonFull_Register_CoreBalanceChange_Update.t.sol @@ -1,17 +1,18 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "test/integration/User.t.sol"; import "test/integration/IntegrationChecks.t.sol"; contract Integration_NonFull_Register_CoreBalanceChange_Update is IntegrationChecks { - // 1. Register for all quorums // 2. (core) Deposit additional tokens // 3. Update stakes // 4. Deregister from all quorums - function testFuzz_registerAll_increaseCoreBalance_update_deregisterAll(uint24 _random) public { + function testFuzz_registerAll_increaseCoreBalance_update_deregisterAll( + uint24 _random + ) public { _configRand({ _randomSeed: _random, _userTypes: DEFAULT | ALT_METHODS, @@ -33,10 +34,7 @@ contract Integration_NonFull_Register_CoreBalanceChange_Update is IntegrationChe check_Register_State(operator, quorums); // Award operator tokens to deposit into core - ( - IStrategy[] memory strategies, - uint[] memory tokenBalances - ) = _dealRandTokens(operator); + (IStrategy[] memory strategies, uint256[] memory tokenBalances) = _dealRandTokens(operator); // 2. (core) Deposit tokens and return the weight added in each initialized quorum operator.depositIntoEigenlayer(strategies, tokenBalances); @@ -56,7 +54,9 @@ contract Integration_NonFull_Register_CoreBalanceChange_Update is IntegrationChe // 1. Register for all quorums // 2. (core) Deposit additional tokens // 3. Deregister from all quorums - function testFuzz_registerAll_increaseCoreBalance_deregisterAll(uint24 _random) public { + function testFuzz_registerAll_increaseCoreBalance_deregisterAll( + uint24 _random + ) public { _configRand({ _randomSeed: _random, _userTypes: DEFAULT | ALT_METHODS, @@ -78,10 +78,7 @@ contract Integration_NonFull_Register_CoreBalanceChange_Update is IntegrationChe check_Register_State(operator, quorums); // Award operator tokens to deposit into core - ( - IStrategy[] memory strategies, - uint[] memory tokenBalances - ) = _dealRandTokens(operator); + (IStrategy[] memory strategies, uint256[] memory tokenBalances) = _dealRandTokens(operator); // 2. (core) Deposit tokens operator.depositIntoEigenlayer(strategies, tokenBalances); @@ -96,7 +93,9 @@ contract Integration_NonFull_Register_CoreBalanceChange_Update is IntegrationChe // 1. Register for all quorums // 2. (core) Queue full withdrawal // 3. updateOperators/updateOperatorsForQuorum - function testFuzz_registerAll_decreaseCoreBalance_update(uint24 _random) public { + function testFuzz_registerAll_decreaseCoreBalance_update( + uint24 _random + ) public { _configRand({ _randomSeed: _random, _userTypes: DEFAULT | ALT_METHODS, @@ -118,7 +117,7 @@ contract Integration_NonFull_Register_CoreBalanceChange_Update is IntegrationChe check_Register_State(operator, quorums); // 2. (core) queue full withdrawal - (IStrategy[] memory strategies, uint[] memory shares) = operator.exitEigenlayer(); + (IStrategy[] memory strategies, uint256[] memory shares) = operator.exitEigenlayer(); check_Withdraw_State(operator, quorums, strategies, shares); // 3. Update stakes @@ -129,7 +128,9 @@ contract Integration_NonFull_Register_CoreBalanceChange_Update is IntegrationChe // 1. Register for all quorums // 2. (core) Queue full withdrawal // 3. Deregister from all quorums - function testFuzz_registerAll_decreaseCoreBalance_deregisterAll(uint24 _random) public { + function testFuzz_registerAll_decreaseCoreBalance_deregisterAll( + uint24 _random + ) public { _configRand({ _randomSeed: _random, _userTypes: DEFAULT | ALT_METHODS, @@ -151,7 +152,7 @@ contract Integration_NonFull_Register_CoreBalanceChange_Update is IntegrationChe check_Register_State(operator, quorums); // 2. (core) queue full withdrawal - (IStrategy[] memory strategies, uint[] memory shares) = operator.exitEigenlayer(); + (IStrategy[] memory strategies, uint256[] memory shares) = operator.exitEigenlayer(); check_Withdraw_State(operator, quorums, strategies, shares); // 3. Deregister from all quorums @@ -163,7 +164,9 @@ contract Integration_NonFull_Register_CoreBalanceChange_Update is IntegrationChe // 1. Register for all quorums // 2. updateOperators/updateOperatorsForQuorum // 3. Deregister from all quorums - function testFuzz_registerAll_update_deregisterAll(uint24 _random) public { + function testFuzz_registerAll_update_deregisterAll( + uint24 _random + ) public { _configRand({ _randomSeed: _random, _userTypes: DEFAULT | ALT_METHODS, diff --git a/test/integration/tests/NonFull_Register_Deregister.t.sol b/test/integration/tests/NonFull_Register_Deregister.t.sol index c30c5261..5eea66e4 100644 --- a/test/integration/tests/NonFull_Register_Deregister.t.sol +++ b/test/integration/tests/NonFull_Register_Deregister.t.sol @@ -1,17 +1,18 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "test/integration/User.t.sol"; import "test/integration/IntegrationChecks.t.sol"; contract Integration_NonFull_Register_Deregister is IntegrationChecks { - using BitmapUtils for *; // 1. Register for all quorums // 2. Deregister from all quorums - function testFuzz_registerAll_deregisterAll(uint24 _random) public { + function testFuzz_registerAll_deregisterAll( + uint24 _random + ) public { _configRand({ _randomSeed: _random, _userTypes: DEFAULT | ALT_METHODS, @@ -41,7 +42,9 @@ contract Integration_NonFull_Register_Deregister is IntegrationChecks { // 1. Register for some quorums // 2. Deregister from some quorums // 3. Deregister from any remaining quorums - function testFuzz_registerSome_deregisterSome_deregisterRemaining(uint24 _random) public { + function testFuzz_registerSome_deregisterSome_deregisterRemaining( + uint24 _random + ) public { _configRand({ _randomSeed: _random, _userTypes: DEFAULT | ALT_METHODS, @@ -69,7 +72,7 @@ contract Integration_NonFull_Register_Deregister is IntegrationChecks { check_Deregister_State(operator, quorumsToRemove); // 3. Deregister from any remaining quorums - bytes memory quorumsRemaining = _calcRemaining({ start: quorums, removed: quorumsToRemove }); + bytes memory quorumsRemaining = _calcRemaining({start: quorums, removed: quorumsToRemove}); if (quorumsRemaining.length != 0) { operator.deregisterOperator(quorumsRemaining); check_Deregister_State(operator, quorumsRemaining); @@ -80,7 +83,9 @@ contract Integration_NonFull_Register_Deregister is IntegrationChecks { // 1. Register for some quorums // 2. Deregister from some quorums // 3. Reregister for some quorums - function testFuzz_registerSome_deregisterSome_reregisterSome(uint24 _random) public { + function testFuzz_registerSome_deregisterSome_reregisterSome( + uint24 _random + ) public { _configRand({ _randomSeed: _random, _userTypes: DEFAULT | ALT_METHODS, diff --git a/test/integration/utils/BitmapStrings.t.sol b/test/integration/utils/BitmapStrings.t.sol index 532f5d8d..7235dab5 100644 --- a/test/integration/utils/BitmapStrings.t.sol +++ b/test/integration/utils/BitmapStrings.t.sol @@ -1,30 +1,24 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "@openzeppelin/contracts/utils/Strings.sol"; library BitmapStrings { - using Strings for *; /// @dev Given an input quorum array, returns a nice, readable string: /// e.g. [0, 1, 2, ...] /// (This is way more readable than logging with log_named_bytes) - function toString(bytes memory bitmapArr) internal pure returns (string memory) { + function toString( + bytes memory bitmapArr + ) internal pure returns (string memory) { string memory result = "["; - for (uint i = 0; i < bitmapArr.length; i++) { + for (uint256 i = 0; i < bitmapArr.length; i++) { if (i == bitmapArr.length - 1) { - result = string.concat( - result, - uint(uint8(bitmapArr[i])).toString() - ); + result = string.concat(result, uint256(uint8(bitmapArr[i])).toString()); } else { - result = string.concat( - result, - uint(uint8(bitmapArr[i])).toString(), - ", " - ); + result = string.concat(result, uint256(uint8(bitmapArr[i])).toString(), ", "); } } diff --git a/test/integration/utils/Sort.t.sol b/test/integration/utils/Sort.t.sol index 46a2fc2b..884a02f5 100644 --- a/test/integration/utils/Sort.t.sol +++ b/test/integration/utils/Sort.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; library Sort { /** @@ -8,7 +8,9 @@ library Sort { * @param addresses The array of addresses to be sorted. * @return sortedAddresses The array of addresses sorted in ascending order. */ - function sortAddresses(address[] memory addresses) internal pure returns (address[] memory) { + function sortAddresses( + address[] memory addresses + ) internal pure returns (address[] memory) { uint256 n = addresses.length; for (uint256 i = 0; i < n; i++) { for (uint256 j = 0; j < n - 1; j++) { diff --git a/test/mocks/AVSDirectoryMock.sol b/test/mocks/AVSDirectoryMock.sol index deef83da..d76f1abf 100644 --- a/test/mocks/AVSDirectoryMock.sol +++ b/test/mocks/AVSDirectoryMock.sol @@ -1,23 +1,86 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; -import {IAVSDirectory, ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol"; +import {IAVSDirectory} from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol"; +import {OperatorSet} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; +import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol"; +import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; +import {IPauserRegistry} from "eigenlayer-contracts/src/contracts/interfaces/IPauserRegistry.sol"; contract AVSDirectoryMock is IAVSDirectory { - mapping(address => mapping(bytes32 => bool)) public operatorSaltIsSpentMapping; - + function initialize(address initialOwner, uint256 initialPausedStatus) external {} + + function createOperatorSets( + uint32[] calldata operatorSetIds + ) external {} + + function becomeOperatorSetAVS() external {} + + function migrateOperatorsToOperatorSets( + address[] calldata operators, + uint32[][] calldata operatorSetIds + ) external {} + + function registerOperatorToOperatorSets( + address operator, + uint32[] calldata operatorSetIds, + ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature + ) external {} + + function forceDeregisterFromOperatorSets( + address operator, + address avs, + uint32[] calldata operatorSetIds, + ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature + ) external {} + + function deregisterOperatorFromOperatorSets( + address operator, + uint32[] calldata operatorSetIds + ) external {} + + function addStrategiesToOperatorSet( + uint32 operatorSetId, + IStrategy[] calldata strategies + ) external {} + + function removeStrategiesFromOperatorSet( + uint32 operatorSetId, + IStrategy[] calldata strategies + ) external {} + + function updateAVSMetadataURI( + string calldata metadataURI + ) external {} + + function cancelSalt( + bytes32 salt + ) external {} + function registerOperatorToAVS( address operator, ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature ) external {} - function deregisterOperatorFromAVS(address operator) external {} + function deregisterOperatorFromAVS( + address operator + ) external {} - function updateAVSMetadataURI(string calldata metadataURI) external {} + function operatorSaltIsSpent(address operator, bytes32 salt) external view returns (bool) {} - function operatorSaltIsSpent(address operator, bytes32 salt) external view returns (bool) { - return operatorSaltIsSpentMapping[operator][salt]; - } + function operatorSetsEnabled( + address avs + ) external view returns (bool) {} + + function isOperatorSet(address avs, uint32 operatorSetId) external view returns (bool) {} + + function getNumOperatorSetsOfOperator( + address operator + ) external view returns (uint256) {} + + function inTotalOperatorSets( + address operator + ) external view returns (uint256) {} function calculateOperatorAVSRegistrationDigestHash( address operator, @@ -26,9 +89,66 @@ contract AVSDirectoryMock is IAVSDirectory { uint256 expiry ) external view returns (bytes32) {} + function calculateOperatorSetRegistrationDigestHash( + address avs, + uint32[] calldata operatorSetIds, + bytes32 salt, + uint256 expiry + ) external view returns (bytes32) {} + + function calculateOperatorSetForceDeregistrationTypehash( + address avs, + uint32[] calldata operatorSetIds, + bytes32 salt, + uint256 expiry + ) external view returns (bytes32) {} + function OPERATOR_AVS_REGISTRATION_TYPEHASH() external view returns (bytes32) {} - function cancelSalt(bytes32 salt) external {} + function OPERATOR_SET_REGISTRATION_TYPEHASH() external view returns (bytes32) {} + + function operatorSetStatus( + address avs, + address operator, + uint32 operatorSetId + ) external view returns (bool registered, uint32 lastDeregisteredTimestamp) {} + + function initialize( + address initialOwner, + IPauserRegistry _pauserRegistry, + uint256 initialPausedStatus + ) external {} + + function operatorSetMemberAtIndex( + OperatorSet memory operatorSet, + uint256 index + ) external view returns (address) {} + + function getOperatorsInOperatorSet( + OperatorSet memory operatorSet, + uint256 start, + uint256 length + ) external view returns (address[] memory operators) {} + + function getStrategiesInOperatorSet( + OperatorSet memory operatorSet + ) external view returns (IStrategy[] memory strategies) {} + + function getNumOperatorsInOperatorSet( + OperatorSet memory operatorSet + ) external view returns (uint256) {} + + function isMember( + address operator, + OperatorSet memory operatorSet + ) external view returns (bool) {} + + function isOperatorSlashable( + address operator, + OperatorSet memory operatorSet + ) external view returns (bool) {} - function domainSeparator() external view returns (bytes32) {} -} \ No newline at end of file + function isOperatorSetBatch( + OperatorSet[] calldata operatorSets + ) external view returns (bool) {} +} diff --git a/test/mocks/AVSRegistrarMock.sol b/test/mocks/AVSRegistrarMock.sol new file mode 100644 index 00000000..9a61b26e --- /dev/null +++ b/test/mocks/AVSRegistrarMock.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.27; + +import {IAVSRegistrar} from "eigenlayer-contracts/src/contracts/interfaces/IAVSRegistrar.sol"; + +contract AVSRegistrarMock is IAVSRegistrar { + function registerOperator( + address operator, + uint32[] calldata operatorSetIds, + bytes calldata data + ) external override {} + + function deregisterOperator( + address operator, + uint32[] calldata operatorSetIds + ) external override {} +} diff --git a/test/mocks/AllocationManagerMock.sol b/test/mocks/AllocationManagerMock.sol new file mode 100644 index 00000000..22b92b74 --- /dev/null +++ b/test/mocks/AllocationManagerMock.sol @@ -0,0 +1,154 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.27; + +import { + IAllocationManager, + OperatorSet +} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; +import {IAVSRegistrar} from "eigenlayer-contracts/src/contracts/interfaces/IAVSRegistrar.sol"; +import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; +import {IPauserRegistry} from "eigenlayer-contracts/src/contracts/interfaces/IPauserRegistry.sol"; + +contract AllocationManagerIntermediate is IAllocationManager { + function initialize(address initialOwner, uint256 initialPausedStatus) external virtual {} + + function slashOperator(address avs, SlashingParams calldata params) external virtual {} + + function modifyAllocations( + address operator, + AllocateParams[] calldata params + ) external virtual {} + + function clearDeallocationQueue( + address operator, + IStrategy[] calldata strategies, + uint16[] calldata numToClear + ) external virtual {} + + function registerForOperatorSets( + address operator, + RegisterParams calldata params + ) external virtual {} + + function deregisterFromOperatorSets( + DeregisterParams calldata params + ) external virtual {} + + function setAllocationDelay(address operator, uint32 delay) external virtual {} + + function setAVSRegistrar(address avs, IAVSRegistrar registrar) external virtual {} + + function updateAVSMetadataURI(address avs, string calldata metadataURI) external virtual {} + + function createOperatorSets(address avs, CreateSetParams[] calldata params) external virtual {} + + function addStrategiesToOperatorSet( + address avs, + uint32 operatorSetId, + IStrategy[] calldata strategies + ) external virtual {} + + function removeStrategiesFromOperatorSet( + address avs, + uint32 operatorSetId, + IStrategy[] calldata strategies + ) external virtual {} + + function getOperatorSetCount( + address avs + ) external view virtual returns (uint256) {} + + function getAllocatedSets( + address operator + ) external view virtual returns (OperatorSet[] memory) {} + + function getAllocatedStrategies( + address operator, + OperatorSet memory operatorSet + ) external view virtual returns (IStrategy[] memory) {} + + function getAllocation( + address operator, + OperatorSet memory operatorSet, + IStrategy strategy + ) external view virtual returns (Allocation memory) {} + + function getAllocations( + address[] memory operators, + OperatorSet memory operatorSet, + IStrategy strategy + ) external view virtual returns (Allocation[] memory) {} + + function getStrategyAllocations( + address operator, + IStrategy strategy + ) external view virtual returns (OperatorSet[] memory, Allocation[] memory) {} + + function getAllocatableMagnitude( + address operator, + IStrategy strategy + ) external view virtual returns (uint64) {} + + function getMaxMagnitude( + address operator, + IStrategy strategy + ) external view virtual returns (uint64) {} + + function getMaxMagnitudes( + address operator, + IStrategy[] calldata strategies + ) external view virtual returns (uint64[] memory) {} + + function getMaxMagnitudes( + address[] calldata operators, + IStrategy strategy + ) external view virtual returns (uint64[] memory) {} + + function getMaxMagnitudesAtBlock( + address operator, + IStrategy[] calldata strategies, + uint32 blockNumber + ) external view virtual returns (uint64[] memory) {} + + function getAllocationDelay( + address operator + ) external view virtual returns (bool isSet, uint32 delay) {} + + function getRegisteredSets( + address operator + ) external view virtual returns (OperatorSet[] memory operatorSets) {} + + function isOperatorSet( + OperatorSet memory operatorSet + ) external view virtual returns (bool) {} + + function getMembers( + OperatorSet memory operatorSet + ) external view virtual returns (address[] memory operators) {} + + function getMemberCount( + OperatorSet memory operatorSet + ) external view virtual returns (uint256) {} + + function getAVSRegistrar( + address avs + ) external view virtual returns (IAVSRegistrar) {} + + function getStrategiesInOperatorSet( + OperatorSet memory operatorSet + ) external view virtual returns (IStrategy[] memory strategies) {} + + function getMinimumSlashableStake( + OperatorSet memory operatorSet, + address[] memory operators, + IStrategy[] memory strategies, + uint32 futureBlock + ) external view virtual returns (uint256[][] memory slashableStake) {} + + function isMemberOfOperatorSet( + address operator, + OperatorSet memory operatorSet + ) external view virtual returns (bool) {} +} + +contract AllocationManagerMock is AllocationManagerIntermediate {} diff --git a/test/mocks/DelegationMock.sol b/test/mocks/DelegationMock.sol index 190e1aa5..11bce967 100644 --- a/test/mocks/DelegationMock.sol +++ b/test/mocks/DelegationMock.sol @@ -1,209 +1,288 @@ // 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"; - - -contract DelegationMock is IDelegationManager { - mapping(address => bool) public isOperator; - mapping(address => mapping(IStrategy => uint256)) public operatorShares; - - function getDelegatableShares(address staker) external view returns (IStrategy[] memory, uint256[] memory) {} +pragma solidity ^0.8.27; + +import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import {console2 as console} from "forge-std/Test.sol"; + +import {IDelegationManager} from + "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; +import {IStrategyManager} from "eigenlayer-contracts/src/contracts/interfaces/IStrategyManager.sol"; +import {StrategyManager} from "eigenlayer-contracts/src/contracts/core/StrategyManager.sol"; +import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; +import {IPauserRegistry} from "eigenlayer-contracts/src/contracts/interfaces/IPauserRegistry.sol"; +import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol"; +import {SlashingLib} from "eigenlayer-contracts/src/contracts/libraries/SlashingLib.sol"; + +contract DelegationIntermediate is IDelegationManager { + function initialize(address initialOwner, uint256 initialPausedStatus) external virtual {} + + function registerAsOperator( + OperatorDetails calldata registeringOperatorDetails, + uint32 allocationDelay, + string calldata metadataURI + ) external virtual {} + + function modifyOperatorDetails( + OperatorDetails calldata newOperatorDetails + ) external virtual {} + + function updateOperatorMetadataURI( + string calldata metadataURI + ) external virtual {} + + function delegateTo( + address operator, + SignatureWithExpiry memory approverSignatureAndExpiry, + bytes32 approverSalt + ) external virtual {} - function setMinWithdrawalDelayBlocks(uint256 newMinWithdrawalDelayBlocks) external {} + function undelegate( + address staker + ) external virtual returns (bytes32[] memory withdrawalRoots) {} - function setStrategyWithdrawalDelayBlocks(IStrategy[] calldata strategies, uint256[] calldata withdrawalDelayBlocks) external {} + function queueWithdrawals( + QueuedWithdrawalParams[] calldata params + ) external virtual returns (bytes32[] memory) {} - function setIsOperator(address operator, bool _isOperatorReturnValue) external { - isOperator[operator] = _isOperatorReturnValue; - } + function completeQueuedWithdrawals( + IERC20[][] calldata tokens, + bool[] calldata receiveAsTokens, + uint256 numToComplete + ) external virtual {} - /// @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; - } + function completeQueuedWithdrawal( + Withdrawal calldata withdrawal, + IERC20[] calldata tokens, + bool receiveAsTokens + ) external virtual {} - mapping (address => address) public delegatedTo; + function completeQueuedWithdrawals( + Withdrawal[] calldata withdrawals, + IERC20[][] calldata tokens, + bool[] calldata receiveAsTokens + ) external virtual {} - function registerAsOperator(OperatorDetails calldata /*registeringOperatorDetails*/, string calldata /*metadataURI*/) external pure {} - - function updateOperatorMetadataURI(string calldata /*metadataURI*/) external pure {} + function increaseDelegatedShares( + address staker, + IStrategy strategy, + uint256 existingDepositShares, + uint256 addedShares + ) external virtual {} - function updateAVSMetadataURI(string calldata /*metadataURI*/) external pure {} + function decreaseBeaconChainScalingFactor( + address staker, + uint256 existingShares, + uint64 proportionOfOldBalance + ) external virtual {} - function delegateTo(address operator, SignatureWithExpiry memory /*approverSignatureAndExpiry*/, bytes32 /*approverSalt*/) external { - delegatedTo[msg.sender] = operator; - } + function burnOperatorShares( + address operator, + IStrategy strategy, + uint64 prevMaxMagnitude, + uint64 newMaxMagnitude + ) external virtual {} - function modifyOperatorDetails(OperatorDetails calldata /*newOperatorDetails*/) external pure {} + function completeQueuedWithdrawal( + Withdrawal calldata withdrawal, + IERC20[] calldata tokens, + uint256 middlewareTimesIndex, + bool receiveAsTokens + ) external virtual {} - function delegateToBySignature( - address /*staker*/, - address /*operator*/, - SignatureWithExpiry memory /*stakerSignatureAndExpiry*/, - SignatureWithExpiry memory /*approverSignatureAndExpiry*/, - bytes32 /*approverSalt*/ - ) external pure {} + function completeQueuedWithdrawals( + Withdrawal[] calldata withdrawals, + IERC20[][] calldata tokens, + uint256[] calldata middlewareTimesIndexes, + bool[] calldata receiveAsTokens + ) external virtual {} - function undelegate(address staker) external returns (bytes32[] memory withdrawalRoot) { - delegatedTo[staker] = address(0); - return withdrawalRoot; - } + function delegatedTo( + address staker + ) external view virtual returns (address) {} - function increaseDelegatedShares(address /*staker*/, IStrategy /*strategy*/, uint256 /*shares*/) external pure {} + function delegationApproverSaltIsSpent( + address _delegationApprover, + bytes32 salt + ) external view virtual returns (bool) {} - function decreaseDelegatedShares( - address /*staker*/, - IStrategy /*strategy*/, - uint256 /*shares*/ - ) external pure {} - - function operatorDetails(address operator) external pure returns (OperatorDetails memory) { - OperatorDetails memory returnValue = OperatorDetails({ - __deprecated_earningsReceiver: operator, - delegationApprover: operator, - stakerOptOutWindowBlocks: 0 - }); - return returnValue; - } + function cumulativeWithdrawalsQueued( + address staker + ) external view virtual returns (uint256) {} - function delegationApprover(address operator) external pure returns (address) { - return operator; - } + function isDelegated( + address staker + ) external view virtual returns (bool) {} - function stakerOptOutWindowBlocks(address /*operator*/) external pure returns (uint256) { - return 0; - } + function isOperator( + address operator + ) external view virtual returns (bool) {} - function minWithdrawalDelayBlocks() external pure returns (uint256) { - return 0; - } + function operatorDetails( + address operator + ) external view virtual returns (OperatorDetails memory) {} - /// @notice return address of the beaconChainETHStrategy - function beaconChainETHStrategy() external view returns (IStrategy) {} + function delegationApprover( + address operator + ) external view virtual returns (address) {} - /** - * @notice Minimum delay enforced by this contract per Strategy for completing queued withdrawals. Measured in blocks, and adjustable by this contract's owner, - * up to a maximum of `MAX_WITHDRAWAL_DELAY_BLOCKS`. Minimum value is 0 (i.e. no delay enforced). - */ - function strategyWithdrawalDelayBlocks(IStrategy /*strategy*/) external pure returns (uint256) { - return 0; - } - function getOperatorShares( address operator, IStrategy[] memory strategies - ) external view returns (uint256[] memory) { - uint256[] memory shares = new uint256[](strategies.length); - for (uint256 i = 0; i < strategies.length; i++) { - shares[i] = operatorShares[operator][strategies[i]]; - } - return shares; - } - - function getWithdrawalDelay(IStrategy[] calldata /*strategies*/) public pure returns (uint256) { - return type(uint256).max; - } - - function isDelegated(address staker) external view returns (bool) { - return (delegatedTo[staker] != address(0)); - } + ) external view virtual returns (uint256[] memory) {} - 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 getOperatorsShares( + address[] memory operators, + IStrategy[] memory strategies + ) external view virtual returns (uint256[][] memory) {} - function calculateCurrentStakerDelegationDigestHash(address /*staker*/, address /*operator*/, uint256 /*expiry*/) external view returns (bytes32) {} + function getSlashableSharesInQueue( + address operator, + IStrategy strategy + ) external view virtual returns (uint256) {} - function calculateStakerDelegationDigestHash(address /*staker*/, uint256 /*stakerNonce*/, address /*operator*/, uint256 /*expiry*/) external view returns (bytes32) {} + function getWithdrawableShares( + address staker, + IStrategy[] memory strategies + ) + external + view + virtual + override + returns (uint256[] memory withdrawableShares, uint256[] memory depositShares) + {} + + function getDepositedShares( + address staker + ) external view virtual returns (IStrategy[] memory, uint256[] memory) {} + + function depositScalingFactor( + address staker, + IStrategy strategy + ) external view virtual returns (uint256) {} + + function getBeaconChainSlashingFactor( + address staker + ) external view virtual returns (uint64) {} + + function getQueuedWithdrawals( + address staker + ) + external + view + virtual + override + returns (Withdrawal[] memory withdrawals, uint256[][] memory shares) + {} + + function calculateWithdrawalRoot( + Withdrawal memory withdrawal + ) external pure virtual 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) {} + address staker, + address operator, + address _delegationApprover, + bytes32 approverSalt, + uint256 expiry + ) external view virtual returns (bytes32) {} - function calculateApproverDigestHash(address /*staker*/, address /*operator*/, uint256 /*expiry*/) - external pure returns (bytes32 approverDigestHash) {} + function beaconChainETHStrategy() external view virtual override returns (IStrategy) {} - function calculateOperatorAVSRegistrationDigestHash(address /*operator*/, address /*avs*/, bytes32 /*salt*/, uint256 /*expiry*/) - external pure returns (bytes32 digestHash) {} + function DELEGATION_APPROVAL_TYPEHASH() external view virtual override returns (bytes32) {} - function DOMAIN_TYPEHASH() external view returns (bytes32) {} + function registerAsOperator( + address initDelegationApprover, + uint32 allocationDelay, + string calldata metadataURI + ) external virtual {} - function STAKER_DELEGATION_TYPEHASH() external view returns (bytes32) {} + function modifyOperatorDetails( + address operator, + address newDelegationApprover + ) external virtual {} - function DELEGATION_APPROVAL_TYPEHASH() external view returns (bytes32) {} + function updateOperatorMetadataURI( + address operator, + string calldata metadataURI + ) external virtual {} - function OPERATOR_AVS_REGISTRATION_TYPEHASH() external view returns (bytes32) {} + function redelegate( + address newOperator, + SignatureWithExpiry memory newOperatorApproverSig, + bytes32 approverSalt + ) external virtual returns (bytes32[] memory withdrawalRoots) {} - function cumulativeWithdrawalsQueued(address staker) external view returns (uint256) {} + function decreaseDelegatedShares( + address staker, + uint256 curDepositShares, + uint64 prevBeaconChainSlashingFactor, + uint256 wadSlashed + ) external virtual {} - function calculateWithdrawalRoot(Withdrawal memory withdrawal) external pure returns (bytes32) {} + function decreaseDelegatedShares( + address staker, + uint256 curDepositShares, + uint64 beaconChainSlashingFactorDecrease + ) external virtual {} - function registerOperatorToAVS(address operator, ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature) external {} + function minWithdrawalDelayBlocks() external view virtual override returns (uint32) {} - function deregisterOperatorFromAVS(address operator) external {} + function slashOperatorShares( + address operator, + IStrategy strategy, + uint64 prevMaxMagnitude, + uint64 newMaxMagnitude + ) external override {} - function operatorSaltIsSpent(address avs, bytes32 salt) external view returns (bool) {} + function getQueuedWithdrawal( + bytes32 withdrawalRoot + ) external view override returns (Withdrawal memory) {} - function domainSeparator() external view returns (bytes32) {} + function getQueuedWithdrawalRoots( + address staker + ) external view override returns (bytes32[] memory) {} - function queueWithdrawals( - QueuedWithdrawalParams[] calldata queuedWithdrawalParams - ) external returns (bytes32[] memory) {} + function convertToDepositShares( + address staker, + IStrategy[] memory strategies, + uint256[] memory withdrawableShares + ) external view override returns (uint256[] memory) {} +} - function completeQueuedWithdrawal( - Withdrawal calldata withdrawal, - IERC20[] calldata tokens, - uint256 middlewareTimesIndex, - bool receiveAsTokens - ) external {} +contract DelegationMock is DelegationIntermediate { + mapping(address => bool) internal _isOperator; + mapping(address => mapping(IStrategy => uint256)) internal _weightOf; - function completeQueuedWithdrawals( - Withdrawal[] calldata withdrawals, - IERC20[][] calldata tokens, - uint256[] calldata middlewareTimesIndexes, - bool[] calldata receiveAsTokens - ) external {} - - // onlyDelegationManager functions in StrategyManager - function addShares( - IStrategyManager strategyManager, - address staker, - IERC20 token, + function setOperatorShares( + address operator, IStrategy strategy, - uint256 shares + uint256 actualWeight ) external { - strategyManager.addShares(staker, token, strategy, shares); + _weightOf[operator][strategy] = actualWeight; } - function removeShares( - IStrategyManager strategyManager, - address staker, - IStrategy strategy, - uint256 shares - ) external { - strategyManager.removeShares(staker, strategy, shares); + function setIsOperator(address operator, bool isOperator) external { + _isOperator[operator] = isOperator; } - function withdrawSharesAsTokens( - IStrategyManager strategyManager, - address recipient, - IStrategy strategy, - uint256 shares, - IERC20 token - ) external { - strategyManager.withdrawSharesAsTokens(recipient, strategy, shares, token); + function isOperator( + address operator + ) external view override returns (bool) { + return _isOperator[operator]; + } + + function getOperatorShares( + address operator, + IStrategy[] calldata strategies + ) external view override returns (uint256[] memory) { + uint256[] memory shares = new uint256[](strategies.length); + for (uint256 i = 0; i < strategies.length; i++) { + shares[i] = _weightOf[operator][strategies[i]]; + } + return shares; + } + + function minWithdrawalDelayBlocks() external view override returns (uint32) { + return 10000; } } diff --git a/test/mocks/ECDSAServiceManagerMock.sol b/test/mocks/ECDSAServiceManagerMock.sol index 528270ae..07a263cf 100644 --- a/test/mocks/ECDSAServiceManagerMock.sol +++ b/test/mocks/ECDSAServiceManagerMock.sol @@ -1,16 +1,25 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "../../src/unaudited/ECDSAServiceManagerBase.sol"; +import {IAllocationManagerTypes} from + "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; contract ECDSAServiceManagerMock is ECDSAServiceManagerBase { constructor( address _avsDirectory, address _stakeRegistry, address _rewardsCoordinator, - address _delegationManager + address _delegationManager, + address _allocationManager ) - ECDSAServiceManagerBase(_avsDirectory, _stakeRegistry, _rewardsCoordinator, _delegationManager) + ECDSAServiceManagerBase( + _avsDirectory, + _stakeRegistry, + _rewardsCoordinator, + _delegationManager, + _allocationManager + ) {} function initialize( @@ -19,4 +28,25 @@ contract ECDSAServiceManagerMock is ECDSAServiceManagerBase { ) public virtual initializer { __ServiceManagerBase_init(initialOwner, rewardsInitiator); } + + function addPendingAdmin( + address admin + ) external {} + + function removePendingAdmin( + address pendingAdmin + ) external {} + + function deregisterOperatorFromOperatorSets( + address operator, + uint32[] memory operatorSetIds + ) external {} + + function removeAdmin( + address admin + ) external {} + + function setAppointee(address appointee, address target, bytes4 selector) external {} + + function removeAppointee(address appointee, address target, bytes4 selector) external {} } diff --git a/test/mocks/ECDSAStakeRegistryMock.sol b/test/mocks/ECDSAStakeRegistryMock.sol index 7ad6043e..fb43079b 100644 --- a/test/mocks/ECDSAStakeRegistryMock.sol +++ b/test/mocks/ECDSAStakeRegistryMock.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "../../src/unaudited/ECDSAStakeRegistry.sol"; @@ -8,7 +8,7 @@ import "../../src/unaudited/ECDSAStakeRegistry.sol"; * @dev This contract is a mock implementation of the ECDSAStakeRegistry for testing purposes. */ contract ECDSAStakeRegistryMock is ECDSAStakeRegistry { - - constructor(IDelegationManager _delegationManager) ECDSAStakeRegistry(_delegationManager) { - } + constructor( + IDelegationManager _delegationManager + ) ECDSAStakeRegistry(_delegationManager) {} } diff --git a/test/mocks/EigenPodManagerMock.sol b/test/mocks/EigenPodManagerMock.sol new file mode 100644 index 00000000..dccdbef8 --- /dev/null +++ b/test/mocks/EigenPodManagerMock.sol @@ -0,0 +1,112 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.9; + +import "forge-std/Test.sol"; +import "eigenlayer-contracts/src/contracts/permissions/Pausable.sol"; +import "eigenlayer-contracts/src/contracts/interfaces/IEigenPodManager.sol"; + +contract EigenPodManagerMock is Test, Pausable, IEigenPodManager { + receive() external payable {} + fallback() external payable {} + + mapping(address => int256) public podShares; + + constructor( + IPauserRegistry _pauserRegistry + ) Pausable(_pauserRegistry) { + _setPausedStatus(0); + } + + function podOwnerShares( + address podOwner + ) external view returns (int256) { + return podShares[podOwner]; + } + + function setPodOwnerShares(address podOwner, int256 shares) external { + podShares[podOwner] = shares; + } + + function denebForkTimestamp() external pure returns (uint64) { + return type(uint64).max; + } + + function createPod() external returns (address) {} + + function stake( + bytes calldata pubkey, + bytes calldata signature, + bytes32 depositDataRoot + ) external payable {} + + function recordBeaconChainETHBalanceUpdate( + address podOwner, + int256 sharesDelta, + uint64 proportionPodBalanceDecrease + ) external {} + + function ownerToPod( + address podOwner + ) external view returns (IEigenPod) {} + + function getPod( + address podOwner + ) external view returns (IEigenPod) {} + + function ethPOS() external view returns (IETHPOSDeposit) {} + + function eigenPodBeacon() external view returns (IBeacon) {} + + function strategyManager() external view returns (IStrategyManager) {} + + function hasPod( + address podOwner + ) external view returns (bool) {} + + function numPods() external view returns (uint256) {} + + function podOwnerDepositShares( + address podOwner + ) external view returns (int256) {} + + function beaconChainETHStrategy() external view returns (IStrategy) {} + + function removeDepositShares( + address staker, + IStrategy strategy, + uint256 depositSharesToRemove + ) external {} + + function stakerDepositShares( + address user, + IStrategy strategy + ) external view returns (uint256 depositShares) {} + + function withdrawSharesAsTokens( + address staker, + IStrategy strategy, + IERC20 token, + uint256 shares + ) external {} + + function addShares( + address staker, + IStrategy strategy, + IERC20 token, + uint256 shares + ) external returns (uint256, uint256) {} + + function beaconChainSlashingFactor( + address staker + ) external view returns (uint64) {} + + function recordBeaconChainETHBalanceUpdate( + address podOwner, + uint256 prevRestakedBalanceWei, + int256 balanceDeltaWei + ) external {} + + function burnableETHShares() external view returns (uint256) {} + + function increaseBurnableShares(IStrategy strategy, uint256 addedSharesToBurn) external {} +} diff --git a/test/mocks/PermissionControllerMock.sol b/test/mocks/PermissionControllerMock.sol new file mode 100644 index 00000000..11452675 --- /dev/null +++ b/test/mocks/PermissionControllerMock.sol @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.27; + +import {IPermissionController} from + "eigenlayer-contracts/src/contracts/interfaces/IPermissionController.sol"; + +contract PermissionControllerIntermediate is IPermissionController { + function addPendingAdmin(address account, address admin) external virtual {} + + function removePendingAdmin(address account, address admin) external virtual {} + + function acceptAdmin( + address account + ) external virtual {} + + function removeAdmin(address account, address admin) external virtual {} + + function setAppointee( + address account, + address appointee, + address target, + bytes4 selector + ) external virtual {} + + function removeAppointee( + address account, + address appointee, + address target, + bytes4 selector + ) external virtual {} + + function isAdmin(address account, address caller) external view virtual returns (bool) {} + + function isPendingAdmin( + address account, + address pendingAdmin + ) external view virtual returns (bool) {} + + function getAdmins( + address account + ) external view virtual returns (address[] memory) {} + + function getPendingAdmins( + address account + ) external view virtual returns (address[] memory) {} + + function canCall( + address account, + address caller, + address target, + bytes4 selector + ) external virtual returns (bool) {} + + function getAppointeePermissions( + address account, + address appointee + ) external virtual returns (address[] memory, bytes4[] memory) {} + + function getAppointees( + address account, + address target, + bytes4 selector + ) external virtual returns (address[] memory) {} +} + +contract PermissionControllerMock is PermissionControllerIntermediate { + mapping(address => mapping(address => mapping(address => mapping(bytes4 => bool)))) internal + _canCall; + + function setCanCall( + address account, + address caller, + address target, + bytes4 selector + ) external { + _canCall[account][caller][target][selector] = true; + } + + function canCall( + address account, + address caller, + address target, + bytes4 selector + ) external override returns (bool) { + if (account == caller) return true; + return _canCall[account][caller][target][selector]; + } +} diff --git a/test/mocks/RegistryCoordinatorMock.sol b/test/mocks/RegistryCoordinatorMock.sol index 378d357e..78c98660 100644 --- a/test/mocks/RegistryCoordinatorMock.sol +++ b/test/mocks/RegistryCoordinatorMock.sol @@ -1,73 +1,164 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; - +pragma solidity ^0.8.27; import "../../src/interfaces/IRegistryCoordinator.sol"; - - -contract RegistryCoordinatorMock is IRegistryCoordinator { - function blsApkRegistry() external view returns (IBLSApkRegistry) {} - - function ejectOperator( - address operator, +import "../../src/interfaces/ISlashingRegistryCoordinator.sol"; +import "../../src/libraries/BN254.sol"; + +abstract contract RegistryCoordinatorMock is IRegistryCoordinator { + // Add missing function declarations from interface + function OPERATOR_CHURN_APPROVAL_TYPEHASH() external pure virtual returns (bytes32); + function PUBKEY_REGISTRATION_TYPEHASH() external pure virtual returns (bytes32); + function allocationManager() external view virtual returns (IAllocationManager); + function calculateOperatorChurnApprovalDigestHash( + address registeringOperator, + bytes32 registeringOperatorId, + OperatorKickParam[] memory operatorKickParams, + bytes32 salt, + uint256 expiry + ) external view virtual returns (bytes32); + function churnApprover() external view virtual returns (address); + function createSlashableStakeQuorum( + OperatorSetParam memory operatorSetParams, + uint96 minimumStake, + IStakeRegistryTypes.StrategyParams[] memory strategyParams, + uint32 lookAheadPeriod + ) external virtual; + function createTotalDelegatedStakeQuorum( + OperatorSetParam memory operatorSetParams, + uint96 minimumStake, + IStakeRegistryTypes.StrategyParams[] memory strategyParams + ) external virtual; + function deregisterOperator( + address operator, + uint32[] memory operatorSetIds + ) external virtual; + function ejectionCooldown() external view virtual returns (uint256); + function ejector() external view virtual returns (address); + function enableOperatorSets() external virtual; + function initialize( + address _initialOwner, + address _churnApprover, + address _ejector, + uint256 _initialPausedStatus, + OperatorSetParam[] memory _operatorSetParams, + uint96[] memory _minimumStakes, + IStakeRegistryTypes.StrategyParams[][] memory _strategyParams, + IStakeRegistryTypes.StakeType[] memory _stakeTypes, + uint32[] memory _lookAheadPeriods + ) external virtual; + function isChurnApproverSaltUsed( + bytes32 salt + ) external view virtual returns (bool); + function lastEjectionTimestamp( + address operator + ) external view virtual returns (uint256); + function registerOperator( + address operator, + uint32[] memory operatorSetIds, + bytes memory data + ) external virtual; + function registerOperator( + bytes memory quorumNumbers, + string memory socket, + IBLSApkRegistryTypes.PubkeyRegistrationParams memory params, + ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature + ) external virtual; + function registerOperatorWithChurn( + bytes calldata quorumNumbers, + string memory socket, + IBLSApkRegistryTypes.PubkeyRegistrationParams memory params, + OperatorKickParam[] memory operatorKickParams, + ISignatureUtils.SignatureWithSaltAndExpiry memory churnApproverSignature, + ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature + ) external virtual; + function setChurnApprover( + address _churnApprover + ) external virtual; + function setEjectionCooldown( + uint256 _ejectionCooldown + ) external virtual; + function setEjector( + address _ejector + ) external virtual; + function setOperatorSetParams( + uint8 quorumNumber, + OperatorSetParam memory operatorSetParams + ) external virtual; + function updateOperators( + address[] memory operators + ) external virtual; + function updateOperatorsForQuorum( + address[][] memory operatorsPerQuorum, 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){} - - 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){} - - /// @notice Returns the operator address for the given `operatorId` - function getOperatorFromId(bytes32 operatorId) external view returns (address) {} - - /// @notice Returns the status for the given `operator` - function getOperatorStatus(address operator) external view returns (IRegistryCoordinator.OperatorStatus){} - - /// @notice Returns task number from when `operator` has been registered. - function getFromTaskNumberForOperator(address operator) external view returns (uint32){} - - 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 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 getQuorumBitmapUpdateByIndex(bytes32 operatorId, uint256 index) external view returns (QuorumBitmapUpdate memory) {} - - /// @notice Returns the current quorum bitmap for the given `operatorId` - function getCurrentQuorumBitmap(bytes32 operatorId) external view returns (uint192) {} - - /// @notice Returns the length of the quorum bitmap history for the given `operatorId` - function getQuorumBitmapHistoryLength(bytes32 operatorId) external view returns (uint256) {} - - function numRegistries() external view returns (uint256){} - - function registries(uint256) external view returns (address){} - - function registerOperator(bytes memory quorumNumbers, bytes calldata) external {} - - function deregisterOperator(bytes calldata quorumNumbers, bytes calldata) external {} + ) external virtual; + function updateSocket( + string memory socket + ) external virtual; + + // Keep existing implementations + function blsApkRegistry() external view virtual returns (IBLSApkRegistry) {} + function ejectOperator(address operator, bytes calldata quorumNumbers) external virtual {} + function getOperatorSetParams( + uint8 quorumNumber + ) external view virtual returns (OperatorSetParam memory) {} + function indexRegistry() external view virtual returns (IIndexRegistry) {} + function stakeRegistry() external view virtual returns (IStakeRegistry) {} + function quorumCount() external view virtual returns (uint8) {} + function getOperator( + address operator + ) external view virtual returns (OperatorInfo memory) {} + function getOperatorId( + address operator + ) external view virtual returns (bytes32) {} + function getOperatorFromId( + bytes32 operatorId + ) external view virtual returns (address) {} + function getOperatorStatus( + address operator + ) external view virtual returns (OperatorStatus) {} + function getQuorumBitmapIndicesAtBlockNumber( + uint32 blockNumber, + bytes32[] memory operatorIds + ) external view virtual returns (uint32[] memory) {} + function getQuorumBitmapAtBlockNumberByIndex( + bytes32 operatorId, + uint32 blockNumber, + uint256 index + ) external view virtual returns (uint192) {} + function getQuorumBitmapUpdateByIndex( + bytes32 operatorId, + uint256 index + ) external view virtual returns (QuorumBitmapUpdate memory) {} + function getCurrentQuorumBitmap( + bytes32 operatorId + ) external view virtual returns (uint192) {} + function getQuorumBitmapHistoryLength( + bytes32 operatorId + ) external view virtual returns (uint256) {} + function numRegistries() external view virtual returns (uint256) {} + function registries( + uint256 + ) external view virtual returns (address) {} + function deregisterOperator( + bytes calldata quorumNumbers + ) external virtual {} - function pubkeyRegistrationMessageHash(address operator) public view returns (BN254.G1Point memory) { - return BN254.hashToG1( - keccak256(abi.encode(operator)) - ); + function pubkeyRegistrationMessageHash( + address operator + ) public view virtual returns (BN254.G1Point memory) { + return BN254.hashToG1(keccak256(abi.encode(operator))); } - function quorumUpdateBlockNumber(uint8 quorumNumber) external view returns (uint256) {} - - function owner() external view returns (address) {} + function quorumUpdateBlockNumber( + uint8 quorumNumber + ) external view virtual returns (uint256) {} + function owner() external view virtual returns (address) {} + function serviceManager() external view virtual returns (IServiceManager) {} - function updateSocket(string memory socket) external {} + function isM2Quorum( + uint8 quorumNumber + ) external view virtual returns (bool) { + return false; + } } diff --git a/test/mocks/RewardsCoordinatorMock.sol b/test/mocks/RewardsCoordinatorMock.sol index c3e36490..b16f7cc5 100644 --- a/test/mocks/RewardsCoordinatorMock.sol +++ b/test/mocks/RewardsCoordinatorMock.sol @@ -1,138 +1,145 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import {IRewardsCoordinator} from "eigenlayer-contracts/src/contracts/interfaces/IRewardsCoordinator.sol"; +import {IPauserRegistry} from "eigenlayer-contracts/src/contracts/interfaces/IPauserRegistry.sol"; +import {IRewardsCoordinator} from + "eigenlayer-contracts/src/contracts/interfaces/IRewardsCoordinator.sol"; +import "./AVSDirectoryMock.sol"; contract RewardsCoordinatorMock is IRewardsCoordinator { - function rewardsUpdater() external view returns (address) {} + function initialize( + address initialOwner, + uint256 initialPausedStatus, + address _rewardsUpdater, + uint32 _activationDelay, + uint16 _defaultSplitBips + ) external override {} - function CALCULATION_INTERVAL_SECONDS() external view returns (uint32) {} + function createAVSRewardsSubmission( + RewardsSubmission[] calldata rewardsSubmissions + ) external override {} - function MAX_REWARDS_DURATION() external view returns (uint32) {} + function createRewardsForAllSubmission( + RewardsSubmission[] calldata rewardsSubmissions + ) external override {} - function MAX_RETROACTIVE_LENGTH() external view returns (uint32) {} + function createRewardsForAllEarners( + RewardsSubmission[] calldata rewardsSubmissions + ) external override {} - function MAX_FUTURE_LENGTH() external view returns (uint32) {} + function createOperatorDirectedAVSRewardsSubmission( + address avs, + OperatorDirectedRewardsSubmission[] calldata operatorDirectedRewardsSubmissions + ) external override {} - function GENESIS_REWARDS_TIMESTAMP() external view returns (uint32) {} + function processClaim(RewardsMerkleClaim calldata claim, address recipient) external override {} - function activationDelay() external view returns (uint32) {} + function processClaims( + RewardsMerkleClaim[] calldata claims, + address recipient + ) external override {} + + function submitRoot(bytes32 root, uint32 rewardsCalculationEndTimestamp) external override {} + + function disableRoot( + uint32 rootIndex + ) external override {} - function claimerFor(address earner) external view returns (address) {} + function setClaimerFor( + address claimer + ) external override {} + + function setClaimerFor(address earner, address claimer) external override {} + + function setActivationDelay( + uint32 _activationDelay + ) external override {} + + function setDefaultOperatorSplit( + uint16 split + ) external override {} + + function setOperatorAVSSplit(address operator, address avs, uint16 split) external override {} + + function setOperatorPISplit(address operator, uint16 split) external override {} + + function setRewardsUpdater( + address _rewardsUpdater + ) external override {} + + function setRewardsForAllSubmitter(address _submitter, bool _newValue) external override {} + + function activationDelay() external view override returns (uint32) {} + + function currRewardsCalculationEndTimestamp() external view override returns (uint32) {} + + function claimerFor( + address earner + ) external view override returns (address) {} function cumulativeClaimed( address claimer, IERC20 token - ) external view returns (uint256) {} + ) external view override returns (uint256) {} + + function defaultOperatorSplitBips() external view override returns (uint16) {} - function defaultOperatorSplitBips() external view returns (uint16) {} + function getOperatorAVSSplit( + address operator, + address avs + ) external view override returns (uint16) {} + + function getOperatorPISplit( + address operator + ) external view override returns (uint16) {} function calculateEarnerLeafHash( EarnerTreeMerkleLeaf calldata leaf - ) external pure returns (bytes32) {} + ) external pure override returns (bytes32) {} function calculateTokenLeafHash( TokenTreeMerkleLeaf calldata leaf - ) external pure returns (bytes32) {} + ) external pure override returns (bytes32) {} function checkClaim( RewardsMerkleClaim calldata claim - ) external view returns (bool) {} - - function currRewardsCalculationEndTimestamp() - external - view - returns (uint32) - {} + ) external view override returns (bool) {} - function getDistributionRootsLength() external view returns (uint256) {} + function getDistributionRootsLength() external view override returns (uint256) {} function getDistributionRootAtIndex( uint256 index - ) external view returns (DistributionRoot memory) {} + ) external view override returns (DistributionRoot memory) {} function getCurrentDistributionRoot() external view + override returns (DistributionRoot memory) {} function getCurrentClaimableDistributionRoot() external view + override returns (DistributionRoot memory) {} function getRootIndexFromHash( bytes32 rootHash - ) external view returns (uint32) {} + ) external view override returns (uint32) {} - function domainSeparator() external view returns (bytes32) {} + function rewardsUpdater() external view override returns (address) {} - function getOperatorAVSSplit( - address operator, - address avs - ) external view returns (uint16) {} + function CALCULATION_INTERVAL_SECONDS() external view override returns (uint32) {} - function getOperatorPISplit( - address operator - ) external view returns (uint16) {} + function MAX_REWARDS_DURATION() external view override returns (uint32) {} - function createAVSRewardsSubmission( - RewardsSubmission[] calldata rewardsSubmissions - ) external {} - - function createRewardsForAllSubmission( - RewardsSubmission[] calldata rewardsSubmission - ) external {} - - function createRewardsForAllEarners( - RewardsSubmission[] calldata rewardsSubmissions - ) external {} - - function createOperatorDirectedAVSRewardsSubmission( - address avs, - OperatorDirectedRewardsSubmission[] - calldata operatorDirectedRewardsSubmissions - ) external {} + function MAX_RETROACTIVE_LENGTH() external view override returns (uint32) {} - function processClaim( - RewardsMerkleClaim calldata claim, - address recipient - ) external {} - - function processClaims( - RewardsMerkleClaim[] calldata claims, - address recipient - ) external {} - - function submitRoot( - bytes32 root, - uint32 rewardsCalculationEndTimestamp - ) external {} - - function disableRoot(uint32 rootIndex) external {} - - function setClaimerFor(address claimer) external {} - - function setActivationDelay(uint32 _activationDelay) external {} - - function setDefaultOperatorSplit(uint16 split) external {} - - function setRewardsUpdater(address _rewardsUpdater) external {} - - function setRewardsForAllSubmitter( - address _submitter, - bool _newValue - ) external {} - - function setOperatorAVSSplit( - address operator, - address avs, - uint16 split - ) external {} + function MAX_FUTURE_LENGTH() external view override returns (uint32) {} - function setOperatorPISplit(address operator, uint16 split) external {} + function GENESIS_REWARDS_TIMESTAMP() external view override returns (uint32) {} } diff --git a/test/mocks/ServiceManagerMock.sol b/test/mocks/ServiceManagerMock.sol index 8af99426..b33df503 100644 --- a/test/mocks/ServiceManagerMock.sol +++ b/test/mocks/ServiceManagerMock.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "../../src/ServiceManagerBase.sol"; @@ -7,10 +7,19 @@ contract ServiceManagerMock is ServiceManagerBase { constructor( IAVSDirectory _avsDirectory, IRewardsCoordinator _rewardsCoordinator, - IRegistryCoordinator _registryCoordinator, - IStakeRegistry _stakeRegistry + ISlashingRegistryCoordinator _slashingRegistryCoordinator, + IStakeRegistry _stakeRegistry, + IPermissionController _permissionController, + IAllocationManager _allocationManager ) - ServiceManagerBase(_avsDirectory, _rewardsCoordinator, _registryCoordinator, _stakeRegistry) + ServiceManagerBase( + _avsDirectory, + _rewardsCoordinator, + _slashingRegistryCoordinator, + _stakeRegistry, + _permissionController, + _allocationManager + ) {} function initialize( diff --git a/test/mocks/StakeRegistryMock.sol b/test/mocks/StakeRegistryMock.sol index f86b938f..f428928e 100644 --- a/test/mocks/StakeRegistryMock.sol +++ b/test/mocks/StakeRegistryMock.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "../../src/interfaces/IStakeRegistry.sol"; import "../../src/interfaces/IRegistryCoordinator.sol"; @@ -12,12 +12,40 @@ contract StakeRegistryMock is IStakeRegistry { // bitmap returned by the mocked `updateOperatorStake` function uint192 updateOperatorStakeReturnBitmap; - function set_updateOperatorStakeReturnBitmap(uint192 newValue) external { + function isOperatorSetQuorum( + uint8 quorumNumber + ) external view returns (bool) {} + + function getStakeHistoryLength( + bytes32 operatorId, + uint8 quorumNumber + ) external view returns (uint256) {} + + function setMinimumStakeForQuorum(uint8 quorumNumber, uint96 minimumStake) external {} + + function setSlashableStakeLookahead(uint8 quorumNumber, uint32 lookAheadBlocks) external {} + + function set_updateOperatorStakeReturnBitmap( + uint192 newValue + ) external { updateOperatorStakeReturnBitmap = newValue; } function registryCoordinator() external view returns (address) {} + function initializeDelegatedStakeQuorum( + uint8 quorumNumber, + uint96 minimumStake, + StrategyParams[] memory _strategyParams + ) external {} + + function initializeSlashableStakeQuorum( + uint8 quorumNumber, + uint96 minimumStake, + uint32 lookAheadPeriod, + StrategyParams[] memory _strategyParams + ) external {} + /** * @notice Registers the `operator` with `operatorId` for the specified `quorumNumbers`. * @param operator The address of the operator to register. @@ -32,8 +60,8 @@ contract StakeRegistryMock is IStakeRegistry { * 4) the operator is not already registered */ function registerOperator( - address operator, - bytes32 operatorId, + address operator, + bytes32 operatorId, bytes memory quorumNumbers ) external returns (uint96[] memory, uint96[] memory) {} @@ -54,14 +82,15 @@ 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, StrategyParams[] memory strategyParams) external {} - - /// @notice Adds new strategies and the associated multipliers to the @param quorumNumber. - function addStrategies( + 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, 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. @@ -87,10 +116,14 @@ contract StakeRegistryMock is IStakeRegistry { function WEIGHTING_DIVISOR() external pure returns (uint256) {} - function strategyParamsLength(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(uint8 quorumNumber) external view returns (uint96) {} + function minimumStakeForQuorum( + uint8 quorumNumber + ) external view returns (uint96) {} /// @notice Returns the strategy and weight multiplier for the `index`'th strategy in the quorum `quorumNumber` function strategyParamsByIndex( @@ -102,32 +135,47 @@ contract StakeRegistryMock is IStakeRegistry { * @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) {} + 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. * @param quorumNumber The quorum number to get the stake for. */ - function getStakeHistory(bytes32 operatorId, uint8 quorumNumber) external view returns (StakeUpdate[] memory) {} + function getStakeHistory( + bytes32 operatorId, + uint8 quorumNumber + ) external view returns (StakeUpdate[] memory) {} - function getTotalStakeHistoryLength(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 getTotalStakeUpdateAtIndex(uint8 quorumNumber, uint256 index) external view returns (StakeUpdate 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) - external - view - returns (uint32) {} + function getStakeUpdateIndexAtBlockNumber( + 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 getTotalStakeIndicesAtBlockNumber(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. @@ -136,20 +184,24 @@ 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 getStakeUpdateAtIndex(uint8 quorumNumber, bytes32 operatorId, uint256 index) - external - view - returns (StakeUpdate memory) {} + function getStakeUpdateAtIndex( + uint8 quorumNumber, + bytes32 operatorId, + uint256 index + ) external view returns (StakeUpdate memory) {} /** * @notice Returns the most recent stake weight for the `operatorId` for a certain quorum * @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 (StakeUpdate 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 - * `index`-th entry in the `operatorIdToStakeHistory[operatorId][quorumNumber]` array if the entry + * `index`-th entry in the `operatorIdToStakeHistory[operatorId][quorumNumber]` array if the entry * corresponds to 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. @@ -158,14 +210,16 @@ 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 getStakeAtBlockNumberAndIndex(uint8 quorumNumber, uint32 blockNumber, bytes32 operatorId, uint256 index) - external - view - returns (uint96) {} + function getStakeAtBlockNumberAndIndex( + uint8 quorumNumber, + uint32 blockNumber, + bytes32 operatorId, + uint256 index + ) external view returns (uint96) {} /** - * @notice Returns the total stake weight for quorum `quorumNumber`, at the `index`-th entry in the - * `totalStakeHistory[quorumNumber]` array if the entry corresponds to the total stake at `blockNumber`. + * @notice Returns the total stake weight for quorum `quorumNumber`, at the `index`-th entry in the + * `totalStakeHistory[quorumNumber]` array if the entry corresponds to the total stake at `blockNumber`. * Reverts otherwise. * @param quorumNumber The quorum number to get the stake for. * @param index Array index for lookup, within the dynamic array `totalStakeHistory[quorumNumber]`. @@ -173,25 +227,35 @@ 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 getTotalStakeAtBlockNumberFromIndex(uint8 quorumNumber, uint32 blockNumber, uint256 index) external view returns (uint96) {} + function getTotalStakeAtBlockNumberFromIndex( + uint8 quorumNumber, + uint32 blockNumber, + uint256 index + ) external view returns (uint96) {} /** * @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) {} + 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 getStakeAtBlockNumber(bytes32 operatorId, uint8 quorumNumber, uint32 blockNumber) - external - view - returns (uint96){} + function getStakeAtBlockNumber( + bytes32 operatorId, + uint8 quorumNumber, + uint32 blockNumber + ) external view returns (uint96) {} /** * @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) {} + function getCurrentTotalStake( + uint8 quorumNumber + ) external view returns (uint96) {} /** * @notice Called by the registry coordinator to update an operator's stake for one @@ -201,14 +265,16 @@ contract StakeRegistryMock is IStakeRegistry { * added to the */ function updateOperatorStake( - address /*operator*/, - bytes32 /*operatorId*/, + address, /*operator*/ + bytes32, /*operatorId*/ bytes calldata /*quorumNumbers*/ ) external returns (uint192) { return updateOperatorStakeReturnBitmap; } - function getMockOperatorId(address operator) external pure returns(bytes32) { + function getMockOperatorId( + address operator + ) external pure returns (bytes32) { return bytes32(uint256(keccak256(abi.encodePacked(operator, "operatorId")))); } } diff --git a/test/unit/AVSRegistrar.t.sol b/test/unit/AVSRegistrar.t.sol new file mode 100644 index 00000000..1dd90226 --- /dev/null +++ b/test/unit/AVSRegistrar.t.sol @@ -0,0 +1,120 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.27; + +import {MockAVSDeployer} from "../utils/MockAVSDeployer.sol"; +import {BN254} from "../../src/libraries/BN254.sol"; +import {IRegistryCoordinator} from "../../src/interfaces/IRegistryCoordinator.sol"; +import {IStakeRegistry} from "../../src/interfaces/IStakeRegistry.sol"; +import {BitmapUtils} from "../../src/libraries/BitmapUtils.sol"; +import {IAVSRegistrar} from "eigenlayer-contracts/src/contracts/interfaces/IAVSRegistrar.sol"; +import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; +import {IAllocationManagerTypes} from + "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol"; +import {AVSRegistrarMock} from "../mocks/AVSRegistrarMock.sol"; +import {console2 as console} from "forge-std/Test.sol"; + +contract AVSRegistrarTest is MockAVSDeployer { + using BN254 for BN254.G1Point; + + AVSRegistrarMock public avsRegistrarMock; + address internal operator = address(420); + + function setUp() public virtual { + _deployMockEigenLayerAndAVS(); + avsRegistrarMock = new AVSRegistrarMock(); + } + + function testSetAVSRegistrar() public { + vm.prank(address(serviceManager)); + allocationManager.setAVSRegistrar( + address(serviceManager), IAVSRegistrar(address(avsRegistrarMock)) + ); + assertEq( + address(allocationManager.getAVSRegistrar(address(serviceManager))), + address(avsRegistrarMock) + ); + } + + function testRegisterOperator() public { + // Set up AVS registrar + vm.prank(address(serviceManager)); + allocationManager.setAVSRegistrar( + address(serviceManager), IAVSRegistrar(address(avsRegistrarMock)) + ); + + // Create operator set + uint32 operatorSetId = 1; + IAllocationManagerTypes.CreateSetParams[] memory createSetParams = + new IAllocationManagerTypes.CreateSetParams[](1); + createSetParams[0] = IAllocationManagerTypes.CreateSetParams({ + operatorSetId: operatorSetId, + strategies: new IStrategy[](0) + }); + + // Create operator set + vm.prank(address(serviceManager)); + allocationManager.createOperatorSets(address(serviceManager), createSetParams); + + // Set up registration params + uint32[] memory operatorSetIds = new uint32[](1); + operatorSetIds[0] = operatorSetId; + bytes memory emptyBytes; + + delegationMock.setIsOperator(operator, true); + + // Register operator + vm.prank(operator); + allocationManager.registerForOperatorSets( + address(operator), + IAllocationManagerTypes.RegisterParams( + address(serviceManager), operatorSetIds, emptyBytes + ) + ); + } + + function testRegisterOperator_RevertsIfNotOperator() public { + vm.prank(address(serviceManager)); + allocationManager.setAVSRegistrar( + address(serviceManager), IAVSRegistrar(address(avsRegistrarMock)) + ); + + // Create operator set + uint32 operatorSetId = 1; + IAllocationManagerTypes.CreateSetParams[] memory createSetParams = + new IAllocationManagerTypes.CreateSetParams[](1); + createSetParams[0] = IAllocationManagerTypes.CreateSetParams({ + operatorSetId: operatorSetId, + strategies: new IStrategy[](0) + }); + + // Create operator set + vm.prank(address(serviceManager)); + allocationManager.createOperatorSets(address(serviceManager), createSetParams); + + // Set up registration params + uint32[] memory operatorSetIds = new uint32[](1); + operatorSetIds[0] = operatorSetId; + bytes memory emptyBytes; + + delegationMock.setIsOperator(operator, false); + + // Register operator + vm.prank(operator); + + vm.expectRevert(); + allocationManager.registerForOperatorSets( + address(operator), + IAllocationManagerTypes.RegisterParams( + address(serviceManager), operatorSetIds, emptyBytes + ) + ); + } + + function testAllocationManagerDeployed() public { + assertTrue(address(allocationManager) != address(0), "AllocationManager not deployed"); + assertTrue( + address(allocationManagerImplementation) != address(0), + "AllocationManager implementation not deployed" + ); + } +} diff --git a/test/unit/BLSApkRegistryUnit.t.sol b/test/unit/BLSApkRegistryUnit.t.sol index 5f800444..785811fd 100644 --- a/test/unit/BLSApkRegistryUnit.t.sol +++ b/test/unit/BLSApkRegistryUnit.t.sol @@ -1,5 +1,5 @@ //SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "forge-std/Test.sol"; import "../harnesses/BLSApkRegistryHarness.sol"; @@ -8,6 +8,7 @@ import "../harnesses/BitmapUtilsWrapper.sol"; import "../utils/BLSMockAVSDeployer.sol"; import {IBLSApkRegistryEvents} from "../events/IBLSApkRegistryEvents.sol"; +import {IBLSApkRegistryErrors} from "../../src/interfaces/IBLSApkRegistry.sol"; contract BLSApkRegistryUnitTests is BLSMockAVSDeployer, IBLSApkRegistryEvents { using BitmapUtils for uint192; @@ -42,8 +43,9 @@ contract BLSApkRegistryUnitTests is BLSMockAVSDeployer, IBLSApkRegistryEvents { * HELPERS AND MODIFIERS * */ - - modifier filterFuzzedAddressInputs(address fuzzedAddress) { + modifier filterFuzzedAddressInputs( + address fuzzedAddress + ) { cheats.assume(!addressIsExcludedFromFuzzedInputs[fuzzedAddress]); _; } @@ -57,26 +59,20 @@ contract BLSApkRegistryUnitTests is BLSMockAVSDeployer, IBLSApkRegistryEvents { addressIsExcludedFromFuzzedInputs[defaultOperator] = true; addressIsExcludedFromFuzzedInputs[address(proxyAdmin)] = true; - pubkeyRegistrationParams.pubkeyG1 = BN254.generatorG1().scalar_mul( - privKey - ); + pubkeyRegistrationParams.pubkeyG1 = BN254.generatorG1().scalar_mul(privKey); defaultPubkey = pubkeyRegistrationParams.pubkeyG1; defaultPubkeyHash = BN254.hashG1Point(defaultPubkey); //privKey*G2 - pubkeyRegistrationParams.pubkeyG2.X[ - 1 - ] = 19_101_821_850_089_705_274_637_533_855_249_918_363_070_101_489_527_618_151_493_230_256_975_900_223_847; - pubkeyRegistrationParams.pubkeyG2.X[ - 0 - ] = 5_334_410_886_741_819_556_325_359_147_377_682_006_012_228_123_419_628_681_352_847_439_302_316_235_957; - pubkeyRegistrationParams.pubkeyG2.Y[ - 1 - ] = 354_176_189_041_917_478_648_604_979_334_478_067_325_821_134_838_555_150_300_539_079_146_482_658_331; - pubkeyRegistrationParams.pubkeyG2.Y[ - 0 - ] = 4_185_483_097_059_047_421_902_184_823_581_361_466_320_657_066_600_218_863_748_375_739_772_335_928_910; + pubkeyRegistrationParams.pubkeyG2.X[1] = + 19101821850089705274637533855249918363070101489527618151493230256975900223847; + pubkeyRegistrationParams.pubkeyG2.X[0] = + 5334410886741819556325359147377682006012228123419628681352847439302316235957; + pubkeyRegistrationParams.pubkeyG2.Y[1] = + 354176189041917478648604979334478067325821134838555150300539079146482658331; + pubkeyRegistrationParams.pubkeyG2.Y[0] = + 4185483097059047421902184823581361466320657066600218863748375739772335928910; // Initialize 3 quorums _initializeQuorum(); @@ -95,14 +91,14 @@ contract BLSApkRegistryUnitTests is BLSMockAVSDeployer, IBLSApkRegistryEvents { initializedQuorums[quorumNumber] = true; // Mark quorum initialized for other tests - initializedQuorumBitmap = uint192( - initializedQuorumBitmap.setBit(quorumNumber) - ); + initializedQuorumBitmap = uint192(initializedQuorumBitmap.setBit(quorumNumber)); initializedQuorumBytes = initializedQuorumBitmap.bitmapToBytesArray(); } /// @dev Doesn't increment nextQuorum as assumes quorumNumber is any valid arbitrary quorumNumber - function _initializeQuorum(uint8 quorumNumber) internal { + function _initializeQuorum( + uint8 quorumNumber + ) internal { cheats.prank(address(registryCoordinator)); // Initialize quorum and mark registered @@ -112,10 +108,10 @@ contract BLSApkRegistryUnitTests is BLSMockAVSDeployer, IBLSApkRegistryEvents { /// @dev initializeQuorum based on passed in bitmap of quorum numbers /// assumes that bitmap does not contain already initailized quorums and doesn't increment nextQuorum - function _initializeFuzzedQuorums(uint192 bitmap) internal { - bytes memory quorumNumbers = bitmapUtilsWrapper.bitmapToBytesArray( - bitmap - ); + function _initializeFuzzedQuorums( + uint192 bitmap + ) internal { + bytes memory quorumNumbers = bitmapUtilsWrapper.bitmapToBytesArray(bitmap); for (uint256 i = 0; i < quorumNumbers.length; i++) { uint8 quorumNumber = uint8(quorumNumbers[i]); @@ -123,7 +119,9 @@ contract BLSApkRegistryUnitTests is BLSMockAVSDeployer, IBLSApkRegistryEvents { } } - function _initializeFuzzedQuorum(uint8 quorumNumber) internal { + function _initializeFuzzedQuorum( + uint8 quorumNumber + ) internal { cheats.assume(!initializedQuorums[quorumNumber]); _initializeQuorum(quorumNumber); } @@ -136,14 +134,16 @@ contract BLSApkRegistryUnitTests is BLSMockAVSDeployer, IBLSApkRegistryEvents { return operator; } - function _getRandomPk(uint256 seed) internal view returns (bytes32) { + function _getRandomPk( + uint256 seed + ) internal view returns (bytes32) { return keccak256(abi.encodePacked(block.timestamp, seed)); } - function _getRandBool(uint256 seed) internal view returns (bool) { - uint256 randomNumber = uint256( - keccak256(abi.encodePacked(block.timestamp, seed)) - ); + function _getRandBool( + uint256 seed + ) internal view returns (bool) { + uint256 randomNumber = uint256(keccak256(abi.encodePacked(block.timestamp, seed))); return randomNumber % 2 == 0; } @@ -152,12 +152,10 @@ contract BLSApkRegistryUnitTests is BLSMockAVSDeployer, IBLSApkRegistryEvents { * Helpers using the default preset BLS key * */ - function _signMessage( address signer ) internal view returns (BN254.G1Point memory) { - BN254.G1Point memory messageHash = registryCoordinator - .pubkeyRegistrationMessageHash(signer); + BN254.G1Point memory messageHash = registryCoordinator.pubkeyRegistrationMessageHash(signer); return BN254.scalar_mul(messageHash, privKey); } @@ -183,28 +181,18 @@ contract BLSApkRegistryUnitTests is BLSMockAVSDeployer, IBLSApkRegistryEvents { function _registerDefaultBLSPubkey( address operator ) internal returns (bytes32) { - pubkeyRegistrationParams.pubkeyRegistrationSignature = _signMessage( - operator - ); - BN254.G1Point memory messageHash = registryCoordinator - .pubkeyRegistrationMessageHash(operator); + pubkeyRegistrationParams.pubkeyRegistrationSignature = _signMessage(operator); + BN254.G1Point memory messageHash = + registryCoordinator.pubkeyRegistrationMessageHash(operator); cheats.prank(address(registryCoordinator)); - return - blsApkRegistry.registerBLSPublicKey( - operator, - pubkeyRegistrationParams, - messageHash - ); + return blsApkRegistry.registerBLSPublicKey(operator, pubkeyRegistrationParams, messageHash); } /** * @dev register operator, assumes operator has a registered BLS public key and that quorumNumbers are valid */ - function _registerOperator( - address operator, - bytes memory quorumNumbers - ) internal { + function _registerOperator(address operator, bytes memory quorumNumbers) internal { bytes32 operatorId = blsApkRegistry.getOperatorId(operator); cheats.prank(address(registryCoordinator)); cheats.expectEmit(true, true, true, true, address(blsApkRegistry)); @@ -215,10 +203,7 @@ contract BLSApkRegistryUnitTests is BLSMockAVSDeployer, IBLSApkRegistryEvents { /** * @dev deregister operator, assumes operator has a registered BLS public key and that quorumNumbers are valid */ - function _deregisterOperator( - address operator, - bytes memory quorumNumbers - ) internal { + function _deregisterOperator(address operator, bytes memory quorumNumbers) internal { bytes32 operatorId = blsApkRegistry.getOperatorId(operator); cheats.prank(address(registryCoordinator)); cheats.expectEmit(true, true, true, true, address(blsApkRegistry)); @@ -231,13 +216,10 @@ contract BLSApkRegistryUnitTests is BLSMockAVSDeployer, IBLSApkRegistryEvents { * Helpers for assertions * */ - function _getApks( bytes memory quorumNumbers ) internal view returns (BN254.G1Point[] memory) { - BN254.G1Point[] memory quorumApks = new BN254.G1Point[]( - quorumNumbers.length - ); + BN254.G1Point[] memory quorumApks = new BN254.G1Point[](quorumNumbers.length); for (uint8 i = 0; i < quorumNumbers.length; i++) { quorumApks[i] = blsApkRegistry.getApk(uint8(quorumNumbers[i])); } @@ -256,9 +238,7 @@ contract BLSApkRegistryUnitTests is BLSMockAVSDeployer, IBLSApkRegistryEvents { "apksBefore and quorumNumbers must be the same length" ); assertEq( - apksBefore.length, - apksAfter.length, - "apksBefore and apksAfter must be the same length" + apksBefore.length, apksAfter.length, "apksBefore and apksAfter must be the same length" ); for (uint256 i = 0; i < apksBefore.length; i++) { uint8 quorumNumber = uint8(quorumNumbers[i]); @@ -270,11 +250,9 @@ contract BLSApkRegistryUnitTests is BLSMockAVSDeployer, IBLSApkRegistryEvents { "quorum apk not updated correctly adding the operator pubkey" ); - uint32 quorumHistoryLength = blsApkRegistry.getApkHistoryLength( - quorumNumber - ); - IBLSApkRegistry.ApkUpdate memory latestApkUpdate = blsApkRegistry - .getApkUpdateAtIndex(quorumNumber, quorumHistoryLength - 1); + uint32 quorumHistoryLength = blsApkRegistry.getApkHistoryLength(quorumNumber); + IBLSApkRegistry.ApkUpdate memory latestApkUpdate = + blsApkRegistry.getApkUpdateAtIndex(quorumNumber, quorumHistoryLength - 1); assertEq( latestApkUpdate.apkHash, bytes24(BN254.hashG1Point(apkAfter)), @@ -309,17 +287,13 @@ contract BLSApkRegistryUnitTests_configAndGetters is BLSApkRegistryUnitTests { cheats.assume(nonCoordinatorAddress != address(registryCoordinator)); cheats.prank(address(nonCoordinatorAddress)); - cheats.expectRevert( - "BLSApkRegistry.onlyRegistryCoordinator: caller is not the registry coordinator" - ); + cheats.expectRevert(IBLSApkRegistryErrors.OnlyRegistryCoordinatorOwner.selector); blsApkRegistry.initializeQuorum(defaultQuorumNumber); } } /// @notice test for BLSApkRegistry.registerBLSPublicKey() -contract BLSApkRegistryUnitTests_registerBLSPublicKey is - BLSApkRegistryUnitTests -{ +contract BLSApkRegistryUnitTests_registerBLSPublicKey is BLSApkRegistryUnitTests { using BN254 for BN254.G1Point; function testFuzz_registerOperator_Revert_WhenNotRegistryCoordinator( @@ -327,21 +301,13 @@ contract BLSApkRegistryUnitTests_registerBLSPublicKey is ) public filterFuzzedAddressInputs(nonCoordinatorAddress) { cheats.assume(nonCoordinatorAddress != address(registryCoordinator)); - pubkeyRegistrationParams.pubkeyRegistrationSignature = _signMessage( - defaultOperator - ); - BN254.G1Point memory messageHash = registryCoordinator - .pubkeyRegistrationMessageHash(defaultOperator); + pubkeyRegistrationParams.pubkeyRegistrationSignature = _signMessage(defaultOperator); + BN254.G1Point memory messageHash = + registryCoordinator.pubkeyRegistrationMessageHash(defaultOperator); cheats.prank(address(nonCoordinatorAddress)); - cheats.expectRevert( - "BLSApkRegistry.onlyRegistryCoordinator: caller is not the registry coordinator" - ); - blsApkRegistry.registerBLSPublicKey( - defaultOperator, - pubkeyRegistrationParams, - messageHash - ); + cheats.expectRevert(IBLSApkRegistryErrors.OnlyRegistryCoordinatorOwner.selector); + blsApkRegistry.registerBLSPublicKey(defaultOperator, pubkeyRegistrationParams, messageHash); } function testFuzz_registerOperator_Revert_WhenZeroPubkeyHash( @@ -349,77 +315,43 @@ contract BLSApkRegistryUnitTests_registerBLSPublicKey is ) public filterFuzzedAddressInputs(operator) { pubkeyRegistrationParams.pubkeyG1.X = 0; pubkeyRegistrationParams.pubkeyG1.Y = 0; - BN254.G1Point memory messageHash = registryCoordinator - .pubkeyRegistrationMessageHash(operator); + BN254.G1Point memory messageHash = + registryCoordinator.pubkeyRegistrationMessageHash(operator); cheats.prank(address(registryCoordinator)); - cheats.expectRevert( - "BLSApkRegistry.registerBLSPublicKey: cannot register zero pubkey" - ); - blsApkRegistry.registerBLSPublicKey( - operator, - pubkeyRegistrationParams, - messageHash - ); + cheats.expectRevert(IBLSApkRegistryErrors.ZeroPubKey.selector); + blsApkRegistry.registerBLSPublicKey(operator, pubkeyRegistrationParams, messageHash); } function testFuzz_registerOperator_Revert_WhenOperatorAlreadyRegistered( address operator ) public filterFuzzedAddressInputs(operator) { - pubkeyRegistrationParams.pubkeyRegistrationSignature = _signMessage( - operator - ); - BN254.G1Point memory messageHash = registryCoordinator - .pubkeyRegistrationMessageHash(operator); + pubkeyRegistrationParams.pubkeyRegistrationSignature = _signMessage(operator); + BN254.G1Point memory messageHash = + registryCoordinator.pubkeyRegistrationMessageHash(operator); cheats.startPrank(address(registryCoordinator)); - blsApkRegistry.registerBLSPublicKey( - operator, - pubkeyRegistrationParams, - messageHash - ); + blsApkRegistry.registerBLSPublicKey(operator, pubkeyRegistrationParams, messageHash); - cheats.expectRevert( - "BLSApkRegistry.registerBLSPublicKey: operator already registered pubkey" - ); - blsApkRegistry.registerBLSPublicKey( - operator, - pubkeyRegistrationParams, - messageHash - ); + cheats.expectRevert(IBLSApkRegistryErrors.OperatorAlreadyRegistered.selector); + blsApkRegistry.registerBLSPublicKey(operator, pubkeyRegistrationParams, messageHash); } function testFuzz_registerOperator_Revert_WhenPubkeyAlreadyRegistered( address operator, address operator2 - ) - public - filterFuzzedAddressInputs(operator) - filterFuzzedAddressInputs(operator2) - { + ) public filterFuzzedAddressInputs(operator) filterFuzzedAddressInputs(operator2) { cheats.assume(operator != address(0)); cheats.assume(operator != operator2); - BN254.G1Point memory messageHash = registryCoordinator - .pubkeyRegistrationMessageHash(operator); - pubkeyRegistrationParams.pubkeyRegistrationSignature = _signMessage( - operator - ); + BN254.G1Point memory messageHash = + registryCoordinator.pubkeyRegistrationMessageHash(operator); + pubkeyRegistrationParams.pubkeyRegistrationSignature = _signMessage(operator); cheats.startPrank(address(registryCoordinator)); - blsApkRegistry.registerBLSPublicKey( - operator, - pubkeyRegistrationParams, - messageHash - ); + blsApkRegistry.registerBLSPublicKey(operator, pubkeyRegistrationParams, messageHash); - cheats.expectRevert( - "BLSApkRegistry.registerBLSPublicKey: public key already registered" - ); - blsApkRegistry.registerBLSPublicKey( - operator2, - pubkeyRegistrationParams, - messageHash - ); + cheats.expectRevert(IBLSApkRegistryErrors.BLSPubkeyAlreadyRegistered.selector); + blsApkRegistry.registerBLSPublicKey(operator2, pubkeyRegistrationParams, messageHash); } /** @@ -429,27 +361,17 @@ contract BLSApkRegistryUnitTests_registerBLSPublicKey is function testFuzz_registerOperator_Revert_WhenInvalidSignature( address operator, address invalidOperator - ) - public - filterFuzzedAddressInputs(operator) - filterFuzzedAddressInputs(invalidOperator) - { + ) public filterFuzzedAddressInputs(operator) filterFuzzedAddressInputs(invalidOperator) { cheats.assume(invalidOperator != operator); - BN254.G1Point memory messageHash = registryCoordinator - .pubkeyRegistrationMessageHash(operator); + BN254.G1Point memory messageHash = + registryCoordinator.pubkeyRegistrationMessageHash(operator); BN254.G1Point memory invalidSignature = _signMessage(invalidOperator); pubkeyRegistrationParams.pubkeyRegistrationSignature = invalidSignature; cheats.startPrank(address(registryCoordinator)); - cheats.expectRevert( - "BLSApkRegistry.registerBLSPublicKey: either the G1 signature is wrong, or G1 and G2 private key do not match" - ); - blsApkRegistry.registerBLSPublicKey( - operator, - pubkeyRegistrationParams, - messageHash - ); + cheats.expectRevert(IBLSApkRegistryErrors.InvalidBLSSignatureOrPrivateKey.selector); + blsApkRegistry.registerBLSPublicKey(operator, pubkeyRegistrationParams, messageHash); } /** @@ -458,24 +380,16 @@ contract BLSApkRegistryUnitTests_registerBLSPublicKey is function testFuzz_registerOperator_Revert_WhenInvalidSignatureMismatchKey( address operator ) public filterFuzzedAddressInputs(operator) { - pubkeyRegistrationParams.pubkeyRegistrationSignature = _signMessage( - operator - ); + pubkeyRegistrationParams.pubkeyRegistrationSignature = _signMessage(operator); BN254.G1Point memory badPubkeyG1 = BN254.generatorG1().scalar_mul(420); // mismatch public keys pubkeyRegistrationParams.pubkeyG1 = badPubkeyG1; - BN254.G1Point memory messageHash = registryCoordinator - .pubkeyRegistrationMessageHash(operator); + BN254.G1Point memory messageHash = + registryCoordinator.pubkeyRegistrationMessageHash(operator); cheats.prank(address(registryCoordinator)); - cheats.expectRevert( - "BLSApkRegistry.registerBLSPublicKey: either the G1 signature is wrong, or G1 and G2 private key do not match" - ); - blsApkRegistry.registerBLSPublicKey( - operator, - pubkeyRegistrationParams, - messageHash - ); + cheats.expectRevert(IBLSApkRegistryErrors.InvalidBLSSignatureOrPrivateKey.selector); + blsApkRegistry.registerBLSPublicKey(operator, pubkeyRegistrationParams, messageHash); } /** @@ -486,47 +400,23 @@ contract BLSApkRegistryUnitTests_registerBLSPublicKey is address operator ) public filterFuzzedAddressInputs(operator) { // sign messagehash for operator with private key - pubkeyRegistrationParams.pubkeyRegistrationSignature = _signMessage( - operator - ); - BN254.G1Point memory messageHash = registryCoordinator - .pubkeyRegistrationMessageHash(operator); + pubkeyRegistrationParams.pubkeyRegistrationSignature = _signMessage(operator); + BN254.G1Point memory messageHash = + registryCoordinator.pubkeyRegistrationMessageHash(operator); cheats.prank(address(registryCoordinator)); cheats.expectEmit(true, true, true, true, address(blsApkRegistry)); emit NewPubkeyRegistration( - operator, - pubkeyRegistrationParams.pubkeyG1, - pubkeyRegistrationParams.pubkeyG2 - ); - blsApkRegistry.registerBLSPublicKey( - operator, - pubkeyRegistrationParams, - messageHash + operator, pubkeyRegistrationParams.pubkeyG1, pubkeyRegistrationParams.pubkeyG2 ); + blsApkRegistry.registerBLSPublicKey(operator, pubkeyRegistrationParams, messageHash); - ( - BN254.G1Point memory registeredPubkey, - bytes32 registeredpkHash - ) = blsApkRegistry.getRegisteredPubkey(operator); - assertEq( - registeredPubkey.X, - defaultPubkey.X, - "registeredPubkey not set correctly" - ); - assertEq( - registeredPubkey.Y, - defaultPubkey.Y, - "registeredPubkey not set correctly" - ); - assertEq( - registeredpkHash, - defaultPubkeyHash, - "registeredpkHash not set correctly" - ); + (BN254.G1Point memory registeredPubkey, bytes32 registeredpkHash) = + blsApkRegistry.getRegisteredPubkey(operator); + assertEq(registeredPubkey.X, defaultPubkey.X, "registeredPubkey not set correctly"); + assertEq(registeredPubkey.Y, defaultPubkey.Y, "registeredPubkey not set correctly"); + assertEq(registeredpkHash, defaultPubkeyHash, "registeredpkHash not set correctly"); assertEq( - blsApkRegistry.pubkeyHashToOperator( - BN254.hashG1Point(defaultPubkey) - ), + blsApkRegistry.pubkeyHashToOperator(BN254.hashG1Point(defaultPubkey)), operator, "operator address not stored correctly" ); @@ -544,9 +434,7 @@ contract BLSApkRegistryUnitTests_registerOperator is BLSApkRegistryUnitTests { cheats.assume(nonCoordinatorAddress != address(registryCoordinator)); cheats.prank(nonCoordinatorAddress); - cheats.expectRevert( - "BLSApkRegistry.onlyRegistryCoordinator: caller is not the registry coordinator" - ); + cheats.expectRevert(IBLSApkRegistryErrors.OnlyRegistryCoordinatorOwner.selector); blsApkRegistry.registerOperator(nonCoordinatorAddress, new bytes(0)); } @@ -554,9 +442,7 @@ contract BLSApkRegistryUnitTests_registerOperator is BLSApkRegistryUnitTests { address operator ) public filterFuzzedAddressInputs(operator) { cheats.prank(address(registryCoordinator)); - cheats.expectRevert( - "BLSApkRegistry.getRegisteredPubkey: operator is not registered" - ); + cheats.expectRevert(IBLSApkRegistryErrors.OperatorNotRegistered.selector); blsApkRegistry.registerOperator(operator, new bytes(1)); } @@ -567,19 +453,13 @@ contract BLSApkRegistryUnitTests_registerOperator is BLSApkRegistryUnitTests { cheats.prank(address(registryCoordinator)); cheats.assume(quorumBitmap > initializedQuorumBitmap); // mask out quorums that are already initialized - quorumBitmap = uint192( - quorumBitmap.minus(uint256(initializedQuorumBitmap)) - ); - bytes memory quorumNumbers = bitmapUtilsWrapper.bitmapToBytesArray( - quorumBitmap - ); + quorumBitmap = uint192(quorumBitmap.minus(uint256(initializedQuorumBitmap))); + bytes memory quorumNumbers = bitmapUtilsWrapper.bitmapToBytesArray(quorumBitmap); _registerDefaultBLSPubkey(operator); cheats.prank(address(registryCoordinator)); - cheats.expectRevert( - "BLSApkRegistry._processQuorumApkUpdate: quorum does not exist" - ); + cheats.expectRevert(IBLSApkRegistryErrors.QuorumDoesNotExist.selector); blsApkRegistry.registerOperator(operator, quorumNumbers); } @@ -595,26 +475,15 @@ contract BLSApkRegistryUnitTests_registerOperator is BLSApkRegistryUnitTests { ) public filterFuzzedAddressInputs(operator) { // Test setup, initialize fuzzed quorums and register operator BLS pubkey cheats.assume(quorumBitmap > initializedQuorumBitmap); - uint192 initializingBitmap = uint192( - quorumBitmap.minus(uint256(initializedQuorumBitmap)) - ); + uint192 initializingBitmap = uint192(quorumBitmap.minus(uint256(initializedQuorumBitmap))); _initializeFuzzedQuorums(initializingBitmap); - bytes memory quorumNumbers = bitmapUtilsWrapper.bitmapToBytesArray( - quorumBitmap - ); - (BN254.G1Point memory pubkey, ) = _registerRandomBLSPubkey( - operator, - randomSeed - ); + bytes memory quorumNumbers = bitmapUtilsWrapper.bitmapToBytesArray(quorumBitmap); + (BN254.G1Point memory pubkey,) = _registerRandomBLSPubkey(operator, randomSeed); // get before values - BN254.G1Point[] memory quorumApksBefore = new BN254.G1Point[]( - quorumNumbers.length - ); + BN254.G1Point[] memory quorumApksBefore = new BN254.G1Point[](quorumNumbers.length); for (uint8 i = 0; i < quorumNumbers.length; i++) { - quorumApksBefore[i] = blsApkRegistry.getApk( - uint8(quorumNumbers[i]) - ); + quorumApksBefore[i] = blsApkRegistry.getApk(uint8(quorumNumbers[i])); } // registerOperator with expected OperatorAddedToQuorums event @@ -628,20 +497,16 @@ contract BLSApkRegistryUnitTests_registerOperator is BLSApkRegistryUnitTests { for (uint8 i = 0; i < quorumNumbers.length; i++) { // Check currentApk[quorumNumber] values uint8 quorumNumber = uint8(quorumNumbers[i]); - BN254.G1Point memory quorumApkAfter = blsApkRegistry.getApk( - uint8(quorumNumbers[i]) - ); + BN254.G1Point memory quorumApkAfter = blsApkRegistry.getApk(uint8(quorumNumbers[i])); assertEq( BN254.hashG1Point(quorumApkAfter), BN254.hashG1Point(quorumApksBefore[i].plus(pubkey)), "quorum apk not updated correctly adding the operator pubkey" ); // Check the latest ApkUpdate values - uint32 quorumHistoryLength = blsApkRegistry.getApkHistoryLength( - quorumNumber - ); - IBLSApkRegistry.ApkUpdate memory latestApkUpdate = blsApkRegistry - .getApkUpdateAtIndex(quorumNumber, quorumHistoryLength - 1); + uint32 quorumHistoryLength = blsApkRegistry.getApkHistoryLength(quorumNumber); + IBLSApkRegistry.ApkUpdate memory latestApkUpdate = + blsApkRegistry.getApkUpdateAtIndex(quorumNumber, quorumHistoryLength - 1); assertEq( latestApkUpdate.apkHash, bytes24(BN254.hashG1Point(quorumApkAfter)), @@ -672,9 +537,7 @@ contract BLSApkRegistryUnitTests_deregisterOperator is BLSApkRegistryUnitTests { cheats.assume(nonCoordinatorAddress != address(registryCoordinator)); cheats.prank(nonCoordinatorAddress); - cheats.expectRevert( - "BLSApkRegistry.onlyRegistryCoordinator: caller is not the registry coordinator" - ); + cheats.expectRevert(IBLSApkRegistryErrors.OnlyRegistryCoordinatorOwner.selector); blsApkRegistry.deregisterOperator(nonCoordinatorAddress, new bytes(0)); } @@ -682,9 +545,7 @@ contract BLSApkRegistryUnitTests_deregisterOperator is BLSApkRegistryUnitTests { address operator ) public filterFuzzedAddressInputs(operator) { cheats.prank(address(registryCoordinator)); - cheats.expectRevert( - "BLSApkRegistry.getRegisteredPubkey: operator is not registered" - ); + cheats.expectRevert(IBLSApkRegistryErrors.OperatorNotRegistered.selector); blsApkRegistry.registerOperator(operator, new bytes(1)); } @@ -695,22 +556,16 @@ contract BLSApkRegistryUnitTests_deregisterOperator is BLSApkRegistryUnitTests { cheats.prank(address(registryCoordinator)); cheats.assume(quorumBitmap > initializedQuorumBitmap); // mask out quorums that are already initialized - quorumBitmap = uint192( - quorumBitmap.minus(uint256(initializedQuorumBitmap)) - ); - bytes memory validQuorumNumbers = bitmapUtilsWrapper.bitmapToBytesArray( - initializedQuorumBitmap - ); - bytes memory invalidQuorumNumbers = bitmapUtilsWrapper - .bitmapToBytesArray(quorumBitmap); + quorumBitmap = uint192(quorumBitmap.minus(uint256(initializedQuorumBitmap))); + bytes memory validQuorumNumbers = + bitmapUtilsWrapper.bitmapToBytesArray(initializedQuorumBitmap); + bytes memory invalidQuorumNumbers = bitmapUtilsWrapper.bitmapToBytesArray(quorumBitmap); _registerDefaultBLSPubkey(operator); _registerOperator(operator, validQuorumNumbers); cheats.prank(address(registryCoordinator)); - cheats.expectRevert( - "BLSApkRegistry._processQuorumApkUpdate: quorum does not exist" - ); + cheats.expectRevert(IBLSApkRegistryErrors.QuorumDoesNotExist.selector); blsApkRegistry.deregisterOperator(operator, invalidQuorumNumbers); } @@ -726,27 +581,16 @@ contract BLSApkRegistryUnitTests_deregisterOperator is BLSApkRegistryUnitTests { ) public filterFuzzedAddressInputs(operator) { // Test setup, initialize fuzzed quorums and register operator BLS pubkey cheats.assume(quorumBitmap > initializedQuorumBitmap); - uint192 initializingBitmap = uint192( - quorumBitmap.minus(uint256(initializedQuorumBitmap)) - ); + uint192 initializingBitmap = uint192(quorumBitmap.minus(uint256(initializedQuorumBitmap))); _initializeFuzzedQuorums(initializingBitmap); - bytes memory quorumNumbers = bitmapUtilsWrapper.bitmapToBytesArray( - quorumBitmap - ); - (BN254.G1Point memory pubkey, ) = _registerRandomBLSPubkey( - operator, - randomSeed - ); + bytes memory quorumNumbers = bitmapUtilsWrapper.bitmapToBytesArray(quorumBitmap); + (BN254.G1Point memory pubkey,) = _registerRandomBLSPubkey(operator, randomSeed); _registerOperator(operator, quorumNumbers); // get before values - BN254.G1Point[] memory quorumApksBefore = new BN254.G1Point[]( - quorumNumbers.length - ); + BN254.G1Point[] memory quorumApksBefore = new BN254.G1Point[](quorumNumbers.length); for (uint8 i = 0; i < quorumNumbers.length; i++) { - quorumApksBefore[i] = blsApkRegistry.getApk( - uint8(quorumNumbers[i]) - ); + quorumApksBefore[i] = blsApkRegistry.getApk(uint8(quorumNumbers[i])); } // registerOperator with expected OperatorAddedToQuorums event @@ -760,20 +604,16 @@ contract BLSApkRegistryUnitTests_deregisterOperator is BLSApkRegistryUnitTests { for (uint8 i = 0; i < quorumNumbers.length; i++) { // Check currentApk[quorumNumber] values uint8 quorumNumber = uint8(quorumNumbers[i]); - BN254.G1Point memory quorumApkAfter = blsApkRegistry.getApk( - uint8(quorumNumbers[i]) - ); + BN254.G1Point memory quorumApkAfter = blsApkRegistry.getApk(uint8(quorumNumbers[i])); assertEq( BN254.hashG1Point(quorumApkAfter), BN254.hashG1Point(quorumApksBefore[i].plus(pubkey.negate())), "quorum apk not updated correctly removing the operator pubkey" ); // Check the latest ApkUpdate values - uint32 quorumHistoryLength = blsApkRegistry.getApkHistoryLength( - quorumNumber - ); - IBLSApkRegistry.ApkUpdate memory latestApkUpdate = blsApkRegistry - .getApkUpdateAtIndex(quorumNumber, quorumHistoryLength - 1); + uint32 quorumHistoryLength = blsApkRegistry.getApkHistoryLength(quorumNumber); + IBLSApkRegistry.ApkUpdate memory latestApkUpdate = + blsApkRegistry.getApkUpdateAtIndex(quorumNumber, quorumHistoryLength - 1); assertEq( latestApkUpdate.apkHash, bytes24(BN254.hashG1Point(quorumApkAfter)), @@ -805,10 +645,7 @@ contract BLSApkRegistryUnitTests_quorumApkUpdates is BLSApkRegistryUnitTests { * @dev register/deregister up to 200 operators and check quorum apk updates * Test uses only the defaultQuorumNumber */ - function testFuzz_quorumApkUpdates( - uint256 numOperators, - uint256[200] memory randSeed - ) public { + function testFuzz_quorumApkUpdates(uint256 numOperators, uint256[200] memory randSeed) public { cheats.assume(0 < numOperators && numOperators <= 200); bytes memory quorumNumbers = new bytes(1); quorumNumbers[0] = bytes1(defaultQuorumNumber); @@ -822,17 +659,11 @@ contract BLSApkRegistryUnitTests_quorumApkUpdates is BLSApkRegistryUnitTests { // register and check quorum apk updates BN254.G1Point[] memory quorumApksBefore = _getApks(quorumNumbers); address operator = _selectNewOperator(); - (BN254.G1Point memory operatorPubkey, ) = _registerRandomBLSPubkey( - operator, - randSeed[i] - ); + (BN254.G1Point memory operatorPubkey,) = _registerRandomBLSPubkey(operator, randSeed[i]); _registerOperator(operator, quorumNumbers); BN254.G1Point[] memory quorumApksAfter = _getApks(quorumNumbers); _assertQuorumApkUpdates( - quorumNumbers, - quorumApksBefore, - quorumApksAfter, - operatorPubkey + quorumNumbers, quorumApksBefore, quorumApksAfter, operatorPubkey ); // deregister and check quorum apk updates @@ -842,10 +673,7 @@ contract BLSApkRegistryUnitTests_quorumApkUpdates is BLSApkRegistryUnitTests { _deregisterOperator(operator, quorumNumbers); quorumApksAfter = _getApks(quorumNumbers); _assertQuorumApkUpdates( - quorumNumbers, - quorumApksBefore, - quorumApksAfter, - operatorPubkey.negate() + quorumNumbers, quorumApksBefore, quorumApksAfter, operatorPubkey.negate() ); } } @@ -863,13 +691,9 @@ contract BLSApkRegistryUnitTests_quorumApkUpdates is BLSApkRegistryUnitTests { cheats.assume(0 < numOperators && numOperators <= 50); cheats.assume(quorumBitmap > initializedQuorumBitmap); // mask out quorums that are already initialized - uint192 initializingBitmap = uint192( - quorumBitmap.minus(uint256(initializedQuorumBitmap)) - ); + uint192 initializingBitmap = uint192(quorumBitmap.minus(uint256(initializedQuorumBitmap))); _initializeFuzzedQuorums(initializingBitmap); - bytes memory quorumNumbers = bitmapUtilsWrapper.bitmapToBytesArray( - quorumBitmap - ); + bytes memory quorumNumbers = bitmapUtilsWrapper.bitmapToBytesArray(quorumBitmap); /** * For each operator, randomly proceed with either registering/deregistering an operator @@ -880,17 +704,11 @@ contract BLSApkRegistryUnitTests_quorumApkUpdates is BLSApkRegistryUnitTests { // register and check quorum apk updates BN254.G1Point[] memory quorumApksBefore = _getApks(quorumNumbers); address operator = _selectNewOperator(); - (BN254.G1Point memory operatorPubkey, ) = _registerRandomBLSPubkey( - operator, - randSeed[i] - ); + (BN254.G1Point memory operatorPubkey,) = _registerRandomBLSPubkey(operator, randSeed[i]); _registerOperator(operator, quorumNumbers); BN254.G1Point[] memory quorumApksAfter = _getApks(quorumNumbers); _assertQuorumApkUpdates( - quorumNumbers, - quorumApksBefore, - quorumApksAfter, - operatorPubkey + quorumNumbers, quorumApksBefore, quorumApksAfter, operatorPubkey ); // deregister and check quorum apk updates @@ -900,10 +718,7 @@ contract BLSApkRegistryUnitTests_quorumApkUpdates is BLSApkRegistryUnitTests { _deregisterOperator(operator, quorumNumbers); quorumApksAfter = _getApks(quorumNumbers); _assertQuorumApkUpdates( - quorumNumbers, - quorumApksBefore, - quorumApksAfter, - operatorPubkey.negate() + quorumNumbers, quorumApksBefore, quorumApksAfter, operatorPubkey.negate() ); } } @@ -925,13 +740,9 @@ contract BLSApkRegistryUnitTests_quorumApkUpdates is BLSApkRegistryUnitTests { _initializeFuzzedQuorum(quorumNumber2); } - BN254.G1Point[] memory quorumApksBefore = new BN254.G1Point[]( - quorumNumbers.length - ); + BN254.G1Point[] memory quorumApksBefore = new BN254.G1Point[](quorumNumbers.length); for (uint8 i = 0; i < quorumNumbers.length; i++) { - quorumApksBefore[i] = blsApkRegistry.getApk( - uint8(quorumNumbers[i]) - ); + quorumApksBefore[i] = blsApkRegistry.getApk(uint8(quorumNumbers[i])); } // use harnessed function to directly set the pubkey, bypassing the ordinary checks @@ -942,16 +753,9 @@ contract BLSApkRegistryUnitTests_quorumApkUpdates is BLSApkRegistryUnitTests { //check quorum apk updates for (uint8 i = 0; i < quorumNumbers.length; i++) { - BN254.G1Point memory quorumApkAfter = blsApkRegistry.getApk( - uint8(quorumNumbers[i]) - ); + BN254.G1Point memory quorumApkAfter = blsApkRegistry.getApk(uint8(quorumNumbers[i])); assertEq( - BN254.hashG1Point( - BN254.plus( - quorumApkAfter, - BN254.negate(quorumApksBefore[i]) - ) - ), + BN254.hashG1Point(BN254.plus(quorumApkAfter, BN254.negate(quorumApksBefore[i]))), BN254.hashG1Point(defaultPubKey), "quorum apk not updated correctly" ); @@ -971,9 +775,7 @@ contract BLSApkRegistryUnitTests_quorumApkUpdates is BLSApkRegistryUnitTests { _registerRandomBLSPubkey(defaultOperator, randSeed); _registerOperator(defaultOperator, quorumNumbers); - BN254.G1Point memory quorumApk = blsApkRegistry.getApk( - defaultQuorumNumber - ); + BN254.G1Point memory quorumApk = blsApkRegistry.getApk(defaultQuorumNumber); BN254.G1Point memory negatedQuorumApk = BN254.negate(quorumApk); //register for one quorum with negative quorum apk @@ -1009,23 +811,18 @@ contract BLSApkRegistryUnitTests_quorumApkUpdates is BLSApkRegistryUnitTests { for (uint256 i = 0; i < numRegistrants; i++) { // generate operator and register them with BLS pubkey address operator = _selectNewOperator(); - (BN254.G1Point memory operatorPubkey, ) = _registerRandomBLSPubkey( - operator, - uint256(keccak256(abi.encodePacked(operator, randSeed))) + (BN254.G1Point memory operatorPubkey,) = _registerRandomBLSPubkey( + operator, uint256(keccak256(abi.encodePacked(operator, randSeed))) ); _registerOperator(operator, quorumNumbers); quorumApk = quorumApk.plus(operatorPubkey); quorumApkHash = bytes24(BN254.hashG1Point(quorumApk)); - uint256 historyLength = blsApkRegistry.getApkHistoryLength( - defaultQuorumNumber - ); + uint256 historyLength = blsApkRegistry.getApkHistoryLength(defaultQuorumNumber); assertEq( quorumApkHash, blsApkRegistry.getApkHashAtBlockNumberAndIndex( - defaultQuorumNumber, - uint32(block.number + blockGap), - historyLength - 1 + defaultQuorumNumber, uint32(block.number + blockGap), historyLength - 1 ), "incorrect quorum apk update" ); @@ -1034,15 +831,11 @@ contract BLSApkRegistryUnitTests_quorumApkUpdates is BLSApkRegistryUnitTests { _deregisterOperator(operator, quorumNumbers); quorumApk = quorumApk.plus(operatorPubkey.negate()); quorumApkHash = bytes24(BN254.hashG1Point(quorumApk)); - historyLength = blsApkRegistry.getApkHistoryLength( - defaultQuorumNumber - ); + historyLength = blsApkRegistry.getApkHistoryLength(defaultQuorumNumber); assertEq( quorumApkHash, blsApkRegistry.getApkHashAtBlockNumberAndIndex( - defaultQuorumNumber, - uint32(block.number + blockGap), - historyLength - 1 + defaultQuorumNumber, uint32(block.number + blockGap), historyLength - 1 ), "incorrect quorum apk update" ); @@ -1072,34 +865,23 @@ contract BLSApkRegistryUnitTests_quorumApkUpdates is BLSApkRegistryUnitTests { for (uint256 i = 0; i < numRegistrants; i++) { address operator = _selectNewOperator(); _registerRandomBLSPubkey( - operator, - uint256(keccak256(abi.encodePacked(operator, randSeed))) + operator, uint256(keccak256(abi.encodePacked(operator, randSeed))) ); _registerOperator(operator, quorumNumbers); cheats.roll(block.number + 100); } if (wrongBlockNumber < startingBlockNumber + indexToCheck * 100) { emit log_named_uint("index too recent: ", indexToCheck); - cheats.expectRevert( - "BLSApkRegistry._validateApkHashAtBlockNumber: index too recent" - ); + cheats.expectRevert(IBLSApkRegistryErrors.BlockNumberTooRecent.selector); blsApkRegistry.getApkHashAtBlockNumberAndIndex( - defaultQuorumNumber, - wrongBlockNumber, - indexToCheck + 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( - "BLSApkRegistry._validateApkHashAtBlockNumber: not latest apk update" - ); + cheats.expectRevert(IBLSApkRegistryErrors.BlockNumberNotLatest.selector); blsApkRegistry.getApkHashAtBlockNumberAndIndex( - defaultQuorumNumber, - wrongBlockNumber, - indexToCheck + defaultQuorumNumber, wrongBlockNumber, indexToCheck ); } } @@ -1125,9 +907,7 @@ contract BLSApkRegistryUnitTests_quorumApkUpdates is BLSApkRegistryUnitTests { BN254.G1Point[] memory quorumApksBefore = new BN254.G1Point[](2); for (uint8 i = 0; i < quorumNumbers.length; i++) { - quorumApksBefore[i] = blsApkRegistry.getApk( - uint8(quorumNumbers[i]) - ); + quorumApksBefore[i] = blsApkRegistry.getApk(uint8(quorumNumbers[i])); } cheats.startPrank(address(registryCoordinator)); @@ -1137,13 +917,9 @@ contract BLSApkRegistryUnitTests_quorumApkUpdates is BLSApkRegistryUnitTests { BN254.G1Point memory quorumApkAfter; for (uint8 i = 0; i < quorumNumbers.length; i++) { quorumApkAfter = blsApkRegistry.getApk(uint8(quorumNumbers[i])); - BN254.G1Point memory quorumApk = blsApkRegistry.getApk( - defaultQuorumNumber - ); + BN254.G1Point memory quorumApk = blsApkRegistry.getApk(defaultQuorumNumber); assertEq( - BN254.hashG1Point( - quorumApksBefore[i].plus(defaultPubKey.negate()) - ), + BN254.hashG1Point(quorumApksBefore[i].plus(defaultPubKey.negate())), BN254.hashG1Point(quorumApkAfter), "quorum apk not updated correctly" ); diff --git a/test/unit/BLSSignatureCheckerUnit.t.sol b/test/unit/BLSSignatureCheckerUnit.t.sol index 3b5cf6e1..ef13c575 100644 --- a/test/unit/BLSSignatureCheckerUnit.t.sol +++ b/test/unit/BLSSignatureCheckerUnit.t.sol @@ -1,24 +1,31 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "../../src/BLSSignatureChecker.sol"; import "../utils/BLSMockAVSDeployer.sol"; +import { + IBLSSignatureCheckerErrors, + IBLSSignatureCheckerTypes +} from "../../src/interfaces/IBLSSignatureChecker.sol"; +import {IBLSApkRegistryErrors} from "../../src/interfaces/IBLSApkRegistry.sol"; +import {QuorumBitmapHistoryLib} from "../../src/libraries/QuorumBitmapHistoryLib.sol"; +import {IStakeRegistryErrors} from "../../src/interfaces/IStakeRegistry.sol"; contract BLSSignatureCheckerUnitTests is BLSMockAVSDeployer { using BN254 for BN254.G1Point; BLSSignatureChecker blsSignatureChecker; - event StaleStakesForbiddenUpdate(bool value); + event StaleStakesForbiddenUpdate(bool value); - function setUp() virtual public { + function setUp() public virtual { _setUpBLSMockAVSDeployer(); blsSignatureChecker = new BLSSignatureChecker(registryCoordinator); } function test_setStaleStakesForbidden_revert_notRegCoordOwner() public { - cheats.expectRevert("BLSSignatureChecker.onlyCoordinatorOwner: caller is not the owner of the registryCoordinator"); + cheats.expectRevert(IBLSSignatureCheckerErrors.OnlyRegistryCoordinatorOwner.selector); blsSignatureChecker.setStaleStakesForbidden(true); } @@ -27,7 +34,9 @@ contract BLSSignatureCheckerUnitTests is BLSMockAVSDeployer { testFuzz_setStaleStakesForbidden(true); } - function testFuzz_setStaleStakesForbidden(bool newState) public { + function testFuzz_setStaleStakesForbidden( + bool newState + ) public { cheats.expectEmit(true, true, true, true, address(blsSignatureChecker)); emit StaleStakesForbiddenUpdate(newState); cheats.prank(registryCoordinatorOwner); @@ -38,35 +47,46 @@ contract BLSSignatureCheckerUnitTests is BLSMockAVSDeployer { // this test checks that a valid signature from maxOperatorsToRegister with a random number of nonsigners is checked // correctly on the BLSSignatureChecker contract when all operators are only regsitered for a single quorum and // the signature is only checked for stakes on that quorum - function testFuzz_checkSignatures_SingleQuorum(uint256 pseudoRandomNumber) public { + function testFuzz_checkSignatures_SingleQuorum( + uint256 pseudoRandomNumber + ) public { uint256 numNonSigners = pseudoRandomNumber % (maxOperatorsToRegister - 1); uint256 quorumBitmap = 1; bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); - (uint32 referenceBlockNumber, BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature) = - _registerSignatoriesAndGetNonSignerStakeAndSignatureRandom(pseudoRandomNumber, numNonSigners, quorumBitmap); + ( + uint32 referenceBlockNumber, + BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature + ) = _registerSignatoriesAndGetNonSignerStakeAndSignatureRandom( + pseudoRandomNumber, numNonSigners, quorumBitmap + ); - bytes32[] memory pubkeyHashes = new bytes32[](nonSignerStakesAndSignature.nonSignerPubkeys.length); + bytes32[] memory pubkeyHashes = + new bytes32[](nonSignerStakesAndSignature.nonSignerPubkeys.length); for (uint256 i = 0; i < nonSignerStakesAndSignature.nonSignerPubkeys.length; ++i) { pubkeyHashes[i] = nonSignerStakesAndSignature.nonSignerPubkeys[i].hashG1Point(); } - bytes32 expectedSignatoryRecordHash = keccak256(abi.encodePacked(referenceBlockNumber, pubkeyHashes)); + bytes32 expectedSignatoryRecordHash = + keccak256(abi.encodePacked(referenceBlockNumber, pubkeyHashes)); uint256 gasBefore = gasleft(); ( BLSSignatureChecker.QuorumStakeTotals memory quorumStakeTotals, bytes32 signatoryRecordHash ) = blsSignatureChecker.checkSignatures( - msgHash, - quorumNumbers, - referenceBlockNumber, - nonSignerStakesAndSignature + msgHash, quorumNumbers, referenceBlockNumber, nonSignerStakesAndSignature ); uint256 gasAfter = gasleft(); emit log_named_uint("gasUsed", gasBefore - gasAfter); - assertTrue(quorumStakeTotals.signedStakeForQuorum[0] > 0, "signedStakeForQuorum should be nonzero"); - assertEq(expectedSignatoryRecordHash, signatoryRecordHash, "signatoryRecordHash does not match expectation"); + assertTrue( + quorumStakeTotals.signedStakeForQuorum[0] > 0, "signedStakeForQuorum should be nonzero" + ); + assertEq( + expectedSignatoryRecordHash, + signatoryRecordHash, + "signatoryRecordHash does not match expectation" + ); // 0 nonSigners: 159908 // 1 nonSigner: 178683 // 2 nonSigners: 197410 @@ -78,82 +98,114 @@ contract BLSSignatureCheckerUnitTests is BLSMockAVSDeployer { uint256 quorumBitmap = 1; bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); - (uint32 referenceBlockNumber, BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature) = - _registerSignatoriesAndGetNonSignerStakeAndSignatureRandom(nonRandomNumber, numNonSigners, quorumBitmap); + ( + uint32 referenceBlockNumber, + BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature + ) = _registerSignatoriesAndGetNonSignerStakeAndSignatureRandom( + nonRandomNumber, numNonSigners, quorumBitmap + ); - bytes32[] memory pubkeyHashes = new bytes32[](nonSignerStakesAndSignature.nonSignerPubkeys.length); + bytes32[] memory pubkeyHashes = + new bytes32[](nonSignerStakesAndSignature.nonSignerPubkeys.length); for (uint256 i = 0; i < nonSignerStakesAndSignature.nonSignerPubkeys.length; ++i) { pubkeyHashes[i] = nonSignerStakesAndSignature.nonSignerPubkeys[i].hashG1Point(); } - bytes32 expectedSignatoryRecordHash = keccak256(abi.encodePacked(referenceBlockNumber, pubkeyHashes)); + bytes32 expectedSignatoryRecordHash = + keccak256(abi.encodePacked(referenceBlockNumber, pubkeyHashes)); uint256 gasBefore = gasleft(); ( BLSSignatureChecker.QuorumStakeTotals memory quorumStakeTotals, bytes32 signatoryRecordHash ) = blsSignatureChecker.checkSignatures( - msgHash, - quorumNumbers, - referenceBlockNumber, - nonSignerStakesAndSignature + msgHash, quorumNumbers, referenceBlockNumber, nonSignerStakesAndSignature ); uint256 gasAfter = gasleft(); emit log_named_uint("gasUsed", gasBefore - gasAfter); - assertEq(expectedSignatoryRecordHash, signatoryRecordHash, "signatoryRecordHash does not match expectation"); + assertEq( + expectedSignatoryRecordHash, + signatoryRecordHash, + "signatoryRecordHash does not match expectation" + ); - assertEq(quorumStakeTotals.signedStakeForQuorum[0], 3000000000000000000, "signedStakeForQuorum incorrect"); - assertEq(quorumStakeTotals.totalStakeForQuorum[0], 4000000000000000000, "totalStakeForQuorum incorrect"); + assertEq( + quorumStakeTotals.signedStakeForQuorum[0], + 3000000000000000000, + "signedStakeForQuorum incorrect" + ); + assertEq( + quorumStakeTotals.totalStakeForQuorum[0], + 4000000000000000000, + "totalStakeForQuorum incorrect" + ); } // this test checks that a valid signature from maxOperatorsToRegister with a random number of nonsigners is checked // correctly on the BLSSignatureChecker contract when all operators are registered for the first 100 quorums // and the signature is only checked for stakes on those quorums - function test_checkSignatures_100Quorums(uint256 pseudoRandomNumber) public { + function test_checkSignatures_100Quorums( + uint256 pseudoRandomNumber + ) public { uint256 numNonSigners = pseudoRandomNumber % (maxOperatorsToRegister - 1); // 100 set bits uint256 quorumBitmap = (1 << 100) - 1; bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); - (uint32 referenceBlockNumber, BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature) = - _registerSignatoriesAndGetNonSignerStakeAndSignatureRandom(pseudoRandomNumber, numNonSigners, quorumBitmap); + ( + uint32 referenceBlockNumber, + BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature + ) = _registerSignatoriesAndGetNonSignerStakeAndSignatureRandom( + pseudoRandomNumber, numNonSigners, quorumBitmap + ); nonSignerStakesAndSignature.sigma = sigma.scalar_mul(quorumNumbers.length); nonSignerStakesAndSignature.apkG2 = oneHundredQuorumApkG2; - bytes32[] memory pubkeyHashes = new bytes32[](nonSignerStakesAndSignature.nonSignerPubkeys.length); + bytes32[] memory pubkeyHashes = + new bytes32[](nonSignerStakesAndSignature.nonSignerPubkeys.length); for (uint256 i = 0; i < nonSignerStakesAndSignature.nonSignerPubkeys.length; ++i) { pubkeyHashes[i] = nonSignerStakesAndSignature.nonSignerPubkeys[i].hashG1Point(); } - bytes32 expectedSignatoryRecordHash = keccak256(abi.encodePacked(referenceBlockNumber, pubkeyHashes)); + bytes32 expectedSignatoryRecordHash = + keccak256(abi.encodePacked(referenceBlockNumber, pubkeyHashes)); uint256 gasBefore = gasleft(); ( BLSSignatureChecker.QuorumStakeTotals memory quorumStakeTotals, bytes32 signatoryRecordHash ) = blsSignatureChecker.checkSignatures( - msgHash, - quorumNumbers, - referenceBlockNumber, - nonSignerStakesAndSignature + msgHash, quorumNumbers, referenceBlockNumber, nonSignerStakesAndSignature ); uint256 gasAfter = gasleft(); emit log_named_uint("gasUsed", gasBefore - gasAfter); for (uint256 i = 0; i < quorumStakeTotals.signedStakeForQuorum.length; ++i) { - assertTrue(quorumStakeTotals.signedStakeForQuorum[i] > 0, "signedStakeForQuorum should be nonzero"); + assertTrue( + quorumStakeTotals.signedStakeForQuorum[i] > 0, + "signedStakeForQuorum should be nonzero" + ); } - assertEq(expectedSignatoryRecordHash, signatoryRecordHash, "signatoryRecordHash does not match expectation"); + assertEq( + expectedSignatoryRecordHash, + signatoryRecordHash, + "signatoryRecordHash does not match expectation" + ); } function test_checkSignatures_revert_inputLengthMismatch() public { uint256 numNonSigners = 0; uint256 quorumBitmap = 1; bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); - (uint32 referenceBlockNumber, BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature) = - _registerSignatoriesAndGetNonSignerStakeAndSignatureRandom(1, numNonSigners, quorumBitmap); + ( + uint32 referenceBlockNumber, + BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature + ) = _registerSignatoriesAndGetNonSignerStakeAndSignatureRandom( + 1, numNonSigners, quorumBitmap + ); - IBLSSignatureChecker.NonSignerStakesAndSignature memory incorrectLengthInputs = IBLSSignatureChecker.NonSignerStakesAndSignature({ + IBLSSignatureCheckerTypes.NonSignerStakesAndSignature memory incorrectLengthInputs = + IBLSSignatureCheckerTypes.NonSignerStakesAndSignature({ nonSignerQuorumBitmapIndices: nonSignerStakesAndSignature.nonSignerQuorumBitmapIndices, nonSignerPubkeys: nonSignerStakesAndSignature.nonSignerPubkeys, quorumApks: nonSignerStakesAndSignature.quorumApks, @@ -166,89 +218,76 @@ contract BLSSignatureCheckerUnitTests is BLSMockAVSDeployer { // make one part of the input incorrect length incorrectLengthInputs.quorumApks = new BN254.G1Point[](5); - cheats.expectRevert("BLSSignatureChecker.checkSignatures: input quorum length mismatch"); + cheats.expectRevert(IBLSSignatureCheckerErrors.InputArrayLengthMismatch.selector); blsSignatureChecker.checkSignatures( - msgHash, - quorumNumbers, - referenceBlockNumber, - incorrectLengthInputs + msgHash, quorumNumbers, referenceBlockNumber, incorrectLengthInputs ); // reset the input to correct values incorrectLengthInputs.quorumApks = nonSignerStakesAndSignature.quorumApks; // make one part of the input incorrect length incorrectLengthInputs.quorumApkIndices = new uint32[](5); - cheats.expectRevert("BLSSignatureChecker.checkSignatures: input quorum length mismatch"); + cheats.expectRevert(IBLSSignatureCheckerErrors.InputArrayLengthMismatch.selector); blsSignatureChecker.checkSignatures( - msgHash, - quorumNumbers, - referenceBlockNumber, - incorrectLengthInputs + msgHash, quorumNumbers, referenceBlockNumber, incorrectLengthInputs ); // reset the input to correct values incorrectLengthInputs.quorumApkIndices = nonSignerStakesAndSignature.quorumApkIndices; // make one part of the input incorrect length incorrectLengthInputs.totalStakeIndices = new uint32[](5); - cheats.expectRevert("BLSSignatureChecker.checkSignatures: input quorum length mismatch"); + cheats.expectRevert(IBLSSignatureCheckerErrors.InputArrayLengthMismatch.selector); blsSignatureChecker.checkSignatures( - msgHash, - quorumNumbers, - referenceBlockNumber, - incorrectLengthInputs + msgHash, quorumNumbers, referenceBlockNumber, incorrectLengthInputs ); // reset the input to correct values incorrectLengthInputs.totalStakeIndices = nonSignerStakesAndSignature.totalStakeIndices; // make one part of the input incorrect length incorrectLengthInputs.nonSignerStakeIndices = new uint32[][](5); - cheats.expectRevert("BLSSignatureChecker.checkSignatures: input quorum length mismatch"); + cheats.expectRevert(IBLSSignatureCheckerErrors.InputArrayLengthMismatch.selector); blsSignatureChecker.checkSignatures( - msgHash, - quorumNumbers, - referenceBlockNumber, - incorrectLengthInputs + msgHash, quorumNumbers, referenceBlockNumber, incorrectLengthInputs ); // reset the input to correct values - incorrectLengthInputs.nonSignerStakeIndices = nonSignerStakesAndSignature.nonSignerStakeIndices; + incorrectLengthInputs.nonSignerStakeIndices = + nonSignerStakesAndSignature.nonSignerStakeIndices; // make one part of the input incorrect length - incorrectLengthInputs.nonSignerQuorumBitmapIndices = new uint32[](nonSignerStakesAndSignature.nonSignerPubkeys.length + 1); - cheats.expectRevert("BLSSignatureChecker.checkSignatures: input nonsigner length mismatch"); + incorrectLengthInputs.nonSignerQuorumBitmapIndices = + new uint32[](nonSignerStakesAndSignature.nonSignerPubkeys.length + 1); + cheats.expectRevert(IBLSSignatureCheckerErrors.InputNonSignerLengthMismatch.selector); blsSignatureChecker.checkSignatures( - msgHash, - quorumNumbers, - referenceBlockNumber, - incorrectLengthInputs + msgHash, quorumNumbers, referenceBlockNumber, incorrectLengthInputs ); // reset the input to correct values - incorrectLengthInputs.nonSignerQuorumBitmapIndices = nonSignerStakesAndSignature.nonSignerQuorumBitmapIndices; + incorrectLengthInputs.nonSignerQuorumBitmapIndices = + nonSignerStakesAndSignature.nonSignerQuorumBitmapIndices; // sanity check for call passing with the correct values blsSignatureChecker.checkSignatures( - msgHash, - quorumNumbers, - referenceBlockNumber, - incorrectLengthInputs + msgHash, quorumNumbers, referenceBlockNumber, incorrectLengthInputs ); } - function test_checkSignatures_revert_referenceBlockNumberInFuture(uint256 pseudoRandomNumber) public { + function test_checkSignatures_revert_referenceBlockNumberInFuture( + uint256 pseudoRandomNumber + ) public { uint256 numNonSigners = pseudoRandomNumber % (maxOperatorsToRegister - 2) + 1; uint256 quorumBitmap = 1; bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); - (/*uint32 referenceBlockNumber*/, BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature) = - _registerSignatoriesAndGetNonSignerStakeAndSignatureRandom(pseudoRandomNumber, numNonSigners, quorumBitmap); - + ( /*uint32 referenceBlockNumber*/ + , BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature + ) = _registerSignatoriesAndGetNonSignerStakeAndSignatureRandom( + pseudoRandomNumber, numNonSigners, quorumBitmap + ); + // Create an invalid reference block: any block number >= the current block uint32 invalidReferenceBlock = uint32(block.number + (pseudoRandomNumber % 20)); - cheats.expectRevert("BLSSignatureChecker.checkSignatures: invalid reference block"); + cheats.expectRevert(IBLSSignatureCheckerErrors.InvalidReferenceBlocknumber.selector); blsSignatureChecker.checkSignatures( - msgHash, - quorumNumbers, - invalidReferenceBlock, - nonSignerStakesAndSignature + msgHash, quorumNumbers, invalidReferenceBlock, nonSignerStakesAndSignature ); } @@ -258,17 +297,19 @@ contract BLSSignatureCheckerUnitTests is BLSMockAVSDeployer { uint256 nonRandomNumber = 777; bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); - (uint32 referenceBlockNumber, BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature) = - _registerSignatoriesAndGetNonSignerStakeAndSignatureRandom(nonRandomNumber, numNonSigners, quorumBitmap); - + ( + uint32 referenceBlockNumber, + BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature + ) = _registerSignatoriesAndGetNonSignerStakeAndSignatureRandom( + nonRandomNumber, numNonSigners, quorumBitmap + ); + // swap out a pubkey to make sure there is a duplicate - nonSignerStakesAndSignature.nonSignerPubkeys[1] = nonSignerStakesAndSignature.nonSignerPubkeys[0]; - cheats.expectRevert("BLSSignatureChecker.checkSignatures: nonSignerPubkeys not sorted"); + nonSignerStakesAndSignature.nonSignerPubkeys[1] = + nonSignerStakesAndSignature.nonSignerPubkeys[0]; + cheats.expectRevert(IBLSSignatureCheckerErrors.NonSignerPubkeysNotSorted.selector); blsSignatureChecker.checkSignatures( - msgHash, - quorumNumbers, - referenceBlockNumber, - nonSignerStakesAndSignature + msgHash, quorumNumbers, referenceBlockNumber, nonSignerStakesAndSignature ); } @@ -278,240 +319,267 @@ contract BLSSignatureCheckerUnitTests is BLSMockAVSDeployer { uint256 nonRandomNumber = 777; bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); - (uint32 referenceBlockNumber, BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature) = - _registerSignatoriesAndGetNonSignerStakeAndSignatureRandom(nonRandomNumber, numNonSigners, quorumBitmap); - + ( + uint32 referenceBlockNumber, + BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature + ) = _registerSignatoriesAndGetNonSignerStakeAndSignatureRandom( + nonRandomNumber, numNonSigners, quorumBitmap + ); + // swap two pubkeys to ensure ordering is wrong - (nonSignerStakesAndSignature.nonSignerPubkeys[0], nonSignerStakesAndSignature.nonSignerPubkeys[1]) = - (nonSignerStakesAndSignature.nonSignerPubkeys[1], nonSignerStakesAndSignature.nonSignerPubkeys[0]); - cheats.expectRevert("BLSSignatureChecker.checkSignatures: nonSignerPubkeys not sorted"); + ( + nonSignerStakesAndSignature.nonSignerPubkeys[0], + nonSignerStakesAndSignature.nonSignerPubkeys[1] + ) = ( + nonSignerStakesAndSignature.nonSignerPubkeys[1], + nonSignerStakesAndSignature.nonSignerPubkeys[0] + ); + cheats.expectRevert(IBLSSignatureCheckerErrors.NonSignerPubkeysNotSorted.selector); blsSignatureChecker.checkSignatures( - msgHash, - quorumNumbers, - referenceBlockNumber, - nonSignerStakesAndSignature + msgHash, quorumNumbers, referenceBlockNumber, nonSignerStakesAndSignature ); } function test_checkSignatures_revert_staleStakes() public { - vm.skip(true); uint256 numNonSigners = 2; uint256 quorumBitmap = 1; uint256 nonRandomNumber = 777; bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); - (uint32 referenceBlockNumber, BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature) = - _registerSignatoriesAndGetNonSignerStakeAndSignatureRandom(nonRandomNumber, numNonSigners, quorumBitmap); + ( + uint32 referenceBlockNumber, + BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature + ) = _registerSignatoriesAndGetNonSignerStakeAndSignatureRandom( + nonRandomNumber, numNonSigners, quorumBitmap + ); // make sure the `staleStakesForbidden` flag is set to 'true' testFuzz_setStaleStakesForbidden(true); - + uint256 stalestUpdateBlock = type(uint256).max; for (uint256 i = 0; i < quorumNumbers.length; ++i) { - uint256 quorumUpdateBlockNumber = registryCoordinator.quorumUpdateBlockNumber(uint8(quorumNumbers[i])); + uint256 quorumUpdateBlockNumber = + registryCoordinator.quorumUpdateBlockNumber(uint8(quorumNumbers[i])); if (quorumUpdateBlockNumber < stalestUpdateBlock) { stalestUpdateBlock = quorumUpdateBlockNumber; } } // move referenceBlockNumber forward to a block number the last block number where the stakes will be considered "not stale" - referenceBlockNumber = uint32(stalestUpdateBlock + delegationMock.minWithdrawalDelayBlocks()) - 1; + referenceBlockNumber = + uint32(stalestUpdateBlock + delegationMock.minWithdrawalDelayBlocks()) - 1; // roll forward to make the reference block number valid // we roll to referenceBlockNumber + 1 because the current block number is not a valid reference block cheats.roll(referenceBlockNumber + 1); blsSignatureChecker.checkSignatures( - msgHash, - quorumNumbers, - referenceBlockNumber, - nonSignerStakesAndSignature + msgHash, quorumNumbers, referenceBlockNumber, nonSignerStakesAndSignature ); // move referenceBlockNumber forward one more block, making the stakes "stale" referenceBlockNumber += 1; // roll forward to reference + 1 to ensure the referenceBlockNumber is still valid cheats.roll(referenceBlockNumber + 1); - cheats.expectRevert("BLSSignatureChecker.checkSignatures: StakeRegistry updates must be within withdrawalDelayBlocks window"); + cheats.expectRevert(IBLSSignatureCheckerErrors.StaleStakesForbidden.selector); blsSignatureChecker.checkSignatures( - msgHash, - quorumNumbers, - referenceBlockNumber, - nonSignerStakesAndSignature + msgHash, quorumNumbers, referenceBlockNumber, nonSignerStakesAndSignature ); } - function test_checkSignatures_revert_incorrectQuorumBitmapIndex(uint256 pseudoRandomNumber) public { + function test_checkSignatures_revert_incorrectQuorumBitmapIndex( + uint256 pseudoRandomNumber + ) public { uint256 numNonSigners = pseudoRandomNumber % (maxOperatorsToRegister - 2) + 1; uint256 quorumBitmap = 1; bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); - (uint32 referenceBlockNumber, BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature) = - _registerSignatoriesAndGetNonSignerStakeAndSignatureRandom(pseudoRandomNumber, numNonSigners, quorumBitmap); - + ( + uint32 referenceBlockNumber, + BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature + ) = _registerSignatoriesAndGetNonSignerStakeAndSignatureRandom( + pseudoRandomNumber, numNonSigners, quorumBitmap + ); + // record a quorumBitmap update via a harnessed function - registryCoordinator._updateOperatorBitmapExternal(nonSignerStakesAndSignature.nonSignerPubkeys[0].hashG1Point(), uint192(quorumBitmap | 2)); + registryCoordinator._updateOperatorBitmapExternal( + nonSignerStakesAndSignature.nonSignerPubkeys[0].hashG1Point(), uint192(quorumBitmap | 2) + ); // set the nonSignerQuorumBitmapIndices to a different value nonSignerStakesAndSignature.nonSignerQuorumBitmapIndices[0] = 1; - cheats.expectRevert("RegCoord.getQuorumBitmapAtBlockNumberByIndex: quorumBitmapUpdate is from after blockNumber"); + cheats.expectRevert(QuorumBitmapHistoryLib.BitmapUpdateIsAfterBlockNumber.selector); blsSignatureChecker.checkSignatures( - msgHash, - quorumNumbers, - referenceBlockNumber, - nonSignerStakesAndSignature + msgHash, quorumNumbers, referenceBlockNumber, nonSignerStakesAndSignature ); } - function test_checkSignatures_revert_incorrectTotalStakeIndex(uint256 pseudoRandomNumber) public { + function test_checkSignatures_revert_incorrectTotalStakeIndex( + uint256 pseudoRandomNumber + ) public { uint256 numNonSigners = pseudoRandomNumber % (maxOperatorsToRegister - 2) + 1; uint256 quorumBitmap = 1; bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); - (uint32 referenceBlockNumber, BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature) = - _registerSignatoriesAndGetNonSignerStakeAndSignatureRandom(pseudoRandomNumber, numNonSigners, quorumBitmap); - + ( + uint32 referenceBlockNumber, + BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature + ) = _registerSignatoriesAndGetNonSignerStakeAndSignatureRandom( + pseudoRandomNumber, numNonSigners, quorumBitmap + ); + // set the totalStakeIndices to a different value nonSignerStakesAndSignature.totalStakeIndices[0] = 0; - cheats.expectRevert("StakeRegistry._validateStakeUpdateAtBlockNumber: there is a newer stakeUpdate available before blockNumber"); + cheats.expectRevert(IStakeRegistryErrors.InvalidBlockNumber.selector); blsSignatureChecker.checkSignatures( - msgHash, - quorumNumbers, - referenceBlockNumber, - nonSignerStakesAndSignature + msgHash, quorumNumbers, referenceBlockNumber, nonSignerStakesAndSignature ); } - function test_checkSignatures_revert_incorrectNonSignerStakeIndex(uint256 pseudoRandomNumber) public { + function test_checkSignatures_revert_incorrectNonSignerStakeIndex( + uint256 pseudoRandomNumber + ) public { uint256 numNonSigners = pseudoRandomNumber % (maxOperatorsToRegister - 2) + 1; uint256 quorumBitmap = 1; bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); - (uint32 referenceBlockNumber, BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature) = - _registerSignatoriesAndGetNonSignerStakeAndSignatureRandom(pseudoRandomNumber, numNonSigners, quorumBitmap); + ( + uint32 referenceBlockNumber, + BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature + ) = _registerSignatoriesAndGetNonSignerStakeAndSignatureRandom( + pseudoRandomNumber, numNonSigners, quorumBitmap + ); bytes32 nonSignerOperatorId = nonSignerStakesAndSignature.nonSignerPubkeys[0].hashG1Point(); - + // record a stake update - stakeRegistry.recordOperatorStakeUpdate( - nonSignerOperatorId, - uint8(quorumNumbers[0]), - 1234 - ); - + stakeRegistry.recordOperatorStakeUpdate(nonSignerOperatorId, uint8(quorumNumbers[0]), 1234); + // set the nonSignerStakeIndices to a different value nonSignerStakesAndSignature.nonSignerStakeIndices[0][0] = 1; - cheats.expectRevert("StakeRegistry._validateStakeUpdateAtBlockNumber: stakeUpdate is from after blockNumber"); + cheats.expectRevert(IStakeRegistryErrors.InvalidBlockNumber.selector); blsSignatureChecker.checkSignatures( - msgHash, - quorumNumbers, - referenceBlockNumber, - nonSignerStakesAndSignature + msgHash, quorumNumbers, referenceBlockNumber, nonSignerStakesAndSignature ); - } - function test_checkSignatures_revert_incorrectQuorumAPKIndex(uint256 pseudoRandomNumber) public { + function test_checkSignatures_revert_incorrectQuorumAPKIndex( + uint256 pseudoRandomNumber + ) public { uint256 numNonSigners = pseudoRandomNumber % (maxOperatorsToRegister - 2) + 1; uint256 quorumBitmap = 1; bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); - (uint32 referenceBlockNumber, BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature) = - _registerSignatoriesAndGetNonSignerStakeAndSignatureRandom(pseudoRandomNumber, numNonSigners, quorumBitmap); + ( + uint32 referenceBlockNumber, + BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature + ) = _registerSignatoriesAndGetNonSignerStakeAndSignatureRandom( + pseudoRandomNumber, numNonSigners, quorumBitmap + ); // set the quorumApkIndices to a different value nonSignerStakesAndSignature.quorumApkIndices[0] = 0; - cheats.expectRevert("BLSApkRegistry._validateApkHashAtBlockNumber: not latest apk update"); + cheats.expectRevert(IBLSApkRegistryErrors.BlockNumberNotLatest.selector); blsSignatureChecker.checkSignatures( - msgHash, - quorumNumbers, - referenceBlockNumber, - nonSignerStakesAndSignature + msgHash, quorumNumbers, referenceBlockNumber, nonSignerStakesAndSignature ); } - function test_checkSignatures_revert_incorrectQuorumAPK(uint256 pseudoRandomNumber) public { + function test_checkSignatures_revert_incorrectQuorumAPK( + uint256 pseudoRandomNumber + ) public { uint256 numNonSigners = pseudoRandomNumber % (maxOperatorsToRegister - 2) + 1; uint256 quorumBitmap = 1; bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); - (uint32 referenceBlockNumber, BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature) = - _registerSignatoriesAndGetNonSignerStakeAndSignatureRandom(pseudoRandomNumber, numNonSigners, quorumBitmap); - + ( + uint32 referenceBlockNumber, + BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature + ) = _registerSignatoriesAndGetNonSignerStakeAndSignatureRandom( + pseudoRandomNumber, numNonSigners, quorumBitmap + ); + // set the quorumApk to a different value - nonSignerStakesAndSignature.quorumApks[0] = nonSignerStakesAndSignature.quorumApks[0].negate(); + nonSignerStakesAndSignature.quorumApks[0] = + nonSignerStakesAndSignature.quorumApks[0].negate(); - cheats.expectRevert("BLSSignatureChecker.checkSignatures: quorumApk hash in storage does not match provided quorum apk"); + cheats.expectRevert(IBLSSignatureCheckerErrors.InvalidQuorumApkHash.selector); blsSignatureChecker.checkSignatures( - msgHash, - quorumNumbers, - referenceBlockNumber, - nonSignerStakesAndSignature + msgHash, quorumNumbers, referenceBlockNumber, nonSignerStakesAndSignature ); } - function test_checkSignatures_revert_incorrectSignature(uint256 pseudoRandomNumber) public { + function test_checkSignatures_revert_incorrectSignature( + uint256 pseudoRandomNumber + ) public { uint256 numNonSigners = pseudoRandomNumber % (maxOperatorsToRegister - 2) + 1; uint256 quorumBitmap = 1; bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); - (uint32 referenceBlockNumber, BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature) = - _registerSignatoriesAndGetNonSignerStakeAndSignatureRandom(pseudoRandomNumber, numNonSigners, quorumBitmap); - + ( + uint32 referenceBlockNumber, + BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature + ) = _registerSignatoriesAndGetNonSignerStakeAndSignatureRandom( + pseudoRandomNumber, numNonSigners, quorumBitmap + ); + // set the sigma to a different value nonSignerStakesAndSignature.sigma = nonSignerStakesAndSignature.sigma.negate(); - cheats.expectRevert("BLSSignatureChecker.checkSignatures: signature is invalid"); + cheats.expectRevert(IBLSSignatureCheckerErrors.InvalidBLSSignature.selector); blsSignatureChecker.checkSignatures( - msgHash, - quorumNumbers, - referenceBlockNumber, - nonSignerStakesAndSignature + msgHash, quorumNumbers, referenceBlockNumber, nonSignerStakesAndSignature ); } - function test_checkSignatures_revert_invalidSignature(uint256 pseudoRandomNumber) public { + function test_checkSignatures_revert_invalidSignature( + uint256 pseudoRandomNumber + ) public { uint256 numNonSigners = pseudoRandomNumber % (maxOperatorsToRegister - 2) + 1; uint256 quorumBitmap = 1; bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); - (uint32 referenceBlockNumber, BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature) = - _registerSignatoriesAndGetNonSignerStakeAndSignatureRandom(pseudoRandomNumber, numNonSigners, quorumBitmap); - + ( + uint32 referenceBlockNumber, + BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature + ) = _registerSignatoriesAndGetNonSignerStakeAndSignatureRandom( + pseudoRandomNumber, numNonSigners, quorumBitmap + ); + // set the sigma to a different value nonSignerStakesAndSignature.sigma.X++; // expect a non-specific low-level revert, since this call will ultimately fail as part of the precompile call cheats.expectRevert(); blsSignatureChecker.checkSignatures( - msgHash, - quorumNumbers, - referenceBlockNumber, - nonSignerStakesAndSignature + msgHash, quorumNumbers, referenceBlockNumber, nonSignerStakesAndSignature ); } - function testBLSSignatureChecker_reverts_emptyQuorums(uint256 pseudoRandomNumber) public { + function testBLSSignatureChecker_reverts_emptyQuorums( + uint256 pseudoRandomNumber + ) public { uint256 numNonSigners = pseudoRandomNumber % (maxOperatorsToRegister - 2) + 1; uint256 quorumBitmap = 1; - (uint32 referenceBlockNumber, BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature) = - _registerSignatoriesAndGetNonSignerStakeAndSignatureRandom(pseudoRandomNumber, numNonSigners, quorumBitmap); - + ( + uint32 referenceBlockNumber, + BLSSignatureChecker.NonSignerStakesAndSignature memory nonSignerStakesAndSignature + ) = _registerSignatoriesAndGetNonSignerStakeAndSignatureRandom( + pseudoRandomNumber, numNonSigners, quorumBitmap + ); + // Create an empty quorumNumbers array bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(0); // expect a non-specific low-level revert, since this call will ultimately fail as part of the precompile call - cheats.expectRevert("BLSSignatureChecker.checkSignatures: empty quorum input"); + cheats.expectRevert(IBLSSignatureCheckerErrors.InputEmptyQuorumNumbers.selector); blsSignatureChecker.checkSignatures( - msgHash, - quorumNumbers, - referenceBlockNumber, - nonSignerStakesAndSignature + msgHash, quorumNumbers, referenceBlockNumber, nonSignerStakesAndSignature ); } } diff --git a/test/unit/BitmapUtils.t.sol b/test/unit/BitmapUtils.t.sol index fd51298d..6e62f466 100644 --- a/test/unit/BitmapUtils.t.sol +++ b/test/unit/BitmapUtils.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "../harnesses/BitmapUtilsWrapper.sol"; // import "../../contracts/libraries/BitmapUtils.sol"; @@ -18,13 +18,15 @@ contract BitmapUtilsUnitTests is Test { contract BitmapUtilsUnitTests_bitwiseOperations is BitmapUtilsUnitTests { /// @notice check for consistency of `countNumOnes` function - function testFuzz_countNumOnes(uint256 input) public { + function testFuzz_countNumOnes( + uint256 input + ) public { uint16 libraryOutput = bitmapUtilsWrapper.countNumOnes(input); // run dumb routine uint16 numOnes = 0; for (uint256 i = 0; i < 256; ++i) { if ((input >> i) & 1 == 1) { - ++numOnes; + ++numOnes; } } assertEq(libraryOutput, numOnes, "inconsistency in countNumOnes function"); @@ -37,7 +39,9 @@ contract BitmapUtilsUnitTests_bitwiseOperations is BitmapUtilsUnitTests { assertTrue(bitmapUtilsWrapper.isSet(255, 7), "isSet function is broken 2"); assertTrue(bitmapUtilsWrapper.isSet(1024, 10), "isSet function is broken 3"); for (uint256 i = 0; i < 256; ++i) { - assertTrue(bitmapUtilsWrapper.isSet(type(uint256).max, uint8(i)), "isSet function is broken 4"); + assertTrue( + bitmapUtilsWrapper.isSet(type(uint256).max, uint8(i)), "isSet function is broken 4" + ); assertFalse(bitmapUtilsWrapper.isSet(0, uint8(i)), "isSet function is broken 5"); } } @@ -46,12 +50,12 @@ contract BitmapUtilsUnitTests_bitwiseOperations is BitmapUtilsUnitTests { // Ensure that numberToAdd isn't already in the bitmap cheats.assume(bitmap | (1 << bitToSet) != bitmap); uint256 updatedBitmap = bitmapUtilsWrapper.setBit(bitmap, bitToSet); - assertTrue( - bitmapUtilsWrapper.isSet(updatedBitmap, bitToSet), "setBit function is broken" - ); + assertTrue(bitmapUtilsWrapper.isSet(updatedBitmap, bitToSet), "setBit function is broken"); } - function testFuzz_isEmpty(uint256 input) public { + function testFuzz_isEmpty( + uint256 input + ) public { if (input == 0) { // assertTrue(bitmapUtilsWrapper.isEmpty(input), "isEmpty function is broken"); assertTrue(bitmapUtilsWrapper.isEmpty(input), "isEmpty function is broken"); @@ -123,16 +127,24 @@ contract BitmapUtilsUnitTests_bytesArrayToBitmap is BitmapUtilsUnitTests { function test_EmptyArrayEncoding() public { bytes memory emptyBytesArray; uint256 returnedBitMap = bitmapUtilsWrapper.orderedBytesArrayToBitmap(emptyBytesArray); - assertEq(returnedBitMap, 0, "BitmapUtilsUnitTests.testEmptyArrayEncoding: empty array not encoded to empty bitmap"); + assertEq( + returnedBitMap, + 0, + "BitmapUtilsUnitTests.testEmptyArrayEncoding: empty array not encoded to empty bitmap" + ); } // ensure that the bitmap encoding of a single uint8 (i.e. a single byte) matches the expected output - function testFuzz_SingleByteEncoding(uint8 fuzzedNumber) public { + function testFuzz_SingleByteEncoding( + uint8 fuzzedNumber + ) public { bytes1 singleByte = bytes1(fuzzedNumber); bytes memory bytesArray = abi.encodePacked(singleByte); uint256 returnedBitMap = bitmapUtilsWrapper.orderedBytesArrayToBitmap(bytesArray); uint256 bitMask = uint256(1 << fuzzedNumber); - assertEq(returnedBitMap, bitMask, "BitmapUtilsUnitTests.testSingleByteEncoding: non-equivalence"); + assertEq( + returnedBitMap, bitMask, "BitmapUtilsUnitTests.testSingleByteEncoding: non-equivalence" + ); } // ensure that the bitmap encoding of a two uint8's (i.e. a two byte array) matches the expected output @@ -142,20 +154,28 @@ contract BitmapUtilsUnitTests_bytesArrayToBitmap is BitmapUtilsUnitTests { bytes1 secondSingleByte = bytes1(secondFuzzedNumber); bytes memory bytesArray = abi.encodePacked(firstSingleByte, secondSingleByte); if (firstFuzzedNumber == secondFuzzedNumber) { - cheats.expectRevert(bytes("BitmapUtils.orderedBytesArrayToBitmap: repeat entry in bytesArray")); + cheats.expectRevert( + bytes("BitmapUtils.orderedBytesArrayToBitmap: repeat entry in bytesArray") + ); bitmapUtilsWrapper.orderedBytesArrayToBitmap(bytesArray); } else { uint256 returnedBitMap = bitmapUtilsWrapper.orderedBytesArrayToBitmap(bytesArray); uint256 firstBitMask = uint256(1 << firstFuzzedNumber); uint256 secondBitMask = uint256(1 << secondFuzzedNumber); uint256 combinedBitMask = firstBitMask | secondBitMask; - assertEq(returnedBitMap, combinedBitMask, "BitmapUtilsUnitTests.testTwoByteEncoding: non-equivalence"); + assertEq( + returnedBitMap, + combinedBitMask, + "BitmapUtilsUnitTests.testTwoByteEncoding: non-equivalence" + ); } } // ensure that converting bytes array => bitmap => bytes array returns the original bytes array (i.e. is lossless and artifactless) // note that this only works on ordered arrays, because unordered arrays will be returned ordered - function testFuzz_BytesArrayToBitmapToBytesArray(bytes memory originalBytesArray) public { + function testFuzz_BytesArrayToBitmapToBytesArray( + bytes memory originalBytesArray + ) public { // filter down to only ordered inputs cheats.assume(bitmapUtilsWrapper.isArrayStrictlyAscendingOrdered(originalBytesArray)); uint256 bitmap = bitmapUtilsWrapper.orderedBytesArrayToBitmap(originalBytesArray); @@ -169,7 +189,9 @@ contract BitmapUtilsUnitTests_bytesArrayToBitmap is BitmapUtilsUnitTests { // ensure that converting bytes array => bitmap => bytes array returns the original bytes array (i.e. is lossless and artifactless) // note that this only works on ordered arrays - function testFuzz_BytesArrayToBitmapToBytesArray_OrderedVersion(bytes memory originalBytesArray) public { + function testFuzz_BytesArrayToBitmapToBytesArray_OrderedVersion( + bytes memory originalBytesArray + ) public { // filter down to only ordered inputs cheats.assume(bitmapUtilsWrapper.isArrayStrictlyAscendingOrdered(originalBytesArray)); uint256 bitmap = bitmapUtilsWrapper.orderedBytesArrayToBitmap(originalBytesArray); @@ -183,15 +205,19 @@ contract BitmapUtilsUnitTests_bytesArrayToBitmap is BitmapUtilsUnitTests { /// @notice Test that for non-strictly ascending bytes array ordering always reverts /// when calling orderedBytesArrayToBitmap - function testFuzz_OrderedBytesArrayToBitmap_Revert_WhenNotOrdered(bytes memory originalBytesArray) public { + function testFuzz_OrderedBytesArrayToBitmap_Revert_WhenNotOrdered( + bytes memory originalBytesArray + ) public { cheats.assume(!bitmapUtilsWrapper.isArrayStrictlyAscendingOrdered(originalBytesArray)); - cheats.expectRevert("BitmapUtils.orderedBytesArrayToBitmap: orderedBytesArray is not ordered"); + cheats.expectRevert(BitmapUtils.BytesArrayNotOrdered.selector); bitmapUtilsWrapper.orderedBytesArrayToBitmap(originalBytesArray); } // ensure that converting bytes array => bitmap => bytes array returns the original bytes array (i.e. is lossless and artifactless) // note that this only works on ordered arrays - function testFuzz_BytesArrayToBitmapToBytesArray_OrderedVersion_Yul(bytes memory originalBytesArray) public { + function testFuzz_BytesArrayToBitmapToBytesArray_OrderedVersion_Yul( + bytes memory originalBytesArray + ) public { // filter down to only ordered inputs cheats.assume(bitmapUtilsWrapper.isArrayStrictlyAscendingOrdered(originalBytesArray)); uint256 bitmap = bitmapUtilsWrapper.orderedBytesArrayToBitmap(originalBytesArray); @@ -206,7 +232,14 @@ contract BitmapUtilsUnitTests_bytesArrayToBitmap is BitmapUtilsUnitTests { // testing one function for a specific input. used for comparing gas costs function test_BytesArrayToBitmap_OrderedVersion_SpecificInput() public { bytes memory originalBytesArray = abi.encodePacked( - bytes1(uint8(5)), bytes1(uint8(6)), bytes1(uint8(7)), bytes1(uint8(8)), bytes1(uint8(9)), bytes1(uint8(10)), bytes1(uint8(11)), bytes1(uint8(12)) + bytes1(uint8(5)), + bytes1(uint8(6)), + bytes1(uint8(7)), + bytes1(uint8(8)), + bytes1(uint8(9)), + bytes1(uint8(10)), + bytes1(uint8(11)), + bytes1(uint8(12)) ); uint256 gasLeftBefore = gasleft(); uint256 bitmap = bitmapUtilsWrapper.orderedBytesArrayToBitmap(originalBytesArray); @@ -219,7 +252,14 @@ contract BitmapUtilsUnitTests_bytesArrayToBitmap is BitmapUtilsUnitTests { // testing one function for a specific input. used for comparing gas costs function test_BytesArrayToBitmap_SpecificInput() public { bytes memory originalBytesArray = abi.encodePacked( - bytes1(uint8(5)), bytes1(uint8(6)), bytes1(uint8(7)), bytes1(uint8(8)), bytes1(uint8(9)), bytes1(uint8(10)), bytes1(uint8(11)), bytes1(uint8(12)) + bytes1(uint8(5)), + bytes1(uint8(6)), + bytes1(uint8(7)), + bytes1(uint8(8)), + bytes1(uint8(9)), + bytes1(uint8(10)), + bytes1(uint8(11)), + bytes1(uint8(12)) ); uint256 gasLeftBefore = gasleft(); uint256 bitmap = bitmapUtilsWrapper.orderedBytesArrayToBitmap(originalBytesArray); @@ -229,7 +269,9 @@ contract BitmapUtilsUnitTests_bytesArrayToBitmap is BitmapUtilsUnitTests { emit log_named_uint("gasSpent", gasSpent); } - function testFuzz_bitmapToBytesArrayToBitmap(uint256 originalBitmap) public { + function testFuzz_bitmapToBytesArrayToBitmap( + uint256 originalBitmap + ) public { uint256 gasLeftBefore = gasleft(); bytes memory bytesArray = bitmapUtilsWrapper.bitmapToBytesArray(originalBitmap); uint256 gasLeftAfter = gasleft(); @@ -265,16 +307,37 @@ contract BitmapUtilsUnitTests_isArrayStrictlyAscendingOrdered is BitmapUtilsUnit function test_DifferentBytesArrayOrdering() public { // Descending order and duplicate element bytes arrays should return false bytes memory descendingBytesArray = abi.encodePacked( - bytes1(uint8(12)), bytes1(uint8(11)), bytes1(uint8(10)), bytes1(uint8(9)), bytes1(uint8(8)), bytes1(uint8(7)), bytes1(uint8(6)), bytes1(uint8(5)) + bytes1(uint8(12)), + bytes1(uint8(11)), + bytes1(uint8(10)), + bytes1(uint8(9)), + bytes1(uint8(8)), + bytes1(uint8(7)), + bytes1(uint8(6)), + bytes1(uint8(5)) ); assertFalse(bitmapUtilsWrapper.isArrayStrictlyAscendingOrdered(descendingBytesArray)); bytes memory duplicateBytesArray = abi.encodePacked( - bytes1(uint8(5)), bytes1(uint8(5)), bytes1(uint8(5)), bytes1(uint8(5)), bytes1(uint8(5)), bytes1(uint8(5)), bytes1(uint8(5)), bytes1(uint8(5)) + bytes1(uint8(5)), + bytes1(uint8(5)), + bytes1(uint8(5)), + bytes1(uint8(5)), + bytes1(uint8(5)), + bytes1(uint8(5)), + bytes1(uint8(5)), + bytes1(uint8(5)) ); assertFalse(bitmapUtilsWrapper.isArrayStrictlyAscendingOrdered(duplicateBytesArray)); // Strictly ascending returns true bytes memory ascendingBytesArray = abi.encodePacked( - bytes1(uint8(5)), bytes1(uint8(6)), bytes1(uint8(7)), bytes1(uint8(8)), bytes1(uint8(9)), bytes1(uint8(10)), bytes1(uint8(11)), bytes1(uint8(12)) + bytes1(uint8(5)), + bytes1(uint8(6)), + bytes1(uint8(7)), + bytes1(uint8(8)), + bytes1(uint8(9)), + bytes1(uint8(10)), + bytes1(uint8(11)), + bytes1(uint8(12)) ); assertTrue(bitmapUtilsWrapper.isArrayStrictlyAscendingOrdered(ascendingBytesArray)); // Empty bytes array and single element bytes array returns true diff --git a/test/unit/ECDSAServiceManager.t.sol b/test/unit/ECDSAServiceManager.t.sol index 3b533d47..aac2bd38 100644 --- a/test/unit/ECDSAServiceManager.t.sol +++ b/test/unit/ECDSAServiceManager.t.sol @@ -1,186 +1,192 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.12; - -import {Test, console} from "forge-std/Test.sol"; - -import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol"; -import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; -import {IRewardsCoordinator} from "eigenlayer-contracts/src/contracts/interfaces/IRewardsCoordinator.sol"; -import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; - -import {ECDSAServiceManagerMock} from "../mocks/ECDSAServiceManagerMock.sol"; -import {ECDSAStakeRegistryMock} from "../mocks/ECDSAStakeRegistryMock.sol"; -import {Quorum, StrategyParams} from "../../src/interfaces/IECDSAStakeRegistryEventsAndErrors.sol"; - -contract MockDelegationManager { - function operatorShares(address, address) external pure returns (uint256) { - return 1000; // Return a dummy value for simplicity - } - - function getOperatorShares( - address, - IStrategy[] memory strategies - ) external pure returns (uint256[] memory) { - uint256[] memory response = new uint256[](strategies.length); - for (uint256 i; i < strategies.length; i++) { - response[i] = 1000; - } - return response; // Return a dummy value for simplicity - } -} - -contract MockAVSDirectory { - function registerOperatorToAVS( - address, - ISignatureUtils.SignatureWithSaltAndExpiry memory - ) external pure {} - - function deregisterOperatorFromAVS(address) external pure {} - - function updateAVSMetadataURI(string memory) external pure {} -} - -contract MockRewardsCoordinator { - function createAVSRewardsSubmission( - IRewardsCoordinator.RewardsSubmission[] calldata - ) external pure {} -} - -contract ECDSAServiceManagerSetup is Test { - MockDelegationManager public mockDelegationManager; - MockAVSDirectory public mockAVSDirectory; - ECDSAStakeRegistryMock public mockStakeRegistry; - MockRewardsCoordinator public mockRewardsCoordinator; - ECDSAServiceManagerMock public serviceManager; - address internal operator1; - address internal operator2; - uint256 internal operator1Pk; - uint256 internal operator2Pk; - - function setUp() public { - mockDelegationManager = new MockDelegationManager(); - mockAVSDirectory = new MockAVSDirectory(); - mockStakeRegistry = new ECDSAStakeRegistryMock( - IDelegationManager(address(mockDelegationManager)) - ); - mockRewardsCoordinator = new MockRewardsCoordinator(); - - serviceManager = new ECDSAServiceManagerMock( - address(mockAVSDirectory), - address(mockStakeRegistry), - address(mockRewardsCoordinator), - address(mockDelegationManager) - ); - - operator1Pk = 1; - operator2Pk = 2; - operator1 = vm.addr(operator1Pk); - operator2 = vm.addr(operator2Pk); - - // Create a quorum - Quorum memory quorum = Quorum({strategies: new StrategyParams[](2)}); - quorum.strategies[0] = StrategyParams({ - strategy: IStrategy(address(420)), - multiplier: 5000 - }); - quorum.strategies[1] = StrategyParams({ - strategy: IStrategy(address(421)), - multiplier: 5000 - }); - address[] memory operators = new address[](0); - - vm.prank(mockStakeRegistry.owner()); - mockStakeRegistry.initialize( - address(serviceManager), - 10_000, // Assuming a threshold weight of 10000 basis points - quorum - ); - ISignatureUtils.SignatureWithSaltAndExpiry memory dummySignature; - - vm.prank(operator1); - mockStakeRegistry.registerOperatorWithSignature( - dummySignature, - operator1 - ); - - vm.prank(operator2); - mockStakeRegistry.registerOperatorWithSignature( - dummySignature, - operator2 - ); - } - - function testRegisterOperatorToAVS() public { - address operator = operator1; - ISignatureUtils.SignatureWithSaltAndExpiry memory signature; - - vm.prank(address(mockStakeRegistry)); - serviceManager.registerOperatorToAVS(operator, signature); - } - - function testDeregisterOperatorFromAVS() public { - address operator = operator1; - - vm.prank(address(mockStakeRegistry)); - serviceManager.deregisterOperatorFromAVS(operator); - } - - function testGetRestakeableStrategies() public { - address[] memory strategies = serviceManager.getRestakeableStrategies(); - } - - function testGetOperatorRestakedStrategies() public { - address operator = operator1; - address[] memory strategies = serviceManager - .getOperatorRestakedStrategies(operator); - } - - function test_Regression_GetOperatorRestakedStrategies_NoShares() public { - address operator = operator1; - IStrategy[] memory strategies = new IStrategy[](2); - strategies[0] = IStrategy(address(420)); - strategies[1] = IStrategy(address(421)); - - uint256[] memory shares = new uint256[](2); - shares[0] = 0; - shares[1] = 1; - - vm.mockCall( - address(mockDelegationManager), - abi.encodeCall( - IDelegationManager.getOperatorShares, - (operator, strategies) - ), - abi.encode(shares) - ); - - address[] memory restakedStrategies = serviceManager - .getOperatorRestakedStrategies(operator); - assertEq( - restakedStrategies.length, - 1, - "Expected no restaked strategies" - ); - } - - function testUpdateAVSMetadataURI() public { - string memory newURI = "https://new-metadata-uri.com"; - - vm.prank(mockStakeRegistry.owner()); - serviceManager.updateAVSMetadataURI(newURI); - } - - function testCreateAVSRewardsSubmission() public { - IRewardsCoordinator.RewardsSubmission[] memory submissions; - - vm.prank(serviceManager.rewardsInitiator()); - serviceManager.createAVSRewardsSubmission(submissions); - } - - function testSetRewardsInitiator() public { - address newInitiator = address(0x123); - - vm.prank(mockStakeRegistry.owner()); - serviceManager.setRewardsInitiator(newInitiator); - } -} +// // SPDX-License-Identifier: MIT +// pragma solidity ^0.8.27; + +// import {Test, console} from "forge-std/Test.sol"; + +// import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol"; +// import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; +// import {IRewardsCoordinator} from "eigenlayer-contracts/src/contracts/interfaces/IRewardsCoordinator.sol"; +// import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; + +// import {ECDSAServiceManagerMock} from "../mocks/ECDSAServiceManagerMock.sol"; +// import {ECDSAStakeRegistryMock} from "../mocks/ECDSAStakeRegistryMock.sol"; +// import {IECDSAStakeRegistryTypes.Quorum, StrategyParams} from "../../src/interfaces/IECDSAStakeRegistry.sol"; + +// contract MockDelegationManager { +// function operatorShares(address, address) external pure returns (uint256) { +// return 1000; // Return a dummy value for simplicity +// } + +// function getOperatorShares( +// address, +// IStrategy[] memory strategies +// ) external pure returns (uint256[] memory) { +// uint256[] memory response = new uint256[](strategies.length); +// for (uint256 i; i < strategies.length; i++) { +// response[i] = 1000; +// } +// return response; // Return a dummy value for simplicity +// } +// } + +// contract MockAVSDirectory { +// function registerOperatorToAVS( +// address, +// ISignatureUtils.SignatureWithSaltAndExpiry memory +// ) external pure {} + +// function deregisterOperatorFromAVS(address) external pure {} + +// function updateAVSMetadataURI(string memory) external pure {} +// } + +// contract MockAllocationManager {} + +// contract MockRewardsCoordinator { +// function createAVSRewardsSubmission( +// address avs, +// IRewardsCoordinator.RewardsSubmission[] calldata +// ) external pure {} +// } + +// contract ECDSAServiceManagerSetup is Test { +// MockDelegationManager public mockDelegationManager; +// MockAVSDirectory public mockAVSDirectory; +// MockAllocationManager public mockAllocationManager; +// ECDSAStakeRegistryMock public mockStakeRegistry; +// MockRewardsCoordinator public mockRewardsCoordinator; +// ECDSAServiceManagerMock public serviceManager; +// address internal operator1; +// address internal operator2; +// uint256 internal operator1Pk; +// uint256 internal operator2Pk; + +// function setUp() public { +// mockDelegationManager = new MockDelegationManager(); +// mockAVSDirectory = new MockAVSDirectory(); +// mockAllocationManager = new MockAllocationManager(); +// mockStakeRegistry = new ECDSAStakeRegistryMock( +// IDelegationManager(address(mockDelegationManager)) +// ); +// mockRewardsCoordinator = new MockRewardsCoordinator(); + +// serviceManager = new ECDSAServiceManagerMock( +// address(mockAVSDirectory), +// address(mockStakeRegistry), +// address(mockRewardsCoordinator), +// address(mockDelegationManager), +// address(mockAllocationManager) +// ); + +// operator1Pk = 1; +// operator2Pk = 2; +// operator1 = vm.addr(operator1Pk); +// operator2 = vm.addr(operator2Pk); + +// // Create a quorum +// IECDSAStakeRegistryTypes.Quorum memory quorum = IECDSAStakeRegistryTypes.Quorum({strategies: new StrategyParams[](2)}); +// quorum.strategies[0] = StrategyParams({ +// strategy: IStrategy(address(420)), +// multiplier: 5000 +// }); +// quorum.strategies[1] = StrategyParams({ +// strategy: IStrategy(address(421)), +// multiplier: 5000 +// }); +// address[] memory operators = new address[](0); + +// vm.prank(mockStakeRegistry.owner()); +// mockStakeRegistry.initialize( +// address(serviceManager), +// 10_000, // Assuming a threshold weight of 10000 basis points +// quorum +// ); +// ISignatureUtils.SignatureWithSaltAndExpiry memory dummySignature; + +// vm.prank(operator1); +// mockStakeRegistry.registerOperatorWithSignature( +// dummySignature, +// operator1 +// ); + +// vm.prank(operator2); +// mockStakeRegistry.registerOperatorWithSignature( +// dummySignature, +// operator2 +// ); +// } + +// function testRegisterOperatorToAVS() public { +// address operator = operator1; +// ISignatureUtils.SignatureWithSaltAndExpiry memory signature; + +// vm.prank(address(mockStakeRegistry)); +// serviceManager.registerOperatorToAVS(operator, signature); +// } + +// function testDeregisterOperatorFromAVS() public { +// address operator = operator1; + +// vm.prank(address(mockStakeRegistry)); +// serviceManager.deregisterOperatorFromAVS(operator); +// } + +// function testGetRestakeableStrategies() public { +// address[] memory strategies = serviceManager.getRestakeableStrategies(); +// } + +// function testGetOperatorRestakedStrategies() public { +// address operator = operator1; +// address[] memory strategies = serviceManager +// .getOperatorRestakedStrategies(operator); +// } + +// function test_Regression_GetOperatorRestakedStrategies_NoShares() public { +// address operator = operator1; +// IStrategy[] memory strategies = new IStrategy[](2); +// strategies[0] = IStrategy(address(420)); +// strategies[1] = IStrategy(address(421)); + +// uint96[] memory shares = new uint96[](2); +// shares[0] = 0; +// shares[1] = 1; + +// vm.mockCall( +// address(mockDelegationManager), +// abi.encodeCall( +// IDelegationManager.getOperatorShares, +// (operator, strategies) +// ), +// abi.encode(shares) +// ); + +// address[] memory restakedStrategies = serviceManager +// .getOperatorRestakedStrategies(operator); +// assertEq( +// restakedStrategies.length, +// 1, +// "Expected no restaked strategies" +// ); +// } + +// function testUpdateAVSMetadataURI() public { +// string memory newURI = "https://new-metadata-uri.com"; + +// vm.prank(mockStakeRegistry.owner()); +// serviceManager.updateAVSMetadataURI(newURI); +// } + +// function testCreateAVSRewardsSubmission() public { +// IRewardsCoordinator.RewardsSubmission[] memory submissions; + +// vm.prank(serviceManager.rewardsInitiator()); +// serviceManager.createAVSRewardsSubmission(submissions); +// } + +// function testSetRewardsInitiator() public { +// address newInitiator = address(0x123); + +// vm.prank(mockStakeRegistry.owner()); +// serviceManager.setRewardsInitiator(newInitiator); +// } +// } diff --git a/test/unit/ECDSAStakeRegistryEqualWeightUnit.t.sol b/test/unit/ECDSAStakeRegistryEqualWeightUnit.t.sol index bc6337c5..befc76c4 100644 --- a/test/unit/ECDSAStakeRegistryEqualWeightUnit.t.sol +++ b/test/unit/ECDSAStakeRegistryEqualWeightUnit.t.sol @@ -1,90 +1,61 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol"; -import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; +import {IDelegationManager} from + "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; -import {ECDSAStakeRegistryEventsAndErrors, Quorum, StrategyParams} from "../../src/interfaces/IECDSAStakeRegistryEventsAndErrors.sol"; +import { + IECDSAStakeRegistry, + IECDSAStakeRegistryTypes +} from "../../src/interfaces/IECDSAStakeRegistry.sol"; import {ECDSAStakeRegistrySetup} from "./ECDSAStakeRegistryUnit.t.sol"; -import {ECDSAStakeRegistryEqualWeight} from "../../src/unaudited/examples/ECDSAStakeRegistryEqualWeight.sol"; +import {ECDSAStakeRegistryEqualWeight} from + "../../src/unaudited/examples/ECDSAStakeRegistryEqualWeight.sol"; contract EqualWeightECDSARegistry is ECDSAStakeRegistrySetup { ECDSAStakeRegistryEqualWeight internal fixedWeightRegistry; function setUp() public virtual override { super.setUp(); - fixedWeightRegistry = new ECDSAStakeRegistryEqualWeight( - IDelegationManager(address(mockDelegationManager)) - ); + fixedWeightRegistry = + new ECDSAStakeRegistryEqualWeight(IDelegationManager(address(mockDelegationManager))); IStrategy mockStrategy = IStrategy(address(0x1234)); - Quorum memory quorum = Quorum({strategies: new StrategyParams[](1)}); - quorum.strategies[0] = StrategyParams({ - strategy: mockStrategy, - multiplier: 10000 - }); - fixedWeightRegistry.initialize( - address(mockServiceManager), - 100, - quorum - ); + IECDSAStakeRegistryTypes.Quorum memory quorum = + IECDSAStakeRegistryTypes.Quorum({strategies: new StrategyParams[](1)}); + quorum.strategies[0] = StrategyParams({strategy: mockStrategy, multiplier: 10000}); + fixedWeightRegistry.initialize(address(mockServiceManager), 100, quorum); fixedWeightRegistry.permitOperator(operator1); fixedWeightRegistry.permitOperator(operator2); ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature; vm.prank(operator1); - fixedWeightRegistry.registerOperatorWithSignature( - operatorSignature, - operator1 - ); + fixedWeightRegistry.registerOperatorWithSignature(operatorSignature, operator1); vm.prank(operator2); - fixedWeightRegistry.registerOperatorWithSignature( - operatorSignature, - operator2 - ); + fixedWeightRegistry.registerOperatorWithSignature(operatorSignature, operator2); } function test_FixedStakeUpdates() public { - assertEq( - fixedWeightRegistry.getLastCheckpointOperatorWeight(operator1), - 1 - ); - assertEq( - fixedWeightRegistry.getLastCheckpointOperatorWeight(operator2), - 1 - ); + assertEq(fixedWeightRegistry.getLastCheckpointOperatorWeight(operator1), 1); + assertEq(fixedWeightRegistry.getLastCheckpointOperatorWeight(operator2), 1); assertEq(fixedWeightRegistry.getLastCheckpointTotalWeight(), 2); vm.roll(block.number + 1); vm.prank(operator1); fixedWeightRegistry.deregisterOperator(); - assertEq( - fixedWeightRegistry.getLastCheckpointOperatorWeight(operator1), - 0 - ); - assertEq( - fixedWeightRegistry.getLastCheckpointOperatorWeight(operator2), - 1 - ); + assertEq(fixedWeightRegistry.getLastCheckpointOperatorWeight(operator1), 0); + assertEq(fixedWeightRegistry.getLastCheckpointOperatorWeight(operator2), 1); assertEq(fixedWeightRegistry.getLastCheckpointTotalWeight(), 1); vm.roll(block.number + 1); ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature; vm.prank(operator1); - fixedWeightRegistry.registerOperatorWithSignature( - operatorSignature, - operator1 - ); + fixedWeightRegistry.registerOperatorWithSignature(operatorSignature, operator1); - assertEq( - fixedWeightRegistry.getLastCheckpointOperatorWeight(operator1), - 1 - ); - assertEq( - fixedWeightRegistry.getLastCheckpointOperatorWeight(operator2), - 1 - ); + assertEq(fixedWeightRegistry.getLastCheckpointOperatorWeight(operator1), 1); + assertEq(fixedWeightRegistry.getLastCheckpointOperatorWeight(operator2), 1); assertEq(fixedWeightRegistry.getLastCheckpointTotalWeight(), 2); vm.roll(block.number + 1); @@ -93,14 +64,8 @@ contract EqualWeightECDSARegistry is ECDSAStakeRegistrySetup { operators[1] = operator2; fixedWeightRegistry.updateOperators(operators); - assertEq( - fixedWeightRegistry.getLastCheckpointOperatorWeight(operator1), - 1 - ); - assertEq( - fixedWeightRegistry.getLastCheckpointOperatorWeight(operator2), - 1 - ); + assertEq(fixedWeightRegistry.getLastCheckpointOperatorWeight(operator1), 1); + assertEq(fixedWeightRegistry.getLastCheckpointOperatorWeight(operator2), 1); assertEq(fixedWeightRegistry.getLastCheckpointTotalWeight(), 2); } } diff --git a/test/unit/ECDSAStakeRegistryPermissionedUnit.t.sol b/test/unit/ECDSAStakeRegistryPermissionedUnit.t.sol index dffb9174..7269e1ce 100644 --- a/test/unit/ECDSAStakeRegistryPermissionedUnit.t.sol +++ b/test/unit/ECDSAStakeRegistryPermissionedUnit.t.sol @@ -1,47 +1,40 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol"; -import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; +import {IDelegationManager} from + "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; -import {ECDSAStakeRegistryEventsAndErrors, Quorum, StrategyParams} from "../../src/interfaces/IECDSAStakeRegistryEventsAndErrors.sol"; +import { + IECDSAStakeRegistry, + IECDSAStakeRegistryTypes, + IECDSAStakeRegistryErrors +} from "../../src/interfaces/IECDSAStakeRegistry.sol"; import {ECDSAStakeRegistrySetup} from "./ECDSAStakeRegistryUnit.t.sol"; -import {ECDSAStakeRegistryPermissioned} from "../../src/unaudited/examples/ECDSAStakeRegistryPermissioned.sol"; +import {ECDSAStakeRegistryPermissioned} from + "../../src/unaudited/examples/ECDSAStakeRegistryPermissioned.sol"; contract PermissionedECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { ECDSAStakeRegistryPermissioned internal permissionedRegistry; function setUp() public virtual override { super.setUp(); - permissionedRegistry = new ECDSAStakeRegistryPermissioned( - IDelegationManager(address(mockDelegationManager)) - ); + permissionedRegistry = + new ECDSAStakeRegistryPermissioned(IDelegationManager(address(mockDelegationManager))); IStrategy mockStrategy = IStrategy(address(0x1234)); - Quorum memory quorum = Quorum({strategies: new StrategyParams[](1)}); - quorum.strategies[0] = StrategyParams({ - strategy: mockStrategy, - multiplier: 10000 - }); - permissionedRegistry.initialize( - address(mockServiceManager), - 100, - quorum - ); + IECDSAStakeRegistryTypes.Quorum memory quorum = + IECDSAStakeRegistryTypes.Quorum({strategies: new StrategyParams[](1)}); + quorum.strategies[0] = StrategyParams({strategy: mockStrategy, multiplier: 10000}); + permissionedRegistry.initialize(address(mockServiceManager), 100, quorum); permissionedRegistry.permitOperator(operator1); permissionedRegistry.permitOperator(operator2); ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature; vm.prank(operator1); - permissionedRegistry.registerOperatorWithSignature( - operatorSignature, - operator1 - ); + permissionedRegistry.registerOperatorWithSignature(operatorSignature, operator1); vm.prank(operator2); - permissionedRegistry.registerOperatorWithSignature( - operatorSignature, - operator1 - ); + permissionedRegistry.registerOperatorWithSignature(operatorSignature, operator1); } function test_RevertsWhen_NotOwner_PermitOperator() public { @@ -83,7 +76,9 @@ contract PermissionedECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { function test_RevertsWhen_NotOperator_EjectOperator() public { address notOperator = address(0xBEEF); - vm.expectRevert(abi.encodeWithSelector(OperatorNotRegistered.selector)); + vm.expectRevert( + abi.encodeWithSelector(IECDSAStakeRegistryErrors.OperatorNotRegistered.selector) + ); permissionedRegistry.ejectOperator(notOperator); } @@ -96,15 +91,10 @@ contract PermissionedECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature; vm.expectRevert( - abi.encodeWithSelector( - ECDSAStakeRegistryPermissioned.OperatorNotAllowlisted.selector - ) + abi.encodeWithSelector(ECDSAStakeRegistryPermissioned.OperatorNotAllowlisted.selector) ); vm.prank(operator3); - permissionedRegistry.registerOperatorWithSignature( - operatorSignature, - operator3 - ); + permissionedRegistry.registerOperatorWithSignature(operatorSignature, operator3); } function test_WhenAllowlisted_RegisterOperatorWithSig() public { @@ -112,10 +102,7 @@ contract PermissionedECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { permissionedRegistry.permitOperator(operator3); ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature; vm.prank(operator3); - permissionedRegistry.registerOperatorWithSignature( - operatorSignature, - operator3 - ); + permissionedRegistry.registerOperatorWithSignature(operatorSignature, operator3); } function test_DeregisterOperator() public { @@ -123,10 +110,7 @@ contract PermissionedECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { permissionedRegistry.permitOperator(operator3); ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature; vm.prank(operator3); - permissionedRegistry.registerOperatorWithSignature( - operatorSignature, - operator3 - ); + permissionedRegistry.registerOperatorWithSignature(operatorSignature, operator3); vm.prank(operator3); permissionedRegistry.deregisterOperator(); diff --git a/test/unit/ECDSAStakeRegistryUnit.t.sol b/test/unit/ECDSAStakeRegistryUnit.t.sol index d374144d..540f8dcc 100644 --- a/test/unit/ECDSAStakeRegistryUnit.t.sol +++ b/test/unit/ECDSAStakeRegistryUnit.t.sol @@ -1,18 +1,26 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {Test, console} from "forge-std/Test.sol"; import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol"; -import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; +import {IDelegationManager} from + "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; import {ECDSAStakeRegistry} from "../../src/unaudited/ECDSAStakeRegistry.sol"; -import {ECDSAStakeRegistryEventsAndErrors, Quorum, StrategyParams} from "../../src/interfaces/IECDSAStakeRegistryEventsAndErrors.sol"; +import { + IECDSAStakeRegistry, + IECDSAStakeRegistryErrors, + IECDSAStakeRegistryTypes, + IECDSAStakeRegistryEvents +} from "../../src/interfaces/IECDSAStakeRegistry.sol"; contract MockServiceManager { // solhint-disable-next-line - function deregisterOperatorFromAVS(address) external {} + function deregisterOperatorFromAVS( + address + ) external {} function registerOperatorToAVS( address, @@ -37,7 +45,7 @@ contract MockDelegationManager { } } -contract ECDSAStakeRegistrySetup is Test, ECDSAStakeRegistryEventsAndErrors { +contract ECDSAStakeRegistrySetup is Test, IECDSAStakeRegistryEvents { MockDelegationManager public mockDelegationManager; MockServiceManager public mockServiceManager; ECDSAStakeRegistry public registry; @@ -57,14 +65,12 @@ contract ECDSAStakeRegistrySetup is Test, ECDSAStakeRegistryEventsAndErrors { mockDelegationManager = new MockDelegationManager(); mockServiceManager = new MockServiceManager(); IStrategy mockStrategy = IStrategy(address(0x1234)); - Quorum memory quorum = Quorum({strategies: new StrategyParams[](1)}); - quorum.strategies[0] = StrategyParams({ - strategy: mockStrategy, - multiplier: 10_000 + IECDSAStakeRegistryTypes.Quorum memory quorum = IECDSAStakeRegistryTypes.Quorum({ + strategies: new IECDSAStakeRegistryTypes.StrategyParams[](1) }); - registry = new ECDSAStakeRegistry( - IDelegationManager(address(mockDelegationManager)) - ); + quorum.strategies[0] = + IECDSAStakeRegistryTypes.StrategyParams({strategy: mockStrategy, multiplier: 10000}); + registry = new ECDSAStakeRegistry(IDelegationManager(address(mockDelegationManager))); registry.initialize(address(mockServiceManager), 100, quorum); ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature; vm.prank(operator1); @@ -79,12 +85,12 @@ contract ECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { function test_UpdateQuorumConfig() public { IStrategy mockStrategy = IStrategy(address(420)); - Quorum memory oldQuorum = registry.quorum(); - Quorum memory newQuorum = Quorum({strategies: new StrategyParams[](1)}); - newQuorum.strategies[0] = StrategyParams({ - strategy: mockStrategy, - multiplier: 10_000 + IECDSAStakeRegistryTypes.Quorum memory oldQuorum = registry.quorum(); + IECDSAStakeRegistryTypes.Quorum memory newQuorum = IECDSAStakeRegistryTypes.Quorum({ + strategies: new IECDSAStakeRegistryTypes.StrategyParams[](1) }); + newQuorum.strategies[0] = + IECDSAStakeRegistryTypes.StrategyParams({strategy: mockStrategy, multiplier: 10000}); address[] memory operators = new address[](2); operators[0] = operator1; operators[1] = operator2; @@ -96,10 +102,10 @@ contract ECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { } function test_RevertsWhen_InvalidQuorum_UpdateQuourmConfig() public { - Quorum memory invalidQuorum = Quorum({ - strategies: new StrategyParams[](1) + IECDSAStakeRegistryTypes.Quorum memory invalidQuorum = IECDSAStakeRegistryTypes.Quorum({ + strategies: new IECDSAStakeRegistryTypes.StrategyParams[](1) }); - invalidQuorum.strategies[0] = StrategyParams({ + invalidQuorum.strategies[0] = IECDSAStakeRegistryTypes.StrategyParams({ /// TODO: Make mock strategy strategy: IStrategy(address(420)), multiplier: 5000 // This should cause the update to revert as it's not the total required @@ -108,19 +114,17 @@ contract ECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { operators[0] = operator1; operators[1] = operator2; - vm.expectRevert( - ECDSAStakeRegistryEventsAndErrors.InvalidQuorum.selector - ); + vm.expectRevert(IECDSAStakeRegistryErrors.InvalidQuorum.selector); registry.updateQuorumConfig(invalidQuorum, operators); } function test_RevertsWhen_NotOwner_UpdateQuorumConfig() public { - Quorum memory validQuorum = Quorum({ - strategies: new StrategyParams[](1) + IECDSAStakeRegistryTypes.Quorum memory validQuorum = IECDSAStakeRegistryTypes.Quorum({ + strategies: new IECDSAStakeRegistryTypes.StrategyParams[](1) }); - validQuorum.strategies[0] = StrategyParams({ + validQuorum.strategies[0] = IECDSAStakeRegistryTypes.StrategyParams({ strategy: IStrategy(address(420)), - multiplier: 10_000 + multiplier: 10000 }); address[] memory operators = new address[](2); @@ -135,7 +139,7 @@ contract ECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { } function test_RevertsWhen_SameQuorum_UpdateQuorumConfig() public { - Quorum memory quorum = registry.quorum(); + IECDSAStakeRegistryTypes.Quorum memory quorum = registry.quorum(); address[] memory operators = new address[](2); operators[0] = operator1; operators[1] = operator2; @@ -145,60 +149,45 @@ contract ECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { } function test_RevertSWhen_Duplicate_UpdateQuorumConfig() public { - Quorum memory invalidQuorum = Quorum({ - strategies: new StrategyParams[](2) - }); - invalidQuorum.strategies[0] = StrategyParams({ - strategy: IStrategy(address(420)), - multiplier: 5000 - }); + IECDSAStakeRegistryTypes.Quorum memory invalidQuorum = + IECDSAStakeRegistryTypes.Quorum({strategies: new StrategyParams[](2)}); + invalidQuorum.strategies[0] = + StrategyParams({strategy: IStrategy(address(420)), multiplier: 5000}); address[] memory operators = new address[](2); operators[0] = operator1; operators[1] = operator2; - invalidQuorum.strategies[1] = StrategyParams({ - strategy: IStrategy(address(420)), - multiplier: 5000 - }); - vm.expectRevert(ECDSAStakeRegistryEventsAndErrors.NotSorted.selector); + invalidQuorum.strategies[1] = + StrategyParams({strategy: IStrategy(address(420)), multiplier: 5000}); + vm.expectRevert(IECDSAStakeRegistryErrors.NotSorted.selector); registry.updateQuorumConfig(invalidQuorum, operators); } function test_RevertSWhen_NotSorted_UpdateQuorumConfig() public { - Quorum memory invalidQuorum = Quorum({ - strategies: new StrategyParams[](2) - }); - invalidQuorum.strategies[0] = StrategyParams({ - strategy: IStrategy(address(420)), - multiplier: 5000 - }); + IECDSAStakeRegistryTypes.Quorum memory invalidQuorum = + IECDSAStakeRegistryTypes.Quorum({strategies: new StrategyParams[](2)}); + invalidQuorum.strategies[0] = + StrategyParams({strategy: IStrategy(address(420)), multiplier: 5000}); address[] memory operators = new address[](2); operators[0] = operator1; operators[1] = operator2; - invalidQuorum.strategies[1] = StrategyParams({ - strategy: IStrategy(address(419)), - multiplier: 5000 - }); - vm.expectRevert(ECDSAStakeRegistryEventsAndErrors.NotSorted.selector); + invalidQuorum.strategies[1] = + StrategyParams({strategy: IStrategy(address(419)), multiplier: 5000}); + vm.expectRevert(IECDSAStakeRegistryErrors.NotSorted.selector); registry.updateQuorumConfig(invalidQuorum, operators); } function test_RevertSWhen_OverMultiplierTotal_UpdateQuorumConfig() public { - Quorum memory invalidQuorum = Quorum({ - strategies: new StrategyParams[](1) - }); - invalidQuorum.strategies[0] = StrategyParams({ - strategy: IStrategy(address(420)), - multiplier: 10_001 - }); + IECDSAStakeRegistryTypes.Quorum memory invalidQuorum = + IECDSAStakeRegistryTypes.Quorum({strategies: new StrategyParams[](1)}); + invalidQuorum.strategies[0] = + StrategyParams({strategy: IStrategy(address(420)), multiplier: 10001}); address[] memory operators = new address[](2); operators[0] = operator1; operators[1] = operator2; - vm.expectRevert( - ECDSAStakeRegistryEventsAndErrors.InvalidQuorum.selector - ); + vm.expectRevert(IECDSAStakeRegistryErrors.InvalidQuorum.selector); registry.updateQuorumConfig(invalidQuorum, operators); } @@ -211,23 +200,17 @@ contract ECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { assertEq(registry.getLastCheckpointOperatorWeight(operator3), 1000); } - function test_RevertsWhen_AlreadyRegistered_RegisterOperatorWithSignature() - public - { + function test_RevertsWhen_AlreadyRegistered_RegisterOperatorWithSignature() public { assertEq(registry.getLastCheckpointOperatorWeight(operator1), 1000); assertEq(registry.getLastCheckpointTotalWeight(), 2000); ISignatureUtils.SignatureWithSaltAndExpiry memory signature; - vm.expectRevert( - ECDSAStakeRegistryEventsAndErrors.OperatorAlreadyRegistered.selector - ); + vm.expectRevert(IECDSAStakeRegistryErrors.OperatorAlreadyRegistered.selector); vm.prank(operator1); registry.registerOperatorWithSignature(signature, operator1); } - function test_RevertsWhen_SignatureIsInvalid_RegisterOperatorWithSignature() - public - { + function test_RevertsWhen_SignatureIsInvalid_RegisterOperatorWithSignature() public { bytes memory signatureData; vm.mockCall( address(mockServiceManager), @@ -258,9 +241,7 @@ contract ECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { function test_RevertsWhen_NotOperator_DeregisterOperator() public { address notOperator = address(0x2); vm.prank(notOperator); - vm.expectRevert( - ECDSAStakeRegistryEventsAndErrors.OperatorNotRegistered.selector - ); + vm.expectRevert(IECDSAStakeRegistryErrors.OperatorNotRegistered.selector); registry.deregisterOperator(); } @@ -284,9 +265,7 @@ contract ECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { operators[0] = operator1; registry.updateOperators(operators); - uint256 updatedWeight = registry.getLastCheckpointOperatorWeight( - operator1 - ); + uint256 updatedWeight = registry.getLastCheckpointOperatorWeight(operator1); assertEq(updatedWeight, 1000); } @@ -318,12 +297,8 @@ contract ECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { registry.updateOperators(operators); - uint256 updatedWeight1 = registry.getLastCheckpointOperatorWeight( - operator1 - ); - uint256 updatedWeight2 = registry.getLastCheckpointOperatorWeight( - operator2 - ); + uint256 updatedWeight1 = registry.getLastCheckpointOperatorWeight(operator1); + uint256 updatedWeight2 = registry.getLastCheckpointOperatorWeight(operator2); assertEq(updatedWeight1, 1000); assertEq(updatedWeight2, 1000); } @@ -335,9 +310,7 @@ contract ECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { registry.updateOperators(operators); - uint256 updatedWeight = registry.getLastCheckpointOperatorWeight( - operator1 - ); + uint256 updatedWeight = registry.getLastCheckpointOperatorWeight(operator1); assertEq(updatedWeight, 1000); } @@ -345,15 +318,10 @@ contract ECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { IStrategy mockStrategy = IStrategy(address(420)); IStrategy mockStrategy2 = IStrategy(address(421)); - Quorum memory quorum = Quorum({strategies: new StrategyParams[](2)}); - quorum.strategies[0] = StrategyParams({ - strategy: mockStrategy, - multiplier: 5000 - }); - quorum.strategies[1] = StrategyParams({ - strategy: mockStrategy2, - multiplier: 5000 - }); + IECDSAStakeRegistryTypes.Quorum memory quorum = + IECDSAStakeRegistryTypes.Quorum({strategies: new StrategyParams[](2)}); + quorum.strategies[0] = StrategyParams({strategy: mockStrategy, multiplier: 5000}); + quorum.strategies[1] = StrategyParams({strategy: mockStrategy2, multiplier: 5000}); address[] memory operators = new address[](2); operators[0] = operator1; @@ -370,21 +338,15 @@ contract ECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { vm.mockCall( address(mockDelegationManager), abi.encodeWithSelector( - MockDelegationManager.getOperatorShares.selector, - operator1, - strategies + MockDelegationManager.getOperatorShares.selector, operator1, strategies ), abi.encode(shares) ); registry.updateOperators(operators); - uint256 updatedWeight1 = registry.getLastCheckpointOperatorWeight( - operator1 - ); - uint256 updatedWeight2 = registry.getLastCheckpointOperatorWeight( - operator2 - ); + uint256 updatedWeight1 = registry.getLastCheckpointOperatorWeight(operator1); + uint256 updatedWeight2 = registry.getLastCheckpointOperatorWeight(operator2); assertEq(updatedWeight1, 525); assertEq(updatedWeight2, 1000); vm.roll(block.number + 1); @@ -442,13 +404,13 @@ contract ECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { } function testUpdateThresholdStake_UpdateThresholdStake() public { - uint256 thresholdWeight = 10_000_000_000; + uint256 thresholdWeight = 10000000000; vm.prank(registry.owner()); registry.updateStakeThreshold(thresholdWeight); } function test_RevertsWhen_NotOwner_UpdateThresholdStake() public { - uint256 thresholdWeight = 10_000_000_000; + uint256 thresholdWeight = 10000000000; address notOwner = address(0x123); vm.prank(notOwner); vm.expectRevert("Ownable: caller is not the owner"); @@ -465,10 +427,7 @@ contract ECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { (v, r, s) = vm.sign(operator2Pk, msgHash); signatures[1] = abi.encodePacked(r, s, v); - registry.isValidSignature( - msgHash, - abi.encode(signers, signatures, block.number - 1) - ); + registry.isValidSignature(msgHash, abi.encode(signers, signatures, block.number - 1)); } function test_RevertsWhen_LengthMismatch_CheckSignatures() public { @@ -479,13 +438,8 @@ contract ECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { (uint8 v, bytes32 r, bytes32 s) = vm.sign(operator1Pk, msgHash); signatures[0] = abi.encode(v, r, s); - vm.expectRevert( - ECDSAStakeRegistryEventsAndErrors.LengthMismatch.selector - ); - registry.isValidSignature( - msgHash, - abi.encode(signers, signatures, block.number - 1) - ); + vm.expectRevert(IECDSAStakeRegistryErrors.LengthMismatch.selector); + registry.isValidSignature(msgHash, abi.encode(signers, signatures, block.number - 1)); } function test_RevertsWhen_InvalidLength_CheckSignatures() public { @@ -493,13 +447,8 @@ contract ECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { address[] memory signers = new address[](0); bytes[] memory signatures = new bytes[](0); - vm.expectRevert( - ECDSAStakeRegistryEventsAndErrors.InvalidLength.selector - ); - registry.isValidSignature( - dataHash, - abi.encode(signers, signatures, block.number - 1) - ); + vm.expectRevert(IECDSAStakeRegistryErrors.InvalidLength.selector); + registry.isValidSignature(dataHash, abi.encode(signers, signatures, block.number - 1)); } function test_RevertsWhen_NotSorted_CheckSignatures() public { @@ -513,11 +462,8 @@ contract ECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { (v, r, s) = vm.sign(operator2Pk, msgHash); signatures[0] = abi.encodePacked(r, s, v); - vm.expectRevert(ECDSAStakeRegistryEventsAndErrors.NotSorted.selector); - registry.isValidSignature( - msgHash, - abi.encode(signers, signatures, block.number - 1) - ); + vm.expectRevert(IECDSAStakeRegistryErrors.NotSorted.selector); + registry.isValidSignature(msgHash, abi.encode(signers, signatures, block.number - 1)); } function test_RevertsWhen_Duplicates_CheckSignatures() public { @@ -534,11 +480,8 @@ contract ECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { signatures[0] = abi.encodePacked(r, s, v); signatures[1] = abi.encodePacked(r, s, v); - vm.expectRevert(ECDSAStakeRegistryEventsAndErrors.NotSorted.selector); - registry.isValidSignature( - msgHash, - abi.encode(signers, signatures, block.number - 1) - ); + vm.expectRevert(IECDSAStakeRegistryErrors.NotSorted.selector); + registry.isValidSignature(msgHash, abi.encode(signers, signatures, block.number - 1)); } function test_RevetsWhen_InvalidSignature_CheckSignatures() public { @@ -548,13 +491,8 @@ contract ECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { bytes[] memory signatures = new bytes[](1); signatures[0] = "invalid-signature"; - vm.expectRevert( - ECDSAStakeRegistryEventsAndErrors.InvalidSignature.selector - ); - registry.isValidSignature( - dataHash, - abi.encode(signers, signatures, block.number - 1) - ); + vm.expectRevert(IECDSAStakeRegistryErrors.InvalidSignature.selector); + registry.isValidSignature(dataHash, abi.encode(signers, signatures, block.number - 1)); } function test_RevertsWhen_InsufficientSignedStake_CheckSignatures() public { @@ -568,7 +506,7 @@ contract ECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { (v, r, s) = vm.sign(operator2Pk, msgHash); signatures[1] = abi.encodePacked(r, s, v); - uint256 thresholdWeight = 10_000_000_000; + uint256 thresholdWeight = 10000000000; vm.prank(registry.owner()); registry.updateStakeThreshold(thresholdWeight); vm.roll(block.number + 1); @@ -576,19 +514,13 @@ contract ECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { vm.mockCall( address(registry), abi.encodeWithSelector( - ECDSAStakeRegistry.getLastCheckpointOperatorWeight.selector, - operator1 + ECDSAStakeRegistry.getLastCheckpointOperatorWeight.selector, operator1 ), abi.encode(50) ); - vm.expectRevert( - ECDSAStakeRegistryEventsAndErrors.InsufficientSignedStake.selector - ); - registry.isValidSignature( - msgHash, - abi.encode(signers, signatures, block.number - 1) - ); + vm.expectRevert(IECDSAStakeRegistryErrors.InsufficientSignedStake.selector); + registry.isValidSignature(msgHash, abi.encode(signers, signatures, block.number - 1)); } function test_RevertsWhen_LengthMismatch_CheckSignaturesAtBlock() public { @@ -599,13 +531,8 @@ contract ECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { signers[1] = operator2; bytes[] memory signatures = new bytes[](1); - vm.expectRevert( - ECDSAStakeRegistryEventsAndErrors.LengthMismatch.selector - ); - registry.isValidSignature( - dataHash, - abi.encode(signers, signatures, referenceBlock) - ); + vm.expectRevert(IECDSAStakeRegistryErrors.LengthMismatch.selector); + registry.isValidSignature(dataHash, abi.encode(signers, signatures, referenceBlock)); } function test_RevertsWhen_InvalidLength_CheckSignaturesAtBlock() public { @@ -614,13 +541,8 @@ contract ECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { address[] memory signers = new address[](0); bytes[] memory signatures = new bytes[](0); - vm.expectRevert( - ECDSAStakeRegistryEventsAndErrors.InvalidLength.selector - ); - registry.isValidSignature( - dataHash, - abi.encode(signers, signatures, referenceBlock) - ); + vm.expectRevert(IECDSAStakeRegistryErrors.InvalidLength.selector); + registry.isValidSignature(dataHash, abi.encode(signers, signatures, referenceBlock)); } function test_RevertsWhen_NotSorted_CheckSignaturesAtBlock() public { @@ -636,16 +558,11 @@ contract ECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { (v, r, s) = vm.sign(operator2Pk, msgHash); signatures[0] = abi.encodePacked(r, s, v); - vm.expectRevert(ECDSAStakeRegistryEventsAndErrors.NotSorted.selector); - registry.isValidSignature( - msgHash, - abi.encode(signers, signatures, referenceBlock) - ); + vm.expectRevert(IECDSAStakeRegistryErrors.NotSorted.selector); + registry.isValidSignature(msgHash, abi.encode(signers, signatures, referenceBlock)); } - function test_RevetsWhen_InsufficientSignedStake_CheckSignaturesAtBlock() - public - { + function test_RevetsWhen_InsufficientSignedStake_CheckSignaturesAtBlock() public { uint32 referenceBlock = 123; msgHash = keccak256("data"); signers = new address[](2); @@ -657,7 +574,7 @@ contract ECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { (v, r, s) = vm.sign(operator2Pk, msgHash); signatures[1] = abi.encodePacked(r, s, v); - uint256 thresholdWeight = 10_000_000_000; + uint256 thresholdWeight = 10000000000; vm.prank(registry.owner()); registry.updateStakeThreshold(thresholdWeight); vm.roll(referenceBlock + 1); @@ -665,20 +582,13 @@ contract ECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { vm.mockCall( address(registry), abi.encodeWithSelector( - ECDSAStakeRegistry.getOperatorWeightAtBlock.selector, - operator1, - referenceBlock + ECDSAStakeRegistry.getOperatorWeightAtBlock.selector, operator1, referenceBlock ), abi.encode(50) ); - vm.expectRevert( - ECDSAStakeRegistryEventsAndErrors.InsufficientSignedStake.selector - ); - registry.isValidSignature( - msgHash, - abi.encode(signers, signatures, referenceBlock) - ); + vm.expectRevert(IECDSAStakeRegistryErrors.InsufficientSignedStake.selector); + registry.isValidSignature(msgHash, abi.encode(signers, signatures, referenceBlock)); } function test_Gas_UpdateOperators() public { @@ -694,10 +604,7 @@ contract ECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { for (uint256 i; i < operators.length; i++) { operators[i] = address(uint160(i)); vm.prank(operators[i]); - registry.registerOperatorWithSignature( - operatorSignature, - operators[i] - ); + registry.registerOperatorWithSignature(operatorSignature, operators[i]); } vm.resumeGasMetering(); registry.updateOperators(operators); @@ -723,10 +630,7 @@ contract ECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { for (uint256 i = 1; i < operators.length + 1; i++) { operators[i - 1] = address(vm.addr(i)); vm.prank(operators[i - 1]); - registry.registerOperatorWithSignature( - operatorSignature, - operators[i - 1] - ); + registry.registerOperatorWithSignature(operatorSignature, operators[i - 1]); (v, r, s) = vm.sign(i, msgHash); signatures[i - 1] = abi.encodePacked(r, s, v); } @@ -735,10 +639,7 @@ contract ECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { vm.roll(block.number + 1); vm.resumeGasMetering(); - registry.isValidSignature( - msgHash, - abi.encode(operators, signatures, block.number - 1) - ); + registry.isValidSignature(msgHash, abi.encode(operators, signatures, block.number - 1)); emit log_named_uint("Gas consumed", before - gasleft()); } @@ -759,9 +660,7 @@ contract ECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { registry.registerOperatorWithSignature(operatorSignature, signer); // Verify that the signing key has been successfully registered for the operator - address registeredSigningKey = registry.getLastestOperatorSigningKey( - operator - ); + address registeredSigningKey = registry.getLatestOperatorSigningKey(operator); assertEq( registeredSigningKey, signer, @@ -783,15 +682,11 @@ contract ECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { registry.updateOperatorSigningKey(address(420)); // Verify that the signing key has been successfully registered for the operator - address registeredSigningKey = registry.getLastestOperatorSigningKey( - operator - ); + address registeredSigningKey = registry.getLatestOperatorSigningKey(operator); vm.roll(block.number + 1); - registeredSigningKey = registry.getOperatorSigningKeyAtBlock( - operator, - uint32(block.number - 1) - ); + registeredSigningKey = + registry.getOperatorSigningKeyAtBlock(operator, uint32(block.number - 1)); assertEq( registeredSigningKey, address(420), @@ -820,10 +715,7 @@ contract ECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { signatures[0] = abi.encodePacked(r, s, v); // Check signatures using the registered signing key - registry.isValidSignature( - dataHash, - abi.encode(operators, signatures, block.number - 1) - ); + registry.isValidSignature(dataHash, abi.encode(operators, signatures, block.number - 1)); } function test_WhenUsingSigningKey_CheckSignaturesAtBlock() public { @@ -835,10 +727,7 @@ contract ECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { // Register operator with the initial signing key vm.prank(operator); - registry.registerOperatorWithSignature( - operatorSignature, - initialSigningKey - ); + registry.registerOperatorWithSignature(operatorSignature, initialSigningKey); vm.roll(block.number + 1); // Prepare data for signature with initial signing key @@ -852,10 +741,7 @@ contract ECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { signatures[0] = abi.encodePacked(r, s, v); // Check signatures using the initial registered signing key - registry.isValidSignature( - dataHash, - abi.encode(operators, signatures, block.number - 1) - ); + registry.isValidSignature(dataHash, abi.encode(operators, signatures, block.number - 1)); // Increase block number vm.roll(block.number + 10); @@ -870,10 +756,7 @@ contract ECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { signatures[0] = abi.encodePacked(r, s, v); // Check signatures using the updated registered signing key - registry.isValidSignature( - dataHash, - abi.encode(operators, signatures, block.number - 1) - ); + registry.isValidSignature(dataHash, abi.encode(operators, signatures, block.number - 1)); } function test_WhenUsingPriorSigningKey_CheckSignaturesAtBlock() public { @@ -885,10 +768,7 @@ contract ECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { // Register operator with the initial signing key vm.prank(operator); - registry.registerOperatorWithSignature( - operatorSignature, - initialSigningKey - ); + registry.registerOperatorWithSignature(operatorSignature, initialSigningKey); vm.roll(block.number + 1); // Prepare data for signature with initial signing key @@ -909,10 +789,7 @@ contract ECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { registry.updateOperatorSigningKey(updatedSigningKey); // Check signatures using the initial registered signing key at the previous block - registry.isValidSignature( - dataHash, - abi.encode(operators, signatures, block.number - 10) - ); + registry.isValidSignature(dataHash, abi.encode(operators, signatures, block.number - 10)); } function test_RevertsWhen_SigningCurrentBlock_IsValidSignature() public { @@ -929,15 +806,10 @@ contract ECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { signatures[0] = signature; vm.expectRevert(abi.encodeWithSignature("InvalidReferenceBlock()")); - registry.isValidSignature( - dataHash, - abi.encode(operators, signatures, currentBlock) - ); + registry.isValidSignature(dataHash, abi.encode(operators, signatures, currentBlock)); } - function test_RevertsWhen_SigningKeyNotValidAtBlock_IsValidSignature() - public - { + function test_RevertsWhen_SigningKeyNotValidAtBlock_IsValidSignature() public { address operator = operator1; uint256 invalidSignerPk = signerPk + 1; address updatedSigningKey = address(vm.addr(invalidSignerPk)); @@ -958,20 +830,14 @@ contract ECDSAStakeRegistryTest is ECDSAStakeRegistrySetup { signatures[0] = signature; vm.expectRevert(abi.encodeWithSignature("InvalidSignature()")); - registry.isValidSignature( - dataHash, - abi.encode(operators, signatures, referenceBlock) - ); + registry.isValidSignature(dataHash, abi.encode(operators, signatures, referenceBlock)); } function _sort( address[] memory operators, bytes[] memory signatures ) internal pure returns (address[] memory, bytes[] memory) { - require( - operators.length == signatures.length, - "Operators and signatures length mismatch" - ); + require(operators.length == signatures.length, "Operators and signatures length mismatch"); uint256 length = operators.length; for (uint256 i = 0; i < length - 1; i++) { diff --git a/test/unit/EjectionManagerUnit.t.sol b/test/unit/EjectionManagerUnit.t.sol index 6ec539fc..ac557c62 100644 --- a/test/unit/EjectionManagerUnit.t.sol +++ b/test/unit/EjectionManagerUnit.t.sol @@ -1,47 +1,54 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {EjectionManager} from "../../src/EjectionManager.sol"; -import {IEjectionManager} from "../../src/interfaces/IEjectionManager.sol"; - +import { + IEjectionManager, + IEjectionManagerErrors, + IEjectionManagerTypes +} from "../../src/interfaces/IEjectionManager.sol"; +import {ISlashingRegistryCoordinatorTypes} from "../../src/interfaces/IRegistryCoordinator.sol"; import "../utils/MockAVSDeployer.sol"; +import {ISlashingRegistryCoordinatorTypes} from "../../src/interfaces/IRegistryCoordinator.sol"; contract EjectionManagerUnitTests is MockAVSDeployer { - event EjectorUpdated(address ejector, bool status); - event QuorumEjectionParamsSet(uint8 quorumNumber, uint32 rateLimitWindow, uint16 ejectableStakePercent); + event QuorumEjectionParamsSet( + uint8 quorumNumber, uint32 rateLimitWindow, uint16 ejectableStakePercent + ); event OperatorEjected(bytes32 operatorId, uint8 quorumNumber); event FailedOperatorEjection(bytes32 operatorId, uint8 quorumNumber, bytes err); EjectionManager public ejectionManager; IEjectionManager public ejectionManagerImplementation; - IEjectionManager.QuorumEjectionParams[] public quorumEjectionParams; + IEjectionManagerTypes.QuorumEjectionParams[] public quorumEjectionParams; uint32 public ratelimitWindow = 1 days; uint16 public ejectableStakePercent = 1000; - function setUp() virtual public { - for(uint8 i = 0; i < numQuorums; i++) { - quorumEjectionParams.push(IEjectionManager.QuorumEjectionParams({ - rateLimitWindow: ratelimitWindow, - ejectableStakePercent: ejectableStakePercent - })); + function setUp() public virtual { + for (uint8 i = 0; i < numQuorums; i++) { + quorumEjectionParams.push( + IEjectionManagerTypes.QuorumEjectionParams({ + rateLimitWindow: ratelimitWindow, + ejectableStakePercent: ejectableStakePercent + }) + ); } defaultMaxOperatorCount = 200; _deployMockEigenLayerAndAVS(); - ejectionManager = EjectionManager(address( - new TransparentUpgradeableProxy( - address(emptyContract), - address(proxyAdmin), - "" + ejectionManager = EjectionManager( + address( + new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), "") ) - )); + ); - ejectionManagerImplementation = new EjectionManager(registryCoordinator, stakeRegistry); + ejectionManagerImplementation = + new EjectionManager(IRegistryCoordinator(address(registryCoordinator)), stakeRegistry); address[] memory ejectors = new address[](1); ejectors[0] = ejector; @@ -73,15 +80,19 @@ contract EjectionManagerUnitTests is MockAVSDeployer { bytes32[][] memory operatorIds = new bytes32[][](numQuorums); for (uint8 i = 0; i < numQuorums; i++) { operatorIds[i] = new bytes32[](operatorsToEject); - for (uint j = 0; j < operatorsToEject; j++) { - operatorIds[i][j] = registryCoordinator.getOperatorId(_incrementAddress(defaultOperator, j)); + for (uint256 j = 0; j < operatorsToEject; j++) { + operatorIds[i][j] = + registryCoordinator.getOperatorId(_incrementAddress(defaultOperator, j)); } } - assertEq(uint8(registryCoordinator.getOperatorStatus(defaultOperator)), uint8(IRegistryCoordinator.OperatorStatus.REGISTERED)); + assertEq( + uint8(registryCoordinator.getOperatorStatus(defaultOperator)), + uint8(ISlashingRegistryCoordinatorTypes.OperatorStatus.REGISTERED) + ); - for(uint8 i = 0; i < numQuorums; i++) { - for(uint8 j = 0; j < operatorsToEject; j++) { + for (uint8 i = 0; i < numQuorums; i++) { + for (uint8 j = 0; j < operatorsToEject; j++) { cheats.expectEmit(true, true, true, true, address(ejectionManager)); emit OperatorEjected(operatorIds[i][j], i); } @@ -90,7 +101,10 @@ contract EjectionManagerUnitTests is MockAVSDeployer { cheats.prank(ejector); ejectionManager.ejectOperators(operatorIds); - assertEq(uint8(registryCoordinator.getOperatorStatus(defaultOperator)), uint8(IRegistryCoordinator.OperatorStatus.DEREGISTERED)); + assertEq( + uint8(registryCoordinator.getOperatorStatus(defaultOperator)), + uint8(ISlashingRegistryCoordinatorTypes.OperatorStatus.DEREGISTERED) + ); } function testEjectOperators_MultipleOperatorInsideRatelimit() public { @@ -102,17 +116,21 @@ contract EjectionManagerUnitTests is MockAVSDeployer { bytes32[][] memory operatorIds = new bytes32[][](numQuorums); for (uint8 i = 0; i < numQuorums; i++) { operatorIds[i] = new bytes32[](operatorsToEject); - for (uint j = 0; j < operatorsToEject; j++) { - operatorIds[i][j] = registryCoordinator.getOperatorId(_incrementAddress(defaultOperator, j)); + for (uint256 j = 0; j < operatorsToEject; j++) { + operatorIds[i][j] = + registryCoordinator.getOperatorId(_incrementAddress(defaultOperator, j)); } } - for(uint8 i = 0; i < operatorsToEject; i++) { - assertEq(uint8(registryCoordinator.getOperatorStatus(_incrementAddress(defaultOperator, i))), uint8(IRegistryCoordinator.OperatorStatus.REGISTERED)); + for (uint8 i = 0; i < operatorsToEject; i++) { + assertEq( + uint8(registryCoordinator.getOperatorStatus(_incrementAddress(defaultOperator, i))), + uint8(ISlashingRegistryCoordinatorTypes.OperatorStatus.REGISTERED) + ); } - for(uint8 i = 0; i < numQuorums; i++) { - for(uint8 j = 0; j < operatorsToEject; j++) { + for (uint8 i = 0; i < numQuorums; i++) { + for (uint8 j = 0; j < operatorsToEject; j++) { cheats.expectEmit(true, true, true, true, address(ejectionManager)); emit OperatorEjected(operatorIds[i][j], i); } @@ -121,8 +139,11 @@ contract EjectionManagerUnitTests is MockAVSDeployer { cheats.prank(ejector); ejectionManager.ejectOperators(operatorIds); - for(uint8 i = 0; i < operatorsToEject; i++) { - assertEq(uint8(registryCoordinator.getOperatorStatus(_incrementAddress(defaultOperator, i))), uint8(IRegistryCoordinator.OperatorStatus.DEREGISTERED)); + for (uint8 i = 0; i < operatorsToEject; i++) { + assertEq( + uint8(registryCoordinator.getOperatorStatus(_incrementAddress(defaultOperator, i))), + uint8(ISlashingRegistryCoordinatorTypes.OperatorStatus.DEREGISTERED) + ); } } @@ -136,17 +157,21 @@ contract EjectionManagerUnitTests is MockAVSDeployer { bytes32[][] memory operatorIds = new bytes32[][](numQuorums); for (uint8 i = 0; i < numQuorums; i++) { operatorIds[i] = new bytes32[](operatorsToEject); - for (uint j = 0; j < operatorsToEject; j++) { - operatorIds[i][j] = registryCoordinator.getOperatorId(_incrementAddress(defaultOperator, j)); + for (uint256 j = 0; j < operatorsToEject; j++) { + operatorIds[i][j] = + registryCoordinator.getOperatorId(_incrementAddress(defaultOperator, j)); } } - for(uint8 i = 0; i < operatorsToEject; i++) { - assertEq(uint8(registryCoordinator.getOperatorStatus(_incrementAddress(defaultOperator, i))), uint8(IRegistryCoordinator.OperatorStatus.REGISTERED)); + for (uint8 i = 0; i < operatorsToEject; i++) { + assertEq( + uint8(registryCoordinator.getOperatorStatus(_incrementAddress(defaultOperator, i))), + uint8(ISlashingRegistryCoordinatorTypes.OperatorStatus.REGISTERED) + ); } - for(uint8 i = 0; i < numQuorums; i++) { - for(uint8 j = 0; j < operatorsCanEject; j++) { + for (uint8 i = 0; i < numQuorums; i++) { + for (uint8 j = 0; j < operatorsCanEject; j++) { cheats.expectEmit(true, true, true, true, address(ejectionManager)); emit OperatorEjected(operatorIds[i][j], i); } @@ -155,12 +180,18 @@ contract EjectionManagerUnitTests is MockAVSDeployer { cheats.prank(ejector); ejectionManager.ejectOperators(operatorIds); - for(uint8 i = 0; i < operatorsCanEject; i++) { - assertEq(uint8(registryCoordinator.getOperatorStatus(_incrementAddress(defaultOperator, i))), uint8(IRegistryCoordinator.OperatorStatus.DEREGISTERED)); + for (uint8 i = 0; i < operatorsCanEject; i++) { + assertEq( + uint8(registryCoordinator.getOperatorStatus(_incrementAddress(defaultOperator, i))), + uint8(ISlashingRegistryCoordinatorTypes.OperatorStatus.DEREGISTERED) + ); } - for(uint8 i = operatorsCanEject; i < operatorsToEject; i++) { - assertEq(uint8(registryCoordinator.getOperatorStatus(_incrementAddress(defaultOperator, i))), uint8(IRegistryCoordinator.OperatorStatus.REGISTERED)); + for (uint8 i = operatorsCanEject; i < operatorsToEject; i++) { + assertEq( + uint8(registryCoordinator.getOperatorStatus(_incrementAddress(defaultOperator, i))), + uint8(ISlashingRegistryCoordinatorTypes.OperatorStatus.REGISTERED) + ); } } @@ -174,17 +205,21 @@ contract EjectionManagerUnitTests is MockAVSDeployer { bytes32[][] memory operatorIds = new bytes32[][](numQuorums); for (uint8 i = 0; i < numQuorums; i++) { operatorIds[i] = new bytes32[](operatorsToEject); - for (uint j = 0; j < operatorsToEject; j++) { - operatorIds[i][j] = registryCoordinator.getOperatorId(_incrementAddress(defaultOperator, j)); + for (uint256 j = 0; j < operatorsToEject; j++) { + operatorIds[i][j] = + registryCoordinator.getOperatorId(_incrementAddress(defaultOperator, j)); } } - for(uint8 i = 0; i < operatorsToEject; i++) { - assertEq(uint8(registryCoordinator.getOperatorStatus(_incrementAddress(defaultOperator, i))), uint8(IRegistryCoordinator.OperatorStatus.REGISTERED)); + for (uint8 i = 0; i < operatorsToEject; i++) { + assertEq( + uint8(registryCoordinator.getOperatorStatus(_incrementAddress(defaultOperator, i))), + uint8(ISlashingRegistryCoordinatorTypes.OperatorStatus.REGISTERED) + ); } - for(uint8 i = 0; i < numQuorums; i++) { - for(uint8 j = 0; j < operatorsCanEject; j++) { + for (uint8 i = 0; i < numQuorums; i++) { + for (uint8 j = 0; j < operatorsCanEject; j++) { cheats.expectEmit(true, true, true, true, address(ejectionManager)); emit OperatorEjected(operatorIds[i][j], i); } @@ -193,23 +228,35 @@ contract EjectionManagerUnitTests is MockAVSDeployer { cheats.prank(ejector); ejectionManager.ejectOperators(operatorIds); - for(uint8 i = 0; i < operatorsCanEject; i++) { - assertEq(uint8(registryCoordinator.getOperatorStatus(_incrementAddress(defaultOperator, i))), uint8(IRegistryCoordinator.OperatorStatus.DEREGISTERED)); + for (uint8 i = 0; i < operatorsCanEject; i++) { + assertEq( + uint8(registryCoordinator.getOperatorStatus(_incrementAddress(defaultOperator, i))), + uint8(ISlashingRegistryCoordinatorTypes.OperatorStatus.DEREGISTERED) + ); } - for(uint8 i = operatorsCanEject; i < operatorsToEject; i++) { - assertEq(uint8(registryCoordinator.getOperatorStatus(_incrementAddress(defaultOperator, i))), uint8(IRegistryCoordinator.OperatorStatus.REGISTERED)); + for (uint8 i = operatorsCanEject; i < operatorsToEject; i++) { + assertEq( + uint8(registryCoordinator.getOperatorStatus(_incrementAddress(defaultOperator, i))), + uint8(ISlashingRegistryCoordinatorTypes.OperatorStatus.REGISTERED) + ); } cheats.prank(ejector); ejectionManager.ejectOperators(operatorIds); - for(uint8 i = 0; i < operatorsCanEject; i++) { - assertEq(uint8(registryCoordinator.getOperatorStatus(_incrementAddress(defaultOperator, i))), uint8(IRegistryCoordinator.OperatorStatus.DEREGISTERED)); + for (uint8 i = 0; i < operatorsCanEject; i++) { + assertEq( + uint8(registryCoordinator.getOperatorStatus(_incrementAddress(defaultOperator, i))), + uint8(ISlashingRegistryCoordinatorTypes.OperatorStatus.DEREGISTERED) + ); } - for(uint8 i = operatorsCanEject; i < operatorsToEject; i++) { - assertEq(uint8(registryCoordinator.getOperatorStatus(_incrementAddress(defaultOperator, i))), uint8(IRegistryCoordinator.OperatorStatus.REGISTERED)); + for (uint8 i = operatorsCanEject; i < operatorsToEject; i++) { + assertEq( + uint8(registryCoordinator.getOperatorStatus(_incrementAddress(defaultOperator, i))), + uint8(ISlashingRegistryCoordinatorTypes.OperatorStatus.REGISTERED) + ); } } @@ -222,17 +269,21 @@ contract EjectionManagerUnitTests is MockAVSDeployer { bytes32[][] memory operatorIds = new bytes32[][](numQuorums); for (uint8 i = 0; i < numQuorums; i++) { operatorIds[i] = new bytes32[](operatorsToEject); - for (uint j = 0; j < operatorsToEject; j++) { - operatorIds[i][j] = registryCoordinator.getOperatorId(_incrementAddress(defaultOperator, j)); + for (uint256 j = 0; j < operatorsToEject; j++) { + operatorIds[i][j] = + registryCoordinator.getOperatorId(_incrementAddress(defaultOperator, j)); } } - for(uint8 i = 0; i < operatorsToEject; i++) { - assertEq(uint8(registryCoordinator.getOperatorStatus(_incrementAddress(defaultOperator, i))), uint8(IRegistryCoordinator.OperatorStatus.REGISTERED)); + for (uint8 i = 0; i < operatorsToEject; i++) { + assertEq( + uint8(registryCoordinator.getOperatorStatus(_incrementAddress(defaultOperator, i))), + uint8(ISlashingRegistryCoordinatorTypes.OperatorStatus.REGISTERED) + ); } - for(uint8 i = 0; i < numQuorums; i++) { - for(uint8 j = 0; j < operatorsToEject; j++) { + for (uint8 i = 0; i < numQuorums; i++) { + for (uint8 j = 0; j < operatorsToEject; j++) { cheats.expectEmit(true, true, true, true, address(ejectionManager)); emit OperatorEjected(operatorIds[i][j], i); } @@ -241,8 +292,11 @@ contract EjectionManagerUnitTests is MockAVSDeployer { cheats.prank(ejector); ejectionManager.ejectOperators(operatorIds); - for(uint8 i = 0; i < operatorsToEject; i++) { - assertEq(uint8(registryCoordinator.getOperatorStatus(_incrementAddress(defaultOperator, i))), uint8(IRegistryCoordinator.OperatorStatus.DEREGISTERED)); + for (uint8 i = 0; i < operatorsToEject; i++) { + assertEq( + uint8(registryCoordinator.getOperatorStatus(_incrementAddress(defaultOperator, i))), + uint8(ISlashingRegistryCoordinatorTypes.OperatorStatus.DEREGISTERED) + ); } cheats.warp(block.timestamp + (ratelimitWindow / 2)); @@ -250,17 +304,26 @@ contract EjectionManagerUnitTests is MockAVSDeployer { operatorIds = new bytes32[][](numQuorums); for (uint8 i = 0; i < numQuorums; i++) { operatorIds[i] = new bytes32[](operatorsToEject); - for (uint j = 0; j < operatorsToEject; j++) { - operatorIds[i][j] = registryCoordinator.getOperatorId(_incrementAddress(defaultOperator, operatorsToEject + j)); + for (uint256 j = 0; j < operatorsToEject; j++) { + operatorIds[i][j] = registryCoordinator.getOperatorId( + _incrementAddress(defaultOperator, operatorsToEject + j) + ); } } - for(uint8 i = 0; i < operatorsToEject; i++) { - assertEq(uint8(registryCoordinator.getOperatorStatus(_incrementAddress(defaultOperator, operatorsToEject + i))), uint8(IRegistryCoordinator.OperatorStatus.REGISTERED)); + for (uint8 i = 0; i < operatorsToEject; i++) { + assertEq( + uint8( + registryCoordinator.getOperatorStatus( + _incrementAddress(defaultOperator, operatorsToEject + i) + ) + ), + uint8(ISlashingRegistryCoordinatorTypes.OperatorStatus.REGISTERED) + ); } - for(uint8 i = 0; i < numQuorums; i++) { - for(uint8 j = 0; j < operatorsToEject; j++) { + for (uint8 i = 0; i < numQuorums; i++) { + for (uint8 j = 0; j < operatorsToEject; j++) { cheats.expectEmit(true, true, true, true, address(ejectionManager)); emit OperatorEjected(operatorIds[i][j], i); } @@ -269,8 +332,15 @@ contract EjectionManagerUnitTests is MockAVSDeployer { cheats.prank(ejector); ejectionManager.ejectOperators(operatorIds); - for(uint8 i = 0; i < operatorsToEject; i++) { - assertEq(uint8(registryCoordinator.getOperatorStatus(_incrementAddress(defaultOperator, operatorsToEject + i))), uint8(IRegistryCoordinator.OperatorStatus.DEREGISTERED)); + for (uint8 i = 0; i < operatorsToEject; i++) { + assertEq( + uint8( + registryCoordinator.getOperatorStatus( + _incrementAddress(defaultOperator, operatorsToEject + i) + ) + ), + uint8(ISlashingRegistryCoordinatorTypes.OperatorStatus.DEREGISTERED) + ); } } @@ -290,17 +360,21 @@ contract EjectionManagerUnitTests is MockAVSDeployer { bytes32[][] memory operatorIds = new bytes32[][](numQuorums); for (uint8 i = 0; i < numQuorums; i++) { operatorIds[i] = new bytes32[](operatorsToEject); - for (uint j = 0; j < operatorsToEject; j++) { - operatorIds[i][j] = registryCoordinator.getOperatorId(_incrementAddress(defaultOperator, j)); + for (uint256 j = 0; j < operatorsToEject; j++) { + operatorIds[i][j] = + registryCoordinator.getOperatorId(_incrementAddress(defaultOperator, j)); } } - for(uint8 i = 0; i < operatorsToEject; i++) { - assertEq(uint8(registryCoordinator.getOperatorStatus(_incrementAddress(defaultOperator, i))), uint8(IRegistryCoordinator.OperatorStatus.REGISTERED)); + for (uint8 i = 0; i < operatorsToEject; i++) { + assertEq( + uint8(registryCoordinator.getOperatorStatus(_incrementAddress(defaultOperator, i))), + uint8(ISlashingRegistryCoordinatorTypes.OperatorStatus.REGISTERED) + ); } - for(uint8 i = 0; i < numQuorums; i++) { - for(uint8 j = 0; j < operatorsToEject; j++) { + for (uint8 i = 0; i < numQuorums; i++) { + for (uint8 j = 0; j < operatorsToEject; j++) { cheats.expectEmit(true, true, true, true, address(ejectionManager)); emit OperatorEjected(operatorIds[i][j], i); } @@ -309,8 +383,11 @@ contract EjectionManagerUnitTests is MockAVSDeployer { cheats.prank(ejector); ejectionManager.ejectOperators(operatorIds); - for(uint8 i = 0; i < operatorsToEject; i++) { - assertEq(uint8(registryCoordinator.getOperatorStatus(_incrementAddress(defaultOperator, i))), uint8(IRegistryCoordinator.OperatorStatus.DEREGISTERED)); + for (uint8 i = 0; i < operatorsToEject; i++) { + assertEq( + uint8(registryCoordinator.getOperatorStatus(_incrementAddress(defaultOperator, i))), + uint8(ISlashingRegistryCoordinatorTypes.OperatorStatus.DEREGISTERED) + ); } } @@ -323,17 +400,21 @@ contract EjectionManagerUnitTests is MockAVSDeployer { bytes32[][] memory operatorIds = new bytes32[][](numQuorums); for (uint8 i = 0; i < numQuorums; i++) { operatorIds[i] = new bytes32[](operatorsToEject); - for (uint j = 0; j < operatorsToEject; j++) { - operatorIds[i][j] = registryCoordinator.getOperatorId(_incrementAddress(defaultOperator, j)); + for (uint256 j = 0; j < operatorsToEject; j++) { + operatorIds[i][j] = + registryCoordinator.getOperatorId(_incrementAddress(defaultOperator, j)); } } - for(uint8 i = 0; i < operatorsToEject; i++) { - assertEq(uint8(registryCoordinator.getOperatorStatus(_incrementAddress(defaultOperator, i))), uint8(IRegistryCoordinator.OperatorStatus.REGISTERED)); + for (uint8 i = 0; i < operatorsToEject; i++) { + assertEq( + uint8(registryCoordinator.getOperatorStatus(_incrementAddress(defaultOperator, i))), + uint8(ISlashingRegistryCoordinatorTypes.OperatorStatus.REGISTERED) + ); } - for(uint8 i = 0; i < numQuorums; i++) { - for(uint8 j = 0; j < operatorsToEject; j++) { + for (uint8 i = 0; i < numQuorums; i++) { + for (uint8 j = 0; j < operatorsToEject; j++) { cheats.expectEmit(true, true, true, true, address(ejectionManager)); emit OperatorEjected(operatorIds[i][j], i); } @@ -342,8 +423,11 @@ contract EjectionManagerUnitTests is MockAVSDeployer { cheats.prank(registryCoordinatorOwner); ejectionManager.ejectOperators(operatorIds); - for(uint8 i = 0; i < operatorsToEject; i++) { - assertEq(uint8(registryCoordinator.getOperatorStatus(_incrementAddress(defaultOperator, i))), uint8(IRegistryCoordinator.OperatorStatus.DEREGISTERED)); + for (uint8 i = 0; i < operatorsToEject; i++) { + assertEq( + uint8(registryCoordinator.getOperatorStatus(_incrementAddress(defaultOperator, i))), + uint8(ISlashingRegistryCoordinatorTypes.OperatorStatus.DEREGISTERED) + ); } } @@ -356,20 +440,24 @@ contract EjectionManagerUnitTests is MockAVSDeployer { bytes32[][] memory operatorIds = new bytes32[][](numQuorums); for (uint8 i = 0; i < numQuorums; i++) { operatorIds[i] = new bytes32[](operatorsToEject); - for (uint j = 0; j < operatorsToEject; j++) { - operatorIds[i][j] = registryCoordinator.getOperatorId(_incrementAddress(defaultOperator, j)); + for (uint256 j = 0; j < operatorsToEject; j++) { + operatorIds[i][j] = + registryCoordinator.getOperatorId(_incrementAddress(defaultOperator, j)); } } cheats.prank(defaultOperator); registryCoordinator.deregisterOperator(BitmapUtils.bitmapToBytesArray(MAX_QUORUM_BITMAP)); - for(uint8 i = 1; i < operatorsToEject; i++) { - assertEq(uint8(registryCoordinator.getOperatorStatus(_incrementAddress(defaultOperator, i))), uint8(IRegistryCoordinator.OperatorStatus.REGISTERED)); + for (uint8 i = 1; i < operatorsToEject; i++) { + assertEq( + uint8(registryCoordinator.getOperatorStatus(_incrementAddress(defaultOperator, i))), + uint8(ISlashingRegistryCoordinatorTypes.OperatorStatus.REGISTERED) + ); } - for(uint8 i = 0; i < numQuorums; i++) { - for(uint8 j = 1; j < operatorsToEject; j++) { + for (uint8 i = 0; i < numQuorums; i++) { + for (uint8 j = 1; j < operatorsToEject; j++) { cheats.expectEmit(true, true, true, true, address(ejectionManager)); emit OperatorEjected(operatorIds[i][j], i); } @@ -378,8 +466,11 @@ contract EjectionManagerUnitTests is MockAVSDeployer { cheats.prank(ejector); ejectionManager.ejectOperators(operatorIds); - for(uint8 i = 0; i < operatorsToEject; i++) { - assertEq(uint8(registryCoordinator.getOperatorStatus(_incrementAddress(defaultOperator, i))), uint8(IRegistryCoordinator.OperatorStatus.DEREGISTERED)); + for (uint8 i = 0; i < operatorsToEject; i++) { + assertEq( + uint8(registryCoordinator.getOperatorStatus(_incrementAddress(defaultOperator, i))), + uint8(ISlashingRegistryCoordinatorTypes.OperatorStatus.DEREGISTERED) + ); } } @@ -387,7 +478,8 @@ contract EjectionManagerUnitTests is MockAVSDeployer { uint8 quorumNumber = 0; ratelimitWindow = 2 days; ejectableStakePercent = 2000; - IEjectionManager.QuorumEjectionParams memory _quorumEjectionParams = IEjectionManager.QuorumEjectionParams({ + IEjectionManagerTypes.QuorumEjectionParams memory _quorumEjectionParams = + IEjectionManagerTypes.QuorumEjectionParams({ rateLimitWindow: ratelimitWindow, ejectableStakePercent: ejectableStakePercent }); @@ -398,7 +490,8 @@ contract EjectionManagerUnitTests is MockAVSDeployer { cheats.prank(registryCoordinatorOwner); ejectionManager.setQuorumEjectionParams(quorumNumber, _quorumEjectionParams); - (uint32 setRatelimitWindow, uint16 setEjectableStakePercent) = ejectionManager.quorumEjectionParams(quorumNumber); + (uint32 setRatelimitWindow, uint16 setEjectableStakePercent) = + ejectionManager.quorumEjectionParams(quorumNumber); assertEq(setRatelimitWindow, _quorumEjectionParams.rateLimitWindow); assertEq(setEjectableStakePercent, _quorumEjectionParams.ejectableStakePercent); } @@ -415,7 +508,7 @@ contract EjectionManagerUnitTests is MockAVSDeployer { function test_Revert_NotPermissioned() public { bytes32[][] memory operatorIds; - cheats.expectRevert("Ejector: Only owner or ejector can eject"); + cheats.expectRevert(IEjectionManagerErrors.OnlyOwnerOrEjector.selector); ejectionManager.ejectOperators(operatorIds); EjectionManager.QuorumEjectionParams memory _quorumEjectionParams; @@ -428,21 +521,24 @@ contract EjectionManagerUnitTests is MockAVSDeployer { function test_Overflow_Regression() public { cheats.prank(registryCoordinatorOwner); - ejectionManager.setQuorumEjectionParams(0, IEjectionManager.QuorumEjectionParams({ - rateLimitWindow: 7 days, - ejectableStakePercent: 9999 - })); + ejectionManager.setQuorumEjectionParams( + 0, + IEjectionManagerTypes.QuorumEjectionParams({ + rateLimitWindow: 7 days, + ejectableStakePercent: 9999 + }) + ); - stakeRegistry.recordTotalStakeUpdate(1, 2_000_000_000 * 1 ether); + stakeRegistry.recordTotalStakeUpdate(1, 2000000000 * 1 ether); ejectionManager.amountEjectableForQuorum(1); } function _registerOperaters(uint8 numOperators, uint96 stake) internal { - for (uint i = 0; i < numOperators; i++) { + for (uint256 i = 0; i < numOperators; i++) { BN254.G1Point memory pubKey = BN254.hashToG1(keccak256(abi.encodePacked(i))); address operator = _incrementAddress(defaultOperator, i); _registerOperatorWithCoordinator(operator, MAX_QUORUM_BITMAP, pubKey, stake); } } -} \ No newline at end of file +} diff --git a/test/unit/IndexRegistryUnit.t.sol b/test/unit/IndexRegistryUnit.t.sol index 1ed44d7d..0a6d424c 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; +pragma solidity ^0.8.27; import "../../src/interfaces/IIndexRegistry.sol"; import "../../src/IndexRegistry.sol"; import "../harnesses/BitmapUtilsWrapper.sol"; -import {IIndexRegistryEvents} from "../events/IIndexRegistryEvents.sol"; +import {IIndexRegistryEvents} from "../../src/interfaces/IIndexRegistry.sol"; import "../utils/MockAVSDeployer.sol"; @@ -45,10 +45,11 @@ contract IndexRegistryUnitTests is MockAVSDeployer, IIndexRegistryEvents { _initializeQuorum(); } - /******************************************************************************* - INTERNAL UNIT TEST HELPERS - *******************************************************************************/ - + /** + * + * INTERNAL UNIT TEST HELPERS + * + */ function _initializeQuorum() internal { uint8 quorumNumber = nextQuorum; nextQuorum++; @@ -63,9 +64,11 @@ contract IndexRegistryUnitTests is MockAVSDeployer, IIndexRegistryEvents { initializedQuorumBitmap = uint192(initializedQuorumBitmap.setBit(quorumNumber)); initializedQuorumBytes = initializedQuorumBitmap.bitmapToBytesArray(); } - + /// @dev Doesn't increment nextQuorum as assumes quorumNumber is any valid arbitrary quorumNumber - function _initializeQuorum(uint8 quorumNumber) internal { + function _initializeQuorum( + uint8 quorumNumber + ) internal { cheats.prank(address(registryCoordinator)); // Initialize quorum and mark registered @@ -75,10 +78,12 @@ contract IndexRegistryUnitTests is MockAVSDeployer, IIndexRegistryEvents { /// @dev initializeQuorum based on passed in bitmap of quorum numbers /// assumes that bitmap does not contain already initailized quorums and doesn't increment nextQuorum - function _initializeFuzzedQuorums(uint192 bitmap) internal { + function _initializeFuzzedQuorums( + uint192 bitmap + ) internal { bytes memory quorumNumbers = bitmapUtilsWrapper.bitmapToBytesArray(bitmap); - for (uint i = 0; i < quorumNumbers.length; i++) { + for (uint256 i = 0; i < quorumNumbers.length; i++) { uint8 quorumNumber = uint8(quorumNumbers[i]); _initializeQuorum(quorumNumber); } @@ -100,7 +105,8 @@ contract IndexRegistryUnitTests is MockAVSDeployer, IIndexRegistryEvents { bytes memory quorumNumbers ) internal returns (uint32[] memory) { cheats.prank(address(registryCoordinator)); - uint32[] memory numOperatorsPerQuorum = indexRegistry.registerOperator(operatorId, quorumNumbers); + uint32[] memory numOperatorsPerQuorum = + indexRegistry.registerOperator(operatorId, quorumNumbers); return numOperatorsPerQuorum; } @@ -112,24 +118,19 @@ contract IndexRegistryUnitTests is MockAVSDeployer, IIndexRegistryEvents { bytes memory quorumNumbers = new bytes(1); quorumNumbers[0] = bytes1(quorumNumber); cheats.prank(address(registryCoordinator)); - uint32[] memory numOperatorsPerQuorum = indexRegistry.registerOperator(operatorId, quorumNumbers); + uint32[] memory numOperatorsPerQuorum = + indexRegistry.registerOperator(operatorId, quorumNumbers); return numOperatorsPerQuorum[0]; } /// @dev deregister an operator for a given set of quorums - function _deregisterOperator( - bytes32 operatorId, - bytes memory quorumNumbers - ) internal { + function _deregisterOperator(bytes32 operatorId, bytes memory quorumNumbers) internal { cheats.prank(address(registryCoordinator)); indexRegistry.deregisterOperator(operatorId, quorumNumbers); } /// @dev deregister an operator for a single quorum - function _deregisterOperatorSingleQuorum( - bytes32 operatorId, - uint8 quorumNumber - ) internal { + function _deregisterOperatorSingleQuorum(bytes32 operatorId, uint8 quorumNumber) internal { bytes memory quorumNumbers = new bytes(1); quorumNumbers[0] = bytes1(quorumNumber); cheats.prank(address(registryCoordinator)); @@ -138,15 +139,15 @@ contract IndexRegistryUnitTests is MockAVSDeployer, IIndexRegistryEvents { /// @dev Uses `rand` to return a random uint, with a range given by `min` and `max` (inclusive) /// @return `min` <= result <= `max` - function _randUint(bytes32 rand, uint min, uint max) internal pure returns (uint) { + function _randUint(bytes32 rand, uint256 min, uint256 max) internal pure returns (uint256) { // hashing makes for more uniform randomness rand = keccak256(abi.encodePacked(rand)); - - uint range = max - min + 1; + + uint256 range = max - min + 1; // calculate the number of bits needed for the range - uint bitsNeeded = 0; - uint tempRange = range; + uint256 bitsNeeded = 0; + uint256 tempRange = range; while (tempRange > 0) { bitsNeeded++; tempRange >>= 1; @@ -154,8 +155,8 @@ contract IndexRegistryUnitTests is MockAVSDeployer, IIndexRegistryEvents { // create a mask for the required number of bits // and extract the value from the hash - uint mask = (1 << bitsNeeded) - 1; - uint value = uint(rand) & mask; + uint256 mask = (1 << bitsNeeded) - 1; + uint256 value = uint256(rand) & mask; // in case value is out of range, wrap around or retry while (value >= range) { @@ -165,18 +166,19 @@ contract IndexRegistryUnitTests is MockAVSDeployer, IIndexRegistryEvents { return min + value; } - /******************************************************************************* - ASSERTION HELPERS - *******************************************************************************/ - + /** + * + * ASSERTION HELPERS + * + */ function _assertQuorumUpdate( uint8 quorumNumber, uint256 expectedNumOperators, uint256 expectedFromBlockNumber ) internal { // Check _totalOperatorsHistory updates for quorum - IIndexRegistry.QuorumUpdate memory quorumUpdate = indexRegistry - .getLatestQuorumUpdate(quorumNumber); + IIndexRegistry.QuorumUpdate memory quorumUpdate = + indexRegistry.getLatestQuorumUpdate(quorumNumber); assertEq( quorumUpdate.numOperators, expectedNumOperators, @@ -201,17 +203,11 @@ contract IndexRegistryUnitTests is MockAVSDeployer, IIndexRegistryEvents { bytes32 operatorId, uint256 expectedFromBlockNumber ) internal { - IIndexRegistry.OperatorUpdate memory operatorUpdate = indexRegistry - .getOperatorUpdateAtIndex(quorumNumber, operatorIndex, arrayIndex); + IIndexRegistry.OperatorUpdate memory operatorUpdate = + indexRegistry.getOperatorUpdateAtIndex(quorumNumber, operatorIndex, arrayIndex); + assertEq(operatorUpdate.operatorId, operatorId, "incorrect operatorId"); assertEq( - operatorUpdate.operatorId, - operatorId, - "incorrect operatorId" - ); - assertEq( - operatorUpdate.fromBlockNumber, - expectedFromBlockNumber, - "fromBlockNumber not correct" + operatorUpdate.fromBlockNumber, expectedFromBlockNumber, "fromBlockNumber not correct" ); } } @@ -222,10 +218,11 @@ contract IndexRegistryUnitTests_configAndGetters is IndexRegistryUnitTests { assertEq(address(indexRegistry.registryCoordinator()), address(registryCoordinator)); } - /******************************************************************************* - UNIT TESTS - GETTERS - *******************************************************************************/ - + /** + * + * UNIT TESTS - GETTERS + * + */ function _assertOperatorListsEqual( bytes32[] memory operatorList1, bytes32[] memory operatorList2, @@ -237,11 +234,7 @@ contract IndexRegistryUnitTests_configAndGetters is IndexRegistryUnitTests { ); for (uint256 i = 0; i < operatorList1.length; i++) { - assertEq( - operatorList1[i], - operatorList2[i], - "operator lists not equal" - ); + assertEq(operatorList1[i], operatorList2[i], "operator lists not equal"); } } @@ -258,7 +251,7 @@ contract IndexRegistryUnitTests_configAndGetters is IndexRegistryUnitTests { currBlockNumber += 10; cheats.roll(currBlockNumber); - + // initialize a new quorum after startBlockNumber uint8 quorumNumber = nextQuorum; _initializeQuorum(); @@ -266,11 +259,8 @@ contract IndexRegistryUnitTests_configAndGetters is IndexRegistryUnitTests { bytes32[] memory currRegisteredOperatorIds = new bytes32[](numOperators); uint256 currIndex = 0; for (uint256 i = 0; i < numOperators; i++) { - uint256 rand = _randUint({ - rand: keccak256(abi.encodePacked(bytes32(i), randSalt)), - min: 0, - max: 1 - }); + uint256 rand = + _randUint({rand: keccak256(abi.encodePacked(bytes32(i), randSalt)), min: 0, max: 1}); // Roll block number currBlockNumber += 10; @@ -282,7 +272,7 @@ contract IndexRegistryUnitTests_configAndGetters is IndexRegistryUnitTests { _deregisterOperatorSingleQuorum(operatorId, quorumNumber); currRegisteredOperatorIds[currIndex] = operatorId; currIndex--; - // register operator + // register operator } else { (, bytes32 operatorId) = _selectNewOperator(); _registerOperatorSingleQuorum(operatorId, quorumNumber); @@ -292,11 +282,11 @@ contract IndexRegistryUnitTests_configAndGetters is IndexRegistryUnitTests { } // should revert with startBlocknumber - cheats.expectRevert("IndexRegistry._operatorCountAtBlockNumber: quorum did not exist at given block number"); - indexRegistry.getOperatorListAtBlockNumber( - quorumNumber, - startBlockNumber + cheats.expectRevert( + "IndexRegistry._operatorCountAtBlockNumber: quorum did not exist at given block number" ); + + indexRegistry.getOperatorListAtBlockNumber(quorumNumber, startBlockNumber); } } @@ -305,16 +295,16 @@ contract IndexRegistryUnitTests_configAndGetters is IndexRegistryUnitTests { * And keeping track of the total operators and their indexes. Checks that the return list of operatorIds * from `getOperatorListForQuorum` matches the expected list of operatorIds */ - function testFuzz_getOperatorListForQuorumAtBlockNumber(uint8 numOperators, bytes32 randSalt) public { + function testFuzz_getOperatorListForQuorumAtBlockNumber( + uint8 numOperators, + bytes32 randSalt + ) public { bytes32[] memory currRegisteredOperatorIds = new bytes32[](numOperators); uint256 currIndex = 0; uint32 currBlockNumber = uint32(block.number); for (uint256 i = 0; i < numOperators; i++) { - uint256 rand = _randUint({ - rand: keccak256(abi.encodePacked(bytes32(i), randSalt)), - min: 0, - max: 1 - }); + uint256 rand = + _randUint({rand: keccak256(abi.encodePacked(bytes32(i), randSalt)), min: 0, max: 1}); // Roll block number currBlockNumber += 10; @@ -326,7 +316,7 @@ contract IndexRegistryUnitTests_configAndGetters is IndexRegistryUnitTests { _deregisterOperatorSingleQuorum(operatorId, defaultQuorumNumber); currRegisteredOperatorIds[currIndex] = operatorId; currIndex--; - // register operator + // register operator } else { (, bytes32 operatorId) = _selectNewOperator(); _registerOperatorSingleQuorum(operatorId, defaultQuorumNumber); @@ -336,16 +326,10 @@ contract IndexRegistryUnitTests_configAndGetters is IndexRegistryUnitTests { } // Check operator list - bytes32[] memory operatorList = indexRegistry.getOperatorListAtBlockNumber( - defaultQuorumNumber, - currBlockNumber - ); + bytes32[] memory operatorList = + indexRegistry.getOperatorListAtBlockNumber(defaultQuorumNumber, currBlockNumber); - _assertOperatorListsEqual( - operatorList, - currRegisteredOperatorIds, - currIndex - ); + _assertOperatorListsEqual(operatorList, currRegisteredOperatorIds, currIndex); } } @@ -353,15 +337,15 @@ contract IndexRegistryUnitTests_configAndGetters is IndexRegistryUnitTests { * @dev Loop for numOperators randomly registering and deregistering operators for a single quorum. * Checks that the totalOperatorsForQuorum returns the correct total after each register/deregister */ - function testFuzz_TotalOperatorUpdatesForOneQuorum(uint8 numOperators, bytes32 randSalt) public { + function testFuzz_TotalOperatorUpdatesForOneQuorum( + uint8 numOperators, + bytes32 randSalt + ) public { bytes32[] memory currRegisteredOperatorIds = new bytes32[](numOperators); uint256 currIndex = 0; for (uint256 i = 0; i < numOperators; i++) { - uint256 rand = _randUint({ - rand: keccak256(abi.encodePacked(bytes32(i), randSalt)), - min: 0, - max: 1 - }); + uint256 rand = + _randUint({rand: keccak256(abi.encodePacked(bytes32(i), randSalt)), min: 0, max: 1}); // deregister operator, must also have at least one registered operator if (rand == 0 && currIndex > 0) { @@ -369,7 +353,7 @@ contract IndexRegistryUnitTests_configAndGetters is IndexRegistryUnitTests { _deregisterOperatorSingleQuorum(operatorId, defaultQuorumNumber); currRegisteredOperatorIds[currIndex] = operatorId; currIndex--; - // register operator + // register operator } else { (, bytes32 operatorId) = _selectNewOperator(); _registerOperatorSingleQuorum(operatorId, defaultQuorumNumber); @@ -387,7 +371,9 @@ contract IndexRegistryUnitTests_configAndGetters is IndexRegistryUnitTests { } } - function testFuzz_viewFunctions_Revert_WhenInvalidQuorumNumber(uint8 quorumNumber) public { + function testFuzz_viewFunctions_Revert_WhenInvalidQuorumNumber( + uint8 quorumNumber + ) public { cheats.assume(quorumNumber >= nextQuorum); cheats.expectRevert(); @@ -407,18 +393,20 @@ contract IndexRegistryUnitTests_configAndGetters is IndexRegistryUnitTests { contract IndexRegistryUnitTests_registerOperator is IndexRegistryUnitTests { using BitmapUtils for *; - - /******************************************************************************* - UNIT TESTS - REGISTRATION - *******************************************************************************/ - - function testFuzz_Revert_WhenNonRegistryCoordinator(address nonRegistryCoordinator) public { + /** + * + * UNIT TESTS - REGISTRATION + * + */ + function testFuzz_Revert_WhenNonRegistryCoordinator( + address nonRegistryCoordinator + ) public { cheats.assume(nonRegistryCoordinator != address(registryCoordinator)); cheats.assume(nonRegistryCoordinator != proxyAdminOwner); bytes memory quorumNumbers = new bytes(defaultQuorumNumber); cheats.prank(nonRegistryCoordinator); - cheats.expectRevert("IndexRegistry.onlyRegistryCoordinator: caller is not the registry coordinator"); + cheats.expectRevert(IIndexRegistryErrors.OnlyRegistryCoordinator.selector); indexRegistry.registerOperator(bytes32(0), quorumNumbers); } @@ -432,14 +420,15 @@ contract IndexRegistryUnitTests_registerOperator is IndexRegistryUnitTests { cheats.assume(invalidBitmap > initializedQuorumBitmap); // mask out quorums that are already initialized and the quorums that are not going to be registered invalidBitmap = uint192(invalidBitmap.minus(uint256(initializedQuorumBitmap))); - bitmap = uint192(bitmap.minus(uint256(initializedQuorumBitmap)).minus(uint256(invalidBitmap))); + bitmap = + uint192(bitmap.minus(uint256(initializedQuorumBitmap)).minus(uint256(invalidBitmap))); // Initialize fuzzed quorum numbers _initializeFuzzedQuorums(bitmap); // Register for invalid quorums, should revert bytes memory invalidQuorumNumbers = bitmapUtilsWrapper.bitmapToBytesArray(invalidBitmap); cheats.prank(address(registryCoordinator)); - cheats.expectRevert("IndexRegistry.registerOperator: quorum does not exist"); + cheats.expectRevert(IIndexRegistryErrors.QuorumDoesNotExist.selector); indexRegistry.registerOperator(operatorId1, invalidQuorumNumbers); } @@ -456,16 +445,12 @@ contract IndexRegistryUnitTests_registerOperator is IndexRegistryUnitTests { (, bytes32 operatorId) = _selectNewOperator(); uint32 numOperators = _registerOperatorSingleQuorum(operatorId, defaultQuorumNumber); - assertEq( - numOperators, - 1, - "IndexRegistry.registerOperator: numOperators is not 1" - ); + assertEq(numOperators, 1, "IndexRegistry.registerOperator: numOperators is not 1"); // Check _totalOperatorsHistory updates _assertQuorumUpdate({ quorumNumber: defaultQuorumNumber, - expectedNumOperators: 1, + expectedNumOperators: 1, expectedFromBlockNumber: block.number }); // Check _indexHistory updates @@ -509,7 +494,7 @@ contract IndexRegistryUnitTests_registerOperator is IndexRegistryUnitTests { // Check _totalOperatorsHistory and _indexHistory updates for quorum 1 _assertQuorumUpdate({ quorumNumber: defaultQuorumNumber, - expectedNumOperators: 1, + expectedNumOperators: 1, expectedFromBlockNumber: block.number }); _assertOperatorUpdate({ @@ -523,7 +508,7 @@ contract IndexRegistryUnitTests_registerOperator is IndexRegistryUnitTests { // Check _totalOperatorsHistory and _indexHistory updates for quorum 2 _assertQuorumUpdate({ quorumNumber: defaultQuorumNumber + 1, - expectedNumOperators: 1, + expectedNumOperators: 1, expectedFromBlockNumber: block.number }); _assertOperatorUpdate({ @@ -550,11 +535,7 @@ contract IndexRegistryUnitTests_registerOperator is IndexRegistryUnitTests { uint32 numOperators = _registerOperatorSingleQuorum(operatorId2, defaultQuorumNumber); // Check return value - assertEq( - numOperators, - 2, - "IndexRegistry.registerOperator: numOperators not 2" - ); + assertEq(numOperators, 2, "IndexRegistry.registerOperator: numOperators not 2"); // Check _totalOperatorsHistory and _indexHistory updates for quorum _assertOperatorUpdate({ @@ -573,7 +554,7 @@ contract IndexRegistryUnitTests_registerOperator is IndexRegistryUnitTests { }); _assertQuorumUpdate({ quorumNumber: defaultQuorumNumber, - expectedNumOperators: 2, + expectedNumOperators: 2, expectedFromBlockNumber: block.number }); } @@ -586,7 +567,9 @@ contract IndexRegistryUnitTests_registerOperator is IndexRegistryUnitTests { * 4. quorumNumbers.length != 0 * 5. operator is not already registerd for any quorums being registered for */ - function testFuzz_registerOperator_MultipleQuorums(uint192 bitmap) public { + function testFuzz_registerOperator_MultipleQuorums( + uint192 bitmap + ) public { // mask out quorums that are already initialized bitmap = uint192(bitmap.minus(uint256(initializedQuorumBitmap))); bytes memory quorumNumbers = bitmapUtilsWrapper.bitmapToBytesArray(bitmap); @@ -619,7 +602,7 @@ contract IndexRegistryUnitTests_registerOperator is IndexRegistryUnitTests { }); _assertQuorumUpdate({ quorumNumber: uint8(quorumNumbers[i]), - expectedNumOperators: 1, + expectedNumOperators: 1, expectedFromBlockNumber: block.number }); } @@ -670,7 +653,7 @@ contract IndexRegistryUnitTests_registerOperator is IndexRegistryUnitTests { }); _assertQuorumUpdate({ quorumNumber: uint8(quorumNumbers[j]), - expectedNumOperators: i + 1, + expectedNumOperators: i + 1, expectedFromBlockNumber: block.number }); } @@ -681,7 +664,11 @@ contract IndexRegistryUnitTests_registerOperator is IndexRegistryUnitTests { 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"); + assertEq( + quorumUpdate.fromBlockNumber, + block.number, + "latest update should be from current block number" + ); } } } @@ -689,17 +676,21 @@ contract IndexRegistryUnitTests_registerOperator is IndexRegistryUnitTests { contract IndexRegistryUnitTests_deregisterOperator is IndexRegistryUnitTests { using BitmapUtils for *; - /******************************************************************************* - UNIT TESTS - DEREGISTRATION - *******************************************************************************/ - function testFuzz_Revert_WhenNonRegistryCoordinator(address nonRegistryCoordinator) public { + /** + * + * UNIT TESTS - DEREGISTRATION + * + */ + function testFuzz_Revert_WhenNonRegistryCoordinator( + address nonRegistryCoordinator + ) public { cheats.assume(nonRegistryCoordinator != address(registryCoordinator)); cheats.assume(nonRegistryCoordinator != address(proxyAdmin)); // de-register an operator bytes memory quorumNumbers = new bytes(defaultQuorumNumber); cheats.prank(nonRegistryCoordinator); - cheats.expectRevert("IndexRegistry.onlyRegistryCoordinator: caller is not the registry coordinator"); + cheats.expectRevert(IIndexRegistryErrors.OnlyRegistryCoordinator.selector); indexRegistry.deregisterOperator(bytes32(0), quorumNumbers); } @@ -713,7 +704,8 @@ contract IndexRegistryUnitTests_deregisterOperator is IndexRegistryUnitTests { cheats.assume(invalidBitmap > initializedQuorumBitmap); // mask out quorums that are already initialized and the quorums that are not going to be registered invalidBitmap = uint192(invalidBitmap.minus(uint256(initializedQuorumBitmap))); - bitmap = uint192(bitmap.minus(uint256(initializedQuorumBitmap)).minus(uint256(invalidBitmap))); + bitmap = + uint192(bitmap.minus(uint256(initializedQuorumBitmap)).minus(uint256(invalidBitmap))); bytes memory quorumNumbers = bitmapUtilsWrapper.bitmapToBytesArray(bitmap); bytes memory invalidQuorumNumbers = bitmapUtilsWrapper.bitmapToBytesArray(invalidBitmap); _initializeFuzzedQuorums(bitmap); @@ -724,7 +716,7 @@ contract IndexRegistryUnitTests_deregisterOperator is IndexRegistryUnitTests { // Deregister for invalid quorums, should revert cheats.prank(address(registryCoordinator)); - cheats.expectRevert("IndexRegistry.registerOperator: quorum does not exist"); + cheats.expectRevert(IIndexRegistryErrors.QuorumDoesNotExist.selector); indexRegistry.deregisterOperator(operatorId1, invalidQuorumNumbers); } @@ -761,7 +753,7 @@ contract IndexRegistryUnitTests_deregisterOperator is IndexRegistryUnitTests { // Check total operators _assertQuorumUpdate({ quorumNumber: defaultQuorumNumber, - expectedNumOperators: 0, + expectedNumOperators: 0, expectedFromBlockNumber: block.number }); } @@ -800,7 +792,7 @@ contract IndexRegistryUnitTests_deregisterOperator is IndexRegistryUnitTests { // Check total operators _assertQuorumUpdate({ quorumNumber: defaultQuorumNumber, - expectedNumOperators: 0, + expectedNumOperators: 0, expectedFromBlockNumber: block.number }); } @@ -830,10 +822,15 @@ contract IndexRegistryUnitTests_deregisterOperator is IndexRegistryUnitTests { // get operator index, if operator index is new quorumCount // then other operator indexes are unchanged // otherwise the popped index operatorId will replace the deregistered operator's index - uint32 operatorIndex = IndexRegistry(address(indexRegistry)).currentOperatorIndex(quorumNumber, operatorId); - uint32 quorumCountBefore = indexRegistry.getLatestQuorumUpdate(quorumNumber).numOperators; - - assertTrue(operatorIndex <= quorumCountBefore - 1, "operator index should be less than quorumCount"); + uint32 operatorIndex = + IndexRegistry(address(indexRegistry)).currentOperatorIndex(quorumNumber, operatorId); + uint32 quorumCountBefore = + indexRegistry.getLatestQuorumUpdate(quorumNumber).numOperators; + + assertTrue( + operatorIndex <= quorumCountBefore - 1, + "operator index should be less than quorumCount" + ); bytes32 operatorIdAtBeforeQuorumCount = indexRegistry.getLatestOperatorUpdate({ quorumNumber: quorumNumber, operatorIndex: quorumCountBefore - 1 @@ -847,7 +844,11 @@ contract IndexRegistryUnitTests_deregisterOperator is IndexRegistryUnitTests { _deregisterOperatorSingleQuorum(operatorId, quorumNumber); if (operatorIndex != quorumCountBefore - 1) { - assertNotEq(operatorIdAtBeforeQuorumCount, operatorId, "operatorId at currentQuorumCount - 1 should not be operatorId we are deregistering"); + assertNotEq( + operatorIdAtBeforeQuorumCount, + operatorId, + "operatorId at currentQuorumCount - 1 should not be operatorId we are deregistering" + ); _assertOperatorUpdate({ quorumNumber: quorumNumber, operatorIndex: operatorIndex, @@ -897,10 +898,15 @@ contract IndexRegistryUnitTests_deregisterOperator is IndexRegistryUnitTests { // get operator index, if operator index is new quorumCount // then other operator indexes are unchanged // otherwise the popped index operatorId will replace the deregistered operator's index - uint32 operatorIndex = IndexRegistry(address(indexRegistry)).currentOperatorIndex(quorumNumber, operatorId); - uint32 quorumCountBefore = indexRegistry.getLatestQuorumUpdate(quorumNumber).numOperators; - - assertTrue(operatorIndex <= quorumCountBefore - 1, "operator index should be less than quorumCount"); + uint32 operatorIndex = + IndexRegistry(address(indexRegistry)).currentOperatorIndex(quorumNumber, operatorId); + uint32 quorumCountBefore = + indexRegistry.getLatestQuorumUpdate(quorumNumber).numOperators; + + assertTrue( + operatorIndex <= quorumCountBefore - 1, + "operator index should be less than quorumCount" + ); bytes32 operatorIdAtBeforeQuorumCount = indexRegistry.getLatestOperatorUpdate({ quorumNumber: quorumNumber, operatorIndex: quorumCountBefore - 1 @@ -933,8 +939,8 @@ contract IndexRegistryUnitTests_deregisterOperator is IndexRegistryUnitTests { /** * @dev Test deregistering an operator with multiple operators already registered. * We deregister the operator with arrayIndex 0 and check that the operator with arrayIndex 2 - * ends up getting swapped with the deregistering operator. - * + * ends up getting swapped with the deregistering operator. + * * Also checking QuorumUpdates and OperatorUpdates as well. */ function test_deregisterOperator_MultipleQuorums() public { @@ -984,13 +990,15 @@ contract IndexRegistryUnitTests_deregisterOperator is IndexRegistryUnitTests { _assertQuorumUpdate({ quorumNumber: uint8(quorumsToRemove[i]), - expectedNumOperators: 2, + expectedNumOperators: 2, expectedFromBlockNumber: block.number }); IIndexRegistry.OperatorUpdate memory operatorUpdate = indexRegistry .getLatestOperatorUpdate({quorumNumber: uint8(quorumsToRemove[i]), operatorIndex: 0}); - assertEq(operatorUpdate.fromBlockNumber, block.number, "fromBlockNumber not set correctly"); + assertEq( + operatorUpdate.fromBlockNumber, block.number, "fromBlockNumber not set correctly" + ); assertEq(operatorUpdate.operatorId, operatorId3, "incorrect operatorId"); } } @@ -1012,7 +1020,7 @@ contract IndexRegistryUnitTests_deregisterOperator is IndexRegistryUnitTests { // mask out quorums that are already initialized uint192 bitmap = uint192(bitmapToRegister.minus(uint256(initializedQuorumBitmap))); _initializeFuzzedQuorums(bitmap); - + bytes memory quorumNumbers = bitmapUtilsWrapper.bitmapToBytesArray(bitmapToRegister); bytes memory quorumsToRemove = bitmapUtilsWrapper.bitmapToBytesArray(bitmapToDeregister); @@ -1028,7 +1036,7 @@ contract IndexRegistryUnitTests_deregisterOperator is IndexRegistryUnitTests { // Check total operators for removed quorums _assertQuorumUpdate({ quorumNumber: uint8(quorumsToRemove[i]), - expectedNumOperators: 1, + expectedNumOperators: 1, expectedFromBlockNumber: block.number }); // Check swapped operator's index for removed quorums @@ -1043,8 +1051,10 @@ contract IndexRegistryUnitTests_deregisterOperator is IndexRegistryUnitTests { // Check operator's index for removed quorums IIndexRegistry.OperatorUpdate memory operatorUpdate = indexRegistry .getLatestOperatorUpdate({quorumNumber: uint8(quorumsToRemove[i]), operatorIndex: 1}); - assertEq(operatorUpdate.fromBlockNumber, block.number, "fromBlockNumber not set correctly"); + assertEq( + operatorUpdate.fromBlockNumber, block.number, "fromBlockNumber not set correctly" + ); assertEq(operatorUpdate.operatorId, bytes32(0), "incorrect operatorId"); } } -} \ No newline at end of file +} diff --git a/test/unit/LibMergeSort.t.sol b/test/unit/LibMergeSort.t.sol new file mode 100644 index 00000000..53031359 --- /dev/null +++ b/test/unit/LibMergeSort.t.sol @@ -0,0 +1,187 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.27; + +import "forge-std/Test.sol"; +import "../../src/libraries/LibMergeSort.sol"; + +contract LibMergeSortTest is Test { + using LibMergeSort for address[]; + + function testMergeSortArrays() public { + address[] memory left = new address[](3); + address[] memory right = new address[](3); + + left[0] = address(0x1); + left[1] = address(0x3); + left[2] = address(0x5); + + right[0] = address(0x2); + right[1] = address(0x4); + right[2] = address(0x6); + + address[] memory expected = new address[](6); + expected[0] = address(0x1); + expected[1] = address(0x2); + expected[2] = address(0x3); + expected[3] = address(0x4); + expected[4] = address(0x5); + expected[5] = address(0x6); + + address[] memory result = left.mergeSortArrays(right); + + for (uint256 i = 0; i < expected.length; i++) { + assertEq(result[i], expected[i], "Array elements are not sorted correctly"); + } + } + + function testMergeSortArraysWithDuplicates() public { + address[] memory left = new address[](3); + address[] memory right = new address[](3); + + left[0] = address(0x1); + left[1] = address(0x3); + left[2] = address(0x5); + + right[0] = address(0x1); + right[1] = address(0x3); + right[2] = address(0x5); + + address[] memory expected = new address[](3); + expected[0] = address(0x1); + expected[1] = address(0x3); + expected[2] = address(0x5); + + address[] memory result = left.mergeSortArrays(right); + assertEq(expected, result, "Not sorted"); + } + + function testMergeSortArraysWithEmptyLeft() public { + address[] memory left = new address[](0); + address[] memory right = new address[](3); + + right[0] = address(0x2); + right[1] = address(0x4); + right[2] = address(0x6); + + address[] memory expected = new address[](3); + expected[0] = address(0x2); + expected[1] = address(0x4); + expected[2] = address(0x6); + + address[] memory result = left.mergeSortArrays(right); + + for (uint256 i = 0; i < expected.length; i++) { + assertEq(result[i], expected[i], "Array elements are not sorted correctly"); + } + } + + function testMergeSortArraysWithEmptyRight() public { + address[] memory left = new address[](3); + address[] memory right = new address[](0); + + left[0] = address(0x1); + left[1] = address(0x3); + left[2] = address(0x5); + + address[] memory expected = new address[](3); + expected[0] = address(0x1); + expected[1] = address(0x3); + expected[2] = address(0x5); + + address[] memory result = left.mergeSortArrays(right); + + for (uint256 i = 0; i < expected.length; i++) { + assertEq(result[i], expected[i], "Array elements are not sorted correctly"); + } + } + + function testMergeSortArrays_Sort() public { + address[] memory left = new address[](3); + address[] memory right = new address[](3); + + left[0] = address(0x3); + left[1] = address(0x1); + left[2] = address(0x2); + + right[0] = address(0x6); + right[1] = address(0x4); + right[2] = address(0x5); + + left = left.sort(); + right = right.sort(); + + address[] memory expected = new address[](6); + expected[0] = address(0x1); + expected[1] = address(0x2); + expected[2] = address(0x3); + expected[3] = address(0x4); + expected[4] = address(0x5); + expected[5] = address(0x6); + + address[] memory result = left.mergeSortArrays(right); + + for (uint256 i = 0; i < expected.length; i++) { + assertEq(result[i], expected[i], "Array elements are not sorted correctly"); + } + } + + /// NOTE: we're assuming the input arrays themselves are unique. + /// Demonstrating behavior of library + function testMergeSortArraysWithDuplicateInLeft() public { + address[] memory left = new address[](4); + address[] memory right = new address[](3); + + left[0] = address(0x1); + left[1] = address(0x3); + left[2] = address(0x3); // Duplicate + left[3] = address(0x5); + + right[0] = address(0x2); + right[1] = address(0x4); + right[2] = address(0x6); + + address[] memory expected = new address[](7); + expected[0] = address(0x1); + expected[1] = address(0x2); + expected[2] = address(0x3); + expected[3] = address(0x3); + expected[4] = address(0x4); + expected[5] = address(0x5); + expected[6] = address(0x6); + + address[] memory result = left.mergeSortArrays(right); + + for (uint256 i = 0; i < expected.length; i++) { + assertEq(result[i], expected[i], "Array elements are not sorted correctly"); + } + } + + function testMergeSortArraysWithDuplicateInRight() public { + address[] memory left = new address[](3); + address[] memory right = new address[](4); + + left[0] = address(0x1); + left[1] = address(0x3); + left[2] = address(0x5); + + right[0] = address(0x2); + right[1] = address(0x4); + right[2] = address(0x4); // Duplicate + right[3] = address(0x6); + + address[] memory expected = new address[](7); + expected[0] = address(0x1); + expected[1] = address(0x2); + expected[2] = address(0x3); + expected[3] = address(0x4); + expected[4] = address(0x4); + expected[5] = address(0x5); + expected[6] = address(0x6); + + address[] memory result = left.mergeSortArrays(right); + + for (uint256 i = 0; i < expected.length; i++) { + assertEq(result[i], expected[i], "Array elements are not sorted correctly"); + } + } +} diff --git a/test/unit/OperatorStateRetrieverUnit.t.sol b/test/unit/OperatorStateRetrieverUnit.t.sol index f79c345c..79a93988 100644 --- a/test/unit/OperatorStateRetrieverUnit.t.sol +++ b/test/unit/OperatorStateRetrieverUnit.t.sol @@ -1,7 +1,9 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "../utils/MockAVSDeployer.sol"; +import {IStakeRegistryErrors} from "../../src/interfaces/IStakeRegistry.sol"; +import {ISlashingRegistryCoordinatorTypes} from "../../src/interfaces/IRegistryCoordinator.sol"; contract OperatorStateRetrieverUnitTests is MockAVSDeployer { using BN254 for BN254.G1Point; @@ -13,7 +15,7 @@ contract OperatorStateRetrieverUnitTests is MockAVSDeployer { function test_getOperatorState_revert_neverRegistered() public { cheats.expectRevert( - "RegCoord.getQuorumBitmapIndexAtBlockNumber: no bitmap update found for operator at blockNumber" + "RegistryCoordinator.getQuorumBitmapIndexAtBlockNumber: no bitmap update found for operatorId" ); operatorStateRetriever.getOperatorState( registryCoordinator, defaultOperatorId, uint32(block.number) @@ -26,7 +28,7 @@ contract OperatorStateRetrieverUnitTests is MockAVSDeployer { // should revert because the operator was registered for the first time after the reference block number cheats.expectRevert( - "RegCoord.getQuorumBitmapIndexAtBlockNumber: no bitmap update found for operator at blockNumber" + "RegistryCoordinator.getQuorumBitmapIndexAtBlockNumber: no bitmap update found for operatorId" ); operatorStateRetriever.getOperatorState( registryCoordinator, defaultOperatorId, registrationBlockNumber - 1 @@ -80,20 +82,24 @@ contract OperatorStateRetrieverUnitTests is MockAVSDeployer { function test_getOperatorState_revert_quorumNotCreatedAtReferenceBlockNumber() public { cheats.roll(registrationBlockNumber); - IRegistryCoordinator.OperatorSetParam memory operatorSetParams = IRegistryCoordinator - .OperatorSetParam({ + ISlashingRegistryCoordinatorTypes.OperatorSetParam memory operatorSetParams = + ISlashingRegistryCoordinatorTypes.OperatorSetParam({ maxOperatorCount: defaultMaxOperatorCount, kickBIPsOfOperatorStake: defaultKickBIPsOfOperatorStake, kickBIPsOfTotalStake: defaultKickBIPsOfTotalStake }); uint96 minimumStake = 1; - IStakeRegistry.StrategyParams[] memory strategyParams = - new IStakeRegistry.StrategyParams[](1); - strategyParams[0] = - IStakeRegistry.StrategyParams({strategy: IStrategy(address(1000)), multiplier: 1e16}); + IStakeRegistryTypes.StrategyParams[] memory strategyParams = + new IStakeRegistryTypes.StrategyParams[](1); + strategyParams[0] = IStakeRegistryTypes.StrategyParams({ + strategy: IStrategy(address(1000)), + multiplier: 1e16 + }); cheats.prank(registryCoordinator.owner()); - registryCoordinator.createQuorum(operatorSetParams, minimumStake, strategyParams); + registryCoordinator.createTotalDelegatedStakeQuorum( + operatorSetParams, minimumStake, strategyParams + ); cheats.expectRevert( "IndexRegistry._operatorCountAtBlockNumber: quorum did not exist at given block number" @@ -143,7 +149,7 @@ contract OperatorStateRetrieverUnitTests is MockAVSDeployer { nonSignerOperatorIds[0] = defaultOperatorId; cheats.expectRevert( - "RegCoord.getQuorumBitmapIndexAtBlockNumber: no bitmap update found for operator at blockNumber" + "RegistryCoordinator.getQuorumBitmapIndexAtBlockNumber: no bitmap update found for operatorId" ); operatorStateRetriever.getCheckSignaturesIndices( registryCoordinator, @@ -164,7 +170,7 @@ contract OperatorStateRetrieverUnitTests is MockAVSDeployer { // should revert because the operator was registered for the first time after the reference block number cheats.expectRevert( - "RegCoord.getQuorumBitmapIndexAtBlockNumber: no bitmap update found for operator at blockNumber" + "RegistryCoordinator.getQuorumBitmapIndexAtBlockNumber: no bitmap update found for operatorId" ); operatorStateRetriever.getCheckSignaturesIndices( registryCoordinator, @@ -186,9 +192,7 @@ contract OperatorStateRetrieverUnitTests is MockAVSDeployer { registryCoordinator.deregisterOperator(BitmapUtils.bitmapToBytesArray(1)); // should revert because the operator was registered for the first time after the reference block number - cheats.expectRevert( - "OperatorStateRetriever.getCheckSignaturesIndices: operator must be registered at blocknumber" - ); + cheats.expectRevert(OperatorStateRetriever.OperatorNotRegistered.selector); operatorStateRetriever.getCheckSignaturesIndices( registryCoordinator, uint32(block.number), @@ -203,7 +207,7 @@ contract OperatorStateRetrieverUnitTests is MockAVSDeployer { _registerOperatorWithCoordinator(defaultOperator, 1, defaultPubKey); - cheats.expectRevert("StakeRegistry.quorumExists: quorum does not exist"); + cheats.expectRevert(IStakeRegistryErrors.QuorumDoesNotExist.selector); operatorStateRetriever.getCheckSignaturesIndices( registryCoordinator, uint32(block.number), @@ -222,24 +226,26 @@ contract OperatorStateRetrieverUnitTests is MockAVSDeployer { bytes32[] memory nonSignerOperatorIds = new bytes32[](1); nonSignerOperatorIds[0] = defaultOperatorId; - IRegistryCoordinator.OperatorSetParam memory operatorSetParams = IRegistryCoordinator - .OperatorSetParam({ + ISlashingRegistryCoordinatorTypes.OperatorSetParam memory operatorSetParams = + ISlashingRegistryCoordinatorTypes.OperatorSetParam({ maxOperatorCount: defaultMaxOperatorCount, kickBIPsOfOperatorStake: defaultKickBIPsOfOperatorStake, kickBIPsOfTotalStake: defaultKickBIPsOfTotalStake }); uint96 minimumStake = 1; - IStakeRegistry.StrategyParams[] memory strategyParams = - new IStakeRegistry.StrategyParams[](1); - strategyParams[0] = - IStakeRegistry.StrategyParams({strategy: IStrategy(address(1000)), multiplier: 1e16}); + IStakeRegistryTypes.StrategyParams[] memory strategyParams = + new IStakeRegistryTypes.StrategyParams[](1); + strategyParams[0] = IStakeRegistryTypes.StrategyParams({ + strategy: IStrategy(address(1000)), + multiplier: 1e16 + }); cheats.prank(registryCoordinator.owner()); - registryCoordinator.createQuorum(operatorSetParams, minimumStake, strategyParams); - - cheats.expectRevert( - "StakeRegistry.getTotalStakeIndicesAtBlockNumber: quorum has no stake history at blockNumber" + registryCoordinator.createTotalDelegatedStakeQuorum( + operatorSetParams, minimumStake, strategyParams ); + + cheats.expectRevert(IStakeRegistryErrors.EmptyStakeHistory.selector); operatorStateRetriever.getCheckSignaturesIndices( registryCoordinator, registrationBlockNumber + 5, @@ -253,6 +259,10 @@ contract OperatorStateRetrieverUnitTests is MockAVSDeployer { uint256 quorumBitmapTwo = 2; uint256 quorumBitmapThree = 3; + assertFalse( + registryCoordinator.operatorSetsEnabled(), "operatorSetsEnabled should be false" + ); + cheats.roll(registrationBlockNumber); _registerOperatorWithCoordinator(defaultOperator, quorumBitmapOne, defaultPubKey); @@ -359,7 +369,9 @@ contract OperatorStateRetrieverUnitTests is MockAVSDeployer { assertEq(checkSignaturesIndices.nonSignerStakeIndices[1][0], 0); } - function testGetOperatorState_Valid(uint256 pseudoRandomNumber) public { + function testGetOperatorState_Valid( + uint256 pseudoRandomNumber + ) public { // register random operators and get the expected indices within the quorums and the metadata for the operators ( OperatorMetadata[] memory operatorMetadatas, @@ -428,7 +440,9 @@ contract OperatorStateRetrieverUnitTests is MockAVSDeployer { ); } - function testCheckSignaturesIndices_NoNonSigners_Valid(uint256 pseudoRandomNumber) public { + function testCheckSignaturesIndices_NoNonSigners_Valid( + uint256 pseudoRandomNumber + ) public { ( OperatorMetadata[] memory operatorMetadatas, uint256[][] memory expectedOperatorOverallIndices @@ -493,7 +507,9 @@ contract OperatorStateRetrieverUnitTests is MockAVSDeployer { } } - function testCheckSignaturesIndices_FewNonSigners_Valid(uint256 pseudoRandomNumber) public { + function testCheckSignaturesIndices_FewNonSigners_Valid( + uint256 pseudoRandomNumber + ) public { ( OperatorMetadata[] memory operatorMetadatas, uint256[][] memory expectedOperatorOverallIndices diff --git a/test/unit/RegistryCoordinatorUnit.t.sol b/test/unit/RegistryCoordinatorUnit.t.sol index cd67e7a2..a734171a 100644 --- a/test/unit/RegistryCoordinatorUnit.t.sol +++ b/test/unit/RegistryCoordinatorUnit.t.sol @@ -1,10 +1,21 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "../utils/MockAVSDeployer.sol"; +import { + ISlashingRegistryCoordinator, + ISlashingRegistryCoordinatorTypes, + ISlashingRegistryCoordinatorErrors +} from "../../src/interfaces/ISlashingRegistryCoordinator.sol"; + +import {IBLSApkRegistryTypes} from "../../src/interfaces/IBLSApkRegistry.sol"; +import {QuorumBitmapHistoryLib} from "../../src/libraries/QuorumBitmapHistoryLib.sol"; +import {BitmapUtils} from "../../src/libraries/BitmapUtils.sol"; +import {console} from "forge-std/console.sol"; contract RegistryCoordinatorUnitTests is MockAVSDeployer { using BN254 for BN254.G1Point; + using stdStorage for StdStorage; uint8 internal constant PAUSED_REGISTER_OPERATOR = 0; uint8 internal constant PAUSED_DEREGISTER_OPERATOR = 1; @@ -19,30 +30,21 @@ contract RegistryCoordinatorUnitTests is MockAVSDeployer { event OperatorSocketUpdate(bytes32 indexed operatorId, string socket); /// @notice emitted whenever the stake of `operator` is updated - event OperatorStakeUpdate( - bytes32 indexed operatorId, - uint8 quorumNumber, - uint96 stake - ); + event OperatorStakeUpdate(bytes32 indexed operatorId, uint8 quorumNumber, uint96 stake); // Emitted when a new operator pubkey is registered for a set of quorums - event OperatorAddedToQuorums( - address operator, - bytes32 operatorId, - bytes quorumNumbers - ); + event OperatorAddedToQuorums(address operator, bytes32 operatorId, bytes quorumNumbers); // Emitted when an operator pubkey is removed from a set of quorums - event OperatorRemovedFromQuorums( - address operator, - bytes32 operatorId, - bytes quorumNumbers - ); + event OperatorRemovedFromQuorums(address operator, bytes32 operatorId, bytes quorumNumbers); // emitted when an operator's index in the orderd operator list for the quorum with number `quorumNumber` is updated event QuorumIndexUpdate(bytes32 indexed operatorId, uint8 quorumNumber, uint32 newIndex); - event OperatorSetParamsUpdated(uint8 indexed quorumNumber, IRegistryCoordinator.OperatorSetParam operatorSetParams); + event OperatorSetParamsUpdated( + uint8 indexed quorumNumber, + ISlashingRegistryCoordinatorTypes.OperatorSetParam operatorSetParams + ); event ChurnApproverUpdated(address prevChurnApprover, address newChurnApprover); @@ -50,7 +52,7 @@ contract RegistryCoordinatorUnitTests is MockAVSDeployer { event QuorumBlockNumberUpdated(uint8 indexed quorumNumber, uint256 blocknumber); - function setUp() virtual public { + function setUp() public virtual { _deployMockEigenLayerAndAVS(numQuorums); } @@ -58,45 +60,54 @@ contract RegistryCoordinatorUnitTests is MockAVSDeployer { uint256 pseudoRandomNumber, bytes memory quorumNumbers, uint96 operatorToKickStake - ) internal returns( - address operatorToRegister, - BN254.G1Point memory operatorToRegisterPubKey, - IRegistryCoordinator.OperatorKickParam[] memory operatorKickParams - ) { + ) + internal + returns ( + address operatorToRegister, + BN254.G1Point memory operatorToRegisterPubKey, + ISlashingRegistryCoordinatorTypes.OperatorKickParam[] memory operatorKickParams + ) + { uint32 kickRegistrationBlockNumber = 100; uint256 quorumBitmap = BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers); cheats.roll(kickRegistrationBlockNumber); - for (uint i = 0; i < defaultMaxOperatorCount - 1; i++) { - BN254.G1Point memory pubKey = BN254.hashToG1(keccak256(abi.encodePacked(pseudoRandomNumber, i))); + for (uint256 i = 0; i < defaultMaxOperatorCount - 1; i++) { + BN254.G1Point memory pubKey = + BN254.hashToG1(keccak256(abi.encodePacked(pseudoRandomNumber, i))); address operator = _incrementAddress(defaultOperator, i); - + _registerOperatorWithCoordinator(operator, quorumBitmap, pubKey); } operatorToRegister = _incrementAddress(defaultOperator, defaultMaxOperatorCount); - operatorToRegisterPubKey = BN254.hashToG1(keccak256(abi.encodePacked(pseudoRandomNumber, defaultMaxOperatorCount))); + operatorToRegisterPubKey = + BN254.hashToG1(keccak256(abi.encodePacked(pseudoRandomNumber, defaultMaxOperatorCount))); bytes32 operatorToRegisterId = BN254.hashG1Point(operatorToRegisterPubKey); bytes32 operatorToKickId; address operatorToKick; - + // register last operator before kick - operatorKickParams = new IRegistryCoordinator.OperatorKickParam[](1); + operatorKickParams = new ISlashingRegistryCoordinatorTypes.OperatorKickParam[](1); { - BN254.G1Point memory pubKey = BN254.hashToG1(keccak256(abi.encodePacked(pseudoRandomNumber, defaultMaxOperatorCount - 1))); + BN254.G1Point memory pubKey = BN254.hashToG1( + keccak256(abi.encodePacked(pseudoRandomNumber, defaultMaxOperatorCount - 1)) + ); operatorToKickId = BN254.hashG1Point(pubKey); operatorToKick = _incrementAddress(defaultOperator, defaultMaxOperatorCount - 1); // register last operator with much more than the kickBIPsOfTotalStake stake - _registerOperatorWithCoordinator(operatorToKick, quorumBitmap, pubKey, operatorToKickStake); + _registerOperatorWithCoordinator( + operatorToKick, quorumBitmap, pubKey, operatorToKickStake + ); bytes32[] memory operatorIdsToSwap = new bytes32[](1); // operatorIdsToSwap[0] = operatorToRegisterId operatorIdsToSwap[0] = operatorToRegisterId; - operatorKickParams[0] = IRegistryCoordinator.OperatorKickParam({ + operatorKickParams[0] = ISlashingRegistryCoordinatorTypes.OperatorKickParam({ quorumNumber: uint8(quorumNumbers[0]), operator: operatorToKick }); @@ -113,24 +124,21 @@ contract RegistryCoordinatorUnitTests_Initialization_Setters is RegistryCoordina assertEq(address(registryCoordinator.indexRegistry()), address(indexRegistry)); assertEq(address(registryCoordinator.serviceManager()), address(serviceManager)); - for (uint i = 0; i < numQuorums; i++) { + for (uint256 i = 0; i < numQuorums; i++) { assertEq( - keccak256(abi.encode(registryCoordinator.getOperatorSetParams(uint8(i)))), + keccak256(abi.encode(registryCoordinator.getOperatorSetParams(uint8(i)))), keccak256(abi.encode(operatorSetParams[i])) ); } - // make sure the contract intializers are disabled + // make sure the contract initializers are disabled cheats.expectRevert(bytes("Initializable: contract is already initialized")); registryCoordinator.initialize( registryCoordinatorOwner, - churnApprover, - ejector, - pauserRegistry, - 0/*initialPausedStatus*/, - operatorSetParams, - new uint96[](0), - new IStakeRegistry.StrategyParams[][](0) + churnApprover, + ejector, + 0, // _initialPausedStatus + address(serviceManager) // _accountIdentifier ); } @@ -139,8 +147,11 @@ contract RegistryCoordinatorUnitTests_Initialization_Setters is RegistryCoordina cheats.expectEmit(true, true, true, true, address(registryCoordinator)); emit OperatorSetParamsUpdated(0, operatorSetParams[1]); registryCoordinator.setOperatorSetParams(0, operatorSetParams[1]); - assertEq(keccak256(abi.encode(registryCoordinator.getOperatorSetParams(0))),keccak256(abi.encode(operatorSetParams[1])), - "operator set params not updated correctly"); + assertEq( + keccak256(abi.encode(registryCoordinator.getOperatorSetParams(0))), + keccak256(abi.encode(operatorSetParams[1])), + "operator set params not updated correctly" + ); } function test_setOperatorSetParams_revert_notOwner() public { @@ -192,23 +203,24 @@ contract RegistryCoordinatorUnitTests_Initialization_Setters is RegistryCoordina cheats.expectEmit(true, true, true, true, address(registryCoordinator)); emit OperatorSocketUpdate(defaultOperatorId, "localhost:32004"); registryCoordinator.updateSocket("localhost:32004"); - assertEq(socketRegistry.getOperatorSocket(defaultOperatorId), "localhost:32004"); } function test_updateSocket_revert_notRegistered() public { cheats.prank(defaultOperator); - cheats.expectRevert("RegCoord.updateSocket: operator not registered"); + cheats.expectRevert(bytes4(keccak256("NotRegistered()"))); registryCoordinator.updateSocket("localhost:32004"); } function test_createQuorum_revert_notOwner() public { - IRegistryCoordinator.OperatorSetParam memory operatorSetParams; + ISlashingRegistryCoordinatorTypes.OperatorSetParam memory operatorSetParams; uint96 minimumStake; - IStakeRegistry.StrategyParams[] memory strategyParams; + IStakeRegistryTypes.StrategyParams[] memory strategyParams; cheats.expectRevert("Ownable: caller is not the owner"); cheats.prank(defaultOperator); - registryCoordinator.createQuorum(operatorSetParams, minimumStake, strategyParams); + registryCoordinator.createTotalDelegatedStakeQuorum( + operatorSetParams, minimumStake, strategyParams + ); } function test_createQuorum() public { @@ -216,26 +228,28 @@ contract RegistryCoordinatorUnitTests_Initialization_Setters is RegistryCoordina // this is necessary since the default setup already configures the max number of quorums, preventing adding more _deployMockEigenLayerAndAVS(0); - IRegistryCoordinator.OperatorSetParam memory operatorSetParams = - IRegistryCoordinator.OperatorSetParam({ - maxOperatorCount: defaultMaxOperatorCount, - kickBIPsOfOperatorStake: defaultKickBIPsOfOperatorStake, - kickBIPsOfTotalStake: defaultKickBIPsOfTotalStake - }); + ISlashingRegistryCoordinatorTypes.OperatorSetParam memory operatorSetParams = + ISlashingRegistryCoordinatorTypes.OperatorSetParam({ + maxOperatorCount: defaultMaxOperatorCount, + kickBIPsOfOperatorStake: defaultKickBIPsOfOperatorStake, + kickBIPsOfTotalStake: defaultKickBIPsOfTotalStake + }); uint96 minimumStake = 1; - IStakeRegistry.StrategyParams[] memory strategyParams = new IStakeRegistry.StrategyParams[](1); - strategyParams[0] = - IStakeRegistry.StrategyParams({ - strategy: IStrategy(address(1000)), - multiplier: 1e16 - }); + IStakeRegistryTypes.StrategyParams[] memory strategyParams = + new IStakeRegistryTypes.StrategyParams[](1); + strategyParams[0] = IStakeRegistryTypes.StrategyParams({ + strategy: IStrategy(address(1000)), + multiplier: 1e16 + }); uint8 quorumCountBefore = registryCoordinator.quorumCount(); cheats.expectEmit(true, true, true, true, address(registryCoordinator)); emit OperatorSetParamsUpdated(quorumCountBefore, operatorSetParams); cheats.prank(registryCoordinatorOwner); - registryCoordinator.createQuorum(operatorSetParams, minimumStake, strategyParams); + registryCoordinator.createTotalDelegatedStakeQuorum( + operatorSetParams, minimumStake, strategyParams + ); uint8 quorumCountAfter = registryCoordinator.quorumCount(); assertEq(quorumCountAfter, quorumCountBefore + 1, "quorum count did not increase properly"); @@ -250,7 +264,6 @@ contract RegistryCoordinatorUnitTests_Initialization_Setters is RegistryCoordina } contract RegistryCoordinatorUnitTests_RegisterOperator is RegistryCoordinatorUnitTests { - function test_registerOperator_revert_paused() public { bytes memory emptyQuorumNumbers = new bytes(0); ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; @@ -260,17 +273,21 @@ contract RegistryCoordinatorUnitTests_RegisterOperator is RegistryCoordinatorUni registryCoordinator.pause(2 ** PAUSED_REGISTER_OPERATOR); cheats.startPrank(defaultOperator); - cheats.expectRevert(bytes("Pausable: index is paused")); - registryCoordinator.registerOperator(emptyQuorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); + cheats.expectRevert(bytes4(keccak256("CurrentlyPaused()"))); + registryCoordinator.registerOperator( + emptyQuorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig + ); } function test_registerOperator_revert_emptyQuorumNumbers() public { bytes memory emptyQuorumNumbers = new bytes(0); ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; - cheats.expectRevert("RegCoord._registerOperator: bitmap cannot be 0"); + cheats.expectRevert(bytes4(keccak256("BitmapEmpty()"))); cheats.prank(defaultOperator); - registryCoordinator.registerOperator(emptyQuorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); + registryCoordinator.registerOperator( + emptyQuorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig + ); } function test_registerOperator_revert_invalidQuorum() public { @@ -279,9 +296,11 @@ contract RegistryCoordinatorUnitTests_RegisterOperator is RegistryCoordinatorUni quorumNumbersTooLarge[0] = 0xC0; - cheats.expectRevert("BitmapUtils.orderedBytesArrayToBitmap: bitmap exceeds max value"); + cheats.expectRevert(BitmapUtils.BitmapValueTooLarge.selector); cheats.prank(defaultOperator); - registryCoordinator.registerOperator(quorumNumbersTooLarge, defaultSocket, pubkeyRegistrationParams, emptySig); + registryCoordinator.registerOperator( + quorumNumbersTooLarge, defaultSocket, pubkeyRegistrationParams, emptySig + ); } function test_registerOperator_revert_nonexistentQuorum() public { @@ -292,8 +311,10 @@ contract RegistryCoordinatorUnitTests_RegisterOperator is RegistryCoordinatorUni quorumNumbersNotCreated[0] = 0x0B; cheats.prank(defaultOperator); - cheats.expectRevert("BitmapUtils.orderedBytesArrayToBitmap: bitmap exceeds max value"); - registryCoordinator.registerOperator(quorumNumbersNotCreated, defaultSocket, pubkeyRegistrationParams, emptySig); + cheats.expectRevert(BitmapUtils.BitmapValueTooLarge.selector); + registryCoordinator.registerOperator( + quorumNumbersNotCreated, defaultSocket, pubkeyRegistrationParams, emptySig + ); } function test_registerOperator_singleQuorum() public { @@ -314,7 +335,9 @@ contract RegistryCoordinatorUnitTests_RegisterOperator is RegistryCoordinatorUni uint256 gasBefore = gasleft(); cheats.prank(defaultOperator); - registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); + registryCoordinator.registerOperator( + quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig + ); uint256 gasAfter = gasleft(); emit log_named_uint("gasUsed, register for single quorum", gasBefore - gasAfter); @@ -322,25 +345,37 @@ contract RegistryCoordinatorUnitTests_RegisterOperator is RegistryCoordinatorUni assertEq(registryCoordinator.getOperatorId(defaultOperator), defaultOperatorId); assertEq( - keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), - keccak256(abi.encode(IRegistryCoordinator.OperatorInfo({ - operatorId: defaultOperatorId, - status: IRegistryCoordinator.OperatorStatus.REGISTERED - }))) + keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), + keccak256( + abi.encode( + ISlashingRegistryCoordinatorTypes.OperatorInfo({ + operatorId: defaultOperatorId, + status: ISlashingRegistryCoordinatorTypes.OperatorStatus.REGISTERED + }) + ) + ) ); assertEq(registryCoordinator.getCurrentQuorumBitmap(defaultOperatorId), quorumBitmap); assertEq( - keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0))), - keccak256(abi.encode(IRegistryCoordinator.QuorumBitmapUpdate({ - quorumBitmap: uint192(quorumBitmap), - updateBlockNumber: uint32(block.number), - nextUpdateBlockNumber: 0 - }))) + keccak256( + abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0)) + ), + keccak256( + abi.encode( + ISlashingRegistryCoordinatorTypes.QuorumBitmapUpdate({ + quorumBitmap: uint192(quorumBitmap), + updateBlockNumber: uint32(block.number), + nextUpdateBlockNumber: 0 + }) + ) + ) ); } // @notice tests registering an operator for a fuzzed assortment of quorums - function testFuzz_registerOperator(uint256 quorumBitmap) public { + function testFuzz_registerOperator( + uint256 quorumBitmap + ) public { // filter the fuzzed input down to only valid quorums quorumBitmap = quorumBitmap & MAX_QUORUM_BITMAP; ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; @@ -348,51 +383,60 @@ contract RegistryCoordinatorUnitTests_RegisterOperator is RegistryCoordinatorUni bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); uint96 actualStake; - for (uint i = 0; i < quorumNumbers.length; i++) { + for (uint256 i = 0; i < quorumNumbers.length; i++) { actualStake = _setOperatorWeight(defaultOperator, uint8(quorumNumbers[i]), defaultStake); } cheats.expectEmit(true, true, true, true, address(registryCoordinator)); emit OperatorSocketUpdate(defaultOperatorId, defaultSocket); - cheats.expectEmit(true, true, true, true, address(registryCoordinator)); - emit OperatorRegistered(defaultOperator, defaultOperatorId); - cheats.expectEmit(true, true, true, true, address(blsApkRegistry)); emit OperatorAddedToQuorums(defaultOperator, defaultOperatorId, quorumNumbers); - - for (uint i = 0; i < quorumNumbers.length; i++) { + for (uint256 i = 0; i < quorumNumbers.length; i++) { cheats.expectEmit(true, true, true, true, address(stakeRegistry)); emit OperatorStakeUpdate(defaultOperatorId, uint8(quorumNumbers[i]), actualStake); - } - - for (uint i = 0; i < quorumNumbers.length; i++) { + } + for (uint256 i = 0; i < quorumNumbers.length; i++) { cheats.expectEmit(true, true, true, true, address(indexRegistry)); emit QuorumIndexUpdate(defaultOperatorId, uint8(quorumNumbers[i]), 0); - } - + } + cheats.expectEmit(true, true, true, true, address(registryCoordinator)); + emit OperatorRegistered(defaultOperator, defaultOperatorId); + uint256 gasBefore = gasleft(); cheats.prank(defaultOperator); - registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); + registryCoordinator.registerOperator( + quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig + ); uint256 gasAfter = gasleft(); emit log_named_uint("gasUsed", gasBefore - gasAfter); emit log_named_uint("numQuorums", quorumNumbers.length); assertEq(registryCoordinator.getOperatorId(defaultOperator), defaultOperatorId); assertEq( - keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), - keccak256(abi.encode(IRegistryCoordinator.OperatorInfo({ - operatorId: defaultOperatorId, - status: IRegistryCoordinator.OperatorStatus.REGISTERED - }))) + keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), + keccak256( + abi.encode( + ISlashingRegistryCoordinatorTypes.OperatorInfo({ + operatorId: defaultOperatorId, + status: ISlashingRegistryCoordinatorTypes.OperatorStatus.REGISTERED + }) + ) + ) ); assertEq(registryCoordinator.getCurrentQuorumBitmap(defaultOperatorId), quorumBitmap); assertEq( - keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0))), - keccak256(abi.encode(IRegistryCoordinator.QuorumBitmapUpdate({ - quorumBitmap: uint192(quorumBitmap), - updateBlockNumber: uint32(block.number), - nextUpdateBlockNumber: 0 - }))) + keccak256( + abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0)) + ), + keccak256( + abi.encode( + ISlashingRegistryCoordinatorTypes.QuorumBitmapUpdate({ + quorumBitmap: uint192(quorumBitmap), + updateBlockNumber: uint32(block.number), + nextUpdateBlockNumber: 0 + }) + ) + ) ); } @@ -408,14 +452,17 @@ contract RegistryCoordinatorUnitTests_RegisterOperator is RegistryCoordinatorUni _setOperatorWeight(defaultOperator, uint8(quorumNumbers[0]), defaultStake); cheats.prank(defaultOperator); cheats.roll(registrationBlockNumber); - registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); + registryCoordinator.registerOperator( + quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig + ); bytes memory newQuorumNumbers = new bytes(1); - newQuorumNumbers[0] = bytes1(defaultQuorumNumber+1); + newQuorumNumbers[0] = bytes1(defaultQuorumNumber + 1); - uint96 actualStake = _setOperatorWeight(defaultOperator, uint8(newQuorumNumbers[0]), defaultStake); - //cheats.expectEmit(true, true, true, true, address(registryCoordinator)); - //emit OperatorSocketUpdate(defaultOperatorId, defaultSocket); + uint96 actualStake = + _setOperatorWeight(defaultOperator, uint8(newQuorumNumbers[0]), defaultStake); + cheats.expectEmit(true, true, true, true, address(registryCoordinator)); + emit OperatorSocketUpdate(defaultOperatorId, defaultSocket); cheats.expectEmit(true, true, true, true, address(blsApkRegistry)); emit OperatorAddedToQuorums(defaultOperator, defaultOperatorId, newQuorumNumbers); cheats.expectEmit(true, true, true, true, address(stakeRegistry)); @@ -424,38 +471,59 @@ contract RegistryCoordinatorUnitTests_RegisterOperator is RegistryCoordinatorUni emit QuorumIndexUpdate(defaultOperatorId, uint8(newQuorumNumbers[0]), 0); cheats.roll(nextRegistrationBlockNumber); cheats.prank(defaultOperator); - registryCoordinator.registerOperator(newQuorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); + registryCoordinator.registerOperator( + newQuorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig + ); - uint256 quorumBitmap = BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers) | BitmapUtils.orderedBytesArrayToBitmap(newQuorumNumbers); + uint256 quorumBitmap = BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers) + | BitmapUtils.orderedBytesArrayToBitmap(newQuorumNumbers); assertEq(registryCoordinator.getOperatorId(defaultOperator), defaultOperatorId); assertEq( - keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), - keccak256(abi.encode(IRegistryCoordinator.OperatorInfo({ - operatorId: defaultOperatorId, - status: IRegistryCoordinator.OperatorStatus.REGISTERED - }))) + keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), + keccak256( + abi.encode( + ISlashingRegistryCoordinatorTypes.OperatorInfo({ + operatorId: defaultOperatorId, + status: ISlashingRegistryCoordinatorTypes.OperatorStatus.REGISTERED + }) + ) + ) ); assertEq(registryCoordinator.getCurrentQuorumBitmap(defaultOperatorId), quorumBitmap); assertEq( - keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0))), - keccak256(abi.encode(IRegistryCoordinator.QuorumBitmapUpdate({ - quorumBitmap: uint192(BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers)), - updateBlockNumber: uint32(registrationBlockNumber), - nextUpdateBlockNumber: uint32(nextRegistrationBlockNumber) - }))) + keccak256( + abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0)) + ), + keccak256( + abi.encode( + ISlashingRegistryCoordinatorTypes.QuorumBitmapUpdate({ + quorumBitmap: uint192(BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers)), + updateBlockNumber: uint32(registrationBlockNumber), + nextUpdateBlockNumber: uint32(nextRegistrationBlockNumber) + }) + ) + ) ); assertEq( - keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 1))), - keccak256(abi.encode(IRegistryCoordinator.QuorumBitmapUpdate({ - quorumBitmap: uint192(quorumBitmap), - updateBlockNumber: uint32(nextRegistrationBlockNumber), - nextUpdateBlockNumber: 0 - }))) + keccak256( + abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 1)) + ), + keccak256( + abi.encode( + ISlashingRegistryCoordinatorTypes.QuorumBitmapUpdate({ + quorumBitmap: uint192(quorumBitmap), + updateBlockNumber: uint32(nextRegistrationBlockNumber), + nextUpdateBlockNumber: 0 + }) + ) + ) ); } - function test_registerOperator_revert_overFilledQuorum(uint256 pseudoRandomNumber) public { + function test_registerOperator_revert_overFilledQuorum( + uint256 pseudoRandomNumber + ) public { uint32 numOperators = defaultMaxOperatorCount; uint32 registrationBlockNumber = 200; ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; @@ -467,23 +535,27 @@ contract RegistryCoordinatorUnitTests_RegisterOperator is RegistryCoordinatorUni cheats.roll(registrationBlockNumber); - for (uint i = 0; i < numOperators; i++) { - BN254.G1Point memory pubKey = BN254.hashToG1(keccak256(abi.encodePacked(pseudoRandomNumber, i))); + for (uint256 i = 0; i < numOperators; i++) { + BN254.G1Point memory pubKey = + BN254.hashToG1(keccak256(abi.encodePacked(pseudoRandomNumber, i))); address operator = _incrementAddress(defaultOperator, i); - + _registerOperatorWithCoordinator(operator, quorumBitmap, pubKey); } address operatorToRegister = _incrementAddress(defaultOperator, numOperators); - BN254.G1Point memory operatorToRegisterPubKey = BN254.hashToG1(keccak256(abi.encodePacked(pseudoRandomNumber, numOperators))); - + BN254.G1Point memory operatorToRegisterPubKey = + BN254.hashToG1(keccak256(abi.encodePacked(pseudoRandomNumber, numOperators))); + blsApkRegistry.setBLSPublicKey(operatorToRegister, operatorToRegisterPubKey); _setOperatorWeight(operatorToRegister, defaultQuorumNumber, defaultStake); cheats.prank(operatorToRegister); - cheats.expectRevert("RegCoord.registerOperator: operator count exceeds maximum"); - registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); + cheats.expectRevert(bytes4(keccak256("MaxQuorumsReached()"))); + registryCoordinator.registerOperator( + quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig + ); } function test_registerOperator_revert_operatorAlreadyRegisteredForQuorum() public { @@ -498,13 +570,16 @@ contract RegistryCoordinatorUnitTests_RegisterOperator is RegistryCoordinatorUni cheats.prank(defaultOperator); cheats.roll(registrationBlockNumber); - registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); + registryCoordinator.registerOperator( + quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig + ); cheats.prank(defaultOperator); cheats.roll(nextRegistrationBlockNumber); - cheats.expectRevert("RegCoord._registerOperator: operator already registered for some quorums"); - - registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); + cheats.expectRevert(bytes4(keccak256("AlreadyRegisteredForQuorums()"))); + registryCoordinator.registerOperator( + quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig + ); } // tests for the internal `_registerOperator` function: @@ -512,8 +587,10 @@ contract RegistryCoordinatorUnitTests_RegisterOperator is RegistryCoordinatorUni bytes memory emptyQuorumNumbers = new bytes(0); ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; - cheats.expectRevert("RegCoord._registerOperator: bitmap cannot be 0"); - registryCoordinator._registerOperatorExternal(defaultOperator, defaultOperatorId, emptyQuorumNumbers, defaultSocket, emptySig); + cheats.expectRevert(bytes4(keccak256("BitmapEmpty()"))); + registryCoordinator._registerOperatorExternal( + defaultOperator, defaultOperatorId, emptyQuorumNumbers, defaultSocket, emptySig + ); } function test_registerOperatorInternal_revert_nonexistentQuorum() public { @@ -522,8 +599,10 @@ contract RegistryCoordinatorUnitTests_RegisterOperator is RegistryCoordinatorUni quorumNumbersTooLarge[0] = 0xC0; - cheats.expectRevert("BitmapUtils.orderedBytesArrayToBitmap: bitmap exceeds max value"); - registryCoordinator._registerOperatorExternal(defaultOperator, defaultOperatorId, quorumNumbersTooLarge, defaultSocket, emptySig); + cheats.expectRevert(BitmapUtils.BitmapValueTooLarge.selector); + registryCoordinator._registerOperatorExternal( + defaultOperator, defaultOperatorId, quorumNumbersTooLarge, defaultSocket, emptySig + ); } function test_registerOperatorInternal_revert_operatorAlreadyRegisteredForQuorum() public { @@ -532,10 +611,14 @@ contract RegistryCoordinatorUnitTests_RegisterOperator is RegistryCoordinatorUni quorumNumbers[0] = bytes1(defaultQuorumNumber); _setOperatorWeight(defaultOperator, uint8(quorumNumbers[0]), defaultStake); - registryCoordinator._registerOperatorExternal(defaultOperator, defaultOperatorId, quorumNumbers, defaultSocket, emptySig); + registryCoordinator._registerOperatorExternal( + defaultOperator, defaultOperatorId, quorumNumbers, defaultSocket, emptySig + ); - cheats.expectRevert("RegCoord._registerOperator: operator already registered for some quorums"); - registryCoordinator._registerOperatorExternal(defaultOperator, defaultOperatorId, quorumNumbers, defaultSocket, emptySig); + cheats.expectRevert(bytes4(keccak256("AlreadyRegisteredForQuorums()"))); + registryCoordinator._registerOperatorExternal( + defaultOperator, defaultOperatorId, quorumNumbers, defaultSocket, emptySig + ); } function test_registerOperatorInternal() public { @@ -554,32 +637,34 @@ contract RegistryCoordinatorUnitTests_RegisterOperator is RegistryCoordinatorUni cheats.expectEmit(true, true, true, true, address(indexRegistry)); emit QuorumIndexUpdate(defaultOperatorId, defaultQuorumNumber, 0); - registryCoordinator._registerOperatorExternal(defaultOperator, defaultOperatorId, quorumNumbers, defaultSocket, emptySig); + registryCoordinator._registerOperatorExternal( + defaultOperator, defaultOperatorId, quorumNumbers, defaultSocket, emptySig + ); uint256 quorumBitmap = BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers); - assertEq(registryCoordinator.getOperatorId(defaultOperator), defaultOperatorId); - assertEq( - keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), - keccak256(abi.encode(IRegistryCoordinator.OperatorInfo({ - operatorId: defaultOperatorId, - status: IRegistryCoordinator.OperatorStatus.REGISTERED - }))) - ); assertEq(registryCoordinator.getCurrentQuorumBitmap(defaultOperatorId), quorumBitmap); assertEq( - keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0))), - keccak256(abi.encode(IRegistryCoordinator.QuorumBitmapUpdate({ - quorumBitmap: uint192(quorumBitmap), - updateBlockNumber: uint32(block.number), - nextUpdateBlockNumber: 0 - }))) + keccak256( + abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0)) + ), + keccak256( + abi.encode( + ISlashingRegistryCoordinatorTypes.QuorumBitmapUpdate({ + quorumBitmap: uint192(quorumBitmap), + updateBlockNumber: uint32(block.number), + nextUpdateBlockNumber: 0 + }) + ) + ) ); } } // @dev note that this contract also contains tests for the `getQuorumBitmapIndicesAtBlockNumber` and `getQuorumBitmapAtBlockNumberByIndex` view fncs -contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is RegistryCoordinatorUnitTests { +contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is + RegistryCoordinatorUnitTests +{ function test_deregisterOperator_revert_paused() public { bytes memory quorumNumbers = new bytes(1); quorumNumbers[0] = bytes1(defaultQuorumNumber); @@ -591,32 +676,44 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist cheats.prank(pauser); registryCoordinator.pause(2 ** PAUSED_DEREGISTER_OPERATOR); - cheats.expectRevert(bytes("Pausable: index is paused")); + cheats.expectRevert(bytes4(keccak256("CurrentlyPaused()"))); cheats.prank(defaultOperator); registryCoordinator.deregisterOperator(quorumNumbers); } function test_deregisterOperator_revert_notRegistered() public { + // enable operatorSets + cheats.prank(registryCoordinator.owner()); + registryCoordinator.enableOperatorSets(); + bytes memory quorumNumbers = new bytes(1); quorumNumbers[0] = bytes1(defaultQuorumNumber); - cheats.expectRevert("RegCoord._deregisterOperator: operator is not registered"); + cheats.expectRevert(bytes4(keccak256("NotRegistered()"))); cheats.prank(defaultOperator); registryCoordinator.deregisterOperator(quorumNumbers); } function test_deregisterOperator_revert_incorrectQuorums() public { + // enable operatorSets + cheats.prank(registryCoordinator.owner()); + registryCoordinator.enableOperatorSets(); + + console.log("quorumCount", registryCoordinator.quorumCount()); + + assertTrue( + registryCoordinator.isM2Quorum(uint8(defaultQuorumNumber)), + "defaultQuorumNumber should be a M2 quorum" + ); + bytes memory quorumNumbers = new bytes(1); quorumNumbers[0] = bytes1(defaultQuorumNumber); uint256 quorumBitmap = BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers); - _registerOperatorWithCoordinator(defaultOperator, quorumBitmap, defaultPubKey); - - quorumNumbers = new bytes(2); - quorumNumbers[0] = bytes1(defaultQuorumNumber + 1); - quorumNumbers[1] = bytes1(defaultQuorumNumber + 2); + quorumNumbers = new bytes(1); + quorumNumbers[0] = bytes1(defaultQuorumNumber); - cheats.expectRevert("RegCoord._deregisterOperator: operator is not registered for quorums"); + cheats.expectRevert(bytes4(keccak256("NotRegistered()"))); cheats.prank(defaultOperator); registryCoordinator.deregisterOperator(quorumNumbers); } @@ -633,10 +730,12 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist _setOperatorWeight(defaultOperator, uint8(quorumNumbers[0]), defaultStake); cheats.startPrank(defaultOperator); - + cheats.roll(registrationBlockNumber); - - registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); + + registryCoordinator.registerOperator( + quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig + ); uint256 quorumBitmap = BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers); @@ -653,26 +752,38 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist emit log_named_uint("gasUsed", gasBefore - gasAfter); assertEq( - keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), - keccak256(abi.encode(IRegistryCoordinator.OperatorInfo({ - operatorId: defaultOperatorId, - status: IRegistryCoordinator.OperatorStatus.DEREGISTERED - }))) + keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), + keccak256( + abi.encode( + ISlashingRegistryCoordinatorTypes.OperatorInfo({ + operatorId: defaultOperatorId, + status: ISlashingRegistryCoordinatorTypes.OperatorStatus.DEREGISTERED + }) + ) + ) ); assertEq(registryCoordinator.getCurrentQuorumBitmap(defaultOperatorId), 0); assertEq( - keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0))), - keccak256(abi.encode(IRegistryCoordinator.QuorumBitmapUpdate({ - quorumBitmap: uint192(quorumBitmap), - updateBlockNumber: registrationBlockNumber, - nextUpdateBlockNumber: deregistrationBlockNumber - }))) + keccak256( + abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0)) + ), + keccak256( + abi.encode( + ISlashingRegistryCoordinatorTypes.QuorumBitmapUpdate({ + quorumBitmap: uint192(quorumBitmap), + updateBlockNumber: registrationBlockNumber, + nextUpdateBlockNumber: deregistrationBlockNumber + }) + ) + ) ); } // @notice verifies that an operator who was registered for a fuzzed set of quorums can be deregistered // @dev deregisters the operator from *all* quorums for which they we registered. - function testFuzz_deregisterOperator_fuzzedQuorumAndSingleOperator(uint256 quorumBitmap) public { + function testFuzz_deregisterOperator_fuzzedQuorumAndSingleOperator( + uint256 quorumBitmap + ) public { ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; uint32 registrationBlockNumber = 100; uint32 deregistrationBlockNumber = 200; @@ -682,19 +793,21 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist cheats.assume(quorumBitmap != 0); bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); - for (uint i = 0; i < quorumNumbers.length; i++) { + for (uint256 i = 0; i < quorumNumbers.length; i++) { _setOperatorWeight(defaultOperator, uint8(quorumNumbers[i]), defaultStake); } cheats.startPrank(defaultOperator); - + cheats.roll(registrationBlockNumber); - - registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); + + registryCoordinator.registerOperator( + quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig + ); cheats.expectEmit(true, true, true, true, address(blsApkRegistry)); emit OperatorRemovedFromQuorums(defaultOperator, defaultOperatorId, quorumNumbers); - for (uint i = 0; i < quorumNumbers.length; i++) { + for (uint256 i = 0; i < quorumNumbers.length; i++) { cheats.expectEmit(true, true, true, true, address(stakeRegistry)); emit OperatorStakeUpdate(defaultOperatorId, uint8(quorumNumbers[i]), 0); } @@ -708,24 +821,35 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist emit log_named_uint("numQuorums", quorumNumbers.length); assertEq( - keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), - keccak256(abi.encode(IRegistryCoordinator.OperatorInfo({ - operatorId: defaultOperatorId, - status: IRegistryCoordinator.OperatorStatus.DEREGISTERED - }))) + keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), + keccak256( + abi.encode( + ISlashingRegistryCoordinatorTypes.OperatorInfo({ + operatorId: defaultOperatorId, + status: ISlashingRegistryCoordinatorTypes.OperatorStatus.DEREGISTERED + }) + ) + ) ); assertEq(registryCoordinator.getCurrentQuorumBitmap(defaultOperatorId), 0); assertEq( - keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0))), - keccak256(abi.encode(IRegistryCoordinator.QuorumBitmapUpdate({ - quorumBitmap: uint192(quorumBitmap), - updateBlockNumber: registrationBlockNumber, - nextUpdateBlockNumber: deregistrationBlockNumber - }))) + keccak256( + abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0)) + ), + keccak256( + abi.encode( + ISlashingRegistryCoordinatorTypes.QuorumBitmapUpdate({ + quorumBitmap: uint192(quorumBitmap), + updateBlockNumber: registrationBlockNumber, + nextUpdateBlockNumber: deregistrationBlockNumber + }) + ) + ) ); } // @notice verifies that an operator who was registered for a fuzzed set of quorums can be deregistered from a subset of those quorums // @dev deregisters the operator from a fuzzed subset of the quorums for which they we registered. + function testFuzz_deregisterOperator_singleOperator_partialDeregistration( uint256 registrationQuorumBitmap, uint256 deregistrationQuorumBitmap @@ -740,23 +864,29 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist // filter the other fuzzed input to a subset of the first fuzzed input deregistrationQuorumBitmap = deregistrationQuorumBitmap & registrationQuorumBitmap; cheats.assume(deregistrationQuorumBitmap != 0); - bytes memory registrationquorumNumbers = BitmapUtils.bitmapToBytesArray(registrationQuorumBitmap); + bytes memory registrationquorumNumbers = + BitmapUtils.bitmapToBytesArray(registrationQuorumBitmap); - for (uint i = 0; i < registrationquorumNumbers.length; i++) { + for (uint256 i = 0; i < registrationquorumNumbers.length; i++) { _setOperatorWeight(defaultOperator, uint8(registrationquorumNumbers[i]), defaultStake); } cheats.startPrank(defaultOperator); - + cheats.roll(registrationBlockNumber); - - registryCoordinator.registerOperator(registrationquorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); - bytes memory deregistrationquorumNumbers = BitmapUtils.bitmapToBytesArray(deregistrationQuorumBitmap); + registryCoordinator.registerOperator( + registrationquorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig + ); + + bytes memory deregistrationquorumNumbers = + BitmapUtils.bitmapToBytesArray(deregistrationQuorumBitmap); cheats.expectEmit(true, true, true, true, address(blsApkRegistry)); - emit OperatorRemovedFromQuorums(defaultOperator, defaultOperatorId, deregistrationquorumNumbers); - for (uint i = 0; i < deregistrationquorumNumbers.length; i++) { + emit OperatorRemovedFromQuorums( + defaultOperator, defaultOperatorId, deregistrationquorumNumbers + ); + for (uint256 i = 0; i < deregistrationquorumNumbers.length; i++) { cheats.expectEmit(true, true, true, true, address(stakeRegistry)); emit OperatorStakeUpdate(defaultOperatorId, uint8(deregistrationquorumNumbers[i]), 0); } @@ -772,96 +902,134 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist // check that the operator is marked as 'degregistered' only if deregistered from *all* quorums if (deregistrationQuorumBitmap == registrationQuorumBitmap) { assertEq( - keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), - keccak256(abi.encode(IRegistryCoordinator.OperatorInfo({ - operatorId: defaultOperatorId, - status: IRegistryCoordinator.OperatorStatus.DEREGISTERED - }))) - ); + keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), + keccak256( + abi.encode( + ISlashingRegistryCoordinatorTypes.OperatorInfo({ + operatorId: defaultOperatorId, + status: ISlashingRegistryCoordinatorTypes.OperatorStatus.DEREGISTERED + }) + ) + ) + ); } else { assertEq( - keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), - keccak256(abi.encode(IRegistryCoordinator.OperatorInfo({ - operatorId: defaultOperatorId, - status: IRegistryCoordinator.OperatorStatus.REGISTERED - }))) - ); + keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), + keccak256( + abi.encode( + ISlashingRegistryCoordinatorTypes.OperatorInfo({ + operatorId: defaultOperatorId, + status: ISlashingRegistryCoordinatorTypes.OperatorStatus.REGISTERED + }) + ) + ) + ); } // ensure that the operator's current quorum bitmap matches the expectation - uint256 expectedQuorumBitmap = BitmapUtils.minus(registrationQuorumBitmap, deregistrationQuorumBitmap); - assertEq(registryCoordinator.getCurrentQuorumBitmap(defaultOperatorId), expectedQuorumBitmap); + uint256 expectedQuorumBitmap = + BitmapUtils.minus(registrationQuorumBitmap, deregistrationQuorumBitmap); + assertEq( + registryCoordinator.getCurrentQuorumBitmap(defaultOperatorId), expectedQuorumBitmap + ); // check that the quorum bitmap history is as expected assertEq( - keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0))), - keccak256(abi.encode(IRegistryCoordinator.QuorumBitmapUpdate({ - quorumBitmap: uint192(registrationQuorumBitmap), - updateBlockNumber: registrationBlockNumber, - nextUpdateBlockNumber: deregistrationBlockNumber - }))) + keccak256( + abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0)) + ), + keccak256( + abi.encode( + ISlashingRegistryCoordinatorTypes.QuorumBitmapUpdate({ + quorumBitmap: uint192(registrationQuorumBitmap), + updateBlockNumber: registrationBlockNumber, + nextUpdateBlockNumber: deregistrationBlockNumber + }) + ) + ) ); // note: there will be no second entry in the operator's bitmap history in the event that the operator has totally deregistered if (deregistrationQuorumBitmap != registrationQuorumBitmap) { assertEq( - keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 1))), - keccak256(abi.encode(IRegistryCoordinator.QuorumBitmapUpdate({ - quorumBitmap: uint192(expectedQuorumBitmap), - updateBlockNumber: deregistrationBlockNumber, - nextUpdateBlockNumber: 0 - }))) + keccak256( + abi.encode( + registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 1) + ) + ), + keccak256( + abi.encode( + ISlashingRegistryCoordinatorTypes.QuorumBitmapUpdate({ + quorumBitmap: uint192(expectedQuorumBitmap), + updateBlockNumber: deregistrationBlockNumber, + nextUpdateBlockNumber: 0 + }) + ) + ) ); } } // @notice registers the max number of operators with fuzzed bitmaps and then deregisters a pseudorandom operator (from all of their quorums) - function testFuzz_deregisterOperator_manyOperators(uint256 pseudoRandomNumber) public { + function testFuzz_deregisterOperator_manyOperators( + uint256 pseudoRandomNumber + ) public { uint32 numOperators = defaultMaxOperatorCount; - + uint32 registrationBlockNumber = 100; uint32 deregistrationBlockNumber = 200; // pad quorumBitmap with 1 until it has numOperators elements uint256[] memory quorumBitmaps = new uint256[](numOperators); - for (uint i = 0; i < numOperators; i++) { + for (uint256 i = 0; i < numOperators; i++) { // limit to maxQuorumsToRegisterFor quorums via mask so we don't run out of gas, make them all register for quorum 0 as well - quorumBitmaps[i] = uint256(keccak256(abi.encodePacked("quorumBitmap", pseudoRandomNumber, i))) & (1 << maxQuorumsToRegisterFor - 1) | 1; + quorumBitmaps[i] = uint256( + keccak256(abi.encodePacked("quorumBitmap", pseudoRandomNumber, i)) + ) & (1 << maxQuorumsToRegisterFor - 1) | 1; } cheats.roll(registrationBlockNumber); - + bytes32[] memory lastOperatorInQuorum = new bytes32[](numQuorums); - for (uint i = 0; i < numOperators; i++) { + for (uint256 i = 0; i < numOperators; i++) { emit log_named_uint("i", i); - BN254.G1Point memory pubKey = BN254.hashToG1(keccak256(abi.encodePacked(pseudoRandomNumber, i))); + BN254.G1Point memory pubKey = + BN254.hashToG1(keccak256(abi.encodePacked(pseudoRandomNumber, i))); bytes32 operatorId = BN254.hashG1Point(pubKey); address operator = _incrementAddress(defaultOperator, i); - + _registerOperatorWithCoordinator(operator, quorumBitmaps[i], pubKey); // for each quorum the operator is in, save the operatorId bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmaps[i]); - for (uint j = 0; j < quorumNumbers.length; j++) { + for (uint256 j = 0; j < quorumNumbers.length; j++) { lastOperatorInQuorum[uint8(quorumNumbers[j])] = operatorId; } } uint256 indexOfOperatorToDeregister = pseudoRandomNumber % numOperators; - address operatorToDeregister = _incrementAddress(defaultOperator, indexOfOperatorToDeregister); - BN254.G1Point memory operatorToDeregisterPubKey = BN254.hashToG1(keccak256(abi.encodePacked(pseudoRandomNumber, indexOfOperatorToDeregister))); + address operatorToDeregister = + _incrementAddress(defaultOperator, indexOfOperatorToDeregister); + BN254.G1Point memory operatorToDeregisterPubKey = BN254.hashToG1( + keccak256(abi.encodePacked(pseudoRandomNumber, indexOfOperatorToDeregister)) + ); bytes32 operatorToDeregisterId = BN254.hashG1Point(operatorToDeregisterPubKey); uint256 operatorToDeregisterQuorumBitmap = quorumBitmaps[indexOfOperatorToDeregister]; - bytes memory operatorToDeregisterQuorumNumbers = BitmapUtils.bitmapToBytesArray(operatorToDeregisterQuorumBitmap); + bytes memory operatorToDeregisterQuorumNumbers = + BitmapUtils.bitmapToBytesArray(operatorToDeregisterQuorumBitmap); bytes32[] memory operatorIdsToSwap = new bytes32[](operatorToDeregisterQuorumNumbers.length); - for (uint i = 0; i < operatorToDeregisterQuorumNumbers.length; i++) { + for (uint256 i = 0; i < operatorToDeregisterQuorumNumbers.length; i++) { operatorIdsToSwap[i] = lastOperatorInQuorum[uint8(operatorToDeregisterQuorumNumbers[i])]; } cheats.expectEmit(true, true, true, true, address(blsApkRegistry)); - emit OperatorRemovedFromQuorums(operatorToDeregister, operatorToDeregisterId, operatorToDeregisterQuorumNumbers); - - for (uint i = 0; i < operatorToDeregisterQuorumNumbers.length; i++) { + emit OperatorRemovedFromQuorums( + operatorToDeregister, operatorToDeregisterId, operatorToDeregisterQuorumNumbers + ); + + for (uint256 i = 0; i < operatorToDeregisterQuorumNumbers.length; i++) { cheats.expectEmit(true, true, true, true, address(stakeRegistry)); - emit OperatorStakeUpdate(operatorToDeregisterId, uint8(operatorToDeregisterQuorumNumbers[i]), 0); + emit OperatorStakeUpdate( + operatorToDeregisterId, uint8(operatorToDeregisterQuorumNumbers[i]), 0 + ); } cheats.roll(deregistrationBlockNumber); @@ -870,20 +1038,32 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist registryCoordinator.deregisterOperator(operatorToDeregisterQuorumNumbers); assertEq( - keccak256(abi.encode(registryCoordinator.getOperator(operatorToDeregister))), - keccak256(abi.encode(IRegistryCoordinator.OperatorInfo({ - operatorId: operatorToDeregisterId, - status: IRegistryCoordinator.OperatorStatus.DEREGISTERED - }))) + keccak256(abi.encode(registryCoordinator.getOperator(operatorToDeregister))), + keccak256( + abi.encode( + ISlashingRegistryCoordinatorTypes.OperatorInfo({ + operatorId: operatorToDeregisterId, + status: ISlashingRegistryCoordinatorTypes.OperatorStatus.DEREGISTERED + }) + ) + ) ); assertEq(registryCoordinator.getCurrentQuorumBitmap(defaultOperatorId), 0); assertEq( - keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(operatorToDeregisterId, 0))), - keccak256(abi.encode(IRegistryCoordinator.QuorumBitmapUpdate({ - quorumBitmap: uint192(operatorToDeregisterQuorumBitmap), - updateBlockNumber: registrationBlockNumber, - nextUpdateBlockNumber: deregistrationBlockNumber - }))) + keccak256( + abi.encode( + registryCoordinator.getQuorumBitmapUpdateByIndex(operatorToDeregisterId, 0) + ) + ), + keccak256( + abi.encode( + ISlashingRegistryCoordinatorTypes.QuorumBitmapUpdate({ + quorumBitmap: uint192(operatorToDeregisterQuorumBitmap), + updateBlockNumber: registrationBlockNumber, + nextUpdateBlockNumber: deregistrationBlockNumber + }) + ) + ) ); } @@ -898,42 +1078,60 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist quorumNumbers[0] = bytes1(defaultQuorumNumber); cheats.startPrank(defaultOperator); - + cheats.roll(reregistrationBlockNumber); - + // store data before registering, to check against later - IRegistryCoordinator.QuorumBitmapUpdate memory previousQuorumBitmapUpdate = + ISlashingRegistryCoordinatorTypes.QuorumBitmapUpdate memory previousQuorumBitmapUpdate = registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0); // re-register the operator - registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); + registryCoordinator.registerOperator( + quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig + ); // check success of registration uint256 quorumBitmap = BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers); assertEq(registryCoordinator.getOperatorId(defaultOperator), defaultOperatorId, "1"); assertEq( - keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), - keccak256(abi.encode(IRegistryCoordinator.OperatorInfo({ - operatorId: defaultOperatorId, - status: IRegistryCoordinator.OperatorStatus.REGISTERED - }))), + keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), + keccak256( + abi.encode( + ISlashingRegistryCoordinatorTypes.OperatorInfo({ + operatorId: defaultOperatorId, + status: ISlashingRegistryCoordinatorTypes.OperatorStatus.REGISTERED + }) + ) + ), "2" ); 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(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0)) + ), keccak256(abi.encode(previousQuorumBitmapUpdate)), "4" ); // check that new entry in bitmap history is as expected - uint historyLength = registryCoordinator.getQuorumBitmapHistoryLength(defaultOperatorId); + uint256 historyLength = registryCoordinator.getQuorumBitmapHistoryLength(defaultOperatorId); assertEq( - keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, historyLength - 1))), - keccak256(abi.encode(IRegistryCoordinator.QuorumBitmapUpdate({ - quorumBitmap: uint192(quorumBitmap), - updateBlockNumber: uint32(reregistrationBlockNumber), - nextUpdateBlockNumber: 0 - }))), + keccak256( + abi.encode( + registryCoordinator.getQuorumBitmapUpdateByIndex( + defaultOperatorId, historyLength - 1 + ) + ) + ), + keccak256( + abi.encode( + ISlashingRegistryCoordinatorTypes.QuorumBitmapUpdate({ + quorumBitmap: uint192(quorumBitmap), + updateBlockNumber: uint32(reregistrationBlockNumber), + nextUpdateBlockNumber: 0 + }) + ) + ), "5" ); } @@ -951,18 +1149,20 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist cheats.roll(registrationBlockNumber); cheats.startPrank(defaultOperator); - registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); + registryCoordinator.registerOperator( + quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig + ); bytes memory emptyQuorumNumbers = new bytes(0); cheats.roll(deregistrationBlockNumber); - cheats.expectRevert("RegCoord._deregisterOperator: bitmap cannot be 0"); + cheats.expectRevert(bytes4(keccak256("BitmapCannotBeZero()"))); registryCoordinator._deregisterOperatorExternal(defaultOperator, emptyQuorumNumbers); } function test_deregisterOperatorExternal_revert_notRegistered() public { bytes memory emptyQuorumNumbers = new bytes(0); - cheats.expectRevert("RegCoord._deregisterOperator: operator is not registered"); + cheats.expectRevert(bytes4(keccak256("NotRegistered()"))); registryCoordinator._deregisterOperatorExternal(defaultOperator, emptyQuorumNumbers); } @@ -978,13 +1178,15 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist cheats.roll(registrationBlockNumber); cheats.startPrank(defaultOperator); - registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); + registryCoordinator.registerOperator( + quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig + ); bytes memory incorrectQuorum = new bytes(1); incorrectQuorum[0] = bytes1(defaultQuorumNumber + 1); cheats.roll(deregistrationBlockNumber); - cheats.expectRevert("RegCoord._deregisterOperator: operator is not registered for quorums"); + cheats.expectRevert(bytes4(keccak256("NotRegisteredForQuorum()"))); registryCoordinator._deregisterOperatorExternal(defaultOperator, incorrectQuorum); } @@ -1005,15 +1207,19 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist cheats.prank(defaultOperator); cheats.roll(registrationBlockNumber); - registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); + registryCoordinator.registerOperator( + quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig + ); cheats.prank(ejector); registryCoordinator.ejectOperator(defaultOperator, quorumNumbers); cheats.prank(defaultOperator); cheats.roll(reregistrationBlockNumber); - cheats.expectRevert("RegCoord._registerOperator: operator cannot reregister yet"); - registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); + cheats.expectRevert(bytes4(keccak256("CannotReregisterYet()"))); + registryCoordinator.registerOperator( + quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig + ); } function test_reregisterOperator_reregistrationDelay() public { @@ -1033,7 +1239,9 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist cheats.prank(defaultOperator); cheats.roll(registrationBlockNumber); - registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); + registryCoordinator.registerOperator( + quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig + ); cheats.prank(ejector); registryCoordinator.ejectOperator(defaultOperator, quorumNumbers); @@ -1041,7 +1249,9 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist cheats.prank(defaultOperator); cheats.roll(reregistrationBlockNumber); cheats.warp(block.timestamp + reregistrationDelay + 1); - registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); + registryCoordinator.registerOperator( + quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig + ); } // note: this is not possible to test, because there is no route to getting the operator registered for nonexistent quorums @@ -1061,68 +1271,101 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist // filter the other fuzzed input to a subset of the first fuzzed input deregistrationQuorumBitmap = deregistrationQuorumBitmap & registrationQuorumBitmap; cheats.assume(deregistrationQuorumBitmap != 0); - bytes memory registrationquorumNumbers = BitmapUtils.bitmapToBytesArray(registrationQuorumBitmap); + bytes memory registrationquorumNumbers = + BitmapUtils.bitmapToBytesArray(registrationQuorumBitmap); - for (uint i = 0; i < registrationquorumNumbers.length; i++) { + for (uint256 i = 0; i < registrationquorumNumbers.length; i++) { _setOperatorWeight(defaultOperator, uint8(registrationquorumNumbers[i]), defaultStake); } cheats.roll(registrationBlockNumber); cheats.startPrank(defaultOperator); - registryCoordinator.registerOperator(registrationquorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); + registryCoordinator.registerOperator( + registrationquorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig + ); - bytes memory deregistrationquorumNumbers = BitmapUtils.bitmapToBytesArray(deregistrationQuorumBitmap); + bytes memory deregistrationquorumNumbers = + BitmapUtils.bitmapToBytesArray(deregistrationQuorumBitmap); cheats.expectEmit(true, true, true, true, address(blsApkRegistry)); - emit OperatorRemovedFromQuorums(defaultOperator, defaultOperatorId, deregistrationquorumNumbers); - for (uint i = 0; i < deregistrationquorumNumbers.length; i++) { + emit OperatorRemovedFromQuorums( + defaultOperator, defaultOperatorId, deregistrationquorumNumbers + ); + for (uint256 i = 0; i < deregistrationquorumNumbers.length; i++) { cheats.expectEmit(true, true, true, true, address(stakeRegistry)); emit OperatorStakeUpdate(defaultOperatorId, uint8(deregistrationquorumNumbers[i]), 0); } cheats.roll(deregistrationBlockNumber); - registryCoordinator._deregisterOperatorExternal(defaultOperator, deregistrationquorumNumbers); + registryCoordinator._deregisterOperatorExternal( + defaultOperator, deregistrationquorumNumbers + ); // check that the operator is marked as 'degregistered' only if deregistered from *all* quorums if (deregistrationQuorumBitmap == registrationQuorumBitmap) { assertEq( - keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), - keccak256(abi.encode(IRegistryCoordinator.OperatorInfo({ - operatorId: defaultOperatorId, - status: IRegistryCoordinator.OperatorStatus.DEREGISTERED - }))) - ); + keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), + keccak256( + abi.encode( + ISlashingRegistryCoordinatorTypes.OperatorInfo({ + operatorId: defaultOperatorId, + status: ISlashingRegistryCoordinatorTypes.OperatorStatus.DEREGISTERED + }) + ) + ) + ); } else { assertEq( - keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), - keccak256(abi.encode(IRegistryCoordinator.OperatorInfo({ - operatorId: defaultOperatorId, - status: IRegistryCoordinator.OperatorStatus.REGISTERED - }))) - ); + keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), + keccak256( + abi.encode( + ISlashingRegistryCoordinatorTypes.OperatorInfo({ + operatorId: defaultOperatorId, + status: ISlashingRegistryCoordinatorTypes.OperatorStatus.REGISTERED + }) + ) + ) + ); } // ensure that the operator's current quorum bitmap matches the expectation - uint256 expectedQuorumBitmap = BitmapUtils.minus(registrationQuorumBitmap, deregistrationQuorumBitmap); - assertEq(registryCoordinator.getCurrentQuorumBitmap(defaultOperatorId), expectedQuorumBitmap); + uint256 expectedQuorumBitmap = + BitmapUtils.minus(registrationQuorumBitmap, deregistrationQuorumBitmap); + assertEq( + registryCoordinator.getCurrentQuorumBitmap(defaultOperatorId), expectedQuorumBitmap + ); // check that the quorum bitmap history is as expected assertEq( - keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0))), - keccak256(abi.encode(IRegistryCoordinator.QuorumBitmapUpdate({ - quorumBitmap: uint192(registrationQuorumBitmap), - updateBlockNumber: registrationBlockNumber, - nextUpdateBlockNumber: deregistrationBlockNumber - }))) + keccak256( + abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0)) + ), + keccak256( + abi.encode( + ISlashingRegistryCoordinatorTypes.QuorumBitmapUpdate({ + quorumBitmap: uint192(registrationQuorumBitmap), + updateBlockNumber: registrationBlockNumber, + nextUpdateBlockNumber: deregistrationBlockNumber + }) + ) + ) ); // note: there will be no second entry in the operator's bitmap history in the event that the operator has totally deregistered if (deregistrationQuorumBitmap != registrationQuorumBitmap) { assertEq( - keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 1))), - keccak256(abi.encode(IRegistryCoordinator.QuorumBitmapUpdate({ - quorumBitmap: uint192(expectedQuorumBitmap), - updateBlockNumber: deregistrationBlockNumber, - nextUpdateBlockNumber: 0 - }))) + keccak256( + abi.encode( + registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 1) + ) + ), + keccak256( + abi.encode( + ISlashingRegistryCoordinatorTypes.QuorumBitmapUpdate({ + quorumBitmap: uint192(expectedQuorumBitmap), + updateBlockNumber: deregistrationBlockNumber, + nextUpdateBlockNumber: 0 + }) + ) + ) ); } } @@ -1136,7 +1379,9 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist _setOperatorWeight(defaultOperator, uint8(quorumNumbers[0]), defaultStake); cheats.prank(defaultOperator); - registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); + registryCoordinator.registerOperator( + quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig + ); cheats.expectEmit(true, true, true, true, address(blsApkRegistry)); emit OperatorRemovedFromQuorums(defaultOperator, defaultOperatorId, quorumNumbers); @@ -1147,18 +1392,22 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist // eject cheats.prank(ejector); registryCoordinator.ejectOperator(defaultOperator, quorumNumbers); - + // make sure the operator is deregistered assertEq( - keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), - keccak256(abi.encode(IRegistryCoordinator.OperatorInfo({ - operatorId: defaultOperatorId, - status: IRegistryCoordinator.OperatorStatus.DEREGISTERED - }))) + keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), + keccak256( + abi.encode( + ISlashingRegistryCoordinatorTypes.OperatorInfo({ + operatorId: defaultOperatorId, + status: ISlashingRegistryCoordinatorTypes.OperatorStatus.DEREGISTERED + }) + ) + ) ); // make sure the operator is not in any quorums assertEq(registryCoordinator.getCurrentQuorumBitmap(defaultOperatorId), 0); - } + } function test_ejectOperator_subsetOfQuorums() public { // register operator with default stake with 2 quorums @@ -1167,12 +1416,14 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist quorumNumbers[1] = bytes1(defaultQuorumNumber + 1); ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; - for (uint i = 0; i < quorumNumbers.length; i++) { + for (uint256 i = 0; i < quorumNumbers.length; i++) { _setOperatorWeight(defaultOperator, uint8(quorumNumbers[i]), defaultStake); } cheats.prank(defaultOperator); - registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); + registryCoordinator.registerOperator( + quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig + ); // eject from only first quorum bytes memory quorumNumbersToEject = new bytes(1); @@ -1186,20 +1437,25 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist cheats.prank(ejector); registryCoordinator.ejectOperator(defaultOperator, quorumNumbersToEject); - + // make sure the operator is registered assertEq( - keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), - keccak256(abi.encode(IRegistryCoordinator.OperatorInfo({ - operatorId: defaultOperatorId, - status: IRegistryCoordinator.OperatorStatus.REGISTERED - }))) + keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), + keccak256( + abi.encode( + ISlashingRegistryCoordinatorTypes.OperatorInfo({ + operatorId: defaultOperatorId, + status: ISlashingRegistryCoordinatorTypes.OperatorStatus.REGISTERED + }) + ) + ) ); // make sure the operator is properly removed from the quorums assertEq( registryCoordinator.getCurrentQuorumBitmap(defaultOperatorId), // quorumsRegisteredFor & ~quorumsEjectedFrom - BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers) & ~BitmapUtils.orderedBytesArrayToBitmap(quorumNumbersToEject) + BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers) + & ~BitmapUtils.orderedBytesArrayToBitmap(quorumNumbersToEject) ); } @@ -1211,9 +1467,11 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist _setOperatorWeight(defaultOperator, uint8(quorumNumbers[0]), defaultStake); cheats.prank(defaultOperator); - registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); - - cheats.expectRevert("RegCoord.onlyEjector: caller is not the ejector"); + registryCoordinator.registerOperator( + quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig + ); + + cheats.expectRevert(bytes4(keccak256("OnlyEjector()"))); cheats.prank(defaultOperator); registryCoordinator.ejectOperator(defaultOperator, quorumNumbers); } @@ -1221,7 +1479,9 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist function test_getQuorumBitmapIndicesAtBlockNumber_revert_notRegistered() public { uint32 blockNumber; bytes32[] memory operatorIds = new bytes32[](1); - cheats.expectRevert("RegCoord.getQuorumBitmapIndexAtBlockNumber: no bitmap update found for operator at blockNumber"); + cheats.expectRevert( + "RegistryCoordinator.getQuorumBitmapIndexAtBlockNumber: no bitmap update found for operatorId" + ); registryCoordinator.getQuorumBitmapIndicesAtBlockNumber(blockNumber, operatorIds); } @@ -1234,24 +1494,38 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist quorumNumbers[0] = bytes1(defaultQuorumNumber); _setOperatorWeight(defaultOperator, uint8(quorumNumbers[0]), defaultStake); cheats.roll(registrationBlockNumber); - cheats.startPrank(defaultOperator); - registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); + cheats.startPrank(defaultOperator); + registryCoordinator.registerOperator( + quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig + ); uint32 blockNumber = 0; bytes32[] memory operatorIds = new bytes32[](1); operatorIds[0] = defaultOperatorId; uint32[] memory returnArray; - cheats.expectRevert("RegCoord.getQuorumBitmapIndexAtBlockNumber: no bitmap update found for operator at blockNumber"); + cheats.expectRevert( + "RegistryCoordinator.getQuorumBitmapIndexAtBlockNumber: no bitmap update found for operatorId" + ); registryCoordinator.getQuorumBitmapIndicesAtBlockNumber(blockNumber, operatorIds); blockNumber = registrationBlockNumber; - returnArray = registryCoordinator.getQuorumBitmapIndicesAtBlockNumber(blockNumber, operatorIds); - assertEq(returnArray[0], 0, "defaultOperator bitmap index at blockNumber registrationBlockNumber was not 0"); + returnArray = + registryCoordinator.getQuorumBitmapIndicesAtBlockNumber(blockNumber, operatorIds); + assertEq( + returnArray[0], + 0, + "defaultOperator bitmap index at blockNumber registrationBlockNumber was not 0" + ); blockNumber = registrationBlockNumber + 1; - returnArray = registryCoordinator.getQuorumBitmapIndicesAtBlockNumber(blockNumber, operatorIds); - assertEq(returnArray[0], 0, "defaultOperator bitmap index at blockNumber registrationBlockNumber + 1 was not 0"); + returnArray = + registryCoordinator.getQuorumBitmapIndicesAtBlockNumber(blockNumber, operatorIds); + assertEq( + returnArray[0], + 0, + "defaultOperator bitmap index at blockNumber registrationBlockNumber + 1 was not 0" + ); } // @notice tests for correct reversion and return values in the event that an operator registers and later deregisters @@ -1264,24 +1538,46 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist operatorIds[0] = defaultOperatorId; uint32[] memory returnArray; - cheats.expectRevert("RegCoord.getQuorumBitmapIndexAtBlockNumber: no bitmap update found for operator at blockNumber"); + cheats.expectRevert( + "RegistryCoordinator.getQuorumBitmapIndexAtBlockNumber: no bitmap update found for operatorId" + ); registryCoordinator.getQuorumBitmapIndicesAtBlockNumber(blockNumber, operatorIds); blockNumber = registrationBlockNumber; - returnArray = registryCoordinator.getQuorumBitmapIndicesAtBlockNumber(blockNumber, operatorIds); - assertEq(returnArray[0], 0, "defaultOperator bitmap index at blockNumber registrationBlockNumber was not 0"); + returnArray = + registryCoordinator.getQuorumBitmapIndicesAtBlockNumber(blockNumber, operatorIds); + assertEq( + returnArray[0], + 0, + "defaultOperator bitmap index at blockNumber registrationBlockNumber was not 0" + ); blockNumber = registrationBlockNumber + 1; - returnArray = registryCoordinator.getQuorumBitmapIndicesAtBlockNumber(blockNumber, operatorIds); - assertEq(returnArray[0], 0, "defaultOperator bitmap index at blockNumber registrationBlockNumber + 1 was not 0"); + returnArray = + registryCoordinator.getQuorumBitmapIndicesAtBlockNumber(blockNumber, operatorIds); + assertEq( + returnArray[0], + 0, + "defaultOperator bitmap index at blockNumber registrationBlockNumber + 1 was not 0" + ); blockNumber = deregistrationBlockNumber; - returnArray = registryCoordinator.getQuorumBitmapIndicesAtBlockNumber(blockNumber, operatorIds); - assertEq(returnArray[0], 1, "defaultOperator bitmap index at blockNumber deregistrationBlockNumber was not 1"); + returnArray = + registryCoordinator.getQuorumBitmapIndicesAtBlockNumber(blockNumber, operatorIds); + assertEq( + returnArray[0], + 1, + "defaultOperator bitmap index at blockNumber deregistrationBlockNumber was not 1" + ); blockNumber = deregistrationBlockNumber + 1; - returnArray = registryCoordinator.getQuorumBitmapIndicesAtBlockNumber(blockNumber, operatorIds); - assertEq(returnArray[0], 1, "defaultOperator bitmap index at blockNumber deregistrationBlockNumber + 1 was not 1"); + returnArray = + registryCoordinator.getQuorumBitmapIndicesAtBlockNumber(blockNumber, operatorIds); + assertEq( + returnArray[0], + 1, + "defaultOperator bitmap index at blockNumber deregistrationBlockNumber + 1 was not 1" + ); } // @notice tests for correct reversion and return values in the event that an operator registers and later deregisters @@ -1297,40 +1593,65 @@ contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is Regist uint192 emptyBitmap = 0; // try an incorrect blockNumber input and confirm reversion - cheats.expectRevert("RegCoord.getQuorumBitmapAtBlockNumberByIndex: quorumBitmapUpdate is from after blockNumber"); - uint192 returnVal = registryCoordinator.getQuorumBitmapAtBlockNumberByIndex(operatorId, blockNumber, index); + cheats.expectRevert(QuorumBitmapHistoryLib.BitmapUpdateIsAfterBlockNumber.selector); + uint192 returnVal = + registryCoordinator.getQuorumBitmapAtBlockNumberByIndex(operatorId, blockNumber, index); blockNumber = registrationBlockNumber; - returnVal = registryCoordinator.getQuorumBitmapAtBlockNumberByIndex(operatorId, blockNumber, index); - assertEq(returnVal, defaultQuorumBitmap, "defaultOperator bitmap index at blockNumber registrationBlockNumber was not defaultQuorumBitmap"); + returnVal = + registryCoordinator.getQuorumBitmapAtBlockNumberByIndex(operatorId, blockNumber, index); + assertEq( + returnVal, + defaultQuorumBitmap, + "defaultOperator bitmap index at blockNumber registrationBlockNumber was not defaultQuorumBitmap" + ); blockNumber = registrationBlockNumber + 1; - returnVal = registryCoordinator.getQuorumBitmapAtBlockNumberByIndex(operatorId, blockNumber, index); - assertEq(returnVal, defaultQuorumBitmap, "defaultOperator bitmap index at blockNumber registrationBlockNumber + 1 was not defaultQuorumBitmap"); + returnVal = + registryCoordinator.getQuorumBitmapAtBlockNumberByIndex(operatorId, blockNumber, index); + assertEq( + returnVal, + defaultQuorumBitmap, + "defaultOperator bitmap index at blockNumber registrationBlockNumber + 1 was not defaultQuorumBitmap" + ); // try an incorrect index input and confirm reversion index = 1; - cheats.expectRevert("RegCoord.getQuorumBitmapAtBlockNumberByIndex: quorumBitmapUpdate is from after blockNumber"); - returnVal = registryCoordinator.getQuorumBitmapAtBlockNumberByIndex(operatorId, blockNumber, index); + cheats.expectRevert(QuorumBitmapHistoryLib.BitmapUpdateIsAfterBlockNumber.selector); + returnVal = + registryCoordinator.getQuorumBitmapAtBlockNumberByIndex(operatorId, blockNumber, index); blockNumber = deregistrationBlockNumber; - returnVal = registryCoordinator.getQuorumBitmapAtBlockNumberByIndex(operatorId, blockNumber, index); - assertEq(returnVal, emptyBitmap, "defaultOperator bitmap index at blockNumber deregistrationBlockNumber was not emptyBitmap"); + returnVal = + registryCoordinator.getQuorumBitmapAtBlockNumberByIndex(operatorId, blockNumber, index); + assertEq( + returnVal, + emptyBitmap, + "defaultOperator bitmap index at blockNumber deregistrationBlockNumber was not emptyBitmap" + ); blockNumber = deregistrationBlockNumber + 1; - returnVal = registryCoordinator.getQuorumBitmapAtBlockNumberByIndex(operatorId, blockNumber, index); - assertEq(returnVal, emptyBitmap, "defaultOperator bitmap index at blockNumber deregistrationBlockNumber + 1 was not emptyBitmap"); + returnVal = + registryCoordinator.getQuorumBitmapAtBlockNumberByIndex(operatorId, blockNumber, index); + assertEq( + returnVal, + emptyBitmap, + "defaultOperator bitmap index at blockNumber deregistrationBlockNumber + 1 was not emptyBitmap" + ); // try an incorrect index input and confirm reversion index = 0; - cheats.expectRevert("RegCoord.getQuorumBitmapAtBlockNumberByIndex: quorumBitmapUpdate is from before blockNumber"); - returnVal = registryCoordinator.getQuorumBitmapAtBlockNumberByIndex(operatorId, blockNumber, index); + cheats.expectRevert(QuorumBitmapHistoryLib.NextBitmapUpdateIsBeforeBlockNumber.selector); + returnVal = + registryCoordinator.getQuorumBitmapAtBlockNumberByIndex(operatorId, blockNumber, index); } } contract RegistryCoordinatorUnitTests_RegisterOperatorWithChurn is RegistryCoordinatorUnitTests { // @notice registers an operator for a single quorum, with a fuzzed pubkey, churning out another operator from the quorum - function testFuzz_registerOperatorWithChurn(uint256 pseudoRandomNumber) public { + function testFuzz_registerOperatorWithChurn( + uint256 pseudoRandomNumber + ) public { uint32 numOperators = defaultMaxOperatorCount; uint32 kickRegistrationBlockNumber = 100; uint32 registrationBlockNumber = 200; @@ -1342,23 +1663,27 @@ contract RegistryCoordinatorUnitTests_RegisterOperatorWithChurn is RegistryCoord cheats.roll(kickRegistrationBlockNumber); - for (uint i = 0; i < numOperators - 1; i++) { - BN254.G1Point memory pubKey = BN254.hashToG1(keccak256(abi.encodePacked(pseudoRandomNumber, i))); + for (uint256 i = 0; i < numOperators - 1; i++) { + BN254.G1Point memory pubKey = + BN254.hashToG1(keccak256(abi.encodePacked(pseudoRandomNumber, i))); address operator = _incrementAddress(defaultOperator, i); - + _registerOperatorWithCoordinator(operator, quorumBitmap, pubKey); } address operatorToRegister = _incrementAddress(defaultOperator, numOperators); - BN254.G1Point memory operatorToRegisterPubKey = BN254.hashToG1(keccak256(abi.encodePacked(pseudoRandomNumber, numOperators))); + BN254.G1Point memory operatorToRegisterPubKey = + BN254.hashToG1(keccak256(abi.encodePacked(pseudoRandomNumber, numOperators))); bytes32 operatorToRegisterId = BN254.hashG1Point(operatorToRegisterPubKey); bytes32 operatorToKickId; address operatorToKick; - + // register last operator before kick - IRegistryCoordinator.OperatorKickParam[] memory operatorKickParams = new IRegistryCoordinator.OperatorKickParam[](1); + ISlashingRegistryCoordinatorTypes.OperatorKickParam[] memory operatorKickParams = + new ISlashingRegistryCoordinatorTypes.OperatorKickParam[](1); { - BN254.G1Point memory pubKey = BN254.hashToG1(keccak256(abi.encodePacked(pseudoRandomNumber, numOperators - 1))); + BN254.G1Point memory pubKey = + BN254.hashToG1(keccak256(abi.encodePacked(pseudoRandomNumber, numOperators - 1))); operatorToKickId = BN254.hashG1Point(pubKey); operatorToKick = _incrementAddress(defaultOperator, numOperators - 1); @@ -1368,7 +1693,7 @@ contract RegistryCoordinatorUnitTests_RegisterOperatorWithChurn is RegistryCoord // operatorIdsToSwap[0] = operatorToRegisterId operatorIdsToSwap[0] = operatorToRegisterId; - operatorKickParams[0] = IRegistryCoordinator.OperatorKickParam({ + operatorKickParams[0] = ISlashingRegistryCoordinatorTypes.OperatorKickParam({ quorumNumber: defaultQuorumNumber, operator: operatorToKick }); @@ -1382,37 +1707,45 @@ contract RegistryCoordinatorUnitTests_RegisterOperatorWithChurn is RegistryCoord cheats.roll(registrationBlockNumber); cheats.expectEmit(true, true, true, true, address(registryCoordinator)); emit OperatorSocketUpdate(operatorToRegisterId, defaultSocket); - cheats.expectEmit(true, true, true, true, address(registryCoordinator)); - emit OperatorRegistered(operatorToRegister, operatorToRegisterId); cheats.expectEmit(true, true, true, true, address(blsApkRegistry)); emit OperatorAddedToQuorums(operatorToRegister, operatorToRegisterId, quorumNumbers); cheats.expectEmit(true, true, true, false, address(stakeRegistry)); - emit OperatorStakeUpdate(operatorToRegisterId, defaultQuorumNumber, registeringStake - 1); + emit OperatorStakeUpdate(operatorToRegisterId, defaultQuorumNumber, registeringStake); cheats.expectEmit(true, true, true, true, address(indexRegistry)); emit QuorumIndexUpdate(operatorToRegisterId, defaultQuorumNumber, numOperators); - + // Then expect the deregistration events cheats.expectEmit(true, true, true, true, address(registryCoordinator)); emit OperatorDeregistered(operatorKickParams[0].operator, operatorToKickId); cheats.expectEmit(true, true, true, true, address(blsApkRegistry)); - emit OperatorRemovedFromQuorums(operatorKickParams[0].operator, operatorToKickId, quorumNumbers); + emit OperatorRemovedFromQuorums( + operatorKickParams[0].operator, operatorToKickId, quorumNumbers + ); cheats.expectEmit(true, true, true, true, address(stakeRegistry)); emit OperatorStakeUpdate(operatorToKickId, defaultQuorumNumber, 0); cheats.expectEmit(true, true, true, true, address(indexRegistry)); emit QuorumIndexUpdate(operatorToRegisterId, defaultQuorumNumber, numOperators - 1); + cheats.expectEmit(true, true, true, true, address(registryCoordinator)); + emit OperatorRegistered(operatorToRegister, operatorToRegisterId); { ISignatureUtils.SignatureWithSaltAndExpiry memory emptyAVSRegSig; ISignatureUtils.SignatureWithSaltAndExpiry memory signatureWithExpiry = - _signOperatorChurnApproval(operatorToRegister, operatorToRegisterId, operatorKickParams, defaultSalt, block.timestamp + 10); + _signOperatorChurnApproval( + operatorToRegister, + operatorToRegisterId, + operatorKickParams, + defaultSalt, + block.timestamp + 10 + ); cheats.prank(operatorToRegister); uint256 gasBefore = gasleft(); registryCoordinator.registerOperatorWithChurn( - quorumNumbers, + quorumNumbers, defaultSocket, pubkeyRegistrationParams, - operatorKickParams, + operatorKickParams, signatureWithExpiry, emptyAVSRegSig ); @@ -1421,38 +1754,54 @@ contract RegistryCoordinatorUnitTests_RegisterOperatorWithChurn is RegistryCoord } assertEq( - keccak256(abi.encode(registryCoordinator.getOperator(operatorToRegister))), - keccak256(abi.encode(IRegistryCoordinator.OperatorInfo({ - operatorId: operatorToRegisterId, - status: IRegistryCoordinator.OperatorStatus.REGISTERED - }))) + keccak256(abi.encode(registryCoordinator.getOperator(operatorToRegister))), + keccak256( + abi.encode( + ISlashingRegistryCoordinatorTypes.OperatorInfo({ + operatorId: operatorToRegisterId, + status: ISlashingRegistryCoordinatorTypes.OperatorStatus.REGISTERED + }) + ) + ) ); assertEq( - keccak256(abi.encode(registryCoordinator.getOperator(operatorToKick))), - keccak256(abi.encode(IRegistryCoordinator.OperatorInfo({ - operatorId: operatorToKickId, - status: IRegistryCoordinator.OperatorStatus.DEREGISTERED - }))) + keccak256(abi.encode(registryCoordinator.getOperator(operatorToKick))), + keccak256( + abi.encode( + ISlashingRegistryCoordinatorTypes.OperatorInfo({ + operatorId: operatorToKickId, + status: ISlashingRegistryCoordinatorTypes.OperatorStatus.DEREGISTERED + }) + ) + ) ); assertEq( - keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(operatorToKickId, 0))), - keccak256(abi.encode(IRegistryCoordinator.QuorumBitmapUpdate({ - quorumBitmap: uint192(quorumBitmap), - updateBlockNumber: kickRegistrationBlockNumber, - nextUpdateBlockNumber: registrationBlockNumber - }))) + keccak256( + abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(operatorToKickId, 0)) + ), + keccak256( + abi.encode( + ISlashingRegistryCoordinatorTypes.QuorumBitmapUpdate({ + quorumBitmap: uint192(quorumBitmap), + updateBlockNumber: kickRegistrationBlockNumber, + nextUpdateBlockNumber: registrationBlockNumber + }) + ) + ) ); } - function test_registerOperatorWithChurn_revert_lessThanKickBIPsOfOperatorStake(uint256 pseudoRandomNumber) public { + function test_registerOperatorWithChurn_revert_lessThanKickBIPsOfOperatorStake( + uint256 pseudoRandomNumber + ) public { bytes memory quorumNumbers = new bytes(1); quorumNumbers[0] = bytes1(defaultQuorumNumber); ISignatureUtils.SignatureWithSaltAndExpiry memory emptyAVSRegSig; - ( - address operatorToRegister, + ( + address operatorToRegister, BN254.G1Point memory operatorToRegisterPubKey, - IRegistryCoordinator.OperatorKickParam[] memory operatorKickParams + ISlashingRegistryCoordinatorTypes.OperatorKickParam[] memory operatorKickParams ) = _test_registerOperatorWithChurn_SetUp(pseudoRandomNumber, quorumNumbers, defaultStake); bytes32 operatorToRegisterId = BN254.hashG1Point(operatorToRegisterPubKey); @@ -1460,60 +1809,81 @@ contract RegistryCoordinatorUnitTests_RegisterOperatorWithChurn is RegistryCoord cheats.roll(registrationBlockNumber); ISignatureUtils.SignatureWithSaltAndExpiry memory signatureWithExpiry = - _signOperatorChurnApproval(operatorToRegister, operatorToRegisterId, operatorKickParams, defaultSalt, block.timestamp + 10); + _signOperatorChurnApproval( + operatorToRegister, + operatorToRegisterId, + operatorKickParams, + defaultSalt, + block.timestamp + 10 + ); cheats.prank(operatorToRegister); - cheats.expectRevert("RegCoord._validateChurn: incoming operator has insufficient stake for churn"); + cheats.expectRevert(bytes4(keccak256("InsufficientStakeForChurn()"))); registryCoordinator.registerOperatorWithChurn( - quorumNumbers, + quorumNumbers, defaultSocket, pubkeyRegistrationParams, - operatorKickParams, + operatorKickParams, signatureWithExpiry, emptyAVSRegSig ); } - function test_registerOperatorWithChurn_revert_lessThanKickBIPsOfTotalStake(uint256 pseudoRandomNumber) public { + function test_registerOperatorWithChurn_revert_lessThanKickBIPsOfTotalStake( + uint256 pseudoRandomNumber + ) public { bytes memory quorumNumbers = new bytes(1); quorumNumbers[0] = bytes1(defaultQuorumNumber); ISignatureUtils.SignatureWithSaltAndExpiry memory emptyAVSRegSig; uint96 operatorToKickStake = defaultMaxOperatorCount * defaultStake; - ( - address operatorToRegister, + ( + address operatorToRegister, BN254.G1Point memory operatorToRegisterPubKey, - IRegistryCoordinator.OperatorKickParam[] memory operatorKickParams - ) = _test_registerOperatorWithChurn_SetUp(pseudoRandomNumber, quorumNumbers, operatorToKickStake); + ISlashingRegistryCoordinatorTypes.OperatorKickParam[] memory operatorKickParams + ) = _test_registerOperatorWithChurn_SetUp( + pseudoRandomNumber, quorumNumbers, operatorToKickStake + ); bytes32 operatorToRegisterId = BN254.hashG1Point(operatorToRegisterPubKey); - // set the stake of the operator to register to the defaultKickBIPsOfOperatorStake multiple of the operatorToKickStake - _setOperatorWeight(operatorToRegister, defaultQuorumNumber, operatorToKickStake * defaultKickBIPsOfOperatorStake / 10000 + 1); + _setOperatorWeight( + operatorToRegister, + defaultQuorumNumber, + operatorToKickStake * defaultKickBIPsOfOperatorStake / 10000 + 1 + ); cheats.roll(registrationBlockNumber); ISignatureUtils.SignatureWithSaltAndExpiry memory signatureWithExpiry = - _signOperatorChurnApproval(operatorToRegister, operatorToRegisterId, operatorKickParams, defaultSalt, block.timestamp + 10); + _signOperatorChurnApproval( + operatorToRegister, + operatorToRegisterId, + operatorKickParams, + defaultSalt, + block.timestamp + 10 + ); cheats.prank(operatorToRegister); - cheats.expectRevert("RegCoord._validateChurn: cannot kick operator with more than kickBIPsOfTotalStake"); + cheats.expectRevert(bytes4(keccak256("CannotKickOperatorAboveThreshold()"))); registryCoordinator.registerOperatorWithChurn( - quorumNumbers, + quorumNumbers, defaultSocket, pubkeyRegistrationParams, - operatorKickParams, + operatorKickParams, signatureWithExpiry, emptyAVSRegSig ); } - function test_registerOperatorWithChurn_revert_invalidChurnApproverSignature(uint256 pseudoRandomNumber) public { + function test_registerOperatorWithChurn_revert_invalidChurnApproverSignature( + uint256 pseudoRandomNumber + ) public { bytes memory quorumNumbers = new bytes(1); quorumNumbers[0] = bytes1(defaultQuorumNumber); ISignatureUtils.SignatureWithSaltAndExpiry memory emptyAVSRegSig; - ( - address operatorToRegister, + ( + address operatorToRegister, , - IRegistryCoordinator.OperatorKickParam[] memory operatorKickParams + ISlashingRegistryCoordinatorTypes.OperatorKickParam[] memory operatorKickParams ) = _test_registerOperatorWithChurn_SetUp(pseudoRandomNumber, quorumNumbers, defaultStake); uint96 registeringStake = defaultKickBIPsOfOperatorStake * defaultStake; @@ -1526,26 +1896,28 @@ contract RegistryCoordinatorUnitTests_RegisterOperatorWithChurn is RegistryCoord hex"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001B"; signatureWithSaltAndExpiry.salt = defaultSalt; cheats.prank(operatorToRegister); - cheats.expectRevert("ECDSA: invalid signature"); + cheats.expectRevert(bytes4(keccak256("InvalidSignature()"))); registryCoordinator.registerOperatorWithChurn( - quorumNumbers, + quorumNumbers, defaultSocket, pubkeyRegistrationParams, - operatorKickParams, + operatorKickParams, signatureWithSaltAndExpiry, emptyAVSRegSig ); } - function test_registerOperatorWithChurn_revert_expiredChurnApproverSignature(uint256 pseudoRandomNumber) public { + function test_registerOperatorWithChurn_revert_expiredChurnApproverSignature( + uint256 pseudoRandomNumber + ) public { bytes memory quorumNumbers = new bytes(1); quorumNumbers[0] = bytes1(defaultQuorumNumber); ISignatureUtils.SignatureWithSaltAndExpiry memory emptyAVSRegSig; - ( - address operatorToRegister, + ( + address operatorToRegister, BN254.G1Point memory operatorToRegisterPubKey, - IRegistryCoordinator.OperatorKickParam[] memory operatorKickParams + ISlashingRegistryCoordinatorTypes.OperatorKickParam[] memory operatorKickParams ) = _test_registerOperatorWithChurn_SetUp(pseudoRandomNumber, quorumNumbers, defaultStake); bytes32 operatorToRegisterId = BN254.hashG1Point(operatorToRegisterPubKey); @@ -1554,14 +1926,20 @@ contract RegistryCoordinatorUnitTests_RegisterOperatorWithChurn is RegistryCoord cheats.roll(registrationBlockNumber); ISignatureUtils.SignatureWithSaltAndExpiry memory signatureWithSaltAndExpiry = - _signOperatorChurnApproval(operatorToRegister, operatorToRegisterId, operatorKickParams, defaultSalt, block.timestamp - 1); + _signOperatorChurnApproval( + operatorToRegister, + operatorToRegisterId, + operatorKickParams, + defaultSalt, + block.timestamp - 1 + ); cheats.prank(operatorToRegister); - cheats.expectRevert("RegCoord._verifyChurnApproverSignature: churnApprover signature expired"); + cheats.expectRevert(bytes4(keccak256("SignatureExpired()"))); registryCoordinator.registerOperatorWithChurn( - quorumNumbers, + quorumNumbers, defaultSocket, pubkeyRegistrationParams, - operatorKickParams, + operatorKickParams, signatureWithSaltAndExpiry, emptyAVSRegSig ); @@ -1576,7 +1954,7 @@ contract RegistryCoordinatorUnitTests_UpdateOperators is RegistryCoordinatorUnit address[] memory operatorsToUpdate = new address[](1); operatorsToUpdate[0] = defaultOperator; - cheats.expectRevert(bytes("Pausable: index is paused")); + cheats.expectRevert(bytes4(keccak256("CurrentlyPaused()"))); registryCoordinator.updateOperators(operatorsToUpdate); } @@ -1590,7 +1968,9 @@ contract RegistryCoordinatorUnitTests_UpdateOperators is RegistryCoordinatorUnit _setOperatorWeight(defaultOperator, uint8(quorumNumbers[0]), defaultStake); cheats.startPrank(defaultOperator); cheats.roll(registrationBlockNumber); - registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); + registryCoordinator.registerOperator( + quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig + ); address[] memory operatorsToUpdate = new address[](1); operatorsToUpdate[0] = defaultOperator; @@ -1600,7 +1980,10 @@ contract RegistryCoordinatorUnitTests_UpdateOperators is RegistryCoordinatorUnit // @notice tests the `updateOperators` function with a single registered operator as input // @dev also sets up return data from the StakeRegistry - function testFuzz_updateOperators_singleOperator(uint192 registrationBitmap, uint192 mockReturnData) public { + function testFuzz_updateOperators_singleOperator( + uint192 registrationBitmap, + uint192 mockReturnData + ) public { // filter fuzzed inputs to only valid inputs cheats.assume(registrationBitmap != 0); mockReturnData = (mockReturnData & registrationBitmap); @@ -1611,11 +1994,13 @@ contract RegistryCoordinatorUnitTests_UpdateOperators is RegistryCoordinatorUnit uint32 registrationBlockNumber = 100; bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(registrationBitmap); for (uint256 i = 0; i < quorumNumbers.length; ++i) { - _setOperatorWeight(defaultOperator, uint8(quorumNumbers[i]), defaultStake); + _setOperatorWeight(defaultOperator, uint8(quorumNumbers[i]), defaultStake); } cheats.startPrank(defaultOperator); cheats.roll(registrationBlockNumber); - registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); + registryCoordinator.registerOperator( + quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig + ); address[] memory operatorsToUpdate = new address[](1); operatorsToUpdate[0] = defaultOperator; @@ -1627,7 +2012,7 @@ contract RegistryCoordinatorUnitTests_UpdateOperators is RegistryCoordinatorUnit uint192 quorumBitmapToRemove = mockReturnData; bytes memory quorumNumbersToRemove = BitmapUtils.bitmapToBytesArray(quorumBitmapToRemove); for (uint256 i = 0; i < quorumNumbersToRemove.length; ++i) { - _setOperatorWeight(defaultOperator, uint8(quorumNumbersToRemove[i]), 0); + _setOperatorWeight(defaultOperator, uint8(quorumNumbersToRemove[i]), 0); } uint256 expectedQuorumBitmap = BitmapUtils.minus(quorumBitmapBefore, quorumBitmapToRemove); @@ -1642,7 +2027,9 @@ contract RegistryCoordinatorUnitTests_UpdateOperators is RegistryCoordinatorUnit operatorsToUpdate[0] = defaultOperator; // force a staticcall to the `updateOperators` function -- this should *pass* because the call should be a strict no-op! - (bool success, ) = address(registryCoordinator).staticcall(abi.encodeWithSignature("updateOperators(address[])", operatorsToUpdate)); + (bool success,) = address(registryCoordinator).staticcall( + abi.encodeWithSignature("updateOperators(address[])", operatorsToUpdate) + ); require(success, "staticcall failed!"); } @@ -1652,12 +2039,12 @@ contract RegistryCoordinatorUnitTests_UpdateOperators is RegistryCoordinatorUnit address[][] memory operatorsToUpdate = new address[][](1); address[] memory operatorArray = new address[](1); - operatorArray[0] = defaultOperator; + operatorArray[0] = defaultOperator; operatorsToUpdate[0] = operatorArray; bytes memory quorumNumbers = new bytes(1); quorumNumbers[0] = bytes1(defaultQuorumNumber); - cheats.expectRevert(bytes("Pausable: index is paused")); + cheats.expectRevert(bytes4(keccak256("CurrentlyPaused()"))); registryCoordinator.updateOperatorsForQuorum(operatorsToUpdate, quorumNumbers); } @@ -1668,7 +2055,7 @@ contract RegistryCoordinatorUnitTests_UpdateOperators is RegistryCoordinatorUnit address[][] memory operatorsToUpdate = new address[][](1); cheats.prank(defaultOperator); - cheats.expectRevert("BitmapUtils.orderedBytesArrayToBitmap: bitmap exceeds max value"); + cheats.expectRevert(BitmapUtils.BitmapValueTooLarge.selector); registryCoordinator.updateOperatorsForQuorum(operatorsToUpdate, quorumNumbersNotCreated); } @@ -1677,19 +2064,19 @@ contract RegistryCoordinatorUnitTests_UpdateOperators is RegistryCoordinatorUnit bytes memory quorumNumbers = new bytes(1); quorumNumbers[0] = bytes1(defaultQuorumNumber); - cheats.expectRevert(bytes("RegCoord.updateOperatorsForQuorum: input length mismatch")); + cheats.expectRevert(bytes4(keccak256("InputLengthMismatch()"))); registryCoordinator.updateOperatorsForQuorum(operatorsToUpdate, quorumNumbers); } function test_updateOperatorsForQuorum_revert_incorrectNumberOfOperators() public { address[][] memory operatorsToUpdate = new address[][](1); address[] memory operatorArray = new address[](1); - operatorArray[0] = defaultOperator; + operatorArray[0] = defaultOperator; operatorsToUpdate[0] = operatorArray; bytes memory quorumNumbers = new bytes(1); quorumNumbers[0] = bytes1(defaultQuorumNumber); - cheats.expectRevert(bytes("RegCoord.updateOperatorsForQuorum: number of updated operators does not match quorum total")); + cheats.expectRevert(bytes4(keccak256("QuorumOperatorCountMismatch()"))); registryCoordinator.updateOperatorsForQuorum(operatorsToUpdate, quorumNumbers); } @@ -1702,20 +2089,24 @@ contract RegistryCoordinatorUnitTests_UpdateOperators is RegistryCoordinatorUnit _setOperatorWeight(defaultOperator, uint8(quorumNumbers[0]), defaultStake); cheats.startPrank(defaultOperator); cheats.roll(registrationBlockNumber); - registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); + registryCoordinator.registerOperator( + quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig + ); address[][] memory operatorsToUpdate = new address[][](1); address[] memory operatorArray = new address[](1); // use an unregistered operator address as input - operatorArray[0] = _incrementAddress(defaultOperator, 1); + operatorArray[0] = _incrementAddress(defaultOperator, 1); operatorsToUpdate[0] = operatorArray; - cheats.expectRevert(bytes("RegCoord.updateOperatorsForQuorum: operator not in quorum")); + cheats.expectRevert(bytes4(keccak256("NotRegisteredForQuorum()"))); registryCoordinator.updateOperatorsForQuorum(operatorsToUpdate, quorumNumbers); } // note: there is not an explicit check for duplicates, as checking for explicit ordering covers this - function test_updateOperatorsForQuorum_revert_duplicateOperator(uint256 pseudoRandomNumber) public { + function test_updateOperatorsForQuorum_revert_duplicateOperator( + uint256 pseudoRandomNumber + ) public { // register 2 operators uint32 numOperators = 2; uint32 registrationBlockNumber = 200; @@ -1723,26 +2114,29 @@ contract RegistryCoordinatorUnitTests_UpdateOperators is RegistryCoordinatorUnit quorumNumbers[0] = bytes1(defaultQuorumNumber); uint256 quorumBitmap = BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers); cheats.roll(registrationBlockNumber); - for (uint i = 0; i < numOperators; i++) { - BN254.G1Point memory pubKey = BN254.hashToG1(keccak256(abi.encodePacked(pseudoRandomNumber, i))); + for (uint256 i = 0; i < numOperators; i++) { + BN254.G1Point memory pubKey = + BN254.hashToG1(keccak256(abi.encodePacked(pseudoRandomNumber, i))); address operator = _incrementAddress(defaultOperator, i); - + _registerOperatorWithCoordinator(operator, quorumBitmap, pubKey); } address[][] memory operatorsToUpdate = new address[][](1); address[] memory operatorArray = new address[](2); // use the same operator address twice as input - operatorArray[0] = defaultOperator; - operatorArray[1] = defaultOperator; + operatorArray[0] = defaultOperator; + operatorArray[1] = defaultOperator; operatorsToUpdate[0] = operatorArray; // note: there is not an explicit check for duplicates, as checking for explicit ordering covers this - cheats.expectRevert(bytes("RegCoord.updateOperatorsForQuorum: operators array must be sorted in ascending address order")); + cheats.expectRevert(bytes4(keccak256("NotSorted()"))); registryCoordinator.updateOperatorsForQuorum(operatorsToUpdate, quorumNumbers); } - function test_updateOperatorsForQuorum_revert_incorrectListOrder(uint256 pseudoRandomNumber) public { + function test_updateOperatorsForQuorum_revert_incorrectListOrder( + uint256 pseudoRandomNumber + ) public { // register 2 operators uint32 numOperators = 2; uint32 registrationBlockNumber = 200; @@ -1750,21 +2144,22 @@ contract RegistryCoordinatorUnitTests_UpdateOperators is RegistryCoordinatorUnit quorumNumbers[0] = bytes1(defaultQuorumNumber); uint256 quorumBitmap = BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers); cheats.roll(registrationBlockNumber); - for (uint i = 0; i < numOperators; i++) { - BN254.G1Point memory pubKey = BN254.hashToG1(keccak256(abi.encodePacked(pseudoRandomNumber, i))); + for (uint256 i = 0; i < numOperators; i++) { + BN254.G1Point memory pubKey = + BN254.hashToG1(keccak256(abi.encodePacked(pseudoRandomNumber, i))); address operator = _incrementAddress(defaultOperator, i); - + _registerOperatorWithCoordinator(operator, quorumBitmap, pubKey); } address[][] memory operatorsToUpdate = new address[][](1); address[] memory operatorArray = new address[](2); // order the operator addresses in descending order, instead of ascending order - operatorArray[0] = _incrementAddress(defaultOperator, 1); - operatorArray[1] = defaultOperator; + operatorArray[0] = _incrementAddress(defaultOperator, 1); + operatorArray[1] = defaultOperator; operatorsToUpdate[0] = operatorArray; - cheats.expectRevert(bytes("RegCoord.updateOperatorsForQuorum: operators array must be sorted in ascending address order")); + cheats.expectRevert(bytes4(keccak256("NotSorted()"))); registryCoordinator.updateOperatorsForQuorum(operatorsToUpdate, quorumNumbers); } @@ -1777,25 +2172,33 @@ contract RegistryCoordinatorUnitTests_UpdateOperators is RegistryCoordinatorUnit _setOperatorWeight(defaultOperator, uint8(quorumNumbers[0]), defaultStake); cheats.startPrank(defaultOperator); cheats.roll(registrationBlockNumber); - registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); + registryCoordinator.registerOperator( + quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig + ); address[][] memory operatorsToUpdate = new address[][](1); address[] memory operatorArray = new address[](1); - operatorArray[0] = defaultOperator; + operatorArray[0] = defaultOperator; operatorsToUpdate[0] = operatorArray; - uint256 quorumUpdateBlockNumberBefore = registryCoordinator.quorumUpdateBlockNumber(defaultQuorumNumber); + uint256 quorumUpdateBlockNumberBefore = + registryCoordinator.quorumUpdateBlockNumber(defaultQuorumNumber); require(quorumUpdateBlockNumberBefore != block.number, "bad test setup!"); cheats.expectEmit(true, true, true, true, address(registryCoordinator)); emit QuorumBlockNumberUpdated(defaultQuorumNumber, block.number); registryCoordinator.updateOperatorsForQuorum(operatorsToUpdate, quorumNumbers); - uint256 quorumUpdateBlockNumberAfter = registryCoordinator.quorumUpdateBlockNumber(defaultQuorumNumber); - assertEq(quorumUpdateBlockNumberAfter, block.number, "quorumUpdateBlockNumber not set correctly"); + uint256 quorumUpdateBlockNumberAfter = + registryCoordinator.quorumUpdateBlockNumber(defaultQuorumNumber); + assertEq( + quorumUpdateBlockNumberAfter, block.number, "quorumUpdateBlockNumber not set correctly" + ); } - function test_updateOperatorsForQuorum_twoOperators(uint256 pseudoRandomNumber) public { + function test_updateOperatorsForQuorum_twoOperators( + uint256 pseudoRandomNumber + ) public { // register 2 operators uint32 numOperators = 2; uint32 registrationBlockNumber = 200; @@ -1803,62 +2206,85 @@ contract RegistryCoordinatorUnitTests_UpdateOperators is RegistryCoordinatorUnit quorumNumbers[0] = bytes1(defaultQuorumNumber); uint256 quorumBitmap = BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers); cheats.roll(registrationBlockNumber); - for (uint i = 0; i < numOperators; i++) { - BN254.G1Point memory pubKey = BN254.hashToG1(keccak256(abi.encodePacked(pseudoRandomNumber, i))); + for (uint256 i = 0; i < numOperators; i++) { + BN254.G1Point memory pubKey = + BN254.hashToG1(keccak256(abi.encodePacked(pseudoRandomNumber, i))); address operator = _incrementAddress(defaultOperator, i); - + _registerOperatorWithCoordinator(operator, quorumBitmap, pubKey); } address[][] memory operatorsToUpdate = new address[][](1); address[] memory operatorArray = new address[](2); // order the operator addresses in descending order, instead of ascending order - operatorArray[0] = defaultOperator; - operatorArray[1] = _incrementAddress(defaultOperator, 1); + operatorArray[0] = defaultOperator; + operatorArray[1] = _incrementAddress(defaultOperator, 1); operatorsToUpdate[0] = operatorArray; - uint256 quorumUpdateBlockNumberBefore = registryCoordinator.quorumUpdateBlockNumber(defaultQuorumNumber); + uint256 quorumUpdateBlockNumberBefore = + registryCoordinator.quorumUpdateBlockNumber(defaultQuorumNumber); require(quorumUpdateBlockNumberBefore != block.number, "bad test setup!"); cheats.expectEmit(true, true, true, true, address(registryCoordinator)); emit QuorumBlockNumberUpdated(defaultQuorumNumber, block.number); registryCoordinator.updateOperatorsForQuorum(operatorsToUpdate, quorumNumbers); - uint256 quorumUpdateBlockNumberAfter = registryCoordinator.quorumUpdateBlockNumber(defaultQuorumNumber); - assertEq(quorumUpdateBlockNumberAfter, block.number, "quorumUpdateBlockNumber not set correctly"); + uint256 quorumUpdateBlockNumberAfter = + registryCoordinator.quorumUpdateBlockNumber(defaultQuorumNumber); + assertEq( + quorumUpdateBlockNumberAfter, block.number, "quorumUpdateBlockNumber not set correctly" + ); } // @notice tests that the internal `_updateOperatorBitmap` function works as expected, for fuzzed inputs - function testFuzz_updateOperatorBitmapInternal_noPreviousEntries(uint192 newBitmap) public { + function testFuzz_updateOperatorBitmapInternal_noPreviousEntries( + uint192 newBitmap + ) public { registryCoordinator._updateOperatorBitmapExternal(defaultOperatorId, newBitmap); assertEq( - keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0))), - keccak256(abi.encode(IRegistryCoordinator.QuorumBitmapUpdate({ - quorumBitmap: uint192(newBitmap), - updateBlockNumber: uint32(block.number), - nextUpdateBlockNumber: 0 - }))) + keccak256( + abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0)) + ), + keccak256( + abi.encode( + ISlashingRegistryCoordinatorTypes.QuorumBitmapUpdate({ + quorumBitmap: uint192(newBitmap), + updateBlockNumber: uint32(block.number), + nextUpdateBlockNumber: 0 + }) + ) + ) ); } // @notice tests that the internal `_updateOperatorBitmap` function works as expected, for fuzzed inputs - function testFuzz_updateOperatorBitmapInternal_previousEntryInCurrentBlock(uint192 newBitmap) public { + function testFuzz_updateOperatorBitmapInternal_previousEntryInCurrentBlock( + uint192 newBitmap + ) public { uint192 pastBitmap = 1; testFuzz_updateOperatorBitmapInternal_noPreviousEntries(pastBitmap); registryCoordinator._updateOperatorBitmapExternal(defaultOperatorId, newBitmap); assertEq( - keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0))), - keccak256(abi.encode(IRegistryCoordinator.QuorumBitmapUpdate({ - quorumBitmap: uint192(newBitmap), - updateBlockNumber: uint32(block.number), - nextUpdateBlockNumber: 0 - }))) + keccak256( + abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0)) + ), + keccak256( + abi.encode( + ISlashingRegistryCoordinatorTypes.QuorumBitmapUpdate({ + quorumBitmap: uint192(newBitmap), + updateBlockNumber: uint32(block.number), + nextUpdateBlockNumber: 0 + }) + ) + ) ); } // @notice tests that the internal `_updateOperatorBitmap` function works as expected, for fuzzed inputs - function testFuzz_updateOperatorBitmapInternal_previousEntryInPastBlock(uint192 newBitmap) public { + function testFuzz_updateOperatorBitmapInternal_previousEntryInPastBlock( + uint192 newBitmap + ) public { uint192 pastBitmap = 1; testFuzz_updateOperatorBitmapInternal_noPreviousEntries(pastBitmap); @@ -1868,20 +2294,535 @@ contract RegistryCoordinatorUnitTests_UpdateOperators is RegistryCoordinatorUnit registryCoordinator._updateOperatorBitmapExternal(defaultOperatorId, newBitmap); assertEq( - keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0))), - keccak256(abi.encode(IRegistryCoordinator.QuorumBitmapUpdate({ - quorumBitmap: uint192(pastBitmap), - updateBlockNumber: uint32(previousBlockNumber), - nextUpdateBlockNumber: uint32(block.number) - }))) + keccak256( + abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0)) + ), + keccak256( + abi.encode( + ISlashingRegistryCoordinatorTypes.QuorumBitmapUpdate({ + quorumBitmap: uint192(pastBitmap), + updateBlockNumber: uint32(previousBlockNumber), + nextUpdateBlockNumber: uint32(block.number) + }) + ) + ) ); assertEq( - keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 1))), - keccak256(abi.encode(IRegistryCoordinator.QuorumBitmapUpdate({ - quorumBitmap: uint192(newBitmap), - updateBlockNumber: uint32(block.number), - nextUpdateBlockNumber: 0 - }))) + keccak256( + abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 1)) + ), + keccak256( + abi.encode( + ISlashingRegistryCoordinatorTypes.QuorumBitmapUpdate({ + quorumBitmap: uint192(newBitmap), + updateBlockNumber: uint32(block.number), + nextUpdateBlockNumber: 0 + }) + ) + ) + ); + } +} + +contract RegistryCoordinatorUnitTests_BeforeMigration is RegistryCoordinatorUnitTests { + function test_registerALMHook_Reverts() public { + cheats.prank(address(registryCoordinator.allocationManager())); + cheats.expectRevert(); + registryCoordinator.registerOperator( + defaultOperator, new uint32[](0), abi.encode(defaultSocket, pubkeyRegistrationParams) + ); + } + + function test_deregisterALMHook_Reverts() public { + uint32[] memory operatorSetIds = new uint32[](1); + operatorSetIds[0] = 0; + cheats.prank(address(registryCoordinator.allocationManager())); + cheats.expectRevert(); + registryCoordinator.deregisterOperator(defaultOperator, operatorSetIds); + } + + function test_CreateTotalDelegatedStakeQuorum() public { + _deployMockEigenLayerAndAVS(0); + // Set up test params + ISlashingRegistryCoordinatorTypes.OperatorSetParam memory operatorSetParams = + ISlashingRegistryCoordinatorTypes.OperatorSetParam({ + maxOperatorCount: 10, + kickBIPsOfOperatorStake: 0, + kickBIPsOfTotalStake: 0 + }); + uint96 minimumStake = 100; + IStakeRegistryTypes.StrategyParams[] memory strategyParams = + new IStakeRegistryTypes.StrategyParams[](1); + strategyParams[0] = IStakeRegistryTypes.StrategyParams({ + strategy: IStrategy(address(0x1)), + multiplier: 1000 + }); + + // Get initial quorum count + uint8 initialQuorumCount = registryCoordinator.quorumCount(); + + // Create quorum with total delegated stake type + cheats.prank(registryCoordinatorOwner); + registryCoordinator.createTotalDelegatedStakeQuorum( + operatorSetParams, minimumStake, strategyParams + ); + + // Verify quorum was created + assertEq(registryCoordinator.quorumCount(), initialQuorumCount + 1); + + // Verify quorum params were set correctly + ISlashingRegistryCoordinatorTypes.OperatorSetParam memory storedParams = + registryCoordinator.getOperatorSetParams(initialQuorumCount); + assertEq(storedParams.maxOperatorCount, operatorSetParams.maxOperatorCount); + assertEq(storedParams.kickBIPsOfOperatorStake, operatorSetParams.kickBIPsOfOperatorStake); + assertEq(storedParams.kickBIPsOfTotalStake, operatorSetParams.kickBIPsOfTotalStake); + } + + function test_CreateSlashableStakeQuorum_Reverts() public { + _deployMockEigenLayerAndAVS(0); + ISlashingRegistryCoordinatorTypes.OperatorSetParam memory operatorSetParams = + ISlashingRegistryCoordinatorTypes.OperatorSetParam({ + maxOperatorCount: 10, + kickBIPsOfOperatorStake: 0, + kickBIPsOfTotalStake: 0 + }); + uint96 minimumStake = 100; + IStakeRegistryTypes.StrategyParams[] memory strategyParams = + new IStakeRegistryTypes.StrategyParams[](1); + strategyParams[0] = IStakeRegistryTypes.StrategyParams({ + strategy: IStrategy(address(0x1)), + multiplier: 1000 + }); + uint32 lookAheadPeriod = 100; + + // Attempt to create quorum with slashable stake type before enabling operator sets + cheats.prank(registryCoordinatorOwner); + cheats.expectRevert(); + registryCoordinator.createSlashableStakeQuorum( + operatorSetParams, minimumStake, strategyParams, lookAheadPeriod + ); + } + + function test_MigrateToOperatorSets() public { + _deployMockEigenLayerAndAVS(0); + cheats.prank(registryCoordinatorOwner); + registryCoordinator.enableOperatorSets(); + assertTrue(registryCoordinator.operatorSetsEnabled()); + } +} + +contract RegistryCoordinatorUnitTests_AfterMigration is RegistryCoordinatorUnitTests { + function test_MigrateToOperatorSets() public { + cheats.prank(registryCoordinatorOwner); + registryCoordinator.enableOperatorSets(); + assertTrue(registryCoordinator.operatorSetsEnabled()); + } + + function test_M2_Deregister() public { + vm.skip(true); + /// Create 2 M2 quorums + _deployMockEigenLayerAndAVS(2); + + address operatorToRegister = address(420); + + ISignatureUtils.SignatureWithSaltAndExpiry memory emptySignature = ISignatureUtils + .SignatureWithSaltAndExpiry({signature: new bytes(0), salt: bytes32(0), expiry: 0}); + + IBLSApkRegistryTypes.PubkeyRegistrationParams memory operatorRegisterApkParams = + IBLSApkRegistryTypes.PubkeyRegistrationParams({ + pubkeyRegistrationSignature: BN254.G1Point({X: 0, Y: 0}), + pubkeyG1: BN254.G1Point({X: 0, Y: 0}), + pubkeyG2: BN254.G2Point({X: [uint256(0), uint256(0)], Y: [uint256(0), uint256(0)]}) + }); + + string memory socket = "socket"; + + // register for quorum 0 + vm.prank(operatorToRegister); + registryCoordinator.registerOperator( + new bytes(1), // Convert 0 to bytes1 first + socket, + operatorRegisterApkParams, + emptySignature + ); + + /// migrate to operator sets + registryCoordinator.enableOperatorSets(); + + /// Deregistration for m2 should for the first two operator sets + vm.prank(defaultOperator); + registryCoordinator.deregisterOperator(new bytes(1)); + + // Verify operator was deregistered by checking their bitmap is empty + bytes32 operatorId = registryCoordinator.getOperatorId(operatorToRegister); + uint192 bitmap = registryCoordinator.getCurrentQuorumBitmap(operatorId); + assertEq(bitmap, 0, "Operator bitmap should be empty after deregistration"); + + // Verify operator status is NEVER_REGISTERED + ISlashingRegistryCoordinatorTypes.OperatorStatus status = + registryCoordinator.getOperatorStatus(operatorToRegister); + assertEq( + uint8(status), + uint8(ISlashingRegistryCoordinatorTypes.OperatorStatus.NEVER_REGISTERED), + "Operator status should be NEVER_REGISTERED" + ); + } + + function test_M2_Register_Reverts() public { + cheats.prank(registryCoordinatorOwner); + registryCoordinator.enableOperatorSets(); + + bytes memory quorumNumbers = new bytes(1); + quorumNumbers[0] = bytes1(uint8(0)); + IBLSApkRegistryTypes.PubkeyRegistrationParams memory params; + ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature; + + cheats.expectRevert(); + registryCoordinator.registerOperator( + quorumNumbers, defaultSocket, params, operatorSignature + ); + } + + function test_createSlashableStakeQuorum() public { + // Deploy with 0 quorums + _deployMockEigenLayerAndAVS(0); + + // Enable operator sets first + cheats.prank(registryCoordinatorOwner); + registryCoordinator.enableOperatorSets(); + + // Create quorum params + ISlashingRegistryCoordinatorTypes.OperatorSetParam memory operatorSetParams = + ISlashingRegistryCoordinatorTypes.OperatorSetParam({ + maxOperatorCount: 10, + kickBIPsOfOperatorStake: 1000, + kickBIPsOfTotalStake: 100 + }); + uint96 minimumStake = 100; + IStakeRegistryTypes.StrategyParams[] memory strategyParams = + new IStakeRegistryTypes.StrategyParams[](1); + strategyParams[0] = + IStakeRegistryTypes.StrategyParams({strategy: IStrategy(address(1)), multiplier: 1}); + uint32 lookAheadPeriod = 100; + + // Create slashable stake quorum + cheats.prank(registryCoordinatorOwner); + registryCoordinator.createSlashableStakeQuorum( + operatorSetParams, minimumStake, strategyParams, lookAheadPeriod + ); + } + + function test_createTotalDelegatedStakeQuorum() public { + // Deploy with 0 quorums + _deployMockEigenLayerAndAVS(0); + + // Enable operator sets first + cheats.prank(registryCoordinatorOwner); + registryCoordinator.enableOperatorSets(); + + // Create quorum params + ISlashingRegistryCoordinatorTypes.OperatorSetParam memory operatorSetParams = + ISlashingRegistryCoordinatorTypes.OperatorSetParam({ + maxOperatorCount: 10, + kickBIPsOfOperatorStake: 1000, + kickBIPsOfTotalStake: 100 + }); + uint96 minimumStake = 100; + IStakeRegistryTypes.StrategyParams[] memory strategyParams = + new IStakeRegistryTypes.StrategyParams[](1); + strategyParams[0] = + IStakeRegistryTypes.StrategyParams({strategy: IStrategy(address(1)), multiplier: 10000}); + + // Create total delegated stake quorum + cheats.prank(registryCoordinatorOwner); + registryCoordinator.createTotalDelegatedStakeQuorum( + operatorSetParams, minimumStake, strategyParams + ); + } + + function test_registerHook() public { + vm.skip(true); + + _deployMockEigenLayerAndAVS(0); + + // Enable operator sets first + cheats.prank(registryCoordinatorOwner); + registryCoordinator.enableOperatorSets(); + + // Create quorum params + ISlashingRegistryCoordinatorTypes.OperatorSetParam memory operatorSetParams = + ISlashingRegistryCoordinatorTypes.OperatorSetParam({ + maxOperatorCount: 10, + kickBIPsOfOperatorStake: 1000, + kickBIPsOfTotalStake: 100 + }); + + uint96 minimumStake = 100; + IStakeRegistryTypes.StrategyParams[] memory strategyParams = + new IStakeRegistryTypes.StrategyParams[](1); + strategyParams[0] = + IStakeRegistryTypes.StrategyParams({strategy: IStrategy(address(1)), multiplier: 10000}); + + // Create total delegated stake quorum + cheats.prank(registryCoordinatorOwner); + registryCoordinator.createTotalDelegatedStakeQuorum(operatorSetParams, 0, strategyParams); + + uint32[] memory operatorSetIds = new uint32[](1); + operatorSetIds[0] = 0; + + string memory socket = "socket"; + IBLSApkRegistryTypes.PubkeyRegistrationParams memory params; + // TODO: + // params = IBLSApkRegistryTypes.PubkeyRegistrationParams({ + // pubkeyG1: defaultPubKey, + // pubkeyG2: defaultPubKeyG2, + // pubkeySignature: defaultPubKeySignature + // }); + + // Encode with RegistrationType.NORMAL + bytes memory data = + abi.encode(ISlashingRegistryCoordinatorTypes.RegistrationType.NORMAL, socket, params); + + cheats.prank(address(registryCoordinator.allocationManager())); + registryCoordinator.registerOperator(defaultOperator, operatorSetIds, data); + } + + function test_registerHook_WithChurn() public { + vm.skip(true); + _deployMockEigenLayerAndAVS(0); + // Enable operator sets first + cheats.prank(registryCoordinatorOwner); + registryCoordinator.enableOperatorSets(); + + // Create quorum params + ISlashingRegistryCoordinatorTypes.OperatorSetParam memory operatorSetParams = + ISlashingRegistryCoordinatorTypes.OperatorSetParam({ + maxOperatorCount: 10, + kickBIPsOfOperatorStake: 1000, + kickBIPsOfTotalStake: 100 + }); + + uint96 minimumStake = 100; + IStakeRegistryTypes.StrategyParams[] memory strategyParams = + new IStakeRegistryTypes.StrategyParams[](1); + strategyParams[0] = + IStakeRegistryTypes.StrategyParams({strategy: IStrategy(address(1)), multiplier: 10000}); + + // Create total delegated stake quorum + cheats.prank(registryCoordinatorOwner); + registryCoordinator.createTotalDelegatedStakeQuorum(operatorSetParams, 0, strategyParams); + + uint32[] memory operatorSetIds = new uint32[](1); + operatorSetIds[0] = 0; + + string memory socket = "socket"; + IBLSApkRegistryTypes.PubkeyRegistrationParams memory params; + // TODO: + // params = IBLSApkRegistryTypes.PubkeyRegistrationParams({ + // pubkeyG1: defaultPubKey, + // pubkeyG2: defaultPubKeyG2, + // pubkeySignature: defaultPubKeySignature + // }); + + ISlashingRegistryCoordinatorTypes.OperatorKickParam[] memory operatorKickParams = + new ISlashingRegistryCoordinatorTypes.OperatorKickParam[](1); + operatorKickParams[0] = ISlashingRegistryCoordinatorTypes.OperatorKickParam({ + operator: address(0x1), + quorumNumber: 0 + }); + + ISignatureUtils.SignatureWithSaltAndExpiry memory churnApproverSignature; + + // Encode with RegistrationType.CHURN + bytes memory data = abi.encode( + ISlashingRegistryCoordinatorTypes.RegistrationType.CHURN, + socket, + params, + operatorKickParams, + churnApproverSignature + ); + + // Prank as allocation manager and call register hook + cheats.prank(address(registryCoordinator.allocationManager())); + registryCoordinator.registerOperator(defaultOperator, operatorSetIds, data); + } + + function test_updateStakesForQuorum() public { + vm.skip(true); + _deployMockEigenLayerAndAVS(0); + + ISlashingRegistryCoordinatorTypes.OperatorSetParam memory operatorSetParams = + ISlashingRegistryCoordinatorTypes.OperatorSetParam({ + maxOperatorCount: defaultMaxOperatorCount, + kickBIPsOfOperatorStake: defaultKickBIPsOfOperatorStake, + kickBIPsOfTotalStake: defaultKickBIPsOfTotalStake + }); + + uint96 minimumStake = 100; + IStakeRegistryTypes.StrategyParams[] memory strategyParams = + new IStakeRegistryTypes.StrategyParams[](1); + strategyParams[0] = + IStakeRegistryTypes.StrategyParams({strategy: IStrategy(address(1)), multiplier: 10000}); + + cheats.prank(registryCoordinatorOwner); + registryCoordinator.createTotalDelegatedStakeQuorum( + operatorSetParams, minimumStake, strategyParams ); + + uint256 quorumBitmap = 0; + + // TODO: register actually and update stakes + } + + function test_deregisterHook() public { + _deployMockEigenLayerAndAVS(0); + // Enable operator sets first + cheats.prank(registryCoordinatorOwner); + registryCoordinator.enableOperatorSets(); + + // Create quorum params + ISlashingRegistryCoordinatorTypes.OperatorSetParam memory operatorSetParams = + ISlashingRegistryCoordinatorTypes.OperatorSetParam({ + maxOperatorCount: 10, + kickBIPsOfOperatorStake: 1000, + kickBIPsOfTotalStake: 100 + }); + + uint96 minimumStake = 100; + IStakeRegistryTypes.StrategyParams[] memory strategyParams = + new IStakeRegistryTypes.StrategyParams[](1); + strategyParams[0] = + IStakeRegistryTypes.StrategyParams({strategy: IStrategy(address(1)), multiplier: 10000}); + + // Create total delegated stake quorum + cheats.prank(registryCoordinatorOwner); + registryCoordinator.createTotalDelegatedStakeQuorum(operatorSetParams, 0, strategyParams); + + // Prank as allocation manager and call register hook + uint32[] memory operatorSetIds = new uint32[](1); + operatorSetIds[0] = 0; + + string memory socket = "socket"; + IBLSApkRegistryTypes.PubkeyRegistrationParams memory params; + // TODO: + // params = IBLSApkRegistryTypes.PubkeyRegistrationParams({ + // pubkeyG1: defaultPubKey, + // pubkeyG2: defaultPubKeyG2, + // pubkeySignature: defaultPubKeySignature + // }); + + // Encode with RegistrationType.NORMAL + bytes memory data = + abi.encode(ISlashingRegistryCoordinatorTypes.RegistrationType.NORMAL, socket, params); + + cheats.startPrank(address(registryCoordinator.allocationManager())); + registryCoordinator.registerOperator(defaultOperator, operatorSetIds, data); + + // registryCoordinator.deregisterOperator(defaultOperator, operatorSetIds); + + cheats.stopPrank(); + } + + function test_registerHook_Reverts_WhenNotALM() public { + _deployMockEigenLayerAndAVS(0); + + // Enable operator sets first + cheats.prank(registryCoordinatorOwner); + registryCoordinator.enableOperatorSets(); + + // Create quorum params + ISlashingRegistryCoordinatorTypes.OperatorSetParam memory operatorSetParams = + ISlashingRegistryCoordinatorTypes.OperatorSetParam({ + maxOperatorCount: 10, + kickBIPsOfOperatorStake: 1000, + kickBIPsOfTotalStake: 100 + }); + + uint96 minimumStake = 100; + IStakeRegistryTypes.StrategyParams[] memory strategyParams = + new IStakeRegistryTypes.StrategyParams[](1); + strategyParams[0] = + IStakeRegistryTypes.StrategyParams({strategy: IStrategy(address(1)), multiplier: 10000}); + + // Create total delegated stake quorum + cheats.prank(registryCoordinatorOwner); + registryCoordinator.createTotalDelegatedStakeQuorum(operatorSetParams, 0, strategyParams); + + uint32[] memory operatorSetIds = new uint32[](1); + operatorSetIds[0] = 0; + + string memory socket = "socket"; + IBLSApkRegistryTypes.PubkeyRegistrationParams memory params; + // TODO: + // params = IBLSApkRegistryTypes.PubkeyRegistrationParams({ + // pubkeyG1: defaultPubKey, + // pubkeyG2: defaultPubKeyG2, + // pubkeySignature: defaultPubKeySignature + // }); + + // Encode with RegistrationType.NORMAL + bytes memory data = + abi.encode(ISlashingRegistryCoordinatorTypes.RegistrationType.NORMAL, socket, params); + + vm.expectRevert(); + registryCoordinator.registerOperator(defaultOperator, operatorSetIds, data); + } + + function test_deregisterHook_Reverts_WhenNotALM() public { + _deployMockEigenLayerAndAVS(0); + + // Enable operator sets first + cheats.prank(registryCoordinatorOwner); + registryCoordinator.enableOperatorSets(); + + assertTrue(registryCoordinator.operatorSetsEnabled(), "operatorSetsEnabled should be true"); + + // Create quorum params + ISlashingRegistryCoordinatorTypes.OperatorSetParam memory operatorSetParams = + ISlashingRegistryCoordinatorTypes.OperatorSetParam({ + maxOperatorCount: 10, + kickBIPsOfOperatorStake: 1000, + kickBIPsOfTotalStake: 100 + }); + + uint96 minimumStake = 100; + IStakeRegistryTypes.StrategyParams[] memory strategyParams = + new IStakeRegistryTypes.StrategyParams[](1); + strategyParams[0] = + IStakeRegistryTypes.StrategyParams({strategy: IStrategy(address(1)), multiplier: 10000}); + + // Create total delegated stake quorum + cheats.prank(registryCoordinatorOwner); + registryCoordinator.createTotalDelegatedStakeQuorum(operatorSetParams, 0, strategyParams); + + // Prank as allocation manager and call register hook + uint32[] memory operatorSetIds = new uint32[](1); + operatorSetIds[0] = 0; + + string memory socket = "socket"; + IBLSApkRegistryTypes.PubkeyRegistrationParams memory params; + // TODO: + // params = IBLSApkRegistryTypes.PubkeyRegistrationParams({ + // pubkeyG1: defaultPubKey, + // pubkeyG2: defaultPubKeyG2, + // pubkeySignature: defaultPubKeySignature + // }); + + bytes memory data = + abi.encode(ISlashingRegistryCoordinatorTypes.RegistrationType.NORMAL, socket, params); + + cheats.prank(address(registryCoordinator.allocationManager())); + registryCoordinator.registerOperator(defaultOperator, operatorSetIds, data); + + cheats.expectRevert(); + registryCoordinator.deregisterOperator(defaultOperator, operatorSetIds); + } + + function test_DeregisterHook_Reverts_WhenM2Quorum() public { + vm.skip(true); + } + + function test_registerHook_Reverts_WhenM2Quorum() public { + vm.skip(true); } } diff --git a/test/unit/ServiceManagerBase.t.sol b/test/unit/ServiceManagerBase.t.sol index a7f6464a..d9647ce8 100644 --- a/test/unit/ServiceManagerBase.t.sol +++ b/test/unit/ServiceManagerBase.t.sol @@ -1,26 +1,36 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "@openzeppelin/contracts/token/ERC20/presets/ERC20PresetFixedSupply.sol"; -import {RewardsCoordinator, IRewardsCoordinator, IERC20} from "eigenlayer-contracts/src/contracts/core/RewardsCoordinator.sol"; +import { + RewardsCoordinator, + IRewardsCoordinator, + IRewardsCoordinatorTypes, + IERC20 +} from "eigenlayer-contracts/src/contracts/core/RewardsCoordinator.sol"; +import {PermissionController} from + "eigenlayer-contracts/src/contracts/permissions/PermissionController.sol"; import {StrategyBase} from "eigenlayer-contracts/src/contracts/strategies/StrategyBase.sol"; +import {IStrategyManager} from "eigenlayer-contracts/src/contracts/interfaces/IStrategyManager.sol"; import {IServiceManagerBaseEvents} from "../events/IServiceManagerBaseEvents.sol"; +import {IServiceManagerErrors} from "../../src/interfaces/IServiceManager.sol"; import "../utils/MockAVSDeployer.sol"; -contract ServiceManagerBase_UnitTests is - MockAVSDeployer, - IServiceManagerBaseEvents -{ +contract ServiceManagerBase_UnitTests is MockAVSDeployer, IServiceManagerBaseEvents { // RewardsCoordinator config - address rewardsUpdater = - address(uint160(uint256(keccak256("rewardsUpdater")))); + address rewardsUpdater = address(uint160(uint256(keccak256("rewardsUpdater")))); uint32 CALCULATION_INTERVAL_SECONDS = 7 days; uint32 MAX_REWARDS_DURATION = 70 days; uint32 MAX_RETROACTIVE_LENGTH = 84 days; uint32 MAX_FUTURE_LENGTH = 28 days; - uint32 GENESIS_REWARDS_TIMESTAMP = 1_712_188_800; + uint32 GENESIS_REWARDS_TIMESTAMP = 1712188800; uint256 MAX_REWARDS_AMOUNT = 1e38 - 1; + uint32 OPERATOR_SET_GENESIS_REWARDS_TIMESTAMP = 0; + /// TODO: what values should these have + uint32 OPERATOR_SET_MAX_RETROACTIVE_LENGTH = 0; + /// TODO: What values these should have + /// @notice Delay in timestamp before a posted root can be claimed against uint32 activationDelay = 7 days; /// @notice the commission for all operators across all avss @@ -28,8 +38,7 @@ contract ServiceManagerBase_UnitTests is // Testing Config and Mocks address serviceManagerOwner; - address rewardsInitiator = - address(uint160(uint256(keccak256("rewardsInitiator")))); + address rewardsInitiator = address(uint160(uint256(keccak256("rewardsInitiator")))); IERC20[] rewardTokens; uint256 mockTokenInitialSupply = 10e50; IStrategy strategyMock1; @@ -41,7 +50,9 @@ contract ServiceManagerBase_UnitTests is // mapping to setting fuzzed inputs mapping(address => bool) public addressIsExcludedFromFuzzedInputs; - modifier filterFuzzedAddressInputs(address fuzzedAddress) { + modifier filterFuzzedAddressInputs( + address fuzzedAddress + ) { cheats.assume(!addressIsExcludedFromFuzzedInputs[fuzzedAddress]); _; } @@ -51,7 +62,10 @@ contract ServiceManagerBase_UnitTests is // Deploy rewards coordinator rewardsCoordinatorImplementation = new RewardsCoordinator( delegationMock, - strategyManagerMock, + IStrategyManager(address(strategyManagerMock)), + allocationManagerMock, + pauserRegistry, + permissionControllerMock, CALCULATION_INTERVAL_SECONDS, MAX_REWARDS_DURATION, MAX_RETROACTIVE_LENGTH, @@ -67,8 +81,7 @@ contract ServiceManagerBase_UnitTests is abi.encodeWithSelector( RewardsCoordinator.initialize.selector, msg.sender, - pauserRegistry, - 0 /*initialPausedStatus*/, + 0, /*initialPausedStatus*/ rewardsUpdater, activationDelay, globalCommissionBips @@ -81,7 +94,9 @@ contract ServiceManagerBase_UnitTests is avsDirectory, rewardsCoordinator, registryCoordinatorImplementation, - stakeRegistryImplementation + stakeRegistryImplementation, + permissionControllerMock, + allocationManagerMock ); serviceManager = ServiceManagerMock( @@ -91,6 +106,7 @@ contract ServiceManagerBase_UnitTests is address(proxyAdmin), abi.encodeWithSelector( ServiceManagerMock.initialize.selector, + serviceManager.owner(), msg.sender, msg.sender ) @@ -111,18 +127,11 @@ contract ServiceManagerBase_UnitTests is } /// @notice deploy token to owner and approve ServiceManager. Used for deploying reward tokens - function _deployMockRewardTokens( - address owner, - uint256 numTokens - ) internal virtual { + function _deployMockRewardTokens(address owner, uint256 numTokens) internal virtual { cheats.startPrank(owner); for (uint256 i = 0; i < numTokens; ++i) { - IERC20 token = new ERC20PresetFixedSupply( - "dog wif hat", - "MOCK1", - mockTokenInitialSupply, - owner - ); + IERC20 token = + new ERC20PresetFixedSupply("dog wif hat", "MOCK1", mockTokenInitialSupply, owner); rewardTokens.push(token); token.approve(address(serviceManager), mockTokenInitialSupply); } @@ -143,34 +152,21 @@ contract ServiceManagerBase_UnitTests is function _setUpDefaultStrategiesAndMultipliers() internal virtual { // Deploy Mock Strategies IERC20 token1 = new ERC20PresetFixedSupply( - "dog wif hat", - "MOCK1", - mockTokenInitialSupply, - address(this) - ); - IERC20 token2 = new ERC20PresetFixedSupply( - "jeo boden", - "MOCK2", - mockTokenInitialSupply, - address(this) + "dog wif hat", "MOCK1", mockTokenInitialSupply, address(this) ); + IERC20 token2 = + new ERC20PresetFixedSupply("jeo boden", "MOCK2", mockTokenInitialSupply, address(this)); IERC20 token3 = new ERC20PresetFixedSupply( - "pepe wif avs", - "MOCK3", - mockTokenInitialSupply, - address(this) + "pepe wif avs", "MOCK3", mockTokenInitialSupply, address(this) ); - strategyImplementation = new StrategyBase(strategyManagerMock); + strategyImplementation = + new StrategyBase(IStrategyManager(address(strategyManagerMock)), pauserRegistry); strategyMock1 = StrategyBase( address( new TransparentUpgradeableProxy( address(strategyImplementation), address(proxyAdmin), - abi.encodeWithSelector( - StrategyBase.initialize.selector, - token1, - pauserRegistry - ) + abi.encodeWithSelector(StrategyBase.initialize.selector, token1, pauserRegistry) ) ) ); @@ -179,11 +175,7 @@ contract ServiceManagerBase_UnitTests is new TransparentUpgradeableProxy( address(strategyImplementation), address(proxyAdmin), - abi.encodeWithSelector( - StrategyBase.initialize.selector, - token2, - pauserRegistry - ) + abi.encodeWithSelector(StrategyBase.initialize.selector, token2, pauserRegistry) ) ) ); @@ -192,11 +184,7 @@ contract ServiceManagerBase_UnitTests is new TransparentUpgradeableProxy( address(strategyImplementation), address(proxyAdmin), - abi.encodeWithSelector( - StrategyBase.initialize.selector, - token3, - pauserRegistry - ) + abi.encodeWithSelector(StrategyBase.initialize.selector, token3, pauserRegistry) ) ) ); @@ -211,22 +199,13 @@ contract ServiceManagerBase_UnitTests is strategyManagerMock.setStrategyWhitelist(strategies[2], true); defaultStrategyAndMultipliers.push( - IRewardsCoordinator.StrategyAndMultiplier( - IStrategy(address(strategies[0])), - 1e18 - ) + IRewardsCoordinatorTypes.StrategyAndMultiplier(IStrategy(address(strategies[0])), 1e18) ); defaultStrategyAndMultipliers.push( - IRewardsCoordinator.StrategyAndMultiplier( - IStrategy(address(strategies[1])), - 2e18 - ) + IRewardsCoordinatorTypes.StrategyAndMultiplier(IStrategy(address(strategies[1])), 2e18) ); defaultStrategyAndMultipliers.push( - IRewardsCoordinator.StrategyAndMultiplier( - IStrategy(address(strategies[2])), - 3e18 - ) + IRewardsCoordinatorTypes.StrategyAndMultiplier(IStrategy(address(strategies[2])), 3e18) ); } @@ -247,10 +226,7 @@ contract ServiceManagerBase_UnitTests is return arr; } - function _maxTimestamp( - uint32 timestamp1, - uint32 timestamp2 - ) internal pure returns (uint32) { + function _maxTimestamp(uint32 timestamp1, uint32 timestamp2) internal pure returns (uint32) { return timestamp1 > timestamp2 ? timestamp1 : timestamp2; } @@ -261,27 +237,18 @@ contract ServiceManagerBase_UnitTests is IRewardsCoordinator.RewardsSubmission[] memory rewardsSubmissions; cheats.prank(caller); - cheats.expectRevert( - "ServiceManagerBase.onlyRewardsInitiator: caller is not the rewards initiator" - ); + cheats.expectRevert(IServiceManagerErrors.OnlyRewardsInitiator.selector); serviceManager.createAVSRewardsSubmission(rewardsSubmissions); } - function test_createAVSRewardsSubmission_Revert_WhenERC20NotApproved() - public - { + function test_createAVSRewardsSubmission_Revert_WhenERC20NotApproved() public { IERC20 token = new ERC20PresetFixedSupply( - "dog wif hat", - "MOCK1", - mockTokenInitialSupply, - rewardsInitiator + "dog wif hat", "MOCK1", mockTokenInitialSupply, rewardsInitiator ); - IRewardsCoordinator.RewardsSubmission[] - memory rewardsSubmissions = new IRewardsCoordinator.RewardsSubmission[]( - 1 - ); - rewardsSubmissions[0] = IRewardsCoordinator.RewardsSubmission({ + IRewardsCoordinatorTypes.RewardsSubmission[] memory rewardsSubmissions = + new IRewardsCoordinatorTypes.RewardsSubmission[](1); + rewardsSubmissions[0] = IRewardsCoordinatorTypes.RewardsSubmission({ strategiesAndMultipliers: defaultStrategyAndMultipliers, token: token, amount: 100, @@ -294,17 +261,14 @@ contract ServiceManagerBase_UnitTests is serviceManager.createAVSRewardsSubmission(rewardsSubmissions); } - function test_createAVSRewardsSubmission_SingleSubmission( + function testFuzz_createAVSRewardsSubmission_SingleSubmission( uint256 startTimestamp, uint256 duration, uint256 amount ) public { // 1. Bound fuzz inputs to valid ranges and amounts IERC20 rewardToken = new ERC20PresetFixedSupply( - "dog wif hat", - "MOCK1", - mockTokenInitialSupply, - rewardsInitiator + "dog wif hat", "MOCK1", mockTokenInitialSupply, rewardsInitiator ); amount = bound(amount, 1, MAX_REWARDS_AMOUNT); duration = bound(duration, 0, MAX_REWARDS_DURATION); @@ -313,24 +277,17 @@ contract ServiceManagerBase_UnitTests is startTimestamp, uint256( _maxTimestamp( - GENESIS_REWARDS_TIMESTAMP, - uint32(block.timestamp) - MAX_RETROACTIVE_LENGTH + GENESIS_REWARDS_TIMESTAMP, uint32(block.timestamp) - MAX_RETROACTIVE_LENGTH ) - ) + - CALCULATION_INTERVAL_SECONDS - - 1, + ) + CALCULATION_INTERVAL_SECONDS - 1, block.timestamp + uint256(MAX_FUTURE_LENGTH) ); - startTimestamp = - startTimestamp - - (startTimestamp % CALCULATION_INTERVAL_SECONDS); + startTimestamp = startTimestamp - (startTimestamp % CALCULATION_INTERVAL_SECONDS); // 2. Create reward submission input param - IRewardsCoordinator.RewardsSubmission[] - memory rewardsSubmissions = new IRewardsCoordinator.RewardsSubmission[]( - 1 - ); - rewardsSubmissions[0] = IRewardsCoordinator.RewardsSubmission({ + IRewardsCoordinatorTypes.RewardsSubmission[] memory rewardsSubmissions = + new IRewardsCoordinatorTypes.RewardsSubmission[](1); + rewardsSubmissions[0] = IRewardsCoordinatorTypes.RewardsSubmission({ strategiesAndMultipliers: defaultStrategyAndMultipliers, token: rewardToken, amount: amount, @@ -343,39 +300,25 @@ contract ServiceManagerBase_UnitTests is rewardToken.approve(address(serviceManager), amount); // 4. call createAVSRewardsSubmission() with expected event emitted - uint256 rewardsInitiatorBalanceBefore = rewardToken.balanceOf( - address(rewardsInitiator) - ); - uint256 rewardsCoordinatorBalanceBefore = rewardToken.balanceOf( - address(rewardsCoordinator) - ); + uint256 rewardsInitiatorBalanceBefore = rewardToken.balanceOf(address(rewardsInitiator)); + uint256 rewardsCoordinatorBalanceBefore = rewardToken.balanceOf(address(rewardsCoordinator)); rewardToken.approve(address(rewardsCoordinator), amount); - uint256 currSubmissionNonce = rewardsCoordinator.submissionNonce( - address(serviceManager) - ); + uint256 currSubmissionNonce = rewardsCoordinator.submissionNonce(address(serviceManager)); bytes32 avsSubmissionHash = keccak256( - abi.encode( - address(serviceManager), - currSubmissionNonce, - rewardsSubmissions[0] - ) + abi.encode(address(serviceManager), currSubmissionNonce, rewardsSubmissions[0]) ); cheats.expectEmit(true, true, true, true, address(rewardsCoordinator)); emit AVSRewardsSubmissionCreated( - address(serviceManager), - currSubmissionNonce, - avsSubmissionHash, - rewardsSubmissions[0] + address(serviceManager), currSubmissionNonce, avsSubmissionHash, rewardsSubmissions[0] ); serviceManager.createAVSRewardsSubmission(rewardsSubmissions); cheats.stopPrank(); assertTrue( rewardsCoordinator.isAVSRewardsSubmissionHash( - address(serviceManager), - avsSubmissionHash + address(serviceManager), avsSubmissionHash ), "reward submission hash not submitted" ); @@ -396,7 +339,7 @@ contract ServiceManagerBase_UnitTests is ); } - function test_createAVSRewardsSubmission_MultipleSubmissions( + function testFuzz_createAVSRewardsSubmission_MultipleSubmissions( uint256 startTimestamp, uint256 duration, uint256 amount, @@ -405,25 +348,15 @@ contract ServiceManagerBase_UnitTests is cheats.assume(2 <= numSubmissions && numSubmissions <= 10); cheats.prank(rewardsCoordinator.owner()); - IRewardsCoordinator.RewardsSubmission[] - memory rewardsSubmissions = new IRewardsCoordinator.RewardsSubmission[]( - numSubmissions - ); + IRewardsCoordinator.RewardsSubmission[] memory rewardsSubmissions = + new IRewardsCoordinator.RewardsSubmission[](numSubmissions); bytes32[] memory avsSubmissionHashes = new bytes32[](numSubmissions); - uint256 startSubmissionNonce = rewardsCoordinator.submissionNonce( - address(serviceManager) - ); + uint256 startSubmissionNonce = rewardsCoordinator.submissionNonce(address(serviceManager)); _deployMockRewardTokens(rewardsInitiator, numSubmissions); - uint256[] memory avsBalancesBefore = _getBalanceForTokens( - rewardTokens, - rewardsInitiator - ); - uint256[] - memory rewardsCoordinatorBalancesBefore = _getBalanceForTokens( - rewardTokens, - address(rewardsCoordinator) - ); + uint256[] memory avsBalancesBefore = _getBalanceForTokens(rewardTokens, rewardsInitiator); + uint256[] memory rewardsCoordinatorBalancesBefore = + _getBalanceForTokens(rewardTokens, address(rewardsCoordinator)); uint256[] memory amounts = new uint256[](numSubmissions); // Create multiple rewards submissions and their expected event @@ -437,45 +370,29 @@ contract ServiceManagerBase_UnitTests is startTimestamp + i, uint256( _maxTimestamp( - GENESIS_REWARDS_TIMESTAMP, - uint32(block.timestamp) - MAX_RETROACTIVE_LENGTH + GENESIS_REWARDS_TIMESTAMP, uint32(block.timestamp) - MAX_RETROACTIVE_LENGTH ) - ) + - CALCULATION_INTERVAL_SECONDS - - 1, + ) + CALCULATION_INTERVAL_SECONDS - 1, block.timestamp + uint256(MAX_FUTURE_LENGTH) ); - startTimestamp = - startTimestamp - - (startTimestamp % CALCULATION_INTERVAL_SECONDS); + startTimestamp = startTimestamp - (startTimestamp % CALCULATION_INTERVAL_SECONDS); // 2. Create reward submission input param - IRewardsCoordinator.RewardsSubmission - memory rewardsSubmission = IRewardsCoordinator - .RewardsSubmission({ - strategiesAndMultipliers: defaultStrategyAndMultipliers, - token: rewardTokens[i], - amount: amounts[i], - startTimestamp: uint32(startTimestamp), - duration: uint32(duration) - }); + IRewardsCoordinatorTypes.RewardsSubmission memory rewardsSubmission = + IRewardsCoordinatorTypes.RewardsSubmission({ + strategiesAndMultipliers: defaultStrategyAndMultipliers, + token: rewardTokens[i], + amount: amounts[i], + startTimestamp: uint32(startTimestamp), + duration: uint32(duration) + }); rewardsSubmissions[i] = rewardsSubmission; // 3. expected event emitted for this rewardsSubmission avsSubmissionHashes[i] = keccak256( - abi.encode( - address(serviceManager), - startSubmissionNonce + i, - rewardsSubmissions[i] - ) - ); - cheats.expectEmit( - true, - true, - true, - true, - address(rewardsCoordinator) + abi.encode(address(serviceManager), startSubmissionNonce + i, rewardsSubmissions[i]) ); + cheats.expectEmit(true, true, true, true, address(rewardsCoordinator)); emit AVSRewardsSubmissionCreated( address(serviceManager), startSubmissionNonce + i, @@ -498,8 +415,7 @@ contract ServiceManagerBase_UnitTests is for (uint256 i = 0; i < numSubmissions; ++i) { assertTrue( rewardsCoordinator.isAVSRewardsSubmissionHash( - address(serviceManager), - avsSubmissionHashes[i] + address(serviceManager), avsSubmissionHashes[i] ), "rewards submission hash not submitted" ); @@ -516,7 +432,7 @@ contract ServiceManagerBase_UnitTests is } } - function test_createAVSRewardsSubmission_MultipleSubmissionsSingleToken( + function testFuzz_createAVSRewardsSubmission_MultipleSubmissionsSingleToken( uint256 startTimestamp, uint256 duration, uint256 amount, @@ -525,26 +441,17 @@ contract ServiceManagerBase_UnitTests is cheats.assume(2 <= numSubmissions && numSubmissions <= 10); cheats.prank(rewardsCoordinator.owner()); - IRewardsCoordinator.RewardsSubmission[] - memory rewardsSubmissions = new IRewardsCoordinator.RewardsSubmission[]( - numSubmissions - ); + IRewardsCoordinator.RewardsSubmission[] memory rewardsSubmissions = + new IRewardsCoordinator.RewardsSubmission[](numSubmissions); bytes32[] memory avsSubmissionHashes = new bytes32[](numSubmissions); - uint256 startSubmissionNonce = rewardsCoordinator.submissionNonce( - address(serviceManager) - ); + uint256 startSubmissionNonce = rewardsCoordinator.submissionNonce(address(serviceManager)); IERC20 rewardToken = new ERC20PresetFixedSupply( - "dog wif hat", - "MOCK1", - mockTokenInitialSupply, - rewardsInitiator + "dog wif hat", "MOCK1", mockTokenInitialSupply, rewardsInitiator ); cheats.prank(rewardsInitiator); rewardToken.approve(address(serviceManager), mockTokenInitialSupply); uint256 avsBalanceBefore = rewardToken.balanceOf(rewardsInitiator); - uint256 rewardsCoordinatorBalanceBefore = rewardToken.balanceOf( - address(rewardsCoordinator) - ); + uint256 rewardsCoordinatorBalanceBefore = rewardToken.balanceOf(address(rewardsCoordinator)); uint256 totalAmount = 0; uint256[] memory amounts = new uint256[](numSubmissions); @@ -561,45 +468,29 @@ contract ServiceManagerBase_UnitTests is startTimestamp + i, uint256( _maxTimestamp( - GENESIS_REWARDS_TIMESTAMP, - uint32(block.timestamp) - MAX_RETROACTIVE_LENGTH + GENESIS_REWARDS_TIMESTAMP, uint32(block.timestamp) - MAX_RETROACTIVE_LENGTH ) - ) + - CALCULATION_INTERVAL_SECONDS - - 1, + ) + CALCULATION_INTERVAL_SECONDS - 1, block.timestamp + uint256(MAX_FUTURE_LENGTH) ); - startTimestamp = - startTimestamp - - (startTimestamp % CALCULATION_INTERVAL_SECONDS); + startTimestamp = startTimestamp - (startTimestamp % CALCULATION_INTERVAL_SECONDS); // 2. Create reward submission input param - IRewardsCoordinator.RewardsSubmission - memory rewardsSubmission = IRewardsCoordinator - .RewardsSubmission({ - strategiesAndMultipliers: defaultStrategyAndMultipliers, - token: rewardToken, - amount: amounts[i], - startTimestamp: uint32(startTimestamp), - duration: uint32(duration) - }); + IRewardsCoordinatorTypes.RewardsSubmission memory rewardsSubmission = + IRewardsCoordinatorTypes.RewardsSubmission({ + strategiesAndMultipliers: defaultStrategyAndMultipliers, + token: rewardToken, + amount: amounts[i], + startTimestamp: uint32(startTimestamp), + duration: uint32(duration) + }); rewardsSubmissions[i] = rewardsSubmission; // 3. expected event emitted for this avs rewards submission avsSubmissionHashes[i] = keccak256( - abi.encode( - address(serviceManager), - startSubmissionNonce + i, - rewardsSubmissions[i] - ) - ); - cheats.expectEmit( - true, - true, - true, - true, - address(rewardsCoordinator) + abi.encode(address(serviceManager), startSubmissionNonce + i, rewardsSubmissions[i]) ); + cheats.expectEmit(true, true, true, true, address(rewardsCoordinator)); emit AVSRewardsSubmissionCreated( address(serviceManager), startSubmissionNonce + i, @@ -632,8 +523,7 @@ contract ServiceManagerBase_UnitTests is for (uint256 i = 0; i < numSubmissions; ++i) { assertTrue( rewardsCoordinator.isAVSRewardsSubmissionHash( - address(serviceManager), - avsSubmissionHashes[i] + address(serviceManager), avsSubmissionHashes[i] ), "rewards submission hash not submitted" ); @@ -641,9 +531,7 @@ contract ServiceManagerBase_UnitTests is } function test_setRewardsInitiator() public { - address newRewardsInitiator = address( - uint160(uint256(keccak256("newRewardsInitiator"))) - ); + address newRewardsInitiator = address(uint160(uint256(keccak256("newRewardsInitiator")))); cheats.prank(serviceManagerOwner); serviceManager.setRewardsInitiator(newRewardsInitiator); assertEq(newRewardsInitiator, serviceManager.rewardsInitiator()); @@ -651,432 +539,9 @@ contract ServiceManagerBase_UnitTests is function test_setRewardsInitiator_revert_notOwner() public { address caller = address(uint160(uint256(keccak256("caller")))); - address newRewardsInitiator = address( - uint160(uint256(keccak256("newRewardsInitiator"))) - ); + address newRewardsInitiator = address(uint160(uint256(keccak256("newRewardsInitiator")))); cheats.expectRevert("Ownable: caller is not the owner"); cheats.prank(caller); serviceManager.setRewardsInitiator(newRewardsInitiator); } - - function testFuzz_setClaimerFor(address claimer) public { - cheats.startPrank(serviceManagerOwner); - cheats.expectEmit(true, true, true, true, address(rewardsCoordinator)); - emit ClaimerForSet( - address(serviceManager), - rewardsCoordinator.claimerFor(address(serviceManager)), - claimer - ); - serviceManager.setClaimerFor(claimer); - assertEq( - claimer, - rewardsCoordinator.claimerFor(address(serviceManager)), - "claimerFor not set" - ); - cheats.stopPrank(); - } - - function testFuzz_setClaimerFor_revert_notOwner( - address caller, - address claimer - ) public filterFuzzedAddressInputs(caller) { - cheats.assume(caller != serviceManagerOwner); - cheats.prank(caller); - cheats.expectRevert("Ownable: caller is not the owner"); - serviceManager.setClaimerFor(claimer); - } -} - -contract ServiceManagerBase_createOperatorDirectedAVSRewardsSubmission is - ServiceManagerBase_UnitTests -{ - // used for stack too deep - struct FuzzOperatorDirectedAVSRewardsSubmission { - uint256 startTimestamp; - uint256 duration; - } - - IRewardsCoordinator.OperatorReward[] defaultOperatorRewards; - - function setUp() public virtual override { - ServiceManagerBase_UnitTests.setUp(); - - address[] memory operators = new address[](3); - operators[0] = makeAddr("operator1"); - operators[1] = makeAddr("operator2"); - operators[2] = makeAddr("operator3"); - operators = _sortAddressArrayAsc(operators); - - defaultOperatorRewards.push( - IRewardsCoordinator.OperatorReward(operators[0], 1e18) - ); - defaultOperatorRewards.push( - IRewardsCoordinator.OperatorReward(operators[1], 2e18) - ); - defaultOperatorRewards.push( - IRewardsCoordinator.OperatorReward(operators[2], 3e18) - ); - - // Set the timestamp to when Rewards v2 will realisticly go out (i.e 6 months) - cheats.warp(GENESIS_REWARDS_TIMESTAMP + 168 days); - } - - /// @dev Sort to ensure that the array is in ascending order for addresses - function _sortAddressArrayAsc( - address[] memory arr - ) internal pure returns (address[] memory) { - uint256 l = arr.length; - for (uint256 i = 0; i < l; i++) { - for (uint256 j = i + 1; j < l; j++) { - if (arr[i] > arr[j]) { - address temp = arr[i]; - arr[i] = arr[j]; - arr[j] = temp; - } - } - } - return arr; - } - - function _getTotalRewardsAmount( - IRewardsCoordinator.OperatorReward[] memory operatorRewards - ) internal pure returns (uint256) { - uint256 totalAmount = 0; - for (uint256 i = 0; i < operatorRewards.length; ++i) { - totalAmount += operatorRewards[i].amount; - } - return totalAmount; - } - - function testFuzz_createOperatorDirectedAVSRewardsSubmission_Revert_WhenNotOwner( - address caller - ) public filterFuzzedAddressInputs(caller) { - cheats.assume(caller != rewardsInitiator); - IRewardsCoordinator.OperatorDirectedRewardsSubmission[] - memory operatorDirectedRewardsSubmissions; - - cheats.prank(caller); - cheats.expectRevert( - "ServiceManagerBase.onlyRewardsInitiator: caller is not the rewards initiator" - ); - serviceManager.createOperatorDirectedAVSRewardsSubmission( - operatorDirectedRewardsSubmissions - ); - } - - function testFuzz_createOperatorDirectedAVSRewardsSubmission_Revert_WhenERC20NotApproved( - uint256 startTimestamp, - uint256 duration - ) public { - // 1. Bound fuzz inputs to valid ranges and amounts - IERC20 rewardToken = new ERC20PresetFixedSupply( - "dog wif hat", - "MOCK1", - mockTokenInitialSupply, - rewardsInitiator - ); - duration = bound(duration, 0, MAX_REWARDS_DURATION); - duration = duration - (duration % CALCULATION_INTERVAL_SECONDS); - startTimestamp = bound( - startTimestamp, - uint256( - _maxTimestamp( - GENESIS_REWARDS_TIMESTAMP, - uint32(block.timestamp) - MAX_RETROACTIVE_LENGTH - ) - ) + - CALCULATION_INTERVAL_SECONDS - - 1, - block.timestamp - duration - 1 - ); - startTimestamp = - startTimestamp - - (startTimestamp % CALCULATION_INTERVAL_SECONDS); - - // 2. Create operator directed rewards submission input param - IRewardsCoordinator.OperatorDirectedRewardsSubmission[] - memory operatorDirectedRewardsSubmissions = new IRewardsCoordinator.OperatorDirectedRewardsSubmission[]( - 1 - ); - operatorDirectedRewardsSubmissions[0] = IRewardsCoordinator - .OperatorDirectedRewardsSubmission({ - strategiesAndMultipliers: defaultStrategyAndMultipliers, - token: rewardToken, - operatorRewards: defaultOperatorRewards, - startTimestamp: uint32(startTimestamp), - duration: uint32(duration), - description: "" - }); - - // 3. Call createOperatorDirectedAVSRewardsSubmission() - cheats.prank(rewardsInitiator); - cheats.expectRevert("ERC20: insufficient allowance"); - serviceManager.createOperatorDirectedAVSRewardsSubmission( - operatorDirectedRewardsSubmissions - ); - } - - /** - * @notice test a single rewards submission asserting for the following - * - correct event emitted - * - submission nonce incrementation by 1, and rewards submission hash being set in storage. - * - rewards submission hash being set in storage - * - token balance before and after of rewards initiator and rewardsCoordinator - */ - function testFuzz_createOperatorDirectedAVSRewardsSubmission_SingleSubmission( - uint256 startTimestamp, - uint256 duration - ) public { - // 1. Bound fuzz inputs to valid ranges and amounts - IERC20 rewardToken = new ERC20PresetFixedSupply( - "dog wif hat", - "MOCK1", - mockTokenInitialSupply, - rewardsInitiator - ); - duration = bound(duration, 0, MAX_REWARDS_DURATION); - duration = duration - (duration % CALCULATION_INTERVAL_SECONDS); - startTimestamp = bound( - startTimestamp, - uint256( - _maxTimestamp( - GENESIS_REWARDS_TIMESTAMP, - uint32(block.timestamp) - MAX_RETROACTIVE_LENGTH - ) - ) + - CALCULATION_INTERVAL_SECONDS - - 1, - block.timestamp - duration - 1 - ); - startTimestamp = - startTimestamp - - (startTimestamp % CALCULATION_INTERVAL_SECONDS); - - // 2. Create operator directed rewards submission input param - IRewardsCoordinator.OperatorDirectedRewardsSubmission[] - memory operatorDirectedRewardsSubmissions = new IRewardsCoordinator.OperatorDirectedRewardsSubmission[]( - 1 - ); - operatorDirectedRewardsSubmissions[0] = IRewardsCoordinator - .OperatorDirectedRewardsSubmission({ - strategiesAndMultipliers: defaultStrategyAndMultipliers, - token: rewardToken, - operatorRewards: defaultOperatorRewards, - startTimestamp: uint32(startTimestamp), - duration: uint32(duration), - description: "" - }); - - // 3. Get total amount - uint256 amount = _getTotalRewardsAmount(defaultOperatorRewards); - - // 4. Approve serviceManager for ERC20 - cheats.startPrank(rewardsInitiator); - rewardToken.approve(address(serviceManager), amount); - - // 3. call createOperatorDirectedAVSRewardsSubmission() with expected event emitted - uint256 rewardsInitiatorBalanceBefore = rewardToken.balanceOf( - rewardsInitiator - ); - uint256 rewardsCoordinatorBalanceBefore = rewardToken.balanceOf( - address(rewardsCoordinator) - ); - uint256 currSubmissionNonce = rewardsCoordinator.submissionNonce( - address(serviceManager) - ); - bytes32 rewardsSubmissionHash = keccak256( - abi.encode( - address(serviceManager), - currSubmissionNonce, - operatorDirectedRewardsSubmissions[0] - ) - ); - cheats.expectEmit(true, true, true, true, address(rewardsCoordinator)); - emit OperatorDirectedAVSRewardsSubmissionCreated( - address(serviceManager), - address(serviceManager), - rewardsSubmissionHash, - currSubmissionNonce, - operatorDirectedRewardsSubmissions[0] - ); - serviceManager.createOperatorDirectedAVSRewardsSubmission( - operatorDirectedRewardsSubmissions - ); - cheats.stopPrank(); - - assertTrue( - rewardsCoordinator.isOperatorDirectedAVSRewardsSubmissionHash( - address(serviceManager), - rewardsSubmissionHash - ), - "rewards submission hash not submitted" - ); - assertEq( - currSubmissionNonce + 1, - rewardsCoordinator.submissionNonce(address(serviceManager)), - "submission nonce not incremented" - ); - assertEq( - rewardsInitiatorBalanceBefore - amount, - rewardToken.balanceOf(rewardsInitiator), - "rewardsInitiator balance not decremented by amount of rewards submission" - ); - assertEq( - rewardsCoordinatorBalanceBefore + amount, - rewardToken.balanceOf(address(rewardsCoordinator)), - "RewardsCoordinator balance not incremented by amount of rewards submission" - ); - } - - /** - * @notice test a multiple rewards submission asserting for the following - * - correct event emitted - * - submission nonce incrementation by 1, and rewards submission hash being set in storage. - * - rewards submission hash being set in storage - * - token balance before and after of rewards initiator and rewardsCoordinator - */ - function testFuzz_createOperatorDirectedAVSRewardsSubmission_MultipleSubmissions( - FuzzOperatorDirectedAVSRewardsSubmission memory param, - uint256 numSubmissions - ) public { - cheats.assume(2 <= numSubmissions && numSubmissions <= 10); - cheats.prank(rewardsCoordinator.owner()); - - IRewardsCoordinator.OperatorDirectedRewardsSubmission[] - memory rewardsSubmissions = new IRewardsCoordinator.OperatorDirectedRewardsSubmission[]( - numSubmissions - ); - bytes32[] memory rewardsSubmissionHashes = new bytes32[]( - numSubmissions - ); - uint256 startSubmissionNonce = rewardsCoordinator.submissionNonce( - address(serviceManager) - ); - _deployMockRewardTokens(rewardsInitiator, numSubmissions); - - uint256[] memory rewardsInitiatorBalancesBefore = _getBalanceForTokens( - rewardTokens, - rewardsInitiator - ); - uint256[] - memory rewardsCoordinatorBalancesBefore = _getBalanceForTokens( - rewardTokens, - address(rewardsCoordinator) - ); - uint256[] memory amounts = new uint256[](numSubmissions); - - // Create multiple rewards submissions and their expected event - for (uint256 i = 0; i < numSubmissions; ++i) { - // 1. Bound fuzz inputs to valid ranges and amounts using randSeed for each - amounts[i] = _getTotalRewardsAmount(defaultOperatorRewards); - param.duration = bound(param.duration, 0, MAX_REWARDS_DURATION); - param.duration = - param.duration - - (param.duration % CALCULATION_INTERVAL_SECONDS); - param.startTimestamp = bound( - param.startTimestamp + i, - uint256( - _maxTimestamp( - GENESIS_REWARDS_TIMESTAMP, - uint32(block.timestamp) - MAX_RETROACTIVE_LENGTH - ) - ) + - CALCULATION_INTERVAL_SECONDS - - 1, - block.timestamp + uint256(MAX_FUTURE_LENGTH) - ); - param.startTimestamp = - param.startTimestamp - - (param.startTimestamp % CALCULATION_INTERVAL_SECONDS); - - param.duration = bound(param.duration, 0, MAX_REWARDS_DURATION); - param.duration = - param.duration - - (param.duration % CALCULATION_INTERVAL_SECONDS); - param.startTimestamp = bound( - param.startTimestamp, - uint256( - _maxTimestamp( - GENESIS_REWARDS_TIMESTAMP, - uint32(block.timestamp) - MAX_RETROACTIVE_LENGTH - ) - ) + - CALCULATION_INTERVAL_SECONDS - - 1, - block.timestamp - param.duration - 1 - ); - param.startTimestamp = - param.startTimestamp - - (param.startTimestamp % CALCULATION_INTERVAL_SECONDS); - - // 2. Create rewards submission input param - IRewardsCoordinator.OperatorDirectedRewardsSubmission - memory rewardsSubmission = IRewardsCoordinator - .OperatorDirectedRewardsSubmission({ - strategiesAndMultipliers: defaultStrategyAndMultipliers, - token: rewardTokens[i], - operatorRewards: defaultOperatorRewards, - startTimestamp: uint32(param.startTimestamp), - duration: uint32(param.duration), - description: "" - }); - rewardsSubmissions[i] = rewardsSubmission; - - // 3. expected event emitted for this rewardsSubmission - rewardsSubmissionHashes[i] = keccak256( - abi.encode( - address(serviceManager), - startSubmissionNonce + i, - rewardsSubmissions[i] - ) - ); - cheats.expectEmit( - true, - true, - true, - true, - address(rewardsCoordinator) - ); - emit OperatorDirectedAVSRewardsSubmissionCreated( - address(serviceManager), - address(serviceManager), - rewardsSubmissionHashes[i], - startSubmissionNonce + i, - rewardsSubmissions[i] - ); - } - - // 4. call createAVSRewardsSubmission() - cheats.prank(rewardsInitiator); - serviceManager.createOperatorDirectedAVSRewardsSubmission( - rewardsSubmissions - ); - - // 5. Check for submissionNonce() and rewardsSubmissionHashes being set - assertEq( - startSubmissionNonce + numSubmissions, - rewardsCoordinator.submissionNonce(address(serviceManager)), - "submission nonce not incremented properly" - ); - - for (uint256 i = 0; i < numSubmissions; ++i) { - assertTrue( - rewardsCoordinator.isOperatorDirectedAVSRewardsSubmissionHash( - address(serviceManager), - rewardsSubmissionHashes[i] - ), - "rewards submission hash not submitted" - ); - assertEq( - rewardsInitiatorBalancesBefore[i] - amounts[i], - rewardTokens[i].balanceOf(rewardsInitiator), - "rewardsInitiator balance not decremented by amount of rewards submission" - ); - assertEq( - rewardsCoordinatorBalancesBefore[i] + amounts[i], - rewardTokens[i].balanceOf(address(rewardsCoordinator)), - "RewardsCoordinator balance not incremented by amount of rewards submission" - ); - } - } } diff --git a/test/unit/ServiceManagerRouter.t.sol b/test/unit/ServiceManagerRouter.t.sol index 9fc2c0f7..3e83f1c4 100644 --- a/test/unit/ServiceManagerRouter.t.sol +++ b/test/unit/ServiceManagerRouter.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {ServiceManagerRouter} from "../../src/ServiceManagerRouter.sol"; import "../utils/MockAVSDeployer.sol"; @@ -19,7 +19,9 @@ contract ServiceManagerRouter_UnitTests is MockAVSDeployer { avsDirectory, rewardsCoordinatorImplementation, registryCoordinatorImplementation, - stakeRegistryImplementation + stakeRegistryImplementation, + permissionControllerMock, + allocationManagerMock ); _registerOperatorWithCoordinator(defaultOperator, MAX_QUORUM_BITMAP, defaultPubKey); @@ -48,17 +50,20 @@ contract ServiceManagerRouter_UnitTests is MockAVSDeployer { } function test_getOperatorRestakedStrategies_noStrats() public { - address[] memory strategies = router.getOperatorRestakedStrategies(address(dummyServiceManager), defaultOperator); + address[] memory strategies = + router.getOperatorRestakedStrategies(address(dummyServiceManager), defaultOperator); assertEq(strategies.length, 0); } function test_getOperatorRestakedStrategies_multipleStrats() public { - address[] memory strategies = router.getOperatorRestakedStrategies(address(serviceManager), defaultOperator); + address[] memory strategies = + router.getOperatorRestakedStrategies(address(serviceManager), defaultOperator); assertEq(strategies.length, 192); } function test_getOperatorRestakedStrategies_badImplementation() public { - address[] memory strategies = router.getOperatorRestakedStrategies(address(emptyContract), defaultOperator); + address[] memory strategies = + router.getOperatorRestakedStrategies(address(emptyContract), defaultOperator); assertEq(strategies.length, 1); assertEq(strategies[0], badReturn); } diff --git a/test/unit/SlashingRegistryCoordinatorUnit.t.sol b/test/unit/SlashingRegistryCoordinatorUnit.t.sol new file mode 100644 index 00000000..844aa6e9 --- /dev/null +++ b/test/unit/SlashingRegistryCoordinatorUnit.t.sol @@ -0,0 +1,2414 @@ +// // SPDX-License-Identifier: BUSL-1.1 +// pragma solidity ^0.8.27; + +// import "../utils/MockAVSDeployer.sol"; +// import {ISlashingRegistryCoordinator, IRegistryCoordinatorErrors} from "../../src/interfaces/ISlashingRegistryCoordinator.sol"; +// import {QuorumBitmapHistoryLib} from "../../src/libraries/QuorumBitmapHistoryLib.sol"; +// import {BitmapUtils} from "../../src/libraries/BitmapUtils.sol"; +// import {console} from "forge-std/console.sol"; + +// contract RegistryCoordinatorUnitTests is MockAVSDeployer { +// using BN254 for BN254.G1Point; + +// uint8 internal constant PAUSED_REGISTER_OPERATOR = 0; +// uint8 internal constant PAUSED_DEREGISTER_OPERATOR = 1; +// uint8 internal constant PAUSED_UPDATE_OPERATOR = 2; +// uint8 internal constant MAX_QUORUM_COUNT = 192; + +// /// 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 OperatorSocketUpdate(bytes32 indexed operatorId, string socket); + +// /// @notice emitted whenever the stake of `operator` is updated +// event OperatorStakeUpdate( +// bytes32 indexed operatorId, +// uint8 quorumNumber, +// uint96 stake +// ); + +// // Emitted when a new operator pubkey is registered for a set of quorums +// event OperatorAddedToQuorums( +// address operator, +// bytes32 operatorId, +// bytes quorumNumbers +// ); + +// // Emitted when an operator pubkey is removed from a set of quorums +// event OperatorRemovedFromQuorums( +// address operator, +// bytes32 operatorId, +// bytes quorumNumbers +// ); + +// // emitted when an operator's index in the orderd operator list for the quorum with number `quorumNumber` is updated +// event QuorumIndexUpdate(bytes32 indexed operatorId, uint8 quorumNumber, uint32 newIndex); + +// event OperatorSetParamsUpdated(uint8 indexed quorumNumber, ISlashingRegistryCoordinatorTypes.OperatorSetParam operatorSetParams); + +// event ChurnApproverUpdated(address prevChurnApprover, address newChurnApprover); + +// event EjectorUpdated(address prevEjector, address newEjector); + +// event QuorumBlockNumberUpdated(uint8 indexed quorumNumber, uint256 blocknumber); + +// function setUp() virtual public { +// _deployMockEigenLayerAndAVS(numQuorums); +// } + +// function _test_registerOperatorWithChurn_SetUp( +// uint256 pseudoRandomNumber, +// bytes memory quorumNumbers, +// uint96 operatorToKickStake +// ) internal returns( +// address operatorToRegister, +// BN254.G1Point memory operatorToRegisterPubKey, +// ISlashingRegistryCoordinator.OperatorKickParam[] memory operatorKickParams +// ) { +// uint32 kickRegistrationBlockNumber = 100; + +// uint256 quorumBitmap = BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers); + +// cheats.roll(kickRegistrationBlockNumber); + +// for (uint i = 0; i < defaultMaxOperatorCount - 1; i++) { +// BN254.G1Point memory pubKey = BN254.hashToG1(keccak256(abi.encodePacked(pseudoRandomNumber, i))); +// address operator = _incrementAddress(defaultOperator, i); + +// _registerOperatorWithCoordinator(operator, quorumBitmap, pubKey); +// } + +// operatorToRegister = _incrementAddress(defaultOperator, defaultMaxOperatorCount); +// operatorToRegisterPubKey = BN254.hashToG1(keccak256(abi.encodePacked(pseudoRandomNumber, defaultMaxOperatorCount))); +// bytes32 operatorToRegisterId = BN254.hashG1Point(operatorToRegisterPubKey); +// bytes32 operatorToKickId; +// address operatorToKick; + +// // register last operator before kick +// operatorKickParams = new ISlashingRegistryCoordinator.OperatorKickParam[](1); +// { +// BN254.G1Point memory pubKey = BN254.hashToG1(keccak256(abi.encodePacked(pseudoRandomNumber, defaultMaxOperatorCount - 1))); +// operatorToKickId = BN254.hashG1Point(pubKey); +// operatorToKick = _incrementAddress(defaultOperator, defaultMaxOperatorCount - 1); + +// // register last operator with much more than the kickBIPsOfTotalStake stake +// _registerOperatorWithCoordinator(operatorToKick, quorumBitmap, pubKey, operatorToKickStake); + +// bytes32[] memory operatorIdsToSwap = new bytes32[](1); +// // operatorIdsToSwap[0] = operatorToRegisterId +// operatorIdsToSwap[0] = operatorToRegisterId; + +// operatorKickParams[0] = ISlashingRegistryCoordinator.OperatorKickParam({ +// quorumNumber: uint8(quorumNumbers[0]), +// operator: operatorToKick +// }); +// } + +// blsApkRegistry.setBLSPublicKey(operatorToRegister, operatorToRegisterPubKey); +// } +// } + +// contract RegistryCoordinatorUnitTests_Initialization_Setters is RegistryCoordinatorUnitTests { +// function test_initialization() public { +// assertEq(address(registryCoordinator.stakeRegistry()), address(stakeRegistry)); +// assertEq(address(registryCoordinator.blsApkRegistry()), address(blsApkRegistry)); +// assertEq(address(registryCoordinator.indexRegistry()), address(indexRegistry)); +// assertEq(address(registryCoordinator.serviceManager()), address(serviceManager)); + +// for (uint i = 0; i < numQuorums; i++) { +// assertEq( +// keccak256(abi.encode(registryCoordinator.getOperatorSetParams(uint8(i)))), +// keccak256(abi.encode(operatorSetParams[i])) +// ); +// } + +// // make sure the contract initializers are disabled +// cheats.expectRevert(bytes("Initializable: contract is already initialized")); +// registryCoordinator.initialize( +// registryCoordinatorOwner, +// churnApprover, +// ejector, +// 0/*initialPausedStatus*/, +// address(serviceManager) // _accountIdentifier +// ); +// } + +// function test_setOperatorSetParams() public { +// cheats.prank(registryCoordinatorOwner); +// cheats.expectEmit(true, true, true, true, address(registryCoordinator)); +// emit OperatorSetParamsUpdated(0, operatorSetParams[1]); +// registryCoordinator.setOperatorSetParams(0, operatorSetParams[1]); +// assertEq(keccak256(abi.encode(registryCoordinator.getOperatorSetParams(0))),keccak256(abi.encode(operatorSetParams[1])), +// "operator set params not updated correctly"); +// } + +// function test_setOperatorSetParams_revert_notOwner() public { +// cheats.expectRevert("Ownable: caller is not the owner"); +// cheats.prank(defaultOperator); +// registryCoordinator.setOperatorSetParams(0, operatorSetParams[0]); +// } + +// function test_setChurnApprover() public { +// address newChurnApprover = address(uint160(uint256(keccak256("newChurnApprover")))); +// cheats.prank(registryCoordinatorOwner); +// cheats.expectEmit(true, true, true, true, address(registryCoordinator)); +// emit ChurnApproverUpdated(churnApprover, newChurnApprover); +// registryCoordinator.setChurnApprover(newChurnApprover); +// assertEq(registryCoordinator.churnApprover(), newChurnApprover); +// } + +// function test_setChurnApprover_revert_notOwner() public { +// address newChurnApprover = address(uint160(uint256(keccak256("newChurnApprover")))); +// cheats.expectRevert("Ownable: caller is not the owner"); +// cheats.prank(defaultOperator); +// registryCoordinator.setChurnApprover(newChurnApprover); +// } + +// function test_setEjector() public { +// address newEjector = address(uint160(uint256(keccak256("newEjector")))); +// cheats.prank(registryCoordinatorOwner); +// cheats.expectEmit(true, true, true, true, address(registryCoordinator)); +// emit EjectorUpdated(ejector, newEjector); +// registryCoordinator.setEjector(newEjector); +// assertEq(registryCoordinator.ejector(), newEjector); +// } + +// function test_setEjector_revert_notOwner() public { +// address newEjector = address(uint160(uint256(keccak256("newEjector")))); +// cheats.expectRevert("Ownable: caller is not the owner"); +// cheats.prank(defaultOperator); +// registryCoordinator.setEjector(newEjector); +// } + +// function test_updateSocket() public { +// bytes memory quorumNumbers = new bytes(1); +// quorumNumbers[0] = bytes1(defaultQuorumNumber); + +// uint256 quorumBitmap = BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers); +// _registerOperatorWithCoordinator(defaultOperator, quorumBitmap, defaultPubKey); + +// cheats.prank(defaultOperator); +// cheats.expectEmit(true, true, true, true, address(registryCoordinator)); +// emit OperatorSocketUpdate(defaultOperatorId, "localhost:32004"); +// registryCoordinator.updateSocket("localhost:32004"); + +// } + +// function test_updateSocket_revert_notRegistered() public { +// cheats.prank(defaultOperator); +// cheats.expectRevert(bytes4(keccak256("NotRegistered()"))); +// registryCoordinator.updateSocket("localhost:32004"); +// } + +// function test_createQuorum_revert_notOwner() public { +// ISlashingRegistryCoordinatorTypes.OperatorSetParam memory operatorSetParams; +// uint96 minimumStake; +// IStakeRegistryTypes.StrategyParams[] memory strategyParams; + +// cheats.expectRevert("Ownable: caller is not the owner"); +// cheats.prank(defaultOperator); +// registryCoordinator.createTotalDelegatedStakeQuorum(operatorSetParams, minimumStake, strategyParams); +// } + +// function test_createQuorum() public { +// // re-run setup, but setting up zero quorums +// // this is necessary since the default setup already configures the max number of quorums, preventing adding more +// _deployMockEigenLayerAndAVS(0); + +// ISlashingRegistryCoordinatorTypes.OperatorSetParam memory operatorSetParams = +// ISlashingRegistryCoordinatorTypes.OperatorSetParam({ +// maxOperatorCount: defaultMaxOperatorCount, +// kickBIPsOfOperatorStake: defaultKickBIPsOfOperatorStake, +// kickBIPsOfTotalStake: defaultKickBIPsOfTotalStake +// }); +// uint96 minimumStake = 1; +// IStakeRegistryTypes.StrategyParams[] memory strategyParams = new IStakeRegistryTypes.StrategyParams[](1); +// strategyParams[0] = +// IStakeRegistryTypes.StrategyParams({ +// strategy: IStrategy(address(1000)), +// multiplier: 1e16 +// }); + +// uint8 quorumCountBefore = registryCoordinator.quorumCount(); + +// cheats.expectEmit(true, true, true, true, address(registryCoordinator)); +// emit OperatorSetParamsUpdated(quorumCountBefore, operatorSetParams); +// cheats.prank(registryCoordinatorOwner); +// registryCoordinator.createTotalDelegatedStakeQuorum(operatorSetParams, minimumStake, strategyParams); + +// uint8 quorumCountAfter = registryCoordinator.quorumCount(); +// assertEq(quorumCountAfter, quorumCountBefore + 1, "quorum count did not increase properly"); +// assertLe(quorumCountAfter, MAX_QUORUM_COUNT, "quorum count exceeded max"); + +// assertEq( +// keccak256(abi.encode(operatorSetParams)), +// keccak256(abi.encode(registryCoordinator.getOperatorSetParams(quorumCountBefore))), +// "OperatorSetParams not stored properly" +// ); +// } +// } + +// contract RegistryCoordinatorUnitTests_RegisterOperator is RegistryCoordinatorUnitTests { + +// function test_registerOperator_revert_paused() public { +// bytes memory emptyQuorumNumbers = new bytes(0); +// ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; + +// // pause registerOperator +// cheats.prank(pauser); +// registryCoordinator.pause(2 ** PAUSED_REGISTER_OPERATOR); + +// cheats.startPrank(defaultOperator); +// cheats.expectRevert(bytes4(keccak256("CurrentlyPaused()"))); +// registryCoordinator.registerOperator(emptyQuorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); +// } + +// function test_registerOperator_revert_emptyQuorumNumbers() public { +// bytes memory emptyQuorumNumbers = new bytes(0); +// ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; + +// cheats.expectRevert(bytes4(keccak256("BitmapEmpty()"))); +// cheats.prank(defaultOperator); +// registryCoordinator.registerOperator(emptyQuorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); +// } + +// function test_registerOperator_revert_invalidQuorum() public { +// bytes memory quorumNumbersTooLarge = new bytes(1); +// ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; + +// quorumNumbersTooLarge[0] = 0xC0; + +// cheats.expectRevert(BitmapUtils.BitmapValueTooLarge.selector); +// cheats.prank(defaultOperator); +// registryCoordinator.registerOperator(quorumNumbersTooLarge, defaultSocket, pubkeyRegistrationParams, emptySig); +// } + +// function test_registerOperator_revert_nonexistentQuorum() public { +// _deployMockEigenLayerAndAVS(10); +// bytes memory quorumNumbersNotCreated = new bytes(1); +// ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; + +// quorumNumbersNotCreated[0] = 0x0B; + +// cheats.prank(defaultOperator); +// cheats.expectRevert(BitmapUtils.BitmapValueTooLarge.selector); +// registryCoordinator.registerOperator(quorumNumbersNotCreated, defaultSocket, pubkeyRegistrationParams, emptySig); +// } + +// function test_registerOperator_singleQuorum() public { +// bytes memory quorumNumbers = new bytes(1); +// ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; +// quorumNumbers[0] = bytes1(defaultQuorumNumber); + +// uint96 actualStake = _setOperatorWeight(defaultOperator, defaultQuorumNumber, defaultStake); + +// cheats.expectEmit(true, true, true, true, address(registryCoordinator)); +// emit OperatorSocketUpdate(defaultOperatorId, defaultSocket); +// cheats.expectEmit(true, true, true, true, address(blsApkRegistry)); +// emit OperatorAddedToQuorums(defaultOperator, defaultOperatorId, quorumNumbers); +// cheats.expectEmit(true, true, true, true, address(stakeRegistry)); +// emit OperatorStakeUpdate(defaultOperatorId, defaultQuorumNumber, actualStake); +// cheats.expectEmit(true, true, true, true, address(indexRegistry)); +// emit QuorumIndexUpdate(defaultOperatorId, defaultQuorumNumber, 0); + +// uint256 gasBefore = gasleft(); +// cheats.prank(defaultOperator); +// registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); +// uint256 gasAfter = gasleft(); +// emit log_named_uint("gasUsed, register for single quorum", gasBefore - gasAfter); + +// uint256 quorumBitmap = BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers); + +// assertEq(registryCoordinator.getOperatorId(defaultOperator), defaultOperatorId); +// assertEq( +// keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), +// keccak256(abi.encode(ISlashingRegistryCoordinator.OperatorInfo({ +// operatorId: defaultOperatorId, +// status: ISlashingRegistryCoordinator.OperatorStatus.REGISTERED +// }))) +// ); +// assertEq(registryCoordinator.getCurrentQuorumBitmap(defaultOperatorId), quorumBitmap); +// assertEq( +// keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0))), +// keccak256(abi.encode(ISlashingRegistryCoordinatorTypes.QuorumBitmapUpdate({ +// quorumBitmap: uint192(quorumBitmap), +// updateBlockNumber: uint32(block.number), +// nextUpdateBlockNumber: 0 +// }))) +// ); +// } + +// // @notice tests registering an operator for a fuzzed assortment of quorums +// function testFuzz_registerOperator(uint256 quorumBitmap) public { +// // filter the fuzzed input down to only valid quorums +// quorumBitmap = quorumBitmap & MAX_QUORUM_BITMAP; +// ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; +// cheats.assume(quorumBitmap != 0); +// bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); + +// uint96 actualStake; +// for (uint i = 0; i < quorumNumbers.length; i++) { +// actualStake = _setOperatorWeight(defaultOperator, uint8(quorumNumbers[i]), defaultStake); +// } + +// cheats.expectEmit(true, true, true, true, address(registryCoordinator)); +// emit OperatorSocketUpdate(defaultOperatorId, defaultSocket); +// cheats.expectEmit(true, true, true, true, address(registryCoordinator)); +// emit OperatorRegistered(defaultOperator, defaultOperatorId); + +// cheats.expectEmit(true, true, true, true, address(blsApkRegistry)); +// emit OperatorAddedToQuorums(defaultOperator, defaultOperatorId, quorumNumbers); + +// for (uint i = 0; i < quorumNumbers.length; i++) { +// cheats.expectEmit(true, true, true, true, address(stakeRegistry)); +// emit OperatorStakeUpdate(defaultOperatorId, uint8(quorumNumbers[i]), actualStake); +// } + +// for (uint i = 0; i < quorumNumbers.length; i++) { +// cheats.expectEmit(true, true, true, true, address(indexRegistry)); +// emit QuorumIndexUpdate(defaultOperatorId, uint8(quorumNumbers[i]), 0); +// } + +// uint256 gasBefore = gasleft(); +// cheats.prank(defaultOperator); +// registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); +// uint256 gasAfter = gasleft(); +// emit log_named_uint("gasUsed", gasBefore - gasAfter); +// emit log_named_uint("numQuorums", quorumNumbers.length); + +// assertEq(registryCoordinator.getOperatorId(defaultOperator), defaultOperatorId); +// assertEq( +// keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), +// keccak256(abi.encode(ISlashingRegistryCoordinator.OperatorInfo({ +// operatorId: defaultOperatorId, +// status: ISlashingRegistryCoordinator.OperatorStatus.REGISTERED +// }))) +// ); +// assertEq(registryCoordinator.getCurrentQuorumBitmap(defaultOperatorId), quorumBitmap); +// assertEq( +// keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0))), +// keccak256(abi.encode(ISlashingRegistryCoordinatorTypes.QuorumBitmapUpdate({ +// quorumBitmap: uint192(quorumBitmap), +// updateBlockNumber: uint32(block.number), +// nextUpdateBlockNumber: 0 +// }))) +// ); +// } + +// // @notice tests registering an operator for a single quorum and later registering them for an additional quorum +// function test_registerOperator_addingQuorumsAfterInitialRegistration() public { +// uint256 registrationBlockNumber = block.number + 100; +// uint256 nextRegistrationBlockNumber = registrationBlockNumber + 100; +// ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; + +// bytes memory quorumNumbers = new bytes(1); +// quorumNumbers[0] = bytes1(defaultQuorumNumber); + +// _setOperatorWeight(defaultOperator, uint8(quorumNumbers[0]), defaultStake); +// cheats.prank(defaultOperator); +// cheats.roll(registrationBlockNumber); +// registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); + +// bytes memory newQuorumNumbers = new bytes(1); +// newQuorumNumbers[0] = bytes1(defaultQuorumNumber+1); + +// uint96 actualStake = _setOperatorWeight(defaultOperator, uint8(newQuorumNumbers[0]), defaultStake); +// cheats.expectEmit(true, true, true, true, address(registryCoordinator)); +// emit OperatorSocketUpdate(defaultOperatorId, defaultSocket); +// cheats.expectEmit(true, true, true, true, address(blsApkRegistry)); +// emit OperatorAddedToQuorums(defaultOperator, defaultOperatorId, newQuorumNumbers); +// cheats.expectEmit(true, true, true, true, address(stakeRegistry)); +// emit OperatorStakeUpdate(defaultOperatorId, uint8(newQuorumNumbers[0]), actualStake); +// cheats.expectEmit(true, true, true, true, address(indexRegistry)); +// emit QuorumIndexUpdate(defaultOperatorId, uint8(newQuorumNumbers[0]), 0); +// cheats.roll(nextRegistrationBlockNumber); +// cheats.prank(defaultOperator); +// registryCoordinator.registerOperator(newQuorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); + +// uint256 quorumBitmap = BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers) | BitmapUtils.orderedBytesArrayToBitmap(newQuorumNumbers); + +// assertEq(registryCoordinator.getOperatorId(defaultOperator), defaultOperatorId); +// assertEq( +// keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), +// keccak256(abi.encode(ISlashingRegistryCoordinator.OperatorInfo({ +// operatorId: defaultOperatorId, +// status: ISlashingRegistryCoordinator.OperatorStatus.REGISTERED +// }))) +// ); +// assertEq(registryCoordinator.getCurrentQuorumBitmap(defaultOperatorId), quorumBitmap); +// assertEq( +// keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0))), +// keccak256(abi.encode(ISlashingRegistryCoordinatorTypes.QuorumBitmapUpdate({ +// quorumBitmap: uint192(BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers)), +// updateBlockNumber: uint32(registrationBlockNumber), +// nextUpdateBlockNumber: uint32(nextRegistrationBlockNumber) +// }))) +// ); +// assertEq( +// keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 1))), +// keccak256(abi.encode(ISlashingRegistryCoordinatorTypes.QuorumBitmapUpdate({ +// quorumBitmap: uint192(quorumBitmap), +// updateBlockNumber: uint32(nextRegistrationBlockNumber), +// nextUpdateBlockNumber: 0 +// }))) +// ); +// } + +// function test_registerOperator_revert_overFilledQuorum(uint256 pseudoRandomNumber) public { +// uint32 numOperators = defaultMaxOperatorCount; +// uint32 registrationBlockNumber = 200; +// ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; + +// bytes memory quorumNumbers = new bytes(1); +// quorumNumbers[0] = bytes1(defaultQuorumNumber); + +// uint256 quorumBitmap = BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers); + +// cheats.roll(registrationBlockNumber); + +// for (uint i = 0; i < numOperators; i++) { +// BN254.G1Point memory pubKey = BN254.hashToG1(keccak256(abi.encodePacked(pseudoRandomNumber, i))); +// address operator = _incrementAddress(defaultOperator, i); + +// _registerOperatorWithCoordinator(operator, quorumBitmap, pubKey); +// } + +// address operatorToRegister = _incrementAddress(defaultOperator, numOperators); +// BN254.G1Point memory operatorToRegisterPubKey = BN254.hashToG1(keccak256(abi.encodePacked(pseudoRandomNumber, numOperators))); + +// blsApkRegistry.setBLSPublicKey(operatorToRegister, operatorToRegisterPubKey); + +// _setOperatorWeight(operatorToRegister, defaultQuorumNumber, defaultStake); + +// cheats.prank(operatorToRegister); +// cheats.expectRevert(bytes4(keccak256("MaxQuorumsReached()"))); +// registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); +// } + +// function test_registerOperator_revert_operatorAlreadyRegisteredForQuorum() public { +// uint256 registrationBlockNumber = block.number + 100; +// uint256 nextRegistrationBlockNumber = registrationBlockNumber + 100; +// ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; + +// bytes memory quorumNumbers = new bytes(1); +// quorumNumbers[0] = bytes1(defaultQuorumNumber); + +// _setOperatorWeight(defaultOperator, uint8(quorumNumbers[0]), defaultStake); +// cheats.prank(defaultOperator); +// cheats.roll(registrationBlockNumber); + +// registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); + +// cheats.prank(defaultOperator); +// cheats.roll(nextRegistrationBlockNumber); +// cheats.expectRevert(bytes4(keccak256("AlreadyRegisteredForQuorums()"))); +// registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); +// } + +// // tests for the internal `_registerOperator` function: +// function test_registerOperatorInternal_revert_noQuorums() public { +// bytes memory emptyQuorumNumbers = new bytes(0); +// ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; + +// cheats.expectRevert(bytes4(keccak256("BitmapEmpty()"))); +// registryCoordinator._registerOperatorExternal(defaultOperator, defaultOperatorId, emptyQuorumNumbers, defaultSocket, emptySig); +// } + +// function test_registerOperatorInternal_revert_nonexistentQuorum() public { +// bytes memory quorumNumbersTooLarge = new bytes(1); +// ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; + +// quorumNumbersTooLarge[0] = 0xC0; + +// cheats.expectRevert(BitmapUtils.BitmapValueTooLarge.selector); +// registryCoordinator._registerOperatorExternal(defaultOperator, defaultOperatorId, quorumNumbersTooLarge, defaultSocket, emptySig); +// } + +// function test_registerOperatorInternal_revert_operatorAlreadyRegisteredForQuorum() public { +// ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; +// bytes memory quorumNumbers = new bytes(1); +// quorumNumbers[0] = bytes1(defaultQuorumNumber); + +// _setOperatorWeight(defaultOperator, uint8(quorumNumbers[0]), defaultStake); +// registryCoordinator._registerOperatorExternal(defaultOperator, defaultOperatorId, quorumNumbers, defaultSocket, emptySig); + +// cheats.expectRevert(bytes4(keccak256("AlreadyRegisteredForQuorums()"))); +// registryCoordinator._registerOperatorExternal(defaultOperator, defaultOperatorId, quorumNumbers, defaultSocket, emptySig); +// } + +// function test_registerOperatorInternal() public { +// bytes memory quorumNumbers = new bytes(1); +// ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; +// quorumNumbers[0] = bytes1(defaultQuorumNumber); + +// defaultStake = _setOperatorWeight(defaultOperator, uint8(quorumNumbers[0]), defaultStake); + +// cheats.expectEmit(true, true, true, true, address(registryCoordinator)); +// emit OperatorSocketUpdate(defaultOperatorId, defaultSocket); +// cheats.expectEmit(true, true, true, true, address(blsApkRegistry)); +// emit OperatorAddedToQuorums(defaultOperator, defaultOperatorId, quorumNumbers); +// cheats.expectEmit(true, true, true, true, address(stakeRegistry)); +// emit OperatorStakeUpdate(defaultOperatorId, defaultQuorumNumber, defaultStake); +// cheats.expectEmit(true, true, true, true, address(indexRegistry)); +// emit QuorumIndexUpdate(defaultOperatorId, defaultQuorumNumber, 0); + +// registryCoordinator._registerOperatorExternal(defaultOperator, defaultOperatorId, quorumNumbers, defaultSocket, emptySig); + +// uint256 quorumBitmap = BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers); + +// assertEq(registryCoordinator.getOperatorId(defaultOperator), defaultOperatorId); +// assertEq( +// keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), +// keccak256(abi.encode(ISlashingRegistryCoordinator.OperatorInfo({ +// operatorId: defaultOperatorId, +// status: ISlashingRegistryCoordinator.OperatorStatus.REGISTERED +// }))) +// ); +// assertEq(registryCoordinator.getCurrentQuorumBitmap(defaultOperatorId), quorumBitmap); +// assertEq( +// keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0))), +// keccak256(abi.encode(ISlashingRegistryCoordinatorTypes.QuorumBitmapUpdate({ +// quorumBitmap: uint192(quorumBitmap), +// updateBlockNumber: uint32(block.number), +// nextUpdateBlockNumber: 0 +// }))) +// ); +// } +// } + +// // @dev note that this contract also contains tests for the `getQuorumBitmapIndicesAtBlockNumber` and `getQuorumBitmapAtBlockNumberByIndex` view fncs +// contract RegistryCoordinatorUnitTests_DeregisterOperator_EjectOperator is RegistryCoordinatorUnitTests { +// function test_deregisterOperator_revert_paused() public { +// bytes memory quorumNumbers = new bytes(1); +// quorumNumbers[0] = bytes1(defaultQuorumNumber); +// uint256 quorumBitmap = BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers); + +// _registerOperatorWithCoordinator(defaultOperator, quorumBitmap, defaultPubKey); + +// // pause deregisterOperator +// cheats.prank(pauser); +// registryCoordinator.pause(2 ** PAUSED_DEREGISTER_OPERATOR); + +// cheats.expectRevert(bytes4(keccak256("CurrentlyPaused()"))); +// cheats.prank(defaultOperator); +// registryCoordinator.deregisterOperator(quorumNumbers); +// } + +// function test_deregisterOperator_revert_notRegistered() public { +// bytes memory quorumNumbers = new bytes(1); +// quorumNumbers[0] = bytes1(defaultQuorumNumber); + +// cheats.expectRevert(bytes4(keccak256("NotRegistered()"))); +// cheats.prank(defaultOperator); +// registryCoordinator.deregisterOperator(quorumNumbers); +// } + +// function test_deregisterOperator_revert_incorrectQuorums() public { +// bytes memory quorumNumbers = new bytes(1); +// quorumNumbers[0] = bytes1(defaultQuorumNumber); +// uint256 quorumBitmap = BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers); + +// _registerOperatorWithCoordinator(defaultOperator, quorumBitmap, defaultPubKey); + +// quorumNumbers = new bytes(2); +// quorumNumbers[0] = bytes1(defaultQuorumNumber + 1); +// quorumNumbers[1] = bytes1(defaultQuorumNumber + 2); + +// cheats.expectRevert(bytes4(keccak256("NotRegisteredForQuorum()"))); +// cheats.prank(defaultOperator); +// registryCoordinator.deregisterOperator(quorumNumbers); +// } + +// // @notice verifies that an operator who was registered for a single quorum can be deregistered +// function test_deregisterOperator_singleQuorumAndSingleOperator() public { +// ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; +// uint32 registrationBlockNumber = 100; +// uint32 deregistrationBlockNumber = 200; + +// bytes memory quorumNumbers = new bytes(1); +// quorumNumbers[0] = bytes1(defaultQuorumNumber); + +// _setOperatorWeight(defaultOperator, uint8(quorumNumbers[0]), defaultStake); + +// cheats.startPrank(defaultOperator); + +// cheats.roll(registrationBlockNumber); + +// registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); + +// uint256 quorumBitmap = BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers); + +// cheats.expectEmit(true, true, true, true, address(blsApkRegistry)); +// emit OperatorRemovedFromQuorums(defaultOperator, defaultOperatorId, quorumNumbers); +// cheats.expectEmit(true, true, true, true, address(stakeRegistry)); +// emit OperatorStakeUpdate(defaultOperatorId, defaultQuorumNumber, 0); + +// cheats.roll(deregistrationBlockNumber); + +// uint256 gasBefore = gasleft(); +// registryCoordinator.deregisterOperator(quorumNumbers); +// uint256 gasAfter = gasleft(); +// emit log_named_uint("gasUsed", gasBefore - gasAfter); + +// assertEq( +// keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), +// keccak256(abi.encode(ISlashingRegistryCoordinator.OperatorInfo({ +// operatorId: defaultOperatorId, +// status: ISlashingRegistryCoordinator.OperatorStatus.DEREGISTERED +// }))) +// ); +// assertEq(registryCoordinator.getCurrentQuorumBitmap(defaultOperatorId), 0); +// assertEq( +// keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0))), +// keccak256(abi.encode(ISlashingRegistryCoordinatorTypes.QuorumBitmapUpdate({ +// quorumBitmap: uint192(quorumBitmap), +// updateBlockNumber: registrationBlockNumber, +// nextUpdateBlockNumber: deregistrationBlockNumber +// }))) +// ); +// } + +// // @notice verifies that an operator who was registered for a fuzzed set of quorums can be deregistered +// // @dev deregisters the operator from *all* quorums for which they we registered. +// function testFuzz_deregisterOperator_fuzzedQuorumAndSingleOperator(uint256 quorumBitmap) public { +// ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; +// uint32 registrationBlockNumber = 100; +// uint32 deregistrationBlockNumber = 200; + +// // filter down fuzzed input to only valid quorums +// quorumBitmap = quorumBitmap & MAX_QUORUM_BITMAP; +// cheats.assume(quorumBitmap != 0); +// bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); + +// for (uint i = 0; i < quorumNumbers.length; i++) { +// _setOperatorWeight(defaultOperator, uint8(quorumNumbers[i]), defaultStake); +// } + +// cheats.startPrank(defaultOperator); + +// cheats.roll(registrationBlockNumber); + +// registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); + +// cheats.expectEmit(true, true, true, true, address(blsApkRegistry)); +// emit OperatorRemovedFromQuorums(defaultOperator, defaultOperatorId, quorumNumbers); +// for (uint i = 0; i < quorumNumbers.length; i++) { +// cheats.expectEmit(true, true, true, true, address(stakeRegistry)); +// emit OperatorStakeUpdate(defaultOperatorId, uint8(quorumNumbers[i]), 0); +// } + +// cheats.roll(deregistrationBlockNumber); + +// uint256 gasBefore = gasleft(); +// registryCoordinator.deregisterOperator(quorumNumbers); +// uint256 gasAfter = gasleft(); +// emit log_named_uint("gasUsed", gasBefore - gasAfter); +// emit log_named_uint("numQuorums", quorumNumbers.length); + +// assertEq( +// keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), +// keccak256(abi.encode(ISlashingRegistryCoordinator.OperatorInfo({ +// operatorId: defaultOperatorId, +// status: ISlashingRegistryCoordinator.OperatorStatus.DEREGISTERED +// }))) +// ); +// assertEq(registryCoordinator.getCurrentQuorumBitmap(defaultOperatorId), 0); +// assertEq( +// keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0))), +// keccak256(abi.encode(ISlashingRegistryCoordinatorTypes.QuorumBitmapUpdate({ +// quorumBitmap: uint192(quorumBitmap), +// updateBlockNumber: registrationBlockNumber, +// nextUpdateBlockNumber: deregistrationBlockNumber +// }))) +// ); +// } +// // @notice verifies that an operator who was registered for a fuzzed set of quorums can be deregistered from a subset of those quorums +// // @dev deregisters the operator from a fuzzed subset of the quorums for which they we registered. +// function testFuzz_deregisterOperator_singleOperator_partialDeregistration( +// uint256 registrationQuorumBitmap, +// uint256 deregistrationQuorumBitmap +// ) public { +// ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; +// uint32 registrationBlockNumber = 100; +// uint32 deregistrationBlockNumber = 200; + +// // filter down fuzzed input to only valid quorums +// registrationQuorumBitmap = registrationQuorumBitmap & MAX_QUORUM_BITMAP; +// cheats.assume(registrationQuorumBitmap != 0); +// // filter the other fuzzed input to a subset of the first fuzzed input +// deregistrationQuorumBitmap = deregistrationQuorumBitmap & registrationQuorumBitmap; +// cheats.assume(deregistrationQuorumBitmap != 0); +// bytes memory registrationquorumNumbers = BitmapUtils.bitmapToBytesArray(registrationQuorumBitmap); + +// for (uint i = 0; i < registrationquorumNumbers.length; i++) { +// _setOperatorWeight(defaultOperator, uint8(registrationquorumNumbers[i]), defaultStake); +// } + +// cheats.startPrank(defaultOperator); + +// cheats.roll(registrationBlockNumber); + +// registryCoordinator.registerOperator(registrationquorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); + +// bytes memory deregistrationquorumNumbers = BitmapUtils.bitmapToBytesArray(deregistrationQuorumBitmap); + +// cheats.expectEmit(true, true, true, true, address(blsApkRegistry)); +// emit OperatorRemovedFromQuorums(defaultOperator, defaultOperatorId, deregistrationquorumNumbers); +// for (uint i = 0; i < deregistrationquorumNumbers.length; i++) { +// cheats.expectEmit(true, true, true, true, address(stakeRegistry)); +// emit OperatorStakeUpdate(defaultOperatorId, uint8(deregistrationquorumNumbers[i]), 0); +// } + +// cheats.roll(deregistrationBlockNumber); + +// uint256 gasBefore = gasleft(); +// registryCoordinator.deregisterOperator(deregistrationquorumNumbers); +// uint256 gasAfter = gasleft(); +// emit log_named_uint("gasUsed", gasBefore - gasAfter); +// emit log_named_uint("numQuorums", deregistrationquorumNumbers.length); + +// // check that the operator is marked as 'degregistered' only if deregistered from *all* quorums +// if (deregistrationQuorumBitmap == registrationQuorumBitmap) { +// assertEq( +// keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), +// keccak256(abi.encode(ISlashingRegistryCoordinator.OperatorInfo({ +// operatorId: defaultOperatorId, +// status: ISlashingRegistryCoordinator.OperatorStatus.DEREGISTERED +// }))) +// ); +// } else { +// assertEq( +// keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), +// keccak256(abi.encode(ISlashingRegistryCoordinator.OperatorInfo({ +// operatorId: defaultOperatorId, +// status: ISlashingRegistryCoordinator.OperatorStatus.REGISTERED +// }))) +// ); +// } +// // ensure that the operator's current quorum bitmap matches the expectation +// uint256 expectedQuorumBitmap = BitmapUtils.minus(registrationQuorumBitmap, deregistrationQuorumBitmap); +// assertEq(registryCoordinator.getCurrentQuorumBitmap(defaultOperatorId), expectedQuorumBitmap); +// // check that the quorum bitmap history is as expected +// assertEq( +// keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0))), +// keccak256(abi.encode(ISlashingRegistryCoordinatorTypes.QuorumBitmapUpdate({ +// quorumBitmap: uint192(registrationQuorumBitmap), +// updateBlockNumber: registrationBlockNumber, +// nextUpdateBlockNumber: deregistrationBlockNumber +// }))) +// ); +// // note: there will be no second entry in the operator's bitmap history in the event that the operator has totally deregistered +// if (deregistrationQuorumBitmap != registrationQuorumBitmap) { +// assertEq( +// keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 1))), +// keccak256(abi.encode(ISlashingRegistryCoordinatorTypes.QuorumBitmapUpdate({ +// quorumBitmap: uint192(expectedQuorumBitmap), +// updateBlockNumber: deregistrationBlockNumber, +// nextUpdateBlockNumber: 0 +// }))) +// ); +// } +// } + +// // @notice registers the max number of operators with fuzzed bitmaps and then deregisters a pseudorandom operator (from all of their quorums) +// function testFuzz_deregisterOperator_manyOperators(uint256 pseudoRandomNumber) public { +// uint32 numOperators = defaultMaxOperatorCount; + +// uint32 registrationBlockNumber = 100; +// uint32 deregistrationBlockNumber = 200; + +// // pad quorumBitmap with 1 until it has numOperators elements +// uint256[] memory quorumBitmaps = new uint256[](numOperators); +// for (uint i = 0; i < numOperators; i++) { +// // limit to maxQuorumsToRegisterFor quorums via mask so we don't run out of gas, make them all register for quorum 0 as well +// quorumBitmaps[i] = uint256(keccak256(abi.encodePacked("quorumBitmap", pseudoRandomNumber, i))) & (1 << maxQuorumsToRegisterFor - 1) | 1; +// } + +// cheats.roll(registrationBlockNumber); + +// bytes32[] memory lastOperatorInQuorum = new bytes32[](numQuorums); +// for (uint i = 0; i < numOperators; i++) { +// emit log_named_uint("i", i); +// BN254.G1Point memory pubKey = BN254.hashToG1(keccak256(abi.encodePacked(pseudoRandomNumber, i))); +// bytes32 operatorId = BN254.hashG1Point(pubKey); +// address operator = _incrementAddress(defaultOperator, i); + +// _registerOperatorWithCoordinator(operator, quorumBitmaps[i], pubKey); + +// // for each quorum the operator is in, save the operatorId +// bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmaps[i]); +// for (uint j = 0; j < quorumNumbers.length; j++) { +// lastOperatorInQuorum[uint8(quorumNumbers[j])] = operatorId; +// } +// } + +// uint256 indexOfOperatorToDeregister = pseudoRandomNumber % numOperators; +// address operatorToDeregister = _incrementAddress(defaultOperator, indexOfOperatorToDeregister); +// BN254.G1Point memory operatorToDeregisterPubKey = BN254.hashToG1(keccak256(abi.encodePacked(pseudoRandomNumber, indexOfOperatorToDeregister))); +// bytes32 operatorToDeregisterId = BN254.hashG1Point(operatorToDeregisterPubKey); +// uint256 operatorToDeregisterQuorumBitmap = quorumBitmaps[indexOfOperatorToDeregister]; +// bytes memory operatorToDeregisterQuorumNumbers = BitmapUtils.bitmapToBytesArray(operatorToDeregisterQuorumBitmap); + +// bytes32[] memory operatorIdsToSwap = new bytes32[](operatorToDeregisterQuorumNumbers.length); +// for (uint i = 0; i < operatorToDeregisterQuorumNumbers.length; i++) { +// operatorIdsToSwap[i] = lastOperatorInQuorum[uint8(operatorToDeregisterQuorumNumbers[i])]; +// } + +// cheats.expectEmit(true, true, true, true, address(blsApkRegistry)); +// emit OperatorRemovedFromQuorums(operatorToDeregister, operatorToDeregisterId, operatorToDeregisterQuorumNumbers); + +// for (uint i = 0; i < operatorToDeregisterQuorumNumbers.length; i++) { +// cheats.expectEmit(true, true, true, true, address(stakeRegistry)); +// emit OperatorStakeUpdate(operatorToDeregisterId, uint8(operatorToDeregisterQuorumNumbers[i]), 0); +// } + +// cheats.roll(deregistrationBlockNumber); + +// cheats.prank(operatorToDeregister); +// registryCoordinator.deregisterOperator(operatorToDeregisterQuorumNumbers); + +// assertEq( +// keccak256(abi.encode(registryCoordinator.getOperator(operatorToDeregister))), +// keccak256(abi.encode(ISlashingRegistryCoordinator.OperatorInfo({ +// operatorId: operatorToDeregisterId, +// status: ISlashingRegistryCoordinator.OperatorStatus.DEREGISTERED +// }))) +// ); +// assertEq(registryCoordinator.getCurrentQuorumBitmap(defaultOperatorId), 0); +// assertEq( +// keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(operatorToDeregisterId, 0))), +// keccak256(abi.encode(ISlashingRegistryCoordinatorTypes.QuorumBitmapUpdate({ +// quorumBitmap: uint192(operatorToDeregisterQuorumBitmap), +// updateBlockNumber: registrationBlockNumber, +// nextUpdateBlockNumber: deregistrationBlockNumber +// }))) +// ); +// } + +// // @notice verify that it is possible for an operator to register, deregister, and then register again! +// function test_reregisterOperator() public { +// test_deregisterOperator_singleQuorumAndSingleOperator(); + +// ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; +// uint32 reregistrationBlockNumber = 201; + +// bytes memory quorumNumbers = new bytes(1); +// quorumNumbers[0] = bytes1(defaultQuorumNumber); + +// cheats.startPrank(defaultOperator); + +// cheats.roll(reregistrationBlockNumber); + +// // store data before registering, to check against later +// ISlashingRegistryCoordinator.QuorumBitmapUpdate memory previousQuorumBitmapUpdate = +// registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0); + +// // re-register the operator +// registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); +// // check success of registration +// uint256 quorumBitmap = BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers); +// assertEq(registryCoordinator.getOperatorId(defaultOperator), defaultOperatorId, "1"); +// assertEq( +// keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), +// keccak256(abi.encode(ISlashingRegistryCoordinator.OperatorInfo({ +// operatorId: defaultOperatorId, +// status: ISlashingRegistryCoordinator.OperatorStatus.REGISTERED +// }))), +// "2" +// ); +// 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)), +// "4" +// ); +// // check that new entry in bitmap history is as expected +// uint historyLength = registryCoordinator.getQuorumBitmapHistoryLength(defaultOperatorId); +// assertEq( +// keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, historyLength - 1))), +// keccak256(abi.encode(ISlashingRegistryCoordinatorTypes.QuorumBitmapUpdate({ +// quorumBitmap: uint192(quorumBitmap), +// updateBlockNumber: uint32(reregistrationBlockNumber), +// nextUpdateBlockNumber: 0 +// }))), +// "5" +// ); +// } + +// // tests for the internal `_deregisterOperator` function: +// function test_deregisterOperatorExternal_revert_noQuorums() public { +// ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; +// uint32 registrationBlockNumber = 100; +// uint32 deregistrationBlockNumber = 200; + +// bytes memory quorumNumbers = new bytes(1); +// quorumNumbers[0] = bytes1(defaultQuorumNumber); + +// _setOperatorWeight(defaultOperator, uint8(quorumNumbers[0]), defaultStake); + +// cheats.roll(registrationBlockNumber); +// cheats.startPrank(defaultOperator); +// registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); + +// bytes memory emptyQuorumNumbers = new bytes(0); + +// cheats.roll(deregistrationBlockNumber); +// cheats.expectRevert(bytes4(keccak256("BitmapCannotBeZero()"))); +// registryCoordinator._deregisterOperatorExternal(defaultOperator, emptyQuorumNumbers); +// } + +// function test_deregisterOperatorExternal_revert_notRegistered() public { +// bytes memory emptyQuorumNumbers = new bytes(0); +// cheats.expectRevert(bytes4(keccak256("NotRegistered()"))); +// registryCoordinator._deregisterOperatorExternal(defaultOperator, emptyQuorumNumbers); +// } + +// function test_deregisterOperatorExternal_revert_incorrectQuorums() public { +// ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; +// uint32 registrationBlockNumber = 100; +// uint32 deregistrationBlockNumber = 200; + +// bytes memory quorumNumbers = new bytes(1); +// quorumNumbers[0] = bytes1(defaultQuorumNumber); + +// _setOperatorWeight(defaultOperator, uint8(quorumNumbers[0]), defaultStake); + +// cheats.roll(registrationBlockNumber); +// cheats.startPrank(defaultOperator); +// registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); + +// bytes memory incorrectQuorum = new bytes(1); +// incorrectQuorum[0] = bytes1(defaultQuorumNumber + 1); + +// cheats.roll(deregistrationBlockNumber); +// cheats.expectRevert(bytes4(keccak256("NotRegisteredForQuorum()"))); +// registryCoordinator._deregisterOperatorExternal(defaultOperator, incorrectQuorum); +// } + +// function test_reregisterOperator_revert_reregistrationDelay() public { +// uint256 reregistrationDelay = 1 days; +// cheats.warp(block.timestamp + reregistrationDelay); +// cheats.prank(registryCoordinatorOwner); +// registryCoordinator.setEjectionCooldown(reregistrationDelay); + +// ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; +// uint32 registrationBlockNumber = 100; +// uint32 reregistrationBlockNumber = 200; + +// bytes memory quorumNumbers = new bytes(1); +// quorumNumbers[0] = bytes1(defaultQuorumNumber); + +// _setOperatorWeight(defaultOperator, uint8(quorumNumbers[0]), defaultStake); + +// cheats.prank(defaultOperator); +// cheats.roll(registrationBlockNumber); +// registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); + +// cheats.prank(ejector); +// registryCoordinator.ejectOperator(defaultOperator, quorumNumbers); + +// cheats.prank(defaultOperator); +// cheats.roll(reregistrationBlockNumber); +// cheats.expectRevert(bytes4(keccak256("CannotReregisterYet()"))); +// registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); +// } + +// function test_reregisterOperator_reregistrationDelay() public { +// uint256 reregistrationDelay = 1 days; +// cheats.warp(block.timestamp + reregistrationDelay); +// cheats.prank(registryCoordinatorOwner); +// registryCoordinator.setEjectionCooldown(reregistrationDelay); + +// ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; +// uint32 registrationBlockNumber = 100; +// uint32 reregistrationBlockNumber = 200; + +// bytes memory quorumNumbers = new bytes(1); +// quorumNumbers[0] = bytes1(defaultQuorumNumber); + +// _setOperatorWeight(defaultOperator, uint8(quorumNumbers[0]), defaultStake); + +// cheats.prank(defaultOperator); +// cheats.roll(registrationBlockNumber); +// registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); + +// cheats.prank(ejector); +// registryCoordinator.ejectOperator(defaultOperator, quorumNumbers); + +// cheats.prank(defaultOperator); +// cheats.roll(reregistrationBlockNumber); +// cheats.warp(block.timestamp + reregistrationDelay + 1); +// registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); +// } + +// // note: this is not possible to test, because there is no route to getting the operator registered for nonexistent quorums +// // function test_deregisterOperatorExternal_revert_nonexistentQuorums() public { + +// function testFuzz_deregisterOperatorInternal_partialDeregistration( +// uint256 registrationQuorumBitmap, +// uint256 deregistrationQuorumBitmap +// ) public { +// ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; +// uint32 registrationBlockNumber = 100; +// uint32 deregistrationBlockNumber = 200; + +// // filter down fuzzed input to only valid quorums +// registrationQuorumBitmap = registrationQuorumBitmap & MAX_QUORUM_BITMAP; +// cheats.assume(registrationQuorumBitmap != 0); +// // filter the other fuzzed input to a subset of the first fuzzed input +// deregistrationQuorumBitmap = deregistrationQuorumBitmap & registrationQuorumBitmap; +// cheats.assume(deregistrationQuorumBitmap != 0); +// bytes memory registrationquorumNumbers = BitmapUtils.bitmapToBytesArray(registrationQuorumBitmap); + +// for (uint i = 0; i < registrationquorumNumbers.length; i++) { +// _setOperatorWeight(defaultOperator, uint8(registrationquorumNumbers[i]), defaultStake); +// } + +// cheats.roll(registrationBlockNumber); +// cheats.startPrank(defaultOperator); +// registryCoordinator.registerOperator(registrationquorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); + +// bytes memory deregistrationquorumNumbers = BitmapUtils.bitmapToBytesArray(deregistrationQuorumBitmap); + +// cheats.expectEmit(true, true, true, true, address(blsApkRegistry)); +// emit OperatorRemovedFromQuorums(defaultOperator, defaultOperatorId, deregistrationquorumNumbers); +// for (uint i = 0; i < deregistrationquorumNumbers.length; i++) { +// cheats.expectEmit(true, true, true, true, address(stakeRegistry)); +// emit OperatorStakeUpdate(defaultOperatorId, uint8(deregistrationquorumNumbers[i]), 0); +// } + +// cheats.roll(deregistrationBlockNumber); + +// registryCoordinator._deregisterOperatorExternal(defaultOperator, deregistrationquorumNumbers); + +// // check that the operator is marked as 'degregistered' only if deregistered from *all* quorums +// if (deregistrationQuorumBitmap == registrationQuorumBitmap) { +// assertEq( +// keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), +// keccak256(abi.encode(ISlashingRegistryCoordinator.OperatorInfo({ +// operatorId: defaultOperatorId, +// status: ISlashingRegistryCoordinator.OperatorStatus.DEREGISTERED +// }))) +// ); +// } else { +// assertEq( +// keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), +// keccak256(abi.encode(ISlashingRegistryCoordinator.OperatorInfo({ +// operatorId: defaultOperatorId, +// status: ISlashingRegistryCoordinator.OperatorStatus.REGISTERED +// }))) +// ); +// } +// // ensure that the operator's current quorum bitmap matches the expectation +// uint256 expectedQuorumBitmap = BitmapUtils.minus(registrationQuorumBitmap, deregistrationQuorumBitmap); +// assertEq(registryCoordinator.getCurrentQuorumBitmap(defaultOperatorId), expectedQuorumBitmap); +// // check that the quorum bitmap history is as expected +// assertEq( +// keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0))), +// keccak256(abi.encode(ISlashingRegistryCoordinatorTypes.QuorumBitmapUpdate({ +// quorumBitmap: uint192(registrationQuorumBitmap), +// updateBlockNumber: registrationBlockNumber, +// nextUpdateBlockNumber: deregistrationBlockNumber +// }))) +// ); +// // note: there will be no second entry in the operator's bitmap history in the event that the operator has totally deregistered +// if (deregistrationQuorumBitmap != registrationQuorumBitmap) { +// assertEq( +// keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 1))), +// keccak256(abi.encode(ISlashingRegistryCoordinatorTypes.QuorumBitmapUpdate({ +// quorumBitmap: uint192(expectedQuorumBitmap), +// updateBlockNumber: deregistrationBlockNumber, +// nextUpdateBlockNumber: 0 +// }))) +// ); +// } +// } + +// function test_ejectOperator_allQuorums() public { +// // register operator with default stake with default quorum number +// bytes memory quorumNumbers = new bytes(1); +// quorumNumbers[0] = bytes1(defaultQuorumNumber); +// ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; + +// _setOperatorWeight(defaultOperator, uint8(quorumNumbers[0]), defaultStake); + +// cheats.prank(defaultOperator); +// registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); + +// cheats.expectEmit(true, true, true, true, address(blsApkRegistry)); +// emit OperatorRemovedFromQuorums(defaultOperator, defaultOperatorId, quorumNumbers); + +// cheats.expectEmit(true, true, true, true, address(stakeRegistry)); +// emit OperatorStakeUpdate(defaultOperatorId, uint8(quorumNumbers[0]), 0); + +// // eject +// cheats.prank(ejector); +// registryCoordinator.ejectOperator(defaultOperator, quorumNumbers); + +// // make sure the operator is deregistered +// assertEq( +// keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), +// keccak256(abi.encode(ISlashingRegistryCoordinator.OperatorInfo({ +// operatorId: defaultOperatorId, +// status: ISlashingRegistryCoordinator.OperatorStatus.DEREGISTERED +// }))) +// ); +// // make sure the operator is not in any quorums +// assertEq(registryCoordinator.getCurrentQuorumBitmap(defaultOperatorId), 0); +// } + +// function test_ejectOperator_subsetOfQuorums() public { +// // register operator with default stake with 2 quorums +// 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++) { +// _setOperatorWeight(defaultOperator, uint8(quorumNumbers[i]), defaultStake); +// } + +// cheats.prank(defaultOperator); +// registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); + +// // eject from only first quorum +// bytes memory quorumNumbersToEject = new bytes(1); +// quorumNumbersToEject[0] = quorumNumbers[0]; + +// cheats.expectEmit(true, true, true, true, address(blsApkRegistry)); +// emit OperatorRemovedFromQuorums(defaultOperator, defaultOperatorId, quorumNumbersToEject); + +// cheats.expectEmit(true, true, true, true, address(stakeRegistry)); +// emit OperatorStakeUpdate(defaultOperatorId, uint8(quorumNumbersToEject[0]), 0); + +// cheats.prank(ejector); +// registryCoordinator.ejectOperator(defaultOperator, quorumNumbersToEject); + +// // make sure the operator is registered +// assertEq( +// keccak256(abi.encode(registryCoordinator.getOperator(defaultOperator))), +// keccak256(abi.encode(ISlashingRegistryCoordinator.OperatorInfo({ +// operatorId: defaultOperatorId, +// status: ISlashingRegistryCoordinator.OperatorStatus.REGISTERED +// }))) +// ); +// // make sure the operator is properly removed from the quorums +// assertEq( +// registryCoordinator.getCurrentQuorumBitmap(defaultOperatorId), +// // quorumsRegisteredFor & ~quorumsEjectedFrom +// BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers) & ~BitmapUtils.orderedBytesArrayToBitmap(quorumNumbersToEject) +// ); +// } + +// function test_ejectOperator_revert_notEjector() public { +// bytes memory quorumNumbers = new bytes(1); +// quorumNumbers[0] = bytes1(defaultQuorumNumber); +// ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; + +// _setOperatorWeight(defaultOperator, uint8(quorumNumbers[0]), defaultStake); + +// cheats.prank(defaultOperator); +// registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); + +// cheats.expectRevert(bytes4(keccak256("OnlyEjector()"))); +// cheats.prank(defaultOperator); +// registryCoordinator.ejectOperator(defaultOperator, quorumNumbers); +// } + +// function test_getQuorumBitmapIndicesAtBlockNumber_revert_notRegistered() public { +// uint32 blockNumber; +// bytes32[] memory operatorIds = new bytes32[](1); +// cheats.expectRevert("RegistryCoordinator.getQuorumBitmapIndexAtBlockNumber: no bitmap update found for operatorId"); +// registryCoordinator.getQuorumBitmapIndicesAtBlockNumber(blockNumber, operatorIds); +// } + +// // @notice tests for correct reversion and return values in the event that an operator registers +// function test_getQuorumBitmapIndicesAtBlockNumber_operatorRegistered() public { +// // register the operator +// ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; +// uint32 registrationBlockNumber = 100; +// bytes memory quorumNumbers = new bytes(1); +// quorumNumbers[0] = bytes1(defaultQuorumNumber); +// _setOperatorWeight(defaultOperator, uint8(quorumNumbers[0]), defaultStake); +// cheats.roll(registrationBlockNumber); +// cheats.startPrank(defaultOperator); +// registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); + +// uint32 blockNumber = 0; +// bytes32[] memory operatorIds = new bytes32[](1); +// operatorIds[0] = defaultOperatorId; + +// uint32[] memory returnArray; +// cheats.expectRevert("RegistryCoordinator.getQuorumBitmapIndexAtBlockNumber: no bitmap update found for operatorId"); +// registryCoordinator.getQuorumBitmapIndicesAtBlockNumber(blockNumber, operatorIds); + +// blockNumber = registrationBlockNumber; +// returnArray = registryCoordinator.getQuorumBitmapIndicesAtBlockNumber(blockNumber, operatorIds); +// assertEq(returnArray[0], 0, "defaultOperator bitmap index at blockNumber registrationBlockNumber was not 0"); + +// blockNumber = registrationBlockNumber + 1; +// returnArray = registryCoordinator.getQuorumBitmapIndicesAtBlockNumber(blockNumber, operatorIds); +// assertEq(returnArray[0], 0, "defaultOperator bitmap index at blockNumber registrationBlockNumber + 1 was not 0"); +// } + +// // @notice tests for correct reversion and return values in the event that an operator registers and later deregisters +// function test_getQuorumBitmapIndicesAtBlockNumber_operatorDeregistered() public { +// test_deregisterOperator_singleQuorumAndSingleOperator(); +// uint32 registrationBlockNumber = 100; +// uint32 deregistrationBlockNumber = 200; +// uint32 blockNumber = 0; +// bytes32[] memory operatorIds = new bytes32[](1); +// operatorIds[0] = defaultOperatorId; + +// uint32[] memory returnArray; +// cheats.expectRevert("RegistryCoordinator.getQuorumBitmapIndexAtBlockNumber: no bitmap update found for operatorId"); +// registryCoordinator.getQuorumBitmapIndicesAtBlockNumber(blockNumber, operatorIds); + +// blockNumber = registrationBlockNumber; +// returnArray = registryCoordinator.getQuorumBitmapIndicesAtBlockNumber(blockNumber, operatorIds); +// assertEq(returnArray[0], 0, "defaultOperator bitmap index at blockNumber registrationBlockNumber was not 0"); + +// blockNumber = registrationBlockNumber + 1; +// returnArray = registryCoordinator.getQuorumBitmapIndicesAtBlockNumber(blockNumber, operatorIds); +// assertEq(returnArray[0], 0, "defaultOperator bitmap index at blockNumber registrationBlockNumber + 1 was not 0"); + +// blockNumber = deregistrationBlockNumber; +// returnArray = registryCoordinator.getQuorumBitmapIndicesAtBlockNumber(blockNumber, operatorIds); +// assertEq(returnArray[0], 1, "defaultOperator bitmap index at blockNumber deregistrationBlockNumber was not 1"); + +// blockNumber = deregistrationBlockNumber + 1; +// returnArray = registryCoordinator.getQuorumBitmapIndicesAtBlockNumber(blockNumber, operatorIds); +// assertEq(returnArray[0], 1, "defaultOperator bitmap index at blockNumber deregistrationBlockNumber + 1 was not 1"); +// } + +// // @notice tests for correct reversion and return values in the event that an operator registers and later deregisters +// function test_getQuorumBitmapAtBlockNumberByIndex_operatorDeregistered() public { +// test_deregisterOperator_singleQuorumAndSingleOperator(); +// uint32 registrationBlockNumber = 100; +// uint32 deregistrationBlockNumber = 200; +// uint32 blockNumber = 0; +// bytes32 operatorId = defaultOperatorId; +// uint256 index = 0; + +// uint192 defaultQuorumBitmap = 1; +// uint192 emptyBitmap = 0; + +// // try an incorrect blockNumber input and confirm reversion +// cheats.expectRevert(QuorumBitmapHistoryLib.BitmapUpdateIsAfterBlockNumber.selector); +// uint192 returnVal = registryCoordinator.getQuorumBitmapAtBlockNumberByIndex(operatorId, blockNumber, index); + +// blockNumber = registrationBlockNumber; +// returnVal = registryCoordinator.getQuorumBitmapAtBlockNumberByIndex(operatorId, blockNumber, index); +// assertEq(returnVal, defaultQuorumBitmap, "defaultOperator bitmap index at blockNumber registrationBlockNumber was not defaultQuorumBitmap"); + +// blockNumber = registrationBlockNumber + 1; +// returnVal = registryCoordinator.getQuorumBitmapAtBlockNumberByIndex(operatorId, blockNumber, index); +// assertEq(returnVal, defaultQuorumBitmap, "defaultOperator bitmap index at blockNumber registrationBlockNumber + 1 was not defaultQuorumBitmap"); + +// // try an incorrect index input and confirm reversion +// index = 1; +// cheats.expectRevert(QuorumBitmapHistoryLib.BitmapUpdateIsAfterBlockNumber.selector); +// returnVal = registryCoordinator.getQuorumBitmapAtBlockNumberByIndex(operatorId, blockNumber, index); + +// blockNumber = deregistrationBlockNumber; +// returnVal = registryCoordinator.getQuorumBitmapAtBlockNumberByIndex(operatorId, blockNumber, index); +// assertEq(returnVal, emptyBitmap, "defaultOperator bitmap index at blockNumber deregistrationBlockNumber was not emptyBitmap"); + +// blockNumber = deregistrationBlockNumber + 1; +// returnVal = registryCoordinator.getQuorumBitmapAtBlockNumberByIndex(operatorId, blockNumber, index); +// assertEq(returnVal, emptyBitmap, "defaultOperator bitmap index at blockNumber deregistrationBlockNumber + 1 was not emptyBitmap"); + +// // try an incorrect index input and confirm reversion +// index = 0; +// cheats.expectRevert(QuorumBitmapHistoryLib.NextBitmapUpdateIsBeforeBlockNumber.selector); +// returnVal = registryCoordinator.getQuorumBitmapAtBlockNumberByIndex(operatorId, blockNumber, index); +// } +// } + +// contract RegistryCoordinatorUnitTests_RegisterOperatorWithChurn is RegistryCoordinatorUnitTests { +// // @notice registers an operator for a single quorum, with a fuzzed pubkey, churning out another operator from the quorum +// function testFuzz_registerOperatorWithChurn(uint256 pseudoRandomNumber) public { +// uint32 numOperators = defaultMaxOperatorCount; +// uint32 kickRegistrationBlockNumber = 100; +// uint32 registrationBlockNumber = 200; + +// bytes memory quorumNumbers = new bytes(1); +// quorumNumbers[0] = bytes1(defaultQuorumNumber); + +// uint256 quorumBitmap = BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers); + +// cheats.roll(kickRegistrationBlockNumber); + +// for (uint i = 0; i < numOperators - 1; i++) { +// BN254.G1Point memory pubKey = BN254.hashToG1(keccak256(abi.encodePacked(pseudoRandomNumber, i))); +// address operator = _incrementAddress(defaultOperator, i); + +// _registerOperatorWithCoordinator(operator, quorumBitmap, pubKey); +// } + +// address operatorToRegister = _incrementAddress(defaultOperator, numOperators); +// BN254.G1Point memory operatorToRegisterPubKey = BN254.hashToG1(keccak256(abi.encodePacked(pseudoRandomNumber, numOperators))); +// bytes32 operatorToRegisterId = BN254.hashG1Point(operatorToRegisterPubKey); +// bytes32 operatorToKickId; +// address operatorToKick; + +// // register last operator before kick +// ISlashingRegistryCoordinator.OperatorKickParam[] memory operatorKickParams = new ISlashingRegistryCoordinator.OperatorKickParam[](1); +// { +// BN254.G1Point memory pubKey = BN254.hashToG1(keccak256(abi.encodePacked(pseudoRandomNumber, numOperators - 1))); +// operatorToKickId = BN254.hashG1Point(pubKey); +// operatorToKick = _incrementAddress(defaultOperator, numOperators - 1); + +// _registerOperatorWithCoordinator(operatorToKick, quorumBitmap, pubKey); + +// bytes32[] memory operatorIdsToSwap = new bytes32[](1); +// // operatorIdsToSwap[0] = operatorToRegisterId +// operatorIdsToSwap[0] = operatorToRegisterId; + +// operatorKickParams[0] = ISlashingRegistryCoordinator.OperatorKickParam({ +// quorumNumber: defaultQuorumNumber, +// operator: operatorToKick +// }); +// } + +// blsApkRegistry.setBLSPublicKey(operatorToRegister, operatorToRegisterPubKey); + +// uint96 registeringStake = defaultKickBIPsOfOperatorStake * defaultStake; +// _setOperatorWeight(operatorToRegister, defaultQuorumNumber, registeringStake); + +// cheats.roll(registrationBlockNumber); +// cheats.expectEmit(true, true, true, true, address(registryCoordinator)); +// emit OperatorSocketUpdate(operatorToRegisterId, defaultSocket); +// cheats.expectEmit(true, true, true, true, address(registryCoordinator)); +// emit OperatorRegistered(operatorToRegister, operatorToRegisterId); + +// cheats.expectEmit(true, true, true, true, address(blsApkRegistry)); +// emit OperatorAddedToQuorums(operatorToRegister, operatorToRegisterId, quorumNumbers); +// cheats.expectEmit(true, true, true, false, address(stakeRegistry)); +// emit OperatorStakeUpdate(operatorToRegisterId, defaultQuorumNumber, registeringStake - 1); +// cheats.expectEmit(true, true, true, true, address(indexRegistry)); +// emit QuorumIndexUpdate(operatorToRegisterId, defaultQuorumNumber, numOperators); + +// cheats.expectEmit(true, true, true, true, address(registryCoordinator)); +// emit OperatorDeregistered(operatorKickParams[0].operator, operatorToKickId); +// cheats.expectEmit(true, true, true, true, address(blsApkRegistry)); +// emit OperatorRemovedFromQuorums(operatorKickParams[0].operator, operatorToKickId, quorumNumbers); +// cheats.expectEmit(true, true, true, true, address(stakeRegistry)); +// emit OperatorStakeUpdate(operatorToKickId, defaultQuorumNumber, 0); +// cheats.expectEmit(true, true, true, true, address(indexRegistry)); +// emit QuorumIndexUpdate(operatorToRegisterId, defaultQuorumNumber, numOperators - 1); + +// { +// ISignatureUtils.SignatureWithSaltAndExpiry memory emptyAVSRegSig; +// ISignatureUtils.SignatureWithSaltAndExpiry memory signatureWithExpiry = +// _signOperatorChurnApproval(operatorToRegister, operatorToRegisterId, operatorKickParams, defaultSalt, block.timestamp + 10); +// cheats.prank(operatorToRegister); +// uint256 gasBefore = gasleft(); +// registryCoordinator.registerOperatorWithChurn( +// quorumNumbers, +// defaultSocket, +// pubkeyRegistrationParams, +// operatorKickParams, +// signatureWithExpiry, +// emptyAVSRegSig +// ); +// uint256 gasAfter = gasleft(); +// emit log_named_uint("gasUsed", gasBefore - gasAfter); +// } + +// assertEq( +// keccak256(abi.encode(registryCoordinator.getOperator(operatorToRegister))), +// keccak256(abi.encode(ISlashingRegistryCoordinator.OperatorInfo({ +// operatorId: operatorToRegisterId, +// status: ISlashingRegistryCoordinator.OperatorStatus.REGISTERED +// }))) +// ); +// assertEq( +// keccak256(abi.encode(registryCoordinator.getOperator(operatorToKick))), +// keccak256(abi.encode(ISlashingRegistryCoordinator.OperatorInfo({ +// operatorId: operatorToKickId, +// status: ISlashingRegistryCoordinator.OperatorStatus.DEREGISTERED +// }))) +// ); +// assertEq( +// keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(operatorToKickId, 0))), +// keccak256(abi.encode(ISlashingRegistryCoordinatorTypes.QuorumBitmapUpdate({ +// quorumBitmap: uint192(quorumBitmap), +// updateBlockNumber: kickRegistrationBlockNumber, +// nextUpdateBlockNumber: registrationBlockNumber +// }))) +// ); +// } + +// function test_registerOperatorWithChurn_revert_lessThanKickBIPsOfOperatorStake(uint256 pseudoRandomNumber) public { +// bytes memory quorumNumbers = new bytes(1); +// quorumNumbers[0] = bytes1(defaultQuorumNumber); +// ISignatureUtils.SignatureWithSaltAndExpiry memory emptyAVSRegSig; + +// ( +// address operatorToRegister, +// BN254.G1Point memory operatorToRegisterPubKey, +// ISlashingRegistryCoordinator.OperatorKickParam[] memory operatorKickParams +// ) = _test_registerOperatorWithChurn_SetUp(pseudoRandomNumber, quorumNumbers, defaultStake); +// bytes32 operatorToRegisterId = BN254.hashG1Point(operatorToRegisterPubKey); + +// _setOperatorWeight(operatorToRegister, defaultQuorumNumber, defaultStake); + +// cheats.roll(registrationBlockNumber); +// ISignatureUtils.SignatureWithSaltAndExpiry memory signatureWithExpiry = +// _signOperatorChurnApproval(operatorToRegister, operatorToRegisterId, operatorKickParams, defaultSalt, block.timestamp + 10); +// cheats.prank(operatorToRegister); +// cheats.expectRevert(bytes4(keccak256("InsufficientStakeForChurn()"))); +// registryCoordinator.registerOperatorWithChurn( +// quorumNumbers, +// defaultSocket, +// pubkeyRegistrationParams, +// operatorKickParams, +// signatureWithExpiry, +// emptyAVSRegSig +// ); +// } + +// function test_registerOperatorWithChurn_revert_lessThanKickBIPsOfTotalStake(uint256 pseudoRandomNumber) public { +// bytes memory quorumNumbers = new bytes(1); +// quorumNumbers[0] = bytes1(defaultQuorumNumber); +// ISignatureUtils.SignatureWithSaltAndExpiry memory emptyAVSRegSig; + +// uint96 operatorToKickStake = defaultMaxOperatorCount * defaultStake; +// ( +// address operatorToRegister, +// BN254.G1Point memory operatorToRegisterPubKey, +// ISlashingRegistryCoordinator.OperatorKickParam[] memory operatorKickParams +// ) = _test_registerOperatorWithChurn_SetUp(pseudoRandomNumber, quorumNumbers, operatorToKickStake); +// bytes32 operatorToRegisterId = BN254.hashG1Point(operatorToRegisterPubKey); + +// // set the stake of the operator to register to the defaultKickBIPsOfOperatorStake multiple of the operatorToKickStake +// _setOperatorWeight(operatorToRegister, defaultQuorumNumber, operatorToKickStake * defaultKickBIPsOfOperatorStake / 10000 + 1); + +// cheats.roll(registrationBlockNumber); +// ISignatureUtils.SignatureWithSaltAndExpiry memory signatureWithExpiry = +// _signOperatorChurnApproval(operatorToRegister, operatorToRegisterId, operatorKickParams, defaultSalt, block.timestamp + 10); +// cheats.prank(operatorToRegister); +// cheats.expectRevert(bytes4(keccak256("CannotKickOperatorAboveThreshold()"))); +// registryCoordinator.registerOperatorWithChurn( +// quorumNumbers, +// defaultSocket, +// pubkeyRegistrationParams, +// operatorKickParams, +// signatureWithExpiry, +// emptyAVSRegSig +// ); +// } + +// function test_registerOperatorWithChurn_revert_invalidChurnApproverSignature(uint256 pseudoRandomNumber) public { +// bytes memory quorumNumbers = new bytes(1); +// quorumNumbers[0] = bytes1(defaultQuorumNumber); +// ISignatureUtils.SignatureWithSaltAndExpiry memory emptyAVSRegSig; + +// ( +// address operatorToRegister, +// , +// ISlashingRegistryCoordinator.OperatorKickParam[] memory operatorKickParams +// ) = _test_registerOperatorWithChurn_SetUp(pseudoRandomNumber, quorumNumbers, defaultStake); + +// uint96 registeringStake = defaultKickBIPsOfOperatorStake * defaultStake; +// _setOperatorWeight(operatorToRegister, defaultQuorumNumber, registeringStake); + +// cheats.roll(registrationBlockNumber); +// ISignatureUtils.SignatureWithSaltAndExpiry memory signatureWithSaltAndExpiry; +// signatureWithSaltAndExpiry.expiry = block.timestamp + 10; +// signatureWithSaltAndExpiry.signature = +// hex"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001B"; +// signatureWithSaltAndExpiry.salt = defaultSalt; +// cheats.prank(operatorToRegister); +// cheats.expectRevert(bytes4(keccak256("InvalidSignature()"))); +// registryCoordinator.registerOperatorWithChurn( +// quorumNumbers, +// defaultSocket, +// pubkeyRegistrationParams, +// operatorKickParams, +// signatureWithSaltAndExpiry, +// emptyAVSRegSig +// ); +// } + +// function test_registerOperatorWithChurn_revert_expiredChurnApproverSignature(uint256 pseudoRandomNumber) public { +// bytes memory quorumNumbers = new bytes(1); +// quorumNumbers[0] = bytes1(defaultQuorumNumber); +// ISignatureUtils.SignatureWithSaltAndExpiry memory emptyAVSRegSig; + +// ( +// address operatorToRegister, +// BN254.G1Point memory operatorToRegisterPubKey, +// ISlashingRegistryCoordinator.OperatorKickParam[] memory operatorKickParams +// ) = _test_registerOperatorWithChurn_SetUp(pseudoRandomNumber, quorumNumbers, defaultStake); +// bytes32 operatorToRegisterId = BN254.hashG1Point(operatorToRegisterPubKey); + +// uint96 registeringStake = defaultKickBIPsOfOperatorStake * defaultStake; +// _setOperatorWeight(operatorToRegister, defaultQuorumNumber, registeringStake); + +// cheats.roll(registrationBlockNumber); +// ISignatureUtils.SignatureWithSaltAndExpiry memory signatureWithSaltAndExpiry = +// _signOperatorChurnApproval(operatorToRegister, operatorToRegisterId, operatorKickParams, defaultSalt, block.timestamp - 1); +// cheats.prank(operatorToRegister); +// cheats.expectRevert(bytes4(keccak256("SignatureExpired()"))); +// registryCoordinator.registerOperatorWithChurn( +// quorumNumbers, +// defaultSocket, +// pubkeyRegistrationParams, +// operatorKickParams, +// signatureWithSaltAndExpiry, +// emptyAVSRegSig +// ); +// } +// } + +// contract RegistryCoordinatorUnitTests_UpdateOperators is RegistryCoordinatorUnitTests { +// function test_updateOperators_revert_paused() public { +// cheats.prank(pauser); +// registryCoordinator.pause(2 ** PAUSED_UPDATE_OPERATOR); + +// address[] memory operatorsToUpdate = new address[](1); +// operatorsToUpdate[0] = defaultOperator; + +// cheats.expectRevert(bytes4(keccak256("CurrentlyPaused()"))); +// registryCoordinator.updateOperators(operatorsToUpdate); +// } + +// // @notice tests the `updateOperators` function with a single registered operator as input +// function test_updateOperators_singleOperator() public { +// // register the default operator +// ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; +// uint32 registrationBlockNumber = 100; +// bytes memory quorumNumbers = new bytes(1); +// quorumNumbers[0] = bytes1(defaultQuorumNumber); +// _setOperatorWeight(defaultOperator, uint8(quorumNumbers[0]), defaultStake); +// cheats.startPrank(defaultOperator); +// cheats.roll(registrationBlockNumber); +// registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); + +// address[] memory operatorsToUpdate = new address[](1); +// operatorsToUpdate[0] = defaultOperator; + +// registryCoordinator.updateOperators(operatorsToUpdate); +// } + +// // @notice tests the `updateOperators` function with a single registered operator as input +// // @dev also sets up return data from the StakeRegistry +// function testFuzz_updateOperators_singleOperator(uint192 registrationBitmap, uint192 mockReturnData) public { +// // filter fuzzed inputs to only valid inputs +// cheats.assume(registrationBitmap != 0); +// mockReturnData = (mockReturnData & registrationBitmap); +// emit log_named_uint("mockReturnData", mockReturnData); + +// // register the default operator +// ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; +// uint32 registrationBlockNumber = 100; +// bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(registrationBitmap); +// for (uint256 i = 0; i < quorumNumbers.length; ++i) { +// _setOperatorWeight(defaultOperator, uint8(quorumNumbers[i]), defaultStake); +// } +// cheats.startPrank(defaultOperator); +// cheats.roll(registrationBlockNumber); +// registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); + +// address[] memory operatorsToUpdate = new address[](1); +// operatorsToUpdate[0] = defaultOperator; + +// uint192 quorumBitmapBefore = registryCoordinator.getCurrentQuorumBitmap(defaultOperatorId); +// assertEq(quorumBitmapBefore, registrationBitmap, "operator bitmap somehow incorrect"); + +// // make the stake registry return info that the operator should be removed from quorums +// uint192 quorumBitmapToRemove = mockReturnData; +// bytes memory quorumNumbersToRemove = BitmapUtils.bitmapToBytesArray(quorumBitmapToRemove); +// for (uint256 i = 0; i < quorumNumbersToRemove.length; ++i) { +// _setOperatorWeight(defaultOperator, uint8(quorumNumbersToRemove[i]), 0); +// } +// uint256 expectedQuorumBitmap = BitmapUtils.minus(quorumBitmapBefore, quorumBitmapToRemove); + +// registryCoordinator.updateOperators(operatorsToUpdate); +// uint192 quorumBitmapAfter = registryCoordinator.getCurrentQuorumBitmap(defaultOperatorId); +// assertEq(expectedQuorumBitmap, quorumBitmapAfter, "quorum bitmap did not update correctly"); +// } + +// // @notice tests the `updateOperators` function with a single *un*registered operator as input +// function test_updateOperators_unregisteredOperator() public view { +// address[] memory operatorsToUpdate = new address[](1); +// operatorsToUpdate[0] = defaultOperator; + +// // force a staticcall to the `updateOperators` function -- this should *pass* because the call should be a strict no-op! +// (bool success, ) = address(registryCoordinator).staticcall(abi.encodeWithSignature("updateOperators(address[])", operatorsToUpdate)); +// require(success, "staticcall failed!"); +// } + +// function test_updateOperatorsForQuorum_revert_paused() public { +// cheats.prank(pauser); +// registryCoordinator.pause(2 ** PAUSED_UPDATE_OPERATOR); + +// address[][] memory operatorsToUpdate = new address[][](1); +// address[] memory operatorArray = new address[](1); +// operatorArray[0] = defaultOperator; +// operatorsToUpdate[0] = operatorArray; +// bytes memory quorumNumbers = new bytes(1); +// quorumNumbers[0] = bytes1(defaultQuorumNumber); + +// cheats.expectRevert(bytes4(keccak256("CurrentlyPaused()"))); +// registryCoordinator.updateOperatorsForQuorum(operatorsToUpdate, quorumNumbers); +// } + +// function test_updateOperatorsForQuorum_revert_nonexistentQuorum() public { +// _deployMockEigenLayerAndAVS(10); +// bytes memory quorumNumbersNotCreated = new bytes(1); +// quorumNumbersNotCreated[0] = 0x0B; +// address[][] memory operatorsToUpdate = new address[][](1); + +// cheats.prank(defaultOperator); +// cheats.expectRevert(BitmapUtils.BitmapValueTooLarge.selector); +// registryCoordinator.updateOperatorsForQuorum(operatorsToUpdate, quorumNumbersNotCreated); +// } + +// function test_updateOperatorsForQuorum_revert_inputLengthMismatch() public { +// address[][] memory operatorsToUpdate = new address[][](2); +// bytes memory quorumNumbers = new bytes(1); +// quorumNumbers[0] = bytes1(defaultQuorumNumber); + +// cheats.expectRevert(bytes4(keccak256("InputLengthMismatch()"))); +// registryCoordinator.updateOperatorsForQuorum(operatorsToUpdate, quorumNumbers); +// } + +// function test_updateOperatorsForQuorum_revert_incorrectNumberOfOperators() public { +// address[][] memory operatorsToUpdate = new address[][](1); +// address[] memory operatorArray = new address[](1); +// operatorArray[0] = defaultOperator; +// operatorsToUpdate[0] = operatorArray; +// bytes memory quorumNumbers = new bytes(1); +// quorumNumbers[0] = bytes1(defaultQuorumNumber); + +// cheats.expectRevert(bytes4(keccak256("QuorumOperatorCountMismatch()"))); +// registryCoordinator.updateOperatorsForQuorum(operatorsToUpdate, quorumNumbers); +// } + +// function test_updateOperatorsForQuorum_revert_unregisteredOperator() public { +// // register the default operator +// ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; +// uint32 registrationBlockNumber = 100; +// bytes memory quorumNumbers = new bytes(1); +// quorumNumbers[0] = bytes1(defaultQuorumNumber); +// _setOperatorWeight(defaultOperator, uint8(quorumNumbers[0]), defaultStake); +// cheats.startPrank(defaultOperator); +// cheats.roll(registrationBlockNumber); +// registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); + +// address[][] memory operatorsToUpdate = new address[][](1); +// address[] memory operatorArray = new address[](1); +// // use an unregistered operator address as input +// operatorArray[0] = _incrementAddress(defaultOperator, 1); +// operatorsToUpdate[0] = operatorArray; + +// cheats.expectRevert(bytes4(keccak256("NotRegisteredForQuorum()"))); +// registryCoordinator.updateOperatorsForQuorum(operatorsToUpdate, quorumNumbers); +// } + +// // note: there is not an explicit check for duplicates, as checking for explicit ordering covers this +// function test_updateOperatorsForQuorum_revert_duplicateOperator(uint256 pseudoRandomNumber) public { +// // register 2 operators +// uint32 numOperators = 2; +// uint32 registrationBlockNumber = 200; +// bytes memory quorumNumbers = new bytes(1); +// quorumNumbers[0] = bytes1(defaultQuorumNumber); +// uint256 quorumBitmap = BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers); +// cheats.roll(registrationBlockNumber); +// for (uint i = 0; i < numOperators; i++) { +// BN254.G1Point memory pubKey = BN254.hashToG1(keccak256(abi.encodePacked(pseudoRandomNumber, i))); +// address operator = _incrementAddress(defaultOperator, i); + +// _registerOperatorWithCoordinator(operator, quorumBitmap, pubKey); +// } + +// address[][] memory operatorsToUpdate = new address[][](1); +// address[] memory operatorArray = new address[](2); +// // use the same operator address twice as input +// operatorArray[0] = defaultOperator; +// operatorArray[1] = defaultOperator; +// operatorsToUpdate[0] = operatorArray; + +// // note: there is not an explicit check for duplicates, as checking for explicit ordering covers this +// cheats.expectRevert(bytes4(keccak256("NotSorted()"))); +// registryCoordinator.updateOperatorsForQuorum(operatorsToUpdate, quorumNumbers); +// } + +// function test_updateOperatorsForQuorum_revert_incorrectListOrder(uint256 pseudoRandomNumber) public { +// // register 2 operators +// uint32 numOperators = 2; +// uint32 registrationBlockNumber = 200; +// bytes memory quorumNumbers = new bytes(1); +// quorumNumbers[0] = bytes1(defaultQuorumNumber); +// uint256 quorumBitmap = BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers); +// cheats.roll(registrationBlockNumber); +// for (uint i = 0; i < numOperators; i++) { +// BN254.G1Point memory pubKey = BN254.hashToG1(keccak256(abi.encodePacked(pseudoRandomNumber, i))); +// address operator = _incrementAddress(defaultOperator, i); + +// _registerOperatorWithCoordinator(operator, quorumBitmap, pubKey); +// } + +// address[][] memory operatorsToUpdate = new address[][](1); +// address[] memory operatorArray = new address[](2); +// // order the operator addresses in descending order, instead of ascending order +// operatorArray[0] = _incrementAddress(defaultOperator, 1); +// operatorArray[1] = defaultOperator; +// operatorsToUpdate[0] = operatorArray; + +// cheats.expectRevert(bytes4(keccak256("NotSorted()"))); +// registryCoordinator.updateOperatorsForQuorum(operatorsToUpdate, quorumNumbers); +// } + +// function test_updateOperatorsForQuorum_singleOperator() public { +// // register the default operator +// ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; +// uint32 registrationBlockNumber = 100; +// bytes memory quorumNumbers = new bytes(1); +// quorumNumbers[0] = bytes1(defaultQuorumNumber); +// _setOperatorWeight(defaultOperator, uint8(quorumNumbers[0]), defaultStake); +// cheats.startPrank(defaultOperator); +// cheats.roll(registrationBlockNumber); +// registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); + +// address[][] memory operatorsToUpdate = new address[][](1); +// address[] memory operatorArray = new address[](1); +// operatorArray[0] = defaultOperator; +// operatorsToUpdate[0] = operatorArray; + +// uint256 quorumUpdateBlockNumberBefore = registryCoordinator.quorumUpdateBlockNumber(defaultQuorumNumber); +// require(quorumUpdateBlockNumberBefore != block.number, "bad test setup!"); + +// cheats.expectEmit(true, true, true, true, address(registryCoordinator)); +// emit QuorumBlockNumberUpdated(defaultQuorumNumber, block.number); +// registryCoordinator.updateOperatorsForQuorum(operatorsToUpdate, quorumNumbers); + +// uint256 quorumUpdateBlockNumberAfter = registryCoordinator.quorumUpdateBlockNumber(defaultQuorumNumber); +// assertEq(quorumUpdateBlockNumberAfter, block.number, "quorumUpdateBlockNumber not set correctly"); +// } + +// function test_updateOperatorsForQuorum_twoOperators(uint256 pseudoRandomNumber) public { +// // register 2 operators +// uint32 numOperators = 2; +// uint32 registrationBlockNumber = 200; +// bytes memory quorumNumbers = new bytes(1); +// quorumNumbers[0] = bytes1(defaultQuorumNumber); +// uint256 quorumBitmap = BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers); +// cheats.roll(registrationBlockNumber); +// for (uint i = 0; i < numOperators; i++) { +// BN254.G1Point memory pubKey = BN254.hashToG1(keccak256(abi.encodePacked(pseudoRandomNumber, i))); +// address operator = _incrementAddress(defaultOperator, i); + +// _registerOperatorWithCoordinator(operator, quorumBitmap, pubKey); +// } + +// address[][] memory operatorsToUpdate = new address[][](1); +// address[] memory operatorArray = new address[](2); +// // order the operator addresses in descending order, instead of ascending order +// operatorArray[0] = defaultOperator; +// operatorArray[1] = _incrementAddress(defaultOperator, 1); +// operatorsToUpdate[0] = operatorArray; + +// uint256 quorumUpdateBlockNumberBefore = registryCoordinator.quorumUpdateBlockNumber(defaultQuorumNumber); +// require(quorumUpdateBlockNumberBefore != block.number, "bad test setup!"); + +// cheats.expectEmit(true, true, true, true, address(registryCoordinator)); +// emit QuorumBlockNumberUpdated(defaultQuorumNumber, block.number); +// registryCoordinator.updateOperatorsForQuorum(operatorsToUpdate, quorumNumbers); + +// uint256 quorumUpdateBlockNumberAfter = registryCoordinator.quorumUpdateBlockNumber(defaultQuorumNumber); +// assertEq(quorumUpdateBlockNumberAfter, block.number, "quorumUpdateBlockNumber not set correctly"); +// } + +// // @notice tests that the internal `_updateOperatorBitmap` function works as expected, for fuzzed inputs +// function testFuzz_updateOperatorBitmapInternal_noPreviousEntries(uint192 newBitmap) public { +// registryCoordinator._updateOperatorBitmapExternal(defaultOperatorId, newBitmap); +// assertEq( +// keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0))), +// keccak256(abi.encode(ISlashingRegistryCoordinatorTypes.QuorumBitmapUpdate({ +// quorumBitmap: uint192(newBitmap), +// updateBlockNumber: uint32(block.number), +// nextUpdateBlockNumber: 0 +// }))) +// ); +// } + +// // @notice tests that the internal `_updateOperatorBitmap` function works as expected, for fuzzed inputs +// function testFuzz_updateOperatorBitmapInternal_previousEntryInCurrentBlock(uint192 newBitmap) public { +// uint192 pastBitmap = 1; +// testFuzz_updateOperatorBitmapInternal_noPreviousEntries(pastBitmap); + +// registryCoordinator._updateOperatorBitmapExternal(defaultOperatorId, newBitmap); +// assertEq( +// keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0))), +// keccak256(abi.encode(ISlashingRegistryCoordinatorTypes.QuorumBitmapUpdate({ +// quorumBitmap: uint192(newBitmap), +// updateBlockNumber: uint32(block.number), +// nextUpdateBlockNumber: 0 +// }))) +// ); +// } + +// // @notice tests that the internal `_updateOperatorBitmap` function works as expected, for fuzzed inputs +// function testFuzz_updateOperatorBitmapInternal_previousEntryInPastBlock(uint192 newBitmap) public { +// uint192 pastBitmap = 1; +// testFuzz_updateOperatorBitmapInternal_noPreviousEntries(pastBitmap); + +// // advance the block number +// uint256 previousBlockNumber = block.number; +// cheats.roll(previousBlockNumber + 1); + +// registryCoordinator._updateOperatorBitmapExternal(defaultOperatorId, newBitmap); +// assertEq( +// keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0))), +// keccak256(abi.encode(ISlashingRegistryCoordinatorTypes.QuorumBitmapUpdate({ +// quorumBitmap: uint192(pastBitmap), +// updateBlockNumber: uint32(previousBlockNumber), +// nextUpdateBlockNumber: uint32(block.number) +// }))) +// ); +// assertEq( +// keccak256(abi.encode(registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 1))), +// keccak256(abi.encode(ISlashingRegistryCoordinatorTypes.QuorumBitmapUpdate({ +// quorumBitmap: uint192(newBitmap), +// updateBlockNumber: uint32(block.number), +// nextUpdateBlockNumber: 0 +// }))) +// ); +// } +// } + +// contract RegistryCoordinatorUnitTests_BeforeMigration is RegistryCoordinatorUnitTests { +// function test_registerALMHook_Reverts() public { +// cheats.prank(address(registryCoordinator.allocationManager())); +// cheats.expectRevert(); +// registryCoordinator.registerOperator(defaultOperator, new uint32[](0), abi.encode(defaultSocket, pubkeyRegistrationParams)); +// } + +// function test_deregisterALMHook_Reverts() public { +// uint32[] memory operatorSetIds = new uint32[](1); +// operatorSetIds[0] = 0; +// cheats.prank(address(registryCoordinator.allocationManager())); +// cheats.expectRevert(); +// registryCoordinator.deregisterOperator(defaultOperator, operatorSetIds); +// } + +// function test_CreateTotalDelegatedStakeQuorum() public { +// _deployMockEigenLayerAndAVS(0); +// // Set up test params +// ISlashingRegistryCoordinatorTypes.OperatorSetParam memory operatorSetParams = ISlashingRegistryCoordinatorTypes.OperatorSetParam({ +// maxOperatorCount: 10, +// kickBIPsOfOperatorStake: 0, +// kickBIPsOfTotalStake: 0 +// }); +// uint96 minimumStake = 100; +// IStakeRegistryTypes.StrategyParams[] memory strategyParams = new IStakeRegistryTypes.StrategyParams[](1); +// strategyParams[0] = IStakeRegistryTypes.StrategyParams({ +// strategy: IStrategy(address(0x1)), +// multiplier: 1000 +// }); + +// // Get initial quorum count +// uint8 initialQuorumCount = registryCoordinator.quorumCount(); + +// // Create quorum with total delegated stake type +// cheats.prank(registryCoordinatorOwner); +// registryCoordinator.createTotalDelegatedStakeQuorum( +// operatorSetParams, +// minimumStake, +// strategyParams +// ); + +// // Verify quorum was created +// assertEq(registryCoordinator.quorumCount(), initialQuorumCount + 1); + +// // Verify quorum params were set correctly +// ISlashingRegistryCoordinatorTypes.OperatorSetParam memory storedParams = registryCoordinator.getOperatorSetParams(initialQuorumCount); +// assertEq(storedParams.maxOperatorCount, operatorSetParams.maxOperatorCount); +// assertEq(storedParams.kickBIPsOfOperatorStake, operatorSetParams.kickBIPsOfOperatorStake); +// assertEq(storedParams.kickBIPsOfTotalStake, operatorSetParams.kickBIPsOfTotalStake); +// } + +// function test_CreateSlashableStakeQuorum_Reverts() public { +// _deployMockEigenLayerAndAVS(0); +// ISlashingRegistryCoordinatorTypes.OperatorSetParam memory operatorSetParams = ISlashingRegistryCoordinatorTypes.OperatorSetParam({ +// maxOperatorCount: 10, +// kickBIPsOfOperatorStake: 0, +// kickBIPsOfTotalStake: 0 +// }); +// uint96 minimumStake = 100; +// IStakeRegistryTypes.StrategyParams[] memory strategyParams = new IStakeRegistryTypes.StrategyParams[](1); +// strategyParams[0] = IStakeRegistryTypes.StrategyParams({ +// strategy: IStrategy(address(0x1)), +// multiplier: 1000 +// }); +// uint32 lookAheadPeriod = 100; + +// // Attempt to create quorum with slashable stake type before enabling operator sets +// cheats.prank(registryCoordinatorOwner); +// cheats.expectRevert(); +// registryCoordinator.createSlashableStakeQuorum( +// operatorSetParams, +// minimumStake, +// strategyParams, +// lookAheadPeriod +// ); +// } + +// function test_MigrateToOperatorSets() public { +// _deployMockEigenLayerAndAVS(0); +// cheats.prank(registryCoordinatorOwner); +// registryCoordinator.enableOperatorSets(); +// assertTrue(registryCoordinator.operatorSetsEnabled()); +// } +// } + +// contract RegistryCoordinatorUnitTests_AfterMigration is RegistryCoordinatorUnitTests { +// function test_MigrateToOperatorSets() public { +// cheats.prank(registryCoordinatorOwner); +// registryCoordinator.enableOperatorSets(); +// assertTrue(registryCoordinator.operatorSetsEnabled()); +// } + +// function test_M2_Deregister() public { +// vm.skip(true); +// /// Create 2 M2 quorums +// _deployMockEigenLayerAndAVS(2); + +// address operatorToRegister = address(420); + +// ISignatureUtils.SignatureWithSaltAndExpiry memory emptySignature = ISignatureUtils.SignatureWithSaltAndExpiry({ +// signature: new bytes(0), +// salt: bytes32(0), +// expiry: 0 +// }); + +// IBLSApkRegistryTypes.PubkeyRegistrationParams memory operatorRegisterApkParams = IBLSApkRegistryTypes.PubkeyRegistrationParams({ +// pubkeyRegistrationSignature: BN254.G1Point({ +// X: 0, +// Y: 0 +// }), +// pubkeyG1: BN254.G1Point({ +// X: 0, +// Y: 0 +// }), +// pubkeyG2: BN254.G2Point({ +// X: [uint256(0), uint256(0)], +// Y: [uint256(0), uint256(0)] +// }) +// }); + +// string memory socket = "socket"; + +// // register for quorum 0 +// vm.prank(operatorToRegister); +// registryCoordinator.registerOperator( +// new bytes(1), // Convert 0 to bytes1 first +// socket, +// operatorRegisterApkParams, +// emptySignature +// ); + +// /// migrate to operator sets +// registryCoordinator.enableOperatorSets(); + +// /// Deregistration for m2 should for the first two operator sets +// vm.prank(defaultOperator); +// registryCoordinator.deregisterOperator(new bytes(1)); + +// // Verify operator was deregistered by checking their bitmap is empty +// bytes32 operatorId = registryCoordinator.getOperatorId(operatorToRegister); +// uint192 bitmap = registryCoordinator.getCurrentQuorumBitmap(operatorId); +// assertEq(bitmap, 0, "Operator bitmap should be empty after deregistration"); + +// // Verify operator status is NEVER_REGISTERED +// ISlashingRegistryCoordinator.OperatorStatus status = registryCoordinator.getOperatorStatus(operatorToRegister); +// assertEq(uint8(status), uint8(ISlashingRegistryCoordinator.OperatorStatus.NEVER_REGISTERED), "Operator status should be NEVER_REGISTERED"); +// } + +// function test_M2_Register_Reverts() public { +// cheats.prank(registryCoordinatorOwner); +// registryCoordinator.enableOperatorSets(); + +// bytes memory quorumNumbers = new bytes(1); +// quorumNumbers[0] = bytes1(uint8(0)); +// IBLSApkRegistryTypes.PubkeyRegistrationParams memory params; +// ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature; + +// cheats.expectRevert(); +// registryCoordinator.registerOperator( +// quorumNumbers, +// defaultSocket, +// params, +// operatorSignature +// ); +// } + +// function test_createSlashableStakeQuorum() public { +// // Deploy with 0 quorums +// _deployMockEigenLayerAndAVS(0); + +// // Enable operator sets first +// cheats.prank(registryCoordinatorOwner); +// registryCoordinator.enableOperatorSets(); + +// // Create quorum params +// ISlashingRegistryCoordinatorTypes.OperatorSetParam memory operatorSetParams = ISlashingRegistryCoordinatorTypes.OperatorSetParam({ +// maxOperatorCount: 10, +// kickBIPsOfOperatorStake: 1000, +// kickBIPsOfTotalStake: 100 +// }); +// uint96 minimumStake = 100; +// IStakeRegistryTypes.StrategyParams[] memory strategyParams = new IStakeRegistryTypes.StrategyParams[](1); +// strategyParams[0] = IStakeRegistryTypes.StrategyParams({ +// strategy: IStrategy(address(1)), +// multiplier: 1 +// }); +// uint32 lookAheadPeriod = 100; + +// // Create slashable stake quorum +// cheats.prank(registryCoordinatorOwner); +// registryCoordinator.createSlashableStakeQuorum( +// operatorSetParams, +// minimumStake, +// strategyParams, +// lookAheadPeriod +// ); +// } + +// function test_createTotalDelegatedStakeQuorum() public { +// // Deploy with 0 quorums +// _deployMockEigenLayerAndAVS(0); + +// // Enable operator sets first +// cheats.prank(registryCoordinatorOwner); +// registryCoordinator.enableOperatorSets(); + +// // Create quorum params +// ISlashingRegistryCoordinatorTypes.OperatorSetParam memory operatorSetParams = ISlashingRegistryCoordinatorTypes.OperatorSetParam({ +// maxOperatorCount: 10, +// kickBIPsOfOperatorStake: 1000, +// kickBIPsOfTotalStake: 100 +// }); +// uint96 minimumStake = 100; +// IStakeRegistryTypes.StrategyParams[] memory strategyParams = new IStakeRegistryTypes.StrategyParams[](1); +// strategyParams[0] = IStakeRegistryTypes.StrategyParams({ +// strategy: IStrategy(address(1)), +// multiplier: 10000 +// }); + +// // Create total delegated stake quorum +// cheats.prank(registryCoordinatorOwner); +// registryCoordinator.createTotalDelegatedStakeQuorum( +// operatorSetParams, +// minimumStake, +// strategyParams +// ); +// } + +// function test_registerHook() public { +// vm.skip(true); + +// _deployMockEigenLayerAndAVS(0); +// // Enable operator sets first +// cheats.prank(registryCoordinatorOwner); +// registryCoordinator.enableOperatorSets(); + +// // Create quorum params +// ISlashingRegistryCoordinatorTypes.OperatorSetParam memory operatorSetParams = ISlashingRegistryCoordinatorTypes.OperatorSetParam({ +// maxOperatorCount: 10, +// kickBIPsOfOperatorStake: 1000, +// kickBIPsOfTotalStake: 100 +// }); + +// uint96 minimumStake = 100; +// IStakeRegistryTypes.StrategyParams[] memory strategyParams = new IStakeRegistryTypes.StrategyParams[](1); +// strategyParams[0] = IStakeRegistryTypes.StrategyParams({ +// strategy: IStrategy(address(1)), +// multiplier: 10000 +// }); + +// // Create total delegated stake quorum +// cheats.prank(registryCoordinatorOwner); +// registryCoordinator.createTotalDelegatedStakeQuorum( +// operatorSetParams, +// 0, +// strategyParams +// ); + +// uint32[] memory operatorSetIds = new uint32[](1); +// operatorSetIds[0] = 0; + +// string memory socket = "socket"; +// IBLSApkRegistryTypes.PubkeyRegistrationParams memory params; +// // TODO: +// // params = IBLSApkRegistryTypes.PubkeyRegistrationParams({ +// // pubkeyG1: defaultPubKey, +// // pubkeyG2: defaultPubKeyG2, +// // pubkeySignature: defaultPubKeySignature +// // }); + +// bytes memory data = abi.encode(socket, params); + +// cheats.prank(address(registryCoordinator.allocationManager())); +// registryCoordinator.registerOperator(defaultOperator, operatorSetIds, data); +// } + +// function test_registerHook_WithChurn() public { +// _deployMockEigenLayerAndAVS(0); +// // Enable operator sets first +// cheats.prank(registryCoordinatorOwner); +// registryCoordinator.enableOperatorSets(); + +// // Create quorum params +// ISlashingRegistryCoordinatorTypes.OperatorSetParam memory operatorSetParams = ISlashingRegistryCoordinatorTypes.OperatorSetParam({ +// maxOperatorCount: 10, +// kickBIPsOfOperatorStake: 1000, +// kickBIPsOfTotalStake: 100 +// }); + +// uint96 minimumStake = 100; +// IStakeRegistryTypes.StrategyParams[] memory strategyParams = new IStakeRegistryTypes.StrategyParams[](1); +// strategyParams[0] = IStakeRegistryTypes.StrategyParams({ +// strategy: IStrategy(address(1)), +// multiplier: 10000 +// }); + +// // Create total delegated stake quorum +// cheats.prank(registryCoordinatorOwner); +// registryCoordinator.createTotalDelegatedStakeQuorum( +// operatorSetParams, +// 0, +// strategyParams +// ); + +// uint32[] memory operatorSetIds = new uint32[](1); +// operatorSetIds[0] = 0; + +// string memory socket = "socket"; +// IBLSApkRegistryTypes.PubkeyRegistrationParams memory params; +// // TODO: +// // params = IBLSApkRegistryTypes.PubkeyRegistrationParams({ +// // pubkeyG1: defaultPubKey, +// // pubkeyG2: defaultPubKeyG2, +// // pubkeySignature: defaultPubKeySignature +// // }); + +// ISlashingRegistryCoordinator.OperatorKickParam[] memory operatorKickParams = new ISlashingRegistryCoordinator.OperatorKickParam[](1); +// operatorKickParams[0] = ISlashingRegistryCoordinator.OperatorKickParam({ +// operator: address(0x1), +// quorumNumber: 0 +// }); + +// ISignatureUtils.SignatureWithSaltAndExpiry memory churnApproverSignature; +// ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature; + +// bytes memory registerParams = abi.encode( +// socket, +// params, +// operatorKickParams, +// churnApproverSignature, +// operatorSignature +// ); + +// // Prank as allocation manager and call register hook +// cheats.prank(address(registryCoordinator.allocationManager())); +// registryCoordinator.registerOperator(defaultOperator, operatorSetIds, registerParams); +// } + +// function test_updateStakesForQuorum() public { +// vm.skip(true); +// _deployMockEigenLayerAndAVS(0); + +// ISlashingRegistryCoordinatorTypes.OperatorSetParam memory operatorSetParams = ISlashingRegistryCoordinatorTypes.OperatorSetParam({ +// maxOperatorCount: defaultMaxOperatorCount, +// kickBIPsOfOperatorStake: defaultKickBIPsOfOperatorStake, +// kickBIPsOfTotalStake: defaultKickBIPsOfTotalStake +// }); + +// uint96 minimumStake = 100; +// IStakeRegistryTypes.StrategyParams[] memory strategyParams = new IStakeRegistryTypes.StrategyParams[](1); +// strategyParams[0] = IStakeRegistryTypes.StrategyParams({ +// strategy: IStrategy(address(1)), +// multiplier: 10000 +// }); + +// cheats.prank(registryCoordinatorOwner); +// registryCoordinator.createTotalDelegatedStakeQuorum( +// operatorSetParams, +// minimumStake, +// strategyParams +// ); + +// uint256 quorumBitmap = 0; + +// // TODO: register actually and update stakes +// } + +// function test_deregisterHook() public { + +// _deployMockEigenLayerAndAVS(0); +// // Enable operator sets first +// cheats.prank(registryCoordinatorOwner); +// registryCoordinator.enableOperatorSets(); + +// // Create quorum params +// ISlashingRegistryCoordinatorTypes.OperatorSetParam memory operatorSetParams = ISlashingRegistryCoordinatorTypes.OperatorSetParam({ +// maxOperatorCount: 10, +// kickBIPsOfOperatorStake: 1000, +// kickBIPsOfTotalStake: 100 +// }); + +// uint96 minimumStake = 100; +// IStakeRegistryTypes.StrategyParams[] memory strategyParams = new IStakeRegistryTypes.StrategyParams[](1); +// strategyParams[0] = IStakeRegistryTypes.StrategyParams({ +// strategy: IStrategy(address(1)), +// multiplier: 10000 +// }); + +// // Create total delegated stake quorum +// cheats.prank(registryCoordinatorOwner); +// registryCoordinator.createTotalDelegatedStakeQuorum( +// operatorSetParams, +// 0, +// strategyParams +// ); + +// // Prank as allocation manager and call register hook +// uint32[] memory operatorSetIds = new uint32[](1); +// operatorSetIds[0] = 0; + +// string memory socket = "socket"; +// IBLSApkRegistryTypes.PubkeyRegistrationParams memory params; +// // TODO: +// // params = IBLSApkRegistryTypes.PubkeyRegistrationParams({ +// // pubkeyG1: defaultPubKey, +// // pubkeyG2: defaultPubKeyG2, +// // pubkeySignature: defaultPubKeySignature +// // }); + +// bytes memory data = abi.encode(socket, params); + +// cheats.startPrank(address(registryCoordinator.allocationManager())); +// registryCoordinator.registerOperator(defaultOperator, operatorSetIds, data); + +// registryCoordinator.deregisterOperator(defaultOperator, operatorSetIds); + +// cheats.stopPrank(); +// } + +// function test_registerHook_Reverts_WhenNotALM() public { + +// _deployMockEigenLayerAndAVS(0); +// // Enable operator sets first +// cheats.prank(registryCoordinatorOwner); +// registryCoordinator.enableOperatorSets(); + +// // Create quorum params +// ISlashingRegistryCoordinatorTypes.OperatorSetParam memory operatorSetParams = ISlashingRegistryCoordinatorTypes.OperatorSetParam({ +// maxOperatorCount: 10, +// kickBIPsOfOperatorStake: 1000, +// kickBIPsOfTotalStake: 100 +// }); + +// uint96 minimumStake = 100; +// IStakeRegistryTypes.StrategyParams[] memory strategyParams = new IStakeRegistryTypes.StrategyParams[](1); +// strategyParams[0] = IStakeRegistryTypes.StrategyParams({ +// strategy: IStrategy(address(1)), +// multiplier: 10000 +// }); + +// // Create total delegated stake quorum +// cheats.prank(registryCoordinatorOwner); +// registryCoordinator.createTotalDelegatedStakeQuorum( +// operatorSetParams, +// 0, +// strategyParams +// ); + +// uint32[] memory operatorSetIds = new uint32[](1); +// operatorSetIds[0] = 0; + +// string memory socket = "socket"; +// IBLSApkRegistryTypes.PubkeyRegistrationParams memory params; +// // TODO: +// // params = IBLSApkRegistryTypes.PubkeyRegistrationParams({ +// // pubkeyG1: defaultPubKey, +// // pubkeyG2: defaultPubKeyG2, +// // pubkeySignature: defaultPubKeySignature +// // }); + +// bytes memory data = abi.encode(socket, params); + +// vm.expectRevert(); +// registryCoordinator.registerOperator(defaultOperator, operatorSetIds, data); +// } + +// function test_deregisterHook_Reverts_WhenNotALM() public { + +// _deployMockEigenLayerAndAVS(0); +// // Enable operator sets first +// cheats.prank(registryCoordinatorOwner); +// registryCoordinator.enableOperatorSets(); + +// // Create quorum params +// ISlashingRegistryCoordinatorTypes.OperatorSetParam memory operatorSetParams = ISlashingRegistryCoordinatorTypes.OperatorSetParam({ +// maxOperatorCount: 10, +// kickBIPsOfOperatorStake: 1000, +// kickBIPsOfTotalStake: 100 +// }); + +// uint96 minimumStake = 100; +// IStakeRegistryTypes.StrategyParams[] memory strategyParams = new IStakeRegistryTypes.StrategyParams[](1); +// strategyParams[0] = IStakeRegistryTypes.StrategyParams({ +// strategy: IStrategy(address(1)), +// multiplier: 10000 +// }); + +// // Create total delegated stake quorum +// cheats.prank(registryCoordinatorOwner); +// registryCoordinator.createTotalDelegatedStakeQuorum( +// operatorSetParams, +// 0, +// strategyParams +// ); + +// // Prank as allocation manager and call register hook +// uint32[] memory operatorSetIds = new uint32[](1); +// operatorSetIds[0] = 0; + +// string memory socket = "socket"; +// IBLSApkRegistryTypes.PubkeyRegistrationParams memory params; +// // TODO: +// // params = IBLSApkRegistryTypes.PubkeyRegistrationParams({ +// // pubkeyG1: defaultPubKey, +// // pubkeyG2: defaultPubKeyG2, +// // pubkeySignature: defaultPubKeySignature +// // }); + +// bytes memory data = abi.encode(socket, params); + +// cheats.prank(address(registryCoordinator.allocationManager())); +// registryCoordinator.registerOperator(defaultOperator, operatorSetIds, data); +// cheats.stopPrank(); + +// cheats.expectRevert(); +// registryCoordinator.deregisterOperator(defaultOperator, operatorSetIds); + +// } + +// function test_DeregisterHook_Reverts_WhenM2Quorum() public { +// vm.skip(true); +// } + +// function test_registerHook_Reverts_WhenM2Quorum() public { +// vm.skip(true); +// } + +// } diff --git a/test/unit/SocketRegistryUnit.t.sol b/test/unit/SocketRegistryUnit.t.sol index af5be131..834b3977 100644 --- a/test/unit/SocketRegistryUnit.t.sol +++ b/test/unit/SocketRegistryUnit.t.sol @@ -7,8 +7,7 @@ import {IRegistryCoordinator} from "../../src/interfaces/IRegistryCoordinator.so import "../utils/MockAVSDeployer.sol"; contract SocketRegistryUnitTests is MockAVSDeployer { - - function setUp() virtual public { + function setUp() public virtual { _deployMockEigenLayerAndAVS(); } @@ -20,8 +19,9 @@ contract SocketRegistryUnitTests is MockAVSDeployer { function test_setOperatorSocket_revert_notRegistryCoordinator() public { vm.startPrank(address(0)); - vm.expectRevert("SocketRegistry.onlyRegistryCoordinator: caller is not the RegistryCoordinator"); + vm.expectRevert( + "SocketRegistry.onlyRegistryCoordinator: caller is not the RegistryCoordinator" + ); socketRegistry.setOperatorSocket(defaultOperatorId, "testSocket"); } - -} \ No newline at end of file +} diff --git a/test/unit/StakeRegistryUnit.t.sol b/test/unit/StakeRegistryUnit.t.sol index 52769f78..34399fc9 100644 --- a/test/unit/StakeRegistryUnit.t.sol +++ b/test/unit/StakeRegistryUnit.t.sol @@ -1,13 +1,14 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; import "test/utils/MockAVSDeployer.sol"; import {StakeRegistry} from "src/StakeRegistry.sol"; -import {IStakeRegistry} from "src/interfaces/IStakeRegistry.sol"; +import {IStakeRegistry, IStakeRegistryErrors} from "src/interfaces/IStakeRegistry.sol"; import {IStakeRegistryEvents} from "test/events/IStakeRegistryEvents.sol"; +import {ISocketRegistry} from "src/interfaces/ISocketRegistry.sol"; import "../utils/MockAVSDeployer.sol"; @@ -33,7 +34,9 @@ contract StakeRegistryUnitTests is MockAVSDeployer, IStakeRegistryEvents { uint256 gasUsed; - modifier fuzzOnlyInitializedQuorums(uint8 quorumNumber) { + modifier fuzzOnlyInitializedQuorums( + uint8 quorumNumber + ) { cheats.assume(initializedQuorumBitmap.isSet(quorumNumber)); _; } @@ -49,11 +52,16 @@ contract StakeRegistryUnitTests is MockAVSDeployer, IStakeRegistryEvents { stakeRegistry, IBLSApkRegistry(blsApkRegistry), IIndexRegistry(indexRegistry), - socketRegistry + ISocketRegistry(socketRegistry), + allocationManager, + pauserRegistry ); stakeRegistryImplementation = new StakeRegistryHarness( - IRegistryCoordinator(address(registryCoordinator)), delegationMock + ISlashingRegistryCoordinator(address(registryCoordinator)), + delegationMock, + avsDirectoryMock, + allocationManager ); stakeRegistry = StakeRegistryHarness( @@ -87,12 +95,14 @@ contract StakeRegistryUnitTests is MockAVSDeployer, IStakeRegistryEvents { * @dev Initialize a new quorum with `minimumStake` * The new quorum's number is sequential, starting with `nextQuorum` */ - function _initializeQuorum(uint96 minimumStake) internal { + function _initializeQuorum( + uint96 minimumStake + ) internal { uint8 quorumNumber = nextQuorum; - IStakeRegistry.StrategyParams[] memory strategyParams = - new IStakeRegistry.StrategyParams[](1); - strategyParams[0] = IStakeRegistry.StrategyParams( + IStakeRegistryTypes.StrategyParams[] memory strategyParams = + new IStakeRegistryTypes.StrategyParams[](1); + strategyParams[0] = IStakeRegistryTypes.StrategyParams( IStrategy(address(uint160(uint256(keccak256(abi.encodePacked(quorumNumber)))))), uint96(WEIGHTING_DIVISOR) ); @@ -100,7 +110,14 @@ contract StakeRegistryUnitTests is MockAVSDeployer, IStakeRegistryEvents { nextQuorum++; cheats.prank(address(registryCoordinator)); - stakeRegistry.initializeQuorum(quorumNumber, minimumStake, strategyParams); + stakeRegistry.initializeDelegatedStakeQuorum(quorumNumber, minimumStake, strategyParams); + + IStakeRegistryTypes.StakeType stakeType = stakeRegistry.stakeTypePerQuorum(quorumNumber); + assertEq( + uint8(stakeType), + uint8(IStakeRegistryTypes.StakeType.TOTAL_DELEGATED), + "invalid stake type" + ); // Mark quorum initialized for other tests initializedQuorumBitmap = uint192(initializedQuorumBitmap.setBit(quorumNumber)); @@ -115,10 +132,10 @@ contract StakeRegistryUnitTests is MockAVSDeployer, IStakeRegistryEvents { function _initializeQuorum(uint96 minimumStake, uint256 numStrats) internal returns (uint8) { uint8 quorumNumber = nextQuorum; - IStakeRegistry.StrategyParams[] memory strategyParams = - new IStakeRegistry.StrategyParams[](numStrats); + IStakeRegistryTypes.StrategyParams[] memory strategyParams = + new IStakeRegistryTypes.StrategyParams[](numStrats); for (uint256 i = 0; i < strategyParams.length; i++) { - strategyParams[i] = IStakeRegistry.StrategyParams( + strategyParams[i] = IStakeRegistryTypes.StrategyParams( IStrategy(address(uint160(uint256(keccak256(abi.encodePacked(quorumNumber, i)))))), uint96(WEIGHTING_DIVISOR) ); @@ -127,7 +144,7 @@ contract StakeRegistryUnitTests is MockAVSDeployer, IStakeRegistryEvents { nextQuorum++; cheats.prank(address(registryCoordinator)); - stakeRegistry.initializeQuorum(quorumNumber, minimumStake, strategyParams); + stakeRegistry.initializeDelegatedStakeQuorum(quorumNumber, minimumStake, strategyParams); // Mark quorum initialized for other tests initializedQuorumBitmap = uint192(initializedQuorumBitmap.setBit(quorumNumber)); @@ -385,7 +402,9 @@ contract StakeRegistryUnitTests is MockAVSDeployer, IStakeRegistryEvents { /// @notice Given a fuzzed bitmap input, returns a bitmap and array of quorum numbers /// that are guaranteed to be initialized. - function _fuzz_getQuorums(uint192 fuzzy_Bitmap) internal view returns (uint192, bytes memory) { + function _fuzz_getQuorums( + uint192 fuzzy_Bitmap + ) internal view returns (uint192, bytes memory) { fuzzy_Bitmap &= initializedQuorumBitmap; cheats.assume(!fuzzy_Bitmap.isEmpty()); @@ -395,7 +414,9 @@ contract StakeRegistryUnitTests is MockAVSDeployer, IStakeRegistryEvents { /// @notice Returns a list of initialized quorums ending in a non-initialized quorum /// @param rand is used to determine how many legitimate quorums to insert, so we can /// check this works for lists of varying lengths - function _fuzz_getInvalidQuorums(bytes32 rand) internal returns (bytes memory) { + function _fuzz_getInvalidQuorums( + bytes32 rand + ) internal returns (bytes memory) { uint256 length = _randUint({rand: rand, min: 1, max: initializedQuorumBytes.length + 1}); bytes memory invalidQuorums = new bytes(length); @@ -427,11 +448,9 @@ contract StakeRegistryUnitTests is MockAVSDeployer, IStakeRegistryEvents { } /// @dev Return the minimum stakes required for a list of quorums - function _getMinimumStakes(bytes memory quorumNumbers) - internal - view - returns (uint96[] memory) - { + function _getMinimumStakes( + bytes memory quorumNumbers + ) internal view returns (uint96[] memory) { uint96[] memory minimumStakes = new uint96[](quorumNumbers.length); for (uint256 i = 0; i < quorumNumbers.length; i++) { @@ -459,11 +478,9 @@ contract StakeRegistryUnitTests is MockAVSDeployer, IStakeRegistryEvents { } /// @dev Return the most recent total stake update history entries - function _getLatestTotalStakeUpdates(bytes memory quorumNumbers) - internal - view - returns (IStakeRegistry.StakeUpdate[] memory) - { + function _getLatestTotalStakeUpdates( + bytes memory quorumNumbers + ) internal view returns (IStakeRegistry.StakeUpdate[] memory) { IStakeRegistry.StakeUpdate[] memory stakeUpdates = new IStakeRegistry.StakeUpdate[](quorumNumbers.length); @@ -496,11 +513,9 @@ contract StakeRegistryUnitTests is MockAVSDeployer, IStakeRegistryEvents { } /// @dev Return the lengths of the total stake update history - function _getTotalStakeHistoryLengths(bytes memory quorumNumbers) - internal - view - returns (uint256[] memory) - { + function _getTotalStakeHistoryLengths( + bytes memory quorumNumbers + ) internal view returns (uint256[] memory) { uint256[] memory historyLengths = new uint256[](quorumNumbers.length); for (uint256 i = 0; i < quorumNumbers.length; i++) { @@ -550,7 +565,9 @@ contract StakeRegistryUnitTests is MockAVSDeployer, IStakeRegistryEvents { } /// @dev Sort to ensure that the array is in desscending order for removeStrategies - function _sortArrayDesc(uint256[] memory arr) internal pure returns (uint256[] memory) { + function _sortArrayDesc( + uint256[] memory arr + ) internal pure returns (uint256[] memory) { uint256 l = arr.length; for (uint256 i = 0; i < l; i++) { for (uint256 j = i + 1; j < l; j++) { @@ -575,22 +592,20 @@ contract StakeRegistryUnitTests_Config is StakeRegistryUnitTests { function testFuzz_initializeQuorum_Revert_WhenNotRegistryCoordinator( uint8 quorumNumber, uint96 minimumStake, - IStakeRegistry.StrategyParams[] memory strategyParams + IStakeRegistryTypes.StrategyParams[] memory strategyParams ) public { - cheats.expectRevert( - "StakeRegistry.onlyRegistryCoordinator: caller is not the RegistryCoordinator" - ); - stakeRegistry.initializeQuorum(quorumNumber, minimumStake, strategyParams); + cheats.expectRevert(IStakeRegistryErrors.OnlySlashingRegistryCoordinator.selector); + stakeRegistry.initializeDelegatedStakeQuorum(quorumNumber, minimumStake, strategyParams); } function testFuzz_initializeQuorum_Revert_WhenQuorumAlreadyExists( uint8 quorumNumber, uint96 minimumStake, - IStakeRegistry.StrategyParams[] memory strategyParams + IStakeRegistryTypes.StrategyParams[] memory strategyParams ) public fuzzOnlyInitializedQuorums(quorumNumber) { - cheats.expectRevert("StakeRegistry.initializeQuorum: quorum already exists"); + cheats.expectRevert(IStakeRegistryErrors.QuorumAlreadyExists.selector); cheats.prank(address(registryCoordinator)); - stakeRegistry.initializeQuorum(quorumNumber, minimumStake, strategyParams); + stakeRegistry.initializeDelegatedStakeQuorum(quorumNumber, minimumStake, strategyParams); } function testFuzz_initializeQuorum_Revert_WhenInvalidArrayLengths( @@ -598,21 +613,71 @@ contract StakeRegistryUnitTests_Config is StakeRegistryUnitTests { uint96 minimumStake ) public { cheats.assume(quorumNumber >= nextQuorum); - IStakeRegistry.StrategyParams[] memory strategyParams = - new IStakeRegistry.StrategyParams[](0); - cheats.expectRevert("StakeRegistry._addStrategyParams: no strategies provided"); + IStakeRegistryTypes.StrategyParams[] memory strategyParams = + new IStakeRegistryTypes.StrategyParams[](0); + cheats.expectRevert(IStakeRegistryErrors.InputArrayLengthZero.selector); cheats.prank(address(registryCoordinator)); - stakeRegistry.initializeQuorum(quorumNumber, minimumStake, strategyParams); + stakeRegistry.initializeDelegatedStakeQuorum(quorumNumber, minimumStake, strategyParams); - strategyParams = new IStakeRegistry.StrategyParams[](MAX_WEIGHING_FUNCTION_LENGTH + 1); + strategyParams = new IStakeRegistryTypes.StrategyParams[](MAX_WEIGHING_FUNCTION_LENGTH + 1); for (uint256 i = 0; i < strategyParams.length; i++) { - strategyParams[i] = IStakeRegistry.StrategyParams( + strategyParams[i] = IStakeRegistryTypes.StrategyParams( IStrategy(address(uint160(uint256(keccak256(abi.encodePacked(i)))))), uint96(1) ); } - cheats.expectRevert("StakeRegistry._addStrategyParams: exceed MAX_WEIGHING_FUNCTION_LENGTH"); + cheats.expectRevert(IStakeRegistryErrors.InputArrayLengthMismatch.selector); cheats.prank(address(registryCoordinator)); - stakeRegistry.initializeQuorum(quorumNumber, minimumStake, strategyParams); + stakeRegistry.initializeDelegatedStakeQuorum(quorumNumber, minimumStake, strategyParams); + } + + event StakeTypeSet(IStakeRegistryTypes.StakeType newStakeType); + + function test_initializeDelegatedStakeQuorum() public { + uint8 quorumNumber = nextQuorum; + uint96 minimumStake = 0; + IStakeRegistryTypes.StrategyParams[] memory strategyParams = + new IStakeRegistryTypes.StrategyParams[](1); + strategyParams[0] = IStakeRegistryTypes.StrategyParams( + IStrategy(address(uint160(uint256(keccak256(abi.encodePacked(quorumNumber)))))), + uint96(WEIGHTING_DIVISOR) + ); + + cheats.prank(address(registryCoordinator)); + cheats.expectEmit(true, true, true, true); + emit StakeTypeSet(IStakeRegistryTypes.StakeType.TOTAL_DELEGATED); + stakeRegistry.initializeDelegatedStakeQuorum(quorumNumber, minimumStake, strategyParams); + + IStakeRegistryTypes.StakeType stakeType = stakeRegistry.stakeTypePerQuorum(quorumNumber); + assertEq( + uint8(stakeType), + uint8(IStakeRegistryTypes.StakeType.TOTAL_DELEGATED), + "invalid stake type" + ); + } + + function test_initializeSlashableStakeQuorum() public { + uint8 quorumNumber = nextQuorum; + uint96 minimumStake = 0; + IStakeRegistryTypes.StrategyParams[] memory strategyParams = + new IStakeRegistryTypes.StrategyParams[](1); + strategyParams[0] = IStakeRegistryTypes.StrategyParams( + IStrategy(address(uint160(uint256(keccak256(abi.encodePacked(quorumNumber)))))), + uint96(WEIGHTING_DIVISOR) + ); + + cheats.prank(address(registryCoordinator)); + cheats.expectEmit(true, true, true, true); + emit StakeTypeSet(IStakeRegistryTypes.StakeType.TOTAL_SLASHABLE); + stakeRegistry.initializeSlashableStakeQuorum( + quorumNumber, minimumStake, 7 days, strategyParams + ); + + IStakeRegistryTypes.StakeType stakeType = stakeRegistry.stakeTypePerQuorum(quorumNumber); + assertEq( + uint8(stakeType), + uint8(IStakeRegistryTypes.StakeType.TOTAL_SLASHABLE), + "invalid stake type" + ); } /** @@ -626,17 +691,17 @@ contract StakeRegistryUnitTests_Config is StakeRegistryUnitTests { ) public { cheats.assume(quorumNumber >= nextQuorum); cheats.assume(0 < multipliers.length && multipliers.length <= MAX_WEIGHING_FUNCTION_LENGTH); - IStakeRegistry.StrategyParams[] memory strategyParams = - new IStakeRegistry.StrategyParams[](multipliers.length); + IStakeRegistryTypes.StrategyParams[] memory strategyParams = + new IStakeRegistryTypes.StrategyParams[](multipliers.length); for (uint256 i = 0; i < strategyParams.length; i++) { cheats.assume(multipliers[i] > 0); - strategyParams[i] = IStakeRegistry.StrategyParams( + strategyParams[i] = IStakeRegistryTypes.StrategyParams( IStrategy(address(uint160(uint256(keccak256(abi.encodePacked(i)))))), multipliers[i] ); } quorumNumber = nextQuorum; cheats.prank(address(registryCoordinator)); - stakeRegistry.initializeQuorum(quorumNumber, minimumStake, strategyParams); + stakeRegistry.initializeDelegatedStakeQuorum(quorumNumber, minimumStake, strategyParams); IStakeRegistry.StakeUpdate memory initialStakeUpdate = stakeRegistry.getTotalStakeUpdateAtIndex(quorumNumber, 0); @@ -680,9 +745,7 @@ contract StakeRegistryUnitTests_Config is StakeRegistryUnitTests { uint8 quorumNumber, uint96 minimumStakeForQuorum ) public fuzzOnlyInitializedQuorums(quorumNumber) { - cheats.expectRevert( - "StakeRegistry.onlyCoordinatorOwner: caller is not the owner of the registryCoordinator" - ); + cheats.expectRevert(IStakeRegistryErrors.OnlySlashingRegistryCoordinatorOwner.selector); stakeRegistry.setMinimumStakeForQuorum(quorumNumber, minimumStakeForQuorum); } @@ -692,7 +755,7 @@ contract StakeRegistryUnitTests_Config is StakeRegistryUnitTests { ) public { // quorums [0,nextQuorum) are initialized, so use an invalid quorumNumber cheats.assume(quorumNumber >= nextQuorum); - cheats.expectRevert("StakeRegistry.quorumExists: quorum does not exist"); + cheats.expectRevert(IStakeRegistryErrors.QuorumDoesNotExist.selector); cheats.prank(registryCoordinatorOwner); stakeRegistry.setMinimumStakeForQuorum(quorumNumber, minimumStakeForQuorum); } @@ -718,21 +781,19 @@ contract StakeRegistryUnitTests_Config is StakeRegistryUnitTests { */ function testFuzz_addStrategies_Revert_WhenNotRegistryCoordinatorOwner( uint8 quorumNumber, - IStakeRegistry.StrategyParams[] memory strategyParams + IStakeRegistryTypes.StrategyParams[] memory strategyParams ) public fuzzOnlyInitializedQuorums(quorumNumber) { - cheats.expectRevert( - "StakeRegistry.onlyCoordinatorOwner: caller is not the owner of the registryCoordinator" - ); + cheats.expectRevert(IStakeRegistryErrors.OnlySlashingRegistryCoordinatorOwner.selector); stakeRegistry.addStrategies(quorumNumber, strategyParams); } function testFuzz_addStrategies_Revert_WhenInvalidQuorum( uint8 quorumNumber, - IStakeRegistry.StrategyParams[] memory strategyParams + IStakeRegistryTypes.StrategyParams[] memory strategyParams ) public { // quorums [0,nextQuorum) are initialized, so use an invalid quorumNumber cheats.assume(quorumNumber >= nextQuorum); - cheats.expectRevert("StakeRegistry.quorumExists: quorum does not exist"); + cheats.expectRevert(IStakeRegistryErrors.QuorumDoesNotExist.selector); cheats.prank(registryCoordinatorOwner); stakeRegistry.addStrategies(quorumNumber, strategyParams); } @@ -742,12 +803,12 @@ contract StakeRegistryUnitTests_Config is StakeRegistryUnitTests { IStrategy strat = IStrategy(address(uint160(uint256(keccak256(abi.encodePacked("duplicate strat")))))); - IStakeRegistry.StrategyParams[] memory strategyParams = - new IStakeRegistry.StrategyParams[](2); - strategyParams[0] = IStakeRegistry.StrategyParams(strat, uint96(WEIGHTING_DIVISOR)); - strategyParams[1] = IStakeRegistry.StrategyParams(strat, uint96(WEIGHTING_DIVISOR)); + IStakeRegistryTypes.StrategyParams[] memory strategyParams = + new IStakeRegistryTypes.StrategyParams[](2); + strategyParams[0] = IStakeRegistryTypes.StrategyParams(strat, uint96(WEIGHTING_DIVISOR)); + strategyParams[1] = IStakeRegistryTypes.StrategyParams(strat, uint96(WEIGHTING_DIVISOR)); - cheats.expectRevert("StakeRegistry._addStrategyParams: cannot add same strategy 2x"); + cheats.expectRevert(IStakeRegistryErrors.InputDuplicateStrategy.selector); cheats.prank(registryCoordinatorOwner); stakeRegistry.addStrategies(quorumNumber, strategyParams); } @@ -757,13 +818,11 @@ contract StakeRegistryUnitTests_Config is StakeRegistryUnitTests { IStrategy strat = IStrategy(address(uint160(uint256(keccak256(abi.encodePacked("duplicate strat")))))); - IStakeRegistry.StrategyParams[] memory strategyParams = - new IStakeRegistry.StrategyParams[](2); - strategyParams[0] = IStakeRegistry.StrategyParams(strat, 0); + IStakeRegistryTypes.StrategyParams[] memory strategyParams = + new IStakeRegistryTypes.StrategyParams[](2); + strategyParams[0] = IStakeRegistryTypes.StrategyParams(strat, 0); - cheats.expectRevert( - "StakeRegistry._addStrategyParams: cannot add strategy with zero weight" - ); + cheats.expectRevert(IStakeRegistryErrors.InputMultiplierZero.selector); cheats.prank(registryCoordinatorOwner); stakeRegistry.addStrategies(quorumNumber, strategyParams); } @@ -786,11 +845,11 @@ contract StakeRegistryUnitTests_Config is StakeRegistryUnitTests { cheats.assume(multipliers[i] > 0); } // Expected events emitted - IStakeRegistry.StrategyParams[] memory strategyParams = - new IStakeRegistry.StrategyParams[](multipliers.length); + IStakeRegistryTypes.StrategyParams[] memory strategyParams = + new IStakeRegistryTypes.StrategyParams[](multipliers.length); for (uint256 i = 0; i < strategyParams.length; i++) { IStrategy strat = IStrategy(address(uint160(uint256(keccak256(abi.encodePacked(i)))))); - strategyParams[i] = IStakeRegistry.StrategyParams(strat, multipliers[i]); + strategyParams[i] = IStakeRegistryTypes.StrategyParams(strat, multipliers[i]); cheats.expectEmit(true, true, true, true, address(stakeRegistry)); emit StrategyAddedToQuorum(quorumNumber, strat); @@ -823,9 +882,7 @@ contract StakeRegistryUnitTests_Config is StakeRegistryUnitTests { uint8 quorumNumber, uint256[] memory indicesToRemove ) public fuzzOnlyInitializedQuorums(quorumNumber) { - cheats.expectRevert( - "StakeRegistry.onlyCoordinatorOwner: caller is not the owner of the registryCoordinator" - ); + cheats.expectRevert(IStakeRegistryErrors.OnlySlashingRegistryCoordinatorOwner.selector); stakeRegistry.removeStrategies(quorumNumber, indicesToRemove); } @@ -835,7 +892,7 @@ contract StakeRegistryUnitTests_Config is StakeRegistryUnitTests { ) public { // quorums [0,nextQuorum) are initialized, so use an invalid quorumNumber cheats.assume(quorumNumber >= nextQuorum); - cheats.expectRevert("StakeRegistry.quorumExists: quorum does not exist"); + cheats.expectRevert(IStakeRegistryErrors.QuorumDoesNotExist.selector); cheats.prank(registryCoordinatorOwner); stakeRegistry.removeStrategies(quorumNumber, indicesToRemove); } @@ -865,7 +922,7 @@ contract StakeRegistryUnitTests_Config is StakeRegistryUnitTests { uint8 quorumNumber = _initializeQuorum(minimumStake, numStrategiesToAdd); uint256[] memory indicesToRemove = new uint256[](0); - cheats.expectRevert("StakeRegistry.removeStrategies: no indices to remove provided"); + cheats.expectRevert(IStakeRegistryErrors.InputArrayLengthZero.selector); cheats.prank(registryCoordinatorOwner); stakeRegistry.removeStrategies(quorumNumber, indicesToRemove); } @@ -926,9 +983,7 @@ contract StakeRegistryUnitTests_Config is StakeRegistryUnitTests { uint256[] calldata strategyIndices, uint96[] calldata newMultipliers ) public fuzzOnlyInitializedQuorums(quorumNumber) { - cheats.expectRevert( - "StakeRegistry.onlyCoordinatorOwner: caller is not the owner of the registryCoordinator" - ); + cheats.expectRevert(IStakeRegistryErrors.OnlySlashingRegistryCoordinatorOwner.selector); stakeRegistry.modifyStrategyParams(quorumNumber, strategyIndices, newMultipliers); } @@ -939,18 +994,17 @@ contract StakeRegistryUnitTests_Config is StakeRegistryUnitTests { ) public { // quorums [0,nextQuorum) are initialized, so use an invalid quorumNumber cheats.assume(quorumNumber >= nextQuorum); - cheats.expectRevert("StakeRegistry.quorumExists: quorum does not exist"); + cheats.expectRevert(IStakeRegistryErrors.QuorumDoesNotExist.selector); cheats.prank(registryCoordinatorOwner); stakeRegistry.modifyStrategyParams(quorumNumber, strategyIndices, newMultipliers); } - function testFuzz_modifyStrategyParams_Revert_WhenEmptyArray(uint8 quorumNumber) - public - fuzzOnlyInitializedQuorums(quorumNumber) - { + function testFuzz_modifyStrategyParams_Revert_WhenEmptyArray( + uint8 quorumNumber + ) public fuzzOnlyInitializedQuorums(quorumNumber) { uint256[] memory strategyIndices = new uint256[](0); uint96[] memory newMultipliers = new uint96[](0); - cheats.expectRevert("StakeRegistry.modifyStrategyParams: no strategy indices provided"); + cheats.expectRevert(IStakeRegistryErrors.InputArrayLengthZero.selector); cheats.prank(registryCoordinatorOwner); stakeRegistry.modifyStrategyParams(quorumNumber, strategyIndices, newMultipliers); } @@ -962,7 +1016,7 @@ contract StakeRegistryUnitTests_Config is StakeRegistryUnitTests { ) public fuzzOnlyInitializedQuorums(quorumNumber) { cheats.assume(strategyIndices.length != newMultipliers.length); cheats.assume(strategyIndices.length > 0); - cheats.expectRevert("StakeRegistry.modifyStrategyParams: input length mismatch"); + cheats.expectRevert(IStakeRegistryErrors.InputArrayLengthMismatch.selector); cheats.prank(registryCoordinatorOwner); stakeRegistry.modifyStrategyParams(quorumNumber, strategyIndices, newMultipliers); } @@ -1021,29 +1075,28 @@ contract StakeRegistryUnitTests_Register is StakeRegistryUnitTests { function test_registerOperator_Revert_WhenNotRegistryCoordinator() public { (address operator, bytes32 operatorId) = _selectNewOperator(); - cheats.expectRevert( - "StakeRegistry.onlyRegistryCoordinator: caller is not the RegistryCoordinator" - ); + cheats.expectRevert(IStakeRegistryErrors.OnlySlashingRegistryCoordinator.selector); stakeRegistry.registerOperator(operator, operatorId, initializedQuorumBytes); } - function testFuzz_Revert_WhenQuorumDoesNotExist(bytes32 rand) public { + function testFuzz_Revert_WhenQuorumDoesNotExist( + bytes32 rand + ) public { RegisterSetup memory setup = _fuzz_setupRegisterOperator(initializedQuorumBitmap, 0); // Get a list of valid quorums ending in an invalid quorum number bytes memory invalidQuorums = _fuzz_getInvalidQuorums(rand); - cheats.expectRevert("StakeRegistry.quorumExists: quorum does not exist"); + cheats.expectRevert(IStakeRegistryErrors.QuorumDoesNotExist.selector); cheats.prank(address(registryCoordinator)); stakeRegistry.registerOperator(setup.operator, setup.operatorId, invalidQuorums); } /// @dev Attempt to register for all quorums, selecting one quorum to attempt with /// insufficient stake - function testFuzz_registerOperator_Revert_WhenInsufficientStake(uint8 failingQuorum) - public - fuzzOnlyInitializedQuorums(failingQuorum) - { + function testFuzz_registerOperator_Revert_WhenInsufficientStake( + uint8 failingQuorum + ) public fuzzOnlyInitializedQuorums(failingQuorum) { (address operator, bytes32 operatorId) = _selectNewOperator(); bytes memory quorumNumbers = initializedQuorumBytes; uint96[] memory minimumStakes = _getMinimumStakes(quorumNumbers); @@ -1067,9 +1120,7 @@ contract StakeRegistryUnitTests_Register is StakeRegistryUnitTests { } // Attempt to register - cheats.expectRevert( - "StakeRegistry.registerOperator: Operator does not meet minimum stake requirement for quorum" - ); + cheats.expectRevert(IStakeRegistryErrors.BelowMinimumStakeRequirement.selector); cheats.prank(address(registryCoordinator)); stakeRegistry.registerOperator(operator, operatorId, quorumNumbers); } @@ -1375,13 +1426,13 @@ contract StakeRegistryUnitTests_Deregister is StakeRegistryUnitTests { fuzzy_addtlStake: 0 }); - cheats.expectRevert( - "StakeRegistry.onlyRegistryCoordinator: caller is not the RegistryCoordinator" - ); + cheats.expectRevert(IStakeRegistryErrors.OnlySlashingRegistryCoordinator.selector); stakeRegistry.deregisterOperator(setup.operatorId, setup.quorumsToRemove); } - function testFuzz_deregisterOperator_Revert_WhenQuorumDoesNotExist(bytes32 rand) public { + function testFuzz_deregisterOperator_Revert_WhenQuorumDoesNotExist( + bytes32 rand + ) public { // Create a new operator registered for all quorums DeregisterSetup memory setup = _fuzz_setupDeregisterOperator({ registeredFor: initializedQuorumBitmap, @@ -1392,7 +1443,7 @@ contract StakeRegistryUnitTests_Deregister is StakeRegistryUnitTests { // Get a list of valid quorums ending in an invalid quorum number bytes memory invalidQuorums = _fuzz_getInvalidQuorums(rand); - cheats.expectRevert("StakeRegistry.quorumExists: quorum does not exist"); + cheats.expectRevert(IStakeRegistryErrors.QuorumDoesNotExist.selector); cheats.prank(address(registryCoordinator)); stakeRegistry.registerOperator(setup.operator, setup.operatorId, invalidQuorums); } @@ -1739,13 +1790,13 @@ contract StakeRegistryUnitTests_StakeUpdates is StakeRegistryUnitTests { UpdateSetup memory setup = _fuzz_setupUpdateOperatorStake({registeredFor: initializedQuorumBitmap, fuzzy_Delta: 0}); - cheats.expectRevert( - "StakeRegistry.onlyRegistryCoordinator: caller is not the RegistryCoordinator" - ); + cheats.expectRevert(IStakeRegistryErrors.OnlySlashingRegistryCoordinator.selector); stakeRegistry.updateOperatorStake(setup.operator, setup.operatorId, setup.quorumNumbers); } - function testFuzz_updateOperatorStake_Revert_WhenQuorumDoesNotExist(bytes32 rand) public { + function testFuzz_updateOperatorStake_Revert_WhenQuorumDoesNotExist( + bytes32 rand + ) public { // Create a new operator registered for all quorums UpdateSetup memory setup = _fuzz_setupUpdateOperatorStake({registeredFor: initializedQuorumBitmap, fuzzy_Delta: 0}); @@ -1753,7 +1804,7 @@ contract StakeRegistryUnitTests_StakeUpdates is StakeRegistryUnitTests { // Get a list of valid quorums ending in an invalid quorum number bytes memory invalidQuorums = _fuzz_getInvalidQuorums(rand); - cheats.expectRevert("StakeRegistry.quorumExists: quorum does not exist"); + cheats.expectRevert(IStakeRegistryErrors.QuorumDoesNotExist.selector); cheats.prank(address(registryCoordinator)); stakeRegistry.updateOperatorStake(setup.operator, setup.operatorId, invalidQuorums); } @@ -1766,7 +1817,9 @@ contract StakeRegistryUnitTests_StakeUpdates is StakeRegistryUnitTests { * updateOperatorStake should then update the operator's stake using the new weight - we test * what happens when the operator remains at/above minimum stake, vs dipping below */ - function testFuzz_updateOperatorStake_SingleOperator_SingleBlock(int8 stakeDelta) public { + function testFuzz_updateOperatorStake_SingleOperator_SingleBlock( + int8 stakeDelta + ) public { UpdateSetup memory setup = _fuzz_setupUpdateOperatorStake({ registeredFor: initializedQuorumBitmap, fuzzy_Delta: stakeDelta @@ -1842,7 +1895,7 @@ contract StakeRegistryUnitTests_StakeUpdates is StakeRegistryUnitTests { "failed to remove delta from total stake" ); assertEq(newOperatorStake.stake, 0, "operator stake should now be zero"); - // Quorum should be added to return bitmap + // IECDSAStakeRegistryTypes.Quorum should be added to return bitmap assertTrue( quorumsToRemove.isSet(quorumNumber), "quorum should be in removal bitmap" ); @@ -2055,7 +2108,7 @@ contract StakeRegistryUnitTests_StakeUpdates is StakeRegistryUnitTests { ); // assertEq(prevTotalStake.stake - stakeRemoved, newTotalStake.stake, "failed to remove delta from total stake"); assertEq(newOperatorStake.stake, 0, "operator stake should now be zero"); - // Quorum should be added to return bitmap + // IECDSAStakeRegistryTypes.Quorum should be added to return bitmap assertTrue( quorumsToRemove.isSet(quorumNumber), "quorum should be in removal bitmap" ); @@ -2120,7 +2173,7 @@ contract StakeRegistryUnitTests_weightOfOperatorForQuorum is StakeRegistryUnitTe * successfully and return a value for weightOfOperatorForQuorum. Fuzz test sets the operator shares * and asserts that the summed weight of the operator is correct. */ - function test_weightOfOperatorForQuorum( + function testFuzz_weightOfOperatorForQuorum( address operator, uint96[] memory multipliers, uint96[] memory shares @@ -2131,8 +2184,8 @@ contract StakeRegistryUnitTests_weightOfOperatorForQuorum is StakeRegistryUnitTe // Initialize quorum with strategies of fuzzed multipliers. // Bound multipliers and shares max values to prevent overflows - IStakeRegistry.StrategyParams[] memory strategyParams = - new IStakeRegistry.StrategyParams[](3); + IStakeRegistryTypes.StrategyParams[] memory strategyParams = + new IStakeRegistryTypes.StrategyParams[](3); for (uint256 i = 0; i < strategyParams.length; i++) { multipliers[i] = uint96( _randUint({ @@ -2146,12 +2199,15 @@ contract StakeRegistryUnitTests_weightOfOperatorForQuorum is StakeRegistryUnitTe IStrategy strat = IStrategy( address(uint160(uint256(keccak256(abi.encodePacked("Voteweighing test", i))))) ); - strategyParams[i] = - IStakeRegistry.StrategyParams(strat, uint96(WEIGHTING_DIVISOR) + multipliers[i]); + strategyParams[i] = IStakeRegistryTypes.StrategyParams( + strat, uint96(WEIGHTING_DIVISOR) + multipliers[i] + ); } cheats.prank(address(registryCoordinator)); uint8 quorumNumber = nextQuorum; - stakeRegistry.initializeQuorum(quorumNumber, 0, /* minimumStake */ strategyParams); + stakeRegistry.initializeDelegatedStakeQuorum( + quorumNumber, 0, /* minimumStake */ strategyParams + ); // set the operator shares for (uint256 i = 0; i < strategyParams.length; i++) { @@ -2175,30 +2231,32 @@ contract StakeRegistryUnitTests_weightOfOperatorForQuorum is StakeRegistryUnitTe } /// @dev consider multipliers for 3 strategies - function test_weightOfOperatorForQuorum_3Strategies( + function testFuzz_weightOfOperatorForQuorum_3Strategies( address operator, uint96[3] memory shares ) public { // 3 LST Strat multipliers, rETH, stETH, ETH uint96[] memory multipliers = new uint96[](3); - multipliers[0] = uint96(1_070_136_092_289_993_178); - multipliers[1] = uint96(1_071_364_636_818_145_808); - multipliers[2] = uint96(1_000_000_000_000_000_000); + multipliers[0] = uint96(1070136092289993178); + multipliers[1] = uint96(1071364636818145808); + multipliers[2] = uint96(1000000000000000000); - IStakeRegistry.StrategyParams[] memory strategyParams = - new IStakeRegistry.StrategyParams[](3); + IStakeRegistryTypes.StrategyParams[] memory strategyParams = + new IStakeRegistryTypes.StrategyParams[](3); for (uint256 i = 0; i < strategyParams.length; i++) { shares[i] = uint96(_randUint({rand: bytes32(uint256(shares[i])), min: 0, max: 1e24})); IStrategy strat = IStrategy( address(uint160(uint256(keccak256(abi.encodePacked("Voteweighing test", i))))) ); - strategyParams[i] = IStakeRegistry.StrategyParams(strat, multipliers[i]); + strategyParams[i] = IStakeRegistryTypes.StrategyParams(strat, multipliers[i]); } // create a valid quorum cheats.prank(address(registryCoordinator)); uint8 quorumNumber = nextQuorum; - stakeRegistry.initializeQuorum(quorumNumber, 0, /* minimumStake */ strategyParams); + stakeRegistry.initializeDelegatedStakeQuorum( + quorumNumber, 0, /* minimumStake */ strategyParams + ); // set the operator shares for (uint256 i = 0; i < strategyParams.length; i++) { diff --git a/test/unit/UpgradeableProxyLib.sol b/test/unit/UpgradeableProxyLib.sol new file mode 100644 index 00000000..0f0986f5 --- /dev/null +++ b/test/unit/UpgradeableProxyLib.sol @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.0; + +import {Vm} from "forge-std/Vm.sol"; +import {ProxyAdmin} from "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; +import {TransparentUpgradeableProxy} from + "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; + +contract EmptyContract {} + +library UpgradeableProxyLib { + bytes32 internal constant IMPLEMENTATION_SLOT = + 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; + bytes32 internal constant ADMIN_SLOT = + 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103; + Vm internal constant vm = Vm(address(uint160(uint256(keccak256("hevm cheat code"))))); + + function deployProxyAdmin() internal returns (address) { + return address(new ProxyAdmin()); + } + + function setUpEmptyProxy( + address admin + ) internal returns (address) { + address emptyContract = address(new EmptyContract()); + return address(new TransparentUpgradeableProxy(emptyContract, admin, "")); + } + + function upgrade(address proxy, address impl) internal { + ProxyAdmin admin = getProxyAdmin(proxy); + admin.upgrade(TransparentUpgradeableProxy(payable(proxy)), impl); + } + + function upgradeAndCall(address proxy, address impl, bytes memory initData) internal { + ProxyAdmin admin = getProxyAdmin(proxy); + admin.upgradeAndCall(TransparentUpgradeableProxy(payable(proxy)), impl, initData); + } + + function getImplementation( + address proxy + ) internal view returns (address) { + bytes32 value = vm.load(proxy, IMPLEMENTATION_SLOT); + return address(uint160(uint256(value))); + } + + function getProxyAdmin( + address proxy + ) internal view returns (ProxyAdmin) { + bytes32 value = vm.load(proxy, ADMIN_SLOT); + return ProxyAdmin(address(uint160(uint256(value)))); + } +} diff --git a/test/unit/Utils.sol b/test/unit/Utils.sol index 0947b1d4..463d8a3b 100644 --- a/test/unit/Utils.sol +++ b/test/unit/Utils.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; import "eigenlayer-contracts/src/contracts/strategies/StrategyBase.sol"; @@ -6,18 +6,17 @@ import "eigenlayer-contracts/src/contracts/strategies/StrategyBase.sol"; contract Utils { address constant dummyAdmin = address(uint160(uint256(keccak256("DummyAdmin")))); - function deployNewStrategy(IERC20 token, IStrategyManager strategyManager, IPauserRegistry pauserRegistry, address admin) public returns (StrategyBase) { - StrategyBase newStrategy = new StrategyBase(strategyManager); + function deployNewStrategy( + IERC20 token, + IStrategyManager strategyManager, + IPauserRegistry pauserRegistry, + address admin + ) public returns (StrategyBase) { + StrategyBase newStrategy = new StrategyBase(strategyManager, pauserRegistry); newStrategy = StrategyBase( - address( - new TransparentUpgradeableProxy( - address(newStrategy), - address(admin), - "" - ) - ) + address(new TransparentUpgradeableProxy(address(newStrategy), address(admin), "")) ); - newStrategy.initialize(token, pauserRegistry); + newStrategy.initialize(token); return newStrategy; } } diff --git a/test/utils/BLSMockAVSDeployer.sol b/test/utils/BLSMockAVSDeployer.sol index 3f5286a3..701fd320 100644 --- a/test/utils/BLSMockAVSDeployer.sol +++ b/test/utils/BLSMockAVSDeployer.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import {BLSSignatureChecker} from "../../src/BLSSignatureChecker.sol"; import {MockAVSDeployer} from "../utils/MockAVSDeployer.sol"; @@ -16,55 +16,73 @@ contract BLSMockAVSDeployer is MockAVSDeployer { BN254.G2Point oneHundredQuorumApkG2; BN254.G1Point sigma; - function _setUpBLSMockAVSDeployer() virtual public { + function _setUpBLSMockAVSDeployer() public virtual { _deployMockEigenLayerAndAVS(); _setAggregatePublicKeysAndSignature(); } - function _setUpBLSMockAVSDeployer(uint8 numQuorumsToAdd) virtual public { + function _setUpBLSMockAVSDeployer( + uint8 numQuorumsToAdd + ) public virtual { _deployMockEigenLayerAndAVS(numQuorumsToAdd); _setAggregatePublicKeysAndSignature(); } function _setAggregatePublicKeysAndSignature() internal { // aggSignerPrivKey*g2 - aggSignerApkG2.X[1] = 19101821850089705274637533855249918363070101489527618151493230256975900223847; - aggSignerApkG2.X[0] = 5334410886741819556325359147377682006012228123419628681352847439302316235957; - aggSignerApkG2.Y[1] = 354176189041917478648604979334478067325821134838555150300539079146482658331; - aggSignerApkG2.Y[0] = 4185483097059047421902184823581361466320657066600218863748375739772335928910; + aggSignerApkG2.X[1] = + 19101821850089705274637533855249918363070101489527618151493230256975900223847; + aggSignerApkG2.X[0] = + 5334410886741819556325359147377682006012228123419628681352847439302316235957; + aggSignerApkG2.Y[1] = + 354176189041917478648604979334478067325821134838555150300539079146482658331; + aggSignerApkG2.Y[0] = + 4185483097059047421902184823581361466320657066600218863748375739772335928910; // 100*aggSignerPrivKey*g2 - oneHundredQuorumApkG2.X[1] = 6187649255575786743153792867265230878737103598736372524337965086852090105771; - oneHundredQuorumApkG2.X[0] = 5334877400925935887383922877430837542135722474116902175395820705628447222839; - oneHundredQuorumApkG2.Y[1] = 4668116328019846503695710811760363536142902258271850958815598072072236299223; - oneHundredQuorumApkG2.Y[0] = 21446056442597180561077194011672151329458819211586246807143487001691968661015; + oneHundredQuorumApkG2.X[1] = + 6187649255575786743153792867265230878737103598736372524337965086852090105771; + oneHundredQuorumApkG2.X[0] = + 5334877400925935887383922877430837542135722474116902175395820705628447222839; + oneHundredQuorumApkG2.Y[1] = + 4668116328019846503695710811760363536142902258271850958815598072072236299223; + oneHundredQuorumApkG2.Y[0] = + 21446056442597180561077194011672151329458819211586246807143487001691968661015; sigma = BN254.hashToG1(msgHash).scalar_mul(aggSignerPrivKey); } - - function _generateSignerAndNonSignerPrivateKeys(uint256 pseudoRandomNumber, uint256 numSigners, uint256 numNonSigners) internal view returns (uint256[] memory, uint256[] memory) { + function _generateSignerAndNonSignerPrivateKeys( + uint256 pseudoRandomNumber, + uint256 numSigners, + uint256 numNonSigners + ) internal view returns (uint256[] memory, uint256[] memory) { uint256[] memory signerPrivateKeys = new uint256[](numSigners); // generate numSigners numbers that add up to aggSignerPrivKey mod BN254.FR_MODULUS uint256 sum = 0; - for (uint i = 0; i < numSigners - 1; i++) { - signerPrivateKeys[i] = uint256(keccak256(abi.encodePacked("signerPrivateKey", pseudoRandomNumber, i))) % BN254.FR_MODULUS; + for (uint256 i = 0; i < numSigners - 1; i++) { + signerPrivateKeys[i] = uint256( + keccak256(abi.encodePacked("signerPrivateKey", pseudoRandomNumber, i)) + ) % BN254.FR_MODULUS; sum = addmod(sum, signerPrivateKeys[i], BN254.FR_MODULUS); } // signer private keys need to add to aggSignerPrivKey - signerPrivateKeys[numSigners - 1] = addmod(aggSignerPrivKey, BN254.FR_MODULUS - sum % BN254.FR_MODULUS, BN254.FR_MODULUS); + signerPrivateKeys[numSigners - 1] = + addmod(aggSignerPrivKey, BN254.FR_MODULUS - sum % BN254.FR_MODULUS, BN254.FR_MODULUS); uint256[] memory nonSignerPrivateKeys = new uint256[](numNonSigners); - for (uint i = 0; i < numNonSigners; i++) { - nonSignerPrivateKeys[i] = uint256(keccak256(abi.encodePacked("nonSignerPrivateKey", pseudoRandomNumber, i))) % BN254.FR_MODULUS; + for (uint256 i = 0; i < numNonSigners; i++) { + nonSignerPrivateKeys[i] = uint256( + keccak256(abi.encodePacked("nonSignerPrivateKey", pseudoRandomNumber, i)) + ) % BN254.FR_MODULUS; } // Sort nonSignerPrivateKeys in order of ascending pubkeyHash // Uses insertion sort to sort array in place - for (uint i = 1; i < nonSignerPrivateKeys.length; i++) { - uint privateKey = nonSignerPrivateKeys[i]; + for (uint256 i = 1; i < nonSignerPrivateKeys.length; i++) { + uint256 privateKey = nonSignerPrivateKeys[i]; bytes32 pubkeyHash = _toPubkeyHash(privateKey); - uint j = i; + uint256 j = i; // Move elements of nonSignerPrivateKeys[0..i-1] that are greater than the current key // to one position ahead of their current position @@ -78,8 +96,15 @@ contract BLSMockAVSDeployer is MockAVSDeployer { return (signerPrivateKeys, nonSignerPrivateKeys); } - function _registerSignatoriesAndGetNonSignerStakeAndSignatureRandom(uint256 pseudoRandomNumber, uint256 numNonSigners, uint256 quorumBitmap) internal returns(uint32, BLSSignatureChecker.NonSignerStakesAndSignature memory) { - (uint256[] memory signerPrivateKeys, uint256[] memory nonSignerPrivateKeys) = _generateSignerAndNonSignerPrivateKeys(pseudoRandomNumber, maxOperatorsToRegister - numNonSigners, numNonSigners); + function _registerSignatoriesAndGetNonSignerStakeAndSignatureRandom( + uint256 pseudoRandomNumber, + uint256 numNonSigners, + uint256 quorumBitmap + ) internal returns (uint32, BLSSignatureChecker.NonSignerStakesAndSignature memory) { + (uint256[] memory signerPrivateKeys, uint256[] memory nonSignerPrivateKeys) = + _generateSignerAndNonSignerPrivateKeys( + pseudoRandomNumber, maxOperatorsToRegister - numNonSigners, numNonSigners + ); bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); // randomly combine signer and non-signer private keys @@ -94,15 +119,17 @@ contract BLSMockAVSDeployer is MockAVSDeployer { { uint256 signerIndex = 0; uint256 nonSignerIndex = 0; - for (uint i = 0; i < maxOperatorsToRegister; i++) { + for (uint256 i = 0; i < maxOperatorsToRegister; i++) { uint256 randomSeed = uint256(keccak256(abi.encodePacked("privKeyCombination", i))); if (randomSeed % 2 == 0 && signerIndex < signerPrivateKeys.length) { privateKeys[i] = signerPrivateKeys[signerIndex]; signerIndex++; } else if (nonSignerIndex < nonSignerPrivateKeys.length) { privateKeys[i] = nonSignerPrivateKeys[nonSignerIndex]; - nonSignerStakesAndSignature.nonSignerPubkeys[nonSignerIndex] = BN254.generatorG1().scalar_mul(privateKeys[i]); - nonSignerOperatorIds[nonSignerIndex] = nonSignerStakesAndSignature.nonSignerPubkeys[nonSignerIndex].hashG1Point(); + nonSignerStakesAndSignature.nonSignerPubkeys[nonSignerIndex] = + BN254.generatorG1().scalar_mul(privateKeys[i]); + nonSignerOperatorIds[nonSignerIndex] = + nonSignerStakesAndSignature.nonSignerPubkeys[nonSignerIndex].hashG1Point(); nonSignerIndex++; } else { privateKeys[i] = signerPrivateKeys[signerIndex]; @@ -113,39 +140,43 @@ contract BLSMockAVSDeployer is MockAVSDeployer { pubkeys[i] = BN254.generatorG1().scalar_mul(privateKeys[i]); // add the public key to each quorum - for (uint j = 0; j < nonSignerStakesAndSignature.quorumApks.length; j++) { - nonSignerStakesAndSignature.quorumApks[j] = nonSignerStakesAndSignature.quorumApks[j].plus(pubkeys[i]); + for (uint256 j = 0; j < nonSignerStakesAndSignature.quorumApks.length; j++) { + nonSignerStakesAndSignature.quorumApks[j] = + nonSignerStakesAndSignature.quorumApks[j].plus(pubkeys[i]); } } } // register all operators for the first quorum - for (uint i = 0; i < maxOperatorsToRegister; i++) { + for (uint256 i = 0; i < maxOperatorsToRegister; i++) { cheats.roll(registrationBlockNumber + blocksBetweenRegistrations * i); _registerOperatorWithCoordinator(operators[i], quorumBitmap, pubkeys[i], defaultStake); } - uint32 referenceBlockNumber = registrationBlockNumber + blocksBetweenRegistrations * uint32(maxOperatorsToRegister) + 1; + uint32 referenceBlockNumber = registrationBlockNumber + + blocksBetweenRegistrations * uint32(maxOperatorsToRegister) + 1; cheats.roll(referenceBlockNumber + 100); - OperatorStateRetriever.CheckSignaturesIndices memory checkSignaturesIndices = operatorStateRetriever.getCheckSignaturesIndices( - registryCoordinator, - referenceBlockNumber, - quorumNumbers, - nonSignerOperatorIds + OperatorStateRetriever.CheckSignaturesIndices memory checkSignaturesIndices = + operatorStateRetriever.getCheckSignaturesIndices( + registryCoordinator, referenceBlockNumber, quorumNumbers, nonSignerOperatorIds ); - nonSignerStakesAndSignature.nonSignerQuorumBitmapIndices = checkSignaturesIndices.nonSignerQuorumBitmapIndices; + nonSignerStakesAndSignature.nonSignerQuorumBitmapIndices = + checkSignaturesIndices.nonSignerQuorumBitmapIndices; nonSignerStakesAndSignature.apkG2 = aggSignerApkG2; nonSignerStakesAndSignature.sigma = sigma; nonSignerStakesAndSignature.quorumApkIndices = checkSignaturesIndices.quorumApkIndices; nonSignerStakesAndSignature.totalStakeIndices = checkSignaturesIndices.totalStakeIndices; - nonSignerStakesAndSignature.nonSignerStakeIndices = checkSignaturesIndices.nonSignerStakeIndices; + nonSignerStakesAndSignature.nonSignerStakeIndices = + checkSignaturesIndices.nonSignerStakeIndices; return (referenceBlockNumber, nonSignerStakesAndSignature); } - function _toPubkeyHash(uint privKey) internal view returns (bytes32) { + function _toPubkeyHash( + uint256 privKey + ) internal view returns (bytes32) { return BN254.generatorG1().scalar_mul(privKey).hashG1Point(); } } diff --git a/test/utils/CoreDeployLib.sol b/test/utils/CoreDeployLib.sol new file mode 100644 index 00000000..d25dd95a --- /dev/null +++ b/test/utils/CoreDeployLib.sol @@ -0,0 +1,271 @@ +// // SPDX-License-Identifier: UNLICENSED +// pragma solidity ^0.8.0; + +// import {ProxyAdmin} from "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; +// import {TransparentUpgradeableProxy} from +// "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +// import {UpgradeableBeacon} from "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol"; +// import {DelegationManager} from "eigenlayer-contracts/src/contracts/core/DelegationManager.sol"; +// import {StrategyManager} from "eigenlayer-contracts/src/contracts/core/StrategyManager.sol"; +// import {AVSDirectory} from "eigenlayer-contracts/src/contracts/core/AVSDirectory.sol"; +// import {EigenPodManager} from "eigenlayer-contracts/src/contracts/pods/EigenPodManager.sol"; +// import {RewardsCoordinator} from "eigenlayer-contracts/src/contracts/core/RewardsCoordinator.sol"; +// import {StrategyBase} from "eigenlayer-contracts/src/contracts/strategies/StrategyBase.sol"; +// import {EigenPod} from "eigenlayer-contracts/src/contracts/pods/EigenPod.sol"; +// import {IETHPOSDeposit} from "eigenlayer-contracts/src/contracts/interfaces/IETHPOSDeposit.sol"; +// import {StrategyBaseTVLLimits} from "eigenlayer-contracts/src/contracts/strategies/StrategyBaseTVLLimits.sol"; +// import {PauserRegistry} from "eigenlayer-contracts/src/contracts/permissions/PauserRegistry.sol"; +// import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; +// import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +// import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol"; +// import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol"; +// import {IBeacon} from "@openzeppelin/contracts/proxy/beacon/IBeacon.sol"; +// import {IStrategyManager} from "eigenlayer-contracts/src/contracts/interfaces/IStrategyManager.sol"; +// import {IEigenPodManager} from "eigenlayer-contracts/src/contracts/interfaces/IEigenPodManager.sol"; +// import {IAVSDirectory} from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol"; +// import {IPauserRegistry} from "eigenlayer-contracts/src/contracts/interfaces/IPauserRegistry.sol"; +// import {StrategyFactory} from "eigenlayer-contracts/src/contracts/strategies/StrategyFactory.sol"; + +// import {UpgradeableProxyLib} from "../unit/UpgradeableProxyLib.sol"; + +// library CoreDeploymentLib { +// using UpgradeableProxyLib for address; + +// struct StrategyManagerConfig { +// uint256 initPausedStatus; +// uint256 initWithdrawalDelayBlocks; +// } + +// struct DelegationManagerConfig { +// uint256 initPausedStatus; +// IStrategy[] strategies; +// uint256 minWithdrawalDelayBlocks; +// uint256[] withdrawalDelayBlocks; + +// } + +// struct EigenPodManagerConfig { +// uint256 initPausedStatus; +// } + +// struct RewardsCoordinatorConfig { +// uint256 initPausedStatus; +// uint256 maxRewardsDuration; +// uint256 maxRetroactiveLength; +// uint256 maxFutureLength; +// uint256 genesisRewardsTimestamp; +// address updater; +// uint256 activationDelay; +// uint256 calculationIntervalSeconds; +// uint256 globalOperatorCommissionBips; +// } + +// struct StrategyFactoryConfig { +// uint256 initPausedStatus; +// } + +// struct DeploymentConfigData { +// StrategyManagerConfig strategyManager; +// DelegationManagerConfig delegationManager; +// EigenPodManagerConfig eigenPodManager; +// RewardsCoordinatorConfig rewardsCoordinator; +// StrategyFactoryConfig strategyFactory; +// } + +// struct DeploymentData { +// address delegationManager; +// address avsDirectory; +// address strategyManager; +// address eigenPodManager; +// address rewardsCoordinator; +// address eigenPodBeacon; +// address pauserRegistry; +// address strategyFactory; +// address strategyBeacon; +// } + +// function deployContracts( +// address proxyAdmin, +// DeploymentConfigData memory configData +// ) internal returns (DeploymentData memory) { +// DeploymentData memory result; + +// result.delegationManager = UpgradeableProxyLib.setUpEmptyProxy(proxyAdmin); +// result.avsDirectory = UpgradeableProxyLib.setUpEmptyProxy(proxyAdmin); +// result.strategyManager = UpgradeableProxyLib.setUpEmptyProxy(proxyAdmin); +// result.eigenPodManager = UpgradeableProxyLib.setUpEmptyProxy(proxyAdmin); +// result.rewardsCoordinator = UpgradeableProxyLib.setUpEmptyProxy(proxyAdmin); +// result.eigenPodBeacon = UpgradeableProxyLib.setUpEmptyProxy(proxyAdmin); +// result.pauserRegistry = UpgradeableProxyLib.setUpEmptyProxy(proxyAdmin); +// result.strategyFactory = UpgradeableProxyLib.setUpEmptyProxy(proxyAdmin); + +// // Deploy the implementation contracts, using the proxy contracts as inputs +// address delegationManagerImpl = address( +// new DelegationManager( +// IStrategyManager(result.strategyManager), +// IEigenPodManager(result.eigenPodManager) +// ) +// ); +// address avsDirectoryImpl = +// address(new AVSDirectory(IDelegationManager(result.delegationManager))); + +// address strategyManagerImpl = address( +// new StrategyManager( +// IDelegationManager(result.delegationManager), +// IEigenPodManager(result.eigenPodManager) +// ) +// ); + +// address strategyFactoryImpl = +// address(new StrategyFactory(IStrategyManager(result.strategyManager))); + +// address ethPOSDeposit; +// if (block.chainid == 1) { +// ethPOSDeposit = 0x00000000219ab540356cBB839Cbe05303d7705Fa; +// } else { +// // For non-mainnet chains, you might want to deploy a mock or read from a config +// // This assumes you have a similar config setup as in M2_Deploy_From_Scratch.s.sol +// /// TODO: Handle Eth pos +// } + +// address eigenPodManagerImpl = address( +// new EigenPodManager( +// IETHPOSDeposit(ethPOSDeposit), +// IBeacon(result.eigenPodBeacon), +// IStrategyManager(result.strategyManager), +// IDelegationManager(result.delegationManager) +// ) +// ); + +// /// TODO: Get actual values +// uint32 CALCULATION_INTERVAL_SECONDS = 1 days; +// uint32 MAX_REWARDS_DURATION = 1 days; +// uint32 MAX_RETROACTIVE_LENGTH = 1; +// uint32 MAX_FUTURE_LENGTH = 1; +// uint32 GENESIS_REWARDS_TIMESTAMP = 10 days; +// address rewardsCoordinatorImpl = address( +// new RewardsCoordinator( +// IDelegationManager(result.delegationManager), +// IStrategyManager(result.strategyManager), +// CALCULATION_INTERVAL_SECONDS, +// MAX_REWARDS_DURATION, +// MAX_RETROACTIVE_LENGTH, +// MAX_FUTURE_LENGTH, +// GENESIS_REWARDS_TIMESTAMP +// ) +// ); + +// /// TODO: Get actual genesis time +// uint64 GENESIS_TIME = 1_564_000; + +// address eigenPodImpl = address( +// new EigenPod( +// IETHPOSDeposit(ethPOSDeposit), +// IEigenPodManager(result.eigenPodManager), +// GENESIS_TIME +// ) +// ); +// address eigenPodBeaconImpl = address(new UpgradeableBeacon(eigenPodImpl)); +// address baseStrategyImpl = +// address(new StrategyBase(IStrategyManager(result.strategyManager))); +// /// TODO: PauserRegistry isn't upgradeable +// address pauserRegistryImpl = address( +// new PauserRegistry( +// new address[](0), // Empty array for pausers +// proxyAdmin // ProxyAdmin as the unpauser +// ) +// ); + +// // Deploy and configure the strategy beacon +// result.strategyBeacon = address(new UpgradeableBeacon(baseStrategyImpl)); + +// // Upgrade contracts +// /// TODO: Get from config +// bytes memory upgradeCall = abi.encodeWithSelector( /// TODO: Fix abi.encodeCall was failing Cannot implicitly convert component at position 4 from "IStrategy[]" to "IStrategy[]" +// DelegationManager.initialize.selector, +// proxyAdmin, // initialOwner +// IPauserRegistry(result.pauserRegistry), // _pauserRegistry +// configData.delegationManager.initPausedStatus, // initialPausedStatus +// configData.delegationManager.minWithdrawalDelayBlocks, // _minWithdrawalDelayBlocks +// configData.delegationManager.strategies, // _strategies +// configData.delegationManager.withdrawalDelayBlocks // _withdrawalDelayBlocks +// ); +// UpgradeableProxyLib.upgradeAndCall( +// result.delegationManager, delegationManagerImpl, upgradeCall +// ); + +// // Upgrade StrategyManager contract +// upgradeCall = abi.encodeCall( +// StrategyManager.initialize, +// ( +// proxyAdmin, // initialOwner +// result.strategyFactory, // initialStrategyWhitelister +// IPauserRegistry(result.pauserRegistry), // _pauserRegistry +// configData.strategyManager.initPausedStatus // initialPausedStatus +// ) +// ); +// UpgradeableProxyLib.upgradeAndCall(result.strategyManager, strategyManagerImpl, upgradeCall); + +// // Upgrade StrategyFactory contract +// upgradeCall = abi.encodeCall( +// StrategyFactory.initialize, +// ( +// proxyAdmin, // initialOwner +// IPauserRegistry(result.pauserRegistry), // _pauserRegistry +// configData.strategyFactory.initPausedStatus, // initialPausedStatus +// IBeacon(result.strategyBeacon) +// ) +// ); +// UpgradeableProxyLib.upgradeAndCall(result.strategyFactory, strategyFactoryImpl, upgradeCall); + +// // Upgrade EigenPodManager contract +// upgradeCall = abi.encodeCall( +// EigenPodManager.initialize, +// ( +// proxyAdmin, // initialOwner +// IPauserRegistry(result.pauserRegistry), // _pauserRegistry +// configData.eigenPodManager.initPausedStatus // initialPausedStatus +// ) +// ); +// UpgradeableProxyLib.upgradeAndCall(result.eigenPodManager, eigenPodManagerImpl, upgradeCall); + +// // Upgrade AVSDirectory contract +// upgradeCall = abi.encodeCall( +// AVSDirectory.initialize, +// ( +// proxyAdmin, // initialOwner +// IPauserRegistry(result.pauserRegistry), // _pauserRegistry +// 0 // TODO: AVS Missing configinitialPausedStatus +// ) +// ); +// UpgradeableProxyLib.upgradeAndCall(result.avsDirectory, avsDirectoryImpl, upgradeCall); + +// // Upgrade RewardsCoordinator contract +// upgradeCall = abi.encodeCall( +// RewardsCoordinator.initialize, +// ( +// proxyAdmin, // initialOwner +// IPauserRegistry(result.pauserRegistry), // _pauserRegistry +// configData.rewardsCoordinator.initPausedStatus, // initialPausedStatus +// /// TODO: is there a setter and is this expected? +// address(0), // rewards updater +// uint32(configData.rewardsCoordinator.activationDelay), // _activationDelay +// uint16(configData.rewardsCoordinator.globalOperatorCommissionBips) // _globalCommissionBips +// ) +// ); +// UpgradeableProxyLib.upgradeAndCall( +// result.rewardsCoordinator, rewardsCoordinatorImpl, upgradeCall +// ); + +// // Upgrade EigenPod contract +// upgradeCall = abi.encodeCall( +// EigenPod.initialize, +// // TODO: Double check this +// (address(result.eigenPodManager)) // _podOwner +// ); +// UpgradeableProxyLib.upgradeAndCall(result.eigenPodBeacon, eigenPodImpl, upgradeCall); + +// return result; +// } + +// } diff --git a/test/utils/Greeter.sol b/test/utils/Greeter.sol deleted file mode 100644 index 35948a85..00000000 --- a/test/utils/Greeter.sol +++ /dev/null @@ -1,11 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.12; - -contract Greeter { - string public greeting; - - function initialize(string memory _greeting) public { - greeting = _greeting; - } -} - diff --git a/test/utils/GreeterProxiable.sol b/test/utils/GreeterProxiable.sol deleted file mode 100644 index e6a6e17c..00000000 --- a/test/utils/GreeterProxiable.sol +++ /dev/null @@ -1,65 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.12; - -interface IERC1822Proxiable { - function proxiableUUID() external view returns (bytes32); -} - -contract Proxiable { - bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; - - string public constant UPGRADE_INTERFACE_VERSION = "5.0.0"; - - function upgradeToAndCall(address newImplementation, bytes calldata data) external { - try IERC1822Proxiable(newImplementation).proxiableUUID() returns (bytes32 slot) { - if (slot != _IMPLEMENTATION_SLOT) { - revert("slot is unsupported as a uuid"); - } - _setImplementation(newImplementation); - if (data.length > 0) { - /** - * Note that using delegate call can make your implementation contract vulnerable if this function - * is not protected with the `onlyProxy` modifier. Again, this contract is for testing only, it is - * not safe for use in production. Instead, use the `UUPSUpgradeable` contract available in - * @openzeppelin/contracts-upgradeable - */ - /// @custom:oz-upgrades-unsafe-allow delegatecall - (bool success, ) = newImplementation.delegatecall(data); - require(success, "upgrade call reverted"); - } else { - _checkNonPayable(); - } - } catch { - revert("the implementation is not UUPS"); - } - } - - function proxiableUUID() external view virtual returns (bytes32) { - return _IMPLEMENTATION_SLOT; - } - - function _checkNonPayable() private { - if (msg.value > 0) { - revert("non-payable upgrade call"); - } - } - - function _setImplementation(address newImplementation) private { - bytes32 slot = _IMPLEMENTATION_SLOT; - // solhint-disable-next-line no-inline-assembly - assembly { - sstore(slot, newImplementation) - } - } -} - - -contract GreeterProxiable is Proxiable { - string public greeting; - - function initialize(string memory _greeting) public { - greeting = _greeting; - } -} - - diff --git a/test/utils/GreeterV2.sol b/test/utils/GreeterV2.sol deleted file mode 100644 index 15a2b9b8..00000000 --- a/test/utils/GreeterV2.sol +++ /dev/null @@ -1,14 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.12; - -contract GreeterV2 { - string public greeting; - - function initialize(string memory _greeting) public { - greeting = _greeting; - } - - function resetGreeting() public { - greeting = "resetted"; - } -} diff --git a/test/utils/GreeterV2Proxiable.sol b/test/utils/GreeterV2Proxiable.sol deleted file mode 100644 index ca2d82c9..00000000 --- a/test/utils/GreeterV2Proxiable.sol +++ /dev/null @@ -1,17 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.12; - -import {Proxiable} from "./GreeterProxiable.sol"; - -contract GreeterV2Proxiable is Proxiable { - string public greeting; - - function initialize(string memory _greeting) public { - greeting = _greeting; - } - - function resetGreeting() public { - greeting = "resetted"; - } -} - diff --git a/test/utils/MockAVSDeployer.sol b/test/utils/MockAVSDeployer.sol index 6db2781f..e527e0f8 100644 --- a/test/utils/MockAVSDeployer.sol +++ b/test/utils/MockAVSDeployer.sol @@ -1,11 +1,9 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; 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 {ISlasher} from "eigenlayer-contracts/src/contracts/interfaces/ISlasher.sol"; 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"; @@ -13,30 +11,43 @@ import {BitmapUtils} from "../../src/libraries/BitmapUtils.sol"; import {BN254} from "../../src/libraries/BN254.sol"; import {OperatorStateRetriever} from "../../src/OperatorStateRetriever.sol"; +import {SlashingRegistryCoordinator} from "../../src/SlashingRegistryCoordinator.sol"; import {RegistryCoordinator} from "../../src/RegistryCoordinator.sol"; import {RegistryCoordinatorHarness} from "../harnesses/RegistryCoordinatorHarness.t.sol"; import {BLSApkRegistry} from "../../src/BLSApkRegistry.sol"; import {ServiceManagerMock} from "../mocks/ServiceManagerMock.sol"; -import {StakeRegistry} from "../../src/StakeRegistry.sol"; +import {StakeRegistry, IStakeRegistryTypes} 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 { + ISlashingRegistryCoordinatorTypes, + ISlashingRegistryCoordinatorTypes +} from "../../src/interfaces/ISlashingRegistryCoordinator.sol"; + +import {ISlashingRegistryCoordinator} from "../../src/interfaces/ISlashingRegistryCoordinator.sol"; import {IServiceManager} from "../../src/interfaces/IServiceManager.sol"; import {SocketRegistry} from "../../src/SocketRegistry.sol"; import {StrategyManagerMock} from "eigenlayer-contracts/src/test/mocks/StrategyManagerMock.sol"; -import {EigenPodManagerMock} from "eigenlayer-contracts/src/test/mocks/EigenPodManagerMock.sol"; +import {EigenPodManagerMock} from "../mocks/EigenPodManagerMock.sol"; import {AVSDirectoryMock} from "../mocks/AVSDirectoryMock.sol"; +import {AllocationManagerMock} from "../mocks/AllocationManagerMock.sol"; import {DelegationMock} from "../mocks/DelegationMock.sol"; import {AVSDirectory} from "eigenlayer-contracts/src/contracts/core/AVSDirectory.sol"; import {IAVSDirectory} from "eigenlayer-contracts/src/contracts/interfaces/IAVSDirectory.sol"; import {RewardsCoordinatorMock} from "../mocks/RewardsCoordinatorMock.sol"; +import {PermissionControllerMock} from "../mocks/PermissionControllerMock.sol"; -import { RewardsCoordinator } from "eigenlayer-contracts/src/contracts/core/RewardsCoordinator.sol"; -import { IRewardsCoordinator } from "eigenlayer-contracts/src/contracts/interfaces/IRewardsCoordinator.sol"; +import {RewardsCoordinator} from "eigenlayer-contracts/src/contracts/core/RewardsCoordinator.sol"; +import {PermissionController} from + "eigenlayer-contracts/src/contracts/permissions/PermissionController.sol"; +import {AllocationManager} from "eigenlayer-contracts/src/contracts/core/AllocationManager.sol"; +import {IRewardsCoordinator} from + "eigenlayer-contracts/src/contracts/interfaces/IRewardsCoordinator.sol"; import {BLSApkRegistryHarness} from "../harnesses/BLSApkRegistryHarness.sol"; import {EmptyContract} from "eigenlayer-contracts/src/test/mocks/EmptyContract.sol"; @@ -53,25 +64,22 @@ contract MockAVSDeployer is Test { ProxyAdmin public proxyAdmin; PauserRegistry public pauserRegistry; - ISlasher public slasher = ISlasher(address(uint160(uint256(keccak256("slasher"))))); - Slasher public slasherImplementation; - EmptyContract public emptyContract; RegistryCoordinatorHarness public registryCoordinatorImplementation; StakeRegistryHarness public stakeRegistryImplementation; IBLSApkRegistry public blsApkRegistryImplementation; IIndexRegistry public indexRegistryImplementation; - SocketRegistry public socketRegistryImplementation; ServiceManagerMock public serviceManagerImplementation; + SocketRegistry public socketRegistryImplementation; OperatorStateRetriever public operatorStateRetriever; RegistryCoordinatorHarness public registryCoordinator; StakeRegistryHarness public stakeRegistry; BLSApkRegistryHarness public blsApkRegistry; IIndexRegistry public indexRegistry; - ServiceManagerMock public serviceManager; SocketRegistry public socketRegistry; + ServiceManagerMock public serviceManager; StrategyManagerMock public strategyManagerMock; DelegationMock public delegationMock; @@ -79,9 +87,13 @@ contract MockAVSDeployer is Test { AVSDirectory public avsDirectory; AVSDirectory public avsDirectoryImplementation; AVSDirectoryMock public avsDirectoryMock; + AllocationManagerMock public allocationManagerMock; + AllocationManager public allocationManager; + AllocationManager public allocationManagerImplementation; RewardsCoordinator public rewardsCoordinator; RewardsCoordinator public rewardsCoordinatorImplementation; RewardsCoordinatorMock public rewardsCoordinatorMock; + PermissionControllerMock public permissionControllerMock; /// @notice StakeRegistry, Constant used as a divisor in calculating weights. uint256 public constant WEIGHTING_DIVISOR = 1e18; @@ -101,19 +113,19 @@ contract MockAVSDeployer is Test { address defaultOperator = address(uint160(uint256(keccak256("defaultOperator")))); bytes32 defaultOperatorId; BN254.G1Point internal defaultPubKey = BN254.G1Point( - 18_260_007_818_883_133_054_078_754_218_619_977_578_772_505_796_600_400_998_181_738_095_793_040_006_897, - 3_432_351_341_799_135_763_167_709_827_653_955_074_218_841_517_684_851_694_584_291_831_827_675_065_899 + 18260007818883133054078754218619977578772505796600400998181738095793040006897, + 3432351341799135763167709827653955074218841517684851694584291831827675065899 ); string defaultSocket = "69.69.69.69:420"; uint96 defaultStake = 1 ether; uint8 defaultQuorumNumber = 0; uint32 defaultMaxOperatorCount = 10; - uint16 defaultKickBIPsOfOperatorStake = 15_000; + uint16 defaultKickBIPsOfOperatorStake = 15000; uint16 defaultKickBIPsOfTotalStake = 150; uint8 numQuorums = 192; - IRegistryCoordinator.OperatorSetParam[] operatorSetParams; + ISlashingRegistryCoordinatorTypes.OperatorSetParam[] operatorSetParams; uint8 maxQuorumsToRegisterFor = 4; uint256 maxOperatorsToRegister = 4; @@ -136,39 +148,24 @@ contract MockAVSDeployer is Test { _deployMockEigenLayerAndAVS(numQuorums); } - function _deployMockEigenLayerAndAVS(uint8 numQuorumsToAdd) internal { + function _deployMockEigenLayerAndAVS( + uint8 numQuorumsToAdd + ) internal { emptyContract = new EmptyContract(); - defaultOperatorId = defaultPubKey.hashG1Point(); cheats.startPrank(proxyAdminOwner); proxyAdmin = new ProxyAdmin(); - address[] memory pausers = new address[](1); pausers[0] = pauser; pauserRegistry = new PauserRegistry(pausers, unpauser); - delegationMock = new DelegationMock(); avsDirectoryMock = new AVSDirectoryMock(); eigenPodManagerMock = new EigenPodManagerMock(pauserRegistry); - strategyManagerMock = new StrategyManagerMock(); - slasherImplementation = new Slasher(strategyManagerMock, delegationMock); - slasher = Slasher( - address( - new TransparentUpgradeableProxy( - address(slasherImplementation), - address(proxyAdmin), - abi.encodeWithSelector( - Slasher.initialize.selector, - msg.sender, - pauserRegistry, - 0 /*initialPausedStatus*/ - ) - ) - ) - ); - avsDirectoryMock = new AVSDirectoryMock(); - avsDirectoryImplementation = new AVSDirectory(delegationMock); + strategyManagerMock = new StrategyManagerMock(delegationMock); + allocationManagerMock = new AllocationManagerMock(); + permissionControllerMock = new PermissionControllerMock(); + avsDirectoryImplementation = new AVSDirectory(delegationMock, pauserRegistry); // TODO: config value avsDirectory = AVSDirectory( address( new TransparentUpgradeableProxy( @@ -176,16 +173,14 @@ contract MockAVSDeployer is Test { address(proxyAdmin), abi.encodeWithSelector( AVSDirectory.initialize.selector, - msg.sender, - pauserRegistry, - 0 /*initialPausedStatus*/ + msg.sender, // initialOwner + 0 // initialPausedStatus ) ) ) ); rewardsCoordinatorMock = new RewardsCoordinatorMock(); - - strategyManagerMock.setAddresses(delegationMock, eigenPodManagerMock, slasher); + strategyManagerMock.setDelegationManager(delegationMock); cheats.stopPrank(); cheats.startPrank(registryCoordinatorOwner); @@ -194,44 +189,47 @@ contract MockAVSDeployer is Test { new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), "") ) ); - stakeRegistry = StakeRegistryHarness( address( new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), "") ) ); - - socketRegistry = SocketRegistry( + indexRegistry = IndexRegistry( address( new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), "") ) ); - - indexRegistry = IndexRegistry( + blsApkRegistry = BLSApkRegistryHarness( address( new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), "") ) ); - - blsApkRegistry = BLSApkRegistryHarness( + serviceManager = ServiceManagerMock( address( new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), "") ) ); - - serviceManager = ServiceManagerMock( + allocationManager = AllocationManager( address( new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), "") ) ); + socketRegistry = SocketRegistry( + address( + new TransparentUpgradeableProxy(address(emptyContract), address(proxyAdmin), "") + ) + ); cheats.stopPrank(); cheats.startPrank(proxyAdminOwner); - stakeRegistryImplementation = - new StakeRegistryHarness(IRegistryCoordinator(registryCoordinator), delegationMock); - + stakeRegistryImplementation = new StakeRegistryHarness( + ISlashingRegistryCoordinator(registryCoordinator), + delegationMock, + avsDirectory, + allocationManagerMock + ); proxyAdmin.upgrade( TransparentUpgradeableProxy(payable(address(stakeRegistry))), address(stakeRegistryImplementation) @@ -245,14 +243,12 @@ contract MockAVSDeployer is Test { ); blsApkRegistryImplementation = new BLSApkRegistryHarness(registryCoordinator); - proxyAdmin.upgrade( TransparentUpgradeableProxy(payable(address(blsApkRegistry))), address(blsApkRegistryImplementation) ); indexRegistryImplementation = new IndexRegistry(registryCoordinator); - proxyAdmin.upgrade( TransparentUpgradeableProxy(payable(address(indexRegistry))), address(indexRegistryImplementation) @@ -262,17 +258,30 @@ contract MockAVSDeployer is Test { avsDirectoryMock, IRewardsCoordinator(address(rewardsCoordinatorMock)), registryCoordinator, - stakeRegistry + stakeRegistry, + permissionControllerMock, + allocationManagerMock ); - proxyAdmin.upgrade( TransparentUpgradeableProxy(payable(address(serviceManager))), address(serviceManagerImplementation) ); + allocationManagerImplementation = new AllocationManager( + delegationMock, + pauserRegistry, + permissionControllerMock, + uint32(7 days), // DEALLOCATION_DELAY + uint32(1 days) // ALLOCATION_CONFIGURATION_DELAY + ); + proxyAdmin.upgrade( + TransparentUpgradeableProxy(payable(address(allocationManager))), + address(allocationManagerImplementation) + ); + serviceManager.initialize({ initialOwner: registryCoordinatorOwner, - rewardsInitiator: address(proxyAdminOwner) + rewardsInitiator: proxyAdminOwner }); // set the public key for an operator, using harnessed function to bypass checks @@ -285,24 +294,46 @@ contract MockAVSDeployer is Test { } // setup the dummy quorum strategies - IStakeRegistry.StrategyParams[][] memory quorumStrategiesConsideredAndMultipliers = - new IStakeRegistry.StrategyParams[][](numQuorumsToAdd); + IStakeRegistryTypes.StrategyParams[][] memory quorumStrategiesConsideredAndMultipliers = + new IStakeRegistryTypes.StrategyParams[][](numQuorumsToAdd); for (uint256 i = 0; i < quorumStrategiesConsideredAndMultipliers.length; i++) { - quorumStrategiesConsideredAndMultipliers[i] = new IStakeRegistry.StrategyParams[](1); - quorumStrategiesConsideredAndMultipliers[i][0] = IStakeRegistry.StrategyParams( + quorumStrategiesConsideredAndMultipliers[i] = + new IStakeRegistryTypes.StrategyParams[](1); + quorumStrategiesConsideredAndMultipliers[i][0] = IStakeRegistryTypes.StrategyParams( IStrategy(address(uint160(i))), uint96(WEIGHTING_DIVISOR) ); } registryCoordinatorImplementation = new RegistryCoordinatorHarness( - serviceManager, stakeRegistry, blsApkRegistry, indexRegistry, socketRegistry + serviceManager, + stakeRegistry, + blsApkRegistry, + indexRegistry, + socketRegistry, + allocationManagerMock, + pauserRegistry ); { + proxyAdmin.upgradeAndCall( + TransparentUpgradeableProxy(payable(address(registryCoordinator))), + address(registryCoordinatorImplementation), + abi.encodeCall( + SlashingRegistryCoordinator.initialize, + ( + registryCoordinatorOwner, // _initialOwner + churnApprover, // _churnApprover + ejector, // _ejector + 0, // _initialPausedStatus + address(serviceManager) // _accountIdentifier + ) + ) + ); + delete operatorSetParams; for (uint256 i = 0; i < numQuorumsToAdd; i++) { // hard code these for now operatorSetParams.push( - IRegistryCoordinator.OperatorSetParam({ + ISlashingRegistryCoordinatorTypes.OperatorSetParam({ maxOperatorCount: defaultMaxOperatorCount, kickBIPsOfOperatorStake: defaultKickBIPsOfOperatorStake, kickBIPsOfTotalStake: defaultKickBIPsOfTotalStake @@ -310,26 +341,48 @@ contract MockAVSDeployer is Test { ); } - proxyAdmin.upgradeAndCall( - TransparentUpgradeableProxy(payable(address(registryCoordinator))), - address(registryCoordinatorImplementation), - abi.encodeWithSelector( - RegistryCoordinator.initialize.selector, - registryCoordinatorOwner, - churnApprover, - ejector, - pauserRegistry, - 0, /*initialPausedStatus*/ - operatorSetParams, - minimumStakeForQuorum, - quorumStrategiesConsideredAndMultipliers - ) - ); + cheats.stopPrank(); + + // add TOTAL_DELEGATED stake type quorums + for (uint256 i = 0; i < numQuorumsToAdd; i++) { + cheats.prank(registryCoordinator.owner()); + registryCoordinator.createTotalDelegatedStakeQuorum( + operatorSetParams[i], + minimumStakeForQuorum[i], + quorumStrategiesConsideredAndMultipliers[i] + ); + } } operatorStateRetriever = new OperatorStateRetriever(); - cheats.stopPrank(); + registryCoordinator.setOperatorSetsEnabled(false); + registryCoordinator.setM2QuorumsDisabled(false); + } + + function _labelContracts() internal { + vm.label(address(emptyContract), "EmptyContract"); + vm.label(address(proxyAdmin), "ProxyAdmin"); + vm.label(address(pauserRegistry), "PauserRegistry"); + vm.label(address(delegationMock), "DelegationMock"); + vm.label(address(avsDirectoryMock), "AVSDirectoryMock"); + vm.label(address(eigenPodManagerMock), "EigenPodManagerMock"); + vm.label(address(strategyManagerMock), "StrategyManagerMock"); + vm.label(address(allocationManagerMock), "AllocationManagerMock"); + vm.label(address(avsDirectoryImplementation), "AVSDirectoryImplementation"); + vm.label(address(avsDirectory), "AVSDirectory"); + vm.label(address(rewardsCoordinatorMock), "RewardsCoordinatorMock"); + vm.label(address(registryCoordinator), "RegistryCoordinator"); + vm.label(address(stakeRegistry), "StakeRegistry"); + vm.label(address(indexRegistry), "IndexRegistry"); + vm.label(address(blsApkRegistry), "BLSApkRegistry"); + vm.label(address(serviceManager), "ServiceManager"); + vm.label(address(allocationManager), "AllocationManager"); + vm.label(address(stakeRegistryImplementation), "StakeRegistryImplementation"); + vm.label(address(blsApkRegistryImplementation), "BLSApkRegistryImplementation"); + vm.label(address(indexRegistryImplementation), "IndexRegistryImplementation"); + vm.label(address(serviceManagerImplementation), "ServiceManagerImplementation"); + vm.label(address(allocationManagerImplementation), "AllocationManagerImplementation"); } /** @@ -395,10 +448,9 @@ contract MockAVSDeployer is Test { ); } - function _registerRandomOperators(uint256 pseudoRandomNumber) - internal - returns (OperatorMetadata[] memory, uint256[][] memory) - { + function _registerRandomOperators( + uint256 pseudoRandomNumber + ) internal returns (OperatorMetadata[] memory, uint256[][] memory) { OperatorMetadata[] memory operatorMetadatas = new OperatorMetadata[](maxOperatorsToRegister); for (uint256 i = 0; i < operatorMetadatas.length; i++) { // limit to 16 quorums so we don't run out of gas, make them all register for quorum 0 as well @@ -481,7 +533,7 @@ contract MockAVSDeployer is Test { function _signOperatorChurnApproval( address registeringOperator, bytes32 registeringOperatorId, - IRegistryCoordinator.OperatorKickParam[] memory operatorKickParams, + ISlashingRegistryCoordinator.OperatorKickParam[] memory operatorKickParams, bytes32 salt, uint256 expiry ) internal view returns (ISignatureUtils.SignatureWithSaltAndExpiry memory) { diff --git a/test/utils/NoInitializer.sol b/test/utils/NoInitializer.sol deleted file mode 100644 index e70616ae..00000000 --- a/test/utils/NoInitializer.sol +++ /dev/null @@ -1,13 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.12; - -contract NoInitializer { - /// @custom:oz-upgrades-unsafe-allow state-variable-immutable - uint256 public immutable a; - - /// @custom:oz-upgrades-unsafe-allow constructor - constructor(uint256 _a) { - a = _a; - } -} - diff --git a/test/utils/Operators.sol b/test/utils/Operators.sol index 95ab65e9..05a2ad78 100644 --- a/test/utils/Operators.sol +++ b/test/utils/Operators.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "../../src/libraries/BN254.sol"; import "forge-std/Test.sol"; @@ -12,19 +12,26 @@ contract Operators is Test { operatorConfigJson = vm.readFile("./src/test/test-data/operators.json"); } - function operatorPrefix(uint256 index) public pure returns(string memory) { + function operatorPrefix( + uint256 index + ) public pure returns (string memory) { return string.concat(".operators[", string.concat(vm.toString(index), "].")); } - function getNumOperators() public view returns(uint256) { + function getNumOperators() public view returns (uint256) { return stdJson.readUint(operatorConfigJson, ".numOperators"); } - function getOperatorAddress(uint256 index) public view returns(address) { - return stdJson.readAddress(operatorConfigJson, string.concat(operatorPrefix(index), "Address")); + function getOperatorAddress( + uint256 index + ) public view returns (address) { + return + stdJson.readAddress(operatorConfigJson, string.concat(operatorPrefix(index), "Address")); } - function getOperatorSchnorrSignature(uint256 index) public view 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 +40,15 @@ contract Operators is Test { return (s, pubkey); } - function getOperatorSecretKey(uint256 index) public view returns(uint256) { + function getOperatorSecretKey( + uint256 index + ) public view returns (uint256) { return readUint(operatorConfigJson, index, "SecretKey"); } - function getOperatorPubkeyG1(uint256 index) public view 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,35 +56,46 @@ contract Operators is Test { return pubkey; } - function getOperatorPubkeyG2(uint256 index) public view 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"), readUint(operatorConfigJson, index, "PubkeyG2.X.A0") - ], + ], Y: [ readUint(operatorConfigJson, index, "PubkeyG2.Y.A1"), readUint(operatorConfigJson, index, "PubkeyG2.Y.A0") ] - }); + }); return pubkey; } - function readUint(string memory json, uint256 index, string memory key) public pure 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))); } - function stringToUint(string memory s) public pure returns (uint256) { + function stringToUint( + string memory s + ) public pure returns (uint256) { bytes memory b = bytes(s); uint256 result = 0; for (uint256 i = 0; i < b.length; i++) { if (uint256(uint8(b[i])) >= 48 && uint256(uint8(b[i])) <= 57) { - result = result * 10 + (uint256(uint8(b[i])) - 48); + result = result * 10 + (uint256(uint8(b[i])) - 48); } } return result; } - function setOperatorJsonFilePath(string memory filepath) public { + + function setOperatorJsonFilePath( + string memory filepath + ) public { operatorConfigJson = vm.readFile(filepath); } } diff --git a/test/utils/Owners.sol b/test/utils/Owners.sol index edb577d4..88155fa3 100644 --- a/test/utils/Owners.sol +++ b/test/utils/Owners.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "forge-std/Test.sol"; import "forge-std/Script.sol"; @@ -13,35 +13,40 @@ contract Owners is Test { ownersConfigJson = vm.readFile("./src/test/test-data/owners.json"); } - function ownerPrefix(uint256 index) public pure returns(string memory) { + function ownerPrefix( + uint256 index + ) public pure returns (string memory) { return string.concat(".owners[", string.concat(vm.toString(index), "].")); } - function getNumOperators() public view returns(uint256) { + function getNumOperators() public view returns (uint256) { return stdJson.readUint(ownersConfigJson, ".numOwners"); } - function getOwnerAddress(uint256 index) public view returns(address) { + function getOwnerAddress( + uint256 index + ) public view returns (address) { return stdJson.readAddress(ownersConfigJson, string.concat(ownerPrefix(index), "Address")); } - function getOwnerAddresses() public returns(address[] memory) { + function getOwnerAddresses() public returns (address[] memory) { for (uint256 i = 0; i < getNumOperators(); i++) { addresses.push(getOwnerAddress(i)); } - return addresses; + return addresses; } - function getReputedOwnerAddresses() public returns(address[] memory) { + function getReputedOwnerAddresses() public returns (address[] memory) { resetOwnersConfigJson("reputedOwners.json"); for (uint256 i = 0; i < getNumOperators(); i++) { addresses.push(getOwnerAddress(i)); } - return addresses; + return addresses; } - function resetOwnersConfigJson(string memory newConfig) public { + function resetOwnersConfigJson( + string memory newConfig + ) public { ownersConfigJson = vm.readFile(newConfig); } - } diff --git a/test/utils/ProofParsing.sol b/test/utils/ProofParsing.sol index f331c2a1..cec8283f 100644 --- a/test/utils/ProofParsing.sol +++ b/test/utils/ProofParsing.sol @@ -1,12 +1,11 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "../../src/libraries/BN254.sol"; import "forge-std/Test.sol"; import "forge-std/StdJson.sol"; - -contract ProofParsing is Test{ +contract ProofParsing is Test { string internal proofConfigJson; string prefix; @@ -16,186 +15,188 @@ contract ProofParsing is Test{ bytes32[46] validatorProof; bytes32[44] historicalSummaryProof; - - bytes32[7] executionPayloadProof; bytes32[4] timestampProofs; - bytes32 slotRoot; bytes32 executionPayloadRoot; - function setJSON(string memory path) public { + function setJSON( + string memory path + ) public { proofConfigJson = vm.readFile(path); } - function getSlot() public view returns(uint256) { + function getSlot() public view returns (uint256) { return stdJson.readUint(proofConfigJson, ".slot"); } - function getValidatorIndex() public view returns(uint256) { + function getValidatorIndex() public view returns (uint256) { return stdJson.readUint(proofConfigJson, ".validatorIndex"); } - function getValidatorPubkeyHash() public view returns(bytes32) { + function getValidatorPubkeyHash() public view returns (bytes32) { return stdJson.readBytes32(proofConfigJson, ".ValidatorFields[0]"); } - function getWithdrawalIndex() public view returns(uint256) { + function getWithdrawalIndex() public view returns (uint256) { return stdJson.readUint(proofConfigJson, ".withdrawalIndex"); } - function getBlockRootIndex() public view returns(uint256) { + function getBlockRootIndex() public view returns (uint256) { return stdJson.readUint(proofConfigJson, ".blockHeaderRootIndex"); } - function getHistoricalSummaryIndex() public view returns(uint256) { + function getHistoricalSummaryIndex() public view returns (uint256) { return stdJson.readUint(proofConfigJson, ".historicalSummaryIndex"); } - function getBeaconStateRoot() public view returns(bytes32) { + function getBeaconStateRoot() public view returns (bytes32) { return stdJson.readBytes32(proofConfigJson, ".beaconStateRoot"); } - function getBlockRoot() public view returns(bytes32) { + function getBlockRoot() public view returns (bytes32) { return stdJson.readBytes32(proofConfigJson, ".blockHeaderRoot"); } - function getSlotRoot() public view returns(bytes32) { + function getSlotRoot() public view returns (bytes32) { return stdJson.readBytes32(proofConfigJson, ".slotRoot"); } - function getBalanceRoot() public view returns(bytes32) { + function getBalanceRoot() public view returns (bytes32) { return stdJson.readBytes32(proofConfigJson, ".balanceRoot"); } - function getTimestampRoot() public view returns(bytes32) { + function getTimestampRoot() public view returns (bytes32) { return stdJson.readBytes32(proofConfigJson, ".timestampRoot"); } - function getExecutionPayloadRoot() public view returns(bytes32) { + function getExecutionPayloadRoot() public view returns (bytes32) { return stdJson.readBytes32(proofConfigJson, ".executionPayloadRoot"); } - function getLatestBlockRoot() public view returns(bytes32) { + function getLatestBlockRoot() public view returns (bytes32) { return stdJson.readBytes32(proofConfigJson, ".latestBlockHeaderRoot"); } - function getExecutionPayloadProof () public returns(bytes32[7] memory) { - for (uint i = 0; i < 7; i++) { + + function getExecutionPayloadProof() public returns (bytes32[7] memory) { + for (uint256 i = 0; i < 7; i++) { prefix = string.concat(".ExecutionPayloadProof[", string.concat(vm.toString(i), "]")); - executionPayloadProof[i] = (stdJson.readBytes32(proofConfigJson, prefix)); + executionPayloadProof[i] = (stdJson.readBytes32(proofConfigJson, prefix)); } return executionPayloadProof; } - function getTimestampProof() public returns(bytes32[4] memory) { - for (uint i = 0; i < 4; i++) { + function getTimestampProof() public returns (bytes32[4] memory) { + for (uint256 i = 0; i < 4; i++) { prefix = string.concat(".TimestampProof[", string.concat(vm.toString(i), "]")); - timestampProofs[i] = (stdJson.readBytes32(proofConfigJson, prefix)); + timestampProofs[i] = (stdJson.readBytes32(proofConfigJson, prefix)); } return timestampProofs; } - function getBlockHeaderProof() public returns(bytes32[18] memory) { - for (uint i = 0; i < 18; i++) { + function getBlockHeaderProof() public returns (bytes32[18] memory) { + for (uint256 i = 0; i < 18; i++) { prefix = string.concat(".BlockHeaderProof[", string.concat(vm.toString(i), "]")); - blockHeaderProof[i] = (stdJson.readBytes32(proofConfigJson, prefix)); + blockHeaderProof[i] = (stdJson.readBytes32(proofConfigJson, prefix)); } return blockHeaderProof; } - function getSlotProof() public returns(bytes32[3] memory) { - for (uint i = 0; i < 3; i++) { + function getSlotProof() public returns (bytes32[3] memory) { + for (uint256 i = 0; i < 3; i++) { prefix = string.concat(".SlotProof[", string.concat(vm.toString(i), "]")); - slotProof[i] = (stdJson.readBytes32(proofConfigJson, prefix)); + slotProof[i] = (stdJson.readBytes32(proofConfigJson, prefix)); } return slotProof; } - function getStateRootProof() public returns(bytes32[] memory) { + function getStateRootProof() public returns (bytes32[] memory) { bytes32[] memory stateRootProof = new bytes32[](3); - for (uint i = 0; i < 3; i++) { - prefix = string.concat(".StateRootAgainstLatestBlockHeaderProof[", string.concat(vm.toString(i), "]")); - stateRootProof[i] = (stdJson.readBytes32(proofConfigJson, prefix)); + for (uint256 i = 0; i < 3; i++) { + prefix = string.concat( + ".StateRootAgainstLatestBlockHeaderProof[", string.concat(vm.toString(i), "]") + ); + stateRootProof[i] = (stdJson.readBytes32(proofConfigJson, prefix)); } return stateRootProof; } - function getWithdrawalProof() public returns(bytes32[9] memory) { - for (uint i = 0; i < 9; i++) { + function getWithdrawalProof() public returns (bytes32[9] memory) { + for (uint256 i = 0; i < 9; i++) { prefix = string.concat(".WithdrawalProof[", string.concat(vm.toString(i), "]")); - withdrawalProof[i] = (stdJson.readBytes32(proofConfigJson, prefix)); + withdrawalProof[i] = (stdJson.readBytes32(proofConfigJson, prefix)); } return withdrawalProof; } - function getValidatorProof() public returns(bytes32[46] memory) { - for (uint i = 0; i < 46; i++) { + function getValidatorProof() public returns (bytes32[46] memory) { + for (uint256 i = 0; i < 46; i++) { prefix = string.concat(".ValidatorProof[", string.concat(vm.toString(i), "]")); - validatorProof[i] = (stdJson.readBytes32(proofConfigJson, prefix)); + validatorProof[i] = (stdJson.readBytes32(proofConfigJson, prefix)); } return validatorProof; } - function getHistoricalSummaryProof() public returns(bytes32[44] memory) { - for (uint i = 0; i < 44; i++) { + function getHistoricalSummaryProof() public returns (bytes32[44] memory) { + for (uint256 i = 0; i < 44; i++) { prefix = string.concat(".HistoricalSummaryProof[", string.concat(vm.toString(i), "]")); - historicalSummaryProof[i] = (stdJson.readBytes32(proofConfigJson, prefix)); + historicalSummaryProof[i] = (stdJson.readBytes32(proofConfigJson, prefix)); } return historicalSummaryProof; } - - function getWithdrawalFields() public returns(bytes32[] memory) { + + function getWithdrawalFields() public returns (bytes32[] memory) { bytes32[] memory withdrawalFields = new bytes32[](4); - for (uint i = 0; i < 4; i++) { + for (uint256 i = 0; i < 4; i++) { prefix = string.concat(".WithdrawalFields[", string.concat(vm.toString(i), "]")); - withdrawalFields[i] = (stdJson.readBytes32(proofConfigJson, prefix)); + withdrawalFields[i] = (stdJson.readBytes32(proofConfigJson, prefix)); } - return withdrawalFields; - + return withdrawalFields; } - function getValidatorFields() public returns(bytes32[] memory) { + function getValidatorFields() public returns (bytes32[] memory) { bytes32[] memory validatorFields = new bytes32[](8); - for (uint i = 0; i < 8; i++) { + for (uint256 i = 0; i < 8; i++) { prefix = string.concat(".ValidatorFields[", string.concat(vm.toString(i), "]")); - validatorFields[i] = (stdJson.readBytes32(proofConfigJson, prefix)); + validatorFields[i] = (stdJson.readBytes32(proofConfigJson, prefix)); } return validatorFields; } - function getValidatorBalanceProof() public returns(bytes32[] memory) { + function getValidatorBalanceProof() public returns (bytes32[] memory) { bytes32[] memory validatorBalanceProof = new bytes32[](44); - for (uint i = 0; i < 44; i++) { + for (uint256 i = 0; i < 44; i++) { prefix = string.concat(".ValidatorBalanceProof[", string.concat(vm.toString(i), "]")); - validatorBalanceProof[i] = (stdJson.readBytes32(proofConfigJson, prefix)); + validatorBalanceProof[i] = (stdJson.readBytes32(proofConfigJson, prefix)); } return validatorBalanceProof; } - function getBalanceUpdateSlotProof() public returns(bytes32[] memory) { + function getBalanceUpdateSlotProof() public returns (bytes32[] memory) { bytes32[] memory balanceUpdateSlotProof = new bytes32[](5); - for (uint i = 0; i < 5; i++) { + for (uint256 i = 0; i < 5; i++) { prefix = string.concat(".slotProof[", string.concat(vm.toString(i), "]")); - balanceUpdateSlotProof[i] = (stdJson.readBytes32(proofConfigJson, prefix)); + balanceUpdateSlotProof[i] = (stdJson.readBytes32(proofConfigJson, prefix)); } return balanceUpdateSlotProof; } - function getWithdrawalCredentialProof() public returns(bytes32[] memory) { + function getWithdrawalCredentialProof() public returns (bytes32[] memory) { bytes32[] memory withdrawalCredenitalProof = new bytes32[](46); - for (uint i = 0; i < 46; i++) { - prefix = string.concat(".WithdrawalCredentialProof[", string.concat(vm.toString(i), "]")); - withdrawalCredenitalProof[i] = (stdJson.readBytes32(proofConfigJson, prefix)); + for (uint256 i = 0; i < 46; i++) { + prefix = + string.concat(".WithdrawalCredentialProof[", string.concat(vm.toString(i), "]")); + withdrawalCredenitalProof[i] = (stdJson.readBytes32(proofConfigJson, prefix)); } return withdrawalCredenitalProof; } - function getValidatorFieldsProof() public returns(bytes32[] memory) { + function getValidatorFieldsProof() public returns (bytes32[] memory) { bytes32[] memory validatorFieldsProof = new bytes32[](46); - for (uint i = 0; i < 46; i++) { + for (uint256 i = 0; i < 46; i++) { prefix = string.concat(".ValidatorFieldsProof[", string.concat(vm.toString(i), "]")); - validatorFieldsProof[i] = (stdJson.readBytes32(proofConfigJson, prefix)); + validatorFieldsProof[i] = (stdJson.readBytes32(proofConfigJson, prefix)); } return validatorFieldsProof; } diff --git a/test/utils/ProxyTestContracts.sol b/test/utils/ProxyTestContracts.sol deleted file mode 100644 index af2bc907..00000000 --- a/test/utils/ProxyTestContracts.sol +++ /dev/null @@ -1,9 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.12; - -import {Greeter} from "./Greeter.sol"; -import {GreeterProxiable} from "./GreeterProxiable.sol"; -import {GreeterV2} from "./GreeterV2.sol"; -import {GreeterV2Proxiable} from "./GreeterV2Proxiable.sol"; -import {WithConstructor} from "./WithConstructor.sol"; -import {NoInitializer} from "./NoInitializer.sol"; diff --git a/test/utils/SignatureCompaction.sol b/test/utils/SignatureCompaction.sol index c10b2db1..eac87690 100644 --- a/test/utils/SignatureCompaction.sol +++ b/test/utils/SignatureCompaction.sol @@ -1,11 +1,12 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.12; +pragma solidity ^0.8.27; import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; //small library for dealing with efficiently-packed signatures, where parameters v,r,s are packed into vs and r (64 bytes instead of 65) library SignatureCompaction { - bytes32 internal constant HALF_CURVE_ORDER = 0x7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0; + bytes32 internal constant HALF_CURVE_ORDER = + 0x7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0; function ecrecoverPacked(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) { (address recovered, ECDSA.RecoverError err) = ECDSA.tryRecover(hash, r, vs); @@ -13,7 +14,11 @@ library SignatureCompaction { return recovered; } - function packSignature(bytes32 r, bytes32 s, uint8 v) internal pure returns (bytes32, bytes32) { + function packSignature( + bytes32 r, + bytes32 s, + uint8 v + ) internal pure returns (bytes32, bytes32) { require(s <= HALF_CURVE_ORDER, "malleable signature, s too high"); //v parity is a single bit, encoded as either v = 27 or v = 28 -- in order to recover the bit we subtract 27 bytes32 vs = bytes32(uint256(bytes32(uint256(v) - 27) << 255) | uint256(s)); diff --git a/test/utils/UpgradeableProxyUtils.sol b/test/utils/UpgradeableProxyUtils.sol deleted file mode 100644 index 6ce5471a..00000000 --- a/test/utils/UpgradeableProxyUtils.sol +++ /dev/null @@ -1,241 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.12; - -import {Vm} from "forge-std/Vm.sol"; -import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; -import {ProxyAdmin} from "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; -import {UpgradeableBeacon} from "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol"; -import {BeaconProxy} from "@openzeppelin/contracts/proxy/beacon/BeaconProxy.sol"; -import {TransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; - -/// Modified from the Openzeppelin foundry upgrades library -/// Modifications: -/// - Made compatible with OZ ^4.x releases -/// - Removed OZ Defender functionality -library UpgradeableProxyUtils { - address private constant CHEATCODE_ADDRESS = 0x7109709ECfa91a80626fF3989D68f67F5b1DD12D; - - // This is the keccak-256 hash of "eip1967.proxy.rollback" subtracted by 1 - bytes32 private constant _ROLLBACK_SLOT = 0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143; - - /** - * @dev Storage slot with the address of the current implementation. - * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is - * validated in the constructor. - */ - bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; - - /** - * @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy. - * This is bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1)) and is validated in the constructor. - */ - bytes32 internal constant _BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50; - - /** - * @dev Storage slot with the admin of the contract. - * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is - * validated in the constructor. - */ - bytes32 internal constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103; - - Vm private constant vm = Vm(CHEATCODE_ADDRESS); - - /** - * @dev Deploys a transparent proxy using the given contract as the implementation. - * - * @param contractName Name of the contract to use as the implementation, e.g. "MyContract.sol" or "MyContract.sol:MyContract" or artifact path relative to the project root directory - * @param initialOwner Address to set as the owner of the ProxyAdmin contract which gets deployed by the proxy - * @param initializerData Encoded call data of the initializer function to call during creation of the proxy, or empty if no initialization is required - * @return Proxy address - */ - function deployTransparentProxy( - string memory contractName, - address initialOwner, - bytes memory initializerData - ) internal returns (address) { - return deployTransparentProxy(contractName, initialOwner, initializerData, ""); - } - - /** - * @dev Deploys a transparent proxy using the given contract as the implementation. - * - * @param contractName Name of the contract to use as the implementation, e.g. "MyContract.sol" or "MyContract.sol:MyContract" or artifact path relative to the project root directory - * @param initialOwner Address to set as the owner of the ProxyAdmin contract which gets deployed by the proxy - * @param initializerData Encoded call data of the initializer function to call during creation of the proxy, or empty if no initialization is required - * @return Proxy address - */ - function deployTransparentProxy( - string memory contractName, - address initialOwner, - bytes memory initializerData, - bytes memory implConstructorArgs - ) internal returns (address) { - address impl = deployImplementation(contractName, implConstructorArgs); - return - address( - _deploy( - "TransparentUpgradeableProxy.sol:TransparentUpgradeableProxy", - abi.encode(impl, initialOwner, initializerData) - ) - ); - } - - /** - * @dev Deploys an upgradeable beacon using the given contract as the implementation. - * - * @param contractName Name of the contract to use as the implementation, e.g. "MyContract.sol" or "MyContract.sol:MyContract" or artifact path relative to the project root directory - * @param initialOwner Address to set as the owner of the UpgradeableBeacon contract which gets deployed - * @return Beacon address - */ - function deployBeacon( - string memory contractName, - address initialOwner, - bytes memory implConstructorArgs - ) internal returns (address) { - address impl = deployImplementation(contractName, implConstructorArgs); - return _deploy("UpgradeableBeacon.sol:UpgradeableBeacon", abi.encode(impl, initialOwner)); - } - - /** - * @dev Deploys a beacon proxy using the given beacon and call data. - * - * @param beacon Address of the beacon to use - * @param data Encoded call data of the initializer function to call during creation of the proxy, or empty if no initialization is required - * @return Proxy address - */ - function deployBeaconProxy(address beacon, bytes memory data) internal returns (address) { - return _deploy("BeaconProxy.sol:BeaconProxy", abi.encode(beacon, data)); - } - - /** - * @dev Validates and deploys an implementation contract, and returns its address. - * - * @param contractName Name of the contract to deploy, e.g. "MyContract.sol" or "MyContract.sol:MyContract" or artifact path relative to the project root directory - * @return Address of the implementation contract - */ - function deployImplementation(string memory contractName, bytes memory implConstructorArgs) internal returns (address) { - return _deploy(contractName, implConstructorArgs); - } - /** - * @dev Gets the admin address of a transparent proxy from its ERC1967 admin storage slot. - * @param proxy Address of a transparent proxy - * @return Admin address - */ - function getAdminAddress(address proxy) internal view returns (address) { - bytes32 adminSlot = vm.load(proxy, _ADMIN_SLOT); - return address(uint160(uint256(adminSlot))); - } - - /** - * @dev Gets the implementation address of a transparent or UUPS proxy from its ERC1967 implementation storage slot. - * @param proxy Address of a transparent or UUPS proxy - * @return Implementation address - */ - function getImplementationAddress(address proxy) internal view returns (address) { - bytes32 implSlot = vm.load(proxy, _IMPLEMENTATION_SLOT); - return address(uint160(uint256(implSlot))); - } - - /** - * @dev Gets the beacon address of a beacon proxy from its ERC1967 beacon storage slot. - * @param proxy Address of a beacon proxy - * @return Beacon address - */ - function getBeaconAddress(address proxy) internal view returns (address) { - bytes32 beaconSlot = vm.load(proxy, _BEACON_SLOT); - return address(uint160(uint256(beaconSlot))); - } - - /** - * @dev Upgrades a proxy to a new implementation contract. - * @param proxy Address of the proxy to upgrade - * @param contractName Name of the new implementation contract to upgrade to, e.g. "MyContract.sol" or "MyContract.sol:MyContract" or artifact path relative to the project root directory - * @param data Encoded call data of an arbitrary function to call during the upgrade process, or empty if no function needs to be called during the upgrade - * @param implConstructorArgs abi encoded constructor arguments for deploying the implementation contract - */ - function upgradeProxy( - address proxy, - string memory contractName, - bytes memory data, - bytes memory implConstructorArgs - ) internal { - address newImpl = _deploy(contractName, implConstructorArgs); - - bytes32 adminSlot = vm.load(proxy, _ADMIN_SLOT); - if (adminSlot == bytes32(0)) { - // No admin contract: upgrade directly using interface - TransparentUpgradeableProxy(payable(proxy)).upgradeToAndCall(newImpl, data); - } else { - ProxyAdmin admin = ProxyAdmin(address(uint160(uint256(adminSlot)))); - admin.upgradeAndCall(TransparentUpgradeableProxy(payable(proxy)), newImpl, data); - } - } - - /** - * @dev Upgrades a proxy to a new implementation contract. - * @param proxy Address of the proxy to upgrade - * @param contractName Name of the new implementation contract to upgrade to, e.g. "MyContract.sol" or "MyContract.sol:MyContract" or artifact path relative to the project root directory - * @param data Encoded call data of an arbitrary function to call during the upgrade process, or empty if no function needs to be called during the upgrade - */ - function upgradeProxy(address proxy, string memory contractName, bytes memory data) internal { - upgradeProxy(proxy, contractName, data, ""); - } - - /** - * @dev Upgrades a beacon to a new implementation contract. - * @param beacon Address of the beacon to upgrade - * @param contractName Name of the new implementation contract to upgrade to, e.g. "MyContract.sol" or "MyContract.sol:MyContract" or artifact path relative to the project root directory - * @param implConstructorArgs abi encoded constructor arguments for deploying the implementation contract - */ - function upgradeBeacon(address beacon, string memory contractName, bytes memory implConstructorArgs) internal { - address newImpl = _deploy(contractName, implConstructorArgs); - UpgradeableBeacon(beacon).upgradeTo(newImpl); - } - - /* - * @param beacon Address of the beacon to upgrade - * @param contractName Name of the new implementation contract to upgrade to, e.g. "MyContract.sol" or "MyContract.sol:MyContract" or artifact path relative to the project root directory - */ - function upgradeBeacon(address beacon, string memory contractName) internal { - upgradeBeacon(beacon, contractName, ""); - } - - function _deploy(string memory contractName, bytes memory implConstructorArgs) private returns (address) { - bytes memory creationCode = Vm(CHEATCODE_ADDRESS).getCode(contractName); - address deployedAddress = _deployFromBytecode(abi.encodePacked(creationCode, implConstructorArgs)); - if (deployedAddress == address(0)) { - revert( - string.concat( - "Failed to deploy contract ", - contractName, - ' using constructor data "', - string(implConstructorArgs), - '"' - ) - ); - } - return deployedAddress; - } - - function _deployFromBytecode(bytes memory bytecode) private returns (address) { - address addr; - assembly { - addr := create(0, add(bytecode, 32), mload(bytecode)) - } - return addr; - } - - /** - * @dev Precompile proxy contracts so that they can be deployed by name via the `_deploy` function. - * - * NOTE: This function is never called and has no effect, but must be kept to ensure that the proxy contracts are included in the compilation. - */ - function _precompileProxyContracts() private pure { - bytes memory dummy; - dummy = type(ERC1967Proxy).creationCode; - dummy = type(TransparentUpgradeableProxy).creationCode; - dummy = type(ProxyAdmin).creationCode; - dummy = type(UpgradeableBeacon).creationCode; - dummy = type(BeaconProxy).creationCode; - } -} diff --git a/test/utils/UpgradeableProxyUtils.t.sol b/test/utils/UpgradeableProxyUtils.t.sol deleted file mode 100644 index f0197078..00000000 --- a/test/utils/UpgradeableProxyUtils.t.sol +++ /dev/null @@ -1,87 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.12; - -import {Test, console} from "forge-std/Test.sol"; -import {Vm} from "forge-std/Vm.sol"; - -import {UpgradeableProxyUtils} from "./UpgradeableProxyUtils.sol"; -import {Proxy} from "@openzeppelin/contracts/proxy/Proxy.sol"; -import {ProxyAdmin} from "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; -import {IBeacon} from "@openzeppelin/contracts/proxy/beacon/IBeacon.sol"; -import {Greeter, GreeterV2, NoInitializer, WithConstructor, GreeterProxiable, GreeterV2Proxiable} from "./ProxyTestContracts.sol"; - -contract UpgradeableProxyUtilsTest is Test { - ProxyAdmin internal admin; - - function setUp() public { - admin = new ProxyAdmin(); - } - - function testTransparent() public { - address proxy = UpgradeableProxyUtils.deployTransparentProxy( - "Greeter.sol", - address(admin), - abi.encodeCall(Greeter.initialize, ("hello")) - ); - Greeter instance = Greeter(proxy); - address implAddressV1 = UpgradeableProxyUtils.getImplementationAddress(proxy); - address adminAddress = UpgradeableProxyUtils.getAdminAddress(proxy); - - assertFalse(adminAddress == address(0)); - assertEq(instance.greeting(), "hello"); - - UpgradeableProxyUtils.upgradeProxy(proxy, "GreeterV2.sol", abi.encodeCall(GreeterV2.resetGreeting, ())); - - address implAddressV2 = UpgradeableProxyUtils.getImplementationAddress(proxy); - - assertEq(UpgradeableProxyUtils.getAdminAddress(proxy), adminAddress); - assertEq(instance.greeting(), "resetted"); - assertFalse(implAddressV2 == implAddressV1); - } - - function testBeacon() public { - address beacon = UpgradeableProxyUtils.deployBeacon("Greeter.sol", address(admin), abi.encode()); - address implAddressV1 = IBeacon(beacon).implementation(); - - address proxy = UpgradeableProxyUtils.deployBeaconProxy(beacon, abi.encodeCall(Greeter.initialize, ("hello"))); - Greeter instance = Greeter(proxy); - - assertEq(UpgradeableProxyUtils.getBeaconAddress(proxy), beacon); - assertEq(instance.greeting(), "hello"); - - UpgradeableProxyUtils.upgradeBeacon(beacon, "GreeterV2.sol"); - address implAddressV2 = IBeacon(beacon).implementation(); - - GreeterV2(address(instance)).resetGreeting(); - - assertEq(instance.greeting(), "resetted"); - assertFalse(implAddressV2 == implAddressV1); - } - - function testUpgradeBeaconWithoutCaller() public { - address beacon = UpgradeableProxyUtils.deployBeacon("Greeter.sol", address(admin), abi.encode()); - UpgradeableProxyUtils.upgradeBeacon(beacon, "GreeterV2.sol", abi.encode()); - } - - function testWithConstructor() public { - bytes memory constructorData = abi.encode(123); - address proxy = UpgradeableProxyUtils.deployTransparentProxy( - "WithConstructor.sol", - msg.sender, - abi.encodeCall(WithConstructor.initialize, (456)), - constructorData - ); - - assertEq(WithConstructor(proxy).a(), 123); - assertEq(WithConstructor(proxy).b(), 456); - } - - function testNoInitializer() public { - /// Can access getCode by File:Contract - bytes memory constructorData = abi.encode(123); - address proxy = UpgradeableProxyUtils.deployTransparentProxy("NoInitializer.sol", msg.sender, "", constructorData); - - assertEq(WithConstructor(proxy).a(), 123); - } - -} diff --git a/test/utils/WithConstructor.sol b/test/utils/WithConstructor.sol deleted file mode 100644 index 146b3ecd..00000000 --- a/test/utils/WithConstructor.sol +++ /dev/null @@ -1,19 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.12; - -contract WithConstructor { - /// @custom:oz-upgrades-unsafe-allow state-variable-immutable - uint256 public immutable a; - - uint256 public b; - - /// @custom:oz-upgrades-unsafe-allow constructor - constructor(uint256 _a) { - a = _a; - } - - function initialize(uint256 _b) public { - b = _b; - } -} -