Commit ef29d8a5 authored by Maurelian's avatar Maurelian Committed by GitHub

bedrock: Make portal upgradeable (#2825)

* bedrock: Make portal upgradeable

* bedrock: rebuild bindings and snapshot

* bedrock: update portal storage layout

* bedrock: Test oracle behind proxy

--wip--

* bedrock: Test portal behind a proxy

* bedrock: Set l2Sender in initialize

* bedrock: workarounds for forge test overflow bug

* bedrock: reorder inheritance in portal

This shifts the order of inheritance so that the initializable
values are in the first slot of the portal, which is consistent with
all other contracts we have that inherit from initializable

* Revert "bedrock: workarounds for forge test overflow bug"

Forge seems to be working again!?

This reverts commit bb8cdf2f3ecce1456c01e668f36b1429e02934d1.

* bedrock: Make ResourceMetering non-abstract

* bedrock: Add test for storage access post upgrade

* chore: update gas snapshot and bindings
parent 5c3b4bfa
---
'@eth-optimism/contracts-bedrock': patch
---
Make the Portal upgradeable
This diff is collapsed.
...@@ -27,26 +27,26 @@ L1CrossDomainMessenger_Test:testCannot_L1MessengerPause() (gas: 10844) ...@@ -27,26 +27,26 @@ L1CrossDomainMessenger_Test:testCannot_L1MessengerPause() (gas: 10844)
L1CrossDomainMessenger_Test:testCannot_L1MessengerUnpause() (gas: 10858) L1CrossDomainMessenger_Test:testCannot_L1MessengerUnpause() (gas: 10858)
L1CrossDomainMessenger_Test:test_L1MessengerMessageVersion() (gas: 8388) L1CrossDomainMessenger_Test:test_L1MessengerMessageVersion() (gas: 8388)
L1CrossDomainMessenger_Test:test_L1MessengerPause() (gas: 31860) L1CrossDomainMessenger_Test:test_L1MessengerPause() (gas: 31860)
L1CrossDomainMessenger_Test:test_L1MessengerRelayMessageSucceeds() (gas: 61283) L1CrossDomainMessenger_Test:test_L1MessengerRelayMessageSucceeds() (gas: 61217)
L1CrossDomainMessenger_Test:test_L1MessengerRelayMessageToSystemContract() (gas: 44903) L1CrossDomainMessenger_Test:test_L1MessengerRelayMessageToSystemContract() (gas: 44837)
L1CrossDomainMessenger_Test:test_L1MessengerRelayShouldRevertIfPaused() (gas: 41587) L1CrossDomainMessenger_Test:test_L1MessengerRelayShouldRevertIfPaused() (gas: 41587)
L1CrossDomainMessenger_Test:test_L1MessengerSendMessage() (gas: 172193) L1CrossDomainMessenger_Test:test_L1MessengerSendMessage() (gas: 172199)
L1CrossDomainMessenger_Test:test_L1MessengerTwiceSendMessage() (gas: 1254199) L1CrossDomainMessenger_Test:test_L1MessengerTwiceSendMessage() (gas: 1254211)
L1CrossDomainMessenger_Test:test_L1MessengerUnpause() (gas: 23804) L1CrossDomainMessenger_Test:test_L1MessengerUnpause() (gas: 23804)
L1CrossDomainMessenger_Test:test_L1MessengerXDomainSenderReverts() (gas: 10599) L1CrossDomainMessenger_Test:test_L1MessengerXDomainSenderReverts() (gas: 10599)
L1CrossDomainMessenger_Test:test_L1MessengerxDomainMessageSenderResets() (gas: 58601) L1CrossDomainMessenger_Test:test_L1MessengerxDomainMessageSenderResets() (gas: 59022)
L1StandardBridge_Test:test_depositERC20() (gas: 452856) L1StandardBridge_Test:test_depositERC20() (gas: 452889)
L1StandardBridge_Test:test_depositERC20To() (gas: 454632) L1StandardBridge_Test:test_depositERC20To() (gas: 454644)
L1StandardBridge_Test:test_depositETH() (gas: 247077) L1StandardBridge_Test:test_depositETH() (gas: 247083)
L1StandardBridge_Test:test_depositETHTo() (gas: 204961) L1StandardBridge_Test:test_depositETHTo() (gas: 204967)
L1StandardBridge_Test:test_finalizeERC20Withdrawal() (gas: 438745) L1StandardBridge_Test:test_finalizeERC20Withdrawal() (gas: 438779)
L1StandardBridge_Test:test_finalizeETHWithdrawal() (gas: 48005) L1StandardBridge_Test:test_finalizeETHWithdrawal() (gas: 48005)
L1StandardBridge_Test:test_initialize() (gas: 14885) L1StandardBridge_Test:test_initialize() (gas: 14885)
L1StandardBridge_Test:test_onlyEOADepositERC20() (gas: 12085) L1StandardBridge_Test:test_onlyEOADepositERC20() (gas: 12085)
L1StandardBridge_Test:test_onlyEOADepositETH() (gas: 30637) L1StandardBridge_Test:test_onlyEOADepositETH() (gas: 30637)
L1StandardBridge_Test:test_onlyL2BridgeFinalizeERC20Withdrawal() (gas: 23565) L1StandardBridge_Test:test_onlyL2BridgeFinalizeERC20Withdrawal() (gas: 23565)
L1StandardBridge_Test:test_onlyPortalFinalizeERC20Withdrawal() (gas: 22919) L1StandardBridge_Test:test_onlyPortalFinalizeERC20Withdrawal() (gas: 22919)
L1StandardBridge_Test:test_receive() (gas: 391817) L1StandardBridge_Test:test_receive() (gas: 391823)
L2CrossDomainMessenger_Test:testCannot_L2MessengerPause() (gas: 10843) L2CrossDomainMessenger_Test:testCannot_L2MessengerPause() (gas: 10843)
L2CrossDomainMessenger_Test:test_L2MessengerMessageVersion() (gas: 8410) L2CrossDomainMessenger_Test:test_L2MessengerMessageVersion() (gas: 8410)
L2CrossDomainMessenger_Test:test_L2MessengerPause() (gas: 31837) L2CrossDomainMessenger_Test:test_L2MessengerPause() (gas: 31837)
...@@ -57,27 +57,28 @@ L2CrossDomainMessenger_Test:test_L2MessengerSendMessage() (gas: 119682) ...@@ -57,27 +57,28 @@ L2CrossDomainMessenger_Test:test_L2MessengerSendMessage() (gas: 119682)
L2CrossDomainMessenger_Test:test_L2MessengerTwiceSendMessage() (gas: 133142) L2CrossDomainMessenger_Test:test_L2MessengerTwiceSendMessage() (gas: 133142)
L2CrossDomainMessenger_Test:test_L2MessengerXDomainSenderReverts() (gas: 10599) L2CrossDomainMessenger_Test:test_L2MessengerXDomainSenderReverts() (gas: 10599)
L2CrossDomainMessenger_Test:test_L2MessengerxDomainMessageSenderResets() (gas: 54881) L2CrossDomainMessenger_Test:test_L2MessengerxDomainMessageSenderResets() (gas: 54881)
L2OutputOracleTest:testCannot_appendEmptyOutput() (gas: 18216) L2OutputOracleTest:testCannot_appendEmptyOutput() (gas: 24163)
L2OutputOracleTest:testCannot_appendFutureTimetamp() (gas: 20183) L2OutputOracleTest:testCannot_appendFutureTimetamp() (gas: 26130)
L2OutputOracleTest:testCannot_appendOnWrongFork() (gas: 20462) L2OutputOracleTest:testCannot_appendOnWrongFork() (gas: 26415)
L2OutputOracleTest:testCannot_appendOutputIfNotSequencer() (gas: 17607) L2OutputOracleTest:testCannot_appendOutputIfNotSequencer() (gas: 23554)
L2OutputOracleTest:testCannot_appendUnexpectedBlockNumber() (gas: 20086) L2OutputOracleTest:testCannot_appendUnexpectedBlockNumber() (gas: 26039)
L2OutputOracleTest:testCannot_deleteL2Output_ifNotOwner() (gas: 18893) L2OutputOracleTest:testCannot_deleteL2Output_ifNotOwner() (gas: 24799)
L2OutputOracleTest:testCannot_deleteL2Output_withWrongRoot() (gas: 83353) L2OutputOracleTest:testCannot_deleteL2Output_withWrongRoot() (gas: 91157)
L2OutputOracleTest:testCannot_deleteL2Output_withWrongTime() (gas: 79346) L2OutputOracleTest:testCannot_deleteL2Output_withWrongTime() (gas: 87150)
L2OutputOracleTest:test_appendWithBlockhashAndHeight() (gas: 69126) L2OutputOracleTest:test_appendWithBlockhashAndHeight() (gas: 75061)
L2OutputOracleTest:test_appendingAnotherOutput() (gas: 70475) L2OutputOracleTest:test_appendingAnotherOutput() (gas: 76861)
L2OutputOracleTest:test_changeSequencer() (gas: 35473) L2OutputOracleTest:test_changeSequencer() (gas: 55776)
L2OutputOracleTest:test_computeL2Timestamp() (gas: 19323) L2OutputOracleTest:test_computeL2Timestamp() (gas: 30288)
L2OutputOracleTest:test_constructor() (gas: 40152) L2OutputOracleTest:test_constructor() (gas: 48845)
L2OutputOracleTest:test_deleteL2Output() (gas: 69237) L2OutputOracleTest:test_deleteL2Output() (gas: 76604)
L2OutputOracleTest:test_getL2Output() (gas: 76099) L2OutputOracleTest:test_getL2Output() (gas: 83012)
L2OutputOracleTest:test_latestBlockNumber() (gas: 69878) L2OutputOracleTest:test_latestBlockNumber() (gas: 76264)
L2OutputOracleTest:test_nextBlockNumber() (gas: 9281) L2OutputOracleTest:test_nextBlockNumber() (gas: 15166)
L2OutputOracleTest:test_updateOwner() (gas: 24185) L2OutputOracleTest:test_updateOwner() (gas: 34646)
L2OutputOracleUpgradeable_Test:test_cannotInitImpl() (gas: 8431) L2OutputOracleUpgradeable_Test:test_cannotInitImpl() (gas: 8453)
L2OutputOracleUpgradeable_Test:test_cannotInitProxy() (gas: 13430) L2OutputOracleUpgradeable_Test:test_cannotInitProxy() (gas: 13430)
L2OutputOracleUpgradeable_Test:test_initValuesOnProxy() (gas: 38906) L2OutputOracleUpgradeable_Test:test_initValuesOnProxy() (gas: 38906)
L2OutputOracleUpgradeable_Test:test_upgrading() (gas: 230843)
L2StandardBridge_Test:test_ERC20BridgeFailed_whenLocalTokenIsBridge() (gas: 133097) L2StandardBridge_Test:test_ERC20BridgeFailed_whenLocalTokenIsBridge() (gas: 133097)
L2StandardBridge_Test:test_cannotWithdrawEthWithoutSendingIt() (gas: 21611) L2StandardBridge_Test:test_cannotWithdrawEthWithoutSendingIt() (gas: 21611)
L2StandardBridge_Test:test_finalizeDeposit() (gas: 93100) L2StandardBridge_Test:test_finalizeDeposit() (gas: 93100)
...@@ -113,21 +114,25 @@ OptimismMintableTokenFactory_Test:test_createStandardL2Token() (gas: 1100125) ...@@ -113,21 +114,25 @@ OptimismMintableTokenFactory_Test:test_createStandardL2Token() (gas: 1100125)
OptimismMintableTokenFactory_Test:test_createStandardL2TokenSameTwice() (gas: 2181161) OptimismMintableTokenFactory_Test:test_createStandardL2TokenSameTwice() (gas: 2181161)
OptimismMintableTokenFactory_Test:test_createStandardL2TokenShouldRevertIfRemoteIsZero() (gas: 9374) OptimismMintableTokenFactory_Test:test_createStandardL2TokenShouldRevertIfRemoteIsZero() (gas: 9374)
OptimismMintableTokenFactory_Test:test_initializeShouldRevert() (gas: 12696) OptimismMintableTokenFactory_Test:test_initializeShouldRevert() (gas: 12696)
OptimismPortal_Test:test_OptimismPortalConstructor() (gas: 11435) OptimismPortalUpgradeable_Test:test_cannotInitImpl() (gas: 10958)
OptimismPortal_Test:test_OptimismPortalContractCreationReverts() (gas: 9192) OptimismPortalUpgradeable_Test:test_cannotInitProxy() (gas: 15956)
OptimismPortal_Test:test_OptimismPortalReceiveEth() (gas: 121771) OptimismPortalUpgradeable_Test:test_initValuesOnProxy() (gas: 15967)
OptimismPortal_Test:test_cannotVerifyRecentWithdrawal() (gas: 21886) OptimismPortalUpgradeable_Test:test_upgrading() (gas: 230843)
OptimismPortal_Test:test_depositTransaction_NoValueContract() (gas: 70746) OptimismPortal_Test:test_OptimismPortalConstructor() (gas: 17286)
OptimismPortal_Test:test_depositTransaction_NoValueEOA() (gas: 71092) OptimismPortal_Test:test_OptimismPortalContractCreationReverts() (gas: 14214)
OptimismPortal_Test:test_depositTransaction_createWithZeroValueForContract() (gas: 70751) OptimismPortal_Test:test_OptimismPortalReceiveEth() (gas: 126643)
OptimismPortal_Test:test_depositTransaction_createWithZeroValueForEOA() (gas: 71095) OptimismPortal_Test:test_cannotVerifyRecentWithdrawal() (gas: 31945)
OptimismPortal_Test:test_depositTransaction_withEthValueAndContractContractCreation() (gas: 77795) OptimismPortal_Test:test_depositTransaction_NoValueContract() (gas: 75774)
OptimismPortal_Test:test_depositTransaction_withEthValueAndEOAContractCreation() (gas: 69925) OptimismPortal_Test:test_depositTransaction_NoValueEOA() (gas: 76120)
OptimismPortal_Test:test_depositTransaction_withEthValueFromContract() (gas: 77456) OptimismPortal_Test:test_depositTransaction_createWithZeroValueForContract() (gas: 75779)
OptimismPortal_Test:test_depositTransaction_withEthValueFromEOA() (gas: 78072) OptimismPortal_Test:test_depositTransaction_createWithZeroValueForEOA() (gas: 76123)
OptimismPortal_Test:test_invalidWithdrawalProof() (gas: 33769) OptimismPortal_Test:test_depositTransaction_withEthValueAndContractContractCreation() (gas: 82823)
OptimismPortal_Test:test_isOutputFinalized() (gas: 109379) OptimismPortal_Test:test_depositTransaction_withEthValueAndEOAContractCreation() (gas: 74941)
OptimismPortal_Test:test_simple_isOutputFinalized() (gas: 17076) OptimismPortal_Test:test_depositTransaction_withEthValueFromContract() (gas: 82484)
OptimismPortal_Test:test_depositTransaction_withEthValueFromEOA() (gas: 83100)
OptimismPortal_Test:test_invalidWithdrawalProof() (gas: 45251)
OptimismPortal_Test:test_isOutputFinalized() (gas: 132162)
OptimismPortal_Test:test_simple_isOutputFinalized() (gas: 23965)
Proxy_Test:test_clashingFunctionSignatures() (gas: 101427) Proxy_Test:test_clashingFunctionSignatures() (gas: 101427)
Proxy_Test:test_implementationKey() (gas: 20942) Proxy_Test:test_implementationKey() (gas: 20942)
Proxy_Test:test_implementationProxyCallIfNotAdmin() (gas: 30021) Proxy_Test:test_implementationProxyCallIfNotAdmin() (gas: 30021)
......
...@@ -91,11 +91,19 @@ ...@@ -91,11 +91,19 @@
+----------------------+----------------------------------------+------+--------+-------+ +----------------------+----------------------------------------+------+--------+-------+
| Name | Type | Slot | Offset | Bytes | | Name | Type | Slot | Offset | Bytes |
+=======================================================================================+ +=======================================================================================+
| params | struct ResourceMetering.ResourceParams | 0 | 0 | 32 | | _initialized | uint8 | 0 | 0 | 1 |
|----------------------+----------------------------------------+------+--------+-------| |----------------------+----------------------------------------+------+--------+-------|
| l2Sender | address | 1 | 0 | 20 | | _initializing | bool | 0 | 1 | 1 |
|----------------------+----------------------------------------+------+--------+-------| |----------------------+----------------------------------------+------+--------+-------|
| finalizedWithdrawals | mapping(bytes32 => bool) | 2 | 0 | 32 | | params | struct ResourceMetering.ResourceParams | 1 | 0 | 32 |
|----------------------+----------------------------------------+------+--------+-------|
| __gap | uint256[49] | 2 | 0 | 1568 |
|----------------------+----------------------------------------+------+--------+-------|
| l2Sender | address | 51 | 0 | 20 |
|----------------------+----------------------------------------+------+--------+-------|
| finalizedWithdrawals | mapping(bytes32 => bool) | 52 | 0 | 32 |
|----------------------+----------------------------------------+------+--------+-------|
| __gap | uint256[48] | 53 | 0 | 1536 |
+----------------------+----------------------------------------+------+--------+-------+ +----------------------+----------------------------------------+------+--------+-------+
======================= =======================
......
...@@ -6,6 +6,7 @@ import { WithdrawalVerifier } from "../libraries/Lib_WithdrawalVerifier.sol"; ...@@ -6,6 +6,7 @@ import { WithdrawalVerifier } from "../libraries/Lib_WithdrawalVerifier.sol";
import { AddressAliasHelper } from "../libraries/AddressAliasHelper.sol"; import { AddressAliasHelper } from "../libraries/AddressAliasHelper.sol";
import { ExcessivelySafeCall } from "../libraries/ExcessivelySafeCall.sol"; import { ExcessivelySafeCall } from "../libraries/ExcessivelySafeCall.sol";
import { ResourceMetering } from "./ResourceMetering.sol"; import { ResourceMetering } from "./ResourceMetering.sol";
import { Initializable } from "@openzeppelin/contracts/proxy/utils/Initializable.sol";
/** /**
* @custom:proxied * @custom:proxied
...@@ -14,7 +15,12 @@ import { ResourceMetering } from "./ResourceMetering.sol"; ...@@ -14,7 +15,12 @@ import { ResourceMetering } from "./ResourceMetering.sol";
* and L2. Messages sent directly to the OptimismPortal have no form of replayability. * and L2. Messages sent directly to the OptimismPortal have no form of replayability.
* Users are encouraged to use the L1CrossDomainMessenger for a higher-level interface. * Users are encouraged to use the L1CrossDomainMessenger for a higher-level interface.
*/ */
contract OptimismPortal is ResourceMetering { contract OptimismPortal is Initializable, ResourceMetering {
/**
* @notice Contract version number.
*/
uint8 public constant OPTIMISM_PORTAL_VERSION = 1;
/** /**
* @notice Emitted when a transaction is deposited from L1 to L2. The parameters of this event * @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. * are read by the rollup node and used to derive deposit transactions on L2.
...@@ -65,7 +71,7 @@ contract OptimismPortal is ResourceMetering { ...@@ -65,7 +71,7 @@ contract OptimismPortal is ResourceMetering {
* of this variable is the default L2 sender address, then we are NOT inside of a call * of this variable is the default L2 sender address, then we are NOT inside of a call
* to finalizeWithdrawalTransaction. * to finalizeWithdrawalTransaction.
*/ */
address public l2Sender = DEFAULT_L2_SENDER; address public l2Sender;
/** /**
* @notice The L2 gas limit set when eth is deposited using the receive() function. * @notice The L2 gas limit set when eth is deposited using the receive() function.
...@@ -83,12 +89,29 @@ contract OptimismPortal is ResourceMetering { ...@@ -83,12 +89,29 @@ contract OptimismPortal is ResourceMetering {
mapping(bytes32 => bool) public finalizedWithdrawals; mapping(bytes32 => bool) public finalizedWithdrawals;
/** /**
* @param _l2Oracle Address of the L2OutputOracle. * @notice Reserve extra slots (to to a total of 50) in the storage layout for future upgrades.
* @param _finalizationPeriodSeconds Finalization time in seconds. */
uint256[48] private __gap;
/**
* @notice The constructor sets immutable values in the implementation.
* This means that these values can only changed by an upgrade. But the efficiency gains
* are worthwhile.
* Also ensures that the implementation is initialized upon deployment.
*/ */
constructor(L2OutputOracle _l2Oracle, uint256 _finalizationPeriodSeconds) { constructor(L2OutputOracle _l2Oracle, uint256 _finalizationPeriodSeconds) {
// Set these immutable values into the bytcode of the implementation.
L2_ORACLE = _l2Oracle; L2_ORACLE = _l2Oracle;
FINALIZATION_PERIOD_SECONDS = _finalizationPeriodSeconds; FINALIZATION_PERIOD_SECONDS = _finalizationPeriodSeconds;
initialize();
}
/**
* @notice Initializes the contract and parent contract(s).
*/
function initialize() public reinitializer(OPTIMISM_PORTAL_VERSION) {
l2Sender = DEFAULT_L2_SENDER;
__ResourceMetering_init();
} }
/** /**
......
...@@ -58,9 +58,23 @@ contract ResourceMetering { ...@@ -58,9 +58,23 @@ contract ResourceMetering {
ResourceParams public params; ResourceParams public params;
/** /**
* @notice Sets initial resource parameter values. * @notice Reserve extra slots (to a total of 50) in the storage layout for future upgrades.
*/
uint256[49] private __gap;
/**
* @notice Set the initial values. In order to enable this contract to be used in an upgradable
* context, the constructor calls a separate init function.
*/ */
constructor() { constructor() {
__ResourceMetering_init();
}
/**
* @notice Sets initial resource parameter values. This function must either be called by the
* initializer function of an upgradeable child contract.
*/
function __ResourceMetering_init() internal {
params = ResourceParams({ params = ResourceParams({
prevBaseFee: INITIAL_BASE_FEE, prevBaseFee: INITIAL_BASE_FEE,
prevBoughtGas: 0, prevBoughtGas: 0,
......
...@@ -17,6 +17,8 @@ import { AddressAliasHelper } from "../libraries/AddressAliasHelper.sol"; ...@@ -17,6 +17,8 @@ import { AddressAliasHelper } from "../libraries/AddressAliasHelper.sol";
import { OVM_ETH } from "../L2/OVM_ETH.sol"; import { OVM_ETH } from "../L2/OVM_ETH.sol";
import { Lib_PredeployAddresses } from "../libraries/Lib_PredeployAddresses.sol"; import { Lib_PredeployAddresses } from "../libraries/Lib_PredeployAddresses.sol";
import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import { Proxy } from "../universal/Proxy.sol";
import { Initializable } from "@openzeppelin/contracts/proxy/utils/Initializable.sol";
contract CommonTest is Test { contract CommonTest is Test {
address alice = address(128); address alice = address(128);
...@@ -46,6 +48,7 @@ contract CommonTest is Test { ...@@ -46,6 +48,7 @@ contract CommonTest is Test {
contract L2OutputOracle_Initializer is CommonTest { contract L2OutputOracle_Initializer is CommonTest {
// Test target // Test target
L2OutputOracle oracle; L2OutputOracle oracle;
L2OutputOracle oracleImpl;
// Constructor arguments // Constructor arguments
address sequencer = 0x000000000000000000000000000000000000AbBa; address sequencer = 0x000000000000000000000000000000000000AbBa;
...@@ -69,7 +72,7 @@ contract L2OutputOracle_Initializer is CommonTest { ...@@ -69,7 +72,7 @@ contract L2OutputOracle_Initializer is CommonTest {
vm.warp(initL1Time); vm.warp(initL1Time);
vm.roll(startingBlockNumber); vm.roll(startingBlockNumber);
// Deploy the L2OutputOracle and transfer owernship to the sequencer // Deploy the L2OutputOracle and transfer owernship to the sequencer
oracle = new L2OutputOracle( oracleImpl = new L2OutputOracle(
submissionInterval, submissionInterval,
genesisL2Output, genesisL2Output,
historicalTotalBlocks, historicalTotalBlocks,
...@@ -79,6 +82,39 @@ contract L2OutputOracle_Initializer is CommonTest { ...@@ -79,6 +82,39 @@ contract L2OutputOracle_Initializer is CommonTest {
sequencer, sequencer,
owner owner
); );
Proxy proxy = new Proxy(alice);
vm.prank(alice);
proxy.upgradeToAndCall(
address(oracleImpl),
abi.encodeWithSelector(
L2OutputOracle.initialize.selector,
genesisL2Output,
startingBlockNumber,
sequencer,
owner
)
);
oracle = L2OutputOracle(address(proxy));
}
}
contract Portal_Initializer is L2OutputOracle_Initializer {
// Test target
OptimismPortal opImpl;
OptimismPortal op;
function setUp() public override virtual {
L2OutputOracle_Initializer.setUp();
opImpl = new OptimismPortal(oracle, 7 days);
Proxy proxy = new Proxy(alice);
vm.prank(alice);
proxy.upgradeToAndCall(
address(opImpl),
abi.encodeWithSelector(
OptimismPortal.initialize.selector
)
);
op = OptimismPortal(payable(address(proxy)));
} }
} }
...@@ -125,7 +161,7 @@ contract Messenger_Initializer is L2OutputOracle_Initializer { ...@@ -125,7 +161,7 @@ contract Messenger_Initializer is L2OutputOracle_Initializer {
super.setUp(); super.setUp();
// Deploy the OptimismPortal // Deploy the OptimismPortal
op = new OptimismPortal(oracle, 100); op = new OptimismPortal(oracle, 7 days);
vm.label(address(op), "OptimismPortal"); vm.label(address(op), "OptimismPortal");
L1Messenger = new L1CrossDomainMessenger(); L1Messenger = new L1CrossDomainMessenger();
...@@ -320,3 +356,19 @@ contract Bridge_Initializer is Messenger_Initializer { ...@@ -320,3 +356,19 @@ contract Bridge_Initializer is Messenger_Initializer {
); );
} }
} }
// Used for testing a future upgrade beyond the current implementations.
// We include some variables so that we can sanity check accessing storage values after an upgrade.
contract NextImpl is Initializable {
// Initializable occupies the zero-th slot.
bytes32 slot1;
bytes32[19] __gap;
bytes32 slot21;
bytes32 public constant slot21Init = bytes32(hex"1337");
function initialize() public reinitializer(2) {
// Slot21 is unused by an of our upgradeable contracts.
// This is used to verify that we can access this value after an upgrade.
slot21 = slot21Init;
}
}
...@@ -110,13 +110,7 @@ contract L1CrossDomainMessenger_Test is Messenger_Initializer { ...@@ -110,13 +110,7 @@ contract L1CrossDomainMessenger_Test is Messenger_Initializer {
// SentMessage event // SentMessage event
vm.expectEmit(true, true, true, true); vm.expectEmit(true, true, true, true);
emit SentMessage( emit SentMessage(recipient, alice, hex"ff", L1Messenger.messageNonce(), 100);
recipient,
alice,
hex"ff",
L1Messenger.messageNonce(),
100
);
vm.prank(alice); vm.prank(alice);
L1Messenger.sendMessage(recipient, hex"ff", uint32(100)); L1Messenger.sendMessage(recipient, hex"ff", uint32(100));
...@@ -128,10 +122,7 @@ contract L1CrossDomainMessenger_Test is Messenger_Initializer { ...@@ -128,10 +122,7 @@ contract L1CrossDomainMessenger_Test is Messenger_Initializer {
L1Messenger.sendMessage(recipient, hex"aa", uint32(500_000)); L1Messenger.sendMessage(recipient, hex"aa", uint32(500_000));
L1Messenger.sendMessage(recipient, hex"aa", uint32(500_000)); L1Messenger.sendMessage(recipient, hex"aa", uint32(500_000));
// the nonce increments for each message sent // the nonce increments for each message sent
assertEq( assertEq(nonce + 2, L1Messenger.messageNonce());
nonce + 2,
L1Messenger.messageNonce()
);
} }
function test_L1MessengerXDomainSenderReverts() external { function test_L1MessengerXDomainSenderReverts() external {
...@@ -151,20 +142,13 @@ contract L1CrossDomainMessenger_Test is Messenger_Initializer { ...@@ -151,20 +142,13 @@ contract L1CrossDomainMessenger_Test is Messenger_Initializer {
vm.expectCall(target, hex"1111"); vm.expectCall(target, hex"1111");
// set the value of op.l2Sender() to be the L2 Cross Domain Messenger. // set the value of op.l2Sender() to be the L2 Cross Domain Messenger.
uint256 senderSlotIndex = 1; uint256 senderSlotIndex = 51;
vm.store(address(op), bytes32(senderSlotIndex), bytes32(abi.encode(sender))); vm.store(address(op), bytes32(senderSlotIndex), bytes32(abi.encode(sender)));
vm.prank(address(op)); vm.prank(address(op));
vm.expectEmit(true, true, true, true); vm.expectEmit(true, true, true, true);
bytes32 hash = CrossDomainHashing.getVersionedHash( bytes32 hash = CrossDomainHashing.getVersionedHash(0, sender, target, 0, 0, hex"1111");
0,
sender,
target,
0,
0,
hex"1111"
);
emit RelayedMessage(hash); emit RelayedMessage(hash);
...@@ -206,7 +190,10 @@ contract L1CrossDomainMessenger_Test is Messenger_Initializer { ...@@ -206,7 +190,10 @@ contract L1CrossDomainMessenger_Test is Messenger_Initializer {
L1Messenger.xDomainMessageSender(); L1Messenger.xDomainMessageSender();
address sender = Lib_PredeployAddresses.L2_CROSS_DOMAIN_MESSENGER; address sender = Lib_PredeployAddresses.L2_CROSS_DOMAIN_MESSENGER;
uint256 senderSlotIndex = 1;
uint256 senderSlotIndex = 51;
bytes32 slotValue = vm.load(address(op), bytes32(senderSlotIndex));
vm.store(address(op), bytes32(senderSlotIndex), bytes32(abi.encode(sender))); vm.store(address(op), bytes32(senderSlotIndex), bytes32(abi.encode(sender)));
vm.prank(address(op)); vm.prank(address(op));
L1Messenger.relayMessage(0, address(0), address(0), 0, 0, hex""); L1Messenger.relayMessage(0, address(0), address(0), 0, 0, hex"");
......
//SPDX-License-Identifier: MIT //SPDX-License-Identifier: MIT
pragma solidity 0.8.10; pragma solidity 0.8.10;
import { L2OutputOracle_Initializer } from "./CommonTest.t.sol"; import { L2OutputOracle_Initializer, NextImpl } from "./CommonTest.t.sol";
import { L2OutputOracle } from "../L1/L2OutputOracle.sol"; import { L2OutputOracle } from "../L1/L2OutputOracle.sol";
import { Proxy } from "../universal/Proxy.sol"; import { Proxy } from "../universal/Proxy.sol";
contract L2OutputOracleTest is L2OutputOracle_Initializer { contract L2OutputOracleTest is L2OutputOracle_Initializer {
bytes32 appendedOutput1 = keccak256(abi.encode(1)); bytes32 appendedOutput1 = keccak256(abi.encode(1));
...@@ -336,28 +337,11 @@ contract L2OutputOracleTest is L2OutputOracle_Initializer { ...@@ -336,28 +337,11 @@ contract L2OutputOracleTest is L2OutputOracle_Initializer {
} }
contract L2OutputOracleUpgradeable_Test is L2OutputOracle_Initializer { contract L2OutputOracleUpgradeable_Test is L2OutputOracle_Initializer {
L2OutputOracle oracleImpl;
Proxy internal proxy; Proxy internal proxy;
uint64 initialBlockNum;
function setUp() override public { function setUp() public override {
super.setUp(); super.setUp();
initialBlockNum = uint64(block.number); proxy = Proxy(payable(address(oracle)));
// Rename the deployed oracle instance for the purposes of this test.
oracleImpl = oracle;
proxy = new Proxy(alice);
vm.prank(alice);
proxy.upgradeToAndCall(
address(oracleImpl),
abi.encodeWithSelector(
L2OutputOracle.initialize.selector,
genesisL2Output,
startingBlockNumber,
sequencer,
owner
)
);
} }
function test_initValuesOnProxy() external { function test_initValuesOnProxy() external {
...@@ -367,7 +351,9 @@ contract L2OutputOracleUpgradeable_Test is L2OutputOracle_Initializer { ...@@ -367,7 +351,9 @@ contract L2OutputOracleUpgradeable_Test is L2OutputOracle_Initializer {
assertEq(startingTimestamp, oracleImpl.STARTING_TIMESTAMP()); assertEq(startingTimestamp, oracleImpl.STARTING_TIMESTAMP());
assertEq(l2BlockTime, oracleImpl.L2_BLOCK_TIME()); assertEq(l2BlockTime, oracleImpl.L2_BLOCK_TIME());
L2OutputOracle.OutputProposal memory initOutput = oracleImpl.getL2Output(startingBlockNumber); L2OutputOracle.OutputProposal memory initOutput = oracleImpl.getL2Output(
startingBlockNumber
);
assertEq(genesisL2Output, initOutput.outputRoot); assertEq(genesisL2Output, initOutput.outputRoot);
assertEq(initL1Time, initOutput.timestamp); assertEq(initL1Time, initOutput.timestamp);
...@@ -384,4 +370,23 @@ contract L2OutputOracleUpgradeable_Test is L2OutputOracle_Initializer { ...@@ -384,4 +370,23 @@ contract L2OutputOracleUpgradeable_Test is L2OutputOracle_Initializer {
vm.expectRevert("Initializable: contract is already initialized"); vm.expectRevert("Initializable: contract is already initialized");
address(oracleImpl).call(abi.encodeWithSelector(L2OutputOracle.initialize.selector)); address(oracleImpl).call(abi.encodeWithSelector(L2OutputOracle.initialize.selector));
} }
function test_upgrading() external {
// Check an unused slot before upgrading.
bytes32 slot21Before = vm.load(address(oracle), bytes32(uint256(21)));
assertEq(bytes32(0), slot21Before);
NextImpl nextImpl = new NextImpl();
vm.startPrank(alice);
proxy.upgradeToAndCall(
address(nextImpl),
abi.encodeWithSelector(NextImpl.initialize.selector)
);
assertEq(proxy.implementation(), address(nextImpl));
// Verify that the NextImpl contract initialized its values according as expected
bytes32 slot21After = vm.load(address(oracle), bytes32(uint256(21)));
bytes32 slot21Expected = NextImpl(address(oracle)).slot21Init();
assertEq(slot21Expected, slot21After);
}
} }
//SPDX-License-Identifier: MIT //SPDX-License-Identifier: MIT
pragma solidity 0.8.10; pragma solidity 0.8.10;
import { L2OutputOracle_Initializer } from "./CommonTest.t.sol"; import { Portal_Initializer, CommonTest, NextImpl } from "./CommonTest.t.sol";
import { AddressAliasHelper } from "../libraries/AddressAliasHelper.sol"; import { AddressAliasHelper } from "../libraries/AddressAliasHelper.sol";
import { L2OutputOracle } from "../L1/L2OutputOracle.sol"; import { L2OutputOracle } from "../L1/L2OutputOracle.sol";
import { OptimismPortal } from "../L1/OptimismPortal.sol"; import { OptimismPortal } from "../L1/OptimismPortal.sol";
import { WithdrawalVerifier } from "../libraries/Lib_WithdrawalVerifier.sol"; import { WithdrawalVerifier } from "../libraries/Lib_WithdrawalVerifier.sol";
import { Proxy } from "../universal/Proxy.sol";
contract OptimismPortal_Test is L2OutputOracle_Initializer { contract OptimismPortal_Test is Portal_Initializer {
event TransactionDeposited( event TransactionDeposited(
address indexed from, address indexed from,
address indexed to, address indexed to,
...@@ -19,14 +20,6 @@ contract OptimismPortal_Test is L2OutputOracle_Initializer { ...@@ -19,14 +20,6 @@ contract OptimismPortal_Test is L2OutputOracle_Initializer {
bytes data bytes data
); );
// Test target
OptimismPortal op;
function setUp() public override {
L2OutputOracle_Initializer.setUp();
op = new OptimismPortal(oracle, 7 days);
}
function test_OptimismPortalConstructor() external { function test_OptimismPortalConstructor() external {
assertEq(op.FINALIZATION_PERIOD_SECONDS(), 7 days); assertEq(op.FINALIZATION_PERIOD_SECONDS(), 7 days);
assertEq(address(op.L2_ORACLE()), address(oracle)); assertEq(address(op.L2_ORACLE()), address(oracle));
...@@ -319,3 +312,52 @@ contract OptimismPortal_Test is L2OutputOracle_Initializer { ...@@ -319,3 +312,52 @@ contract OptimismPortal_Test is L2OutputOracle_Initializer {
assertEq(op.isOutputFinalized(checkpoint + 1), false); assertEq(op.isOutputFinalized(checkpoint + 1), false);
} }
} }
contract OptimismPortalUpgradeable_Test is Portal_Initializer {
Proxy internal proxy;
uint64 initialBlockNum;
function setUp() public override {
super.setUp();
initialBlockNum = uint64(block.number);
proxy = Proxy(payable(address(op)));
}
function test_initValuesOnProxy() external {
(uint128 prevBaseFee, uint64 prevBoughtGas, uint64 prevBlockNum) = OptimismPortal(
payable(address(proxy))
).params();
assertEq(prevBaseFee, opImpl.INITIAL_BASE_FEE());
assertEq(prevBoughtGas, 0);
assertEq(prevBlockNum, initialBlockNum);
}
function test_cannotInitProxy() external {
vm.expectRevert("Initializable: contract is already initialized");
address(proxy).call(abi.encodeWithSelector(OptimismPortal.initialize.selector));
}
function test_cannotInitImpl() external {
vm.expectRevert("Initializable: contract is already initialized");
address(opImpl).call(abi.encodeWithSelector(OptimismPortal.initialize.selector));
}
function test_upgrading() external {
// Check an unused slot before upgrading.
bytes32 slot21Before = vm.load(address(op), bytes32(uint256(21)));
assertEq(bytes32(0), slot21Before);
NextImpl nextImpl = new NextImpl();
vm.startPrank(alice);
proxy.upgradeToAndCall(
address(nextImpl),
abi.encodeWithSelector(NextImpl.initialize.selector)
);
assertEq(proxy.implementation(), address(nextImpl));
// Verify that the NextImpl contract initialized its values according as expected
bytes32 slot21After = vm.load(address(op), bytes32(uint256(21)));
bytes32 slot21Expected = NextImpl(address(op)).slot21Init();
assertEq(slot21Expected, slot21After);
}
}
...@@ -3,8 +3,13 @@ pragma solidity 0.8.10; ...@@ -3,8 +3,13 @@ pragma solidity 0.8.10;
import { CommonTest } from "./CommonTest.t.sol"; import { CommonTest } from "./CommonTest.t.sol";
import { ResourceMetering } from "../L1/ResourceMetering.sol"; import { ResourceMetering } from "../L1/ResourceMetering.sol";
import { Proxy } from "../universal/Proxy.sol";
contract MeterUser is ResourceMetering { contract MeterUser is ResourceMetering {
constructor() {
__ResourceMetering_init();
}
function use(uint64 _amount) public metered(_amount) {} function use(uint64 _amount) public metered(_amount) {}
} }
......
...@@ -29,15 +29,18 @@ const deployFn: DeployFunction = async (hre) => { ...@@ -29,15 +29,18 @@ const deployFn: DeployFunction = async (hre) => {
const proxy = await hre.deployments.get('OptimismPortalProxy') const proxy = await hre.deployments.get('OptimismPortalProxy')
const Proxy = await hre.ethers.getContractAt('Proxy', proxy.address) const Proxy = await hre.ethers.getContractAt('Proxy', proxy.address)
const portal = await hre.deployments.get('OptimismPortal')
const tx = await Proxy.upgradeTo(portal.address)
await tx.wait()
const OptimismPortal = await hre.ethers.getContractAt( const OptimismPortal = await hre.ethers.getContractAt(
'OptimismPortal', 'OptimismPortal',
proxy.address proxy.address
) )
const portal = await hre.deployments.get('OptimismPortal')
const tx = await Proxy.upgradeToAndCall(
portal.address,
OptimismPortal.interface.encodeFunctionData('initialize()')
)
await tx.wait()
const l2Oracle = await OptimismPortal.L2_ORACLE() const l2Oracle = await OptimismPortal.L2_ORACLE()
if (l2Oracle !== oracle.address) { if (l2Oracle !== oracle.address) {
throw new Error('L2 Oracle mismatch') throw new Error('L2 Oracle mismatch')
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment