Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: drop slashing reporting #370

Merged
merged 9 commits into from
Jan 7, 2025
Merged
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
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -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",
106 changes: 106 additions & 0 deletions script/DeployCSVerifierElectra.s.sol
Original file line number Diff line number Diff line change
@@ -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.
});
}
}
5 changes: 0 additions & 5 deletions script/fork-helpers/NodeOperators.s.sol
Original file line number Diff line number Diff line change
@@ -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,
4 changes: 2 additions & 2 deletions script/gindex.mjs
Original file line number Diff line number Diff line change
@@ -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];

{
40 changes: 5 additions & 35 deletions src/CSModule.sol
Original file line number Diff line number Diff line change
@@ -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);
madlabman marked this conversation as resolved.
Show resolved Hide resolved

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
47 changes: 0 additions & 47 deletions src/CSVerifier.sol
Original file line number Diff line number Diff line change
@@ -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,
19 changes: 3 additions & 16 deletions src/interfaces/ICSModule.sol
Original file line number Diff line number Diff line change
@@ -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
12 changes: 0 additions & 12 deletions src/interfaces/ICSVerifier.sol
Original file line number Diff line number Diff line change
@@ -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.
173 changes: 18 additions & 155 deletions test/CSModule.t.sol
Original file line number Diff line number Diff line change
@@ -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
110 changes: 0 additions & 110 deletions test/CSVerifier.t.sol
Original file line number Diff line number Diff line change
@@ -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();
71 changes: 0 additions & 71 deletions test/fixtures/CSVerifier/slashing.json

This file was deleted.

92 changes: 66 additions & 26 deletions yarn.lock
Original file line number Diff line number Diff line change
@@ -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"