From 72eace1ef0bd42725176bc3282c1b4b8fa2dfda3 Mon Sep 17 00:00:00 2001
From: Don Perignom <10616301+madlabman@users.noreply.github.com>
Date: Tue, 7 Jan 2025 10:50:41 +0100
Subject: [PATCH] feat: drop slashing reporting (#370)

Pectra hard fork will bring a significant reduction in the initial
slashing penalty, so it no longer makes sense to keep the method that
delivers initial slashings to the module.
---
 package.json                            |   2 +-
 script/DeployCSVerifierElectra.s.sol    | 106 +++++++++++++++
 script/fork-helpers/NodeOperators.s.sol |   5 -
 script/gindex.mjs                       |   4 +-
 src/CSModule.sol                        |  40 +-----
 src/CSVerifier.sol                      |  47 -------
 src/interfaces/ICSModule.sol            |  19 +--
 src/interfaces/ICSVerifier.sol          |  12 --
 test/CSModule.t.sol                     | 173 +++---------------------
 test/CSVerifier.t.sol                   | 110 ---------------
 test/fixtures/CSVerifier/slashing.json  |  71 ----------
 yarn.lock                               |  92 +++++++++----
 12 files changed, 201 insertions(+), 480 deletions(-)
 create mode 100644 script/DeployCSVerifierElectra.s.sol
 delete mode 100644 test/fixtures/CSVerifier/slashing.json

diff --git a/package.json b/package.json
index d172cf8a..b9a0506a 100644
--- a/package.json
+++ b/package.json
@@ -14,7 +14,7 @@
     "gindex": "node script/gindex.mjs"
   },
   "dependencies": {
-    "@lodestar/types": "^1.18.1",
+    "@lodestar/types": "^1.23.1",
     "@openzeppelin/contracts": "5.0.2",
     "@openzeppelin/contracts-upgradeable": "5.0.2",
     "@openzeppelin/merkle-tree": "^1.0.6",
diff --git a/script/DeployCSVerifierElectra.s.sol b/script/DeployCSVerifierElectra.s.sol
new file mode 100644
index 00000000..254e87ef
--- /dev/null
+++ b/script/DeployCSVerifierElectra.s.sol
@@ -0,0 +1,106 @@
+// SPDX-FileCopyrightText: 2024 Lido <info@lido.fi>
+// SPDX-License-Identifier: GPL-3.0
+
+// Usage: forge script ./script/DeployCSVerifierElectra.s.sol:DeployCSVerifier[Holesky|Mainnet]
+
+pragma solidity 0.8.24;
+
+import { Script } from "forge-std/Script.sol";
+import { console2 as console } from "forge-std/console2.sol";
+
+import { CSVerifier } from "../src/CSVerifier.sol";
+import { GIndex } from "../src/lib/GIndex.sol";
+import { Slot } from "../src/lib/Types.sol";
+
+struct Config {
+    address withdrawalVault;
+    address module;
+    GIndex gIFirstWithdrawalPrev;
+    GIndex gIFirstWithdrawalCurr;
+    GIndex gIFirstValidatorPrev;
+    GIndex gIFirstValidatorCurr;
+    GIndex gIHistoricalSummariesPrev;
+    GIndex gIHistoricalSummariesCurr;
+    Slot firstSupportedSlot;
+    Slot pivotSlot;
+    uint64 slotsPerEpoch;
+}
+
+// Check the constants below via `yarn run gindex`.
+
+GIndex constant HISTORICAL_SUMMARIES_DENEB = GIndex.wrap(
+    0x0000000000000000000000000000000000000000000000000000000000003b00
+);
+GIndex constant FIRST_WITHDRAWAL_DENEB = GIndex.wrap(
+    0x0000000000000000000000000000000000000000000000000000000000e1c004
+);
+GIndex constant FIRST_VALIDATOR_DENEB = GIndex.wrap(
+    0x0000000000000000000000000000000000000000000000000056000000000028
+);
+
+GIndex constant HISTORICAL_SUMMARIES_ELECTRA = GIndex.wrap(
+    0x0000000000000000000000000000000000000000000000000000000000005b00
+);
+GIndex constant FIRST_WITHDRAWAL_ELECTRA = GIndex.wrap(
+    0x000000000000000000000000000000000000000000000000000000000161c004
+);
+GIndex constant FIRST_VALIDATOR_ELECTRA = GIndex.wrap(
+    0x0000000000000000000000000000000000000000000000000096000000000028
+);
+
+abstract contract DeployCSVerifier is Script {
+    Config internal config;
+
+    function run() public {
+        CSVerifier verifier = new CSVerifier({
+            withdrawalAddress: config.withdrawalVault,
+            module: config.module,
+            slotsPerEpoch: config.slotsPerEpoch,
+            gIFirstWithdrawalPrev: config.gIFirstWithdrawalPrev,
+            gIFirstWithdrawalCurr: config.gIFirstWithdrawalCurr,
+            gIFirstValidatorPrev: config.gIFirstValidatorPrev,
+            gIFirstValidatorCurr: config.gIFirstValidatorCurr,
+            gIHistoricalSummariesPrev: config.gIHistoricalSummariesPrev,
+            gIHistoricalSummariesCurr: config.gIHistoricalSummariesCurr,
+            firstSupportedSlot: config.firstSupportedSlot,
+            pivotSlot: config.pivotSlot
+        });
+        console.log("CSVerifier deployed at:", address(verifier));
+    }
+}
+
+contract DeployCSVerifierHolesky is DeployCSVerifier {
+    constructor() {
+        config = Config({
+            withdrawalVault: 0xF0179dEC45a37423EAD4FaD5fCb136197872EAd9,
+            module: 0x4562c3e63c2e586cD1651B958C22F88135aCAd4f,
+            slotsPerEpoch: 32,
+            gIFirstWithdrawalPrev: FIRST_WITHDRAWAL_DENEB,
+            gIFirstWithdrawalCurr: FIRST_WITHDRAWAL_ELECTRA,
+            gIFirstValidatorPrev: FIRST_VALIDATOR_DENEB,
+            gIFirstValidatorCurr: FIRST_VALIDATOR_ELECTRA,
+            gIHistoricalSummariesPrev: HISTORICAL_SUMMARIES_DENEB,
+            gIHistoricalSummariesCurr: HISTORICAL_SUMMARIES_ELECTRA,
+            firstSupportedSlot: Slot.wrap(950272), // 269_568 * 32, @see https://github.com/eth-clients/mainnet/blob/main/metadata/config.yaml#L52
+            pivotSlot: Slot.wrap(0) // TODO: Update with Electra slot.
+        });
+    }
+}
+
+contract DeployCSVerifierMainnet is DeployCSVerifier {
+    constructor() {
+        config = Config({
+            withdrawalVault: 0xB9D7934878B5FB9610B3fE8A5e441e8fad7E293f,
+            module: 0xdA7dE2ECdDfccC6c3AF10108Db212ACBBf9EA83F,
+            slotsPerEpoch: 32,
+            gIFirstWithdrawalPrev: FIRST_WITHDRAWAL_DENEB,
+            gIFirstWithdrawalCurr: FIRST_WITHDRAWAL_ELECTRA,
+            gIFirstValidatorPrev: FIRST_VALIDATOR_DENEB,
+            gIFirstValidatorCurr: FIRST_VALIDATOR_ELECTRA,
+            gIHistoricalSummariesPrev: HISTORICAL_SUMMARIES_DENEB,
+            gIHistoricalSummariesCurr: HISTORICAL_SUMMARIES_ELECTRA,
+            firstSupportedSlot: Slot.wrap(8626176), // 29_696 * 32, @see https://github.com/eth-clients/holesky/blob/main/metadata/config.yaml#L38
+            pivotSlot: Slot.wrap(0) // TODO: Update with Electra slot.
+        });
+    }
+}
diff --git a/script/fork-helpers/NodeOperators.s.sol b/script/fork-helpers/NodeOperators.s.sol
index a03230af..afb87770 100644
--- a/script/fork-helpers/NodeOperators.s.sol
+++ b/script/fork-helpers/NodeOperators.s.sol
@@ -223,11 +223,6 @@ contract NodeOperators is
         );
     }
 
-    function slash(uint256 noId, uint256 keyIndex) external broadcastVerifier {
-        csm.submitInitialSlashing(noId, keyIndex);
-        assertTrue(csm.isValidatorSlashed(noId, keyIndex));
-    }
-
     function targetLimit(
         uint256 noId,
         uint256 targetLimitMode,
diff --git a/script/gindex.mjs b/script/gindex.mjs
index 99de19b1..1d62abce 100644
--- a/script/gindex.mjs
+++ b/script/gindex.mjs
@@ -3,8 +3,8 @@
 import { concatGindices } from "@chainsafe/persistent-merkle-tree";
 import { ssz } from "@lodestar/types";
 
-for (const fork of ["deneb"]) {
-  /** @type ssz.deneb */
+for (const fork of ["deneb", "electra"]) {
+  /** @type ssz.deneb|ssz.electra */
   const Fork = ssz[fork];
 
   {
diff --git a/src/CSModule.sol b/src/CSModule.sol
index 564be754..c9f9af0d 100644
--- a/src/CSModule.sol
+++ b/src/CSModule.sol
@@ -70,8 +70,9 @@ contract CSModule is
 
     uint256 private _nonce;
     mapping(uint256 => NodeOperator) private _nodeOperators;
-    // @dev see _keyPointer function for details of noKeyIndexPacked structure
+    /// @dev see _keyPointer function for details of noKeyIndexPacked structure
     mapping(uint256 noKeyIndexPacked => bool) private _isValidatorWithdrawn;
+    /// @dev DEPRECATED! No writes expected after Pectra hard-fork
     mapping(uint256 noKeyIndexPacked => bool) private _isValidatorSlashed;
 
     uint64 private _totalDepositedValidators;
@@ -888,13 +889,13 @@ contract CSModule is
         emit WithdrawalSubmitted(nodeOperatorId, keyIndex, amount, pubkey);
 
         if (isSlashed) {
+            // NOTE: Can't remove the check so far to avoid double-accounting of penalty. Make sure
+            // we decided to go with CSVerifier with no processSalshingProof function deployed first
+            // with some meaningful grace period.
             if (_isValidatorSlashed[pointer]) {
-                // Account already burned value
                 unchecked {
                     amount += INITIAL_SLASHING_PENALTY;
                 }
-            } else {
-                _isValidatorSlashed[pointer] = true;
             }
             // Bond curve should be reset to default in case of slashing. See https://hackmd.io/@lido/SygBLW5ja
             accounting.resetBondCurve(nodeOperatorId);
@@ -913,37 +914,6 @@ contract CSModule is
         });
     }
 
-    /// @inheritdoc ICSModule
-    function submitInitialSlashing(
-        uint256 nodeOperatorId,
-        uint256 keyIndex
-    ) external onlyRole(VERIFIER_ROLE) {
-        _onlyExistingNodeOperator(nodeOperatorId);
-        NodeOperator storage no = _nodeOperators[nodeOperatorId];
-        if (keyIndex >= no.totalDepositedKeys) {
-            revert SigningKeysInvalidOffset();
-        }
-
-        uint256 pointer = _keyPointer(nodeOperatorId, keyIndex);
-
-        if (_isValidatorSlashed[pointer]) {
-            revert AlreadySubmitted();
-        }
-
-        _isValidatorSlashed[pointer] = true;
-
-        bytes memory pubkey = SigningKeys.loadKeys(nodeOperatorId, keyIndex, 1);
-        emit InitialSlashingSubmitted(nodeOperatorId, keyIndex, pubkey);
-
-        accounting.penalize(nodeOperatorId, INITIAL_SLASHING_PENALTY);
-
-        // Nonce should be updated if depositableValidators change
-        _updateDepositableValidatorsCount({
-            nodeOperatorId: nodeOperatorId,
-            incrementNonceIfUpdated: true
-        });
-    }
-
     /// @inheritdoc IStakingModule
     /// @dev Resets the key removal charge
     /// @dev Changing the WC means that the current deposit data in the queue is not valid anymore and can't be deposited
diff --git a/src/CSVerifier.sol b/src/CSVerifier.sol
index 925bdd1a..4ce50582 100644
--- a/src/CSVerifier.sol
+++ b/src/CSVerifier.sol
@@ -103,53 +103,6 @@ contract CSVerifier is ICSVerifier {
         PIVOT_SLOT = pivotSlot;
     }
 
-    /// @inheritdoc ICSVerifier
-    function processSlashingProof(
-        ProvableBeaconBlockHeader calldata beaconBlock,
-        SlashingWitness calldata witness,
-        uint256 nodeOperatorId,
-        uint256 keyIndex
-    ) external {
-        if (beaconBlock.header.slot < FIRST_SUPPORTED_SLOT) {
-            revert UnsupportedSlot(beaconBlock.header.slot);
-        }
-
-        {
-            bytes32 trustedHeaderRoot = _getParentBlockRoot(
-                beaconBlock.rootsTimestamp
-            );
-            if (trustedHeaderRoot != beaconBlock.header.hashTreeRoot()) {
-                revert InvalidBlockHeader();
-            }
-        }
-
-        bytes memory pubkey = MODULE.getSigningKeys(
-            nodeOperatorId,
-            keyIndex,
-            1
-        );
-
-        Validator memory validator = Validator({
-            pubkey: pubkey,
-            withdrawalCredentials: witness.withdrawalCredentials,
-            effectiveBalance: witness.effectiveBalance,
-            slashed: true,
-            activationEligibilityEpoch: witness.activationEligibilityEpoch,
-            activationEpoch: witness.activationEpoch,
-            exitEpoch: witness.exitEpoch,
-            withdrawableEpoch: witness.withdrawableEpoch
-        });
-
-        SSZ.verifyProof({
-            proof: witness.validatorProof,
-            root: beaconBlock.header.stateRoot,
-            leaf: validator.hashTreeRoot(),
-            gI: _getValidatorGI(witness.validatorIndex, beaconBlock.header.slot)
-        });
-
-        MODULE.submitInitialSlashing(nodeOperatorId, keyIndex);
-    }
-
     /// @inheritdoc ICSVerifier
     function processWithdrawalProof(
         ProvableBeaconBlockHeader calldata beaconBlock,
diff --git a/src/interfaces/ICSModule.sol b/src/interfaces/ICSModule.sol
index d26afe20..b0dd8f85 100644
--- a/src/interfaces/ICSModule.sol
+++ b/src/interfaces/ICSModule.sol
@@ -107,11 +107,6 @@ interface ICSModule is IQueueLib, INOAddresses, IAssetRecovererLib {
         uint256 amount,
         bytes pubkey
     );
-    event InitialSlashingSubmitted(
-        uint256 indexed nodeOperatorId,
-        uint256 keyIndex,
-        bytes pubkey
-    );
 
     event PublicRelease();
     event KeyRemovalChargeSet(uint256 amount);
@@ -516,16 +511,6 @@ interface ICSModule is IQueueLib, INOAddresses, IAssetRecovererLib {
         uint256 keysCount
     ) external view returns (bytes memory keys, bytes memory signatures);
 
-    /// @notice Report Node Operator's key as slashed and apply the initial slashing penalty
-    /// @notice Called by the Verifier contract.
-    ///         See `CSVerifier.processSlashingProof` to use this method permissionless
-    /// @param nodeOperatorId ID of the Node Operator
-    /// @param keyIndex Index of the slashed key in the Node Operator's keys storage
-    function submitInitialSlashing(
-        uint256 nodeOperatorId,
-        uint256 keyIndex
-    ) external;
-
     /// @notice Report Node Operator's key as withdrawn and settle withdrawn amount
     /// @notice Called by the Verifier contract.
     ///         See `CSVerifier.processWithdrawalProof` to use this method permissionless
@@ -540,7 +525,9 @@ interface ICSModule is IQueueLib, INOAddresses, IAssetRecovererLib {
         bool isSlashed
     ) external;
 
-    /// @notice Check if the given Node Operator's key is reported as slashed
+    /// @notice DEPRECATED! Check if the given Node Operator's key is reported as slashed
+    /// @notice Since pectra update the contract doesn't store slashing flag of a withdrawn
+    /// validator
     /// @param nodeOperatorId ID of the Node Operator
     /// @param keyIndex Index of the key to check
     /// @return Validator reported as slashed flag
diff --git a/src/interfaces/ICSVerifier.sol b/src/interfaces/ICSVerifier.sol
index 02f52647..8412d7c4 100644
--- a/src/interfaces/ICSVerifier.sol
+++ b/src/interfaces/ICSVerifier.sol
@@ -87,18 +87,6 @@ interface ICSVerifier {
 
     function MODULE() external view returns (ICSModule);
 
-    /// @notice Verify slashing proof and report slashing to the module for valid proofs
-    /// @param beaconBlock Beacon block header
-    /// @param witness Slashing witness against the `beaconBlock`'s state root.
-    /// @param nodeOperatorId ID of the Node Operator
-    /// @param keyIndex Index of the validator key in the Node Operator's key storage
-    function processSlashingProof(
-        ProvableBeaconBlockHeader calldata beaconBlock,
-        SlashingWitness calldata witness,
-        uint256 nodeOperatorId,
-        uint256 keyIndex
-    ) external;
-
     /// @notice Verify withdrawal proof and report withdrawal to the module for valid proofs
     /// @param beaconBlock Beacon block header
     /// @param witness Withdrawal witness against the `beaconBlock`'s state root.
diff --git a/test/CSModule.t.sol b/test/CSModule.t.sol
index 3ef5d091..868f8ce3 100644
--- a/test/CSModule.t.sol
+++ b/test/CSModule.t.sol
@@ -6092,6 +6092,8 @@ contract CSMCompensateELRewardsStealingPenalty is CSMCommon {
 }
 
 contract CsmSubmitWithdrawal is CSMCommon {
+    using stdStorage for StdStorage;
+
     function test_submitWithdrawal() public assertInvariants {
         uint256 keyIndex = 0;
         uint256 noId = createNodeOperator();
@@ -6162,24 +6164,35 @@ contract CsmSubmitWithdrawal is CSMCommon {
         uint256 noId = createNodeOperator();
         csm.obtainDepositData(1, "");
 
-        csm.submitInitialSlashing(noId, 0);
+        // Pretend someone called csm.submitInitialSlashing(noId, 0) before, so we have
+        // _isValidatorSlashed[...] == true in the module.
+        stdstore
+            .target(address(csm))
+            .sig(csm.isValidatorSlashed.selector)
+            .with_key(noId)
+            .with_key(keyIndex)
+            .checked_write(true);
+
         assertTrue(csm.isValidatorSlashed(noId, keyIndex));
 
-        uint256 exitBalance = DEPOSIT_SIZE - csm.INITIAL_SLASHING_PENALTY();
+        uint256 diffAfterInitialPenalty = 0.75432 ether;
+        uint256 exitBalance = DEPOSIT_SIZE -
+            csm.INITIAL_SLASHING_PENALTY() -
+            diffAfterInitialPenalty;
 
         vm.expectCall(
             address(accounting),
             abi.encodeWithSelector(
                 accounting.penalize.selector,
                 noId,
-                0.05 ether
+                diffAfterInitialPenalty
             )
         );
         vm.expectCall(
             address(accounting),
             abi.encodeWithSelector(accounting.resetBondCurve.selector, noId)
         );
-        csm.submitWithdrawal(noId, keyIndex, exitBalance - 0.05 ether, true);
+        csm.submitWithdrawal(noId, keyIndex, exitBalance, true);
     }
 
     function test_submitWithdrawal_slashedIsNotReported()
@@ -6207,7 +6220,7 @@ contract CsmSubmitWithdrawal is CSMCommon {
             abi.encodeWithSelector(accounting.resetBondCurve.selector, noId)
         );
         csm.submitWithdrawal(noId, keyIndex, exitBalance - 0.05 ether, true);
-        assertTrue(csm.isValidatorSlashed(noId, keyIndex));
+        assertFalse(csm.isValidatorSlashed(noId, keyIndex)); // We do not track it anymore.
     }
 
     function test_submitWithdrawal_unbondedKeys() public assertInvariants {
@@ -6251,118 +6264,6 @@ contract CsmSubmitWithdrawal is CSMCommon {
     }
 }
 
-contract CsmSubmitInitialSlashing is CSMCommon {
-    function test_submitInitialSlashing() public assertInvariants {
-        uint256 keyIndex = 0;
-        uint256 noId = createNodeOperator(2);
-        (bytes memory pubkey, ) = csm.obtainDepositData(1, "");
-        uint256 penaltyAmount = csm.INITIAL_SLASHING_PENALTY();
-        uint256 nonce = csm.getNonce();
-
-        vm.expectEmit(true, true, true, true, address(csm));
-        emit ICSModule.InitialSlashingSubmitted(noId, keyIndex, pubkey);
-        vm.expectCall(
-            address(accounting),
-            abi.encodeWithSelector(
-                accounting.penalize.selector,
-                noId,
-                penaltyAmount
-            )
-        );
-        csm.submitInitialSlashing(noId, keyIndex);
-
-        bool slashed = csm.isValidatorSlashed(noId, keyIndex);
-        assertTrue(slashed);
-
-        assertEq(csm.getNonce(), nonce + 1);
-    }
-
-    function test_submitInitialSlashing_Overbonded() public assertInvariants {
-        uint256 noId = createNodeOperator(2);
-        vm.deal(nodeOperator, 32 ether);
-        vm.prank(nodeOperator);
-        csm.depositETH{ value: 32 ether }(0);
-        (bytes memory pubkey, ) = csm.obtainDepositData(1, "");
-        uint256 penaltyAmount = csm.INITIAL_SLASHING_PENALTY();
-
-        uint256 nonce = csm.getNonce();
-
-        vm.expectEmit(true, true, true, true, address(csm));
-        emit ICSModule.InitialSlashingSubmitted(noId, 0, pubkey);
-        vm.expectCall(
-            address(accounting),
-            abi.encodeWithSelector(
-                accounting.penalize.selector,
-                noId,
-                penaltyAmount
-            )
-        );
-        csm.submitInitialSlashing(noId, 0);
-
-        assertEq(csm.getNonce(), nonce);
-    }
-
-    function test_submitInitialSlashing_differentKeys()
-        public
-        assertInvariants
-    {
-        uint256 noId = createNodeOperator(2);
-        (bytes memory pubkeys, ) = csm.obtainDepositData(2, "");
-
-        bytes memory pubkey0 = slice(pubkeys, 0, 48);
-        bytes memory pubkey1 = slice(pubkeys, 48, 48);
-
-        vm.expectEmit(true, true, true, true, address(csm));
-        emit ICSModule.InitialSlashingSubmitted(noId, 0, pubkey0);
-        csm.submitInitialSlashing(noId, 0);
-
-        vm.expectEmit(true, true, true, true, address(csm));
-        emit ICSModule.InitialSlashingSubmitted(noId, 1, pubkey1);
-        csm.submitInitialSlashing(noId, 1);
-    }
-
-    function test_submitInitialSlashing_outOfBond() public assertInvariants {
-        uint256 keyIndex = 0;
-        uint256 noId = createNodeOperator();
-        csm.obtainDepositData(1, "");
-
-        csm.reportELRewardsStealingPenalty(
-            noId,
-            blockhash(block.number),
-            DEPOSIT_SIZE - csm.INITIAL_SLASHING_PENALTY()
-        );
-        csm.submitInitialSlashing(noId, keyIndex);
-    }
-
-    function test_submitInitialSlashing_RevertWhen_NoNodeOperator()
-        public
-        assertInvariants
-    {
-        vm.expectRevert(ICSModule.NodeOperatorDoesNotExist.selector);
-        csm.submitInitialSlashing(0, 0);
-    }
-
-    function test_submitInitialSlashing_RevertWhen_InvalidKeyIndexOffset()
-        public
-    {
-        uint256 noId = createNodeOperator();
-        vm.expectRevert(ICSModule.SigningKeysInvalidOffset.selector);
-        csm.submitInitialSlashing(noId, 0);
-    }
-
-    function test_submitInitialSlashing_RevertWhen_AlreadySubmitted()
-        public
-        assertInvariants
-    {
-        uint256 noId = createNodeOperator();
-        csm.obtainDepositData(1, "");
-
-        csm.submitInitialSlashing(noId, 0);
-        vm.expectRevert(ICSModule.AlreadySubmitted.selector);
-        csm.submitInitialSlashing(noId, 0);
-    }
-}
-
 contract CsmGetStakingModuleSummary is CSMCommon {
     function test_getStakingModuleSummary_depositableValidators()
         public
@@ -6579,31 +6480,6 @@ contract CSMAccessControl is CSMCommonNoRoles {
         csm.submitWithdrawal(noId, 0, 1 ether, false);
     }
 
-    function test_verifierRole_submitInitialSlashing() public {
-        csm.activatePublicRelease();
-        uint256 noId = createNodeOperator();
-        bytes32 role = csm.VERIFIER_ROLE();
-
-        vm.startPrank(admin);
-        csm.grantRole(role, actor);
-        csm.grantRole(csm.STAKING_ROUTER_ROLE(), admin);
-        csm.obtainDepositData(1, "");
-        vm.stopPrank();
-
-        vm.prank(actor);
-        csm.submitInitialSlashing(noId, 0);
-    }
-
-    function test_verifierRole_submitInitialSlashing_revert() public {
-        csm.activatePublicRelease();
-        uint256 noId = createNodeOperator();
-        bytes32 role = csm.VERIFIER_ROLE();
-
-        vm.prank(stranger);
-        expectRoleRevert(stranger, role);
-        csm.submitInitialSlashing(noId, 0);
-    }
-
     function test_recovererRole() public {
         bytes32 role = csm.RECOVERER_ROLE();
         vm.prank(admin);
@@ -7231,19 +7107,6 @@ contract CSMDepositableValidatorsCount is CSMCommon {
         assertEq(csm.getNonce(), nonce + 1);
     }
 
-    function test_depositableValidatorsCountChanges_OnInitialSlashing()
-        public
-        assertInvariants
-    {
-        // 1 key becomes unbonded till withdrawal.
-        uint256 noId = createNodeOperator(2);
-        csm.obtainDepositData(1, "");
-        assertEq(csm.getNodeOperator(noId).depositableValidatorsCount, 1);
-        csm.submitInitialSlashing(noId, 0); // The first key was slashed.
-        assertEq(csm.getNodeOperator(noId).depositableValidatorsCount, 0);
-        assertEq(getStakingModuleSummary().depositableValidatorsCount, 0);
-    }
-
     function test_depositableValidatorsCountChanges_OnWithdrawal()
         public
         assertInvariants
diff --git a/test/CSVerifier.t.sol b/test/CSVerifier.t.sol
index bda7d22f..ead089fa 100644
--- a/test/CSVerifier.t.sol
+++ b/test/CSVerifier.t.sol
@@ -35,13 +35,6 @@ contract CSVerifierTestBase is Test, Utilities {
         ICSVerifier.WithdrawalWitness witness;
     }
 
-    struct SlashingFixture {
-        bytes32 _blockRoot;
-        bytes _pubkey;
-        ICSVerifier.ProvableBeaconBlockHeader beaconBlock;
-        ICSVerifier.SlashingWitness witness;
-    }
-
     CSVerifier public verifier;
     Stub public module;
     Slot public firstSupportedSlot;
@@ -169,109 +162,6 @@ contract CSVerifierTestConstructor is CSVerifierTestBase {
     }
 }
 
-contract CSVerifierSlashingTest is CSVerifierTestBase {
-    function setUp() public {
-        module = new Stub();
-
-        verifier = new CSVerifier({
-            withdrawalAddress: nextAddress("WITHDRAWAL_ADDRESS"),
-            module: address(module),
-            slotsPerEpoch: 32,
-            gIHistoricalSummariesPrev: pack(0x0, 0), // We don't care of the value for this test.
-            gIHistoricalSummariesCurr: pack(0x0, 0), // We don't care of the value for this test.
-            gIFirstWithdrawalPrev: pack(0xe1c0, 4),
-            gIFirstWithdrawalCurr: pack(0xe1c0, 4),
-            gIFirstValidatorPrev: pack(0x560000000000, 40),
-            gIFirstValidatorCurr: pack(0x560000000000, 40),
-            firstSupportedSlot: Slot.wrap(100_500), // Any value less than the slots from the fixtures.
-            pivotSlot: Slot.wrap(100_500)
-        });
-    }
-
-    function test_processSlashingProof() public {
-        SlashingFixture memory fixture = abi.decode(
-            _readFixture("slashing.json"),
-            (SlashingFixture)
-        );
-
-        _setMocksSlashing(fixture);
-
-        verifier.processSlashingProof(
-            fixture.beaconBlock,
-            fixture.witness,
-            0,
-            0
-        );
-    }
-
-    function test_processSlashingProof_RevertWhen_UnsupportedSlot() public {
-        SlashingFixture memory fixture = abi.decode(
-            _readFixture("slashing.json"),
-            (SlashingFixture)
-        );
-
-        _setMocksSlashing(fixture);
-
-        fixture.beaconBlock.header.slot = verifier.FIRST_SUPPORTED_SLOT().dec();
-
-        vm.expectRevert(
-            abi.encodeWithSelector(
-                ICSVerifier.UnsupportedSlot.selector,
-                fixture.beaconBlock.header.slot
-            )
-        );
-        verifier.processSlashingProof(
-            fixture.beaconBlock,
-            fixture.witness,
-            0,
-            0
-        );
-    }
-
-    function test_processSlashingProof_RevertWhen_InvalidBlockHeader() public {
-        SlashingFixture memory fixture = abi.decode(
-            _readFixture("slashing.json"),
-            (SlashingFixture)
-        );
-
-        _setMocksSlashing(fixture);
-
-        vm.mockCall(
-            verifier.BEACON_ROOTS(),
-            abi.encode(fixture.beaconBlock.rootsTimestamp),
-            abi.encode("lol")
-        );
-
-        vm.expectRevert(ICSVerifier.InvalidBlockHeader.selector);
-        verifier.processSlashingProof(
-            fixture.beaconBlock,
-            fixture.witness,
-            0,
-            0
-        );
-    }
-
-    function _setMocksSlashing(SlashingFixture memory fixture) internal {
-        vm.mockCall(
-            verifier.BEACON_ROOTS(),
-            abi.encode(fixture.beaconBlock.rootsTimestamp),
-            abi.encode(fixture._blockRoot)
-        );
-
-        vm.mockCall(
-            address(module),
-            abi.encodeWithSelector(ICSModule.getSigningKeys.selector, 0, 0),
-            abi.encode(fixture._pubkey)
-        );
-
-        vm.mockCall(
-            address(module),
-            abi.encodeWithSelector(ICSModule.submitInitialSlashing.selector),
-            ""
-        );
-    }
-}
-
 contract CSVerifierWithdrawalTest is CSVerifierTestBase {
     function setUp() public {
         module = new Stub();
diff --git a/test/fixtures/CSVerifier/slashing.json b/test/fixtures/CSVerifier/slashing.json
deleted file mode 100644
index 703e47e6..00000000
--- a/test/fixtures/CSVerifier/slashing.json
+++ /dev/null
@@ -1,71 +0,0 @@
-{
-  "_blockRoot": "0x56073a5bf24e8a3ea2033ad10a5039a7a7a6884086b67053c90d38f104ae89cf",
-  "_pubkey": "0xa5b3dfbe60eb74b9224ec56bb253e18cf032c999818f10bc51fc13a9c5584eb66624796a400c2047ac248146f58a2d3d",
-  "beaconBlock": {
-    "blockHeader": {
-      "00__slot": 1743359,
-      "01__proposerIndex": 1337,
-      "02__parentRoot": "0x5db6dfb2b5e735bafb437a76b9e525e958d2aef589649e862bfbc02964edf5ab",
-      "03__stateRoot": "0x21205c716572ae05692c0f8a4c64fd84e504cbb1a16fa0371701adbab756dd72",
-      "04__bodyRoot": "0x459390eed4479eb49b71efadcc3b540bbc60073f196e0409588d6cc9eafbe5fa"
-    },
-    "rootsTimestamp": 42
-  },
-  "witness": {
-    "01__validatorIndex": 1551477,
-    "02__withdrawalCredentials": "0x010000000000000000000000c93c3e1c11037f5bd50f21cfc1a02aba5427b2f3",
-    "03__effectiveBalance": 0,
-    "04__activationEligibilityEpoch": 21860,
-    "05__activationEpoch": 21866,
-    "06__exitEpoch": 41672,
-    "07__withdrawableEpoch": 41928,
-    "08__validatorProof": [
-      "0x3efdddf56d4e2f27814f3c7a33242b208eba5496d4375ae1354189cb45022265",
-      "0xa80637a489bc503b27c5b8667d7147ed1c52f945d52aae090d1911941ba3bc0a",
-      "0x55437fead4a169949a4686ee6d0d7777d0006000439d01e8f1ff86ed3b944555",
-      "0x1ded2cca8f4b1667158ee2db6c5bc13488283921d0bc19ee870e9e96182e8ab9",
-      "0x6e8978026de507444dff6c59d0159f56ac57bc0d838b0060c81547de5e4c57b8",
-      "0x3a01de7f6c7c3840419cf3fcf7910d791e0d7ef471288331d5fe56398b7f1b3f",
-      "0x1bfe62a72cfbcef5a057464913e141d625ecf04eaa34c3c85e047a32a7b28ec8",
-      "0x31129869b19b584b2032d8b3fa901ec86ca3213983620a2e085b14506a53b9b6",
-      "0xb010816d1a36de59273332db53d2c20eb91a07b8c5327790a1d2c6cdbe9cdeba",
-      "0x9acaa36e34da8ba19c54d7b9f6d9e5740febc1b30b61cb19d0891e79c2642243",
-      "0x43c6392e38689b6666857ed9dba67b486421dce3824878abd891107ff2b62757",
-      "0xe38fab163d8350d6ffd316794bfb000d97a72c85eccc4062e80308e94a9939d8",
-      "0x96428f8477bf31469220152f22fb9c321e74aa08774dd5b4de6d11e8fc23d272",
-      "0x384a25acafbec9f1c547eb89766051cf16cb4fd4d49a7ddadf7bd32e01ef4489",
-      "0x4c82fe5eca765bbd31dae8cb40b2229526d89c64205a5d5048551dfd9f0215c6",
-      "0x552980838151f3db4e1e3e69689b481f784f947a147ec9b2df4f6d9d1eaf1147",
-      "0xa527b49b664e1311993cb4d5d77c8e3ef9bbe06b142e76f1035a5768b1443c79",
-      "0x889f02af50613a82f8e1ed3f654bf1f829c58e4cd1d67bf608793cfe80ec6165",
-      "0xbc676437f6c3c377e4aac6eb1a73c19e6a35db70a44604d791172912b23e2b8e",
-      "0x06a06bbdd7f1700337393726ed1ca6e63a5a591607dcacf1766119753ec81292",
-      "0xef1b63eac20336d5cd32028b1963f7c80869ae34ba13ece0965c51540abc1709",
-      "0x8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9c",
-      "0xfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167",
-      "0xe71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d7",
-      "0x31206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc0",
-      "0x21352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544",
-      "0x619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a46765",
-      "0x7cdd2986268250628d0c10e385c58c6191e6fbe05191bcc04f133f2cea72c1c4",
-      "0x848930bd7ba8cac54661072113fb278869e07bb8587f91392933374d017bcbe1",
-      "0x8869ff2c22b28cc10510d9853292803328be4fb0e80495e8bb8d271f5b889636",
-      "0xb5fe28e79f1b850f8658246ce9b6a1e7b49fc06db7143e8fe0b4f2b0c5523a5c",
-      "0x985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7",
-      "0xc6f67e02e6e4e1bdefb994c6098953f34636ba2b6ca20a4721d2b26a886722ff",
-      "0x1c9a7e5ff1cf48b4ad1582d3f4e4a1004f3b20d8c5a2b71387a4254ad933ebc5",
-      "0x2f075ae229646b6f6aed19a5e372cf295081401eb893ff599b3f9acc0c0d3e7d",
-      "0x328921deb59612076801e8cd61592107b5c67c79b846595cc6320c395b46362c",
-      "0xbfb909fdb236ad2411b4e4883810a074b840464689986c3f8a8091827e17c327",
-      "0x55d8fb3687ba3ba49f342c77f5a1f89bec83d811446e1a467139213d640b6a74",
-      "0xf7210d4f8e7e1039790e7bf4efa207555a10a6db1dd4b95da313aaa88b88fe76",
-      "0xad21b516cbc645ffe34ab5de1c8aef8cd4e7f8d2b51e8e1456adc7563cda206f",
-      "0xcb2c1a0000000000000000000000000000000000000000000000000000000000",
-      "0xbc36040000000000000000000000000000000000000000000000000000000000",
-      "0x0ed6189bc73badc7cf2cd2f0e54551a3b1d2192ee26bbb58d670d069b31b148e",
-      "0x80eb44447d4f078e878a8b5fd2e3d3833a368e1d12239503e9f7b4605a0d782a",
-      "0xbb2952772995323016b98233c26e96e5c54955fda62e643cb56981da6aab7365",
-      "0xda5ca7afba0d19d345e85d2825fc3078eefdd76ead776b108fe0eac9aa96e5e6"
-    ]
-  }
-}
diff --git a/yarn.lock b/yarn.lock
index 176e3fda..3572db2b 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -33,30 +33,70 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@chainsafe/as-sha256@npm:^0.4.1, @chainsafe/as-sha256@npm:^0.4.2":
-  version: 0.4.2
-  resolution: "@chainsafe/as-sha256@npm:0.4.2"
-  checksum: 10c0/af1abf43340e93fb67b570b85dc88e71c97d00dbc3e68f1d54fe3bb0d99fd8ad7f9047f3f1aeb4c27900698e6653382e123c58b888b0c3946afb9f2067e35c55
+"@chainsafe/as-sha256@npm:0.5.0":
+  version: 0.5.0
+  resolution: "@chainsafe/as-sha256@npm:0.5.0"
+  checksum: 10c0/53c4249940d824d593b89196c14e2a0f75dc13080d1c4dc5c9b0a04ee40a06185c2811f997620090decbe24c3e69ea23b0e52780671721a848e7ba1186a0f986
   languageName: node
   linkType: hard
 
-"@chainsafe/persistent-merkle-tree@npm:^0.7.1":
-  version: 0.7.2
-  resolution: "@chainsafe/persistent-merkle-tree@npm:0.7.2"
+"@chainsafe/hashtree-darwin-arm64@npm:1.0.1":
+  version: 1.0.1
+  resolution: "@chainsafe/hashtree-darwin-arm64@npm:1.0.1"
+  conditions: os=darwin & cpu=arm64
+  languageName: node
+  linkType: hard
+
+"@chainsafe/hashtree-linux-arm64-gnu@npm:1.0.1":
+  version: 1.0.1
+  resolution: "@chainsafe/hashtree-linux-arm64-gnu@npm:1.0.1"
+  conditions: os=linux & cpu=arm64 & libc=glibc
+  languageName: node
+  linkType: hard
+
+"@chainsafe/hashtree-linux-x64-gnu@npm:1.0.1":
+  version: 1.0.1
+  resolution: "@chainsafe/hashtree-linux-x64-gnu@npm:1.0.1"
+  conditions: os=linux & cpu=x64 & libc=glibc
+  languageName: node
+  linkType: hard
+
+"@chainsafe/hashtree@npm:1.0.1":
+  version: 1.0.1
+  resolution: "@chainsafe/hashtree@npm:1.0.1"
   dependencies:
-    "@chainsafe/as-sha256": "npm:^0.4.2"
+    "@chainsafe/hashtree-darwin-arm64": "npm:1.0.1"
+    "@chainsafe/hashtree-linux-arm64-gnu": "npm:1.0.1"
+    "@chainsafe/hashtree-linux-x64-gnu": "npm:1.0.1"
+  dependenciesMeta:
+    "@chainsafe/hashtree-darwin-arm64":
+      optional: true
+    "@chainsafe/hashtree-linux-arm64-gnu":
+      optional: true
+    "@chainsafe/hashtree-linux-x64-gnu":
+      optional: true
+  checksum: 10c0/fb2589727f222875f2e89459424809782717ce1e24a60c08ca413874134f219061a0389114a14c1fcb66a81ea34f3511a4d5916d8094f7961137fc6230d3c53f
+  languageName: node
+  linkType: hard
+
+"@chainsafe/persistent-merkle-tree@npm:0.8.0":
+  version: 0.8.0
+  resolution: "@chainsafe/persistent-merkle-tree@npm:0.8.0"
+  dependencies:
+    "@chainsafe/as-sha256": "npm:0.5.0"
+    "@chainsafe/hashtree": "npm:1.0.1"
     "@noble/hashes": "npm:^1.3.0"
-  checksum: 10c0/23b3afb8435c2f294c53c2116b32ad3c0998c1e9012165bfc2d26c9a024a61948b36762a1219150fb6beac627f69b5cb8bb81e1766ef779c17e97a4dd0cc8e19
+  checksum: 10c0/5fd2567c815f21540195f825b58751220a66849946b6333df40de5969f062a556240bc0679eeb8b1bf69d807c2ba10a2c8052e71ef62b0322bcc56e18b242d3a
   languageName: node
   linkType: hard
 
-"@chainsafe/ssz@npm:^0.15.1":
-  version: 0.15.1
-  resolution: "@chainsafe/ssz@npm:0.15.1"
+"@chainsafe/ssz@npm:^0.18.0":
+  version: 0.18.0
+  resolution: "@chainsafe/ssz@npm:0.18.0"
   dependencies:
-    "@chainsafe/as-sha256": "npm:^0.4.1"
-    "@chainsafe/persistent-merkle-tree": "npm:^0.7.1"
-  checksum: 10c0/59115f52606ed1240e4be9a935cbe4133a34cfd46a43f1ce9e5019f52dd085de730451fcd29ca3e4e018637acaa6fd9189f52f517ea5853801b15698fcbde7ec
+    "@chainsafe/as-sha256": "npm:0.5.0"
+    "@chainsafe/persistent-merkle-tree": "npm:0.8.0"
+  checksum: 10c0/fd99ef7f98d04d1b1cd8b865a3f4861953ecd6946923073a65184c02f7e699ee6cc6bd62c4f690b70e7af91b8118f972671d2ecf583f92a0c8144ba5b96b549e
   languageName: node
   linkType: hard
 
@@ -273,21 +313,21 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@lodestar/params@npm:^1.18.1":
-  version: 1.18.1
-  resolution: "@lodestar/params@npm:1.18.1"
-  checksum: 10c0/c86db04b6351acdbeae315c51346660aa4b4cb5c89ce13a3ecf0022a2f3e5f53e188756fa7151ad4ab858f4c0089a5feed235b1798525bd2e39ce9ae64cfcbdf
+"@lodestar/params@npm:^1.23.1":
+  version: 1.23.1
+  resolution: "@lodestar/params@npm:1.23.1"
+  checksum: 10c0/bffe4125990f2e1e6a4cba317a681f93743db83f3fc0773b1c1eadc2d3b6f787678890f44271258c8de8f9a12a56ad878a45a6f786e2eb5d28e264c25b6c5f84
   languageName: node
   linkType: hard
 
-"@lodestar/types@npm:^1.18.1":
-  version: 1.18.1
-  resolution: "@lodestar/types@npm:1.18.1"
+"@lodestar/types@npm:^1.23.1":
+  version: 1.23.1
+  resolution: "@lodestar/types@npm:1.23.1"
   dependencies:
-    "@chainsafe/ssz": "npm:^0.15.1"
-    "@lodestar/params": "npm:^1.18.1"
+    "@chainsafe/ssz": "npm:^0.18.0"
+    "@lodestar/params": "npm:^1.23.1"
     ethereum-cryptography: "npm:^2.0.0"
-  checksum: 10c0/7cb118c6ec10969cac72b9f4de1e8809690385fd18e4d249892882139471d6c28a459e178ae326de578f84f9e684fcc915dc8abefd7cc5203a1080ba23aeca99
+  checksum: 10c0/626da54aca6ff8ce1a3d5b583116edccbb6a85d1d3ec01e43d82b0030cbd50b4a94b4e3781796fe3491c93fa25f55273bde67b26a34baaee56f7c0401df89b9c
   languageName: node
   linkType: hard
 
@@ -755,7 +795,7 @@ __metadata:
   version: 0.0.0-use.local
   resolution: "community-staking-module@workspace:."
   dependencies:
-    "@lodestar/types": "npm:^1.18.1"
+    "@lodestar/types": "npm:^1.23.1"
     "@openzeppelin/contracts": "npm:5.0.2"
     "@openzeppelin/contracts-upgradeable": "npm:5.0.2"
     "@openzeppelin/merkle-tree": "npm:^1.0.6"