diff --git a/contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol b/contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol index 7038980fd..df034e95d 100644 --- a/contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol +++ b/contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol @@ -142,6 +142,18 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver { _; } + /** + * Modifies a function so that only a given address can call it. + */ + modifier onlyCallableBy( + address _allowed + ) { + if (ovmADDRESS() != _allowed) { + _revertWithFlag(RevertFlag.CALLER_NOT_ALLOWED); + } + _; + } + /************************************ * Transaction Execution Entrypoint * @@ -992,6 +1004,7 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver { || flag == RevertFlag.UNSAFE_BYTECODE || flag == RevertFlag.STATIC_VIOLATION || flag == RevertFlag.CREATOR_NOT_ALLOWED + || flag == RevertFlag.CALLER_NOT_ALLOWED ) { transactionRecord.ovmGasRefund = ovmGasRefund; } @@ -1815,6 +1828,52 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver { messageRecord.nuisanceGasLeft = 0; } + + /********************* + * Upgrade Functions * + *********************/ + + /** + * Sets the code of an ovm contract. + * @param _address Address to update the code of. + * @param _code Bytecode to put into the ovm account. + */ + function ovmSETCODE( + address _address, + bytes memory _code + ) + override + external + onlyCallableBy(resolve("OVM_Upgrader")) + { + _checkAccountLoad(_address); + ovmStateManager.putAccountCode(_address, _code); + } + + + /** + * Sets the storage slot of an OVM contract. + * @param _address OVM account to set storage of. + * @param _key Key to set set. + * @param _value Value to store at the given key. + */ + function ovmSETSTORAGE( + address _address, + bytes32 _key, + bytes32 _value + ) + override + external + onlyCallableBy(resolve("OVM_Upgrader")) + { + _putContractStorage( + _address, + _key, + _value + ); + } + + /***************************** * L2-only Helper Functions * *****************************/ diff --git a/contracts/optimistic-ethereum/OVM/execution/OVM_StateManager.sol b/contracts/optimistic-ethereum/OVM/execution/OVM_StateManager.sol index 2d8a95bc1..fc3f7318a 100644 --- a/contracts/optimistic-ethereum/OVM/execution/OVM_StateManager.sol +++ b/contracts/optimistic-ethereum/OVM/execution/OVM_StateManager.sol @@ -143,6 +143,23 @@ contract OVM_StateManager is iOVM_StateManager { account.codeHash = EMPTY_ACCOUNT_CODE_HASH; } + /** + * Inserts the given bytecode into the given OVM account. + * @param _address Address of the account to overwrite code onto. + * @param _code Bytecode to put at the address. + */ + function putAccountCode( + address _address, + bytes memory _code + ) + override + public + authenticated + { + Lib_OVMCodec.Account storage account = accounts[_address]; + account.codeHash = keccak256(_code); + } + /** * Retrieves an account from the state. * @param _address Address of the account to retrieve. diff --git a/contracts/optimistic-ethereum/OVM/predeploys/OVM_Upgrader.sol b/contracts/optimistic-ethereum/OVM/predeploys/OVM_Upgrader.sol new file mode 100644 index 000000000..8eda6ee3a --- /dev/null +++ b/contracts/optimistic-ethereum/OVM/predeploys/OVM_Upgrader.sol @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: MIT +pragma solidity >0.5.0 <0.8.0; + +/* Library Imports */ +import { Lib_SafeExecutionManagerWrapper } from "../../libraries/wrappers/Lib_SafeExecutionManagerWrapper.sol"; + +contract OVM_Upgrader { + // TODO: implement me +} diff --git a/contracts/optimistic-ethereum/iOVM/execution/iOVM_ExecutionManager.sol b/contracts/optimistic-ethereum/iOVM/execution/iOVM_ExecutionManager.sol index 9be045cfc..c0b582c5a 100644 --- a/contracts/optimistic-ethereum/iOVM/execution/iOVM_ExecutionManager.sol +++ b/contracts/optimistic-ethereum/iOVM/execution/iOVM_ExecutionManager.sol @@ -18,7 +18,8 @@ interface iOVM_ExecutionManager { UNSAFE_BYTECODE, CREATE_COLLISION, STATIC_VIOLATION, - CREATOR_NOT_ALLOWED + CREATOR_NOT_ALLOWED, + CALLER_NOT_ALLOWED } enum GasMetadataKey { @@ -153,4 +154,12 @@ interface iOVM_ExecutionManager { ***************************************/ function getMaxTransactionGasLimit() external view returns (uint _maxTransactionGasLimit); + + + /********************* + * Upgrade Functions * + *********************/ + + function ovmSETCODE(address _address, bytes memory _code) external; + function ovmSETSTORAGE(address _address, bytes32 _key, bytes32 _value) external; } diff --git a/contracts/optimistic-ethereum/iOVM/execution/iOVM_StateManager.sol b/contracts/optimistic-ethereum/iOVM/execution/iOVM_StateManager.sol index 1c4870a58..0fbd669fa 100644 --- a/contracts/optimistic-ethereum/iOVM/execution/iOVM_StateManager.sol +++ b/contracts/optimistic-ethereum/iOVM/execution/iOVM_StateManager.sol @@ -42,6 +42,7 @@ interface iOVM_StateManager { function putAccount(address _address, Lib_OVMCodec.Account memory _account) external; function putEmptyAccount(address _address) external; + function putAccountCode(address _address, bytes memory _code) external; function getAccount(address _address) external view returns (Lib_OVMCodec.Account memory _account); function hasAccount(address _address) external view returns (bool _exists); function hasEmptyAccount(address _address) external view returns (bool _exists); @@ -49,7 +50,7 @@ interface iOVM_StateManager { function getAccountNonce(address _address) external view returns (uint256 _nonce); function getAccountEthAddress(address _address) external view returns (address _ethAddress); function getAccountStorageRoot(address _address) external view returns (bytes32 _storageRoot); - function initPendingAccount(address _address) external; + function initPendingAccount(address _address) external; // todo: deprecate/combine these two with this change? function commitPendingAccount(address _address, address _ethAddress, bytes32 _codeHash) external; function testAndSetAccountLoaded(address _address) external returns (bool _wasAccountAlreadyLoaded); function testAndSetAccountChanged(address _address) external returns (bool _wasAccountAlreadyChanged); diff --git a/contracts/optimistic-ethereum/libraries/wrappers/Lib_SafeExecutionManagerWrapper.sol b/contracts/optimistic-ethereum/libraries/wrappers/Lib_SafeExecutionManagerWrapper.sol index 14dc89bdf..8fa117730 100644 --- a/contracts/optimistic-ethereum/libraries/wrappers/Lib_SafeExecutionManagerWrapper.sol +++ b/contracts/optimistic-ethereum/libraries/wrappers/Lib_SafeExecutionManagerWrapper.sol @@ -322,6 +322,42 @@ library Lib_SafeExecutionManagerWrapper { ); } + /** + * Performs a safe ovmSETCODE call. + */ + function safeSETCODE( + address _address, + bytes memory _code + ) + internal + { + _safeExecutionManagerInteraction( + abi.encodeWithSignature( + "ovmSETCODE(address,bytes)", + _address, + _code + ) + ); + } + + /** + * Performs a safe ovmSETSTORAGE call. + */ + function ovmSETSTORAGE( + address _address, + bytes memory _code + ) + internal + { + _safeExecutionManagerInteraction( + abi.encodeWithSignature( + "ovmSETSTORAGE(address,bytes)", + _address, + _code + ) + ); + } + /********************* * Private Functions * *********************/ diff --git a/package.json b/package.json index be9108895..6438a4141 100644 --- a/package.json +++ b/package.json @@ -50,7 +50,7 @@ }, "devDependencies": { "@eth-optimism/plugins": "^1.0.0-alpha.2", - "@eth-optimism/smock": "^1.0.0-alpha.3", + "@eth-optimism/smock": "1.0.0-alpha.4", "@nomiclabs/hardhat-ethers": "^2.0.1", "@nomiclabs/hardhat-waffle": "^2.0.1", "@typechain/ethers-v5": "1.0.0", diff --git a/src/contract-deployment/config.ts b/src/contract-deployment/config.ts index b12d04960..043ad5e4d 100644 --- a/src/contract-deployment/config.ts +++ b/src/contract-deployment/config.ts @@ -226,6 +226,10 @@ export const makeContractDeployConfig = async ( '0x0000000000000000000000000000000000000000', // will be overridden by geth when state dump is ingested. Storage key: 0x0000000000000000000000000000000000000000000000000000000000000008 ], }, + OVM_Upgrader: { + factory: getContractFactory('OVM_Upgrader'), + params: [], + }, 'OVM_ChainStorageContainer:CTC:batches': { factory: getContractFactory('OVM_ChainStorageContainer'), params: [AddressManager.address, 'OVM_CanonicalTransactionChain'], diff --git a/src/contract-dumps.ts b/src/contract-dumps.ts index 04a10c978..b41201fae 100644 --- a/src/contract-dumps.ts +++ b/src/contract-dumps.ts @@ -152,6 +152,7 @@ export const makeStateDump = async (cfg: RollupDeployConfig): Promise => { 'OVM_ExecutionManager', 'OVM_StateManager', 'OVM_ETH', + 'OVM_Upgrader', 'mockOVM_ECDSAContractAccount', ], deployOverrides: {}, @@ -170,6 +171,7 @@ export const makeStateDump = async (cfg: RollupDeployConfig): Promise => { OVM_ETH: '0x4200000000000000000000000000000000000006', OVM_L2CrossDomainMessenger: '0x4200000000000000000000000000000000000007', Lib_AddressManager: '0x4200000000000000000000000000000000000008', + OVM_Upgrader: '0x4200000000000000000000000000000000000009', ERC1820Registry: '0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24', } diff --git a/test/contracts/OVM/execution/OVM_ExecutionManager/nuisance-gas.spec.ts b/test/contracts/OVM/execution/OVM_ExecutionManager/nuisance-gas.spec.ts index f630eb1d3..7aaf225df 100644 --- a/test/contracts/OVM/execution/OVM_ExecutionManager/nuisance-gas.spec.ts +++ b/test/contracts/OVM/execution/OVM_ExecutionManager/nuisance-gas.spec.ts @@ -188,7 +188,7 @@ const test_nuisanceGas: TestDefinition = { // This is because there is natural gas consumption between the ovmCALL(GAS/2) and ovmCREATE, which allots nuisance gas via _getNuisanceGasLimit. // This means that the ovmCREATE exception, DOES consumes all nuisance gas allotted, but that allotment // is less than the full OVM_TX_GAS_LIMIT / 2 which is alloted to the parent ovmCALL. - nuisanceGasLeft: 4531286, + nuisanceGasLeft: 4200163, }, }, }, diff --git a/test/contracts/OVM/execution/OVM_ExecutionManager/ovmCREATE.spec.ts b/test/contracts/OVM/execution/OVM_ExecutionManager/ovmCREATE.spec.ts index 147fac7be..42f723a4e 100644 --- a/test/contracts/OVM/execution/OVM_ExecutionManager/ovmCREATE.spec.ts +++ b/test/contracts/OVM/execution/OVM_ExecutionManager/ovmCREATE.spec.ts @@ -744,6 +744,24 @@ const test_ovmCREATE: TestDefinition = { }, ], }, + { + name: 'ovmCREATE(UNSAFE_CODE)', + steps: [ + { + functionName: 'ovmCREATE', + functionParams: { + bytecode: UNSAFE_BYTECODE, + }, + expectedReturnStatus: true, + expectedReturnValue: { + address: ethers.constants.AddressZero, + revertData: encodeSolidityError( + 'Constructor attempted to deploy unsafe bytecode.' + ), + }, + }, + ], + }, ], subTests: [ { diff --git a/test/contracts/OVM/execution/OVM_ExecutionManager/ovmSETCODE.spec.ts b/test/contracts/OVM/execution/OVM_ExecutionManager/ovmSETCODE.spec.ts new file mode 100644 index 000000000..4ea33ce91 --- /dev/null +++ b/test/contracts/OVM/execution/OVM_ExecutionManager/ovmSETCODE.spec.ts @@ -0,0 +1,188 @@ +/* External Imports */ +import { ethers } from 'ethers' +// import { merge } from 'lodash' + +/* Internal Imports */ +import { + ExecutionManagerTestRunner, + TestDefinition, + OVM_TX_GAS_LIMIT, + NON_NULL_BYTES32, + getStorageXOR, + REVERT_FLAGS, +} from '../../../../helpers' + +const UPGRADER_ADDRESS = '0x4200000000000000000000000000000000000009' +const UPGRADED_ADDRESS = '0x1234123412341234123412341234123412341234' +const UPGRADED_CODE = '0x1234' +const UPGRADED_CODEHASH = ethers.utils.keccak256(UPGRADED_CODE) + +const sharedPreState = { + ExecutionManager: { + ovmStateManager: '$OVM_STATE_MANAGER', + ovmSafetyChecker: '$OVM_SAFETY_CHECKER', + messageRecord: { + nuisanceGasLeft: OVM_TX_GAS_LIMIT, + }, + }, + StateManager: { + owner: '$OVM_EXECUTION_MANAGER', + accounts: { + $DUMMY_OVM_ADDRESS_1: { + codeHash: NON_NULL_BYTES32, + ethAddress: '$OVM_CALL_HELPER', + }, + [UPGRADER_ADDRESS]: { + codeHash: NON_NULL_BYTES32, + ethAddress: '$OVM_CALL_HELPER', + }, + }, + contractStorage: { + [UPGRADED_ADDRESS]: { + [NON_NULL_BYTES32]: getStorageXOR(ethers.constants.HashZero), + }, + }, + }, +} + +const verifiedUpgradePreState = { + StateManager: { + accounts: { + [UPGRADED_ADDRESS]: { + codeHash: NON_NULL_BYTES32, + ethAddress: '$OVM_CALL_HELPER', + }, + }, + // verifiedContractStorage: { + // [UPGRADED_ADDRESS]: { + // [NON_NULL_BYTES32]: true, + // }, + // }, + }, +} + +const test_ovmSETCODEFunctionality: TestDefinition = { + name: 'Functionality tests for ovmSETCODE', + preState: sharedPreState, + subTests: [ + { + name: 'ovmSETCODE -- success case', + preState: verifiedUpgradePreState, + postState: { + StateManager: { + accounts: { + [UPGRADED_ADDRESS]: { + codeHash: UPGRADED_CODEHASH, + ethAddress: '$OVM_CALL_HELPER', + }, + }, + }, + }, + parameters: [ + { + name: 'success case', + steps: [ + { + functionName: 'ovmCALL', + functionParams: { + gasLimit: OVM_TX_GAS_LIMIT, + target: UPGRADER_ADDRESS, + subSteps: [ + { + functionName: 'ovmSETCODE', + functionParams: { + address: UPGRADED_ADDRESS, + code: UPGRADED_CODE, + }, + expectedReturnStatus: true, + }, + ], + }, + expectedReturnStatus: true, + }, + ], + }, + ], + }, + { + name: 'ovmSETCODE -- unauthorized case', + preState: verifiedUpgradePreState, + parameters: [ + { + name: 'unauthorized case', + steps: [ + { + functionName: 'ovmCALL', + functionParams: { + gasLimit: OVM_TX_GAS_LIMIT, + target: '$DUMMY_OVM_ADDRESS_1', + subSteps: [ + { + functionName: 'ovmSETCODE', + functionParams: { + address: UPGRADED_ADDRESS, + code: UPGRADED_CODE, + }, + expectedReturnStatus: false, + expectedReturnValue: { + flag: REVERT_FLAGS.CALLER_NOT_ALLOWED, + onlyValidateFlag: true, + }, + }, + ], + }, + expectedReturnStatus: false, + }, + ], + }, + ], + }, + ], +} + +const test_ovmSETCODEAccess: TestDefinition = { + name: 'State access compliance tests for ovmSETCODE', + preState: sharedPreState, + subTests: [ + { + name: 'ovmSETSTORAGE (UNVERIFIED_ACCOUNT)', + parameters: [ + { + name: 'ovmSETSTORAGE with a missing account', + expectInvalidStateAccess: true, + steps: [ + { + functionName: 'ovmCALL', + functionParams: { + gasLimit: OVM_TX_GAS_LIMIT, + target: UPGRADER_ADDRESS, + subSteps: [ + { + functionName: 'ovmSETCODE', + functionParams: { + address: UPGRADED_ADDRESS, + code: UPGRADED_CODE, + }, + expectedReturnStatus: false, + expectedReturnValue: { + flag: REVERT_FLAGS.INVALID_STATE_ACCESS, + onlyValidateFlag: true, + }, + }, + ], + }, + expectedReturnStatus: false, + expectedReturnValue: { + flag: REVERT_FLAGS.INVALID_STATE_ACCESS, + onlyValidateFlag: true, + }, + }, + ], + }, + ], + }, + ], +} +const runner = new ExecutionManagerTestRunner() +runner.run(test_ovmSETCODEFunctionality) +runner.run(test_ovmSETCODEAccess) diff --git a/test/contracts/OVM/execution/OVM_ExecutionManager/ovmSETSTORAGE.spec.ts b/test/contracts/OVM/execution/OVM_ExecutionManager/ovmSETSTORAGE.spec.ts new file mode 100644 index 000000000..c54efb41f --- /dev/null +++ b/test/contracts/OVM/execution/OVM_ExecutionManager/ovmSETSTORAGE.spec.ts @@ -0,0 +1,236 @@ +/* External Imports */ +import { ethers } from 'ethers' +// import { merge } from 'lodash' + +/* Internal Imports */ +import { + ExecutionManagerTestRunner, + TestDefinition, + OVM_TX_GAS_LIMIT, + NON_NULL_BYTES32, + getStorageXOR, + REVERT_FLAGS, +} from '../../../../helpers' + +const UPGRADER_ADDRESS = '0x4200000000000000000000000000000000000009' +const UPGRADED_ADDRESS = '0x1234123412341234123412341234123412341234' + +const sharedPreState = { + ExecutionManager: { + ovmStateManager: '$OVM_STATE_MANAGER', + ovmSafetyChecker: '$OVM_SAFETY_CHECKER', + messageRecord: { + nuisanceGasLeft: OVM_TX_GAS_LIMIT, + }, + }, + StateManager: { + owner: '$OVM_EXECUTION_MANAGER', + accounts: { + $DUMMY_OVM_ADDRESS_1: { + codeHash: NON_NULL_BYTES32, + ethAddress: '$OVM_CALL_HELPER', + }, + [UPGRADER_ADDRESS]: { + codeHash: NON_NULL_BYTES32, + ethAddress: '$OVM_CALL_HELPER', + }, + }, + contractStorage: { + [UPGRADED_ADDRESS]: { + [NON_NULL_BYTES32]: getStorageXOR(ethers.constants.HashZero), + }, + }, + }, +} + +const verifiedUpgradePreState = { + StateManager: { + accounts: { + [UPGRADED_ADDRESS]: { + codeHash: NON_NULL_BYTES32, + ethAddress: '$OVM_CALL_HELPER', + }, + }, + verifiedContractStorage: { + [UPGRADED_ADDRESS]: { + [NON_NULL_BYTES32]: true, + }, + }, + }, +} + +const test_ovmSETSTORAGEFunctionality: TestDefinition = { + name: 'Functionality tests for ovmSETSTORAGE', + preState: sharedPreState, + subTests: [ + { + name: 'ovmSETSTORAGE -- success case', + preState: verifiedUpgradePreState, + postState: { + StateManager: { + contractStorage: { + [UPGRADED_ADDRESS]: { + [NON_NULL_BYTES32]: getStorageXOR(NON_NULL_BYTES32), + }, + }, + }, + }, + parameters: [ + { + name: 'success case', + steps: [ + { + functionName: 'ovmCALL', + functionParams: { + gasLimit: OVM_TX_GAS_LIMIT, + target: UPGRADER_ADDRESS, + subSteps: [ + { + functionName: 'ovmSETSTORAGE', + functionParams: { + address: UPGRADED_ADDRESS, + key: NON_NULL_BYTES32, + value: NON_NULL_BYTES32, + }, + expectedReturnStatus: true, + }, + ], + }, + expectedReturnStatus: true, + }, + ], + }, + ], + }, + { + name: 'ovmSETSTORAGE -- unauthorized case', + preState: verifiedUpgradePreState, + parameters: [ + { + name: 'unauthorized case', + steps: [ + { + functionName: 'ovmCALL', + functionParams: { + gasLimit: OVM_TX_GAS_LIMIT, + target: '$DUMMY_OVM_ADDRESS_1', + subSteps: [ + { + functionName: 'ovmSETSTORAGE', + functionParams: { + address: UPGRADED_ADDRESS, + key: NON_NULL_BYTES32, + value: NON_NULL_BYTES32, + }, + expectedReturnStatus: false, + expectedReturnValue: { + flag: REVERT_FLAGS.CALLER_NOT_ALLOWED, + onlyValidateFlag: true, + }, + }, + ], + }, + expectedReturnStatus: false, + }, + ], + }, + ], + }, + ], +} + +const test_ovmSETSTORAGEAccess: TestDefinition = { + name: 'State access compliance tests for ovmSETSTORAGE', + preState: sharedPreState, + subTests: [ + { + name: 'ovmSETSTORAGE (UNVERIFIED_ACCOUNT)', + parameters: [ + { + name: 'ovmSETSTORAGE with a missing account', + expectInvalidStateAccess: true, + steps: [ + { + functionName: 'ovmCALL', + functionParams: { + gasLimit: OVM_TX_GAS_LIMIT, + target: UPGRADER_ADDRESS, + subSteps: [ + { + functionName: 'ovmSETSTORAGE', + functionParams: { + address: UPGRADED_ADDRESS, + key: NON_NULL_BYTES32, + value: NON_NULL_BYTES32, + }, + expectedReturnStatus: false, + expectedReturnValue: { + flag: REVERT_FLAGS.INVALID_STATE_ACCESS, + onlyValidateFlag: true, + }, + }, + ], + }, + expectedReturnStatus: false, + expectedReturnValue: { + flag: REVERT_FLAGS.INVALID_STATE_ACCESS, + onlyValidateFlag: true, + }, + }, + ], + }, + ], + }, + { + name: 'ovmSETSTORAGE (UNVERIFIED_SLOT)', + preState: { + StateManager: { + accounts: { + [UPGRADED_ADDRESS]: { + codeHash: NON_NULL_BYTES32, + ethAddress: '$OVM_CALL_HELPER', + }, + }, + }, + }, + parameters: [ + { + name: 'ovmSETSTORAGE with a missing storage slot', + expectInvalidStateAccess: true, + steps: [ + { + functionName: 'ovmCALL', + functionParams: { + gasLimit: OVM_TX_GAS_LIMIT, + target: UPGRADER_ADDRESS, + subSteps: [ + { + functionName: 'ovmSETSTORAGE', + functionParams: { + address: UPGRADED_ADDRESS, + key: NON_NULL_BYTES32, + value: NON_NULL_BYTES32, + }, + expectedReturnStatus: false, + expectedReturnValue: { + flag: REVERT_FLAGS.INVALID_STATE_ACCESS, + onlyValidateFlag: true, + }, + }, + ], + }, + expectedReturnStatus: false, + expectedReturnValue: { + flag: REVERT_FLAGS.INVALID_STATE_ACCESS, + onlyValidateFlag: true, + }, + }, + ], + }, + ], + }, + ], +} +const runner = new ExecutionManagerTestRunner() +runner.run(test_ovmSETSTORAGEFunctionality) +runner.run(test_ovmSETSTORAGEAccess) diff --git a/test/helpers/codec/revert-flags.ts b/test/helpers/codec/revert-flags.ts index c0326b25d..0486b5f95 100644 --- a/test/helpers/codec/revert-flags.ts +++ b/test/helpers/codec/revert-flags.ts @@ -42,4 +42,5 @@ export const REVERT_FLAGS = { CREATE_COLLISION: 5, STATIC_VIOLATION: 6, CREATOR_NOT_ALLOWED: 7, + CALLER_NOT_ALLOWED: 8, } diff --git a/test/helpers/constants.ts b/test/helpers/constants.ts index 9c873399d..ad995056b 100644 --- a/test/helpers/constants.ts +++ b/test/helpers/constants.ts @@ -4,6 +4,9 @@ import { defaultAccounts } from 'ethereum-waffle' import { fromHexString, toHexString } from '@eth-optimism/core-utils' import xor from 'buffer-xor' +/* Internal Imports */ +import { getContractDefinition } from '../../src' + export const DEFAULT_ACCOUNTS = defaultAccounts export const DEFAULT_ACCOUNTS_HARDHAT = defaultAccounts.map((account) => { return { @@ -35,8 +38,16 @@ export const NUISANCE_GAS_COSTS = { MIN_GAS_FOR_INVALID_STATE_ACCESS: 30000, } -// TODO: get this exported/imported somehow in a way that we can do math on it. unfortunately using require('.....artifacts/contract.json') is erroring... -export const Helper_TestRunner_BYTELEN = 3654 +let len +// This is hacky, but `hardhat compile` evaluates this file for some reason. +// Feels better to have something hacky then a constant we have to keep re-hardcoding. +try { + len = fromHexString( + getContractDefinition('Helper_TestRunner').deployedBytecode + ).byteLength +} catch {} + +export const Helper_TestRunner_BYTELEN = len export const STORAGE_XOR = '0xfeedfacecafebeeffeedfacecafebeeffeedfacecafebeeffeedfacecafebeef' diff --git a/test/helpers/test-runner/test-runner.ts b/test/helpers/test-runner/test-runner.ts index ed93796fd..46b602e98 100644 --- a/test/helpers/test-runner/test-runner.ts +++ b/test/helpers/test-runner/test-runner.ts @@ -28,6 +28,8 @@ import { isTestStep_EXTCODEHASH, isTestStep_EXTCODECOPY, isTestStep_REVERT, + isTestStep_SETCODE, + isTestStep_SETSTORAGE, } from './test.types' import { encodeRevertData, REVERT_FLAGS } from '../codec' import { @@ -404,7 +406,9 @@ export class ExecutionManagerTestRunner { isTestStep_EXTCODESIZE(step) || isTestStep_EXTCODEHASH(step) || isTestStep_EXTCODECOPY(step) || - isTestStep_CREATEEOA(step) + isTestStep_CREATEEOA(step) || + isTestStep_SETCODE(step) || + isTestStep_SETSTORAGE(step) ) { functionParams = Object.values(step.functionParams) } else if (isTestStep_CALL(step)) { diff --git a/test/helpers/test-runner/test.types.ts b/test/helpers/test-runner/test.types.ts index fa799dd6b..f6e4c70ab 100644 --- a/test/helpers/test-runner/test.types.ts +++ b/test/helpers/test-runner/test.types.ts @@ -169,6 +169,27 @@ export interface TestStep_Run { expectedRevertValue?: string } +export interface TestStep_SETCODE { + functionName: 'ovmSETCODE' + functionParams: { + address: string + code: string + } + expectedReturnStatus: boolean + expectedReturnValue?: RevertFlagError +} + +export interface TestStep_SETSTORAGE { + functionName: 'ovmSETSTORAGE' + functionParams: { + address: string + key: string + value: string + } + expectedReturnStatus: boolean + expectedReturnValue?: RevertFlagError +} + export type TestStep = | TestStep_Context | TestStep_SSTORE @@ -183,6 +204,8 @@ export type TestStep = | TestStep_EXTCODECOPY | TestStep_REVERT | TestStep_evm + | TestStep_SETCODE + | TestStep_SETSTORAGE export interface ParsedTestStep { functionName: string @@ -287,6 +310,18 @@ export const isTestStep_Run = ( return step.functionName === 'run' } +export const isTestStep_SETCODE = ( + step: TestStep | TestStep_Run +): step is TestStep_SETCODE => { + return step.functionName === 'ovmSETCODE' +} + +export const isTestStep_SETSTORAGE = ( + step: TestStep | TestStep_Run +): step is TestStep_SETSTORAGE => { + return step.functionName === 'ovmSETSTORAGE' +} + interface TestState { ExecutionManager: any StateManager: any diff --git a/yarn.lock b/yarn.lock index 437f273c9..845acf9ef 100644 --- a/yarn.lock +++ b/yarn.lock @@ -78,10 +78,10 @@ dependencies: node-fetch "^2.6.1" -"@eth-optimism/smock@^1.0.0-alpha.3": - version "1.0.0-alpha.3" - resolved "https://registry.yarnpkg.com/@eth-optimism/smock/-/smock-1.0.0-alpha.3.tgz#5f3e8f137407c4c62f06aed60bac3dc282632f89" - integrity sha512-TKqbmElCWQ0qM6qj8JajqOijZVKl47L/5v2NnEWBJERKZ6zkuFxT0Y8HtUCM3r4ZEURuXFbRxRLP/ZTrOG6axg== +"@eth-optimism/smock@1.0.0-alpha.4": + version "1.0.0-alpha.4" + resolved "https://registry.yarnpkg.com/@eth-optimism/smock/-/smock-1.0.0-alpha.4.tgz#d19ac8d76ba797ee420a6f53b14b83d5a33b52f8" + integrity sha512-7lSiidNhQ1gy7LRo6nfoCIXbGpQwVpNRW/LKZqc/yc2GzTJX70rPkcnE2sgIV+rLVctHnokU7VuSfMDq+BYH9w== dependencies: "@eth-optimism/core-utils" "^0.1.10" "@ethersproject/abi" "^5.0.13"