Skip to content

Commit

Permalink
test: unit test slashers (#380)
Browse files Browse the repository at this point in the history
* fix: remappings

* fix: remove unused OnlySlasher error (#376)

* refactor: VetoableSlasher (#377)

* chore: use inheritdoc for SlashingRegCoord (#378)

* test: slasher contracts and add some natspec

* chore: unneeded comment

* chore: redundant comments

* test: more extensive setup for slashOperator

* test: integrate core deployment lib

* test: unit test slashing

* test: vetoable slasher

* chore: forge fmt

* chore: format and solve interfaces with inheritdoc

---------

Co-authored-by: clandestine.eth <[email protected]>
Co-authored-by: Noel <[email protected]>
Co-authored-by: Yash Patil <[email protected]>
  • Loading branch information
4 people authored Feb 5, 2025
1 parent 9994210 commit 9e4afb8
Show file tree
Hide file tree
Showing 12 changed files with 1,203 additions and 102 deletions.
10 changes: 0 additions & 10 deletions src/RegistryCoordinator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -237,14 +237,4 @@ contract RegistryCoordinator is RegistryCoordinatorStorage {
) external view returns (bool) {
return _isM2Quorum(quorumNumber);
}

/**
* @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 calculatePubkeyRegistrationMessageHash(
address operator
) public view returns (bytes32) {
return _hashTypedDataV4(keccak256(abi.encode(PUBKEY_REGISTRATION_TYPEHASH, operator)));
}
}
10 changes: 10 additions & 0 deletions src/SlashingRegistryCoordinator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -1066,4 +1066,14 @@ contract SlashingRegistryCoordinator is
_hashTypedDataV4(keccak256(abi.encode(PUBKEY_REGISTRATION_TYPEHASH, operator)))
);
}

/**
* @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 calculatePubkeyRegistrationMessageHash(
address operator
) public view returns (bytes32) {
return _hashTypedDataV4(keccak256(abi.encode(PUBKEY_REGISTRATION_TYPEHASH, operator)));
}
}
24 changes: 24 additions & 0 deletions src/interfaces/IInstantSlasher.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.27;

import {IAllocationManager} from
"eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol";
import {ISlasher} from "./ISlasher.sol";

/// @title IInstantSlasher
/// @notice A slashing contract that immediately executes slashing requests without any delay or veto period
/// @dev Extends base interfaces to provide access controlled slashing functionality
interface IInstantSlasher is ISlasher {
/// @notice Initializes the contract with a slasher address
/// @param _slasher Address authorized to create and fulfill slashing requests
function initialize(
address _slasher
) external;

/// @notice Immediately executes a slashing request
/// @param _slashingParams Parameters defining the slashing request including operator and amount
/// @dev Can only be called by the authorized slasher
function fulfillSlashingRequest(
IAllocationManager.SlashingParams memory _slashingParams
) external;
}
74 changes: 9 additions & 65 deletions src/interfaces/ISlasher.sol
Original file line number Diff line number Diff line change
@@ -1,85 +1,24 @@
// 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.
/// @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.
*/
/// @notice Structure containing details about a 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.
*/
/// @notice Emitted when an operator is successfully slashed
event OperatorSlashed(
uint256 indexed slashingRequestId,
address indexed operator,
Expand All @@ -89,4 +28,9 @@ interface ISlasherEvents is ISlasherTypes {
);
}

interface ISlasher is ISlasherErrors, ISlasherEvents {}
/// @title ISlasher
/// @notice Base interface containing shared functionality for all slasher implementations
interface ISlasher is ISlasherErrors, ISlasherEvents {
/// @notice Returns the address authorized to create and fulfill slashing requests
function slasher() external view returns (address);
}
8 changes: 8 additions & 0 deletions src/interfaces/ISlashingRegistryCoordinator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -452,6 +452,14 @@ interface ISlashingRegistryCoordinator is

/// VIEW

/**
* @notice Returns the hash of the message that operators must sign with their BLS key to register
* @param operator The operator's Ethereum address
*/
function calculatePubkeyRegistrationMessageHash(
address operator
) external view returns (bytes32);

/**
* @notice Returns the operator set parameters for a given quorum.
* @param quorumNumber The identifier of the quorum to query.
Expand Down
92 changes: 92 additions & 0 deletions src/interfaces/IVetoableSlasher.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.27;

import {IAllocationManager} from
"eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol";
import {ISlasher} from "./ISlasher.sol";

interface IVetoableSlasherErrors {
/// @notice Thrown when a caller without veto committee privileges attempts a restricted operation
error OnlyVetoCommittee();
/// @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 IVetoableSlasherTypes {
/// @notice Represents the status of a slashing request
enum SlashingStatus {
Requested,
Cancelled,
Completed
}

/// @notice Structure containing details about a vetoable slashing request
struct VetoableSlashingRequest {
IAllocationManager.SlashingParams params;
uint256 requestTimestamp;
SlashingStatus status;
}
}

interface IVetoableSlasherEvents {
/// @notice Emitted when a new slashing request is created
event SlashingRequested(
uint256 indexed requestId,
address indexed operator,
uint32 operatorSetId,
uint256[] wadsToSlash,
string description
);

/// @notice Emitted when a slashing request is cancelled by the veto committee
event SlashingRequestCancelled(uint256 indexed requestId);
}

/// @title IVetoableSlasher
/// @notice A slashing contract that implements a veto mechanism allowing a designated committee to cancel slashing requests
/// @dev Extends base interfaces and adds a veto period during which slashing requests can be cancelled
interface IVetoableSlasher is
ISlasher,
IVetoableSlasherErrors,
IVetoableSlasherTypes,
IVetoableSlasherEvents
{
/// @notice Duration of the veto period during which the veto committee can cancel slashing requests
/// @dev Set to 3 days (259,200 seconds)
function VETO_PERIOD() external view returns (uint256);

/// @notice Address of the committee that has veto power over slashing requests
function vetoCommittee() external view returns (address);

/// @notice Initializes the contract with a veto committee and slasher address
/// @param _vetoCommittee Address of the committee that can veto slashing requests
/// @param _slasher Address authorized to create and fulfill slashing requests
function initialize(address _vetoCommittee, address _slasher) external;

/// @notice Queues a new slashing request
/// @param params Parameters defining the slashing request including operator and amount
/// @dev Can only be called by the authorized slasher
function queueSlashingRequest(
IAllocationManager.SlashingParams calldata params
) external;

/// @notice Cancels a pending slashing request
/// @param requestId The ID of the slashing request to cancel
/// @dev Can only be called by the veto committee during the veto period
function cancelSlashingRequest(
uint256 requestId
) external;

/// @notice Executes a slashing request after the veto period has passed
/// @param requestId The ID of the slashing request to fulfill
/// @dev Can only be called by the authorized slasher after the veto period
function fulfillSlashingRequest(
uint256 requestId
) external;
}
16 changes: 11 additions & 5 deletions src/slashers/InstantSlasher.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,31 @@ 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";
import {ISlashingRegistryCoordinator} from "../interfaces/ISlashingRegistryCoordinator.sol";
import {IInstantSlasher} from "../interfaces/IInstantSlasher.sol";

contract InstantSlasher is SlasherBase {
/// @title InstantSlasher
/// @notice A slashing contract that immediately executes slashing requests without any delay or veto period
/// @dev Extends SlasherBase to provide access controlled slashing functionality
contract InstantSlasher is IInstantSlasher, SlasherBase {
constructor(
IAllocationManager _allocationManager,
ISlashingRegistryCoordinator _slashingRegistryCoordinator,
address _slasher
) SlasherBase(_allocationManager, _slashingRegistryCoordinator) {}

/// @inheritdoc IInstantSlasher
function initialize(
address _slasher
) external initializer {
) external override initializer {
__SlasherBase_init(_slasher);
}

/// @inheritdoc IInstantSlasher
function fulfillSlashingRequest(
IAllocationManager.SlashingParams memory _slashingParams
) external virtual onlySlasher {
IAllocationManager.SlashingParams calldata _slashingParams
) external virtual override(IInstantSlasher) onlySlasher {
uint256 requestId = nextRequestId++;
_fulfillSlashingRequest(requestId, _slashingParams);
}
Expand Down
Loading

0 comments on commit 9e4afb8

Please sign in to comment.