Skip to content

Commit

Permalink
feat: Make CSVerifier pausable (#379)
Browse files Browse the repository at this point in the history
Co-authored-by: Vladimir Gorkavenko <[email protected]>
  • Loading branch information
dgusakov and vgorkavenko authored Jan 13, 2025
1 parent 72eace1 commit 4a8d0c4
Show file tree
Hide file tree
Showing 14 changed files with 353 additions and 31 deletions.
26 changes: 19 additions & 7 deletions script/DeployBase.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,8 @@ abstract contract DeployBase is Script {
),
pivotSlot: Slot.wrap(
uint64(config.verifierSupportedEpoch * config.slotsPerEpoch)
)
),
admin: deployer
});

accounting.initialize({
Expand Down Expand Up @@ -259,11 +260,18 @@ abstract contract DeployBase is Script {
_avgPerfLeewayBP: config.avgPerfLeewayBP
});

address gateSeal = _deployGateSeal();
address[] memory sealables = new address[](4);
sealables[0] = address(csm);
sealables[1] = address(accounting);
sealables[2] = address(oracle);
sealables[3] = address(verifier);
address gateSeal = _deployGateSeal(sealables);

csm.grantRole(csm.PAUSE_ROLE(), gateSeal);
oracle.grantRole(oracle.PAUSE_ROLE(), gateSeal);
accounting.grantRole(accounting.PAUSE_ROLE(), gateSeal);
verifier.grantRole(verifier.PAUSE_ROLE(), gateSeal);

accounting.grantRole(
accounting.SET_BOND_CURVE_ROLE(),
config.setResetBondCurveAddress
Expand Down Expand Up @@ -291,6 +299,12 @@ abstract contract DeployBase is Script {
csm.grantRole(csm.DEFAULT_ADMIN_ROLE(), config.aragonAgent);
csm.revokeRole(csm.DEFAULT_ADMIN_ROLE(), deployer);

verifier.grantRole(
verifier.DEFAULT_ADMIN_ROLE(),
config.aragonAgent
);
verifier.revokeRole(verifier.DEFAULT_ADMIN_ROLE(), deployer);

accounting.grantRole(
accounting.DEFAULT_ADMIN_ROLE(),
config.aragonAgent
Expand Down Expand Up @@ -350,14 +364,12 @@ abstract contract DeployBase is Script {
return address(proxy);
}

function _deployGateSeal() internal returns (address) {
function _deployGateSeal(
address[] memory sealables
) internal returns (address) {
IGateSealFactory gateSealFactory = IGateSealFactory(
config.gateSealFactory
);
address[] memory sealables = new address[](3);
sealables[0] = address(csm);
sealables[1] = address(accounting);
sealables[2] = address(oracle);

address committee = config.sealingCommittee == address(0)
? deployer
Expand Down
10 changes: 7 additions & 3 deletions script/DeployCSVerifierElectra.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ struct Config {
Slot firstSupportedSlot;
Slot pivotSlot;
uint64 slotsPerEpoch;
address admin;
}

// Check the constants below via `yarn run gindex`.
Expand Down Expand Up @@ -63,7 +64,8 @@ abstract contract DeployCSVerifier is Script {
gIHistoricalSummariesPrev: config.gIHistoricalSummariesPrev,
gIHistoricalSummariesCurr: config.gIHistoricalSummariesCurr,
firstSupportedSlot: config.firstSupportedSlot,
pivotSlot: config.pivotSlot
pivotSlot: config.pivotSlot,
admin: config.admin
});
console.log("CSVerifier deployed at:", address(verifier));
}
Expand All @@ -82,7 +84,8 @@ contract DeployCSVerifierHolesky is DeployCSVerifier {
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.
pivotSlot: Slot.wrap(0), // TODO: Update with Electra slot.
admin: 0xE92329EC7ddB11D25e25b3c21eeBf11f15eB325d // Aragon Agent
});
}
}
Expand All @@ -100,7 +103,8 @@ contract DeployCSVerifierMainnet is DeployCSVerifier {
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.
pivotSlot: Slot.wrap(0), // TODO: Update with Electra slot.
admin: 0x3e40D73EB977Dc6a537aF587D48316feE66E9C8c // Aragon Agent
});
}
}
18 changes: 17 additions & 1 deletion script/DeployImplementationsBase.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,24 @@ abstract contract DeployImplementationsBase is DeployBase {
),
pivotSlot: Slot.wrap(
uint64(config.verifierSupportedEpoch * config.slotsPerEpoch)
)
),
admin: deployer
});

address[] memory sealables = new address[](4);
sealables[0] = address(csm);
sealables[1] = address(accounting);
sealables[2] = address(oracle);
sealables[3] = address(verifier);
gateSeal = _deployGateSeal(sealables);

verifier.grantRole(verifier.PAUSE_ROLE(), address(gateSeal));
verifier.grantRole(
verifier.DEFAULT_ADMIN_ROLE(),
config.aragonAgent
);
verifier.revokeRole(verifier.DEFAULT_ADMIN_ROLE(), deployer);

JsonObj memory deployJson = Json.newObj();
deployJson.set("CSModuleImpl", address(csmImpl));
deployJson.set("CSAccountingImpl", address(accountingImpl));
Expand All @@ -91,6 +106,7 @@ abstract contract DeployImplementationsBase is DeployBase {
deployJson.set("CSVerifier", address(verifier));
deployJson.set("CSEarlyAdoption", address(earlyAdoption));
deployJson.set("HashConsensus", address(hashConsensus));
deployJson.set("GateSeal", address(gateSeal));
deployJson.set("git-ref", gitRef);
vm.writeJson(
deployJson.str,
Expand Down
21 changes: 21 additions & 0 deletions script/fork-helpers/SimulateVote.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import { DeploymentFixtures } from "test/helpers/Fixtures.sol";
import { IStakingRouter } from "../../src/interfaces/IStakingRouter.sol";
import { OssifiableProxy } from "../../src/lib/proxy/OssifiableProxy.sol";
import { CSModule } from "../../src/CSModule.sol";
import { CSAccounting } from "../../src/CSAccounting.sol";
import { CSFeeOracle } from "../../src/CSFeeOracle.sol";
import { IBurner } from "../../src/interfaces/IBurner.sol";
import { ForkHelpersCommon } from "./Common.sol";

Expand Down Expand Up @@ -100,10 +102,29 @@ contract SimulateVote is Script, DeploymentFixtures, ForkHelpersCommon {

address admin = _prepareAdmin(deploymentConfig.csm);
csm = CSModule(deploymentConfig.csm);
accounting = CSAccounting(deploymentConfig.accounting);
oracle = CSFeeOracle(deploymentConfig.oracle);

vm.startBroadcast(admin);
csm.revokeRole(csm.VERIFIER_ROLE(), address(deploymentConfig.verifier));
csm.grantRole(csm.VERIFIER_ROLE(), address(upgradeConfig.verifier));

csm.revokeRole(csm.PAUSE_ROLE(), address(deploymentConfig.gateSeal));
accounting.revokeRole(
accounting.PAUSE_ROLE(),
address(deploymentConfig.gateSeal)
);
oracle.revokeRole(
oracle.PAUSE_ROLE(),
address(deploymentConfig.gateSeal)
);

csm.grantRole(csm.PAUSE_ROLE(), address(upgradeConfig.gateSeal));
accounting.grantRole(
accounting.PAUSE_ROLE(),
address(upgradeConfig.gateSeal)
);
oracle.grantRole(oracle.PAUSE_ROLE(), address(upgradeConfig.gateSeal));
vm.stopBroadcast();
}
}
27 changes: 23 additions & 4 deletions src/CSVerifier.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ pragma solidity 0.8.24;

import { ICSVerifier } from "./interfaces/ICSVerifier.sol";
import { ICSModule } from "./interfaces/ICSModule.sol";
import { AccessControlEnumerable } from "@openzeppelin/contracts/access/extensions/AccessControlEnumerable.sol";
import { PausableUntil } from "./lib/utils/PausableUntil.sol";

import { BeaconBlockHeader, Slot, Validator, Withdrawal } from "./lib/Types.sol";
import { GIndex } from "./lib/GIndex.sol";
Expand All @@ -22,13 +24,16 @@ function gweiToWei(uint64 amount) pure returns (uint256) {
return uint256(amount) * 1 gwei;
}

contract CSVerifier is ICSVerifier {
contract CSVerifier is ICSVerifier, AccessControlEnumerable, PausableUntil {
using { amountWei } for Withdrawal;

using SSZ for BeaconBlockHeader;
using SSZ for Withdrawal;
using SSZ for Validator;

bytes32 public constant PAUSE_ROLE = keccak256("PAUSE_ROLE");
bytes32 public constant RESUME_ROLE = keccak256("RESUME_ROLE");

// See `BEACON_ROOTS_ADDRESS` constant in the EIP-4788.
address public constant BEACON_ROOTS =
0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02;
Expand Down Expand Up @@ -77,10 +82,12 @@ contract CSVerifier is ICSVerifier {
GIndex gIHistoricalSummariesPrev,
GIndex gIHistoricalSummariesCurr,
Slot firstSupportedSlot,
Slot pivotSlot
Slot pivotSlot,
address admin
) {
if (withdrawalAddress == address(0)) revert ZeroWithdrawalAddress();
if (module == address(0)) revert ZeroModuleAddress();
if (admin == address(0)) revert ZeroAdminAddress();

if (slotsPerEpoch == 0) revert InvalidChainConfig();
if (firstSupportedSlot > pivotSlot) revert InvalidPivotSlot();
Expand All @@ -101,6 +108,18 @@ contract CSVerifier is ICSVerifier {

FIRST_SUPPORTED_SLOT = firstSupportedSlot;
PIVOT_SLOT = pivotSlot;

_grantRole(DEFAULT_ADMIN_ROLE, admin);
}

/// @inheritdoc ICSVerifier
function resume() external onlyRole(RESUME_ROLE) {
_resume();
}

/// @inheritdoc ICSVerifier
function pauseFor(uint256 duration) external onlyRole(PAUSE_ROLE) {
_pauseFor(duration);
}

/// @inheritdoc ICSVerifier
Expand All @@ -109,7 +128,7 @@ contract CSVerifier is ICSVerifier {
WithdrawalWitness calldata witness,
uint256 nodeOperatorId,
uint256 keyIndex
) external {
) external whenResumed {
if (beaconBlock.header.slot < FIRST_SUPPORTED_SLOT) {
revert UnsupportedSlot(beaconBlock.header.slot);
}
Expand Down Expand Up @@ -151,7 +170,7 @@ contract CSVerifier is ICSVerifier {
WithdrawalWitness calldata witness,
uint256 nodeOperatorId,
uint256 keyIndex
) external {
) external whenResumed {
if (beaconBlock.header.slot < FIRST_SUPPORTED_SLOT) {
revert UnsupportedSlot(beaconBlock.header.slot);
}
Expand Down
12 changes: 12 additions & 0 deletions src/interfaces/ICSVerifier.sol
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,13 @@ interface ICSVerifier {
error UnsupportedSlot(Slot slot);
error ZeroModuleAddress();
error ZeroWithdrawalAddress();
error ZeroAdminAddress();
error InvalidPivotSlot();

function PAUSE_ROLE() external view returns (bytes32);

function RESUME_ROLE() external view returns (bytes32);

function BEACON_ROOTS() external view returns (address);

function SLOTS_PER_EPOCH() external view returns (uint64);
Expand All @@ -87,6 +92,13 @@ interface ICSVerifier {

function MODULE() external view returns (ICSModule);

/// @notice Pause write methods calls for `duration` seconds
/// @param duration Duration of the pause in seconds
function pauseFor(uint256 duration) external;

/// @notice Resume write methods calls
function resume() 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.
Expand Down
Loading

0 comments on commit 4a8d0c4

Please sign in to comment.