Skip to content

Commit

Permalink
fix chainsecurity: Slash Execution Denial
Browse files Browse the repository at this point in the history
  • Loading branch information
1kresh committed Aug 14, 2024
1 parent e4ddd29 commit 5c2fbb2
Show file tree
Hide file tree
Showing 4 changed files with 236 additions and 14 deletions.
23 changes: 17 additions & 6 deletions src/contracts/hints/SlasherHints.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {Checkpoints} from "src/contracts/libraries/Checkpoints.sol";

import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol";
import {Time} from "@openzeppelin/contracts/utils/types/Time.sol";

contract BaseSlasherHints is Hints, BaseSlasher {
using Checkpoints for Checkpoints.Trace256;
Expand Down Expand Up @@ -152,12 +153,19 @@ contract VetoSlasherHints is Hints, VetoSlasher {
address operator,
uint48 captureTimestamp
) external view returns (bytes memory) {
bytes memory resolverHint = resolverHint(slasher, subnetwork, captureTimestamp);
bytes memory captureResolverHint = resolverHint(slasher, subnetwork, captureTimestamp);
bytes memory currentResolverHint = resolverHint(slasher, subnetwork, Time.timestamp());
bytes memory slashableStakeHints =
BaseSlasherHints(BASE_SLASHER_HINTS).slashableStakeHints(slasher, subnetwork, operator, captureTimestamp);

if (resolverHint.length > 0 || slashableStakeHints.length > 0) {
return abi.encode(ExecuteSlashHints({resolverHint: resolverHint, slashableStakeHints: slashableStakeHints}));
if (captureResolverHint.length > 0 || currentResolverHint.length > 0 || slashableStakeHints.length > 0) {
return abi.encode(
ExecuteSlashHints({
captureResolverHint: captureResolverHint,
currentResolverHint: currentResolverHint,
slashableStakeHints: slashableStakeHints
})
);
}
}

Expand All @@ -166,10 +174,13 @@ contract VetoSlasherHints is Hints, VetoSlasher {
bytes32 subnetwork,
uint48 captureTimestamp
) external view returns (bytes memory) {
bytes memory resolverHint_ = resolverHint(slasher, subnetwork, captureTimestamp);
bytes memory captureResolverHint = resolverHint(slasher, subnetwork, captureTimestamp);
bytes memory currentResolverHint = resolverHint(slasher, subnetwork, Time.timestamp());

if (resolverHint_.length > 0) {
return abi.encode(VetoSlashHints({resolverHint: resolverHint_}));
if (captureResolverHint.length > 0 || currentResolverHint.length > 0) {
return abi.encode(
VetoSlashHints({captureResolverHint: captureResolverHint, currentResolverHint: currentResolverHint})
);
}
}

Expand Down
17 changes: 14 additions & 3 deletions src/contracts/slasher/VetoSlasher.sol
Original file line number Diff line number Diff line change
Expand Up @@ -138,8 +138,10 @@ contract VetoSlasher is BaseSlasher, IVetoSlasher {
SlashRequest storage request = slashRequests[slashIndex];

if (
request.vetoDeadline > Time.timestamp()
&& resolverAt(request.subnetwork, request.captureTimestamp, executeSlashHints.resolverHint) != address(0)
resolverAt(request.subnetwork, request.captureTimestamp, executeSlashHints.captureResolverHint)
!= address(0)
&& resolverAt(request.subnetwork, Time.timestamp(), executeSlashHints.currentResolverHint) != address(0)
&& request.vetoDeadline > Time.timestamp()
) {
revert VetoPeriodNotEnded();
}
Expand Down Expand Up @@ -198,7 +200,16 @@ contract VetoSlasher is BaseSlasher, IVetoSlasher {

SlashRequest storage request = slashRequests[slashIndex];

if (msg.sender != resolverAt(request.subnetwork, request.captureTimestamp, vetoSlashHints.resolverHint)) {
address captureResolver =
resolverAt(request.subnetwork, request.captureTimestamp, vetoSlashHints.captureResolverHint);
if (
captureResolver == address(0)
|| resolverAt(request.subnetwork, Time.timestamp(), vetoSlashHints.currentResolverHint) == address(0)
) {
revert NoResolver();
}

if (msg.sender != captureResolver) {
revert NotResolver();
}

Expand Down
13 changes: 9 additions & 4 deletions src/interfaces/slasher/IVetoSlasher.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ interface IVetoSlasher {
error InvalidCaptureTimestamp();
error InvalidResolverSetEpochsDelay();
error InvalidVetoDuration();
error NoResolver();
error NotNetwork();
error NotResolver();
error SlashPeriodEnded();
Expand Down Expand Up @@ -53,20 +54,24 @@ interface IVetoSlasher {

/**
* @notice Hints for a slash execute.
* @param resolverHint hint for the resolver checkpoint
* @param captureResolverHint hint for the resolver checkpoint at the capture time
* @param currentResolverHint hint for the resolver checkpoint at the current time
* @param slashableStakeHints hints for the slashable stake checkpoints
*/
struct ExecuteSlashHints {
bytes resolverHint;
bytes captureResolverHint;
bytes currentResolverHint;
bytes slashableStakeHints;
}

/**
* @notice Hints for a slash veto.
* @param resolverHint hint for the resolver checkpoint
* @param captureResolverHint hint for the resolver checkpoint at the capture time
* @param currentResolverHint hint for the resolver checkpoint at the current time
*/
struct VetoSlashHints {
bytes resolverHint;
bytes captureResolverHint;
bytes currentResolverHint;
}

/**
Expand Down
197 changes: 196 additions & 1 deletion test/slasher/VetoSlasher.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -678,7 +678,7 @@ contract VetoSlasherTest is Test {
assertEq(slasher.cumulativeSlash(alice.subnetwork(0), alice), slashAmountReal1);
}

function test_ExecuteSlashWithoutResolver(
function test_ExecuteSlashWithoutResolver1(
uint48 epochDuration,
uint48 vetoDuration,
uint256 depositAmount,
Expand Down Expand Up @@ -767,6 +767,99 @@ contract VetoSlasherTest is Test {
assertEq(slasher.cumulativeSlash(alice.subnetwork(0), alice), slashAmountReal1);
}

function test_ExecuteSlashWithoutResolver2(
uint48 epochDuration,
uint48 vetoDuration,
uint256 depositAmount,
uint256 networkLimit,
uint256 operatorNetworkLimit1,
uint256 slashAmount1
) public {
epochDuration = uint48(bound(epochDuration, 1, 10 days));
vetoDuration = uint48(bound(vetoDuration, 0, type(uint48).max / 2));
vm.assume(vetoDuration < epochDuration);
depositAmount = bound(depositAmount, 1, 100 * 10 ** 18);
networkLimit = bound(networkLimit, 1, type(uint256).max);
operatorNetworkLimit1 = bound(operatorNetworkLimit1, 1, type(uint256).max / 2);
slashAmount1 = bound(slashAmount1, 1, type(uint256).max);

uint256 blockTimestamp = block.timestamp * block.timestamp / block.timestamp * block.timestamp / block.timestamp;
blockTimestamp = blockTimestamp + 1_720_700_948;
vm.warp(blockTimestamp);

(vault, delegator, slasher) = _getVaultAndDelegatorAndSlasher(epochDuration, vetoDuration);

// address network = alice;
_registerNetwork(alice, alice);
_setMaxNetworkLimit(alice, 0, type(uint256).max);

_registerOperator(alice);

_optInOperatorVault(alice);

_optInOperatorNetwork(alice, address(alice));

_deposit(alice, depositAmount);

_setNetworkLimit(alice, alice, networkLimit);

_setOperatorNetworkLimit(alice, alice, alice, operatorNetworkLimit1);

_setResolver(alice, 0, alice, "");

_setResolver(alice, 0, address(0), "");

blockTimestamp = blockTimestamp + 3 * epochDuration;
vm.warp(blockTimestamp);

uint256 slashAmountReal1 =
Math.min(slashAmount1, Math.min(depositAmount, Math.min(networkLimit, operatorNetworkLimit1)));

_requestSlash(alice, alice, alice, slashAmount1, uint48(blockTimestamp - 1), "");

(
bytes32 subnetwork_,
address operator_,
uint256 amount_,
uint48 captureTimestamp_,
// uint48 vetoDeadline_,
,
bool completed_
) = slasher.slashRequests(0);

assertEq(subnetwork_, alice.subnetwork(0));
assertEq(operator_, alice);
assertEq(amount_, slashAmountReal1);
assertEq(captureTimestamp_, uint48(blockTimestamp - 1));
// assertEq(vetoDeadline_, uint48(blockTimestamp + slasher.vetoDuration()));
assertEq(completed_, false);

assertEq(_executeSlash(alice, 0, ""), slashAmountReal1);

assertEq(vault.totalStake(), depositAmount - Math.min(slashAmountReal1, depositAmount));

(
subnetwork_,
operator_,
amount_,
captureTimestamp_,
// vetoDeadline_,
,
completed_
) = slasher.slashRequests(0);

assertEq(subnetwork_, alice.subnetwork(0));
assertEq(operator_, alice);
assertEq(amount_, slashAmountReal1);
assertEq(captureTimestamp_, uint48(blockTimestamp - 1));
// assertEq(vetoDeadline_, uint48(blockTimestamp + vetoDuration));
assertEq(completed_, true);

assertEq(slasher.cumulativeSlashAt(alice.subnetwork(0), alice, uint48(blockTimestamp - 1), ""), 0);
assertEq(slasher.cumulativeSlashAt(alice.subnetwork(0), alice, uint48(blockTimestamp), ""), slashAmountReal1);
assertEq(slasher.cumulativeSlash(alice.subnetwork(0), alice), slashAmountReal1);
}

function test_ExecuteSlashRevertOutdatedCaptureTimestamp1(
uint48 epochDuration,
uint48 vetoDuration,
Expand Down Expand Up @@ -1243,6 +1336,108 @@ contract VetoSlasherTest is Test {
_vetoSlash(alice, 0, "");
}

function test_VetoSlashRevertNoResolver1(
uint48 epochDuration,
uint48 vetoDuration,
uint256 depositAmount,
uint256 networkLimit,
uint256 operatorNetworkLimit1,
uint256 slashAmount1
) public {
epochDuration = uint48(bound(epochDuration, 1, 10 days));
vetoDuration = uint48(bound(vetoDuration, 1, type(uint48).max / 2));
vm.assume(vetoDuration + 1 <= epochDuration);
depositAmount = bound(depositAmount, 1, 100 * 10 ** 18);
networkLimit = bound(networkLimit, 1, type(uint256).max);
operatorNetworkLimit1 = bound(operatorNetworkLimit1, 1, type(uint256).max / 2);
slashAmount1 = bound(slashAmount1, 1, type(uint256).max);

uint256 blockTimestamp = block.timestamp * block.timestamp / block.timestamp * block.timestamp / block.timestamp;
blockTimestamp = blockTimestamp + 1_720_700_948;
vm.warp(blockTimestamp);

(vault, delegator, slasher) = _getVaultAndDelegatorAndSlasher(epochDuration, vetoDuration);

// address network = alice;
_registerNetwork(alice, alice);
_setMaxNetworkLimit(alice, 0, type(uint256).max);

_registerOperator(alice);

_optInOperatorVault(alice);

_optInOperatorNetwork(alice, address(alice));

_deposit(alice, depositAmount);

_setNetworkLimit(alice, alice, networkLimit);

_setOperatorNetworkLimit(alice, alice, alice, operatorNetworkLimit1);

blockTimestamp = blockTimestamp + 1;
vm.warp(blockTimestamp);

slashAmount1 = Math.min(slashAmount1, Math.min(depositAmount, Math.min(networkLimit, operatorNetworkLimit1)));

_requestSlash(alice, alice, alice, slashAmount1, uint48(blockTimestamp - 1), "");

vm.expectRevert(IVetoSlasher.NoResolver.selector);
_vetoSlash(alice, 0, "");
}

function test_VetoSlashRevertNoResolver2(
uint48 epochDuration,
uint48 vetoDuration,
uint256 depositAmount,
uint256 networkLimit,
uint256 operatorNetworkLimit1,
uint256 slashAmount1
) public {
epochDuration = uint48(bound(epochDuration, 1, 10 days));
vetoDuration = uint48(bound(vetoDuration, 1, type(uint48).max / 2));
vm.assume(vetoDuration + 1 <= epochDuration);
depositAmount = bound(depositAmount, 1, 100 * 10 ** 18);
networkLimit = bound(networkLimit, 1, type(uint256).max);
operatorNetworkLimit1 = bound(operatorNetworkLimit1, 1, type(uint256).max / 2);
slashAmount1 = bound(slashAmount1, 1, type(uint256).max);

uint256 blockTimestamp = block.timestamp * block.timestamp / block.timestamp * block.timestamp / block.timestamp;
blockTimestamp = blockTimestamp + 1_720_700_948;
vm.warp(blockTimestamp);

(vault, delegator, slasher) = _getVaultAndDelegatorAndSlasher(epochDuration, vetoDuration);

// address network = alice;
_registerNetwork(alice, alice);
_setMaxNetworkLimit(alice, 0, type(uint256).max);

_registerOperator(alice);

_optInOperatorVault(alice);

_optInOperatorNetwork(alice, address(alice));

_deposit(alice, depositAmount);

_setNetworkLimit(alice, alice, networkLimit);

_setOperatorNetworkLimit(alice, alice, alice, operatorNetworkLimit1);

_setResolver(alice, 0, alice, "");

_setResolver(alice, 0, address(0), "");

blockTimestamp = blockTimestamp + 3 * epochDuration;
vm.warp(blockTimestamp);

slashAmount1 = Math.min(slashAmount1, Math.min(depositAmount, Math.min(networkLimit, operatorNetworkLimit1)));

_requestSlash(alice, alice, alice, slashAmount1, uint48(blockTimestamp - 1), "");

vm.expectRevert(IVetoSlasher.NoResolver.selector);
_vetoSlash(alice, 0, "");
}

function test_VetoSlashRevertNotResolver(
uint48 epochDuration,
uint48 vetoDuration,
Expand Down

0 comments on commit 5c2fbb2

Please sign in to comment.