Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
N
nebula
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
exchain
nebula
Commits
a8b042bf
Commit
a8b042bf
authored
Feb 17, 2023
by
Mark Tyneway
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
contracts-bedrock: make the portal pausable
parent
a6a14b70
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
186 additions
and
8 deletions
+186
-8
OptimismPortal.sol
packages/contracts-bedrock/contracts/L1/OptimismPortal.sol
+58
-4
FuzzOptimismPortal.sol
...ontracts-bedrock/contracts/echidna/FuzzOptimismPortal.sol
+5
-1
CommonTest.t.sol
packages/contracts-bedrock/contracts/test/CommonTest.t.sol
+13
-3
OptimismPortal.t.sol
...ges/contracts-bedrock/contracts/test/OptimismPortal.t.sol
+104
-0
013-OptimismPortalImpl.ts
packages/contracts-bedrock/deploy/013-OptimismPortalImpl.ts
+6
-0
No files found.
packages/contracts-bedrock/contracts/L1/OptimismPortal.sol
View file @
a8b042bf
...
...
@@ -58,6 +58,11 @@ contract OptimismPortal is Initializable, ResourceMetering, Semver {
*/
L2OutputOracle public immutable L2_ORACLE;
/**
* @notice Address that has the ability to pause and unpause deposits and withdrawals.
*/
address public immutable GUARDIAN;
/**
* @notice Address of the L2 account which initiated a withdrawal in this transaction. If the
* of this variable is the default L2 sender address, then we are NOT inside of a call
...
...
@@ -75,6 +80,13 @@ contract OptimismPortal is Initializable, ResourceMetering, Semver {
*/
mapping(bytes32 => ProvenWithdrawal) public provenWithdrawals;
/**
* @notice Determines if cross domain messaging is paused. When set to true,
* deposits and withdrawals are paused. This may be removed in the
* future.
*/
bool public paused = false;
/**
* @notice Emitted when a transaction is deposited from L1 to L2. The parameters of this event
* are read by the rollup node and used to derive deposit transactions on L2.
...
...
@@ -110,14 +122,38 @@ contract OptimismPortal is Initializable, ResourceMetering, Semver {
*/
event WithdrawalFinalized(bytes32 indexed withdrawalHash, bool success);
/**
* @notice Emitted when the pause is triggered.
*
* @param account Address of the account triggering the pause.
*/
event Paused(address account);
/**
* @notice Emitted when the pause is lifted.
*
* @param account Address of the account triggering the unpause.
*/
event Unpaused(address account);
/**
* @notice Reverts when paused.
*/
modifier whenNotPaused() {
require(paused == false, "OptimismPortal: paused");
_;
}
/**
* @custom:semver 1.0.0
*
* @param _l2Oracle Address of the L2OutputOracle contract.
* @param _guardian Address that can pause deposits and withdrawals.
* @param _finalizationPeriodSeconds Output finalization time in seconds.
*/
constructor(L2OutputOracle _l2Oracle,
uint256 _finalizationPeriodSeconds) Semver(1, 0
, 0) {
constructor(L2OutputOracle _l2Oracle,
address _guardian, uint256 _finalizationPeriodSeconds) Semver(1, 1
, 0) {
L2_ORACLE = _l2Oracle;
GUARDIAN = _guardian;
FINALIZATION_PERIOD_SECONDS = _finalizationPeriodSeconds;
initialize();
}
...
...
@@ -130,6 +166,24 @@ contract OptimismPortal is Initializable, ResourceMetering, Semver {
__ResourceMetering_init();
}
/**
* @notice
*/
function pause() external {
require(msg.sender == GUARDIAN, "OptimismPortal: only guardian can pause");
paused = true;
emit Paused(msg.sender);
}
/**
* @notice
*/
function unpause() external {
require(msg.sender == GUARDIAN, "OptimismPortal: only guardian can unpause");
paused = false;
emit Unpaused(msg.sender);
}
/**
* @notice Accepts value so that users can send ETH directly to this contract and have the
* funds be deposited to their address on L2. This is intended as a convenience
...
...
@@ -162,7 +216,7 @@ contract OptimismPortal is Initializable, ResourceMetering, Semver {
uint256 _l2OutputIndex,
Types.OutputRootProof calldata _outputRootProof,
bytes[] calldata _withdrawalProof
) external {
) external
whenNotPaused
{
// Prevent users from creating a deposit transaction where this address is the message
// sender on L2. Because this is checked here, we do not need to check again in
// `finalizeWithdrawalTransaction`.
...
...
@@ -240,7 +294,7 @@ contract OptimismPortal is Initializable, ResourceMetering, Semver {
*
* @param _tx Withdrawal transaction to finalize.
*/
function finalizeWithdrawalTransaction(Types.WithdrawalTransaction memory _tx) external {
function finalizeWithdrawalTransaction(Types.WithdrawalTransaction memory _tx) external
whenNotPaused
{
// Make sure that the l2Sender has not yet been set. The l2Sender is set to a value other
// than the default value when a withdrawal transaction is being finalized. This check is
// a defacto reentrancy guard.
...
...
@@ -361,7 +415,7 @@ contract OptimismPortal is Initializable, ResourceMetering, Semver {
uint64 _gasLimit,
bool _isCreation,
bytes memory _data
) public payable metered(_gasLimit) {
) public payable
whenNotPaused
metered(_gasLimit) {
// Just to be safe, make sure that people specify address(0) as the target when doing
// contract creations.
if (_isCreation) {
...
...
packages/contracts-bedrock/contracts/echidna/FuzzOptimismPortal.sol
View file @
a8b042bf
...
...
@@ -9,7 +9,11 @@ contract EchidnaFuzzOptimismPortal {
bool internal failedToComplete;
constructor() {
portal = new OptimismPortal(L2OutputOracle(address(0)), 10);
portal = new OptimismPortal({
_l2Oracle: L2OutputOracle(address(0)),
_guardian: address(0),
_finalizationPeriodSeconds: 10
});
}
// A test intended to identify any unexpected halting conditions
...
...
packages/contracts-bedrock/contracts/test/CommonTest.t.sol
View file @
a8b042bf
...
...
@@ -99,6 +99,7 @@ contract L2OutputOracle_Initializer is CommonTest {
uint256 internal l2BlockTime = 2;
uint256 internal startingBlockNumber = 200;
uint256 internal startingTimestamp = 1000;
address guardian;
// Test data
uint256 initL1Time;
...
...
@@ -118,7 +119,8 @@ contract L2OutputOracle_Initializer is CommonTest {
}
function setUp() public virtual override {
super.setUp();
guardian = makeAddr("guardian");
// By default the first block has timestamp and number zero, which will cause underflows in the
// tests, so we'll move forward to these block values.
initL1Time = startingTimestamp + 1;
...
...
@@ -164,7 +166,11 @@ contract Portal_Initializer is L2OutputOracle_Initializer {
function setUp() public virtual override {
super.setUp();
opImpl = new OptimismPortal(oracle, 7 days);
opImpl = new OptimismPortal({
_l2Oracle: oracle,
_guardian: guardian,
_finalizationPeriodSeconds: 7 days
});
Proxy proxy = new Proxy(multisig);
vm.prank(multisig);
proxy.upgradeToAndCall(
...
...
@@ -223,7 +229,11 @@ contract Messenger_Initializer is L2OutputOracle_Initializer {
super.setUp();
// Deploy the OptimismPortal
op = new OptimismPortal(oracle, 7 days);
op = new OptimismPortal({
_l2Oracle: oracle,
_guardian: guardian,
_finalizationPeriodSeconds: 7 days
});
vm.label(address(op), "OptimismPortal");
// Deploy the address manager
...
...
packages/contracts-bedrock/contracts/test/OptimismPortal.t.sol
View file @
a8b042bf
...
...
@@ -11,10 +11,70 @@ import { Hashing } from "../libraries/Hashing.sol";
import { Proxy } from "../universal/Proxy.sol";
contract OptimismPortal_Test is Portal_Initializer {
event Paused(address);
event Unpaused(address);
function test_constructor_succeeds() external {
assertEq(op.FINALIZATION_PERIOD_SECONDS(), 7 days);
assertEq(address(op.L2_ORACLE()), address(oracle));
assertEq(op.l2Sender(), 0x000000000000000000000000000000000000dEaD);
assertEq(op.paused(), false);
}
function test_pause_succeeds() external {
address guardian = op.GUARDIAN();
assertEq(op.paused(), false);
vm.expectEmit(true, true, true, true, address(op));
emit Paused(guardian);
vm.prank(guardian);
op.pause();
assertEq(op.paused(), true);
}
function test_pause_onlyOwner_reverts() external {
assertEq(op.paused(), false);
assertTrue(op.GUARDIAN() != alice);
vm.expectRevert("OptimismPortal: only guardian can pause");
vm.prank(alice);
op.pause();
assertEq(op.paused(), false);
}
function test_unpause_succeeds() external {
address guardian = op.GUARDIAN();
vm.prank(guardian);
op.pause();
assertEq(op.paused(), true);
vm.expectEmit(true, true, true, true, address(op));
emit Unpaused(guardian);
vm.prank(guardian);
op.unpause();
assertEq(op.paused(), false);
}
function test_unpause_onlyOwner_reverts() external {
address guardian = op.GUARDIAN();
vm.prank(guardian);
op.pause();
assertEq(op.paused(), true);
assertTrue(op.GUARDIAN() != alice);
vm.expectRevert("OptimismPortal: only guardian can unpause");
vm.prank(alice);
op.unpause();
assertEq(op.paused(), true);
}
function test_receive_succeeds() external {
...
...
@@ -30,6 +90,23 @@ contract OptimismPortal_Test is Portal_Initializer {
assertEq(address(op).balance, 100);
}
/**
* @notice Deposit transactions should revert when paused
*/
function test_depositTransaction_paused_reverts() external {
vm.prank(op.GUARDIAN());
op.pause();
vm.expectRevert("OptimismPortal: paused");
op.depositTransaction({
_to: address(1),
_value: 100,
_gasLimit: 200_000,
_isCreation: false,
_data: hex""
});
}
// Test: depositTransaction fails when contract creation has a non-zero destination address
function test_depositTransaction_contractCreation_reverts() external {
// contract creation must have a target of address(0)
...
...
@@ -343,6 +420,22 @@ contract OptimismPortal_FinalizeWithdrawal_Test is Portal_Initializer {
assertFalse(op.finalizedWithdrawals(Hashing.hashWithdrawal(_defaultTx)));
}
/**
* @notice Proving withdrawal transactions should revert when paused
*/
function test_proveWithdrawalTransaction_paused_reverts() external {
vm.prank(op.GUARDIAN());
op.pause();
vm.expectRevert("OptimismPortal: paused");
op.proveWithdrawalTransaction({
_tx: _defaultTx,
_l2OutputIndex: _proposedOutputIndex,
_outputRootProof: _outputRootProof,
_withdrawalProof: _withdrawalProof
});
}
// Test: proveWithdrawalTransaction cannot prove a withdrawal with itself (the OptimismPortal) as the target.
function test_proveWithdrawalTransaction_onSelfCall_reverts() external {
_defaultTx.target = address(op);
...
...
@@ -539,6 +632,17 @@ contract OptimismPortal_FinalizeWithdrawal_Test is Portal_Initializer {
assert(address(bob).balance == bobBalanceBefore + 100);
}
/**
* @notice Finalizing withdrawal transactions should revert when paused
*/
function test_finalizeWithdrawalTransaction_paused_reverts() external {
vm.prank(op.GUARDIAN());
op.pause();
vm.expectRevert("OptimismPortal: paused");
op.finalizeWithdrawalTransaction(_defaultTx);
}
// Test: finalizeWithdrawalTransaction reverts if the withdrawal has not been proven.
function test_finalizeWithdrawalTransaction_ifWithdrawalNotProven_reverts() external {
uint256 bobBalanceBefore = address(bob).balance;
...
...
packages/contracts-bedrock/deploy/013-OptimismPortalImpl.ts
View file @
a8b042bf
...
...
@@ -19,6 +19,7 @@ const deployFn: DeployFunction = async (hre) => {
args
:
[
L2OutputOracleProxy
.
address
,
hre
.
deployConfig
.
finalizationPeriodSeconds
,
hre
.
deployConfig
.
finalSystemOwner
,
],
postDeployAction
:
async
(
contract
)
=>
{
await
assertContractVariable
(
...
...
@@ -31,6 +32,11 @@ const deployFn: DeployFunction = async (hre) => {
'
FINALIZATION_PERIOD_SECONDS
'
,
hre
.
deployConfig
.
finalizationPeriodSeconds
)
await
assertContractVariable
(
contract
,
'
GUARDIAN
'
,
hre
.
deployConfig
.
finalSystemOwner
)
},
})
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment