Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: Add AVS/Operator Registration Support in RegistryCoordinator #99

Merged
merged 6 commits into from
Dec 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lib/eigenlayer-contracts
Submodule eigenlayer-contracts updated 66 files
+2 −2 .github/workflows/certora-prover.yml
+6 −1 .gitignore
+1 −0 .solhintignore
+54 −54 README.md
+40 −0 certora/harnesses/EigenPodHarness.sol
+4 −0 certora/harnesses/EigenPodManagerHarness.sol
+1 −0 certora/scripts/core/verifyDelegationManager.sh
+0 −19 certora/scripts/core/verifySlasher.sh
+1 −0 certora/scripts/core/verifyStrategyManager.sh
+1 −0 certora/scripts/libraries/verifyStructuredLinkedList.sh
+23 −0 certora/scripts/pods/verifyEigenPod.sh
+1 −0 certora/scripts/pods/verifyEigenPodManager.sh
+1 −0 certora/scripts/strategies/verifyStrategyBase.sh
+245 −0 certora/specs/pods/EigenPod.spec
+56 −2 certora/specs/pods/EigenPodManager.spec
+70 −31 docs/README.md
+0 −50 docs/RolesAndActors.md
+57 −28 docs/core/DelegationManager.md
+19 −14 docs/core/EigenPodManager.md
+1 −1 docs/core/StrategyManager.md
+0 −52 docs/outdated/EigenLayer-delegation-flow.md
+0 −41 docs/outdated/EigenLayer-deposit-flow.md
+0 −127 docs/outdated/EigenLayer-tech-spec.md
+0 −36 docs/outdated/EigenLayer-withdrawal-flow.md
+0 −84 docs/outdated/EigenPods.md
+0 −136 docs/outdated/Guaranteed-stake-updates.md
+0 −15 docs/outdated/Middleware-registration-operator-flow.md
+1 −1 script/middleware/DeployOpenEigenLayer.s.sol
+1 −1 script/milestone/M2Deploy.s.sol
+4 −1 script/testing/M2_Deploy_From_Scratch.s.sol
+2 −1 script/testing/M2_deploy_from_scratch.mainnet.config.json
+114 −55 src/contracts/core/DelegationManager.sol
+15 −3 src/contracts/core/DelegationManagerStorage.sol
+61 −575 src/contracts/core/Slasher.sol
+61 −6 src/contracts/interfaces/IDelegationManager.sol
+0 −13 src/contracts/interfaces/IStakeRegistryStub.sol
+2 −2 src/contracts/libraries/Merkle.sol
+2 −2 src/contracts/pods/EigenPod.sol
+7 −3 src/contracts/pods/EigenPodManager.sol
+2 −0 src/contracts/pods/EigenPodPausingConstants.sol
+2 −9 src/test/Delegation.t.sol
+2 −121 src/test/DepositWithdraw.t.sol
+3 −1 src/test/EigenLayerDeployer.t.sol
+0 −2 src/test/EigenLayerTestHelper.t.sol
+8 −8 src/test/EigenPod.t.sol
+0 −339 src/test/Slasher.t.sol
+1 −1 src/test/WithdrawalMigration.t.sol
+0 −11 src/test/Withdrawals.t.sol
+15 −4 src/test/events/IDelegationManagerEvents.sol
+268 −8 src/test/integration/IntegrationBase.t.sol
+147 −0 src/test/integration/IntegrationChecks.t.sol
+12 −7 src/test/integration/IntegrationDeployer.t.sol
+133 −5 src/test/integration/User.t.sol
+93 −0 src/test/integration/mocks/BeaconChainMock.t.sol
+71 −261 src/test/integration/tests/Deposit_Delegate_Queue_Complete.t.sol
+87 −0 src/test/integration/tests/Deposit_Delegate_Redelegate_Complete.t.sol
+223 −0 src/test/integration/tests/Deposit_Delegate_Undelegate_Complete.t.sol
+71 −0 src/test/integration/tests/Deposit_Delegate_UpdateBalance.t.sol
+15 −4 src/test/mocks/DelegationManagerMock.sol
+0 −8 src/test/mocks/StakeRegistryStub.sol
+10 −9 src/test/mocks/StrategyManagerMock.sol
+0 −7 src/test/tree/DelegationManagerUnit.tree
+727 −65 src/test/unit/DelegationUnit.t.sol
+1 −1 src/test/unit/EigenPodManagerUnit.t.sol
+3 −1 src/test/unit/EigenPodUnit.t.sol
+0 −799 src/test/unit/SlasherUnit.t.sol
40 changes: 32 additions & 8 deletions src/RegistryCoordinator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {EIP1271SignatureUtils} from "eigenlayer-contracts/src/contracts/librarie
import {IPauserRegistry} from "eigenlayer-contracts/src/contracts/interfaces/IPauserRegistry.sol";
import {Pausable} from "eigenlayer-contracts/src/contracts/permissions/Pausable.sol";
import {ISlasher} from "eigenlayer-contracts/src/contracts/interfaces/ISlasher.sol";
import {IDelegationManager} from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol";

import {IRegistryCoordinator} from "src/interfaces/IRegistryCoordinator.sol";
import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol";
Expand Down Expand Up @@ -62,6 +63,8 @@ contract RegistryCoordinator is
IStakeRegistry public immutable stakeRegistry;
/// @notice the Index Registry contract that will keep track of operators' indexes
IIndexRegistry public immutable indexRegistry;
/// @notice The Delegation Manager contract to record operator avs relationships
IDelegationManager public immutable delegationManager;

/// @notice the current number of quorums supported by the registry coordinator
uint8 public quorumCount;
Expand Down Expand Up @@ -98,11 +101,13 @@ contract RegistryCoordinator is
}

constructor(
IDelegationManager _delegationManager,
ISlasher _slasher,
IStakeRegistry _stakeRegistry,
IBLSApkRegistry _blsApkRegistry,
IIndexRegistry _indexRegistry
) EIP712("AVSRegistryCoordinator", "v0.0.1") {
delegationManager = _delegationManager;
slasher = _slasher;
stakeRegistry = _stakeRegistry;
blsApkRegistry = _blsApkRegistry;
Expand Down Expand Up @@ -151,10 +156,13 @@ contract RegistryCoordinator is
* @notice Registers msg.sender as an operator for one or more quorums. If any quorum reaches its maximum
* operator capacity, this method will fail.
* @param quorumNumbers is an ordered byte array containing the quorum numbers being registered for
* @param socket is the socket of the operator
* @param operatorSignature is the signature of the operator used by the AVS to register the operator in the delegation manager
*/
function registerOperator(
bytes calldata quorumNumbers,
string calldata socket
string calldata socket,
SignatureWithSaltAndExpiry memory operatorSignature
) external onlyWhenNotPaused(PAUSED_REGISTER_OPERATOR) {
bytes32 operatorId = blsApkRegistry.getOperatorId(msg.sender);

Expand All @@ -163,7 +171,8 @@ contract RegistryCoordinator is
operator: msg.sender,
operatorId: operatorId,
quorumNumbers: quorumNumbers,
socket: socket
socket: socket,
operatorSignature: operatorSignature
});

for (uint256 i = 0; i < quorumNumbers.length; i++) {
Expand All @@ -189,12 +198,14 @@ contract RegistryCoordinator is
* @param operatorKickParams are used to determine which operator is removed to maintain quorum capacity as the
* operator registers for quorums.
* @param churnApproverSignature is the signature of the churnApprover on the operator kick params
* @param operatorSignature is the signature of the operator used by the AVS to register the operator in the delegation manager
*/
function registerOperatorWithChurn(
bytes calldata quorumNumbers,
string calldata socket,
OperatorKickParam[] calldata operatorKickParams,
SignatureWithSaltAndExpiry memory churnApproverSignature
SignatureWithSaltAndExpiry memory churnApproverSignature,
SignatureWithSaltAndExpiry memory operatorSignature
) external onlyWhenNotPaused(PAUSED_REGISTER_OPERATOR) {
require(operatorKickParams.length == quorumNumbers.length, "RegistryCoordinator.registerOperatorWithChurn: input length mismatch");

Expand All @@ -212,10 +223,10 @@ contract RegistryCoordinator is
operator: msg.sender,
operatorId: operatorId,
quorumNumbers: quorumNumbers,
socket: socket
socket: socket,
operatorSignature: operatorSignature
});

uint256 kickIndex = 0;
wadealexc marked this conversation as resolved.
Show resolved Hide resolved
for (uint256 i = 0; i < quorumNumbers.length; i++) {
uint8 quorumNumber = uint8(quorumNumbers[i]);

Expand All @@ -236,7 +247,6 @@ contract RegistryCoordinator is
});

_deregisterOperator(operatorKickParams[i].operator, quorumNumbers[i:i+1]);
kickIndex++;
}
}
}
Expand Down Expand Up @@ -404,6 +414,15 @@ contract RegistryCoordinator is
_setEjector(_ejector);
}

/**
* @notice Sets the metadata URI for the AVS
* @param _metadataURI is the metadata URI for the AVS
* @dev only callable by the service manager owner
*/
function setMetadataURI(string memory _metadataURI) external onlyOwner {
delegationManager.updateAVSMetadataURI(_metadataURI);
}

/*******************************************************************************
INTERNAL FUNCTIONS
*******************************************************************************/
Expand All @@ -422,7 +441,8 @@ contract RegistryCoordinator is
address operator,
bytes32 operatorId,
bytes calldata quorumNumbers,
string memory socket
string memory socket,
SignatureWithSaltAndExpiry memory operatorSignature
) internal virtual returns (RegisterResults memory) {
/**
* Get bitmap of quorums to register for and operator's current bitmap. Validate that:
Expand Down Expand Up @@ -454,6 +474,9 @@ contract RegistryCoordinator is
status: OperatorStatus.REGISTERED
});

// Register the operator with the delegation manager
delegationManager.registerOperatorToAVS(operator, operatorSignature);

emit OperatorRegistered(operator, operatorId);
}

Expand Down Expand Up @@ -533,9 +556,10 @@ contract RegistryCoordinator is
newBitmap: newBitmap
});

// If the operator is no longer registered for any quorums, update their status
// If the operator is no longer registered for any quorums, update their status and deregister from delegationManager
if (newBitmap.isEmpty()) {
operatorInfo.status = OperatorStatus.DEREGISTERED;
delegationManager.deregisterOperatorFromAVS(operator);
emit OperatorDeregistered(operator, operatorId);
}

Expand Down
2 changes: 1 addition & 1 deletion src/interfaces/IRegistryCoordinator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ interface IRegistryCoordinator {

/// @notice emitted when all the operators for a quorum are updated at once
event QuorumBlockNumberUpdated(uint8 indexed quorumNumber, uint256 blocknumber);

// DATA STRUCTURES
enum OperatorStatus
{
Expand Down
3 changes: 2 additions & 1 deletion test/harnesses/RegistryCoordinatorHarness.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@ import "src/RegistryCoordinator.sol";
// wrapper around the RegistryCoordinator contract that exposes the internal functions for unit testing.
contract RegistryCoordinatorHarness is RegistryCoordinator {
constructor(
IDelegationManager _delegationManager,
ISlasher _slasher,
IStakeRegistry _stakeRegistry,
IBLSApkRegistry _blsApkRegistry,
IIndexRegistry _indexRegistry
) RegistryCoordinator(_slasher, _stakeRegistry, _blsApkRegistry, _indexRegistry) {
) RegistryCoordinator(_delegationManager, _slasher, _stakeRegistry, _blsApkRegistry, _indexRegistry) {
_transferOwnership(msg.sender);
}

Expand Down
175 changes: 175 additions & 0 deletions test/integration/CoreRegistration.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.12;

import "test/utils/MockAVSDeployer.sol";
import { DelegationManager } from "eigenlayer-contracts/src/contracts/core/DelegationManager.sol";
import { IDelegationManager } from "eigenlayer-contracts/src/contracts/interfaces/IDelegationManager.sol";

contract Test_CoreRegistration is MockAVSDeployer {
// Contracts
DelegationManager public delegationManager;

// Operator info
uint256 operatorPrivateKey = 420;
address operator;

// Dummy vals used across tests
bytes32 emptySalt;
uint256 maxExpiry = type(uint256).max;
string emptyStringForMetadataURI;

function setUp() public {
_deployMockEigenLayerAndAVS();

// Deploy New DelegationManager
DelegationManager delegationManagerImplementation = new DelegationManager(strategyManagerMock, slasher, eigenPodManagerMock);
delegationManager = DelegationManager(
address(
new TransparentUpgradeableProxy(
address(delegationManagerImplementation),
address(proxyAdmin),
abi.encodeWithSelector(
DelegationManager.initialize.selector,
address(this),
pauserRegistry,
0, // 0 is initialPausedStatus
50400 // Initial withdrawal delay blocks
)
)
)
);

// Deploy New RegistryCoordinator
registryCoordinatorImplementation = new RegistryCoordinatorHarness(
delegationManager,
slasher,
stakeRegistry,
blsApkRegistry,
indexRegistry
);

// Upgrade Registry Coordinator
cheats.prank(proxyAdminOwner);
proxyAdmin.upgrade(
TransparentUpgradeableProxy(payable(address(registryCoordinator))),
address(registryCoordinatorImplementation)
);

// Set operator address
operator = cheats.addr(operatorPrivateKey);
pubkeyCompendium.setBLSPublicKey(operator, defaultPubKey);

// Register operator to EigenLayer
cheats.prank(operator);
delegationManager.registerAsOperator(
IDelegationManager.OperatorDetails({
earningsReceiver: operator,
delegationApprover: address(0),
stakerOptOutWindowBlocks: 0
}),
emptyStringForMetadataURI
);

// Set operator weight in single quorum
bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(MAX_QUORUM_BITMAP);
for (uint i = 0; i < quorumNumbers.length; i++) {
stakeRegistry.setOperatorWeight(uint8(quorumNumbers[i]), operator, defaultStake);
}
}

function test_registerOperator_coreStateChanges() public {
bytes memory quorumNumbers = new bytes(1);

// Get operator signature
ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature = _getOperatorSignature(
operatorPrivateKey,
operator,
address(registryCoordinator),
emptySalt,
maxExpiry
);

// Register operator
cheats.prank(operator);
registryCoordinator.registerOperator(quorumNumbers, defaultSocket, operatorSignature);

// Check operator is registered
IDelegationManager.OperatorAVSRegistrationStatus operatorStatus = delegationManager.avsOperatorStatus(address(registryCoordinator), operator);
assertEq(uint8(operatorStatus), uint8(IDelegationManager.OperatorAVSRegistrationStatus.REGISTERED));
}

function test_deregisterOperator_coreStateChanges() public {
// Register operator
bytes memory quorumNumbers = new bytes(1);
_registerOperator(quorumNumbers);

// Deregister Operator
cheats.prank(operator);
registryCoordinator.deregisterOperator(quorumNumbers);

// Check operator is deregistered
IDelegationManager.OperatorAVSRegistrationStatus operatorStatus = delegationManager.avsOperatorStatus(address(registryCoordinator), operator);
assertEq(uint8(operatorStatus), uint8(IDelegationManager.OperatorAVSRegistrationStatus.UNREGISTERED));
}

function test_deregisterOperator_notGloballyDeregistered() public {
// Register operator with all quorums
bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(MAX_QUORUM_BITMAP);
emit log_named_bytes("quorumNumbers", quorumNumbers);
_registerOperator(quorumNumbers);

// Deregister Operator with single quorum
quorumNumbers = new bytes(1);
cheats.prank(operator);
registryCoordinator.deregisterOperator(quorumNumbers);

// Check operator is still registered
IDelegationManager.OperatorAVSRegistrationStatus operatorStatus = delegationManager.avsOperatorStatus(address(registryCoordinator), operator);
assertEq(uint8(operatorStatus), uint8(IDelegationManager.OperatorAVSRegistrationStatus.REGISTERED));
}

function test_setMetadataURI_fail_notServiceManagerOwner() public {
cheats.prank(operator);
cheats.expectRevert("Ownable: caller is not the owner");
registryCoordinator.setMetadataURI("Test MetadataURI");
}

function test_setMetadataURI() public {
cheats.prank(registryCoordinatorOwner);
registryCoordinator.setMetadataURI("Test MetadataURI");
}

// Utils
function _registerOperator(bytes memory quorumNumbers) internal {
// Get operator signature
ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature = _getOperatorSignature(
operatorPrivateKey,
operator,
address(registryCoordinator),
emptySalt,
maxExpiry
);

// Register operator
cheats.prank(operator);
registryCoordinator.registerOperator(quorumNumbers, defaultSocket, operatorSignature);
}

function _getOperatorSignature(
uint256 _operatorPrivateKey,
address operatorToSign,
address avs,
bytes32 salt,
uint256 expiry
) internal view returns (ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature) {
operatorSignature.salt = salt;
operatorSignature.expiry = expiry;
{
bytes32 digestHash = delegationManager.calculateOperatorAVSRegistrationDigestHash(operatorToSign, avs, salt, expiry);
(uint8 v, bytes32 r, bytes32 s) = cheats.sign(_operatorPrivateKey, digestHash);
operatorSignature.signature = abi.encodePacked(r, s, v);
}
return operatorSignature;
}

}
Loading
Loading