mirror of
https://github.com/datahaven-xyz/datahaven
synced 2026-05-24 09:50:01 +00:00
## Summary This PR makes `removeValidatorFromAllowlist` immediately revoke active validator membership instead of only preventing future registrations. Previously, removing a validator from the allowlist only blocked future registration attempts, but did not remove validators that were already active in the operator set. This change closes that gap so allowlist removal immediately takes effect for active validator membership as well. ### What changed - Updated `DataHavenServiceManager.removeValidatorFromAllowlist` to force-deregister validators from the `VALIDATORS_SET_ID` when they are currently registered. - Extracted the deregistration flow into an internal helper so the same deregistration logic is reused consistently. ### Tests Added regression tests covering: - removing an allowlisted validator that is actively registered - removing an allowlisted validator that was never registered - ensuring removed validators are excluded from validator-set message generation
224 lines
8.9 KiB
Solidity
224 lines
8.9 KiB
Solidity
// SPDX-License-Identifier: UNLICENSED
|
|
pragma solidity ^0.8.27;
|
|
|
|
import {AVSDeployer} from "./utils/AVSDeployer.sol";
|
|
import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol";
|
|
import {
|
|
IAllocationManagerErrors,
|
|
IAllocationManager,
|
|
IAllocationManagerTypes,
|
|
IAllocationManagerEvents
|
|
} from "eigenlayer-contracts/src/contracts/interfaces/IAllocationManager.sol";
|
|
import {DataHavenServiceManager} from "../src/DataHavenServiceManager.sol";
|
|
import {
|
|
IDataHavenServiceManagerEvents,
|
|
IDataHavenServiceManager
|
|
} from "../src/interfaces/IDataHavenServiceManager.sol";
|
|
import {OperatorSet} from "eigenlayer-contracts/src/contracts/libraries/OperatorSetLib.sol";
|
|
import "forge-std/Test.sol";
|
|
|
|
contract SlashingTest is AVSDeployer {
|
|
address operator = address(0xabcd);
|
|
address public snowbridgeAgent = address(uint160(uint256(keccak256("snowbridgeAgent"))));
|
|
|
|
function setUp() public virtual {
|
|
_deployMockEigenLayerAndAVS();
|
|
}
|
|
|
|
function test_fulfilSlashingRequest() public {
|
|
address solochainOperator = address(0xBEEF);
|
|
|
|
// Allow our operator to register
|
|
vm.prank(avsOwner);
|
|
serviceManager.addValidatorToAllowlist(operator);
|
|
|
|
// Configure the rewards initiator (because only the reward agent can submit slashing request)
|
|
vm.prank(avsOwner);
|
|
serviceManager.setSnowbridgeInitiator(snowbridgeAgent);
|
|
|
|
vm.prank(operator);
|
|
delegationManager.registerAsOperator(address(0), 0, "");
|
|
|
|
uint32[] memory operatorSetIds = new uint32[](1);
|
|
operatorSetIds[0] = serviceManager.VALIDATORS_SET_ID();
|
|
IAllocationManagerTypes.RegisterParams memory registerParams =
|
|
IAllocationManagerTypes.RegisterParams({
|
|
avs: address(serviceManager),
|
|
operatorSetIds: operatorSetIds,
|
|
data: abi.encodePacked(solochainOperator)
|
|
});
|
|
|
|
vm.prank(operator);
|
|
allocationManager.registerForOperatorSets(operator, registerParams);
|
|
|
|
DataHavenServiceManager.SlashingRequest[] memory slashings =
|
|
new DataHavenServiceManager.SlashingRequest[](1);
|
|
uint256[] memory wadsToSlash = new uint256[](3); // 3 wadsToSlash because we have register 3 strategies for the Validator set
|
|
wadsToSlash[0] = 1e16;
|
|
wadsToSlash[1] = 1e16;
|
|
wadsToSlash[2] = 1e16;
|
|
|
|
OperatorSet memory operatorSet =
|
|
OperatorSet({avs: address(serviceManager), id: serviceManager.VALIDATORS_SET_ID()});
|
|
IStrategy[] memory strategies = allocationManager.getStrategiesInOperatorSet(operatorSet);
|
|
|
|
slashings[0] = IDataHavenServiceManager.SlashingRequest(
|
|
solochainOperator, strategies, wadsToSlash, "Testing slashing"
|
|
);
|
|
|
|
console.log(block.number);
|
|
vm.roll(block.number + uint32(7 days) + 1);
|
|
console.log(block.number);
|
|
|
|
// Because the current magnitude for the allocation is 0
|
|
uint256[] memory wadsToSlashed = new uint256[](3);
|
|
|
|
// We emit the event we expect to see.
|
|
vm.prank(snowbridgeAgent);
|
|
vm.expectEmit();
|
|
emit IAllocationManagerEvents.OperatorSlashed(
|
|
operator, operatorSet, strategies, wadsToSlashed, "Testing slashing"
|
|
);
|
|
vm.expectEmit();
|
|
emit IDataHavenServiceManagerEvents.SlashingComplete();
|
|
serviceManager.slashValidatorsOperator(slashings);
|
|
}
|
|
|
|
function test_fulfilSlashingRequestForOnlyOneStrategy() public {
|
|
address solochainOperator = address(0xBEEF);
|
|
|
|
// Allow our operator to register
|
|
vm.prank(avsOwner);
|
|
serviceManager.addValidatorToAllowlist(operator);
|
|
|
|
// Configure the rewards initiator (because only the reward agent can submit slashing request)
|
|
vm.prank(avsOwner);
|
|
serviceManager.setSnowbridgeInitiator(snowbridgeAgent);
|
|
|
|
vm.prank(operator);
|
|
delegationManager.registerAsOperator(address(0), 0, "");
|
|
|
|
uint32[] memory operatorSetIds = new uint32[](1);
|
|
operatorSetIds[0] = serviceManager.VALIDATORS_SET_ID();
|
|
IAllocationManagerTypes.RegisterParams memory registerParams =
|
|
IAllocationManagerTypes.RegisterParams({
|
|
avs: address(serviceManager),
|
|
operatorSetIds: operatorSetIds,
|
|
data: abi.encodePacked(solochainOperator)
|
|
});
|
|
|
|
vm.prank(operator);
|
|
allocationManager.registerForOperatorSets(operator, registerParams);
|
|
|
|
OperatorSet memory operatorSet =
|
|
OperatorSet({avs: address(serviceManager), id: serviceManager.VALIDATORS_SET_ID()});
|
|
IStrategy[] memory strategies = allocationManager.getStrategiesInOperatorSet(operatorSet);
|
|
|
|
DataHavenServiceManager.SlashingRequest[] memory slashings =
|
|
new DataHavenServiceManager.SlashingRequest[](1);
|
|
uint256[] memory wadsToSlash = new uint256[](1); // We only want to slash 1 strategy
|
|
wadsToSlash[0] = 1e16;
|
|
|
|
IStrategy[] memory strategiesToSlash = new IStrategy[](1);
|
|
strategiesToSlash[0] = strategies[0];
|
|
|
|
slashings[0] = IDataHavenServiceManager.SlashingRequest(
|
|
solochainOperator, strategiesToSlash, wadsToSlash, "Testing slashing"
|
|
);
|
|
|
|
console.log(block.number);
|
|
vm.roll(block.number + uint32(7 days) + 1);
|
|
console.log(block.number);
|
|
|
|
// Because the current magnitude for the allocation is 0
|
|
uint256[] memory wadsToSlashed = new uint256[](1);
|
|
|
|
// We emit the event we expect to see.
|
|
vm.prank(snowbridgeAgent);
|
|
vm.expectEmit();
|
|
emit IAllocationManagerEvents.OperatorSlashed(
|
|
operator, operatorSet, strategiesToSlash, wadsToSlashed, "Testing slashing"
|
|
);
|
|
vm.expectEmit();
|
|
emit IDataHavenServiceManagerEvents.SlashingComplete();
|
|
serviceManager.slashValidatorsOperator(slashings);
|
|
}
|
|
|
|
function test_fulfilSlashingRequest_skipsUnknownSolochainAddress() public {
|
|
// Configure the rewards initiator (because only the reward agent can submit slashing request)
|
|
vm.prank(avsOwner);
|
|
serviceManager.setSnowbridgeInitiator(snowbridgeAgent);
|
|
|
|
address unknownSolochainOperator = address(0xDEAD);
|
|
DataHavenServiceManager.SlashingRequest[] memory slashings =
|
|
new DataHavenServiceManager.SlashingRequest[](1);
|
|
slashings[0] = IDataHavenServiceManager.SlashingRequest(
|
|
unknownSolochainOperator,
|
|
new IStrategy[](0),
|
|
new uint256[](0),
|
|
"Testing unknown solochain operator"
|
|
);
|
|
|
|
// Unknown solochain address is silently skipped; SlashingComplete is still emitted
|
|
vm.prank(snowbridgeAgent);
|
|
vm.expectEmit();
|
|
emit IDataHavenServiceManagerEvents.SlashingComplete();
|
|
serviceManager.slashValidatorsOperator(slashings);
|
|
}
|
|
|
|
function test_fulfilSlashingRequest_afterAllowlistRemovalStillResolvesDuringDeallocationDelay()
|
|
public
|
|
{
|
|
address solochainOperator = address(0xBEEF);
|
|
|
|
vm.prank(avsOwner);
|
|
serviceManager.addValidatorToAllowlist(operator);
|
|
|
|
vm.prank(avsOwner);
|
|
serviceManager.setSnowbridgeInitiator(snowbridgeAgent);
|
|
|
|
vm.prank(operator);
|
|
delegationManager.registerAsOperator(address(0), 0, "");
|
|
|
|
uint32[] memory operatorSetIds = new uint32[](1);
|
|
operatorSetIds[0] = serviceManager.VALIDATORS_SET_ID();
|
|
IAllocationManagerTypes.RegisterParams memory registerParams =
|
|
IAllocationManagerTypes.RegisterParams({
|
|
avs: address(serviceManager),
|
|
operatorSetIds: operatorSetIds,
|
|
data: abi.encodePacked(solochainOperator)
|
|
});
|
|
|
|
vm.prank(operator);
|
|
allocationManager.registerForOperatorSets(operator, registerParams);
|
|
|
|
vm.prank(avsOwner);
|
|
serviceManager.removeValidatorFromAllowlist(operator);
|
|
|
|
DataHavenServiceManager.SlashingRequest[] memory slashings =
|
|
new DataHavenServiceManager.SlashingRequest[](1);
|
|
uint256[] memory wadsToSlash = new uint256[](3);
|
|
wadsToSlash[0] = 1e16;
|
|
wadsToSlash[1] = 1e16;
|
|
wadsToSlash[2] = 1e16;
|
|
|
|
OperatorSet memory operatorSet =
|
|
OperatorSet({avs: address(serviceManager), id: serviceManager.VALIDATORS_SET_ID()});
|
|
IStrategy[] memory strategies = allocationManager.getStrategiesInOperatorSet(operatorSet);
|
|
|
|
slashings[0] = IDataHavenServiceManager.SlashingRequest(
|
|
solochainOperator, strategies, wadsToSlash, "Testing slashing"
|
|
);
|
|
|
|
uint256[] memory wadsToSlashed = new uint256[](3);
|
|
|
|
vm.prank(snowbridgeAgent);
|
|
vm.expectEmit();
|
|
emit IAllocationManagerEvents.OperatorSlashed(
|
|
operator, operatorSet, strategies, wadsToSlashed, "Testing slashing"
|
|
);
|
|
vm.expectEmit();
|
|
emit IDataHavenServiceManagerEvents.SlashingComplete();
|
|
serviceManager.slashValidatorsOperator(slashings);
|
|
}
|
|
}
|