You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
298 lines
9.8 KiB
298 lines
9.8 KiB
7 months ago
|
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||
|
pragma solidity >=0.8.0;
|
||
|
|
||
|
/*@@@@@@@ @@@@@@@@@
|
||
|
@@@@@@@@@ @@@@@@@@@
|
||
|
@@@@@@@@@ @@@@@@@@@
|
||
|
@@@@@@@@@ @@@@@@@@@
|
||
|
@@@@@@@@@@@@@@@@@@@@@@@@@
|
||
|
@@@@@ HYPERLANE @@@@@@@
|
||
|
@@@@@@@@@@@@@@@@@@@@@@@@@
|
||
|
@@@@@@@@@ @@@@@@@@@
|
||
|
@@@@@@@@@ @@@@@@@@@
|
||
|
@@@@@@@@@ @@@@@@@@@
|
||
|
@@@@@@@@@ @@@@@@@@*/
|
||
|
|
||
|
// ============ Internal Imports ============
|
||
|
import {Enrollment, EnrollmentStatus, EnumerableMapEnrollment} from "../libs/EnumerableMapEnrollment.sol";
|
||
|
import {IAVSDirectory} from "../interfaces/avs/vendored/IAVSDirectory.sol";
|
||
|
import {IRemoteChallenger} from "../interfaces/avs/IRemoteChallenger.sol";
|
||
|
import {ISlasher} from "../interfaces/avs/vendored/ISlasher.sol";
|
||
|
import {ECDSAServiceManagerBase} from "./ECDSAServiceManagerBase.sol";
|
||
|
|
||
|
contract HyperlaneServiceManager is ECDSAServiceManagerBase {
|
||
|
// ============ Libraries ============
|
||
|
|
||
|
using EnumerableMapEnrollment for EnumerableMapEnrollment.AddressToEnrollmentMap;
|
||
|
|
||
|
// ============ Public Storage ============
|
||
|
|
||
|
// Slasher contract responsible for slashing operators
|
||
|
// @dev slasher needs to be updated once slashing is implemented
|
||
|
ISlasher internal slasher;
|
||
|
|
||
|
// ============ Events ============
|
||
|
|
||
|
/**
|
||
|
* @notice Emitted when an operator is enrolled in a challenger
|
||
|
* @param operator The address of the operator
|
||
|
* @param challenger The address of the challenger
|
||
|
*/
|
||
|
event OperatorEnrolledToChallenger(
|
||
|
address operator,
|
||
|
IRemoteChallenger challenger
|
||
|
);
|
||
|
|
||
|
/**
|
||
|
* @notice Emitted when an operator is queued for unenrollment from a challenger
|
||
|
* @param operator The address of the operator
|
||
|
* @param challenger The address of the challenger
|
||
|
* @param unenrollmentStartBlock The block number at which the unenrollment was queued
|
||
|
* @param challengeDelayBlocks The number of blocks to wait before unenrollment is complete
|
||
|
*/
|
||
|
event OperatorQueuedUnenrollmentFromChallenger(
|
||
|
address operator,
|
||
|
IRemoteChallenger challenger,
|
||
|
uint256 unenrollmentStartBlock,
|
||
|
uint256 challengeDelayBlocks
|
||
|
);
|
||
|
|
||
|
/**
|
||
|
* @notice Emitted when an operator is unenrolled from a challenger
|
||
|
* @param operator The address of the operator
|
||
|
* @param challenger The address of the challenger
|
||
|
* @param unenrollmentEndBlock The block number at which the unenrollment was completed
|
||
|
*/
|
||
|
event OperatorUnenrolledFromChallenger(
|
||
|
address operator,
|
||
|
IRemoteChallenger challenger,
|
||
|
uint256 unenrollmentEndBlock
|
||
|
);
|
||
|
|
||
|
// ============ Internal Storage ============
|
||
|
|
||
|
// Mapping of operators to challengers they are enrolled in (enumerable required for remove-all)
|
||
|
mapping(address => EnumerableMapEnrollment.AddressToEnrollmentMap)
|
||
|
internal enrolledChallengers;
|
||
|
|
||
|
// ============ Modifiers ============
|
||
|
|
||
|
// Only allows the challenger the operator is enrolled in to call the function
|
||
|
modifier onlyEnrolledChallenger(address operator) {
|
||
|
(bool exists, ) = enrolledChallengers[operator].tryGet(msg.sender);
|
||
|
require(
|
||
|
exists,
|
||
|
"HyperlaneServiceManager: Operator not enrolled in challenger"
|
||
|
);
|
||
|
_;
|
||
|
}
|
||
|
|
||
|
// ============ Constructor ============
|
||
|
|
||
|
constructor(
|
||
|
address _avsDirectory,
|
||
|
address _stakeRegistry,
|
||
|
address _paymentCoordinator,
|
||
|
address _delegationManager
|
||
|
)
|
||
|
ECDSAServiceManagerBase(
|
||
|
_avsDirectory,
|
||
|
_stakeRegistry,
|
||
|
_paymentCoordinator,
|
||
|
_delegationManager
|
||
|
)
|
||
|
{}
|
||
|
|
||
|
/**
|
||
|
* @notice Initializes the HyperlaneServiceManager contract with the owner address
|
||
|
*/
|
||
|
function initialize(address _owner) public initializer {
|
||
|
__ServiceManagerBase_init(_owner);
|
||
|
}
|
||
|
|
||
|
// ============ External Functions ============
|
||
|
|
||
|
/**
|
||
|
* @notice Enrolls as an operator into a list of challengers
|
||
|
* @param _challengers The list of challengers to enroll into
|
||
|
*/
|
||
|
function enrollIntoChallengers(
|
||
|
IRemoteChallenger[] memory _challengers
|
||
|
) external {
|
||
|
for (uint256 i = 0; i < _challengers.length; i++) {
|
||
|
enrollIntoChallenger(_challengers[i]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @notice starts an operator for unenrollment from a list of challengers
|
||
|
* @param _challengers The list of challengers to unenroll from
|
||
|
*/
|
||
|
function startUnenrollment(
|
||
|
IRemoteChallenger[] memory _challengers
|
||
|
) external {
|
||
|
for (uint256 i = 0; i < _challengers.length; i++) {
|
||
|
startUnenrollment(_challengers[i]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @notice Completes the unenrollment of an operator from a list of challengers
|
||
|
* @param _challengers The list of challengers to unenroll from
|
||
|
*/
|
||
|
function completeUnenrollment(address[] memory _challengers) external {
|
||
|
_completeUnenrollment(msg.sender, _challengers);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @notice Sets the slasher contract responsible for slashing operators
|
||
|
* @param _slasher The address of the slasher contract
|
||
|
*/
|
||
|
function setSlasher(ISlasher _slasher) external onlyOwner {
|
||
|
slasher = _slasher;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @notice returns the status of a challenger an operator is enrolled in
|
||
|
* @param _operator The address of the operator
|
||
|
* @param _challenger specified IRemoteChallenger contract
|
||
|
*/
|
||
|
function getChallengerEnrollment(
|
||
|
address _operator,
|
||
|
IRemoteChallenger _challenger
|
||
|
) external view returns (Enrollment memory enrollment) {
|
||
|
return enrolledChallengers[_operator].get(address(_challenger));
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @notice forwards a call to the Slasher contract to freeze an operator
|
||
|
* @param operator The address of the operator to freeze.
|
||
|
* @dev only the enrolled challengers can call this function
|
||
|
*/
|
||
|
function freezeOperator(
|
||
|
address operator
|
||
|
) external virtual onlyEnrolledChallenger(operator) {
|
||
|
slasher.freezeOperator(operator);
|
||
|
}
|
||
|
|
||
|
// ============ Public Functions ============
|
||
|
|
||
|
/**
|
||
|
* @notice returns the list of challengers an operator is enrolled in
|
||
|
* @param _operator The address of the operator
|
||
|
*/
|
||
|
function getOperatorChallengers(
|
||
|
address _operator
|
||
|
) public view returns (address[] memory) {
|
||
|
return enrolledChallengers[_operator].keys();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @notice Enrolls as an operator into a single challenger
|
||
|
* @param challenger The challenger to enroll into
|
||
|
*/
|
||
|
function enrollIntoChallenger(IRemoteChallenger challenger) public {
|
||
|
require(
|
||
|
enrolledChallengers[msg.sender].set(
|
||
|
address(challenger),
|
||
|
Enrollment(EnrollmentStatus.ENROLLED, 0)
|
||
|
)
|
||
|
);
|
||
|
emit OperatorEnrolledToChallenger(msg.sender, challenger);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @notice starts an operator for unenrollment from a challenger
|
||
|
* @param challenger The challenger to unenroll from
|
||
|
*/
|
||
|
function startUnenrollment(IRemoteChallenger challenger) public {
|
||
|
(bool exists, Enrollment memory enrollment) = enrolledChallengers[
|
||
|
msg.sender
|
||
|
].tryGet(address(challenger));
|
||
|
require(
|
||
|
exists && enrollment.status == EnrollmentStatus.ENROLLED,
|
||
|
"HyperlaneServiceManager: challenger isn't enrolled"
|
||
|
);
|
||
|
|
||
|
enrolledChallengers[msg.sender].set(
|
||
|
address(challenger),
|
||
|
Enrollment(
|
||
|
EnrollmentStatus.PENDING_UNENROLLMENT,
|
||
|
uint248(block.number)
|
||
|
)
|
||
|
);
|
||
|
emit OperatorQueuedUnenrollmentFromChallenger(
|
||
|
msg.sender,
|
||
|
challenger,
|
||
|
block.number,
|
||
|
challenger.challengeDelayBlocks()
|
||
|
);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @notice Completes the unenrollment of an operator from a challenger
|
||
|
* @param challenger The challenger to unenroll from
|
||
|
*/
|
||
|
function completeUnenrollment(address challenger) public {
|
||
|
_completeUnenrollment(msg.sender, challenger);
|
||
|
}
|
||
|
|
||
|
// ============ Internal Functions ============
|
||
|
|
||
|
/**
|
||
|
* @notice Completes the unenrollment of an operator from a list of challengers
|
||
|
* @param operator The address of the operator
|
||
|
* @param _challengers The list of challengers to unenroll from
|
||
|
*/
|
||
|
function _completeUnenrollment(
|
||
|
address operator,
|
||
|
address[] memory _challengers
|
||
|
) internal {
|
||
|
for (uint256 i = 0; i < _challengers.length; i++) {
|
||
|
_completeUnenrollment(operator, _challengers[i]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @notice Completes the unenrollment of an operator from a challenger
|
||
|
* @param operator The address of the operator
|
||
|
* @param _challenger The challenger to unenroll from
|
||
|
*/
|
||
|
function _completeUnenrollment(
|
||
|
address operator,
|
||
|
address _challenger
|
||
|
) internal {
|
||
|
IRemoteChallenger challenger = IRemoteChallenger(_challenger);
|
||
|
(bool exists, Enrollment memory enrollment) = enrolledChallengers[
|
||
|
operator
|
||
|
].tryGet(address(challenger));
|
||
|
|
||
|
require(
|
||
|
exists &&
|
||
|
enrollment.status == EnrollmentStatus.PENDING_UNENROLLMENT &&
|
||
|
block.number >=
|
||
|
enrollment.unenrollmentStartBlock +
|
||
|
challenger.challengeDelayBlocks(),
|
||
|
"HyperlaneServiceManager: Invalid unenrollment"
|
||
|
);
|
||
|
|
||
|
enrolledChallengers[operator].remove(address(challenger));
|
||
|
emit OperatorUnenrolledFromChallenger(
|
||
|
operator,
|
||
|
challenger,
|
||
|
block.number
|
||
|
);
|
||
|
}
|
||
|
|
||
|
/// @inheritdoc ECDSAServiceManagerBase
|
||
|
function _deregisterOperatorFromAVS(
|
||
|
address operator
|
||
|
) internal virtual override {
|
||
|
address[] memory challengers = getOperatorChallengers(operator);
|
||
|
_completeUnenrollment(operator, challengers);
|
||
|
|
||
|
IAVSDirectory(avsDirectory).deregisterOperatorFromAVS(operator);
|
||
|
emit OperatorDeregisteredFromAVS(operator);
|
||
|
}
|
||
|
}
|