-
Notifications
You must be signed in to change notification settings - Fork 138
feat(l2): add timelock to provide exit window #5666
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
base: main
Are you sure you want to change the base?
Conversation
Lines of code reportTotal lines added: Detailed view |
| tx_hashes.push(initialize_tx_hash); | ||
| } | ||
|
|
||
| let initialize_bridge_address_tx_hash = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We now initialize the bridge in the initialize() of the OnChainProposer, this is not necessary anymore.
|
|
||
| tx_hashes.push(initialize_bridge_address_tx_hash); | ||
|
|
||
| if opts.on_chain_proposer_owner != initializer.address() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't need to transfer ownership anymore, we now initialize the ownership correctly in initialize() instead of transferring it later.
| // Logic for updating Timelock contract. Should be triggered by the timelock itself so that it respects min time. | ||
| function _authorizeUpgrade( | ||
| address /*newImplementation*/ | ||
| ) internal view override { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use an onlySelf modifier here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done ad29f8a
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR introduces a Timelock contract to manage the OnChainProposer contract, providing an exit window for users before critical operations take effect. The Timelock implements role-based access control with SEQUENCER and SECURITY_COUNCIL roles, where sequencers can commit and verify batches, and the security council can perform emergency operations without delay.
Key Changes
- New Timelock contract that acts as an intermediary between sequencers and the OnChainProposer, with support for role-based access control and emergency operations
- OnChainProposer initialization now includes bridge address setup, eliminating the need for a separate
initializeBridgeAddress()call and the subsequent ownership transfer process - Rust sequencer components updated to route transactions through the Timelock address (for non-based deployments) instead of directly to the OnChainProposer
Reviewed changes
Copilot reviewed 13 out of 13 changed files in this pull request and generated 10 comments.
Show a summary per file
| File | Description |
|---|---|
| crates/l2/contracts/src/l1/Timelock.sol | New upgradeable Timelock contract implementing TimelockControllerUpgradeable with role-based access for sequencer operations and security council emergency functions |
| crates/l2/contracts/src/l1/OnChainProposer.sol | Removed sequencer authorization mapping and modifier; changed access control to onlyOwner (expected to be Timelock); integrated bridge initialization into main initialize() |
| crates/l2/contracts/src/l1/based/OnChainProposer.sol | Similar access control changes as non-based version; bridge initialization moved into main initialize() with validation |
| crates/l2/contracts/src/l1/interfaces/IOnChainProposer.sol | Removed initializeBridgeAddress() function from interface as it's no longer needed |
| crates/l2/contracts/src/l1/based/interfaces/IOnChainProposer.sol | Removed initializeBridgeAddress() function from based interface |
| crates/l2/sequencer/l1_proof_verifier.rs | Added timelock_address field; transactions now routed to timelock_address if present, falling back to on_chain_proposer_address |
| crates/l2/sequencer/l1_proof_sender.rs | Added timelock_address field with same routing logic as proof verifier |
| crates/l2/sequencer/l1_committer.rs | Added timelock_address field; for non-based deployments requires timelock_address; based deployments use on_chain_proposer_address directly |
| crates/l2/sequencer/configs.rs | Added timelock_address Option field to CommitterConfig |
| cmd/ethrex/l2/options.rs | Added timelock-address CLI option and environment variable support for committer configuration |
| cmd/ethrex/l2/deployer.rs | Added Timelock deployment and initialization; updated OnChainProposer initialization signatures to include bridge parameter; removed separate bridge initialization and ownership transfer logic |
| cmd/ethrex/l2/command.rs | Updated dev mode initialization to set timelock_address from deployed contracts |
| cmd/ethrex/build_l2.rs | Added Timelock contract to the list of contracts to compile |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| contract Timelock is | ||
| TimelockControllerUpgradeable, | ||
| UUPSUpgradeable, | ||
| IOnChainProposer |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can be reverted if necessary, I implemented IOnChainProposer in order to inherit its docs and to interact with the contract in a more direct manner. The other path is to remove the functions and call them only through schedule, execute and emergencyExecute and the Timelock contract would have way less functions
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think it's a good idea to inherit IOnChainProposer. It seems confusing
|
Current Status: Based workflow wasn't finishing to run, it got stuck in a loop. I'm trying to fix it by just having timelock logic if it's non-based and in based leave everything as it was. Edit: this was fixed, now TDX test failed. Maybe it was because of flakiness, I'll check that out. |
| Some(timelock_deployment.clone()), | ||
| Some(timelock_deployment.proxy_address), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why does this return both deployment and address (being the last a field of the former)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I use the address multiple times then and it's best to have it in a timelock_address variable that's an option. Otherwise I have to do .map(|t| t.proxy_address) more than once unnecessarily. This way I just have the variable for direct use and it's simpler
cmd/ethrex/l2/deployer.rs
Outdated
| if !opts.deploy_based_contracts { | ||
| let timelock_address = | ||
| contract_addresses | ||
| .timelock_address | ||
| .ok_or(DeployerError::InternalError( | ||
| "Timelock address missing".to_string(), | ||
| ))?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd go for
if let Some(timelock_address) = contract_addresses.timelock_addressInstead of checking based
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done 2681bb2
| &deployer, | ||
| eth_client, | ||
| Overrides { | ||
| nonce: Some(deployer_nonce), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The nonce should be kept across initializers if no_wait is used
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I understand you correctly you mean that we should manually increase the nonce. Before I believe it was working fine (for based) because we were using get_block Pending though. But it's true that this may be flaky if you send them too fast maybe. I made some changes in this commit, let me know if I got it wrong
a56ece8
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, I reverted it for now because clippy was complaining and I didn't think it was worth to handle that but we can check what the best thing to do is later!
| /// @dev The sequencer addresses that are authorized to commit and verify batches. | ||
| mapping(address _authorizedAddress => bool) | ||
| public authorizedSequencerAddresses; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can't delete this, we need to mark it as deprecated in a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
5d8747b
You are right, didn't contemplate the case of already deployed OnChainProposers
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should add our ITimelock interface
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added
| /// @param target The address that was called. | ||
| /// @param value The ETH value that was sent. | ||
| /// @param data The calldata that was forwarded to `target`. | ||
| event EmergencyExecution(address indexed target, uint256 value, bytes data); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Move this event to the interface
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
|
|
||
| /// @dev Restricts calls to the timelock itself. | ||
| modifier onlySelf() { | ||
| require( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use custom errors instead of strings
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| address[] memory, | ||
| address | ||
| ) public pure override { | ||
| revert("Timelock: use the custom initialize function"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ditto
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| /// @inheritdoc IOnChainProposer | ||
| function lastCommittedBatch() external view returns (uint256) { | ||
| return onChainProposer.lastCommittedBatch(); | ||
| } | ||
|
|
||
| /// @inheritdoc IOnChainProposer | ||
| function lastVerifiedBatch() external view returns (uint256) { | ||
| return onChainProposer.lastVerifiedBatch(); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wouldn't add view functions in the timelock
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed them now that the Timelock is no longer IOnChainProposer
| } | ||
|
|
||
| /// @inheritdoc IOnChainProposer | ||
| /// @custom:access Restricted to accounts with the `SECURITY_COUNCIL` role. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need all this functions having emergencyExecute()?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Edit: I removed IOnChainProposer so I removed all these functions as well because of that
…egation in the OnChainProposer
This reverts commit a56ece8.
Motivation
Description