Commit 0c62c8e7 authored by Matthew Slipper's avatar Matthew Slipper Committed by GitHub

Merge pull request #8240 from ethereum-optimism/cl/ctb/deployment-checks

feat(ctb): Further modularization of post-deployment checks
parents fbe7bad4 f1b1f4b3
...@@ -8,4 +8,4 @@ GasBenchMark_L1StandardBridge_Finalize:test_finalizeETHWithdrawal_benchmark() (g ...@@ -8,4 +8,4 @@ GasBenchMark_L1StandardBridge_Finalize:test_finalizeETHWithdrawal_benchmark() (g
GasBenchMark_L2OutputOracle:test_proposeL2Output_benchmark() (gas: 86653) GasBenchMark_L2OutputOracle:test_proposeL2Output_benchmark() (gas: 86653)
GasBenchMark_OptimismPortal:test_depositTransaction_benchmark() (gas: 68485) GasBenchMark_OptimismPortal:test_depositTransaction_benchmark() (gas: 68485)
GasBenchMark_OptimismPortal:test_depositTransaction_benchmark_1() (gas: 68988) GasBenchMark_OptimismPortal:test_depositTransaction_benchmark_1() (gas: 68988)
GasBenchMark_OptimismPortal:test_proveWithdrawalTransaction_benchmark() (gas: 143255) GasBenchMark_OptimismPortal:test_proveWithdrawalTransaction_benchmark() (gas: 143259)
\ No newline at end of file \ No newline at end of file
...@@ -30,6 +30,17 @@ struct Artifact { ...@@ -30,6 +30,17 @@ struct Artifact {
string userdoc; string userdoc;
} }
/// @notice Contains information about a storage slot. Mirrors the layout of the storage
/// slot object in Forge artifacts so that we can deserialize JSON into this struct.
struct StorageSlot {
uint256 astId;
string _contract;
string label;
uint256 offset;
string slot;
string _type;
}
/// @title Deployer /// @title Deployer
/// @author tynes /// @author tynes
/// @notice A contract that can make deploying and interacting with deployments easy. /// @notice A contract that can make deploying and interacting with deployments easy.
...@@ -427,6 +438,36 @@ abstract contract Deployer is Script { ...@@ -427,6 +438,36 @@ abstract contract Deployer is Script {
return string(res); return string(res);
} }
/// @dev Pulls the `_initialized` storage slot information from the Forge artifacts for a given contract.
function getInitializedSlot(string memory _contractName) internal returns (StorageSlot memory slot_) {
string memory storageLayout = getStorageLayout(_contractName);
string[] memory command = new string[](3);
command[0] = Executables.bash;
command[1] = "-c";
command[2] = string.concat(
Executables.echo,
" '",
storageLayout,
"'",
" | ",
Executables.jq,
" '.storage[] | select(.label == \"_initialized\" and .type == \"t_uint8\")'"
);
bytes memory rawSlot = vm.parseJson(string(vm.ffi(command)));
slot_ = abi.decode(rawSlot, (StorageSlot));
}
/// @dev Returns the value of the internal `_initialized` storage slot for a given contract.
function loadInitializedSlot(string memory _contractName, bool _isProxy) internal returns (uint8 initialized_) {
StorageSlot memory slot = getInitializedSlot(_contractName);
if (_isProxy) {
_contractName = string.concat(_contractName, "Proxy");
}
bytes32 slotVal = vm.load(mustGetAddress(_contractName), bytes32(vm.parseUint(slot.slot)));
initialized_ = uint8((uint256(slotVal) >> (slot.offset * 8)) & 0xFF);
}
/// @notice Adds a deployment to the temp deployments file /// @notice Adds a deployment to the temp deployments file
function _writeTemp(string memory _name, address _deployed) internal { function _writeTemp(string memory _name, address _deployed) internal {
vm.writeJson({ json: stdJson.serialize("", _name, _deployed), path: tempDeploymentsPath }); vm.writeJson({ json: stdJson.serialize("", _name, _deployed), path: tempDeploymentsPath });
......
...@@ -9,6 +9,7 @@ import { SystemConfig } from "src/L1/SystemConfig.sol"; ...@@ -9,6 +9,7 @@ import { SystemConfig } from "src/L1/SystemConfig.sol";
import { ResourceMetering } from "src/L1/ResourceMetering.sol"; import { ResourceMetering } from "src/L1/ResourceMetering.sol";
import { OptimismPortal } from "src/L1/OptimismPortal.sol"; import { OptimismPortal } from "src/L1/OptimismPortal.sol";
import "src/L1/ProtocolVersions.sol"; import "src/L1/ProtocolVersions.sol";
import "scripts/Deployer.sol";
/// @title Initializer_Test /// @title Initializer_Test
/// @dev Ensures that the `initialize()` function on contracts cannot be called more than /// @dev Ensures that the `initialize()` function on contracts cannot be called more than
...@@ -20,18 +21,7 @@ contract Initializer_Test is Bridge_Initializer { ...@@ -20,18 +21,7 @@ contract Initializer_Test is Bridge_Initializer {
struct InitializeableContract { struct InitializeableContract {
address target; address target;
bytes initCalldata; bytes initCalldata;
StorageSlot initializedSlot; uint8 initializedSlotVal;
}
/// @notice Contains information about a storage slot. Mirrors the layout of the storage
/// slot object in Forge artifacts so that we can deserialize JSON into this struct.
struct StorageSlot {
uint256 astId;
string _contract;
string label;
uint256 offset;
string slot;
string _type;
} }
/// @notice Contains the addresses of the contracts to test as well as the calldata /// @notice Contains the addresses of the contracts to test as well as the calldata
...@@ -50,7 +40,7 @@ contract Initializer_Test is Bridge_Initializer { ...@@ -50,7 +40,7 @@ contract Initializer_Test is Bridge_Initializer {
InitializeableContract({ InitializeableContract({
target: address(l1CrossDomainMessenger), target: address(l1CrossDomainMessenger),
initCalldata: abi.encodeCall(l1CrossDomainMessenger.initialize, ()), initCalldata: abi.encodeCall(l1CrossDomainMessenger.initialize, ()),
initializedSlot: _getInitializedSlot("L1CrossDomainMessenger") initializedSlotVal: loadInitializedSlot("L1CrossDomainMessenger", true)
}) })
); );
// L2OutputOracle // L2OutputOracle
...@@ -58,7 +48,7 @@ contract Initializer_Test is Bridge_Initializer { ...@@ -58,7 +48,7 @@ contract Initializer_Test is Bridge_Initializer {
InitializeableContract({ InitializeableContract({
target: address(l2OutputOracle), target: address(l2OutputOracle),
initCalldata: abi.encodeCall(l2OutputOracle.initialize, (0, 0)), initCalldata: abi.encodeCall(l2OutputOracle.initialize, (0, 0)),
initializedSlot: _getInitializedSlot("L2OutputOracle") initializedSlotVal: loadInitializedSlot("L2OutputOracle", true)
}) })
); );
// OptimismPortal // OptimismPortal
...@@ -66,7 +56,7 @@ contract Initializer_Test is Bridge_Initializer { ...@@ -66,7 +56,7 @@ contract Initializer_Test is Bridge_Initializer {
InitializeableContract({ InitializeableContract({
target: address(optimismPortal), target: address(optimismPortal),
initCalldata: abi.encodeCall(optimismPortal.initialize, (false)), initCalldata: abi.encodeCall(optimismPortal.initialize, (false)),
initializedSlot: _getInitializedSlot("OptimismPortal") initializedSlotVal: loadInitializedSlot("OptimismPortal", true)
}) })
); );
// SystemConfig // SystemConfig
...@@ -92,7 +82,7 @@ contract Initializer_Test is Bridge_Initializer { ...@@ -92,7 +82,7 @@ contract Initializer_Test is Bridge_Initializer {
}) })
) )
), ),
initializedSlot: _getInitializedSlot("SystemConfig") initializedSlotVal: loadInitializedSlot("SystemConfig", true)
}) })
); );
// ProtocolVersions // ProtocolVersions
...@@ -102,7 +92,7 @@ contract Initializer_Test is Bridge_Initializer { ...@@ -102,7 +92,7 @@ contract Initializer_Test is Bridge_Initializer {
initCalldata: abi.encodeCall( initCalldata: abi.encodeCall(
protocolVersions.initialize, (address(0), ProtocolVersion.wrap(1), ProtocolVersion.wrap(2)) protocolVersions.initialize, (address(0), ProtocolVersion.wrap(1), ProtocolVersion.wrap(2))
), ),
initializedSlot: _getInitializedSlot("ProtocolVersions") initializedSlotVal: loadInitializedSlot("ProtocolVersions", true)
}) })
); );
} }
...@@ -120,15 +110,8 @@ contract Initializer_Test is Bridge_Initializer { ...@@ -120,15 +110,8 @@ contract Initializer_Test is Bridge_Initializer {
for (uint256 i; i < contracts.length; i++) { for (uint256 i; i < contracts.length; i++) {
InitializeableContract memory _contract = contracts[i]; InitializeableContract memory _contract = contracts[i];
// Load the `_initialized` slot from the storage of the target contract. // Assert that the contract is already initialized.
uint256 initSlotOffset = _contract.initializedSlot.offset; assertEq(_contract.initializedSlotVal, 1);
bytes32 initSlotVal = vm.load(_contract.target, bytes32(vm.parseUint(_contract.initializedSlot.slot)));
// Pull out the 8-bit `_initialized` flag from the storage slot. The offset in forge artifacts is
// relative to the least-significant bit and signifies the *byte offset*, so we need to shift the
// value to the right by the offset * 8 and then mask out the low-order byte to retrieve the flag.
uint8 init = uint8((uint256(initSlotVal) >> (initSlotOffset * 8)) & 0xFF);
assertEq(init, 1);
// Then, attempt to re-initialize the contract. This should fail. // Then, attempt to re-initialize the contract. This should fail.
(bool success, bytes memory returnData) = _contract.target.call(_contract.initCalldata); (bool success, bytes memory returnData) = _contract.target.call(_contract.initCalldata);
...@@ -137,26 +120,6 @@ contract Initializer_Test is Bridge_Initializer { ...@@ -137,26 +120,6 @@ contract Initializer_Test is Bridge_Initializer {
} }
} }
/// @dev Pulls the `_initialized` storage slot information from the Forge artifacts for a given contract.
function _getInitializedSlot(string memory _contractName) internal returns (StorageSlot memory slot_) {
string memory storageLayout = getStorageLayout(_contractName);
string[] memory command = new string[](3);
command[0] = Executables.bash;
command[1] = "-c";
command[2] = string.concat(
Executables.echo,
" '",
storageLayout,
"'",
" | ",
Executables.jq,
" '.storage[] | select(.label == \"_initialized\" and .type == \"t_uint8\")'"
);
bytes memory rawSlot = vm.parseJson(string(vm.ffi(command)));
slot_ = abi.decode(rawSlot, (StorageSlot));
}
/// @dev Returns the number of contracts that are `Initializable` in `src/L1`. /// @dev Returns the number of contracts that are `Initializable` in `src/L1`.
function _getNumL1Initializable() internal returns (uint256 numContracts_) { function _getNumL1Initializable() internal returns (uint256 numContracts_) {
string[] memory command = new string[](3); string[] memory command = new string[](3);
......
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