-
Notifications
You must be signed in to change notification settings - Fork 409
EN 2FA validator for Era #2037
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
Open
vladbochok
wants to merge
12
commits into
draft-v31
Choose a base branch
from
vb-validator-2fa-era
base: draft-v31
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
EN 2FA validator for Era #2037
Changes from 10 commits
Commits
Show all changes
12 commits
Select commit
Hold shift + click to select a range
1f99d0a
Add Era 2FA validator
vladbochok 4b92171
Remove artifact
vladbochok 431df16
Remove reinitializeV2, use standard initializer for fresh proxy deplo…
vladbochok 0b04097
block initialize from inherited contract
vladbochok ba61e82
Apply suggestions from code review
vladbochok dc4a7fc
CI
vladbochok bb514aa
Lints
vladbochok afe6536
Update AllContractsHashes.json
vladbochok 5f902cf
Merge branch 'draft-v31' into vb-validator-2fa-era
vladbochok 0cdbcab
chore: Updated hashes from CI
ab76538
Add comment
vladbochok b0cd0c9
chore: Updated hashes from CI
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
221 changes: 221 additions & 0 deletions
221
l1-contracts/contracts/state-transition/validators/EraMultisigValidator.sol
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,221 @@ | ||
| // SPDX-License-Identifier: MIT | ||
| pragma solidity 0.8.28; | ||
|
|
||
| import {EIP712Upgradeable} from "@openzeppelin/contracts-upgradeable-v4/utils/cryptography/EIP712Upgradeable.sol"; | ||
| import {AddressHasNoCode} from "../../common/L1ContractErrors.sol"; | ||
| import {ValidatorTimelock} from "./ValidatorTimelock.sol"; | ||
| import {IValidatorTimelock} from "./interfaces/IValidatorTimelock.sol"; | ||
| import {IEraMultisigValidator} from "./interfaces/IEraMultisigValidator.sol"; | ||
|
|
||
| /// @author Matter Labs | ||
| /// @custom:security-contact security@matterlabs.dev | ||
| /// @notice A multisig wrapper around `ValidatorTimelock` that requires a threshold of approvals | ||
| /// before batch execution can proceed. Designed for Era chains (not ZKsync OS chains) that want | ||
| /// additional security through 2FA: independent nodes verify the execution and sign off on the | ||
| /// state transition before it can be finalized on L1. | ||
| /// @dev This contract sits between the executor EOA and the `ValidatorTimelock`. Commit and prove | ||
| /// calls are forwarded directly, while execute calls require that enough multisig members have | ||
| /// pre-approved the exact execution parameters via `approveHash`. | ||
| /// @dev Expected to be deployed as a TransparentUpgradeableProxy. | ||
| contract EraMultisigValidator is IEraMultisigValidator, ValidatorTimelock, EIP712Upgradeable { | ||
| /// @dev EIP-712 typehash for the ExecuteBatches struct. | ||
| bytes32 internal constant EXECUTE_BATCHES_TYPEHASH = | ||
| keccak256( | ||
| "ExecuteBatches(address chainAddress,uint256 processBatchFrom,uint256 processBatchTo,bytes batchData)" | ||
| ); | ||
|
|
||
| /// @inheritdoc IEraMultisigValidator | ||
| address public override validatorTimelock; | ||
|
|
||
| /// @inheritdoc IEraMultisigValidator | ||
| mapping(address => bool) public override executionMultisigMember; | ||
|
|
||
| /// @inheritdoc IEraMultisigValidator | ||
| mapping(address => mapping(bytes32 => bool)) public override individualApprovals; | ||
|
|
||
| /// @dev Addresses that have approved a given hash. Iterated at execution time | ||
| /// to count only current members. | ||
| mapping(bytes32 => address[]) internal hashApprovers; | ||
|
|
||
| /// @inheritdoc IEraMultisigValidator | ||
| uint256 public override threshold; | ||
|
|
||
| /// @dev Reserved storage space to allow for layout changes in future upgrades. | ||
| uint256[44] private __gap; | ||
|
|
||
| constructor(address _bridgeHub) ValidatorTimelock(_bridgeHub) { | ||
| _disableInitializers(); | ||
| } | ||
|
|
||
| /// @dev Disable the inherited 2-param `initialize` from `ValidatorTimelock` / `IValidatorTimelock`. | ||
| function initialize(address, uint32) external pure override(ValidatorTimelock, IValidatorTimelock) { | ||
| revert InitializeNotAvailable(); | ||
| } | ||
|
|
||
| /// @inheritdoc IEraMultisigValidator | ||
| function initialize( | ||
| address _initialOwner, | ||
| uint32 _initialExecutionDelay, | ||
| address _validatorTimelock | ||
| ) external initializer { | ||
| _validatorTimelockInit(_initialOwner, _initialExecutionDelay); | ||
| _initializeEraMultisig(_validatorTimelock); | ||
| } | ||
|
|
||
| /// @dev Shared initialization logic for EIP-712 and the validator timelock address. | ||
| function _initializeEraMultisig(address _validatorTimelock) internal { | ||
| __EIP712_init("EraMultisigValidator", "1"); | ||
| if (_validatorTimelock.code.length == 0) { | ||
| revert AddressHasNoCode(_validatorTimelock); | ||
| } | ||
| validatorTimelock = _validatorTimelock; | ||
| } | ||
vladbochok marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| /// @inheritdoc IEraMultisigValidator | ||
| function approveHash(bytes32 _hash) external { | ||
| if (!executionMultisigMember[msg.sender]) { | ||
| revert NotSigner(); | ||
| } | ||
| if (individualApprovals[msg.sender][_hash]) { | ||
| revert AlreadySigned(); | ||
| } | ||
| individualApprovals[msg.sender][_hash] = true; | ||
| hashApprovers[_hash].push(msg.sender); | ||
| emit HashApproved(msg.sender, _hash); | ||
| } | ||
|
|
||
| /// @inheritdoc IEraMultisigValidator | ||
| function getApprovals(bytes32 _hash) public view returns (uint256) { | ||
| uint256 count = 0; | ||
| address[] storage approvers = hashApprovers[_hash]; | ||
| uint256 length = approvers.length; | ||
| for (uint256 i = 0; i < length; ++i) { | ||
| if (executionMultisigMember[approvers[i]]) { | ||
| ++count; | ||
| } | ||
| } | ||
| return count; | ||
| } | ||
vladbochok marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| /// @inheritdoc IEraMultisigValidator | ||
| function changeThreshold(uint256 _newThreshold) external onlyOwner { | ||
| threshold = _newThreshold; | ||
| emit ThresholdChanged(_newThreshold); | ||
| } | ||
|
|
||
| /// @inheritdoc IEraMultisigValidator | ||
| function changeExecutionMultisigMember( | ||
| address[] calldata _addressesToAdd, | ||
| address[] calldata _addressesToRemove | ||
| ) external onlyOwner { | ||
| uint256 addLength = _addressesToAdd.length; | ||
| for (uint256 i = 0; i < addLength; ++i) { | ||
| executionMultisigMember[_addressesToAdd[i]] = true; | ||
| emit MultisigMemberChanged(_addressesToAdd[i], true); | ||
| } | ||
| uint256 removeLength = _addressesToRemove.length; | ||
| for (uint256 i = 0; i < removeLength; ++i) { | ||
| executionMultisigMember[_addressesToRemove[i]] = false; | ||
| emit MultisigMemberChanged(_addressesToRemove[i], false); | ||
| } | ||
| } | ||
|
|
||
| /// @inheritdoc IValidatorTimelock | ||
| function precommitSharedBridge( | ||
| address _chainAddress, | ||
| uint256, | ||
| bytes calldata | ||
| ) public override(ValidatorTimelock, IValidatorTimelock) onlyRole(_chainAddress, PRECOMMITTER_ROLE) { | ||
| _propagateToValidatorTimelock(); | ||
| } | ||
|
|
||
| /// @inheritdoc IValidatorTimelock | ||
| function revertBatchesSharedBridge( | ||
| address _chainAddress, | ||
| uint256 | ||
| ) public override(ValidatorTimelock, IValidatorTimelock) onlyRole(_chainAddress, REVERTER_ROLE) { | ||
| _propagateToValidatorTimelock(); | ||
| } | ||
|
|
||
| /// @inheritdoc IValidatorTimelock | ||
| function commitBatchesSharedBridge( | ||
| address _chainAddress, | ||
| uint256, | ||
| uint256, | ||
| bytes calldata | ||
| ) public override(ValidatorTimelock, IValidatorTimelock) onlyRole(_chainAddress, COMMITTER_ROLE) { | ||
| _propagateToValidatorTimelock(); | ||
vladbochok marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| /// @inheritdoc IValidatorTimelock | ||
| function proveBatchesSharedBridge( | ||
| address _chainAddress, | ||
| uint256, | ||
| uint256, | ||
| bytes calldata | ||
| ) public override(ValidatorTimelock, IValidatorTimelock) onlyRole(_chainAddress, PROVER_ROLE) { | ||
| _propagateToValidatorTimelock(); | ||
| } | ||
|
|
||
| /// @inheritdoc IValidatorTimelock | ||
| /// @dev In addition to the base role check, this override requires that the execution parameters | ||
| /// have been approved by at least `threshold` multisig members before forwarding. | ||
| function executeBatchesSharedBridge( | ||
| address _chainAddress, | ||
| uint256 _processBatchFrom, | ||
| uint256 _processBatchTo, | ||
| bytes calldata _batchData | ||
| ) public override(ValidatorTimelock, IValidatorTimelock) onlyRole(_chainAddress, EXECUTOR_ROLE) { | ||
| bytes32 approvedHash = calculateHash(_chainAddress, _processBatchFrom, _processBatchTo, _batchData); | ||
| if (getApprovals(approvedHash) < threshold) { | ||
| revert NotEnoughSignatures(); | ||
| } | ||
| _propagateToValidatorTimelock(); | ||
| } | ||
|
|
||
| /// @inheritdoc IEraMultisigValidator | ||
| function calculateHash( | ||
| address _chainAddress, | ||
| uint256 _processBatchFrom, | ||
| uint256 _processBatchTo, | ||
| bytes calldata _batchData | ||
| ) public view returns (bytes32) { | ||
| return | ||
| _hashTypedDataV4( | ||
| keccak256( | ||
| abi.encode( | ||
| EXECUTE_BATCHES_TYPEHASH, | ||
| _chainAddress, | ||
| _processBatchFrom, | ||
| _processBatchTo, | ||
| keccak256(_batchData) | ||
| ) | ||
| ) | ||
| ); | ||
| } | ||
|
|
||
| /// @dev Forwards the current calldata to the downstream `ValidatorTimelock`. | ||
| function _propagateToValidatorTimelock() internal { | ||
| address validatorTimelock_ = validatorTimelock; | ||
| assembly { | ||
| // Copy function signature and arguments from calldata at zero position into memory at pointer position | ||
| calldatacopy(0, 0, calldatasize()) | ||
| // Call the ValidatorTimelock contract, returns 0 on error | ||
| let result := call(gas(), validatorTimelock_, 0, 0, calldatasize(), 0, 0) | ||
| // Get the size of the last return data | ||
| let size := returndatasize() | ||
| // Copy the size length of bytes from return data at zero position to pointer position | ||
| returndatacopy(0, 0, size) | ||
| // Depending on the result value | ||
| switch result | ||
| case 0 { | ||
| // End execution and revert state changes | ||
| revert(0, size) | ||
| } | ||
| default { | ||
| // Return data with length of size at pointers position | ||
| return(0, size) | ||
| } | ||
| } | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
comment