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 +// 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"