Skip to content

Commit

Permalink
Merge pull request #44 from symbioticfi/global-cumul
Browse files Browse the repository at this point in the history
Reduce slashing exposure
  • Loading branch information
1kresh authored Sep 26, 2024
2 parents 24d47af + 55ea39f commit de5f50e
Show file tree
Hide file tree
Showing 6 changed files with 384 additions and 64 deletions.
43 changes: 38 additions & 5 deletions src/contracts/hints/SlasherHints.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {BaseDelegatorHints} from "./DelegatorHints.sol";
import {BaseSlasher} from "../slasher/BaseSlasher.sol";
import {Hints} from "./Hints.sol";
import {Slasher} from "../slasher/Slasher.sol";
import {VaultHints} from "./VaultHints.sol";
import {Vault} from "../vault/Vault.sol";
import {VetoSlasher} from "../slasher/VetoSlasher.sol";

Expand All @@ -18,17 +19,36 @@ contract BaseSlasherHints is Hints, BaseSlasher {
using Checkpoints for Checkpoints.Trace256;

address public immutable BASE_DELEGATOR_HINTS;
address public immutable VAULT_HINTS;
address public immutable SLASHER_HINTS;
address public immutable VETO_SLASHER_HINTS;

constructor(
address baseDelegatorHints
) BaseSlasher(address(0), address(0), address(0), 0) {
constructor(address baseDelegatorHints, address vaultHints) BaseSlasher(address(0), address(0), address(0), 0) {
BASE_DELEGATOR_HINTS = baseDelegatorHints;
VAULT_HINTS = vaultHints;
SLASHER_HINTS = address(new SlasherHints(address(this)));
VETO_SLASHER_HINTS = address(new VetoSlasherHints(address(this)));
}

function globalCumulativeSlashHintInternal(
uint48 timestamp
) external view internalFunction returns (bool exists, uint32 hint) {
(exists,,, hint) = _globalCumulativeSlash.upperLookupRecentCheckpoint(timestamp);
}

function globalCumulativeSlashHint(address slasher, uint48 timestamp) public view returns (bytes memory) {
(bool exists, uint32 hint_) = abi.decode(
_selfStaticDelegateCall(
slasher, abi.encodeWithSelector(BaseSlasherHints.globalCumulativeSlashHintInternal.selector, timestamp)
),
(bool, uint32)
);

if (exists) {
return abi.encode(hint_);
}
}

function cumulativeSlashHintInternal(
bytes32 subnetwork,
address operator,
Expand Down Expand Up @@ -64,15 +84,28 @@ contract BaseSlasherHints is Hints, BaseSlasher {
address operator,
uint48 captureTimestamp
) external view returns (bytes memory) {
bytes memory activeStakeHint =
VaultHints(VAULT_HINTS).activeStakeHint(BaseSlasher(slasher).vault(), captureTimestamp);

bytes memory stakeHints = BaseDelegatorHints(BASE_DELEGATOR_HINTS).stakeHints(
Vault(BaseSlasher(slasher).vault()).delegator(), subnetwork, operator, captureTimestamp
);

bytes memory globalCumulativeSlashFromHint = globalCumulativeSlashHint(slasher, captureTimestamp);

bytes memory cumulativeSlashFromHint = cumulativeSlashHint(slasher, subnetwork, operator, captureTimestamp);

if (stakeHints.length > 0 || cumulativeSlashFromHint.length > 0) {
if (
activeStakeHint.length > 0 || stakeHints.length > 0 || globalCumulativeSlashFromHint.length > 0
|| cumulativeSlashFromHint.length > 0
) {
return abi.encode(
SlashableStakeHints({stakeHints: stakeHints, cumulativeSlashFromHint: cumulativeSlashFromHint})
SlashableStakeHints({
activeStakeHint: activeStakeHint,
stakeHints: stakeHints,
globalCumulativeSlashFromHint: globalCumulativeSlashFromHint,
cumulativeSlashFromHint: cumulativeSlashFromHint
})
);
}
}
Expand Down
38 changes: 32 additions & 6 deletions src/contracts/slasher/BaseSlasher.sol
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ abstract contract BaseSlasher is Entity, StaticDelegateCallable, ReentrancyGuard
*/
mapping(bytes32 subnetwork => mapping(address operator => uint48 value)) public latestSlashedCaptureTimestamp;

Checkpoints.Trace256 internal _globalCumulativeSlash;

mapping(bytes32 subnetwork => mapping(address operator => Checkpoints.Trace256 amount)) internal _cumulativeSlash;

modifier onlyNetworkMiddleware(
Expand All @@ -61,6 +63,20 @@ abstract contract BaseSlasher is Entity, StaticDelegateCallable, ReentrancyGuard
NETWORK_MIDDLEWARE_SERVICE = networkMiddlewareService;
}

/**
* @inheritdoc IBaseSlasher
*/
function globalCumulativeSlashAt(uint48 timestamp, bytes memory hint) public view returns (uint256) {
return _globalCumulativeSlash.upperLookupRecent(timestamp, hint);
}

/**
* @inheritdoc IBaseSlasher
*/
function globalCumulativeSlash() public view returns (uint256) {
return _globalCumulativeSlash.latest();
}

/**
* @inheritdoc IBaseSlasher
*/
Expand Down Expand Up @@ -101,15 +117,24 @@ abstract contract BaseSlasher is Entity, StaticDelegateCallable, ReentrancyGuard
return 0;
}

uint256 activeStake = IVault(vault).activeStakeAt(captureTimestamp, slashableStakeHints.activeStakeHint);
uint256 stakeAmount = IBaseDelegator(IVault(vault).delegator()).stakeAt(
subnetwork, operator, captureTimestamp, slashableStakeHints.stakeHints
);
return stakeAmount
- Math.min(
cumulativeSlash(subnetwork, operator)
- cumulativeSlashAt(subnetwork, operator, captureTimestamp, slashableStakeHints.cumulativeSlashFromHint),
stakeAmount
);
return Math.min(
activeStake
- Math.min(
globalCumulativeSlash()
- globalCumulativeSlashAt(captureTimestamp, slashableStakeHints.globalCumulativeSlashFromHint),
activeStake
),
stakeAmount
- Math.min(
cumulativeSlash(subnetwork, operator)
- cumulativeSlashAt(subnetwork, operator, captureTimestamp, slashableStakeHints.cumulativeSlashFromHint),
stakeAmount
)
);
}

function _checkNetworkMiddleware(
Expand Down Expand Up @@ -141,6 +166,7 @@ abstract contract BaseSlasher is Entity, StaticDelegateCallable, ReentrancyGuard
}

function _updateCumulativeSlash(bytes32 subnetwork, address operator, uint256 amount) internal {
_globalCumulativeSlash.push(Time.timestamp(), globalCumulativeSlash() + amount);
_cumulativeSlash[subnetwork][operator].push(Time.timestamp(), cumulativeSlash(subnetwork, operator) + amount);
}

Expand Down
18 changes: 18 additions & 0 deletions src/interfaces/slasher/IBaseSlasher.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,15 @@ interface IBaseSlasher is IEntity {

/**
* @notice Hints for a slashable stake.
* @param activeStakeHint hint for the active stake checkpoint
* @param stakeHints hints for the stake checkpoints
* @param globalCumulativeSlashFromHint hint for the global cumulative slash amount at a capture timestamp
* @param cumulativeSlashFromHint hint for the cumulative slash amount at a capture timestamp
*/
struct SlashableStakeHints {
bytes activeStakeHint;
bytes stakeHints;
bytes globalCumulativeSlashFromHint;
bytes cumulativeSlashFromHint;
}

Expand Down Expand Up @@ -44,6 +48,20 @@ interface IBaseSlasher is IEntity {
*/
function latestSlashedCaptureTimestamp(bytes32 subnetwork, address operator) external view returns (uint48);

/**
* @notice Get a global cumulative slash amount until a given timestamp (inclusively) using a hint.
* @param timestamp time point to get the cumulative slash amount until (inclusively)
* @param hint hint for the checkpoint index
* @return cumulative slash amount until the given timestamp (inclusively)
*/
function globalCumulativeSlashAt(uint48 timestamp, bytes memory hint) external view returns (uint256);

/**
* @notice Get a global cumulative slash amount.
* @return cumulative slash amount
*/
function globalCumulativeSlash() external view returns (uint256);

/**
* @notice Get a cumulative slash amount for an operator on a subnetwork until a given timestamp (inclusively) using a hint.
* @param subnetwork full identifier of the subnetwork (address of the network concatenated with the uint96 identifier)
Expand Down
6 changes: 4 additions & 2 deletions test/delegator/FullRestakeDelegator.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -887,14 +887,16 @@ contract FullRestakeDelegatorTest is Test {

uint256 slashAmount1Real =
Math.min(slashAmount1, Math.min(depositAmount, Math.min(networkLimit, operatorNetworkLimit1)));
vm.assume(slashAmount1Real < depositAmount);
assertEq(_slash(alice, network, alice, slashAmount1, uint48(blockTimestamp - 1), ""), slashAmount1Real);

assertEq(delegator.networkLimit(network.subnetwork(0)), networkLimit);
assertEq(delegator.operatorNetworkLimit(network.subnetwork(0), alice), operatorNetworkLimit1);
assertEq(delegator.operatorNetworkLimit(network.subnetwork(0), bob), operatorNetworkLimit2);

uint256 slashAmount2Real =
Math.min(slashAmount2, Math.min(depositAmount, Math.min(networkLimit, operatorNetworkLimit2)));
uint256 slashAmount2Real = Math.min(
slashAmount2, Math.min(depositAmount - slashAmount1Real, Math.min(networkLimit, operatorNetworkLimit2))
);
assertEq(_slash(alice, network, bob, slashAmount2, uint48(blockTimestamp - 1), ""), slashAmount2Real);

assertEq(delegator.networkLimit(network.subnetwork(0)), networkLimit);
Expand Down
Loading

0 comments on commit de5f50e

Please sign in to comment.