From d7da296bfe6ad7bfd2da1cbd8ab124f8a640746a Mon Sep 17 00:00:00 2001 From: Sam MacPherson Date: Tue, 7 May 2024 14:27:36 +0900 Subject: [PATCH] [SC-377] Decouple executor (#13) * forge install: openzeppelin-contracts v5.0.2 * refactor executor to split out business logic from bridging * re-add support for gnosis/arbitrum; add base integration test * eof newlines --- .gitmodules | 3 + lib/openzeppelin-contracts | 1 + src/executors/ArbitrumBridgeExecutor.sol | 61 ------- src/executors/AuthBridgeExecutor.sol | 56 +++++++ src/executors/GnosisBridgeExecutor.sol | 150 ------------------ src/executors/L2BridgeExecutor.sol | 74 --------- src/executors/OptimismBridgeExecutor.sol | 63 -------- src/executors/ZkEVMBridgeExecutor.sol | 94 ----------- src/interfaces/IAuthBridgeExecutor.sol | 31 ++++ src/interfaces/IL2BridgeExecutor.sol | 52 ------ .../BridgeExecutorReceiverArbitrum.sol | 35 ++++ .../BridgeExecutorReceiverGnosis.sol | 37 +++++ .../BridgeExecutorReceiverOptimism.sol | 35 ++++ test/ArbitrumCrosschainTest.t.sol | 37 +++-- test/BaseCrosschainTest.t.sol | 60 +++++++ test/CrosschainTestBase.sol | 115 +++++++------- test/GnosisCrosschainTest.t.sol | 89 ++--------- test/OptimismCrosschainTest.t.sol | 24 +-- test/ZkEVMCrosschainTest.t.sol | 59 ------- test/mocks/GnosisReconfigurationPayload.sol | 45 ------ 20 files changed, 369 insertions(+), 752 deletions(-) create mode 160000 lib/openzeppelin-contracts delete mode 100644 src/executors/ArbitrumBridgeExecutor.sol create mode 100644 src/executors/AuthBridgeExecutor.sol delete mode 100644 src/executors/GnosisBridgeExecutor.sol delete mode 100644 src/executors/L2BridgeExecutor.sol delete mode 100644 src/executors/OptimismBridgeExecutor.sol delete mode 100644 src/executors/ZkEVMBridgeExecutor.sol create mode 100644 src/interfaces/IAuthBridgeExecutor.sol delete mode 100644 src/interfaces/IL2BridgeExecutor.sol create mode 100644 src/receivers/BridgeExecutorReceiverArbitrum.sol create mode 100644 src/receivers/BridgeExecutorReceiverGnosis.sol create mode 100644 src/receivers/BridgeExecutorReceiverOptimism.sol create mode 100644 test/BaseCrosschainTest.t.sol delete mode 100644 test/ZkEVMCrosschainTest.t.sol delete mode 100644 test/mocks/GnosisReconfigurationPayload.sol diff --git a/.gitmodules b/.gitmodules index 24bc7df..971eeea 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,3 +4,6 @@ [submodule "lib/xchain-helpers"] path = lib/xchain-helpers url = https://github.com/marsfoundation/xchain-helpers +[submodule "lib/openzeppelin-contracts"] + path = lib/openzeppelin-contracts + url = https://github.com/OpenZeppelin/openzeppelin-contracts diff --git a/lib/openzeppelin-contracts b/lib/openzeppelin-contracts new file mode 160000 index 0000000..dbb6104 --- /dev/null +++ b/lib/openzeppelin-contracts @@ -0,0 +1 @@ +Subproject commit dbb6104ce834628e473d2173bbc9d47f81a9eec3 diff --git a/src/executors/ArbitrumBridgeExecutor.sol b/src/executors/ArbitrumBridgeExecutor.sol deleted file mode 100644 index 85f44d5..0000000 --- a/src/executors/ArbitrumBridgeExecutor.sol +++ /dev/null @@ -1,61 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0 -pragma solidity ^0.8.10; - -import { L2BridgeExecutor } from './L2BridgeExecutor.sol'; - -/** - * @title ArbitrumBridgeExecutor - * @author Aave - * @notice Implementation of the Arbitrum Bridge Executor, able to receive cross-chain transactions from Ethereum - * @dev Queuing an ActionsSet into this Executor can only be done by the L2 Address Alias of the L1 EthereumGovernanceExecutor - */ -contract ArbitrumBridgeExecutor is L2BridgeExecutor { - uint160 internal constant OFFSET = uint160(0x1111000000000000000000000000000000001111); - - /// @notice Utility function that converts the msg.sender viewed in the L2 to the - /// address in the L1 that submitted a tx to the inbox - /// @param l2Address L2 address as viewed in msg.sender - /// @return l1Address the address in the L1 that triggered the tx to L2 - function undoL1ToL2Alias(address l2Address) internal pure returns (address l1Address) { - unchecked { - l1Address = address(uint160(l2Address) - OFFSET); - } - } - - /// @inheritdoc L2BridgeExecutor - modifier onlyEthereumGovernanceExecutor() override { - if (undoL1ToL2Alias(msg.sender) != _ethereumGovernanceExecutor) - revert UnauthorizedEthereumExecutor(); - _; - } - - /** - * @dev Constructor - * - * @param ethereumGovernanceExecutor The address of the EthereumGovernanceExecutor - * @param delay The delay before which an actions set can be executed - * @param gracePeriod The time period after a delay during which an actions set can be executed - * @param minimumDelay The minimum bound a delay can be set to - * @param maximumDelay The maximum bound a delay can be set to - * @param guardian The address of the guardian, which can cancel queued proposals (can be zero) - */ - constructor( - address ethereumGovernanceExecutor, - uint256 delay, - uint256 gracePeriod, - uint256 minimumDelay, - uint256 maximumDelay, - address guardian - ) - L2BridgeExecutor( - ethereumGovernanceExecutor, - delay, - gracePeriod, - minimumDelay, - maximumDelay, - guardian - ) - { - // Intentionally left blank - } -} diff --git a/src/executors/AuthBridgeExecutor.sol b/src/executors/AuthBridgeExecutor.sol new file mode 100644 index 0000000..d8ce754 --- /dev/null +++ b/src/executors/AuthBridgeExecutor.sol @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: AGPL-3.0 +pragma solidity ^0.8.10; + +import { AccessControl } from 'lib/openzeppelin-contracts/contracts/access/AccessControl.sol'; + +import { IAuthBridgeExecutor } from '../interfaces/IAuthBridgeExecutor.sol'; +import { BridgeExecutorBase } from './BridgeExecutorBase.sol'; + +/** + * @title AuthBridgeExecutor + * @notice Queue up proposals from an authorized bridge. + */ +contract AuthBridgeExecutor is IAuthBridgeExecutor, AccessControl, BridgeExecutorBase { + + bytes32 public constant AUTHORIZED_BRIDGE_ROLE = keccak256('AUTHORIZED_BRIDGE_ROLE'); + + /** + * @dev Constructor + * + * @param delay The delay before which an actions set can be executed + * @param gracePeriod The time period after a delay during which an actions set can be executed + * @param minimumDelay The minimum bound a delay can be set to + * @param maximumDelay The maximum bound a delay can be set to + * @param guardian The address of the guardian, which can cancel queued proposals (can be zero) + */ + constructor( + uint256 delay, + uint256 gracePeriod, + uint256 minimumDelay, + uint256 maximumDelay, + address guardian + ) + BridgeExecutorBase( + delay, + gracePeriod, + minimumDelay, + maximumDelay, + guardian + ) + { + _grantRole(DEFAULT_ADMIN_ROLE, msg.sender); + _setRoleAdmin(AUTHORIZED_BRIDGE_ROLE, DEFAULT_ADMIN_ROLE); + } + + /// @inheritdoc IAuthBridgeExecutor + function queue( + address[] memory targets, + uint256[] memory values, + string[] memory signatures, + bytes[] memory calldatas, + bool[] memory withDelegatecalls + ) external onlyRole(AUTHORIZED_BRIDGE_ROLE) { + _queue(targets, values, signatures, calldatas, withDelegatecalls); + } + +} diff --git a/src/executors/GnosisBridgeExecutor.sol b/src/executors/GnosisBridgeExecutor.sol deleted file mode 100644 index 9d8e46d..0000000 --- a/src/executors/GnosisBridgeExecutor.sol +++ /dev/null @@ -1,150 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0 -pragma solidity ^0.8.10; - -import { BridgeExecutorBase } from './BridgeExecutorBase.sol'; - -interface IAMB { - function messageSender() external view returns (address); - - function messageSourceChainId() external view returns (bytes32); -} - -/** - * @title GnosisBridgeExecutor - * @author Gnosis - * @notice Implementation of the AMB Bridge Executor, able to receive cross-chain transactions from Ethereum - * @dev Queuing an ActionsSet into this Executor can only be done by the AMB contract and must be from the designated - * controller from the correct origin chain. - */ -contract GnosisBridgeExecutor is BridgeExecutorBase { - error UnauthorizedAMB(); - error UnauthorizedChainId(); - error UnauthorizedController(); - - /** - * @dev Emitted when Amb address is updated - * @param oldAmbAddress the old address - * @param newAmbAddress the new address - **/ - event AmbAddressUpdated( - address indexed oldAmbAddress, - address indexed newAmbAddress - ); - - /** - * @dev Emitted when controller address is updated - * @param oldControllerAddress the old address - * @param newControllerAddress the new address - **/ - event ControllerUpdated( - address indexed oldControllerAddress, - address indexed newControllerAddress - ); - - /** - * @dev Emitted when chainId is updated - * @param oldChainIdAddress the old Id - * @param newChainIdAddress the new Id - **/ - event ChainIdUpdated( - bytes32 indexed oldChainIdAddress, - bytes32 indexed newChainIdAddress - ); - - // Address of the AMB contract forwarding the cross-chain transaction from Ethereum - IAMB public amb; - // Address of the orginating sender of the message - address public controller; - // Chain ID of the origin - bytes32 public chainId; - - /** - * @dev Check that the amb, chainId, and owner are valid - **/ - modifier onlyValid() { - if (msg.sender != address(amb)) revert UnauthorizedAMB(); - if (amb.messageSourceChainId() != chainId) revert UnauthorizedChainId(); - if (amb.messageSender() != controller) revert UnauthorizedController(); - _; - } - - /** - * @dev Constructor - * @param _amb The AMB contract on the foreign chain - * @param _controller Address of the authorized controller contract on the other side of the bridge - * @param _chainId Address of the authorized chainId from which owner can initiate transactions - * @param delay The delay before which an actions set can be executed - * @param gracePeriod The time period after a delay during which an actions set can be executed - * @param minimumDelay The minimum bound a delay can be set to - * @param maximumDelay The maximum bound a delay can be set to - * @param guardian The address of the guardian, which can cancel queued proposals (can be zero) - */ - constructor( - IAMB _amb, - address _controller, - bytes32 _chainId, - uint256 delay, - uint256 gracePeriod, - uint256 minimumDelay, - uint256 maximumDelay, - address guardian - ) - BridgeExecutorBase( - delay, - gracePeriod, - minimumDelay, - maximumDelay, - guardian - ) - { - amb = _amb; - controller = _controller; - chainId = _chainId; - } - - /** - * @notice Queue an ActionsSet - * @dev If a signature is empty, calldata is used for the execution, calldata is appended to signature otherwise - * @param targets Array of targets to be called by the actions set - * @param values Array of values to pass in each call by the actions set - * @param signatures Array of function signatures to encode in each call by the actions (can be empty) - * @param calldatas Array of calldata to pass in each call by the actions set - * @param withDelegatecalls Array of whether to delegatecall for each call of the actions set - **/ - function queue( - address[] memory targets, - uint256[] memory values, - string[] memory signatures, - bytes[] memory calldatas, - bool[] memory withDelegatecalls - ) external onlyValid { - _queue(targets, values, signatures, calldatas, withDelegatecalls); - } - - /// @dev Set the AMB contract address - /// @param _amb Address of the AMB contract - /// @notice This can only be called by this contract - function setAmb(address _amb) public onlyThis { - require(address(amb) != _amb, 'AMB address already set to this'); - emit AmbAddressUpdated(address(amb), _amb); - amb = IAMB(_amb); - } - - /// @dev Set the approved chainId - /// @param _chainId ID of the approved network - /// @notice This can only be called by this contract - function setChainId(bytes32 _chainId) public onlyThis { - require(chainId != _chainId, 'chainId already set to this'); - emit ChainIdUpdated(chainId, _chainId); - chainId = _chainId; - } - - /// @dev Set the controller address - /// @param _controller Set the address of controller on the other side of the bridge - /// @notice This can only be called by this contract - function setController(address _controller) public onlyThis { - require(controller != _controller, 'controller already set to this'); - emit ControllerUpdated(controller, _controller); - controller = _controller; - } -} diff --git a/src/executors/L2BridgeExecutor.sol b/src/executors/L2BridgeExecutor.sol deleted file mode 100644 index de02f52..0000000 --- a/src/executors/L2BridgeExecutor.sol +++ /dev/null @@ -1,74 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0 -pragma solidity ^0.8.10; - -import { IL2BridgeExecutor } from '../interfaces/IL2BridgeExecutor.sol'; - -import { BridgeExecutorBase } from './BridgeExecutorBase.sol'; - -/** - * @title L2BridgeExecutor - * @author Aave - * @notice Abstract contract that implements bridge executor functionality for L2 - * @dev It does not implement the `onlyEthereumGovernanceExecutor` modifier. This should instead be done in the inheriting - * contract with proper configuration and adjustments depending on the L2 - */ -abstract contract L2BridgeExecutor is BridgeExecutorBase, IL2BridgeExecutor { - // Address of the Ethereum Governance Executor, which should be able to queue actions sets - address internal _ethereumGovernanceExecutor; - - /** - * @dev Only the Ethereum Governance Executor should be able to call functions marked by this modifier. - **/ - modifier onlyEthereumGovernanceExecutor() virtual; - - /** - * @dev Constructor - * - * @param ethereumGovernanceExecutor The address of the EthereumGovernanceExecutor - * @param delay The delay before which an actions set can be executed - * @param gracePeriod The time period after a delay during which an actions set can be executed - * @param minimumDelay The minimum bound a delay can be set to - * @param maximumDelay The maximum bound a delay can be set to - * @param guardian The address of the guardian, which can cancel queued proposals (can be zero) - */ - constructor( - address ethereumGovernanceExecutor, - uint256 delay, - uint256 gracePeriod, - uint256 minimumDelay, - uint256 maximumDelay, - address guardian - ) - BridgeExecutorBase( - delay, - gracePeriod, - minimumDelay, - maximumDelay, - guardian - ) - { - _ethereumGovernanceExecutor = ethereumGovernanceExecutor; - } - - /// @inheritdoc IL2BridgeExecutor - function queue( - address[] memory targets, - uint256[] memory values, - string[] memory signatures, - bytes[] memory calldatas, - bool[] memory withDelegatecalls - ) external onlyEthereumGovernanceExecutor { - _queue(targets, values, signatures, calldatas, withDelegatecalls); - } - - /// @inheritdoc IL2BridgeExecutor - function updateEthereumGovernanceExecutor(address ethereumGovernanceExecutor) external onlyThis { - emit EthereumGovernanceExecutorUpdate(_ethereumGovernanceExecutor, ethereumGovernanceExecutor); - _ethereumGovernanceExecutor = ethereumGovernanceExecutor; - } - - /// @inheritdoc IL2BridgeExecutor - function getEthereumGovernanceExecutor() external view returns (address) { - return _ethereumGovernanceExecutor; - } -} diff --git a/src/executors/OptimismBridgeExecutor.sol b/src/executors/OptimismBridgeExecutor.sol deleted file mode 100644 index ebe4724..0000000 --- a/src/executors/OptimismBridgeExecutor.sol +++ /dev/null @@ -1,63 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0 -pragma solidity ^0.8.10; - -import { L2BridgeExecutor } from './L2BridgeExecutor.sol'; - -interface ICrossDomainMessenger { - function xDomainMessageSender() external view returns (address); -} - -/** - * @title OptimismBridgeExecutor - * @author Aave - * @notice Implementation of the Optimism Bridge Executor, able to receive cross-chain transactions from Ethereum - * @dev Queuing an ActionsSet into this Executor can only be done by the Optimism L2 Cross Domain Messenger and having - * the EthereumGovernanceExecutor as xDomainMessageSender - */ -contract OptimismBridgeExecutor is L2BridgeExecutor { - // Address of the Optimism L2 Cross Domain Messenger, in charge of redirecting cross-chain transactions in L2 - address public immutable OVM_L2_CROSS_DOMAIN_MESSENGER; - - /// @inheritdoc L2BridgeExecutor - modifier onlyEthereumGovernanceExecutor() override { - if ( - msg.sender != OVM_L2_CROSS_DOMAIN_MESSENGER || - ICrossDomainMessenger(OVM_L2_CROSS_DOMAIN_MESSENGER) - .xDomainMessageSender() != - _ethereumGovernanceExecutor - ) revert UnauthorizedEthereumExecutor(); - _; - } - - /** - * @dev Constructor - * - * @param ovmL2CrossDomainMessenger The address of the Optimism L2CrossDomainMessenger - * @param ethereumGovernanceExecutor The address of the EthereumGovernanceExecutor - * @param delay The delay before which an actions set can be executed - * @param gracePeriod The time period after a delay during which an actions set can be executed - * @param minimumDelay The minimum bound a delay can be set to - * @param maximumDelay The maximum bound a delay can be set to - * @param guardian The address of the guardian, which can cancel queued proposals (can be zero) - */ - constructor( - address ovmL2CrossDomainMessenger, - address ethereumGovernanceExecutor, - uint256 delay, - uint256 gracePeriod, - uint256 minimumDelay, - uint256 maximumDelay, - address guardian - ) - L2BridgeExecutor( - ethereumGovernanceExecutor, - delay, - gracePeriod, - minimumDelay, - maximumDelay, - guardian - ) - { - OVM_L2_CROSS_DOMAIN_MESSENGER = ovmL2CrossDomainMessenger; - } -} diff --git a/src/executors/ZkEVMBridgeExecutor.sol b/src/executors/ZkEVMBridgeExecutor.sol deleted file mode 100644 index d0e85f0..0000000 --- a/src/executors/ZkEVMBridgeExecutor.sol +++ /dev/null @@ -1,94 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0 -pragma solidity ^0.8.10; - -import { BridgeExecutorBase } from "./BridgeExecutorBase.sol"; -import { IL2BridgeExecutor } from "../interfaces/IL2BridgeExecutor.sol"; - -interface IZkEVMBridgeMessageReceiver { - function onMessageReceived(address originAddress, uint32 originNetwork, bytes memory data) external payable; -} - -/** - * @title ZkEVMBridgeExecutor - * @notice Implementation of the ZkEVM Bridge Executor, able to receive cross-chain transactions from Ethereum - * @dev Queuing an ActionsSet into this Executor can only be done by the ZkEVM Bridge and having - * the EthereumGovernanceExecutor as the sender - */ -contract ZkEVMBridgeExecutor is BridgeExecutorBase, IZkEVMBridgeMessageReceiver { - error UnauthorizedBridgeCaller(); - error InvalidOriginNetwork(); - error InvalidMethodId(); - error UnauthorizedEthereumExecutor(); - - /** - * @dev Emitted when the Ethereum Governance Executor is updated - * @param oldEthereumGovernanceExecutor The address of the old EthereumGovernanceExecutor - * @param newEthereumGovernanceExecutor The address of the new EthereumGovernanceExecutor - * - */ - event EthereumGovernanceExecutorUpdate( - address oldEthereumGovernanceExecutor, address newEthereumGovernanceExecutor - ); - - // Address of the Ethereum Governance Executor, which should be able to queue actions sets - address public ethereumGovernanceExecutor; - - uint32 public immutable originNetworkId; - - // Address of the ZkEVM bridge - address public immutable zkEVMBridge; - - /** - * @dev Constructor - * - * @param bridge The address of the ZkEVM Bridge - * @param ethereumGovernanceExecutor_ The address of the EthereumGovernanceExecutor - * @param delay The delay before which an actions set can be executed - * @param gracePeriod The time period after a delay during which an actions set can be executed - * @param minimumDelay The minimum bound a delay can be set to - * @param maximumDelay The maximum bound a delay can be set to - * @param guardian The address of the guardian, which can cancel queued proposals (can be zero) - */ - constructor( - address bridge, - address ethereumGovernanceExecutor_, - uint256 delay, - uint256 gracePeriod, - uint256 minimumDelay, - uint256 maximumDelay, - address guardian, - uint32 originNetworkId_ - ) BridgeExecutorBase(delay, gracePeriod, minimumDelay, maximumDelay, guardian) { - originNetworkId = originNetworkId_; - ethereumGovernanceExecutor = ethereumGovernanceExecutor_; - zkEVMBridge = bridge; - } - - function onMessageReceived(address originAddress, uint32 originNetwork, bytes calldata data) external payable { - if (msg.sender != zkEVMBridge) revert UnauthorizedBridgeCaller(); - if (originAddress != ethereumGovernanceExecutor) revert UnauthorizedEthereumExecutor(); - if (originNetwork != originNetworkId) revert InvalidOriginNetwork(); - bytes4 methodId = bytes4(data[0:4]); - if (methodId != IL2BridgeExecutor.queue.selector) revert InvalidMethodId(); - - ( - address[] memory targets, - uint256[] memory values, - string[] memory signatures, - bytes[] memory calldatas, - bool[] memory withDelegatecalls - ) = abi.decode(data[4:], (address[], uint256[], string[], bytes[], bool[])); - - _queue(targets, values, signatures, calldatas, withDelegatecalls); - } - - /** - * @notice Update the address of the Ethereum Governance Executor - * @param ethereumGovernanceExecutor_ The address of the new EthereumGovernanceExecutor - * - */ - function updateEthereumGovernanceExecutor(address ethereumGovernanceExecutor_) external onlyThis { - emit EthereumGovernanceExecutorUpdate(ethereumGovernanceExecutor, ethereumGovernanceExecutor_); - ethereumGovernanceExecutor = ethereumGovernanceExecutor_; - } -} diff --git a/src/interfaces/IAuthBridgeExecutor.sol b/src/interfaces/IAuthBridgeExecutor.sol new file mode 100644 index 0000000..9c8bbcb --- /dev/null +++ b/src/interfaces/IAuthBridgeExecutor.sol @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: AGPL-3.0 +pragma solidity ^0.8.10; + +import { IAccessControl } from 'lib/openzeppelin-contracts/contracts/access/IAccessControl.sol'; + +import { IExecutorBase } from './IExecutorBase.sol'; + +/** + * @title IAuthBridgeExecutor + * @notice Defines the basic interface for the AuthBridgeExecutor contract. + */ +interface IAuthBridgeExecutor is IAccessControl, IExecutorBase { + + /** + * @notice Queue an ActionsSet + * @dev If a signature is empty, calldata is used for the execution, calldata is appended to signature otherwise + * @param targets Array of targets to be called by the actions set + * @param values Array of values to pass in each call by the actions set + * @param signatures Array of function signatures to encode in each call by the actions (can be empty) + * @param calldatas Array of calldata to pass in each call by the actions set + * @param withDelegatecalls Array of whether to delegatecall for each call of the actions set + **/ + function queue( + address[] memory targets, + uint256[] memory values, + string[] memory signatures, + bytes[] memory calldatas, + bool[] memory withDelegatecalls + ) external; + +} diff --git a/src/interfaces/IL2BridgeExecutor.sol b/src/interfaces/IL2BridgeExecutor.sol deleted file mode 100644 index 0080607..0000000 --- a/src/interfaces/IL2BridgeExecutor.sol +++ /dev/null @@ -1,52 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0 -pragma solidity ^0.8.10; - -import { IExecutorBase } from './IExecutorBase.sol'; - -/** - * @title IL2BridgeExecutorBase - * @author Aave - * @notice Defines the basic interface for the L2BridgeExecutor abstract contract - */ -interface IL2BridgeExecutor is IExecutorBase { - error UnauthorizedEthereumExecutor(); - - /** - * @dev Emitted when the Ethereum Governance Executor is updated - * @param oldEthereumGovernanceExecutor The address of the old EthereumGovernanceExecutor - * @param newEthereumGovernanceExecutor The address of the new EthereumGovernanceExecutor - **/ - event EthereumGovernanceExecutorUpdate( - address oldEthereumGovernanceExecutor, - address newEthereumGovernanceExecutor - ); - - /** - * @notice Queue an ActionsSet - * @dev If a signature is empty, calldata is used for the execution, calldata is appended to signature otherwise - * @param targets Array of targets to be called by the actions set - * @param values Array of values to pass in each call by the actions set - * @param signatures Array of function signatures to encode in each call by the actions (can be empty) - * @param calldatas Array of calldata to pass in each call by the actions set - * @param withDelegatecalls Array of whether to delegatecall for each call of the actions set - **/ - function queue( - address[] memory targets, - uint256[] memory values, - string[] memory signatures, - bytes[] memory calldatas, - bool[] memory withDelegatecalls - ) external; - - /** - * @notice Update the address of the Ethereum Governance Executor - * @param ethereumGovernanceExecutor The address of the new EthereumGovernanceExecutor - **/ - function updateEthereumGovernanceExecutor(address ethereumGovernanceExecutor) external; - - /** - * @notice Returns the address of the Ethereum Governance Executor - * @return The address of the EthereumGovernanceExecutor - **/ - function getEthereumGovernanceExecutor() external view returns (address); -} diff --git a/src/receivers/BridgeExecutorReceiverArbitrum.sol b/src/receivers/BridgeExecutorReceiverArbitrum.sol new file mode 100644 index 0000000..5a7accb --- /dev/null +++ b/src/receivers/BridgeExecutorReceiverArbitrum.sol @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later +pragma solidity ^0.8.0; + +import { ArbitrumReceiver } from 'xchain-helpers/ArbitrumReceiver.sol'; + +import { IAuthBridgeExecutor } from '../interfaces/IAuthBridgeExecutor.sol'; + +contract BridgeExecutorReceiverArbitrum is ArbitrumReceiver { + + IAuthBridgeExecutor public executor; + + constructor( + address _l1Authority, + IAuthBridgeExecutor _executor + ) ArbitrumReceiver(_l1Authority) { + executor = _executor; + } + + function queue( + address[] memory targets, + uint256[] memory values, + string[] memory signatures, + bytes[] memory calldatas, + bool[] memory withDelegatecalls + ) external onlyCrossChainMessage { + executor.queue( + targets, + values, + signatures, + calldatas, + withDelegatecalls + ); + } + +} diff --git a/src/receivers/BridgeExecutorReceiverGnosis.sol b/src/receivers/BridgeExecutorReceiverGnosis.sol new file mode 100644 index 0000000..553039f --- /dev/null +++ b/src/receivers/BridgeExecutorReceiverGnosis.sol @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later +pragma solidity ^0.8.0; + +import { GnosisReceiver } from 'xchain-helpers/GnosisReceiver.sol'; + +import { IAuthBridgeExecutor } from '../interfaces/IAuthBridgeExecutor.sol'; + +contract BridgeExecutorReceiverGnosis is GnosisReceiver { + + IAuthBridgeExecutor public executor; + + constructor( + address _l2CrossDomain, + uint256 _chainId, + address _l1Authority, + IAuthBridgeExecutor _executor + ) GnosisReceiver(_l2CrossDomain, _chainId, _l1Authority) { + executor = _executor; + } + + function queue( + address[] memory targets, + uint256[] memory values, + string[] memory signatures, + bytes[] memory calldatas, + bool[] memory withDelegatecalls + ) external onlyCrossChainMessage { + executor.queue( + targets, + values, + signatures, + calldatas, + withDelegatecalls + ); + } + +} diff --git a/src/receivers/BridgeExecutorReceiverOptimism.sol b/src/receivers/BridgeExecutorReceiverOptimism.sol new file mode 100644 index 0000000..f5f483c --- /dev/null +++ b/src/receivers/BridgeExecutorReceiverOptimism.sol @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later +pragma solidity ^0.8.0; + +import { OptimismReceiver } from 'xchain-helpers/OptimismReceiver.sol'; + +import { IAuthBridgeExecutor } from '../interfaces/IAuthBridgeExecutor.sol'; + +contract BridgeExecutorReceiverOptimism is OptimismReceiver { + + IAuthBridgeExecutor public executor; + + constructor( + address _l1Authority, + IAuthBridgeExecutor _executor + ) OptimismReceiver(_l1Authority) { + executor = _executor; + } + + function queue( + address[] memory targets, + uint256[] memory values, + string[] memory signatures, + bytes[] memory calldatas, + bool[] memory withDelegatecalls + ) external onlyCrossChainMessage { + executor.queue( + targets, + values, + signatures, + calldatas, + withDelegatecalls + ); + } + +} diff --git a/test/ArbitrumCrosschainTest.t.sol b/test/ArbitrumCrosschainTest.t.sol index e995b70..44a55e3 100644 --- a/test/ArbitrumCrosschainTest.t.sol +++ b/test/ArbitrumCrosschainTest.t.sol @@ -6,32 +6,36 @@ import 'forge-std/Test.sol'; import { Domain, ArbitrumDomain } from 'xchain-helpers/testing/ArbitrumDomain.sol'; import { XChainForwarders } from 'xchain-helpers/XChainForwarders.sol'; -import { ArbitrumBridgeExecutor } from '../src/executors/ArbitrumBridgeExecutor.sol'; +import { AuthBridgeExecutor } from '../src/executors/AuthBridgeExecutor.sol'; +import { BridgeExecutorReceiverArbitrum } from '../src/receivers/BridgeExecutorReceiverArbitrum.sol'; import { IPayload } from './interfaces/IPayload.sol'; import { CrosschainPayload, CrosschainTestBase } from './CrosschainTestBase.sol'; contract ArbitrumCrosschainPayload is CrosschainPayload { - constructor(IPayload _targetPayload, address _bridgeExecutor) - CrosschainPayload(_targetPayload, _bridgeExecutor) {} + + constructor(IPayload _targetPayload, address _bridgeReceiver) + CrosschainPayload(_targetPayload, _bridgeReceiver) {} function execute() external override { XChainForwarders.sendMessageArbitrumOne( - bridgeExecutor, + bridgeReceiver, encodeCrosschainExecutionMessage(), 1_000_000, 1 gwei, block.basefee + 10 gwei ); } + } contract ArbitrumCrosschainTest is CrosschainTestBase { - function deployCrosschainPayload(IPayload targetPayload, address bridgeExecutor) + + function deployCrosschainPayload(IPayload targetPayload, address bridgeReceiver) public override returns (IPayload) { - return IPayload(new ArbitrumCrosschainPayload(targetPayload, bridgeExecutor)); + return IPayload(new ArbitrumCrosschainPayload(targetPayload, bridgeReceiver)); } function setUp() public { @@ -39,18 +43,21 @@ contract ArbitrumCrosschainTest is CrosschainTestBase { bridgedDomain = new ArbitrumDomain(getChain('arbitrum_one'), hostDomain); bridgedDomain.selectFork(); - bridgeExecutor = address( - new ArbitrumBridgeExecutor( - defaultL2BridgeExecutorArgs.ethereumGovernanceExecutor, - defaultL2BridgeExecutorArgs.delay, - defaultL2BridgeExecutorArgs.gracePeriod, - defaultL2BridgeExecutorArgs.minimumDelay, - defaultL2BridgeExecutorArgs.maximumDelay, - defaultL2BridgeExecutorArgs.guardian - ) + bridgeExecutor = new AuthBridgeExecutor( + defaultL2BridgeExecutorArgs.delay, + defaultL2BridgeExecutorArgs.gracePeriod, + defaultL2BridgeExecutorArgs.minimumDelay, + defaultL2BridgeExecutorArgs.maximumDelay, + defaultL2BridgeExecutorArgs.guardian ); + bridgeReceiver = address(new BridgeExecutorReceiverArbitrum( + defaultL2BridgeExecutorArgs.ethereumGovernanceExecutor, + bridgeExecutor + )); + bridgeExecutor.grantRole(bridgeExecutor.AUTHORIZED_BRIDGE_ROLE(), bridgeReceiver); hostDomain.selectFork(); vm.deal(L1_EXECUTOR, 0.01 ether); } + } diff --git a/test/BaseCrosschainTest.t.sol b/test/BaseCrosschainTest.t.sol new file mode 100644 index 0000000..4213fbd --- /dev/null +++ b/test/BaseCrosschainTest.t.sol @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import 'forge-std/Test.sol'; + +import { Domain, OptimismDomain } from 'xchain-helpers/testing/OptimismDomain.sol'; +import { XChainForwarders } from 'xchain-helpers/XChainForwarders.sol'; + +import { AuthBridgeExecutor } from '../src/executors/AuthBridgeExecutor.sol'; +import { BridgeExecutorReceiverOptimism } from '../src/receivers/BridgeExecutorReceiverOptimism.sol'; + +import { IPayload } from './interfaces/IPayload.sol'; + +import { CrosschainPayload, CrosschainTestBase } from './CrosschainTestBase.sol'; + +contract BaseCrosschainPayload is CrosschainPayload { + + constructor(IPayload _targetPayload, address _bridgeReceiver) + CrosschainPayload(_targetPayload, _bridgeReceiver) {} + + function execute() external override { + XChainForwarders.sendMessageBase( + bridgeReceiver, + encodeCrosschainExecutionMessage(), + 1_000_000 + ); + } + +} + +contract BaseCrosschainTest is CrosschainTestBase { + + function deployCrosschainPayload(IPayload targetPayload, address bridgeReceiver) + public override returns (IPayload) + { + return IPayload(new BaseCrosschainPayload(targetPayload, bridgeReceiver)); + } + + function setUp() public { + hostDomain = new Domain(getChain('mainnet')); + bridgedDomain = new OptimismDomain(getChain('base'), hostDomain); + + bridgedDomain.selectFork(); + bridgeExecutor = new AuthBridgeExecutor( + defaultL2BridgeExecutorArgs.delay, + defaultL2BridgeExecutorArgs.gracePeriod, + defaultL2BridgeExecutorArgs.minimumDelay, + defaultL2BridgeExecutorArgs.maximumDelay, + defaultL2BridgeExecutorArgs.guardian + ); + bridgeReceiver = address(new BridgeExecutorReceiverOptimism( + defaultL2BridgeExecutorArgs.ethereumGovernanceExecutor, + bridgeExecutor + )); + bridgeExecutor.grantRole(bridgeExecutor.AUTHORIZED_BRIDGE_ROLE(), bridgeReceiver); + + hostDomain.selectFork(); + } + +} diff --git a/test/CrosschainTestBase.sol b/test/CrosschainTestBase.sol index c8eddc3..ca08c68 100644 --- a/test/CrosschainTestBase.sol +++ b/test/CrosschainTestBase.sol @@ -6,7 +6,9 @@ import 'forge-std/Test.sol'; import { BridgedDomain } from 'xchain-helpers/testing/BridgedDomain.sol'; import { Domain } from 'xchain-helpers/testing/Domain.sol'; -import { IL2BridgeExecutor, IExecutorBase } from '../src/interfaces/IL2BridgeExecutor.sol'; +import { IAuthBridgeExecutor } from '../src/interfaces/IAuthBridgeExecutor.sol'; +import { IExecutorBase } from '../src/interfaces/IExecutorBase.sol'; +import { AuthBridgeExecutor } from '../src/executors/AuthBridgeExecutor.sol'; import { IL1Executor } from './interfaces/IL1Executor.sol'; import { IPayload } from './interfaces/IPayload.sol'; @@ -26,11 +28,11 @@ struct L2BridgeExecutorArguments { abstract contract CrosschainPayload is IPayload { IPayload immutable targetPayload; - address immutable bridgeExecutor; + address immutable bridgeReceiver; - constructor(IPayload _targetPayload, address _bridgeExecutor) { + constructor(IPayload _targetPayload, address _bridgeReceiver) { targetPayload = _targetPayload; - bridgeExecutor = _bridgeExecutor; + bridgeReceiver = _bridgeReceiver; } function execute() external virtual; @@ -48,7 +50,7 @@ abstract contract CrosschainPayload is IPayload { withDelegatecalls[0] = true; return abi.encodeWithSelector( - IL2BridgeExecutor.queue.selector, + IAuthBridgeExecutor.queue.selector, targets, values, signatures, @@ -75,12 +77,13 @@ abstract contract CrosschainTestBase is Test { guardian: GUARDIAN }); - Domain public hostDomain; + Domain public hostDomain; BridgedDomain public bridgedDomain; - address public bridgeExecutor; + AuthBridgeExecutor public bridgeExecutor; + address public bridgeReceiver; - function deployCrosschainPayload(IPayload targetPayload, address bridgeExecutor) public virtual returns (IPayload); + function deployCrosschainPayload(IPayload targetPayload, address bridgeReceiver) public virtual returns (IPayload); function preparePayloadExecution() public { bridgedDomain.selectFork(); @@ -91,7 +94,7 @@ abstract contract CrosschainTestBase is Test { IPayload crosschainPayload = deployCrosschainPayload( targetPayload, - bridgeExecutor + bridgeReceiver ); vm.prank(L1_PAUSE_PROXY); @@ -114,9 +117,9 @@ abstract contract CrosschainTestBase is Test { skip(delay); - vm.expectEmit(bridgeExecutor); + vm.expectEmit(address(bridgeExecutor)); emit TestEvent(); - IL2BridgeExecutor(bridgeExecutor).execute(0); + bridgeExecutor.execute(0); } function testFuzz_actionExecutionFailsAfterGracePeriod(uint delay) public { @@ -131,7 +134,7 @@ abstract contract CrosschainTestBase is Test { skip(delay); vm.expectRevert(IExecutorBase.OnlyQueuedActions.selector); - IL2BridgeExecutor(bridgeExecutor).execute(0); + bridgeExecutor.execute(0); } function testFuzz_actionExecutionFailsBeforeTimelock(uint delay) public { @@ -142,23 +145,23 @@ abstract contract CrosschainTestBase is Test { skip(delay); vm.expectRevert(IExecutorBase.TimelockNotFinished.selector); - IL2BridgeExecutor(bridgeExecutor).execute(0); + bridgeExecutor.execute(0); } function test_nonExistentActionExecutionFails() public { vm.expectRevert(); - IL2BridgeExecutor(bridgeExecutor).execute(0); + bridgeExecutor.execute(0); preparePayloadExecution(); skip(defaultL2BridgeExecutorArgs.delay); vm.expectRevert(); - IL2BridgeExecutor(bridgeExecutor).execute(1); + bridgeExecutor.execute(1); - vm.expectEmit(bridgeExecutor); + vm.expectEmit(address(bridgeExecutor)); emit TestEvent(); - IL2BridgeExecutor(bridgeExecutor).execute(0); + bridgeExecutor.execute(0); } function test_onlyGuardianCanCancel() public { @@ -167,24 +170,24 @@ abstract contract CrosschainTestBase is Test { preparePayloadExecution(); assertEq( - IL2BridgeExecutor(bridgeExecutor).getActionsSetById(0).canceled, + bridgeExecutor.getActionsSetById(0).canceled, false ); vm.expectRevert(); vm.prank(notGuardian); - IL2BridgeExecutor(bridgeExecutor).cancel(0); + bridgeExecutor.cancel(0); assertEq( - IL2BridgeExecutor(bridgeExecutor).getActionsSetById(0).canceled, + bridgeExecutor.getActionsSetById(0).canceled, false ); vm.prank(defaultL2BridgeExecutorArgs.guardian); - IL2BridgeExecutor(bridgeExecutor).cancel(0); + bridgeExecutor.cancel(0); assertEq( - IL2BridgeExecutor(bridgeExecutor).getActionsSetById(0).canceled, + bridgeExecutor.getActionsSetById(0).canceled, true ); } @@ -193,11 +196,11 @@ abstract contract CrosschainTestBase is Test { preparePayloadExecution(); vm.prank(defaultL2BridgeExecutorArgs.guardian); - IL2BridgeExecutor(bridgeExecutor).cancel(0); + bridgeExecutor.cancel(0); vm.expectRevert(IExecutorBase.OnlyQueuedActions.selector); vm.prank(defaultL2BridgeExecutorArgs.guardian); - IL2BridgeExecutor(bridgeExecutor).cancel(0); + bridgeExecutor.cancel(0); } function test_executedActionCannotBeCanceled() public { @@ -205,11 +208,11 @@ abstract contract CrosschainTestBase is Test { skip(defaultL2BridgeExecutorArgs.delay); - IL2BridgeExecutor(bridgeExecutor).execute(0); + bridgeExecutor.execute(0); vm.expectRevert(IExecutorBase.OnlyQueuedActions.selector); vm.prank(defaultL2BridgeExecutorArgs.guardian); - IL2BridgeExecutor(bridgeExecutor).cancel(0); + bridgeExecutor.cancel(0); } function test_expiredActionCannotBeCanceled() public { @@ -219,7 +222,7 @@ abstract contract CrosschainTestBase is Test { vm.expectRevert(IExecutorBase.OnlyQueuedActions.selector); vm.prank(defaultL2BridgeExecutorArgs.guardian); - IL2BridgeExecutor(bridgeExecutor).cancel(0); + bridgeExecutor.cancel(0); } function test_canceledActionCannotBeExecuted() public { @@ -228,10 +231,10 @@ abstract contract CrosschainTestBase is Test { skip(defaultL2BridgeExecutorArgs.delay); vm.prank(defaultL2BridgeExecutorArgs.guardian); - IL2BridgeExecutor(bridgeExecutor).cancel(0); + bridgeExecutor.cancel(0); vm.expectRevert(IExecutorBase.OnlyQueuedActions.selector); - IL2BridgeExecutor(bridgeExecutor).execute(0); + bridgeExecutor.execute(0); } function test_executingMultipleActions() public { @@ -244,60 +247,60 @@ abstract contract CrosschainTestBase is Test { skip(defaultL2BridgeExecutorArgs.delay); assertEq( - IL2BridgeExecutor(bridgeExecutor).getActionsSetById(0).executed, + bridgeExecutor.getActionsSetById(0).executed, false ); assertEq( - IL2BridgeExecutor(bridgeExecutor).getActionsSetById(1).executed, + bridgeExecutor.getActionsSetById(1).executed, false ); assertEq( - IL2BridgeExecutor(bridgeExecutor).getActionsSetById(2).executed, + bridgeExecutor.getActionsSetById(2).executed, false ); - IL2BridgeExecutor(bridgeExecutor).execute(1); + bridgeExecutor.execute(1); assertEq( - IL2BridgeExecutor(bridgeExecutor).getActionsSetById(0).executed, + bridgeExecutor.getActionsSetById(0).executed, false ); assertEq( - IL2BridgeExecutor(bridgeExecutor).getActionsSetById(1).executed, + bridgeExecutor.getActionsSetById(1).executed, true ); assertEq( - IL2BridgeExecutor(bridgeExecutor).getActionsSetById(2).executed, + bridgeExecutor.getActionsSetById(2).executed, false ); - IL2BridgeExecutor(bridgeExecutor).execute(2); + bridgeExecutor.execute(2); assertEq( - IL2BridgeExecutor(bridgeExecutor).getActionsSetById(0).executed, + bridgeExecutor.getActionsSetById(0).executed, false ); assertEq( - IL2BridgeExecutor(bridgeExecutor).getActionsSetById(1).executed, + bridgeExecutor.getActionsSetById(1).executed, true ); assertEq( - IL2BridgeExecutor(bridgeExecutor).getActionsSetById(2).executed, + bridgeExecutor.getActionsSetById(2).executed, true ); - IL2BridgeExecutor(bridgeExecutor).execute(0); + bridgeExecutor.execute(0); assertEq( - IL2BridgeExecutor(bridgeExecutor).getActionsSetById(0).executed, + bridgeExecutor.getActionsSetById(0).executed, true ); assertEq( - IL2BridgeExecutor(bridgeExecutor).getActionsSetById(1).executed, + bridgeExecutor.getActionsSetById(1).executed, true ); assertEq( - IL2BridgeExecutor(bridgeExecutor).getActionsSetById(2).executed, + bridgeExecutor.getActionsSetById(2).executed, true ); } @@ -306,23 +309,23 @@ abstract contract CrosschainTestBase is Test { bridgedDomain.selectFork(); assertEq( - IL2BridgeExecutor(bridgeExecutor).getDelay(), + bridgeExecutor.getDelay(), defaultL2BridgeExecutorArgs.delay ); assertEq( - IL2BridgeExecutor(bridgeExecutor).getGracePeriod(), + bridgeExecutor.getGracePeriod(), defaultL2BridgeExecutorArgs.gracePeriod ); assertEq( - IL2BridgeExecutor(bridgeExecutor).getMinimumDelay(), + bridgeExecutor.getMinimumDelay(), defaultL2BridgeExecutorArgs.minimumDelay ); assertEq( - IL2BridgeExecutor(bridgeExecutor).getMaximumDelay(), + bridgeExecutor.getMaximumDelay(), defaultL2BridgeExecutorArgs.maximumDelay ); assertEq( - IL2BridgeExecutor(bridgeExecutor).getGuardian(), + bridgeExecutor.getGuardian(), defaultL2BridgeExecutorArgs.guardian ); @@ -347,7 +350,7 @@ abstract contract CrosschainTestBase is Test { IPayload crosschainPayload = deployCrosschainPayload( reconfigurationPayload, - bridgeExecutor + bridgeReceiver ); vm.prank(L1_PAUSE_PROXY); @@ -360,26 +363,26 @@ abstract contract CrosschainTestBase is Test { skip(defaultL2BridgeExecutorArgs.delay); - IL2BridgeExecutor(bridgeExecutor).execute(0); + bridgeExecutor.execute(0); assertEq( - IL2BridgeExecutor(bridgeExecutor).getDelay(), + bridgeExecutor.getDelay(), newL2BridgeExecutorParams.delay ); assertEq( - IL2BridgeExecutor(bridgeExecutor).getGracePeriod(), + bridgeExecutor.getGracePeriod(), newL2BridgeExecutorParams.gracePeriod ); assertEq( - IL2BridgeExecutor(bridgeExecutor).getMinimumDelay(), + bridgeExecutor.getMinimumDelay(), newL2BridgeExecutorParams.minimumDelay ); assertEq( - IL2BridgeExecutor(bridgeExecutor).getMaximumDelay(), + bridgeExecutor.getMaximumDelay(), newL2BridgeExecutorParams.maximumDelay ); assertEq( - IL2BridgeExecutor(bridgeExecutor).getGuardian(), + bridgeExecutor.getGuardian(), newL2BridgeExecutorParams.guardian ); } diff --git a/test/GnosisCrosschainTest.t.sol b/test/GnosisCrosschainTest.t.sol index 8752f61..f1e7879 100644 --- a/test/GnosisCrosschainTest.t.sol +++ b/test/GnosisCrosschainTest.t.sol @@ -6,25 +6,21 @@ import 'forge-std/Test.sol'; import { Domain, GnosisDomain } from 'xchain-helpers/testing/GnosisDomain.sol'; import { XChainForwarders } from 'xchain-helpers/XChainForwarders.sol'; -import { IAMB, GnosisBridgeExecutor } from '../src/executors/GnosisBridgeExecutor.sol'; +import { AuthBridgeExecutor } from '../src/executors/AuthBridgeExecutor.sol'; +import { BridgeExecutorReceiverGnosis } from '../src/receivers/BridgeExecutorReceiverGnosis.sol'; -import { IL2BridgeExecutor } from '../src/interfaces/IL2BridgeExecutor.sol'; - -import { IL1Executor } from './interfaces/IL1Executor.sol'; -import { IPayload } from './interfaces/IPayload.sol'; - -import { GnosisReconfigurationPayload } from './mocks/GnosisReconfigurationPayload.sol'; +import { IPayload } from './interfaces/IPayload.sol'; import { CrosschainPayload, CrosschainTestBase } from './CrosschainTestBase.sol'; contract GnosisCrosschainPayload is CrosschainPayload { - constructor(IPayload _targetPayload, address _bridgeExecutor) - CrosschainPayload(_targetPayload, _bridgeExecutor) {} + constructor(IPayload _targetPayload, address _bridgeReceiver) + CrosschainPayload(_targetPayload, _bridgeReceiver) {} function execute() external override { XChainForwarders.sendMessageGnosis( - bridgeExecutor, + bridgeReceiver, encodeCrosschainExecutionMessage(), 1_000_000 ); @@ -33,87 +29,36 @@ contract GnosisCrosschainPayload is CrosschainPayload { } contract GnosisCrosschainTest is CrosschainTestBase { - IAMB public constant AMB = IAMB(0x75Df5AF045d91108662D8080fD1FEFAd6aA0bb59); - bytes32 public constant MAINNET_CHAIN_ID = bytes32(uint256(1)); + address constant AMB = 0x75Df5AF045d91108662D8080fD1FEFAd6aA0bb59; - function deployCrosschainPayload(IPayload targetPayload, address bridgeExecutor) + function deployCrosschainPayload(IPayload targetPayload, address bridgeReceiver) public override returns (IPayload) { - return IPayload(new GnosisCrosschainPayload(targetPayload, bridgeExecutor)); + return IPayload(new GnosisCrosschainPayload(targetPayload, bridgeReceiver)); } function setUp() public { - hostDomain = new Domain(getChain('mainnet')); + hostDomain = new Domain(getChain('mainnet')); bridgedDomain = new GnosisDomain(getChain('gnosis_chain'), hostDomain); bridgedDomain.selectFork(); - bridgeExecutor = address(new GnosisBridgeExecutor( - AMB, - defaultL2BridgeExecutorArgs.ethereumGovernanceExecutor, - MAINNET_CHAIN_ID, + bridgeExecutor = new AuthBridgeExecutor( defaultL2BridgeExecutorArgs.delay, defaultL2BridgeExecutorArgs.gracePeriod, defaultL2BridgeExecutorArgs.minimumDelay, defaultL2BridgeExecutorArgs.maximumDelay, defaultL2BridgeExecutorArgs.guardian - )); - - hostDomain.selectFork(); - } - - function test_gnosisSpecificSelfReconfiguration() public { - bridgedDomain.selectFork(); - - assertEq( - address(GnosisBridgeExecutor(bridgeExecutor).amb()), - address(AMB) - ); - assertEq( - GnosisBridgeExecutor(bridgeExecutor).controller(), - defaultL2BridgeExecutorArgs.ethereumGovernanceExecutor ); - assertEq( - GnosisBridgeExecutor(bridgeExecutor).chainId(), - MAINNET_CHAIN_ID - ); - - address newAmb = makeAddr('newAMB'); - address newController = makeAddr('newController'); - - bytes32 newChainId = bytes32(uint256(2)); - - IPayload reconfigurationPayload = IPayload(new GnosisReconfigurationPayload( - newAmb, - newController, - newChainId + bridgeReceiver = address(new BridgeExecutorReceiverGnosis( + AMB, + 1, // Ethereum chainid + defaultL2BridgeExecutorArgs.ethereumGovernanceExecutor, + bridgeExecutor )); + bridgeExecutor.grantRole(bridgeExecutor.AUTHORIZED_BRIDGE_ROLE(), bridgeReceiver); hostDomain.selectFork(); - - IPayload crosschainPayload = deployCrosschainPayload( - reconfigurationPayload, - bridgeExecutor - ); - - vm.prank(L1_PAUSE_PROXY); - IL1Executor(L1_EXECUTOR).exec( - address(crosschainPayload), - abi.encodeWithSelector(IPayload.execute.selector) - ); - - bridgedDomain.relayFromHost(true); - - skip(defaultL2BridgeExecutorArgs.delay); - - IL2BridgeExecutor(bridgeExecutor).execute(0); - - assertEq(address(GnosisBridgeExecutor(bridgeExecutor).amb()), newAmb); - assertEq( - GnosisBridgeExecutor(bridgeExecutor).controller(), - newController - ); - assertEq(GnosisBridgeExecutor(bridgeExecutor).chainId(), newChainId); } } diff --git a/test/OptimismCrosschainTest.t.sol b/test/OptimismCrosschainTest.t.sol index 321f4df..47c34ec 100644 --- a/test/OptimismCrosschainTest.t.sol +++ b/test/OptimismCrosschainTest.t.sol @@ -6,7 +6,8 @@ import 'forge-std/Test.sol'; import { Domain, OptimismDomain } from 'xchain-helpers/testing/OptimismDomain.sol'; import { XChainForwarders } from 'xchain-helpers/XChainForwarders.sol'; -import { OptimismBridgeExecutor } from '../src/executors/OptimismBridgeExecutor.sol'; +import { AuthBridgeExecutor } from '../src/executors/AuthBridgeExecutor.sol'; +import { BridgeExecutorReceiverOptimism } from '../src/receivers/BridgeExecutorReceiverOptimism.sol'; import { IPayload } from './interfaces/IPayload.sol'; @@ -14,12 +15,12 @@ import { CrosschainPayload, CrosschainTestBase } from './CrosschainTestBase.sol' contract OptimismCrosschainPayload is CrosschainPayload { - constructor(IPayload _targetPayload, address _bridgeExecutor) - CrosschainPayload(_targetPayload, _bridgeExecutor) {} + constructor(IPayload _targetPayload, address _bridgeReceiver) + CrosschainPayload(_targetPayload, _bridgeReceiver) {} function execute() external override { XChainForwarders.sendMessageOptimismMainnet( - bridgeExecutor, + bridgeReceiver, encodeCrosschainExecutionMessage(), 1_000_000 ); @@ -29,12 +30,10 @@ contract OptimismCrosschainPayload is CrosschainPayload { contract OptimismCrosschainTest is CrosschainTestBase { - address public constant OVM_L2_CROSS_DOMAIN_MESSENGER = 0x4200000000000000000000000000000000000007; - - function deployCrosschainPayload(IPayload targetPayload, address bridgeExecutor) + function deployCrosschainPayload(IPayload targetPayload, address bridgeReceiver) public override returns (IPayload) { - return IPayload(new OptimismCrosschainPayload(targetPayload, bridgeExecutor)); + return IPayload(new OptimismCrosschainPayload(targetPayload, bridgeReceiver)); } function setUp() public { @@ -42,15 +41,18 @@ contract OptimismCrosschainTest is CrosschainTestBase { bridgedDomain = new OptimismDomain(getChain('optimism'), hostDomain); bridgedDomain.selectFork(); - bridgeExecutor = address(new OptimismBridgeExecutor( - OVM_L2_CROSS_DOMAIN_MESSENGER, - defaultL2BridgeExecutorArgs.ethereumGovernanceExecutor, + bridgeExecutor = new AuthBridgeExecutor( defaultL2BridgeExecutorArgs.delay, defaultL2BridgeExecutorArgs.gracePeriod, defaultL2BridgeExecutorArgs.minimumDelay, defaultL2BridgeExecutorArgs.maximumDelay, defaultL2BridgeExecutorArgs.guardian + ); + bridgeReceiver = address(new BridgeExecutorReceiverOptimism( + defaultL2BridgeExecutorArgs.ethereumGovernanceExecutor, + bridgeExecutor )); + bridgeExecutor.grantRole(bridgeExecutor.AUTHORIZED_BRIDGE_ROLE(), bridgeReceiver); hostDomain.selectFork(); } diff --git a/test/ZkEVMCrosschainTest.t.sol b/test/ZkEVMCrosschainTest.t.sol deleted file mode 100644 index 02bfd6e..0000000 --- a/test/ZkEVMCrosschainTest.t.sol +++ /dev/null @@ -1,59 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; - -import 'forge-std/Test.sol'; - -import { Domain, ZkEVMDomain } from 'xchain-helpers/testing/ZkEVMDomain.sol'; -import { XChainForwarders } from 'xchain-helpers/XChainForwarders.sol'; - -import { ZkEVMBridgeExecutor } from '../src/executors/ZkEVMBridgeExecutor.sol'; - -import { IPayload } from './interfaces/IPayload.sol'; - -import { CrosschainPayload, CrosschainTestBase } from './CrosschainTestBase.sol'; - -contract ZkEVMCrosschainPayload is CrosschainPayload { - - constructor(IPayload _targetPayload, address _bridgeExecutor) - CrosschainPayload(_targetPayload, _bridgeExecutor) {} - - function execute() external override { - XChainForwarders.sendMessageZkEVM( - bridgeExecutor, - encodeCrosschainExecutionMessage() - ); - } - -} - -// FIXME: zkEVM bridging is broken, marking as abstract to temporarily disable until it's fixed -abstract contract ZkEVMCrosschainTest is CrosschainTestBase { - address constant ZKEVM_BRIDGE = 0x2a3DD3EB832aF982ec71669E178424b10Dca2EDe; - - function deployCrosschainPayload(IPayload targetPayload, address bridgeExecutor) - public override returns (IPayload) - { - return IPayload(new ZkEVMCrosschainPayload(targetPayload, bridgeExecutor)); - } - - function setUp() public { - setChain("zkevm", ChainData("ZkEVM", 1101, "https://zkevm-rpc.com")); - hostDomain = new Domain(getChain('mainnet')); - bridgedDomain = new ZkEVMDomain(getChain('zkevm'), hostDomain); - - bridgedDomain.selectFork(); - bridgeExecutor = address(new ZkEVMBridgeExecutor( - ZKEVM_BRIDGE, - defaultL2BridgeExecutorArgs.ethereumGovernanceExecutor, - defaultL2BridgeExecutorArgs.delay, - defaultL2BridgeExecutorArgs.gracePeriod, - defaultL2BridgeExecutorArgs.minimumDelay, - defaultL2BridgeExecutorArgs.maximumDelay, - defaultL2BridgeExecutorArgs.guardian, - 0 - )); - - hostDomain.selectFork(); - } - -} diff --git a/test/mocks/GnosisReconfigurationPayload.sol b/test/mocks/GnosisReconfigurationPayload.sol deleted file mode 100644 index 14313de..0000000 --- a/test/mocks/GnosisReconfigurationPayload.sol +++ /dev/null @@ -1,45 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.10; - -import { GnosisBridgeExecutor } from '../../src/executors/GnosisBridgeExecutor.sol'; - -import { IPayload } from '../interfaces/IPayload.sol'; - -/** - * @dev This payload reconfigures Gnosis bridge executor to a given state - */ -contract GnosisReconfigurationPayload is IPayload { - - address public immutable newAmb; - address public immutable newController; - - bytes32 public immutable newChainId; - - constructor( - address _newAmb, - address _newController, - bytes32 _newChainId - ) { - newAmb = _newAmb; - newController = _newController; - newChainId = _newChainId; - } - - function execute() external override { - GnosisBridgeExecutor(address(this)).setAmb(getNewAmb()); - GnosisBridgeExecutor(address(this)).setController(getNewController()); - GnosisBridgeExecutor(address(this)).setChainId(getNewChainId()); - } - - function getNewAmb() public view returns (address) { - return newAmb; - } - - function getNewController() public view returns (address) { - return newController; - } - - function getNewChainId() public view returns (bytes32) { - return newChainId; - } -}