From c38535465a29b20580379d4900dca72025c06252 Mon Sep 17 00:00:00 2001 From: Kresh Date: Fri, 20 Sep 2024 03:36:30 +0800 Subject: [PATCH] feat: weaken sequencing checks --- src/contracts/slasher/BaseSlasher.sol | 22 +++++++++++++++------- src/contracts/slasher/Slasher.sol | 2 +- src/contracts/slasher/VetoSlasher.sol | 4 ++-- src/interfaces/slasher/IBaseSlasher.sol | 5 ++--- test/slasher/Slasher.t.sol | 15 +++++++-------- test/slasher/VetoSlasher.t.sol | 12 ++++++------ 6 files changed, 33 insertions(+), 27 deletions(-) diff --git a/src/contracts/slasher/BaseSlasher.sol b/src/contracts/slasher/BaseSlasher.sol index f6c8d740..7e285252 100644 --- a/src/contracts/slasher/BaseSlasher.sol +++ b/src/contracts/slasher/BaseSlasher.sol @@ -39,7 +39,7 @@ abstract contract BaseSlasher is Entity, StaticDelegateCallable, ReentrancyGuard /** * @inheritdoc IBaseSlasher */ - mapping(bytes32 subnetwork => uint48 value) public latestSlashedCaptureTimestamp; + mapping(bytes32 subnetwork => mapping(address operator => uint48 value)) public latestSlashedCaptureTimestamp; mapping(bytes32 subnetwork => mapping(address operator => Checkpoints.Trace256 amount)) internal _cumulativeSlash; @@ -96,7 +96,7 @@ abstract contract BaseSlasher is Entity, StaticDelegateCallable, ReentrancyGuard if ( captureTimestamp < Time.timestamp() - IVault(vault).epochDuration() || captureTimestamp >= Time.timestamp() - || captureTimestamp < latestSlashedCaptureTimestamp[subnetwork] + || captureTimestamp < latestSlashedCaptureTimestamp[subnetwork][operator] ) { return 0; } @@ -120,15 +120,23 @@ abstract contract BaseSlasher is Entity, StaticDelegateCallable, ReentrancyGuard } } - function _checkLatestSlashedCaptureTimestamp(bytes32 subnetwork, uint48 captureTimestamp) internal view { - if (captureTimestamp < latestSlashedCaptureTimestamp[subnetwork]) { + function _checkLatestSlashedCaptureTimestamp( + bytes32 subnetwork, + address operator, + uint48 captureTimestamp + ) internal view { + if (captureTimestamp < latestSlashedCaptureTimestamp[subnetwork][operator]) { revert OutdatedCaptureTimestamp(); } } - function _updateLatestSlashedCaptureTimestamp(bytes32 subnetwork, uint48 captureTimestamp) internal { - if (latestSlashedCaptureTimestamp[subnetwork] < captureTimestamp) { - latestSlashedCaptureTimestamp[subnetwork] = captureTimestamp; + function _updateLatestSlashedCaptureTimestamp( + bytes32 subnetwork, + address operator, + uint48 captureTimestamp + ) internal { + if (latestSlashedCaptureTimestamp[subnetwork][operator] < captureTimestamp) { + latestSlashedCaptureTimestamp[subnetwork][operator] = captureTimestamp; } } diff --git a/src/contracts/slasher/Slasher.sol b/src/contracts/slasher/Slasher.sol index 1d1648e3..797f455c 100644 --- a/src/contracts/slasher/Slasher.sol +++ b/src/contracts/slasher/Slasher.sol @@ -46,7 +46,7 @@ contract Slasher is BaseSlasher, ISlasher { revert InsufficientSlash(); } - _updateLatestSlashedCaptureTimestamp(subnetwork, captureTimestamp); + _updateLatestSlashedCaptureTimestamp(subnetwork, operator, captureTimestamp); _updateCumulativeSlash(subnetwork, operator, slashedAmount); diff --git a/src/contracts/slasher/VetoSlasher.sol b/src/contracts/slasher/VetoSlasher.sol index 9967ff5a..e88b81b8 100644 --- a/src/contracts/slasher/VetoSlasher.sol +++ b/src/contracts/slasher/VetoSlasher.sol @@ -154,7 +154,7 @@ contract VetoSlasher is BaseSlasher, IVetoSlasher { revert SlashPeriodEnded(); } - _checkLatestSlashedCaptureTimestamp(request.subnetwork, request.captureTimestamp); + _checkLatestSlashedCaptureTimestamp(request.subnetwork, request.operator, request.captureTimestamp); if (request.completed) { revert SlashRequestCompleted(); @@ -162,7 +162,7 @@ contract VetoSlasher is BaseSlasher, IVetoSlasher { request.completed = true; - _updateLatestSlashedCaptureTimestamp(request.subnetwork, request.captureTimestamp); + _updateLatestSlashedCaptureTimestamp(request.subnetwork, request.operator, request.captureTimestamp); slashedAmount = Math.min( request.amount, diff --git a/src/interfaces/slasher/IBaseSlasher.sol b/src/interfaces/slasher/IBaseSlasher.sol index f243b882..d86d474c 100644 --- a/src/interfaces/slasher/IBaseSlasher.sol +++ b/src/interfaces/slasher/IBaseSlasher.sol @@ -39,11 +39,10 @@ interface IBaseSlasher is IEntity { /** * @notice Get the latest capture timestamp that was slashed on a subnetwork. * @param subnetwork full identifier of the subnetwork (address of the network concatenated with the uint96 identifier) + * @param operator address of the operator * @return latest capture timestamp that was slashed */ - function latestSlashedCaptureTimestamp( - bytes32 subnetwork - ) external view returns (uint48); + function latestSlashedCaptureTimestamp(bytes32 subnetwork, address operator) external view returns (uint48); /** * @notice Get a cumulative slash amount for an operator on a subnetwork until a given timestamp (inclusively) using a hint. diff --git a/test/slasher/Slasher.t.sol b/test/slasher/Slasher.t.sol index fdce3b21..b4ba75bb 100644 --- a/test/slasher/Slasher.t.sol +++ b/test/slasher/Slasher.t.sol @@ -230,14 +230,14 @@ contract SlasherTest is Test { slasher.slashableStake(network.subnetwork(0), alice, uint48(blockTimestamp - 1), ""), delegator.stakeAt(network.subnetwork(0), alice, uint48(blockTimestamp - 1), "") ); - assertEq(slasher.latestSlashedCaptureTimestamp(network.subnetwork(0)), 0); + assertEq(slasher.latestSlashedCaptureTimestamp(network.subnetwork(0), alice), 0); assertEq( Math.min(slashAmount1, delegator.stakeAt(network.subnetwork(0), alice, uint48(blockTimestamp - 1), "")), _slash(alice, network, alice, slashAmount1, uint48(blockTimestamp - 1), "") ); - assertEq(slasher.latestSlashedCaptureTimestamp(network.subnetwork(0)), uint48(blockTimestamp - 1)); + assertEq(slasher.latestSlashedCaptureTimestamp(network.subnetwork(0), alice), uint48(blockTimestamp - 1)); assertEq(slasher.cumulativeSlashAt(alice.subnetwork(0), alice, uint48(blockTimestamp - 1), ""), 0); assertEq( slasher.cumulativeSlashAt(alice.subnetwork(0), alice, uint48(blockTimestamp), ""), @@ -254,6 +254,7 @@ contract SlasherTest is Test { - Math.min(slashAmount1, delegator.stakeAt(network.subnetwork(0), alice, uint48(blockTimestamp - 1), "")) ); + assertEq(slasher.latestSlashedCaptureTimestamp(network.subnetwork(0), bob), 0); assertEq(slasher.slashableStake(network.subnetwork(0), bob, uint48(blockTimestamp - epochDuration - 1), ""), 0); assertEq(slasher.slashableStake(network.subnetwork(0), bob, uint48(blockTimestamp), ""), 0); assertEq( @@ -266,7 +267,7 @@ contract SlasherTest is Test { _slash(alice, network, bob, slashAmount2, uint48(blockTimestamp - 1), "") ); - assertEq(slasher.latestSlashedCaptureTimestamp(network.subnetwork(0)), uint48(blockTimestamp - 1)); + assertEq(slasher.latestSlashedCaptureTimestamp(network.subnetwork(0), bob), uint48(blockTimestamp - 1)); assertEq(slasher.cumulativeSlashAt(alice.subnetwork(0), bob, uint48(blockTimestamp - 1), ""), 0); assertEq( slasher.cumulativeSlashAt(alice.subnetwork(0), bob, uint48(blockTimestamp), ""), @@ -293,7 +294,7 @@ contract SlasherTest is Test { vm.assume(slashAmountReal3 > 0); assertEq(slashAmountReal3, _slash(alice, network, alice, slashAmount3, uint48(blockTimestamp - 2), "")); - assertEq(slasher.latestSlashedCaptureTimestamp(network.subnetwork(0)), uint48(blockTimestamp - 2)); + assertEq(slasher.latestSlashedCaptureTimestamp(network.subnetwork(0), alice), uint48(blockTimestamp - 2)); assertEq(slasher.cumulativeSlashAt(alice.subnetwork(0), alice, uint48(blockTimestamp - 2), ""), 0); assertEq( slasher.cumulativeSlashAt(alice.subnetwork(0), alice, uint48(blockTimestamp - 1), ""), @@ -612,8 +613,7 @@ contract SlasherTest is Test { uint256 operatorNetworkLimit1, uint256 operatorNetworkLimit2, uint256 slashAmount1, - uint256 slashAmount2, - uint256 slashAmount3 + uint256 slashAmount2 ) public { epochDuration = uint48(bound(epochDuration, 2, 10 days)); depositAmount = bound(depositAmount, 1, 100 * 10 ** 18); @@ -622,7 +622,6 @@ contract SlasherTest is Test { operatorNetworkLimit2 = bound(operatorNetworkLimit2, 1, type(uint256).max / 2); slashAmount1 = bound(slashAmount1, 1, type(uint256).max); slashAmount2 = bound(slashAmount2, 1, type(uint256).max); - slashAmount3 = bound(slashAmount3, 1, type(uint256).max); uint256 blockTimestamp = block.timestamp * block.timestamp / block.timestamp * block.timestamp / block.timestamp; blockTimestamp = blockTimestamp + 1_720_700_948; @@ -656,7 +655,7 @@ contract SlasherTest is Test { _slash(alice, network, alice, slashAmount1, uint48(blockTimestamp - 1), ""); vm.expectRevert(ISlasher.InsufficientSlash.selector); - _slash(alice, network, bob, slashAmount2, uint48(blockTimestamp - 2), ""); + _slash(alice, network, alice, slashAmount2, uint48(blockTimestamp - 2), ""); } function test_SlashRevertNotNetworkMiddleware( diff --git a/test/slasher/VetoSlasher.t.sol b/test/slasher/VetoSlasher.t.sol index ca52ed48..8ffb0e77 100644 --- a/test/slasher/VetoSlasher.t.sol +++ b/test/slasher/VetoSlasher.t.sol @@ -624,7 +624,7 @@ contract VetoSlasherTest is Test { assertTrue(blockTimestamp - uint48(blockTimestamp - 1) <= epochDuration); - assertEq(slasher.latestSlashedCaptureTimestamp(alice.subnetwork(0)), 0); + assertEq(slasher.latestSlashedCaptureTimestamp(alice.subnetwork(0), alice), 0); assertEq(_executeSlash(alice, 0, ""), slashAmountReal1); @@ -640,7 +640,7 @@ contract VetoSlasherTest is Test { completed_ ) = slasher.slashRequests(0); - assertEq(slasher.latestSlashedCaptureTimestamp(alice.subnetwork(0)), captureTimestamp_); + assertEq(slasher.latestSlashedCaptureTimestamp(alice.subnetwork(0), alice), captureTimestamp_); assertEq(subnetwork_, alice.subnetwork(0)); assertEq(operator_, alice); @@ -700,7 +700,7 @@ contract VetoSlasherTest is Test { completed_ ) = slasher.slashRequests(1); - assertEq(slasher.latestSlashedCaptureTimestamp(alice.subnetwork(0)), captureTimestamp_); + assertEq(slasher.latestSlashedCaptureTimestamp(alice.subnetwork(0), alice), captureTimestamp_); assertEq(subnetwork_, alice.subnetwork(0)); assertEq(operator_, alice); @@ -810,7 +810,7 @@ contract VetoSlasherTest is Test { assertTrue(blockTimestamp - uint48(blockTimestamp - 1) <= epochDuration); - assertEq(slasher.latestSlashedCaptureTimestamp(alice.subnetwork(0)), 0); + assertEq(slasher.latestSlashedCaptureTimestamp(alice.subnetwork(0), alice), 0); assertEq(_executeSlash(alice, 0, ""), slashAmountReal1); @@ -826,7 +826,7 @@ contract VetoSlasherTest is Test { completed_ ) = slasher.slashRequests(0); - assertEq(slasher.latestSlashedCaptureTimestamp(alice.subnetwork(0)), captureTimestamp_); + assertEq(slasher.latestSlashedCaptureTimestamp(alice.subnetwork(0), alice), captureTimestamp_); assertEq(subnetwork_, alice.subnetwork(0)); assertEq(operator_, alice); @@ -859,7 +859,7 @@ contract VetoSlasherTest is Test { completed_ ) = slasher.slashRequests(1); - assertEq(slasher.latestSlashedCaptureTimestamp(alice.subnetwork(0)), captureTimestamp_); + assertEq(slasher.latestSlashedCaptureTimestamp(alice.subnetwork(0), alice), captureTimestamp_); assertEq(subnetwork_, alice.subnetwork(0)); assertEq(operator_, alice);