Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions script/Deployments.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,7 @@ contract Deploy is Script, Test {
Registry registry = new Registry(registryConfig);
emit log_address(address(registry));
urc = address(registry);
vm.serializeAddress(taiyiAddresses, "urc", address(registry));
}

function setupHoleskyAddresses() internal {
Expand Down
2 changes: 1 addition & 1 deletion src/libs/SlashingLib.sol
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,7 @@ library SlashingLib {
revert OperatorUnregistered();
}

if (registeredAt + registry.getConfig().fraudProofWindow > block.number) {
if (registeredAt + registry.getConfig().fraudProofWindow > block.timestamp) {
revert OperatorFraudProofPeriodNotOver();
}
}
Expand Down
127 changes: 63 additions & 64 deletions src/slasher/LinglongSlasher.sol
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,16 @@ contract LinglongSlasher is Initializable, OwnableUpgradeable, LinglongSlasherSt
error SlashingInProgress();
error NotURC();

/// @dev Struct to hold slashing parameters
struct SlashingParams {
bytes32 commitmentHash;
bytes32 violationType;
address challengerContract;
address operator;
address middleware;
ITaiyiRegistryCoordinator.RestakingProtocol protocol;
}

/// @notice Constructor - disabled for upgradeable pattern
constructor() {
_disableInitializers();
Expand Down Expand Up @@ -210,20 +220,66 @@ contract LinglongSlasher is Initializable, OwnableUpgradeable, LinglongSlasherSt
return challengerImpls[challenger].violationType;
}

/// @dev Helper function to validate and prepare slashing parameters
function _prepareSlashingParams(
Delegation calldata delegation,
Commitment calldata commitment,
bytes calldata evidence
)
internal
view
returns (SlashingParams memory params)
{
params.commitmentHash = keccak256(abi.encode(commitment, evidence));
if (slashedCommitments[params.commitmentHash]) revert AlreadySlashed();
if (commitment.slasher != address(this)) revert InvalidSlasher();

params.violationType = URCCommitmentTypeToViolationType[commitment.commitmentType];
params.challengerContract = violationTypeChallengers[params.violationType];
params.operator = _extractOperatorFromCommitment(commitment);
params.middleware = _findMiddleware(params.operator);

if (params.middleware == address(0)) {
revert OperatorNotInSet(params.operator, 0);
}

params.protocol = ITaiyiRegistryCoordinator(TAIYI_REGISTRY_COORDINATOR)
.getMiddlewareProtocol(params.middleware);
}

/// @inheritdoc ISlasher
function slash(
ISlasher.Delegation calldata, /* delegation */
ISlasher.Commitment calldata, /* commitment */
bytes calldata, /* evidence */
address /* challenger */
Delegation calldata delegation,
Commitment calldata commitment,
address committer,
bytes calldata evidence,
address challenger
)
external
pure
override
returns (uint256 /* slashAmountGwei */ )
{
// This method is required by ISlasher but not used
revert MethodNotSupported();
SlashingParams memory params =
_prepareSlashingParams(delegation, commitment, evidence);

// Execute slashing based on protocol
bool executed = _executeSlashingByProtocol(
params.operator,
params.middleware,
params.challengerContract,
commitment.payload,
params.protocol
);

// If direct execution is requested, execute the slashing
if (executed) {
slashedCommitments[params.commitmentHash] = true;
emit SlashingResult(params.operator, true);
}

// Always return 0 for slashAmountGwei
// This is because collateral management is handled by EigenLayer/Symbiotic
return 0;
}

/// @dev Helper function to find middleware for an operator
Expand Down Expand Up @@ -284,63 +340,6 @@ contract LinglongSlasher is Initializable, OwnableUpgradeable, LinglongSlasherSt
}
}

/// @inheritdoc ISlasher
/// @notice Slash an operator for a given commitment
/// @dev The URC Registry will call this function to slash a registered operator if supplied with a valid commitment and evidence
/// @param commitment The commitment message
/// @param evidence Arbitrary evidence for the slashing
/// @return slashAmountGwei The amount of Gwei slashed (always 0 for Taiyi, as EigenLayer handles actual slashing)
function slashFromOptIn(
ISlasher.Commitment calldata commitment,
bytes calldata evidence,
address /* challenger */
)
external
override
onlyInitialized
onlyURC
returns (uint256 slashAmountGwei)
{
// Prevent double slashing by tracking the commitment hash
bytes32 commitmentHash = keccak256(abi.encode(commitment, evidence));
if (slashedCommitments[commitmentHash]) revert AlreadySlashed();
if (commitment.slasher != address(this)) revert InvalidSlasher();

// Get violation type and challenger contract
bytes32 violationType =
URCCommitmentTypeToViolationType[commitment.commitmentType];
address challengerContract = violationTypeChallengers[violationType];

// Extract operator from commitment
address operator = _extractOperatorFromCommitment(commitment);

// Find the middleware address for this operator
address middleware = _findMiddleware(operator);
if (middleware == address(0)) {
revert OperatorNotInSet(operator, 0);
}

// Check which protocol the middleware belongs to
ITaiyiRegistryCoordinator.RestakingProtocol protocol = ITaiyiRegistryCoordinator(
TAIYI_REGISTRY_COORDINATOR
).getMiddlewareProtocol(middleware);

// Execute slashing based on protocol
bool executed = _executeSlashingByProtocol(
operator, middleware, challengerContract, commitment.payload, protocol
);

// If direct execution is requested, execute the slashing
if (executed) {
slashedCommitments[commitmentHash] = true;
emit SlashingResult(operator, true);
}

// Always return 0 for slashAmountGwei
// This is because collateral management is handled by EigenLayer/Symbiotic
return 0;
}

/// @dev Extract the operator address from a commitment
/// @param commitment The commitment to extract from
/// @return operator The extracted operator address
Expand Down
3 changes: 2 additions & 1 deletion test/EigenLayerMiddleware.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,7 @@ contract EigenlayerMiddlewareTest is Test, G2Operations {
slasher.setTaiyiRegistryCoordinator(address(registryCoordinator));
vm.stopPrank();

vm.roll(uint256(eigenLayerDeployer.MIN_WITHDRAWAL_DELAY() + 1000));
// Perform the slashing via the Registry
uint256 slashAmount =
registry.slashCommitment(registrationRoot, commitment, evidence);
Expand Down Expand Up @@ -986,7 +987,7 @@ contract EigenlayerMiddlewareTest is Test, G2Operations {
vm.startPrank(primaryOp);

// Wait for the fraud proof window to pass
vm.roll(block.number + 100 days);
vm.warp(block.timestamp + 3 hours);

// Call optInToSlasher function with the registration root
middleware.optInToSlasher(
Expand Down
2 changes: 1 addition & 1 deletion test/SymbioticMiddleware.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ contract SymbioticMiddlewareTest is POCBaseTest {
data[1] = abi.encode("validator-2-metadata");

// Wait for fraud proof window
vm.roll(block.number + 100 days);
vm.warp(block.timestamp + 3 hours);

// Call middleware's optInToSlasher
vm.startPrank(operator);
Expand Down
2 changes: 1 addition & 1 deletion test/utils/EigenlayerDeployer.sol
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ contract EigenlayerDeployer is Operators {
uint256 REQUIRED_BALANCE_WEI = 32 ether;
uint64 MAX_PARTIAL_WTIHDRAWAL_AMOUNT_GWEI = 1 ether / 1e9;
uint64 GOERLI_GENESIS_TIME = 1_616_508_000;
uint32 MIN_WITHDRAWAL_DELAY = 86_400;
uint32 public MIN_WITHDRAWAL_DELAY = 86_400;

address pauser;
address unpauser;
Expand Down
2 changes: 1 addition & 1 deletion test/utils/MockChallenger.sol
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ contract MockLinglongChallenger is ILinglongChallenger {
}

function getSlashAmount() external pure returns (uint256) {
return 1 ether;
return 1e18; // 100 WAD percent
}

function verifyProof(bytes memory) external pure returns (VerificationStatus) {
Expand Down