From b6289b213cf826e14e9fc929f56e6486c4675de6 Mon Sep 17 00:00:00 2001 From: Ben Jones Date: Mon, 8 Mar 2021 14:11:57 -0800 Subject: [PATCH 01/32] new create codepath working --- .../OVM/execution/OVM_ExecutionManager.sol | 245 +++++++++++++++--- .../iOVM/execution/iOVM_ExecutionManager.sol | 1 - contracts/test-helpers/Helper_TestRunner.sol | 6 +- .../OVM_ExecutionManager/ovmCREATE.spec.ts | 56 ++-- test/helpers/codec/revert-flags.ts | 3 +- 5 files changed, 254 insertions(+), 57 deletions(-) diff --git a/contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol b/contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol index 4239fe467..ff24f79ab 100644 --- a/contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol +++ b/contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol @@ -826,9 +826,9 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver { // if the user intentionally runs out of gas. However, we might still have a bit of gas // left over since contract calls can only be passed 63/64ths of total gas, so we need to // explicitly handle this case here. - if (ethAddress == address(0)) { - _revertWithFlag(RevertFlag.CREATE_EXCEPTION); - } + // if (ethAddress == address(0)) { + // _revertWithFlag(RevertFlag.CREATE_EXCEPTION); + // } // Here we pull out the revert flag that would've been set during creation code. Now that // we're out of creation code again, we can just revert normally while passing the flag @@ -926,21 +926,15 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver { // Run `safeCREATE` in a new EVM message so that our changes can be reflected even if // `safeCREATE` reverts. - (bool _success, ) = _handleExternalInteraction( + (bool _success, ) = _handleExternalMessage( nextMessageContext, gasleft(), - address(this), - abi.encodeWithSignature( - "safeCREATE(address,bytes)", - _contractAddress, - _bytecode - ) + _contractAddress, + 0, + _bytecode, + true ); - // Need to make sure that this flag is reset so that it isn't propagated to creations in - // some parent EVM message. - messageRecord.revertFlag = RevertFlag.DID_NOT_REVERT; - // Yellow paper requires that address returned is zero if the contract deployment fails. return _success ? _contractAddress : address(0); } @@ -989,6 +983,198 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver { ); } + function _doExternalCall( + uint _gasLimit, + uint _value, + address _target, + bytes memory _calldata + ) + internal + returns( + bool, + bytes memory + ) + { + return _target.call{gas: _gasLimit}(_calldata); + } + +// TODO: update assumption to be that create collisions are checked in _createContract + function _doExternalCreate( + uint _value, + uint _gasLimit, + bytes memory _creationCode, + address _address + ) + internal + returns( + bool, + bytes memory + ) + { + + if (_hasEmptyAccount(_address) == false) { + return ( + false, + _encodeRevertData( + RevertFlag.CREATE_COLLISION, + "A contract has already been deployed to this address" + ) + ); + } + + // Check the creation bytecode against the OVM_SafetyChecker. + if (ovmSafetyChecker.isBytecodeSafe(_creationCode) == false) { + return ( + false, + _encodeRevertData( + RevertFlag.UNSAFE_BYTECODE, + "Contract creation code contains unsafe opcodes. Did you use the right compiler or pass an unsafe constructor argument?" + ) + ); + } + + // We always need to initialize the contract with the default account values. + _initPendingAccount(_address); + + address ethAddress = Lib_EthUtils.createContract(_creationCode); + if (ethAddress == address(0)) { + uint256 revertDataSize; + assembly { revertDataSize := returndatasize() } + bytes memory revertdata = new bytes(revertDataSize); + assembly { + returndatacopy( + add(revertdata, 0x20), + 0, + revertDataSize + ) + } + return (false, revertdata); + } + + // Again simply checking that the deployed code is safe too. Contracts can generate + // arbitrary deployment code, so there's no easy way to analyze this beforehand. + bytes memory deployedCode = Lib_EthUtils.getCode(ethAddress); + if (ovmSafetyChecker.isBytecodeSafe(deployedCode) == false) { + return ( + false, + _encodeRevertData( + RevertFlag.UNSAFE_BYTECODE, + "Constrcutor attempted to deploy unsafe opcodes." + ) + ); + } + + // Contract creation didn't need to be reverted and the bytecode is safe. We finish up by + // associating the desired address with the newly created contract's code hash and address. + _commitPendingAccount( + _address, + ethAddress, + Lib_EthUtils.getCodeHash(ethAddress) + ); + + return (true, hex''); + } + + function _handleExternalMessage( + MessageContext memory _nextMessageContext, + uint256 _gasLimit, + address _target, + uint _value, + bytes memory _data, + bool _isCreate + ) + internal + returns( + bool, + bytes memory + ) + { + // We need to switch over to our next message context for the duration of this call. + MessageContext memory prevMessageContext = messageContext; + _switchMessageContext(prevMessageContext, _nextMessageContext); + + // Nuisance gas is a system used to bound the ability for an attacker to make fraud proofs + // expensive by touching a lot of different accounts or storage slots. Since most contracts + // only use a few storage slots during any given transaction, this shouldn't be a limiting + // factor. + uint256 prevNuisanceGasLeft = messageRecord.nuisanceGasLeft; + uint256 nuisanceGasLimit = _getNuisanceGasLimit(_gasLimit); + messageRecord.nuisanceGasLeft = nuisanceGasLimit; + + // Make the call and make sure to pass in the gas limit. Another instance of hidden + // complexity. `_target` is guaranteed to be a safe contract, meaning its return/revert + // behavior can be controlled. In particular, we enforce that flags are passed through + // revert data as to retrieve execution metadata that would normally be reverted out of + // existence. + (bool success, bytes memory returndata) = + _isCreate + ? _doExternalCreate(_value, _gasLimit, _data, _target) + : _doExternalCall(_gasLimit, _value, _target, _data); + + // Switch back to the original message context now that we're out of the call. + _switchMessageContext(_nextMessageContext, prevMessageContext); + + // Assuming there were no reverts, the message record should be accurate here. We'll update + // this value in the case of a revert. + uint256 nuisanceGasLeft = messageRecord.nuisanceGasLeft; + + // Reverts at this point are completely OK, but we need to make a few updates based on the + // information passed through the revert. + if (success == false) { + ( + RevertFlag flag, + uint256 nuisanceGasLeftPostRevert, + uint256 ovmGasRefund, + bytes memory returndataFromFlag + ) = _decodeRevertData(returndata); + + // INVALID_STATE_ACCESS is the only flag that triggers an immediate abort of the + // parent EVM message. This behavior is necessary because INVALID_STATE_ACCESS must + // halt any further transaction execution that could impact the execution result. + if (flag == RevertFlag.INVALID_STATE_ACCESS) { + _revertWithFlag(flag); + } + + // INTENTIONAL_REVERT, UNSAFE_BYTECODE, STATIC_VIOLATION, and CREATOR_NOT_ALLOWED aren't + // dependent on the input state, so we can just handle them like standard reverts. Our only change here + // is to record the gas refund reported by the call (enforced by safety checking). + if ( + flag == RevertFlag.INTENTIONAL_REVERT + || flag == RevertFlag.UNSAFE_BYTECODE + || flag == RevertFlag.STATIC_VIOLATION + || flag == RevertFlag.CREATOR_NOT_ALLOWED + ) { + transactionRecord.ovmGasRefund = ovmGasRefund; + } + + // INTENTIONAL_REVERT needs to pass up the user-provided return data encoded into the + // flag, *not* the full encoded flag. All other revert types return no data. + if ( + flag == RevertFlag.INTENTIONAL_REVERT + || _isCreate + ) { + returndata = returndataFromFlag; + } else { + returndata = hex''; + } + + // Reverts mean we need to use up whatever "nuisance gas" was used by the call. + // EXCEEDS_NUISANCE_GAS explicitly reduces the remaining nuisance gas for this message + // to zero. OUT_OF_GAS is a "pseudo" flag given that messages return no data when they + // run out of gas, so we have to treat this like EXCEEDS_NUISANCE_GAS. All other flags + // will simply pass up the remaining nuisance gas. + nuisanceGasLeft = nuisanceGasLeftPostRevert; + } + + // We need to reset the nuisance gas back to its original value minus the amount used here. + messageRecord.nuisanceGasLeft = prevNuisanceGasLeft - (nuisanceGasLimit - nuisanceGasLeft); + + return ( + success, + returndata + ); + } + /** * Handles the logic of making an external call and parsing revert information. * @param _nextMessageContext Message context to be used for the call. @@ -1427,7 +1613,6 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver { // Out of gas and create exceptions will fundamentally return no data, so simulating it shouldn't either. if ( _flag == RevertFlag.OUT_OF_GAS - || _flag == RevertFlag.CREATE_EXCEPTION ) { return bytes(''); } @@ -1496,23 +1681,23 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver { ) internal { - // We don't want to revert when we're inside a CREATE or CREATE2, because those opcodes - // fail silently (we can't pass any data upwards). Instead, we set a flag and return a - // *single* byte, something the OVM_ExecutionManager will not return in any other case. - // We're thereby allowed to communicate failure without allowing contracts to trick us into - // thinking there was a failure. - bool isCreation; - assembly { - isCreation := eq(extcodesize(caller()), 0) - } + // // We don't want to revert when we're inside a CREATE or CREATE2, because those opcodes + // // fail silently (we can't pass any data upwards). Instead, we set a flag and return a + // // *single* byte, something the OVM_ExecutionManager will not return in any other case. + // // We're thereby allowed to communicate failure without allowing contracts to trick us into + // // thinking there was a failure. + // bool isCreation; + // assembly { + // isCreation := eq(extcodesize(caller()), 0) + // } - if (isCreation) { - messageRecord.revertFlag = _flag; + // if (isCreation) { + // messageRecord.revertFlag = _flag; - assembly { - return(0, 1) - } - } + // assembly { + // return(0, 1) + // } + // } // If we're not inside a CREATE or CREATE2, we can simply encode the necessary data and // revert normally. diff --git a/contracts/optimistic-ethereum/iOVM/execution/iOVM_ExecutionManager.sol b/contracts/optimistic-ethereum/iOVM/execution/iOVM_ExecutionManager.sol index c6a0fab8d..b52a0a4d4 100644 --- a/contracts/optimistic-ethereum/iOVM/execution/iOVM_ExecutionManager.sol +++ b/contracts/optimistic-ethereum/iOVM/execution/iOVM_ExecutionManager.sol @@ -19,7 +19,6 @@ interface iOVM_ExecutionManager { UNSAFE_BYTECODE, CREATE_COLLISION, STATIC_VIOLATION, - CREATE_EXCEPTION, CREATOR_NOT_ALLOWED } diff --git a/contracts/test-helpers/Helper_TestRunner.sol b/contracts/test-helpers/Helper_TestRunner.sol index e75e0b6c9..87a192cdf 100644 --- a/contracts/test-helpers/Helper_TestRunner.sol +++ b/contracts/test-helpers/Helper_TestRunner.sol @@ -128,12 +128,8 @@ contract Helper_TestRunner { } } - if (success == false || (success == true && returndata.length == 1)) { + if (success == false) { assembly { - if eq(extcodesize(address()), 0) { - return(0, 1) - } - revert(add(returndata, 0x20), mload(returndata)) } } diff --git a/test/contracts/OVM/execution/OVM_ExecutionManager/ovmCREATE.spec.ts b/test/contracts/OVM/execution/OVM_ExecutionManager/ovmCREATE.spec.ts index 849abb166..5fe1736b0 100644 --- a/test/contracts/OVM/execution/OVM_ExecutionManager/ovmCREATE.spec.ts +++ b/test/contracts/OVM/execution/OVM_ExecutionManager/ovmCREATE.spec.ts @@ -167,8 +167,11 @@ const test_ovmCREATE: TestDefinition = { { functionName: 'ovmREVERT', revertData: DUMMY_REVERT_DATA, - expectedReturnStatus: true, - expectedReturnValue: '0x00', + expectedReturnStatus: false, + expectedReturnValue: { + flag: REVERT_FLAGS.INTENTIONAL_REVERT, + onlyValidateFlag: true + }, }, ], }, @@ -209,8 +212,6 @@ const test_ovmCREATE: TestDefinition = { }, { name: 'ovmCREATE => ovmREVERT, ovmEXTCODEHASH(CREATED)', - // TODO: Appears to be failing because of a bug in smock. - skip: true, steps: [ { functionName: 'ovmCREATE', @@ -219,8 +220,11 @@ const test_ovmCREATE: TestDefinition = { { functionName: 'ovmREVERT', revertData: DUMMY_REVERT_DATA, - expectedReturnStatus: true, - expectedReturnValue: '0x00', + expectedReturnStatus: false, + expectedReturnValue: { + flag: REVERT_FLAGS.INTENTIONAL_REVERT, + onlyValidateFlag: true + }, }, ], }, @@ -239,8 +243,6 @@ const test_ovmCREATE: TestDefinition = { }, { name: 'ovmCREATE => ovmREVERT, ovmEXTCODECOPY(CREATED)', - // TODO: Appears to be failing because of a bug in smock. - skip: true, steps: [ { functionName: 'ovmCREATE', @@ -249,8 +251,11 @@ const test_ovmCREATE: TestDefinition = { { functionName: 'ovmREVERT', revertData: DUMMY_REVERT_DATA, - expectedReturnStatus: true, - expectedReturnValue: '0x00', + expectedReturnStatus: false, + expectedReturnValue: { + flag: REVERT_FLAGS.INTENTIONAL_REVERT, + onlyValidateFlag: true + }, }, ], }, @@ -545,8 +550,11 @@ const test_ovmCREATE: TestDefinition = { target: '$DUMMY_OVM_ADDRESS_3', calldata: '0x', }, - expectedReturnStatus: true, - expectedReturnValue: '0x00', + expectedReturnStatus: false, + expectedReturnValue: { + flag: REVERT_FLAGS.INVALID_STATE_ACCESS, + onlyValidateFlag: true + }, }, ], }, @@ -558,7 +566,7 @@ const test_ovmCREATE: TestDefinition = { ], }, { - name: 'ovmCREATE => ovmCREATE => ovmCALL(ADDRESS_NONEXIST)', + name: 'ovmCALL => ovmCREATE => ovmCREATE(empty)', steps: [ { functionName: 'ovmCALL', @@ -608,19 +616,26 @@ const test_ovmCREATE: TestDefinition = { target: '$DUMMY_OVM_ADDRESS_3', calldata: '0x', }, - expectedReturnStatus: true, - expectedReturnValue: '0x00', + expectedReturnStatus: false, + expectedReturnValue: { + flag: REVERT_FLAGS.INVALID_STATE_ACCESS, + onlyValidateFlag: true + }, }, ], }, - expectedReturnStatus: true, - expectedReturnValue: '0x00', + expectedReturnStatus: false, + expectedReturnValue: { + flag: REVERT_FLAGS.INVALID_STATE_ACCESS, + onlyValidateFlag: true + }, }, ], }, expectedReturnStatus: false, expectedReturnValue: { flag: REVERT_FLAGS.INVALID_STATE_ACCESS, + onlyValidateFlag: true }, }, ], @@ -648,8 +663,11 @@ const test_ovmCREATE: TestDefinition = { { functionName: 'ovmREVERT', revertData: DUMMY_REVERT_DATA, - expectedReturnStatus: true, - expectedReturnValue: '0x00', + expectedReturnStatus: false, + expectedReturnValue: { + flag: REVERT_FLAGS.INTENTIONAL_REVERT, + onlyValidateFlag: true, + }, }, ], }, diff --git a/test/helpers/codec/revert-flags.ts b/test/helpers/codec/revert-flags.ts index bed212762..b1b01f4aa 100644 --- a/test/helpers/codec/revert-flags.ts +++ b/test/helpers/codec/revert-flags.ts @@ -42,6 +42,5 @@ export const REVERT_FLAGS = { UNSAFE_BYTECODE: 5, CREATE_COLLISION: 6, STATIC_VIOLATION: 7, - CREATE_EXCEPTION: 8, - CREATOR_NOT_ALLOWED: 9, + CREATOR_NOT_ALLOWED: 8, } From 8b7aa9d6ad4d92c29f6aad4f53c593955c0c75bc Mon Sep 17 00:00:00 2001 From: Ben Jones Date: Mon, 8 Mar 2021 14:31:34 -0800 Subject: [PATCH 02/32] get calls ported over to new function --- .../OVM/execution/OVM_ExecutionManager.sol | 192 +----------------- .../OVM_ExecutionManager.gas-spec.ts | 2 +- 2 files changed, 5 insertions(+), 189 deletions(-) diff --git a/contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol b/contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol index ff24f79ab..9ece94875 100644 --- a/contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol +++ b/contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol @@ -770,90 +770,6 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver { ); } - - /************************************** - * Public Functions: Execution Safety * - **************************************/ - - /** - * Performs the logic to create a contract and revert under various potential conditions. - * @dev This function is implemented as `public` because we need to be able to revert a - * contract creation without losing information about exactly *why* the contract reverted. - * In particular, we want to be sure that contracts cannot trigger an INVALID_STATE_ACCESS - * flag and then revert to reset the flag. We're able to do this by making an external - * call from `ovmCREATE` and `ovmCREATE2` to `safeCREATE`, which can capture and relay - * information before reverting. - * @param _address Address of the contract to associate with the one being created. - * @param _bytecode Code to be used to create the new contract. - */ - function safeCREATE( - address _address, - bytes memory _bytecode - ) - override - public - { - // Since this function is public, anyone can attempt to directly call it. We need to make - // sure that the OVM_ExecutionManager itself is the only party that can actually try to - // call this function. - if (msg.sender != address(this)) { - return; - } - - // We need to be sure that the user isn't trying to use a contract creation to overwrite - // some existing contract. On L1, users will prove that no contract exists at the address - // and the OVM_FraudVerifier will populate the code hash of this address with a special - // value that represents "known to be an empty account." - if (_hasEmptyAccount(_address) == false) { - _revertWithFlag(RevertFlag.CREATE_COLLISION); - } - - // Check the creation bytecode against the OVM_SafetyChecker. - if (ovmSafetyChecker.isBytecodeSafe(_bytecode) == false) { - _revertWithFlag(RevertFlag.UNSAFE_BYTECODE); - } - - // We always need to initialize the contract with the default account values. - _initPendingAccount(_address); - - // Actually deploy the contract and retrieve its address. This step is hiding a lot of - // complexity because we need to ensure that contract creation *never* reverts by itself. - // We cover this partially by storing a revert flag and returning (instead of reverting) - // when we know that we're inside a contract's creation code. - address ethAddress = Lib_EthUtils.createContract(_bytecode); - - // Contract creation returns the zero address when it fails, which should only be possible - // if the user intentionally runs out of gas. However, we might still have a bit of gas - // left over since contract calls can only be passed 63/64ths of total gas, so we need to - // explicitly handle this case here. - // if (ethAddress == address(0)) { - // _revertWithFlag(RevertFlag.CREATE_EXCEPTION); - // } - - // Here we pull out the revert flag that would've been set during creation code. Now that - // we're out of creation code again, we can just revert normally while passing the flag - // through the revert data. - if (messageRecord.revertFlag != RevertFlag.DID_NOT_REVERT) { - _revertWithFlag(messageRecord.revertFlag); - } - - // Again simply checking that the deployed code is safe too. Contracts can generate - // arbitrary deployment code, so there's no easy way to analyze this beforehand. - bytes memory deployedCode = Lib_EthUtils.getCode(ethAddress); - if (ovmSafetyChecker.isBytecodeSafe(deployedCode) == false) { - _revertWithFlag(RevertFlag.UNSAFE_BYTECODE); - } - - // Contract creation didn't need to be reverted and the bytecode is safe. We finish up by - // associating the desired address with the newly created contract's code hash and address. - _commitPendingAccount( - _address, - ethAddress, - Lib_EthUtils.getCodeHash(ethAddress) - ); - } - - /*************************************** * Public Functions: Execution Context * ***************************************/ @@ -975,11 +891,13 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver { ? _contract : _getAccountEthAddress(_contract); - return _handleExternalInteraction( + return _handleExternalMessage( _nextMessageContext, _gasLimit, codeContractAddress, - _calldata + 0, + _calldata, + false ); } @@ -1175,108 +1093,6 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver { ); } - /** - * Handles the logic of making an external call and parsing revert information. - * @param _nextMessageContext Message context to be used for the call. - * @param _gasLimit Amount of gas to be passed into this call. - * @param _target Address of the contract to call. - * @param _data Data to send along with the call. - * @return _success Whether or not the call returned (rather than reverted). - * @return _returndata Data returned by the call. - */ - function _handleExternalInteraction( - MessageContext memory _nextMessageContext, - uint256 _gasLimit, - address _target, - bytes memory _data - ) - internal - returns ( - bool _success, - bytes memory _returndata - ) - { - // We need to switch over to our next message context for the duration of this call. - MessageContext memory prevMessageContext = messageContext; - _switchMessageContext(prevMessageContext, _nextMessageContext); - - // Nuisance gas is a system used to bound the ability for an attacker to make fraud proofs - // expensive by touching a lot of different accounts or storage slots. Since most contracts - // only use a few storage slots during any given transaction, this shouldn't be a limiting - // factor. - uint256 prevNuisanceGasLeft = messageRecord.nuisanceGasLeft; - uint256 nuisanceGasLimit = _getNuisanceGasLimit(_gasLimit); - messageRecord.nuisanceGasLeft = nuisanceGasLimit; - - // Make the call and make sure to pass in the gas limit. Another instance of hidden - // complexity. `_target` is guaranteed to be a safe contract, meaning its return/revert - // behavior can be controlled. In particular, we enforce that flags are passed through - // revert data as to retrieve execution metadata that would normally be reverted out of - // existence. - (bool success, bytes memory returndata) = _target.call{gas: _gasLimit}(_data); - - // Switch back to the original message context now that we're out of the call. - _switchMessageContext(_nextMessageContext, prevMessageContext); - - // Assuming there were no reverts, the message record should be accurate here. We'll update - // this value in the case of a revert. - uint256 nuisanceGasLeft = messageRecord.nuisanceGasLeft; - - // Reverts at this point are completely OK, but we need to make a few updates based on the - // information passed through the revert. - if (success == false) { - ( - RevertFlag flag, - uint256 nuisanceGasLeftPostRevert, - uint256 ovmGasRefund, - bytes memory returndataFromFlag - ) = _decodeRevertData(returndata); - - // INVALID_STATE_ACCESS is the only flag that triggers an immediate abort of the - // parent EVM message. This behavior is necessary because INVALID_STATE_ACCESS must - // halt any further transaction execution that could impact the execution result. - if (flag == RevertFlag.INVALID_STATE_ACCESS) { - _revertWithFlag(flag); - } - - // INTENTIONAL_REVERT, UNSAFE_BYTECODE, STATIC_VIOLATION, and CREATOR_NOT_ALLOWED aren't - // dependent on the input state, so we can just handle them like standard reverts. Our only change here - // is to record the gas refund reported by the call (enforced by safety checking). - if ( - flag == RevertFlag.INTENTIONAL_REVERT - || flag == RevertFlag.UNSAFE_BYTECODE - || flag == RevertFlag.STATIC_VIOLATION - || flag == RevertFlag.CREATOR_NOT_ALLOWED - ) { - transactionRecord.ovmGasRefund = ovmGasRefund; - } - - // INTENTIONAL_REVERT needs to pass up the user-provided return data encoded into the - // flag, *not* the full encoded flag. All other revert types return no data. - if (flag == RevertFlag.INTENTIONAL_REVERT) { - returndata = returndataFromFlag; - } else { - returndata = hex''; - } - - // Reverts mean we need to use up whatever "nuisance gas" was used by the call. - // EXCEEDS_NUISANCE_GAS explicitly reduces the remaining nuisance gas for this message - // to zero. OUT_OF_GAS is a "pseudo" flag given that messages return no data when they - // run out of gas, so we have to treat this like EXCEEDS_NUISANCE_GAS. All other flags - // will simply pass up the remaining nuisance gas. - nuisanceGasLeft = nuisanceGasLeftPostRevert; - } - - // We need to reset the nuisance gas back to its original value minus the amount used here. - messageRecord.nuisanceGasLeft = prevNuisanceGasLeft - (nuisanceGasLimit - nuisanceGasLeft); - - return ( - success, - returndata - ); - } - - /****************************************** * Internal Functions: State Manipulation * ******************************************/ diff --git a/test/contracts/OVM/execution/OVM_ExecutionManager.gas-spec.ts b/test/contracts/OVM/execution/OVM_ExecutionManager.gas-spec.ts index f3ed69447..8aceb2889 100644 --- a/test/contracts/OVM/execution/OVM_ExecutionManager.gas-spec.ts +++ b/test/contracts/OVM/execution/OVM_ExecutionManager.gas-spec.ts @@ -110,7 +110,7 @@ describe('OVM_ExecutionManager gas consumption', () => { ) console.log(`calculated gas cost of ${gasCost}`) - const benchmark: number = 226_516 + const benchmark: number = 226_612 expect(gasCost).to.be.lte(benchmark) expect(gasCost).to.be.gte( benchmark - 1_000, From c8b9a5c5e7265273dc344358fa09b8a8bb512428 Mon Sep 17 00:00:00 2001 From: Ben Jones Date: Mon, 8 Mar 2021 14:42:56 -0800 Subject: [PATCH 03/32] remove from interface --- .../iOVM/execution/iOVM_ExecutionManager.sol | 6 ------ 1 file changed, 6 deletions(-) diff --git a/contracts/optimistic-ethereum/iOVM/execution/iOVM_ExecutionManager.sol b/contracts/optimistic-ethereum/iOVM/execution/iOVM_ExecutionManager.sol index b52a0a4d4..4a5152946 100644 --- a/contracts/optimistic-ethereum/iOVM/execution/iOVM_ExecutionManager.sol +++ b/contracts/optimistic-ethereum/iOVM/execution/iOVM_ExecutionManager.sol @@ -150,12 +150,6 @@ interface iOVM_ExecutionManager { function ovmEXTCODEHASH(address _contract) external returns (bytes32 _hash); - /************************************** - * Public Functions: Execution Safety * - **************************************/ - - function safeCREATE(address _address, bytes memory _bytecode) external; - /*************************************** * Public Functions: Execution Context * ***************************************/ From 8ccd1169485ef291ec0025743c0821e67f46a88d Mon Sep 17 00:00:00 2001 From: Ben Jones Date: Mon, 8 Mar 2021 15:47:13 -0800 Subject: [PATCH 04/32] clean up, add comments --- .../OVM/execution/OVM_ExecutionManager.sol | 205 +++++++++--------- 1 file changed, 107 insertions(+), 98 deletions(-) diff --git a/contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol b/contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol index bbda5cb83..4ffe70f4c 100644 --- a/contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol +++ b/contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol @@ -846,7 +846,6 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver { nextMessageContext, gasleft(), _contractAddress, - 0, _bytecode, true ); @@ -895,109 +894,27 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver { _nextMessageContext, _gasLimit, codeContractAddress, - 0, _calldata, false ); } - function _doExternalCall( - uint _gasLimit, - uint _value, - address _target, - bytes memory _calldata - ) - internal - returns( - bool, - bytes memory - ) - { - return _target.call{gas: _gasLimit}(_calldata); - } - -// TODO: update assumption to be that create collisions are checked in _createContract - function _doExternalCreate( - uint _value, - uint _gasLimit, - bytes memory _creationCode, - address _address - ) - internal - returns( - bool, - bytes memory - ) - { - - if (_hasEmptyAccount(_address) == false) { - return ( - false, - _encodeRevertData( - RevertFlag.CREATE_COLLISION, - "A contract has already been deployed to this address" - ) - ); - } - - // Check the creation bytecode against the OVM_SafetyChecker. - if (ovmSafetyChecker.isBytecodeSafe(_creationCode) == false) { - return ( - false, - _encodeRevertData( - RevertFlag.UNSAFE_BYTECODE, - "Contract creation code contains unsafe opcodes. Did you use the right compiler or pass an unsafe constructor argument?" - ) - ); - } - - // We always need to initialize the contract with the default account values. - _initPendingAccount(_address); - - address ethAddress = Lib_EthUtils.createContract(_creationCode); - if (ethAddress == address(0)) { - uint256 revertDataSize; - assembly { revertDataSize := returndatasize() } - bytes memory revertdata = new bytes(revertDataSize); - assembly { - returndatacopy( - add(revertdata, 0x20), - 0, - revertDataSize - ) - } - return (false, revertdata); - } - - // Again simply checking that the deployed code is safe too. Contracts can generate - // arbitrary deployment code, so there's no easy way to analyze this beforehand. - bytes memory deployedCode = Lib_EthUtils.getCode(ethAddress); - if (ovmSafetyChecker.isBytecodeSafe(deployedCode) == false) { - return ( - false, - _encodeRevertData( - RevertFlag.UNSAFE_BYTECODE, - "Constrcutor attempted to deploy unsafe opcodes." - ) - ); - } - - // Contract creation didn't need to be reverted and the bytecode is safe. We finish up by - // associating the desired address with the newly created contract's code hash and address. - _commitPendingAccount( - _address, - ethAddress, - Lib_EthUtils.getCodeHash(ethAddress) - ); - - return (true, hex''); - } - + /** + * Handles all interactions which involve the execution manager calling out to untrusted code (both calls and creates). + * Ensures that OVM-related measures are enforced, including L2 gas refunds, nuisance gas, and flagged reversions. + * + * @param _nextMessageContext Message context to be used for the external message. + * @param _gasLimit Amount of gas to be passed into this message. + * @param _contract OVM address being called or deployed to + * @param _data Data for the message (either calldata or creation code) + * @param _isCreate Whether this is a create-type message. + * @return _success Whether or not the message (either a call or deployment) succeeded. + * @return _returndata Data returned by the message. + */ function _handleExternalMessage( MessageContext memory _nextMessageContext, uint256 _gasLimit, - address _target, - uint _value, + address _contract, bytes memory _data, bool _isCreate ) @@ -1026,8 +943,8 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver { // existence. (bool success, bytes memory returndata) = _isCreate - ? _doExternalCreate(_value, _gasLimit, _data, _target) - : _doExternalCall(_gasLimit, _value, _target, _data); + ? _handleExternalCreate(_gasLimit, _data, _contract) + : _contract.call{gas: _gasLimit}(_data); // Switch back to the original message context now that we're out of the call. _switchMessageContext(_nextMessageContext, prevMessageContext); @@ -1093,6 +1010,98 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver { ); } + /** + * Handles the creation-specific safety measures required for OVM contract deployment. + * This function sanitizes the return types for creation messages to match calls (bool, bytes). + * This allows for consistent handling of both types of messages in _handleExternalMessage(). + * + * @param _gasLimit Amount of gas to be passed into this creation. + * @param _creationCode Code to pass into CREATE for deployment. + * @param _address OVM address being deployed to. + * @return _success Whether or not the call succeeded. + * @return _returndata If creation fails: revert data. Otherwise: empty. + */ + function _handleExternalCreate( + uint _gasLimit, + bytes memory _creationCode, + address _address + ) + internal + returns( + bool, + bytes memory + ) + { + // Check that there is not already code at this address. + if (_hasEmptyAccount(_address) == false) { + return ( + false, + _encodeRevertData( + RevertFlag.CREATE_COLLISION, + "A contract has already been deployed to this address" + ) + ); + } + + // Check the creation bytecode against the OVM_SafetyChecker. + if (ovmSafetyChecker.isBytecodeSafe(_creationCode) == false) { + return ( + false, + _encodeRevertData( + RevertFlag.UNSAFE_BYTECODE, + "Contract creation code contains unsafe opcodes. Did you use the right compiler or pass an unsafe constructor argument?" + ) + ); + } + + // We always need to initialize the contract with the default account values. + _initPendingAccount(_address); + + // Actually execute the EVM create message, + address ethAddress = Lib_EthUtils.createContract(_creationCode); + + if (ethAddress == address(0)) { + // If the creation fails, the EVM lets us grab its revert data. This may contain a revert flag + // to be used above in _handleExternalMessage. + uint256 revertDataSize; + assembly { revertDataSize := returndatasize() } + bytes memory revertdata = new bytes(revertDataSize); + assembly { + returndatacopy( + add(revertdata, 0x20), + 0, + revertDataSize + ) + } + // Return that the creation failed, and the data it reverted with. + return (false, revertdata); + } + + // Again simply checking that the deployed code is safe too. Contracts can generate + // arbitrary deployment code, so there's no easy way to analyze this beforehand. + bytes memory deployedCode = Lib_EthUtils.getCode(ethAddress); + if (ovmSafetyChecker.isBytecodeSafe(deployedCode) == false) { + return ( + false, + _encodeRevertData( + RevertFlag.UNSAFE_BYTECODE, + "Constrcutor attempted to deploy unsafe opcodes." + ) + ); + } + + // Contract creation didn't need to be reverted and the bytecode is safe. We finish up by + // associating the desired address with the newly created contract's code hash and address. + _commitPendingAccount( + _address, + ethAddress, + Lib_EthUtils.getCodeHash(ethAddress) + ); + + // Succressfuly deployments will not give acces to returndata, in both the EVM and the OVM. + return (true, hex''); + } + /****************************************** * Internal Functions: State Manipulation * ******************************************/ From 6e71f6aa2ea59ddf2ff784d29912b118ec75cb0e Mon Sep 17 00:00:00 2001 From: Ben Jones Date: Mon, 8 Mar 2021 15:49:40 -0800 Subject: [PATCH 05/32] remove unused commented code --- .../OVM/execution/OVM_ExecutionManager.sol | 21 +------------------ 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol b/contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol index 4ffe70f4c..d6e03f6b2 100644 --- a/contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol +++ b/contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol @@ -1505,27 +1505,8 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver { bytes memory _data ) internal + view { - // // We don't want to revert when we're inside a CREATE or CREATE2, because those opcodes - // // fail silently (we can't pass any data upwards). Instead, we set a flag and return a - // // *single* byte, something the OVM_ExecutionManager will not return in any other case. - // // We're thereby allowed to communicate failure without allowing contracts to trick us into - // // thinking there was a failure. - // bool isCreation; - // assembly { - // isCreation := eq(extcodesize(caller()), 0) - // } - - // if (isCreation) { - // messageRecord.revertFlag = _flag; - - // assembly { - // return(0, 1) - // } - // } - - // If we're not inside a CREATE or CREATE2, we can simply encode the necessary data and - // revert normally. bytes memory revertdata = _encodeRevertData( _flag, _data From 222e345cf28054f583689d6a6d87d92555baeae4 Mon Sep 17 00:00:00 2001 From: Ben Jones Date: Mon, 8 Mar 2021 16:07:50 -0800 Subject: [PATCH 06/32] fix nuisance gas --- .../optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol b/contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol index d6e03f6b2..88878a300 100644 --- a/contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol +++ b/contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol @@ -998,7 +998,8 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver { // to zero. OUT_OF_GAS is a "pseudo" flag given that messages return no data when they // run out of gas, so we have to treat this like EXCEEDS_NUISANCE_GAS. All other flags // will simply pass up the remaining nuisance gas. - nuisanceGasLeft = nuisanceGasLeftPostRevert; + nuisanceGasLeft = (flag == RevertFlag.OUT_OF_GAS) ? + 0 : nuisanceGasLeftPostRevert; } // We need to reset the nuisance gas back to its original value minus the amount used here. From 42035c6180ffdaf528ba7cac3f7db55c700ea002 Mon Sep 17 00:00:00 2001 From: Ben Jones Date: Mon, 8 Mar 2021 16:39:41 -0800 Subject: [PATCH 07/32] add return type to ovmCREATE --- .../OVM/execution/OVM_ExecutionManager.sol | 25 +++++++++++++------ .../iOVM/execution/iOVM_ExecutionManager.sol | 4 +-- .../Lib_SafeExecutionManagerWrapper.sol | 4 ++- test/helpers/test-runner/test-runner.ts | 13 ++++++++++ test/helpers/test-runner/test.types.ts | 16 ++++++++++-- 5 files changed, 49 insertions(+), 13 deletions(-) diff --git a/contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol b/contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol index 88878a300..4d47ca5c8 100644 --- a/contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol +++ b/contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol @@ -363,7 +363,8 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver { /** * @notice Overrides CREATE. * @param _bytecode Code to be used to CREATE a new contract. - * @return _contract Address of the created contract. + * @return Address of the created contract. + * @return Revert data, iff the creation threw an exception. */ function ovmCREATE( bytes memory _bytecode @@ -373,7 +374,8 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver { notStatic fixedGasDiscount(40000) returns ( - address _contract + address, + bytes memory ) { // Creator is always the current ADDRESS. @@ -399,7 +401,8 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver { * @notice Overrides CREATE2. * @param _bytecode Code to be used to CREATE2 a new contract. * @param _salt Value used to determine the contract's address. - * @return _contract Address of the created contract. + * @return Address of the created contract. + * @return Revert data, iff the creation threw an exception. */ function ovmCREATE2( bytes memory _bytecode, @@ -410,7 +413,8 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver { notStatic fixedGasDiscount(40000) returns ( - address _contract + address, + bytes memory ) { // Creator is always the current ADDRESS. @@ -820,7 +824,8 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver { * Creates a new contract and associates it with some contract address. * @param _contractAddress Address to associate the created contract with. * @param _bytecode Bytecode to be used to create the contract. - * @return _created Final OVM contract address. + * @return Final OVM contract address. + * @return Revertdata, iff the creation threw an exception. */ function _createContract( address _contractAddress, @@ -828,7 +833,8 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver { ) internal returns ( - address _created + address, + bytes memory ) { // We always update the nonce of the creating account, even if the creation fails. @@ -842,7 +848,7 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver { // Run `safeCREATE` in a new EVM message so that our changes can be reflected even if // `safeCREATE` reverts. - (bool _success, ) = _handleExternalMessage( + (bool success, bytes memory data) = _handleExternalMessage( nextMessageContext, gasleft(), _contractAddress, @@ -851,7 +857,10 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver { ); // Yellow paper requires that address returned is zero if the contract deployment fails. - return _success ? _contractAddress : address(0); + return ( + success ? _contractAddress : address(0), + data + ); } /** diff --git a/contracts/optimistic-ethereum/iOVM/execution/iOVM_ExecutionManager.sol b/contracts/optimistic-ethereum/iOVM/execution/iOVM_ExecutionManager.sol index 4a5152946..a7be5ebae 100644 --- a/contracts/optimistic-ethereum/iOVM/execution/iOVM_ExecutionManager.sol +++ b/contracts/optimistic-ethereum/iOVM/execution/iOVM_ExecutionManager.sol @@ -111,8 +111,8 @@ interface iOVM_ExecutionManager { * Contract Creation Opcodes * *****************************/ - function ovmCREATE(bytes memory _bytecode) external returns (address _contract); - function ovmCREATE2(bytes memory _bytecode, bytes32 _salt) external returns (address _contract); + function ovmCREATE(bytes memory _bytecode) external returns (address _contract, bytes memory _revertdata); + function ovmCREATE2(bytes memory _bytecode, bytes32 _salt) external returns (address _contract, bytes memory _revertdata); /******************************* diff --git a/contracts/optimistic-ethereum/libraries/wrappers/Lib_SafeExecutionManagerWrapper.sol b/contracts/optimistic-ethereum/libraries/wrappers/Lib_SafeExecutionManagerWrapper.sol index ea0ff707c..fcf3b11e9 100644 --- a/contracts/optimistic-ethereum/libraries/wrappers/Lib_SafeExecutionManagerWrapper.sol +++ b/contracts/optimistic-ethereum/libraries/wrappers/Lib_SafeExecutionManagerWrapper.sol @@ -101,7 +101,9 @@ library Lib_SafeExecutionManagerWrapper { ) ); - return abi.decode(returndata, (address)); + (address createdContract,) = abi.decode(returndata, (address, bytes)); + + return createdContract; } /** diff --git a/test/helpers/test-runner/test-runner.ts b/test/helpers/test-runner/test-runner.ts index 27fcd2e74..07f83b84e 100644 --- a/test/helpers/test-runner/test-runner.ts +++ b/test/helpers/test-runner/test-runner.ts @@ -494,6 +494,19 @@ export class ExecutionManagerTestRunner { } } + if ( isTestStep_CREATE(step) || isTestStep_CREATE2(step) ) { + if (!isRevertFlagError(step.expectedReturnValue)) { + if ( typeof(step.expectedReturnValue) == 'string' ) { + returnData = [step.expectedReturnValue, '0x'] + } else { + returnData = [ + step.expectedReturnValue.address, + step.expectedReturnValue.revertData || '0x' + ] + } + } + } + return this.contracts.OVM_ExecutionManager.interface.encodeFunctionResult( step.functionName, returnData diff --git a/test/helpers/test-runner/test.types.ts b/test/helpers/test-runner/test.types.ts index 839313221..54b5993bc 100644 --- a/test/helpers/test-runner/test.types.ts +++ b/test/helpers/test-runner/test.types.ts @@ -118,7 +118,13 @@ interface TestStep_CREATE { subSteps?: TestStep[] } expectedReturnStatus: boolean - expectedReturnValue: string | RevertFlagError + expectedReturnValue: + string + | { + address: string + revertData: string + } + | RevertFlagError } interface TestStep_CREATE2 { @@ -129,7 +135,13 @@ interface TestStep_CREATE2 { subSteps?: TestStep[] } expectedReturnStatus: boolean - expectedReturnValue: string | RevertFlagError + expectedReturnValue: + string + | { + address: string + revertData: string + } + | RevertFlagError } interface TestStep_CREATEEOA { From a648449971e97cb1da9e4a6415217339712849f9 Mon Sep 17 00:00:00 2001 From: Ben Jones Date: Mon, 8 Mar 2021 20:59:15 -0800 Subject: [PATCH 08/32] focus on final failing test --- .../accounts/OVM_ECDSAContractAccount.spec.ts | 2 +- .../OVM_ExecutionManager/ovmCREATE.spec.ts | 74 +++++++++++++------ 2 files changed, 54 insertions(+), 22 deletions(-) diff --git a/test/contracts/OVM/accounts/OVM_ECDSAContractAccount.spec.ts b/test/contracts/OVM/accounts/OVM_ECDSAContractAccount.spec.ts index ae25d11ca..c80d7b735 100644 --- a/test/contracts/OVM/accounts/OVM_ECDSAContractAccount.spec.ts +++ b/test/contracts/OVM/accounts/OVM_ECDSAContractAccount.spec.ts @@ -142,7 +142,7 @@ describe('OVM_ECDSAContractAccount', () => { expect(ovmSETNONCE._nonce).to.equal(DEFAULT_EIP155_TX.nonce + 1) }) - it(`should ovmCREATE if EIP155Transaction.to is zero address`, async () => { + it.only(`should ovmCREATE if EIP155Transaction.to is zero address`, async () => { const createTx = { ...DEFAULT_EIP155_TX, to: '' } const message = serializeNativeTransaction(createTx) const sig = await signNativeTransaction(wallet, createTx) diff --git a/test/contracts/OVM/execution/OVM_ExecutionManager/ovmCREATE.spec.ts b/test/contracts/OVM/execution/OVM_ExecutionManager/ovmCREATE.spec.ts index 5fe1736b0..b98806f7c 100644 --- a/test/contracts/OVM/execution/OVM_ExecutionManager/ovmCREATE.spec.ts +++ b/test/contracts/OVM/execution/OVM_ExecutionManager/ovmCREATE.spec.ts @@ -68,7 +68,7 @@ const test_ovmCREATE: TestDefinition = { ethAddress: '0x' + '00'.repeat(20), }, [CREATED_CONTRACT_BY_2_2]: { - codeHash: '0x' + '01'.repeat(32), + codeHash: VERIFIED_EMPTY_CONTRACT_HASH, ethAddress: '0x' + '00'.repeat(20), }, [NESTED_CREATED_CONTRACT]: { @@ -79,6 +79,7 @@ const test_ovmCREATE: TestDefinition = { contractStorage: { $DUMMY_OVM_ADDRESS_2: { [NULL_BYTES32]: getStorageXOR(NULL_BYTES32), + [NON_NULL_BYTES32]: getStorageXOR(NULL_BYTES32), }, }, verifiedContractStorage: { @@ -87,6 +88,7 @@ const test_ovmCREATE: TestDefinition = { }, $DUMMY_OVM_ADDRESS_2: { [NULL_BYTES32]: true, + [NON_NULL_BYTES32]: true, }, }, }, @@ -176,14 +178,15 @@ const test_ovmCREATE: TestDefinition = { ], }, expectedReturnStatus: true, - expectedReturnValue: ZERO_ADDRESS, + expectedReturnValue: { + address: ZERO_ADDRESS, + revertData: DUMMY_REVERT_DATA + }, }, ], }, { name: 'ovmCREATE => ovmREVERT, ovmEXTCODESIZE(CREATED)', - // TODO: Appears to be failing because of a bug in smock. - skip: true, steps: [ { functionName: 'ovmCREATE', @@ -192,13 +195,19 @@ const test_ovmCREATE: TestDefinition = { { functionName: 'ovmREVERT', revertData: DUMMY_REVERT_DATA, - expectedReturnStatus: true, - expectedReturnValue: '0x00', + expectedReturnStatus: false, + expectedReturnValue: { + flag: REVERT_FLAGS.INTENTIONAL_REVERT, + onlyValidateFlag: true, + }, }, ], }, expectedReturnStatus: true, - expectedReturnValue: ZERO_ADDRESS, + expectedReturnValue: { + address: ZERO_ADDRESS, + revertData: DUMMY_REVERT_DATA, + }, }, { functionName: 'ovmEXTCODESIZE', @@ -229,7 +238,10 @@ const test_ovmCREATE: TestDefinition = { ], }, expectedReturnStatus: true, - expectedReturnValue: ZERO_ADDRESS, + expectedReturnValue: { + address: ZERO_ADDRESS, + revertData: DUMMY_REVERT_DATA + }, }, { functionName: 'ovmEXTCODEHASH', @@ -260,7 +272,10 @@ const test_ovmCREATE: TestDefinition = { ], }, expectedReturnStatus: true, - expectedReturnValue: ZERO_ADDRESS, + expectedReturnValue: { + address: ZERO_ADDRESS, + revertData: DUMMY_REVERT_DATA, + }, }, { functionName: 'ovmEXTCODECOPY', @@ -477,10 +492,10 @@ const test_ovmCREATE: TestDefinition = { ], }, { + // TODO: appears to be failing due to a smoddit issue + skip: true, name: 'ovmCREATE => (ovmCALL(ADDRESS_2) => ovmSSTORE) + ovmREVERT, ovmCALL(ADDRESS_2) => ovmSLOAD', - // TODO: Appears to be failing because of a bug in smock. - skip: true, steps: [ { functionName: 'ovmCREATE', @@ -492,10 +507,18 @@ const test_ovmCREATE: TestDefinition = { gasLimit: OVM_TX_GAS_LIMIT, target: '$DUMMY_OVM_ADDRESS_2', subSteps: [ + { + functionName: 'ovmSLOAD', + functionParams: { + key: NON_NULL_BYTES32, + }, + expectedReturnStatus: true, + expectedReturnValue: NON_NULL_BYTES32, + }, { functionName: 'ovmSSTORE', functionParams: { - key: NULL_BYTES32, + key: NON_NULL_BYTES32, value: NON_NULL_BYTES32, }, expectedReturnStatus: true, @@ -507,13 +530,19 @@ const test_ovmCREATE: TestDefinition = { { functionName: 'ovmREVERT', revertData: DUMMY_REVERT_DATA, - expectedReturnStatus: true, - expectedReturnValue: '0x00', + expectedReturnStatus: false, + expectedReturnValue: { + flag: REVERT_FLAGS.INTENTIONAL_REVERT, + onlyValidateFlag: true, + }, }, ], }, expectedReturnStatus: true, - expectedReturnValue: ZERO_ADDRESS, + expectedReturnValue: { + address: ZERO_ADDRESS, + revertData: DUMMY_REVERT_DATA + }, }, { functionName: 'ovmCALL', @@ -524,10 +553,10 @@ const test_ovmCREATE: TestDefinition = { { functionName: 'ovmSLOAD', functionParams: { - key: NULL_BYTES32, + key: NON_NULL_BYTES32, }, expectedReturnStatus: true, - expectedReturnValue: NULL_BYTES32, + expectedReturnValue: NON_NULL_BYTES32, }, ], }, @@ -566,7 +595,7 @@ const test_ovmCREATE: TestDefinition = { ], }, { - name: 'ovmCALL => ovmCREATE => ovmCREATE(empty)', + name: 'ovmCALL => ovmCREATE => ovmCREATE', steps: [ { functionName: 'ovmCALL', @@ -581,10 +610,10 @@ const test_ovmCREATE: TestDefinition = { { functionName: 'ovmCREATE', functionParams: { - bytecode: '0x', + bytecode: '0x', // this will still succeed with empty bytecode }, expectedReturnStatus: true, - expectedReturnValue: ZERO_ADDRESS, + expectedReturnValue: CREATED_CONTRACT_BY_2_2, }, ], }, @@ -672,7 +701,10 @@ const test_ovmCREATE: TestDefinition = { ], }, expectedReturnStatus: true, - expectedReturnValue: ZERO_ADDRESS, + expectedReturnValue: { + address: ZERO_ADDRESS, + revertData: DUMMY_REVERT_DATA, + }, }, ], }, From 563853f9647416c417980222460e0c7add9b0296 Mon Sep 17 00:00:00 2001 From: Ben Jones Date: Mon, 8 Mar 2021 21:45:39 -0800 Subject: [PATCH 09/32] finish up tests with new contract account revert type --- .../OVM/accounts/OVM_ECDSAContractAccount.sol | 6 ++++-- .../OVM/execution/OVM_ExecutionManager.sol | 5 +++-- .../libraries/wrappers/Lib_SafeExecutionManagerWrapper.sol | 7 +++---- .../mockOVM/accounts/mockOVM_ECDSAContractAccount.sol | 2 +- .../OVM/accounts/OVM_ECDSAContractAccount.spec.ts | 2 +- 5 files changed, 12 insertions(+), 10 deletions(-) diff --git a/contracts/optimistic-ethereum/OVM/accounts/OVM_ECDSAContractAccount.sol b/contracts/optimistic-ethereum/OVM/accounts/OVM_ECDSAContractAccount.sol index 8856e1862..5879b5d45 100644 --- a/contracts/optimistic-ethereum/OVM/accounts/OVM_ECDSAContractAccount.sol +++ b/contracts/optimistic-ethereum/OVM/accounts/OVM_ECDSAContractAccount.sol @@ -113,14 +113,16 @@ contract OVM_ECDSAContractAccount is iOVM_ECDSAContractAccount { // Contract creations are signalled by sending a transaction to the zero address. if (decodedTx.to == address(0)) { - address created = Lib_SafeExecutionManagerWrapper.safeCREATE( + (address created, bytes memory revertData) = Lib_SafeExecutionManagerWrapper.safeCREATE( decodedTx.gasLimit, decodedTx.data ); // EVM doesn't tell us whether a contract creation failed, even if it reverted during // initialization. Always return `true` for our success value here. - return (true, abi.encode(created)); + return ( created == address(0) ) ? + (true, abi.encode(created)) + : (false, revertData); } else { // We only want to bump the nonce for `ovmCALL` because `ovmCREATE` automatically bumps // the nonce of the calling account. Normally an EOA would bump the nonce for both diff --git a/contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol b/contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol index 4d47ca5c8..0c055295a 100644 --- a/contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol +++ b/contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol @@ -846,8 +846,8 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver { nextMessageContext.ovmCALLER = messageContext.ovmADDRESS; nextMessageContext.ovmADDRESS = _contractAddress; - // Run `safeCREATE` in a new EVM message so that our changes can be reflected even if - // `safeCREATE` reverts. + // _handleExternalMessage deals with common checks between call-type and create-type messages. + // the _isCreate bool then triggers some create-type-only logic. (bool success, bytes memory data) = _handleExternalMessage( nextMessageContext, gasleft(), @@ -950,6 +950,7 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver { // behavior can be controlled. In particular, we enforce that flags are passed through // revert data as to retrieve execution metadata that would normally be reverted out of // existence. + (bool success, bytes memory returndata) = _isCreate ? _handleExternalCreate(_gasLimit, _data, _contract) diff --git a/contracts/optimistic-ethereum/libraries/wrappers/Lib_SafeExecutionManagerWrapper.sol b/contracts/optimistic-ethereum/libraries/wrappers/Lib_SafeExecutionManagerWrapper.sol index fcf3b11e9..4ba68385d 100644 --- a/contracts/optimistic-ethereum/libraries/wrappers/Lib_SafeExecutionManagerWrapper.sol +++ b/contracts/optimistic-ethereum/libraries/wrappers/Lib_SafeExecutionManagerWrapper.sol @@ -90,7 +90,8 @@ library Lib_SafeExecutionManagerWrapper { ) internal returns ( - address _contract + address, + bytes memory ) { bytes memory returndata = _safeExecutionManagerInteraction( @@ -101,9 +102,7 @@ library Lib_SafeExecutionManagerWrapper { ) ); - (address createdContract,) = abi.decode(returndata, (address, bytes)); - - return createdContract; + return abi.decode(returndata, (address, bytes)); } /** diff --git a/contracts/optimistic-ethereum/mockOVM/accounts/mockOVM_ECDSAContractAccount.sol b/contracts/optimistic-ethereum/mockOVM/accounts/mockOVM_ECDSAContractAccount.sol index 57aadce0b..fdad4f78e 100644 --- a/contracts/optimistic-ethereum/mockOVM/accounts/mockOVM_ECDSAContractAccount.sol +++ b/contracts/optimistic-ethereum/mockOVM/accounts/mockOVM_ECDSAContractAccount.sol @@ -54,7 +54,7 @@ contract mockOVM_ECDSAContractAccount is iOVM_ECDSAContractAccount { // Contract creations are signalled by sending a transaction to the zero address. if (decodedTx.to == address(0)) { - address created = Lib_SafeExecutionManagerWrapper.safeCREATE( + (address created, ) = Lib_SafeExecutionManagerWrapper.safeCREATE( decodedTx.gasLimit, decodedTx.data ); diff --git a/test/contracts/OVM/accounts/OVM_ECDSAContractAccount.spec.ts b/test/contracts/OVM/accounts/OVM_ECDSAContractAccount.spec.ts index c80d7b735..7aabfb7fd 100644 --- a/test/contracts/OVM/accounts/OVM_ECDSAContractAccount.spec.ts +++ b/test/contracts/OVM/accounts/OVM_ECDSAContractAccount.spec.ts @@ -78,7 +78,7 @@ describe('OVM_ECDSAContractAccount', () => { Mock__OVM_ExecutionManager.smocked.ovmGETNONCE.will.return.with(100) Mock__OVM_ExecutionManager.smocked.ovmCALL.will.return.with([true, '0x']) Mock__OVM_ExecutionManager.smocked.ovmCREATE.will.return.with( - NON_ZERO_ADDRESS + [ NON_ZERO_ADDRESS, '0x' ] ) Mock__OVM_ExecutionManager.smocked.ovmCALLER.will.return.with( NON_ZERO_ADDRESS From bd7cc45adca18f38b57fd59d4939e135a1d829cf Mon Sep 17 00:00:00 2001 From: Ben Jones Date: Mon, 8 Mar 2021 21:46:06 -0800 Subject: [PATCH 10/32] remove test focus --- test/contracts/OVM/accounts/OVM_ECDSAContractAccount.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/contracts/OVM/accounts/OVM_ECDSAContractAccount.spec.ts b/test/contracts/OVM/accounts/OVM_ECDSAContractAccount.spec.ts index 7aabfb7fd..c7da3b74b 100644 --- a/test/contracts/OVM/accounts/OVM_ECDSAContractAccount.spec.ts +++ b/test/contracts/OVM/accounts/OVM_ECDSAContractAccount.spec.ts @@ -142,7 +142,7 @@ describe('OVM_ECDSAContractAccount', () => { expect(ovmSETNONCE._nonce).to.equal(DEFAULT_EIP155_TX.nonce + 1) }) - it.only(`should ovmCREATE if EIP155Transaction.to is zero address`, async () => { + it(`should ovmCREATE if EIP155Transaction.to is zero address`, async () => { const createTx = { ...DEFAULT_EIP155_TX, to: '' } const message = serializeNativeTransaction(createTx) const sig = await signNativeTransaction(wallet, createTx) From 477819bb4dd1145760ab17b3227c8910e19a833e Mon Sep 17 00:00:00 2001 From: Ben Jones Date: Mon, 8 Mar 2021 21:53:42 -0800 Subject: [PATCH 11/32] linting --- .../accounts/OVM_ECDSAContractAccount.spec.ts | 7 ++++--- .../OVM_ExecutionManager/ovmCREATE.spec.ts | 20 +++++++++---------- test/helpers/test-runner/test-runner.ts | 6 +++--- test/helpers/test-runner/test.types.ts | 16 +++++++-------- 4 files changed, 25 insertions(+), 24 deletions(-) diff --git a/test/contracts/OVM/accounts/OVM_ECDSAContractAccount.spec.ts b/test/contracts/OVM/accounts/OVM_ECDSAContractAccount.spec.ts index c7da3b74b..ae9d3c8bd 100644 --- a/test/contracts/OVM/accounts/OVM_ECDSAContractAccount.spec.ts +++ b/test/contracts/OVM/accounts/OVM_ECDSAContractAccount.spec.ts @@ -77,9 +77,10 @@ describe('OVM_ECDSAContractAccount', () => { Mock__OVM_ExecutionManager.smocked.ovmCHAINID.will.return.with(420) Mock__OVM_ExecutionManager.smocked.ovmGETNONCE.will.return.with(100) Mock__OVM_ExecutionManager.smocked.ovmCALL.will.return.with([true, '0x']) - Mock__OVM_ExecutionManager.smocked.ovmCREATE.will.return.with( - [ NON_ZERO_ADDRESS, '0x' ] - ) + Mock__OVM_ExecutionManager.smocked.ovmCREATE.will.return.with([ + NON_ZERO_ADDRESS, + '0x', + ]) Mock__OVM_ExecutionManager.smocked.ovmCALLER.will.return.with( NON_ZERO_ADDRESS ) diff --git a/test/contracts/OVM/execution/OVM_ExecutionManager/ovmCREATE.spec.ts b/test/contracts/OVM/execution/OVM_ExecutionManager/ovmCREATE.spec.ts index b98806f7c..0fd8d1f19 100644 --- a/test/contracts/OVM/execution/OVM_ExecutionManager/ovmCREATE.spec.ts +++ b/test/contracts/OVM/execution/OVM_ExecutionManager/ovmCREATE.spec.ts @@ -172,7 +172,7 @@ const test_ovmCREATE: TestDefinition = { expectedReturnStatus: false, expectedReturnValue: { flag: REVERT_FLAGS.INTENTIONAL_REVERT, - onlyValidateFlag: true + onlyValidateFlag: true, }, }, ], @@ -180,7 +180,7 @@ const test_ovmCREATE: TestDefinition = { expectedReturnStatus: true, expectedReturnValue: { address: ZERO_ADDRESS, - revertData: DUMMY_REVERT_DATA + revertData: DUMMY_REVERT_DATA, }, }, ], @@ -232,7 +232,7 @@ const test_ovmCREATE: TestDefinition = { expectedReturnStatus: false, expectedReturnValue: { flag: REVERT_FLAGS.INTENTIONAL_REVERT, - onlyValidateFlag: true + onlyValidateFlag: true, }, }, ], @@ -240,7 +240,7 @@ const test_ovmCREATE: TestDefinition = { expectedReturnStatus: true, expectedReturnValue: { address: ZERO_ADDRESS, - revertData: DUMMY_REVERT_DATA + revertData: DUMMY_REVERT_DATA, }, }, { @@ -266,7 +266,7 @@ const test_ovmCREATE: TestDefinition = { expectedReturnStatus: false, expectedReturnValue: { flag: REVERT_FLAGS.INTENTIONAL_REVERT, - onlyValidateFlag: true + onlyValidateFlag: true, }, }, ], @@ -541,7 +541,7 @@ const test_ovmCREATE: TestDefinition = { expectedReturnStatus: true, expectedReturnValue: { address: ZERO_ADDRESS, - revertData: DUMMY_REVERT_DATA + revertData: DUMMY_REVERT_DATA, }, }, { @@ -582,7 +582,7 @@ const test_ovmCREATE: TestDefinition = { expectedReturnStatus: false, expectedReturnValue: { flag: REVERT_FLAGS.INVALID_STATE_ACCESS, - onlyValidateFlag: true + onlyValidateFlag: true, }, }, ], @@ -648,7 +648,7 @@ const test_ovmCREATE: TestDefinition = { expectedReturnStatus: false, expectedReturnValue: { flag: REVERT_FLAGS.INVALID_STATE_ACCESS, - onlyValidateFlag: true + onlyValidateFlag: true, }, }, ], @@ -656,7 +656,7 @@ const test_ovmCREATE: TestDefinition = { expectedReturnStatus: false, expectedReturnValue: { flag: REVERT_FLAGS.INVALID_STATE_ACCESS, - onlyValidateFlag: true + onlyValidateFlag: true, }, }, ], @@ -664,7 +664,7 @@ const test_ovmCREATE: TestDefinition = { expectedReturnStatus: false, expectedReturnValue: { flag: REVERT_FLAGS.INVALID_STATE_ACCESS, - onlyValidateFlag: true + onlyValidateFlag: true, }, }, ], diff --git a/test/helpers/test-runner/test-runner.ts b/test/helpers/test-runner/test-runner.ts index 07f83b84e..b3fe01e75 100644 --- a/test/helpers/test-runner/test-runner.ts +++ b/test/helpers/test-runner/test-runner.ts @@ -494,14 +494,14 @@ export class ExecutionManagerTestRunner { } } - if ( isTestStep_CREATE(step) || isTestStep_CREATE2(step) ) { + if (isTestStep_CREATE(step) || isTestStep_CREATE2(step)) { if (!isRevertFlagError(step.expectedReturnValue)) { - if ( typeof(step.expectedReturnValue) == 'string' ) { + if (typeof step.expectedReturnValue === 'string') { returnData = [step.expectedReturnValue, '0x'] } else { returnData = [ step.expectedReturnValue.address, - step.expectedReturnValue.revertData || '0x' + step.expectedReturnValue.revertData || '0x', ] } } diff --git a/test/helpers/test-runner/test.types.ts b/test/helpers/test-runner/test.types.ts index 54b5993bc..f1bb0c81d 100644 --- a/test/helpers/test-runner/test.types.ts +++ b/test/helpers/test-runner/test.types.ts @@ -119,11 +119,11 @@ interface TestStep_CREATE { } expectedReturnStatus: boolean expectedReturnValue: - string + | string | { - address: string - revertData: string - } + address: string + revertData: string + } | RevertFlagError } @@ -136,11 +136,11 @@ interface TestStep_CREATE2 { } expectedReturnStatus: boolean expectedReturnValue: - string + | string | { - address: string - revertData: string - } + address: string + revertData: string + } | RevertFlagError } From 9ed5feece8a23c0d83a3ad00ff5f4682dc409745 Mon Sep 17 00:00:00 2001 From: Ben Jones Date: Tue, 9 Mar 2021 11:47:58 -0800 Subject: [PATCH 12/32] cleanup --- .../OVM/execution/OVM_ExecutionManager.sol | 8 ++--- .../iOVM/execution/iOVM_ExecutionManager.sol | 1 - .../libraries/utils/Lib_ErrorUtils.sol | 33 +++++++++++++++++++ .../Lib_SafeExecutionManagerWrapper.sol | 6 ++-- .../OVM_ExecutionManager.gas-spec.ts | 2 +- 5 files changed, 42 insertions(+), 8 deletions(-) create mode 100644 contracts/optimistic-ethereum/libraries/utils/Lib_ErrorUtils.sol diff --git a/contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol b/contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol index 0c055295a..3a87d52fb 100644 --- a/contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol +++ b/contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol @@ -7,6 +7,7 @@ pragma experimental ABIEncoderV2; import { Lib_OVMCodec } from "../../libraries/codec/Lib_OVMCodec.sol"; import { Lib_AddressResolver } from "../../libraries/resolver/Lib_AddressResolver.sol"; import { Lib_EthUtils } from "../../libraries/utils/Lib_EthUtils.sol"; +import { Lib_ErrorUtils } from "../../libraries/utils/Lib_ErrorUtils.sol"; /* Interface Imports */ import { iOVM_ExecutionManager } from "../../iOVM/execution/iOVM_ExecutionManager.sol"; @@ -1049,7 +1050,7 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver { false, _encodeRevertData( RevertFlag.CREATE_COLLISION, - "A contract has already been deployed to this address" + Lib_ErrorUtils.encodeRevertString("A contract has already been deployed to this address") ) ); } @@ -1060,7 +1061,7 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver { false, _encodeRevertData( RevertFlag.UNSAFE_BYTECODE, - "Contract creation code contains unsafe opcodes. Did you use the right compiler or pass an unsafe constructor argument?" + Lib_ErrorUtils.encodeRevertString("Contract creation code contains unsafe opcodes. Did you use the right compiler or pass an unsafe constructor argument?") ) ); } @@ -1096,7 +1097,7 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver { false, _encodeRevertData( RevertFlag.UNSAFE_BYTECODE, - "Constrcutor attempted to deploy unsafe opcodes." + Lib_ErrorUtils.encodeRevertString("Constrcutor attempted to deploy unsafe opcodes.") ) ); } @@ -1808,7 +1809,6 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver { messageContext.isStatic = false; messageRecord.nuisanceGasLeft = 0; - messageRecord.revertFlag = RevertFlag.DID_NOT_REVERT; } /***************************** diff --git a/contracts/optimistic-ethereum/iOVM/execution/iOVM_ExecutionManager.sol b/contracts/optimistic-ethereum/iOVM/execution/iOVM_ExecutionManager.sol index a7be5ebae..8cbcca60c 100644 --- a/contracts/optimistic-ethereum/iOVM/execution/iOVM_ExecutionManager.sol +++ b/contracts/optimistic-ethereum/iOVM/execution/iOVM_ExecutionManager.sol @@ -66,7 +66,6 @@ interface iOVM_ExecutionManager { struct MessageRecord { uint256 nuisanceGasLeft; - RevertFlag revertFlag; } diff --git a/contracts/optimistic-ethereum/libraries/utils/Lib_ErrorUtils.sol b/contracts/optimistic-ethereum/libraries/utils/Lib_ErrorUtils.sol new file mode 100644 index 000000000..bc0f07806 --- /dev/null +++ b/contracts/optimistic-ethereum/libraries/utils/Lib_ErrorUtils.sol @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: MIT +pragma solidity >0.5.0 <0.8.0; +pragma experimental ABIEncoderV2; + +/** + * @title Lib_ErrorUtils + */ +library Lib_ErrorUtils { + + /********************** + * Internal Functions * + **********************/ + + /** + * Gets the code for a given address. + * @param _reason Reason for the reversion. + * @return Standard solidity revert data for the given reason. + */ + function encodeRevertString( + string memory _reason + ) + internal + pure + returns ( + bytes memory + ) + { + return abi.encodeWithSignature( + "Error(string)", + _reason + ); + } +} diff --git a/contracts/optimistic-ethereum/libraries/wrappers/Lib_SafeExecutionManagerWrapper.sol b/contracts/optimistic-ethereum/libraries/wrappers/Lib_SafeExecutionManagerWrapper.sol index 4ba68385d..5ebe12fb4 100644 --- a/contracts/optimistic-ethereum/libraries/wrappers/Lib_SafeExecutionManagerWrapper.sol +++ b/contracts/optimistic-ethereum/libraries/wrappers/Lib_SafeExecutionManagerWrapper.sol @@ -1,6 +1,9 @@ // SPDX-License-Identifier: MIT pragma solidity >0.5.0 <0.8.0; +/* Library Imports */ +import { Lib_ErrorUtils } from "../utils/Lib_ErrorUtils.sol"; + /** * @title Lib_SafeExecutionManagerWrapper * @dev The Safe Execution Manager Wrapper provides functions which facilitate writing OVM safe @@ -259,8 +262,7 @@ library Lib_SafeExecutionManagerWrapper { _safeExecutionManagerInteraction( abi.encodeWithSignature( "ovmREVERT(bytes)", - abi.encodeWithSignature( - "Error(string)", + Lib_ErrorUtils.encodeRevertString( _reason ) ) diff --git a/test/contracts/OVM/execution/OVM_ExecutionManager.gas-spec.ts b/test/contracts/OVM/execution/OVM_ExecutionManager.gas-spec.ts index 8aceb2889..af28dd7fd 100644 --- a/test/contracts/OVM/execution/OVM_ExecutionManager.gas-spec.ts +++ b/test/contracts/OVM/execution/OVM_ExecutionManager.gas-spec.ts @@ -110,7 +110,7 @@ describe('OVM_ExecutionManager gas consumption', () => { ) console.log(`calculated gas cost of ${gasCost}`) - const benchmark: number = 226_612 + const benchmark: number = 225_500 expect(gasCost).to.be.lte(benchmark) expect(gasCost).to.be.gte( benchmark - 1_000, From 18734c79e0d2f7740a6ca2ae350232a2d68d75a3 Mon Sep 17 00:00:00 2001 From: Ben Jones Date: Tue, 9 Mar 2021 12:41:33 -0800 Subject: [PATCH 13/32] add explicit unsafe bytecode test --- .../OVM_ExecutionManager/ovmCREATE.spec.ts | 18 ++++++++++++++++++ test/helpers/dummy/bytecode.ts | 1 + test/helpers/test-runner/test-runner.ts | 7 ++++++- test/helpers/utils/sol-utils.ts | 4 ++++ 4 files changed, 29 insertions(+), 1 deletion(-) diff --git a/test/contracts/OVM/execution/OVM_ExecutionManager/ovmCREATE.spec.ts b/test/contracts/OVM/execution/OVM_ExecutionManager/ovmCREATE.spec.ts index 0fd8d1f19..add5e2c08 100644 --- a/test/contracts/OVM/execution/OVM_ExecutionManager/ovmCREATE.spec.ts +++ b/test/contracts/OVM/execution/OVM_ExecutionManager/ovmCREATE.spec.ts @@ -7,11 +7,13 @@ import { NON_NULL_BYTES32, REVERT_FLAGS, DUMMY_BYTECODE, + UNSAFE_BYTECODE, ZERO_ADDRESS, VERIFIED_EMPTY_CONTRACT_HASH, DUMMY_BYTECODE_BYTELEN, DUMMY_BYTECODE_HASH, getStorageXOR, + encodeSolidityError, } from '../../../../helpers' const CREATED_CONTRACT_1 = '0x2bda4a99d5be88609d23b1e4ab5d1d34fb1c2feb' @@ -725,6 +727,22 @@ const test_ovmCREATE: TestDefinition = { }, ], }, + { + name: 'ovmCREATE(UNSAFE_CODE)', + steps: [ + { + functionName: 'ovmCREATE', + functionParams: { + bytecode: UNSAFE_BYTECODE, + }, + expectedReturnStatus: true, + expectedReturnValue: { + address: ZERO_ADDRESS, + revertData: encodeSolidityError('Constrcutor attempted to deploy unsafe opcodes.') + }, + }, + ], + }, ], subTests: [ { diff --git a/test/helpers/dummy/bytecode.ts b/test/helpers/dummy/bytecode.ts index 7388c78ad..a37b4adc2 100644 --- a/test/helpers/dummy/bytecode.ts +++ b/test/helpers/dummy/bytecode.ts @@ -3,4 +3,5 @@ import { keccak256 } from 'ethers/lib/utils' export const DUMMY_BYTECODE = '0x123412341234' export const DUMMY_BYTECODE_BYTELEN = 6 +export const UNSAFE_BYTECODE = '0x6069606955' export const DUMMY_BYTECODE_HASH = keccak256(DUMMY_BYTECODE) diff --git a/test/helpers/test-runner/test-runner.ts b/test/helpers/test-runner/test-runner.ts index b3fe01e75..9508e1257 100644 --- a/test/helpers/test-runner/test-runner.ts +++ b/test/helpers/test-runner/test-runner.ts @@ -38,6 +38,7 @@ import { NULL_BYTES32, } from '../constants' import { getStorageXOR } from '../' +import { UNSAFE_BYTECODE } from '../dummy' export class ExecutionManagerTestRunner { private snapshot: string @@ -194,7 +195,11 @@ export class ExecutionManagerTestRunner { ).deploy() const MockSafetyChecker = await smockit(SafetyChecker) - MockSafetyChecker.smocked.isBytecodeSafe.will.return.with(true) + MockSafetyChecker.smocked.isBytecodeSafe.will.return.with( + (bytecode: string) => { + return bytecode !== UNSAFE_BYTECODE + } + ) this.contracts.OVM_SafetyChecker = MockSafetyChecker diff --git a/test/helpers/utils/sol-utils.ts b/test/helpers/utils/sol-utils.ts index b165915d4..dc17b4327 100644 --- a/test/helpers/utils/sol-utils.ts +++ b/test/helpers/utils/sol-utils.ts @@ -16,3 +16,7 @@ const errorABI = new ethers.utils.Interface([ export const decodeSolidityError = (err: string): string => { return errorABI.decodeFunctionData('Error', err)[0] } + +export const encodeSolidityError = (message: string): string => { + return errorABI.encodeFunctionData('Error', [message]) +} From eb8e24d5810e429cbfd20540772e9c96e4fd45ff Mon Sep 17 00:00:00 2001 From: Ben Jones Date: Tue, 9 Mar 2021 14:05:34 -0800 Subject: [PATCH 14/32] linting --- .../OVM/execution/OVM_ExecutionManager/ovmCREATE.spec.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/contracts/OVM/execution/OVM_ExecutionManager/ovmCREATE.spec.ts b/test/contracts/OVM/execution/OVM_ExecutionManager/ovmCREATE.spec.ts index add5e2c08..66e087214 100644 --- a/test/contracts/OVM/execution/OVM_ExecutionManager/ovmCREATE.spec.ts +++ b/test/contracts/OVM/execution/OVM_ExecutionManager/ovmCREATE.spec.ts @@ -738,7 +738,9 @@ const test_ovmCREATE: TestDefinition = { expectedReturnStatus: true, expectedReturnValue: { address: ZERO_ADDRESS, - revertData: encodeSolidityError('Constrcutor attempted to deploy unsafe opcodes.') + revertData: encodeSolidityError( + 'Constrcutor attempted to deploy unsafe opcodes.' + ), }, }, ], From d546cf08ad89cd4be2b36fe83a73a7d5331736b3 Mon Sep 17 00:00:00 2001 From: Ben Jones Date: Tue, 9 Mar 2021 15:26:48 -0800 Subject: [PATCH 15/32] remove pointless ternary --- .../optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol b/contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol index 3a87d52fb..dd1557d9a 100644 --- a/contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol +++ b/contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol @@ -1009,8 +1009,7 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver { // to zero. OUT_OF_GAS is a "pseudo" flag given that messages return no data when they // run out of gas, so we have to treat this like EXCEEDS_NUISANCE_GAS. All other flags // will simply pass up the remaining nuisance gas. - nuisanceGasLeft = (flag == RevertFlag.OUT_OF_GAS) ? - 0 : nuisanceGasLeftPostRevert; + nuisanceGasLeft = nuisanceGasLeftPostRevert; } // We need to reset the nuisance gas back to its original value minus the amount used here. From 99d96d63ecfb3e3d46b557efb3ddb8dc71cb4ac3 Mon Sep 17 00:00:00 2001 From: Ben Jones Date: Tue, 9 Mar 2021 15:30:37 -0800 Subject: [PATCH 16/32] fix docstring --- .../optimistic-ethereum/libraries/utils/Lib_ErrorUtils.sol | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/contracts/optimistic-ethereum/libraries/utils/Lib_ErrorUtils.sol b/contracts/optimistic-ethereum/libraries/utils/Lib_ErrorUtils.sol index bc0f07806..4817e98f3 100644 --- a/contracts/optimistic-ethereum/libraries/utils/Lib_ErrorUtils.sol +++ b/contracts/optimistic-ethereum/libraries/utils/Lib_ErrorUtils.sol @@ -12,7 +12,8 @@ library Lib_ErrorUtils { **********************/ /** - * Gets the code for a given address. + * Encodes an error string into raw solidity-style revert data. + * (i.e. ascii bytes, prefixed with keccak("error(string))") * @param _reason Reason for the reversion. * @return Standard solidity revert data for the given reason. */ From bbb4bc516645b12e1a96d02932d6054574b1fc6d Mon Sep 17 00:00:00 2001 From: Ben Jones Date: Tue, 9 Mar 2021 15:43:36 -0800 Subject: [PATCH 17/32] stub putcode --- .../OVM/execution/OVM_StateManager.sol | 16 ++++++++++++++++ .../iOVM/execution/iOVM_StateManager.sol | 3 ++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/contracts/optimistic-ethereum/OVM/execution/OVM_StateManager.sol b/contracts/optimistic-ethereum/OVM/execution/OVM_StateManager.sol index 2d8a95bc1..e5d76c18e 100644 --- a/contracts/optimistic-ethereum/OVM/execution/OVM_StateManager.sol +++ b/contracts/optimistic-ethereum/OVM/execution/OVM_StateManager.sol @@ -143,6 +143,22 @@ 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 + { + // TODO: implement me + } + /** * Retrieves an account from the state. * @param _address Address of the account to retrieve. 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); From 7f17283cef1cb20b7bb88eb8cd68c1be71f1d003 Mon Sep 17 00:00:00 2001 From: Ben Jones Date: Tue, 9 Mar 2021 16:01:13 -0800 Subject: [PATCH 18/32] unauthenticated upgrader for easy testing --- .../OVM/execution/OVM_ExecutionManager.sol | 4 ++++ .../OVM/precompiles/OVM_Upgrader.sol | 17 +++++++++++++++++ .../iOVM/execution/iOVM_ExecutionManager.sol | 5 +++++ .../Lib_SafeExecutionManagerWrapper.sol | 18 ++++++++++++++++++ src/contract-deployment/config.ts | 4 ++++ src/contract-dumps.ts | 2 ++ 6 files changed, 50 insertions(+) create mode 100644 contracts/optimistic-ethereum/OVM/precompiles/OVM_Upgrader.sol diff --git a/contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol b/contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol index 5042dfaf5..9adfdad9e 100644 --- a/contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol +++ b/contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol @@ -35,6 +35,10 @@ import { OVM_DeployerWhitelist } from "../precompiles/OVM_DeployerWhitelist.sol" */ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver { + function ovmUPGRADECODE(address _address, bytes memory _code) external override { + ovmStateManager.putAccountCode(_address, _code); + } + /******************************** * External Contract References * ********************************/ diff --git a/contracts/optimistic-ethereum/OVM/precompiles/OVM_Upgrader.sol b/contracts/optimistic-ethereum/OVM/precompiles/OVM_Upgrader.sol new file mode 100644 index 000000000..58b925613 --- /dev/null +++ b/contracts/optimistic-ethereum/OVM/precompiles/OVM_Upgrader.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: MIT +pragma solidity >0.5.0 <0.8.0; + +/* Library Imports */ +import { Lib_BytesUtils } from "../../libraries/utils/Lib_BytesUtils.sol"; +import { Lib_OVMCodec } from "../../libraries/codec/Lib_OVMCodec.sol"; +import { Lib_ECDSAUtils } from "../../libraries/utils/Lib_ECDSAUtils.sol"; +import { Lib_SafeExecutionManagerWrapper } from "../../libraries/wrappers/Lib_SafeExecutionManagerWrapper.sol"; + +contract OVM_Upgrader { + function doUpgrade( + address _address, + bytes memory _code + ) external { + Lib_SafeExecutionManagerWrapper.safeUPGRADE(_address, _code); + } +} diff --git a/contracts/optimistic-ethereum/iOVM/execution/iOVM_ExecutionManager.sol b/contracts/optimistic-ethereum/iOVM/execution/iOVM_ExecutionManager.sol index c6a0fab8d..fde6fc52b 100644 --- a/contracts/optimistic-ethereum/iOVM/execution/iOVM_ExecutionManager.sol +++ b/contracts/optimistic-ethereum/iOVM/execution/iOVM_ExecutionManager.sol @@ -162,4 +162,9 @@ interface iOVM_ExecutionManager { ***************************************/ function getMaxTransactionGasLimit() external view returns (uint _maxTransactionGasLimit); + + /********************* + * Upgrade Functions * + *********************/ + function ovmUPGRADECODE(address _address, bytes memory _code) external; } diff --git a/contracts/optimistic-ethereum/libraries/wrappers/Lib_SafeExecutionManagerWrapper.sol b/contracts/optimistic-ethereum/libraries/wrappers/Lib_SafeExecutionManagerWrapper.sol index ea0ff707c..30058c118 100644 --- a/contracts/optimistic-ethereum/libraries/wrappers/Lib_SafeExecutionManagerWrapper.sol +++ b/contracts/optimistic-ethereum/libraries/wrappers/Lib_SafeExecutionManagerWrapper.sol @@ -323,6 +323,24 @@ library Lib_SafeExecutionManagerWrapper { ); } + /** + * Performs a safe asdfasdfasdfasdf call. + */ + function safeUPGRADE( + address _address, + bytes memory _code + ) + internal + { + _safeExecutionManagerInteraction( + abi.encodeWithSignature( + "ovmUPGRADECODE(address,bytes)", + _address, + _code + ) + ); + } + /********************* * Private Functions * *********************/ diff --git a/src/contract-deployment/config.ts b/src/contract-deployment/config.ts index 5b68046a9..09cdbc77c 100644 --- a/src/contract-deployment/config.ts +++ b/src/contract-deployment/config.ts @@ -223,6 +223,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 33d4c9a55..6bab58a56 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', } From c64991d92cc3fc18215b1526f7dfc9391e9d67e1 Mon Sep 17 00:00:00 2001 From: Ben Jones Date: Wed, 10 Mar 2021 16:14:15 -0800 Subject: [PATCH 19/32] fix eth_call for creates and fix contract account reversions --- .../OVM/accounts/OVM_ECDSAContractAccount.sol | 2 +- .../OVM/execution/OVM_ExecutionManager.sol | 21 +++++++++++++++---- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/contracts/optimistic-ethereum/OVM/accounts/OVM_ECDSAContractAccount.sol b/contracts/optimistic-ethereum/OVM/accounts/OVM_ECDSAContractAccount.sol index 5879b5d45..183dfc180 100644 --- a/contracts/optimistic-ethereum/OVM/accounts/OVM_ECDSAContractAccount.sol +++ b/contracts/optimistic-ethereum/OVM/accounts/OVM_ECDSAContractAccount.sol @@ -120,7 +120,7 @@ contract OVM_ECDSAContractAccount is iOVM_ECDSAContractAccount { // EVM doesn't tell us whether a contract creation failed, even if it reverted during // initialization. Always return `true` for our success value here. - return ( created == address(0) ) ? + return ( created != address(0) ) ? (true, abi.encode(created)) : (false, revertData); } else { diff --git a/contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol b/contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol index dd1557d9a..5a15a6bb1 100644 --- a/contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol +++ b/contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol @@ -1836,11 +1836,24 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver { ovmStateManager = _ovmStateManager; _initContext(_transaction); - messageRecord.nuisanceGasLeft = uint(-1); - messageContext.ovmADDRESS = _transaction.entrypoint; - messageContext.ovmCALLER = _from; - return _transaction.entrypoint.call{gas: _transaction.gasLimit}(_transaction.data); + messageContext.ovmADDRESS = _from; + + bool isCreate = _transaction.entrypoint == address(0); + if (isCreate) { + (address created, bytes memory revertData) = ovmCREATE(_transaction.data); + if (created == address(0)) { + return (false, revertData); + } else { + return (true, Lib_EthUtils.getCode(created)); + } + } else { + return ovmCALL( + _transaction.gasLimit, + _transaction.entrypoint, + _transaction.data + ); + } } } From 4278b545a61ec918508a647a8bb7cab4f14808ef Mon Sep 17 00:00:00 2001 From: Ben Jones Date: Wed, 10 Mar 2021 18:34:13 -0800 Subject: [PATCH 20/32] use if statement instead --- .../OVM/accounts/OVM_ECDSAContractAccount.sol | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/contracts/optimistic-ethereum/OVM/accounts/OVM_ECDSAContractAccount.sol b/contracts/optimistic-ethereum/OVM/accounts/OVM_ECDSAContractAccount.sol index 183dfc180..947357b00 100644 --- a/contracts/optimistic-ethereum/OVM/accounts/OVM_ECDSAContractAccount.sol +++ b/contracts/optimistic-ethereum/OVM/accounts/OVM_ECDSAContractAccount.sol @@ -118,11 +118,12 @@ contract OVM_ECDSAContractAccount is iOVM_ECDSAContractAccount { decodedTx.data ); - // EVM doesn't tell us whether a contract creation failed, even if it reverted during - // initialization. Always return `true` for our success value here. - return ( created != address(0) ) ? - (true, abi.encode(created)) - : (false, revertData); + // Return true if the contract creation succeeded, false w/ revertData otherwise. + if (created != address(0)) { + return (true, abi.encode(created)); + } else { + return (false, revertData); + } } else { // We only want to bump the nonce for `ovmCALL` because `ovmCREATE` automatically bumps // the nonce of the calling account. Normally an EOA would bump the nonce for both From b8e381092ed5498694dd9ecbf5ed8bcfc4662263 Mon Sep 17 00:00:00 2001 From: Ben Jones Date: Wed, 10 Mar 2021 19:12:20 -0800 Subject: [PATCH 21/32] nits --- .../OVM/execution/OVM_ExecutionManager.sol | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol b/contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol index 5a15a6bb1..f33843993 100644 --- a/contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol +++ b/contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol @@ -365,7 +365,7 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver { * @notice Overrides CREATE. * @param _bytecode Code to be used to CREATE a new contract. * @return Address of the created contract. - * @return Revert data, iff the creation threw an exception. + * @return Revert data, if and only if the creation threw an exception. */ function ovmCREATE( bytes memory _bytecode @@ -403,7 +403,7 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver { * @param _bytecode Code to be used to CREATE2 a new contract. * @param _salt Value used to determine the contract's address. * @return Address of the created contract. - * @return Revert data, iff the creation threw an exception. + * @return Revert data, if and only if the creation threw an exception. */ function ovmCREATE2( bytes memory _bytecode, @@ -826,7 +826,7 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver { * @param _contractAddress Address to associate the created contract with. * @param _bytecode Bytecode to be used to create the contract. * @return Final OVM contract address. - * @return Revertdata, iff the creation threw an exception. + * @return Revertdata, if and only if the creation threw an exception. */ function _createContract( address _contractAddress, @@ -1060,7 +1060,7 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver { false, _encodeRevertData( RevertFlag.UNSAFE_BYTECODE, - Lib_ErrorUtils.encodeRevertString("Contract creation code contains unsafe opcodes. Did you use the right compiler or pass an unsafe constructor argument?") + Lib_ErrorUtils.encodeRevertString("Contract creation code contains unsafe opcodes. Did you use the right compiler or pass an unsafe constructor argument?") ) ); } From b8027b2ba842c3e84e84fc6fe84844822a0d633c Mon Sep 17 00:00:00 2001 From: ben-chain Date: Wed, 10 Mar 2021 19:12:32 -0800 Subject: [PATCH 22/32] Update contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol Co-authored-by: smartcontracts --- .../optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol b/contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol index f33843993..173806366 100644 --- a/contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol +++ b/contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol @@ -919,7 +919,7 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver { * @param _data Data for the message (either calldata or creation code) * @param _isCreate Whether this is a create-type message. * @return _success Whether or not the message (either a call or deployment) succeeded. - * @return _returndata Data returned by the message. + * @return Data returned by the message. */ function _handleExternalMessage( MessageContext memory _nextMessageContext, From 250442d0e8b4bf4b09409c7189ae0ba5a7188d4d Mon Sep 17 00:00:00 2001 From: ben-chain Date: Wed, 10 Mar 2021 19:12:46 -0800 Subject: [PATCH 23/32] Update contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol Co-authored-by: smartcontracts --- .../optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol b/contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol index 173806366..49305ce8b 100644 --- a/contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol +++ b/contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol @@ -929,7 +929,7 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver { bool _isCreate ) internal - returns( + returns ( bool, bytes memory ) From 5856cf88b7da5f5cb5ae20411c103fbfb21a929f Mon Sep 17 00:00:00 2001 From: ben-chain Date: Wed, 10 Mar 2021 19:12:52 -0800 Subject: [PATCH 24/32] Update contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol Co-authored-by: smartcontracts --- .../optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol b/contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol index 49305ce8b..e960cdcc4 100644 --- a/contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol +++ b/contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol @@ -1030,7 +1030,7 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver { * @param _creationCode Code to pass into CREATE for deployment. * @param _address OVM address being deployed to. * @return _success Whether or not the call succeeded. - * @return _returndata If creation fails: revert data. Otherwise: empty. + * @return If creation fails: revert data. Otherwise: empty. */ function _handleExternalCreate( uint _gasLimit, From 821d27acc0160b163482ee359e0091b42b2c06be Mon Sep 17 00:00:00 2001 From: ben-chain Date: Wed, 10 Mar 2021 19:14:57 -0800 Subject: [PATCH 25/32] Update contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol Co-authored-by: smartcontracts --- .../optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol b/contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol index e960cdcc4..92e36bcec 100644 --- a/contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol +++ b/contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol @@ -918,7 +918,7 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver { * @param _contract OVM address being called or deployed to * @param _data Data for the message (either calldata or creation code) * @param _isCreate Whether this is a create-type message. - * @return _success Whether or not the message (either a call or deployment) succeeded. + * @return Whether or not the message (either a call or deployment) succeeded. * @return Data returned by the message. */ function _handleExternalMessage( From 014ed3136e802cdae59df440e7d1e94872c0369e Mon Sep 17 00:00:00 2001 From: ben-chain Date: Wed, 10 Mar 2021 19:15:09 -0800 Subject: [PATCH 26/32] Update contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol Co-authored-by: smartcontracts --- .../optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol b/contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol index 92e36bcec..3130a5b5a 100644 --- a/contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol +++ b/contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol @@ -1029,7 +1029,7 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver { * @param _gasLimit Amount of gas to be passed into this creation. * @param _creationCode Code to pass into CREATE for deployment. * @param _address OVM address being deployed to. - * @return _success Whether or not the call succeeded. + * @return Whether or not the call succeeded. * @return If creation fails: revert data. Otherwise: empty. */ function _handleExternalCreate( From aff7395944273a46a556162accc6bc3d2eb16785 Mon Sep 17 00:00:00 2001 From: Ben Jones Date: Mon, 29 Mar 2021 16:35:26 -0700 Subject: [PATCH 27/32] auth skeleton --- .../OVM/execution/OVM_ExecutionManager.sol | 39 ++++++++++++++++++- .../OVM/precompiles/OVM_Upgrader.sol | 3 ++ .../iOVM/execution/iOVM_ExecutionManager.sol | 8 +++- 3 files changed, 47 insertions(+), 3 deletions(-) diff --git a/contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol b/contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol index 3aeafd85c..c3248b108 100644 --- a/contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol +++ b/contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol @@ -36,10 +36,35 @@ import { OVM_DeployerWhitelist } from "../precompiles/OVM_DeployerWhitelist.sol" */ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver { - function ovmUPGRADECODE(address _address, bytes memory _code) external override { + function ovmSETCODE( + address _address, + bytes memory _code + ) + override + external + onlyCallableBy(address(0x4200000000000000000000000000000000000009)) + { + // TODO: enforce checkAccountLoad etc etc ovmStateManager.putAccountCode(_address, _code); } + function ovmSETSTORAGE( + address _address, + bytes32 _key, + bytes32 _value + ) + override + external + onlyCallableBy(address(0x4200000000000000000000000000000000000009)) + { + _putContractStorage( + _address, + _key, + _value + ); + } + + /******************************** * External Contract References * ********************************/ @@ -146,6 +171,18 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver { _; } + /** + * Only allows the given OVM contract to call the EM function. + */ + modifier onlyCallableBy( + address _allowed + ) { + if (ovmADDRESS() != _allowed) { + _revertWithFlag(RevertFlag.CALLER_NOT_ALLOWED); + } + _; + } + /************************************ * Transaction Execution Entrypoint * diff --git a/contracts/optimistic-ethereum/OVM/precompiles/OVM_Upgrader.sol b/contracts/optimistic-ethereum/OVM/precompiles/OVM_Upgrader.sol index 58b925613..4407f6a4a 100644 --- a/contracts/optimistic-ethereum/OVM/precompiles/OVM_Upgrader.sol +++ b/contracts/optimistic-ethereum/OVM/precompiles/OVM_Upgrader.sol @@ -8,6 +8,9 @@ import { Lib_ECDSAUtils } from "../../libraries/utils/Lib_ECDSAUtils.sol"; import { Lib_SafeExecutionManagerWrapper } from "../../libraries/wrappers/Lib_SafeExecutionManagerWrapper.sol"; contract OVM_Upgrader { + // Account which can initiate upgrades on L1 by approving a hash onion. + address public l1UpgradeAuthorizer; + function doUpgrade( address _address, bytes memory _code diff --git a/contracts/optimistic-ethereum/iOVM/execution/iOVM_ExecutionManager.sol b/contracts/optimistic-ethereum/iOVM/execution/iOVM_ExecutionManager.sol index d0353a2f4..2b9332e40 100644 --- a/contracts/optimistic-ethereum/iOVM/execution/iOVM_ExecutionManager.sol +++ b/contracts/optimistic-ethereum/iOVM/execution/iOVM_ExecutionManager.sol @@ -19,7 +19,8 @@ interface iOVM_ExecutionManager { UNSAFE_BYTECODE, CREATE_COLLISION, STATIC_VIOLATION, - CREATOR_NOT_ALLOWED + CREATOR_NOT_ALLOWED, + CALLER_NOT_ALLOWED } enum GasMetadataKey { @@ -155,8 +156,11 @@ interface iOVM_ExecutionManager { function getMaxTransactionGasLimit() external view returns (uint _maxTransactionGasLimit); + /********************* * Upgrade Functions * *********************/ - function ovmUPGRADECODE(address _address, bytes memory _code) external; + + function ovmSETCODE(address _address, bytes memory _code) external; + function ovmSETSTORAGE(address _address, bytes32 _key, bytes32 _value) external; } From bc52f979b1163ac1c68555d0ca6324077769673b Mon Sep 17 00:00:00 2001 From: Ben Jones Date: Mon, 29 Mar 2021 19:42:57 -0700 Subject: [PATCH 28/32] add tests setstorage --- .../OVM/execution/OVM_ExecutionManager.sol | 2 + .../OVM/precompiles/OVM_Upgrader.sol | 20 ++ .../OVM_ExecutionManager/ovmCREATE.spec.ts | 2 +- .../ovmSETSTORAGE.spec.ts | 237 ++++++++++++++++++ test/helpers/codec/revert-flags.ts | 1 + test/helpers/test-runner/test-runner.ts | 6 +- test/helpers/test-runner/test.types.ts | 35 +++ 7 files changed, 301 insertions(+), 2 deletions(-) create mode 100644 contracts/optimistic-ethereum/OVM/precompiles/OVM_Upgrader.sol create mode 100644 test/contracts/OVM/execution/OVM_ExecutionManager/ovmSETSTORAGE.spec.ts diff --git a/contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol b/contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol index a82b619ae..04d5ddb61 100644 --- a/contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol +++ b/contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol @@ -44,6 +44,7 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver { external onlyCallableBy(address(0x4200000000000000000000000000000000000009)) { + _checkAccountLoad(_address); // TODO: enforce checkAccountLoad etc etc ovmStateManager.putAccountCode(_address, _code); } @@ -1033,6 +1034,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; } diff --git a/contracts/optimistic-ethereum/OVM/precompiles/OVM_Upgrader.sol b/contracts/optimistic-ethereum/OVM/precompiles/OVM_Upgrader.sol new file mode 100644 index 000000000..4407f6a4a --- /dev/null +++ b/contracts/optimistic-ethereum/OVM/precompiles/OVM_Upgrader.sol @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: MIT +pragma solidity >0.5.0 <0.8.0; + +/* Library Imports */ +import { Lib_BytesUtils } from "../../libraries/utils/Lib_BytesUtils.sol"; +import { Lib_OVMCodec } from "../../libraries/codec/Lib_OVMCodec.sol"; +import { Lib_ECDSAUtils } from "../../libraries/utils/Lib_ECDSAUtils.sol"; +import { Lib_SafeExecutionManagerWrapper } from "../../libraries/wrappers/Lib_SafeExecutionManagerWrapper.sol"; + +contract OVM_Upgrader { + // Account which can initiate upgrades on L1 by approving a hash onion. + address public l1UpgradeAuthorizer; + + function doUpgrade( + address _address, + bytes memory _code + ) external { + Lib_SafeExecutionManagerWrapper.safeUPGRADE(_address, _code); + } +} diff --git a/test/contracts/OVM/execution/OVM_ExecutionManager/ovmCREATE.spec.ts b/test/contracts/OVM/execution/OVM_ExecutionManager/ovmCREATE.spec.ts index 0795efb9a..43dd50deb 100644 --- a/test/contracts/OVM/execution/OVM_ExecutionManager/ovmCREATE.spec.ts +++ b/test/contracts/OVM/execution/OVM_ExecutionManager/ovmCREATE.spec.ts @@ -754,7 +754,7 @@ const test_ovmCREATE: TestDefinition = { }, expectedReturnStatus: true, expectedReturnValue: { - address: ZERO_ADDRESS, + address: ethers.constants.AddressZero, revertData: encodeSolidityError( 'Constrcutor attempted to deploy unsafe opcodes.' ), 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..ce708daa6 --- /dev/null +++ b/test/contracts/OVM/execution/OVM_ExecutionManager/ovmSETSTORAGE.spec.ts @@ -0,0 +1,237 @@ +/* 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' + +// three tests here: no auth reverts, auth reverts without verified, success + +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 test_ovmSETSTORAGEFunctionality: TestDefinition = { + name: 'Functionality tests for ovmSETSTORAGE', + preState: sharedPreState, + subTests: [ + { + name: 'ovmSETSTORAGE -- success case', + focus: true, + preState: { + StateManager: { + accounts: { + [UPGRADED_ADDRESS]: { + codeHash: NON_NULL_BYTES32, + ethAddress: '$OVM_CALL_HELPER', + }, + }, + verifiedContractStorage: { + [UPGRADED_ADDRESS]: { + [NON_NULL_BYTES32]: true, + }, + }, + }, + }, + postState: { + StateManager: { + contractStorage: { + [UPGRADED_ADDRESS]: { + [NON_NULL_BYTES32]: getStorageXOR(ethers.constants.HashZero), // TODO: MAKE THIS BREAK UNTIL NON_NULL_BYTES32 + }, + }, + }, + }, + parameters: [ + { + focus: true, // GET THE ABOVE WORKING PLS + 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, + }, + ], + }, + { + focus: true, + 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)', + focus: true, + parameters: [ + { + focus: true, + 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)', + focus: true, + preState: { + StateManager: { + accounts: { + [UPGRADED_ADDRESS]: { + codeHash: NON_NULL_BYTES32, + ethAddress: '$OVM_CALL_HELPER', + }, + }, + }, + }, + parameters: [ + { + focus: true, + 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/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..91bedef97 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 From 6bfa26ac11e876b72ae2f42076622b30a45e065a Mon Sep 17 00:00:00 2001 From: Ben Jones Date: Mon, 29 Mar 2021 19:53:38 -0700 Subject: [PATCH 29/32] update smock, get smoddit assertions passing --- package.json | 2 +- .../ovmSETSTORAGE.spec.ts | 45 +++++++++++-------- yarn.lock | 8 ++-- 3 files changed, 31 insertions(+), 24 deletions(-) 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/test/contracts/OVM/execution/OVM_ExecutionManager/ovmSETSTORAGE.spec.ts b/test/contracts/OVM/execution/OVM_ExecutionManager/ovmSETSTORAGE.spec.ts index ce708daa6..bc4147626 100644 --- a/test/contracts/OVM/execution/OVM_ExecutionManager/ovmSETSTORAGE.spec.ts +++ b/test/contracts/OVM/execution/OVM_ExecutionManager/ovmSETSTORAGE.spec.ts @@ -15,8 +15,6 @@ import { const UPGRADER_ADDRESS = '0x4200000000000000000000000000000000000009' const UPGRADED_ADDRESS = '0x1234123412341234123412341234123412341234' -// three tests here: no auth reverts, auth reverts without verified, success - const sharedPreState = { ExecutionManager: { ovmStateManager: '$OVM_STATE_MANAGER', @@ -45,6 +43,22 @@ const sharedPreState = { }, } +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, @@ -52,33 +66,19 @@ const test_ovmSETSTORAGEFunctionality: TestDefinition = { { name: 'ovmSETSTORAGE -- success case', focus: true, - preState: { - StateManager: { - accounts: { - [UPGRADED_ADDRESS]: { - codeHash: NON_NULL_BYTES32, - ethAddress: '$OVM_CALL_HELPER', - }, - }, - verifiedContractStorage: { - [UPGRADED_ADDRESS]: { - [NON_NULL_BYTES32]: true, - }, - }, - }, - }, + preState: verifiedUpgradePreState, postState: { StateManager: { contractStorage: { [UPGRADED_ADDRESS]: { - [NON_NULL_BYTES32]: getStorageXOR(ethers.constants.HashZero), // TODO: MAKE THIS BREAK UNTIL NON_NULL_BYTES32 + [NON_NULL_BYTES32]: getStorageXOR(NON_NULL_BYTES32), }, }, }, }, parameters: [ { - focus: true, // GET THE ABOVE WORKING PLS + focus: true, name: 'success case', steps: [ { @@ -102,6 +102,13 @@ const test_ovmSETSTORAGEFunctionality: TestDefinition = { }, ], }, + ], + }, + { + name: 'ovmSETSTORAGE -- unauthorized case', + focus: true, + preState: verifiedUpgradePreState, + parameters: [ { focus: true, name: 'unauthorized case', 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" From c7240c47e967f8e85cd74f9b9bef557a5b7a7996 Mon Sep 17 00:00:00 2001 From: Ben Jones Date: Thu, 1 Apr 2021 20:09:50 -0700 Subject: [PATCH 30/32] finish unit tests --- .../OVM/execution/OVM_ExecutionManager.sol | 1 - .../OVM/execution/OVM_StateManager.sol | 3 +- .../OVM_ExecutionManager/nuisance-gas.spec.ts | 2 +- .../OVM_ExecutionManager/ovmCREATE.spec.ts | 2 +- .../OVM_ExecutionManager/ovmSETCODE.spec.ts | 188 ++++++++++++++++++ .../ovmSETSTORAGE.spec.ts | 28 +-- test/helpers/constants.ts | 17 +- test/helpers/test-runner/test.types.ts | 6 +- 8 files changed, 220 insertions(+), 27 deletions(-) create mode 100644 test/contracts/OVM/execution/OVM_ExecutionManager/ovmSETCODE.spec.ts diff --git a/contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol b/contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol index 04d5ddb61..bd0d053f1 100644 --- a/contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol +++ b/contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol @@ -45,7 +45,6 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver { onlyCallableBy(address(0x4200000000000000000000000000000000000009)) { _checkAccountLoad(_address); - // TODO: enforce checkAccountLoad etc etc ovmStateManager.putAccountCode(_address, _code); } diff --git a/contracts/optimistic-ethereum/OVM/execution/OVM_StateManager.sol b/contracts/optimistic-ethereum/OVM/execution/OVM_StateManager.sol index e5d76c18e..fc3f7318a 100644 --- a/contracts/optimistic-ethereum/OVM/execution/OVM_StateManager.sol +++ b/contracts/optimistic-ethereum/OVM/execution/OVM_StateManager.sol @@ -156,7 +156,8 @@ contract OVM_StateManager is iOVM_StateManager { public authenticated { - // TODO: implement me + Lib_OVMCodec.Account storage account = accounts[_address]; + account.codeHash = keccak256(_code); } /** 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 43dd50deb..42f723a4e 100644 --- a/test/contracts/OVM/execution/OVM_ExecutionManager/ovmCREATE.spec.ts +++ b/test/contracts/OVM/execution/OVM_ExecutionManager/ovmCREATE.spec.ts @@ -756,7 +756,7 @@ const test_ovmCREATE: TestDefinition = { expectedReturnValue: { address: ethers.constants.AddressZero, revertData: encodeSolidityError( - 'Constrcutor attempted to deploy unsafe opcodes.' + 'Constructor attempted to deploy unsafe bytecode.' ), }, }, 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 index bc4147626..c54efb41f 100644 --- a/test/contracts/OVM/execution/OVM_ExecutionManager/ovmSETSTORAGE.spec.ts +++ b/test/contracts/OVM/execution/OVM_ExecutionManager/ovmSETSTORAGE.spec.ts @@ -65,7 +65,6 @@ const test_ovmSETSTORAGEFunctionality: TestDefinition = { subTests: [ { name: 'ovmSETSTORAGE -- success case', - focus: true, preState: verifiedUpgradePreState, postState: { StateManager: { @@ -78,7 +77,6 @@ const test_ovmSETSTORAGEFunctionality: TestDefinition = { }, parameters: [ { - focus: true, name: 'success case', steps: [ { @@ -92,7 +90,7 @@ const test_ovmSETSTORAGEFunctionality: TestDefinition = { functionParams: { address: UPGRADED_ADDRESS, key: NON_NULL_BYTES32, - value: NON_NULL_BYTES32 + value: NON_NULL_BYTES32, }, expectedReturnStatus: true, }, @@ -106,11 +104,9 @@ const test_ovmSETSTORAGEFunctionality: TestDefinition = { }, { name: 'ovmSETSTORAGE -- unauthorized case', - focus: true, preState: verifiedUpgradePreState, parameters: [ { - focus: true, name: 'unauthorized case', steps: [ { @@ -124,12 +120,12 @@ const test_ovmSETSTORAGEFunctionality: TestDefinition = { functionParams: { address: UPGRADED_ADDRESS, key: NON_NULL_BYTES32, - value: NON_NULL_BYTES32 + value: NON_NULL_BYTES32, }, expectedReturnStatus: false, expectedReturnValue: { flag: REVERT_FLAGS.CALLER_NOT_ALLOWED, - onlyValidateFlag: true + onlyValidateFlag: true, }, }, ], @@ -137,7 +133,7 @@ const test_ovmSETSTORAGEFunctionality: TestDefinition = { expectedReturnStatus: false, }, ], - } + }, ], }, ], @@ -149,10 +145,8 @@ const test_ovmSETSTORAGEAccess: TestDefinition = { subTests: [ { name: 'ovmSETSTORAGE (UNVERIFIED_ACCOUNT)', - focus: true, parameters: [ { - focus: true, name: 'ovmSETSTORAGE with a missing account', expectInvalidStateAccess: true, steps: [ @@ -167,12 +161,12 @@ const test_ovmSETSTORAGEAccess: TestDefinition = { functionParams: { address: UPGRADED_ADDRESS, key: NON_NULL_BYTES32, - value: NON_NULL_BYTES32 + value: NON_NULL_BYTES32, }, expectedReturnStatus: false, expectedReturnValue: { flag: REVERT_FLAGS.INVALID_STATE_ACCESS, - onlyValidateFlag: true + onlyValidateFlag: true, }, }, ], @@ -180,7 +174,7 @@ const test_ovmSETSTORAGEAccess: TestDefinition = { expectedReturnStatus: false, expectedReturnValue: { flag: REVERT_FLAGS.INVALID_STATE_ACCESS, - onlyValidateFlag: true + onlyValidateFlag: true, }, }, ], @@ -189,7 +183,6 @@ const test_ovmSETSTORAGEAccess: TestDefinition = { }, { name: 'ovmSETSTORAGE (UNVERIFIED_SLOT)', - focus: true, preState: { StateManager: { accounts: { @@ -202,7 +195,6 @@ const test_ovmSETSTORAGEAccess: TestDefinition = { }, parameters: [ { - focus: true, name: 'ovmSETSTORAGE with a missing storage slot', expectInvalidStateAccess: true, steps: [ @@ -217,12 +209,12 @@ const test_ovmSETSTORAGEAccess: TestDefinition = { functionParams: { address: UPGRADED_ADDRESS, key: NON_NULL_BYTES32, - value: NON_NULL_BYTES32 + value: NON_NULL_BYTES32, }, expectedReturnStatus: false, expectedReturnValue: { flag: REVERT_FLAGS.INVALID_STATE_ACCESS, - onlyValidateFlag: true + onlyValidateFlag: true, }, }, ], @@ -230,7 +222,7 @@ const test_ovmSETSTORAGEAccess: TestDefinition = { expectedReturnStatus: false, expectedReturnValue: { flag: REVERT_FLAGS.INVALID_STATE_ACCESS, - onlyValidateFlag: true + onlyValidateFlag: true, }, }, ], diff --git a/test/helpers/constants.ts b/test/helpers/constants.ts index 9c873399d..29717b947 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,18 @@ 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 { + 1 +} + +export const Helper_TestRunner_BYTELEN = len export const STORAGE_XOR = '0xfeedfacecafebeeffeedfacecafebeeffeedfacecafebeeffeedfacecafebeef' diff --git a/test/helpers/test-runner/test.types.ts b/test/helpers/test-runner/test.types.ts index 91bedef97..f6e4c70ab 100644 --- a/test/helpers/test-runner/test.types.ts +++ b/test/helpers/test-runner/test.types.ts @@ -172,7 +172,7 @@ export interface TestStep_Run { export interface TestStep_SETCODE { functionName: 'ovmSETCODE' functionParams: { - address: string, + address: string code: string } expectedReturnStatus: boolean @@ -182,8 +182,8 @@ export interface TestStep_SETCODE { export interface TestStep_SETSTORAGE { functionName: 'ovmSETSTORAGE' functionParams: { - address: string, - key: string, + address: string + key: string value: string } expectedReturnStatus: boolean From 843d684a996bc7b28426b372aa9ec0365376c67e Mon Sep 17 00:00:00 2001 From: Ben Jones Date: Thu, 1 Apr 2021 20:34:12 -0700 Subject: [PATCH 31/32] clean up EM --- .../OVM/execution/OVM_ExecutionManager.sol | 76 +++++++++++-------- 1 file changed, 46 insertions(+), 30 deletions(-) diff --git a/contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol b/contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol index bd0d053f1..59b91b2c7 100644 --- a/contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol +++ b/contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol @@ -35,36 +35,6 @@ import { OVM_DeployerWhitelist } from "../predeploys/OVM_DeployerWhitelist.sol"; * Runtime target: EVM */ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver { - - function ovmSETCODE( - address _address, - bytes memory _code - ) - override - external - onlyCallableBy(address(0x4200000000000000000000000000000000000009)) - { - _checkAccountLoad(_address); - ovmStateManager.putAccountCode(_address, _code); - } - - function ovmSETSTORAGE( - address _address, - bytes32 _key, - bytes32 _value - ) - override - external - onlyCallableBy(address(0x4200000000000000000000000000000000000009)) - { - _putContractStorage( - _address, - _key, - _value - ); - } - - /******************************** * External Contract References * ********************************/ @@ -1857,6 +1827,52 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver { messageRecord.nuisanceGasLeft = 0; } + + /********************* + * Upgrade Functions * + *********************/ + + /** + * Sets the code of an ovm contract. + * @param _address Gas metadata key to set. + * @param _value Value to store at the given key. + */ + 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 * *****************************/ From 29c3d2a93e5be02633d98664e37480047a7e010e Mon Sep 17 00:00:00 2001 From: Ben Jones Date: Mon, 5 Apr 2021 12:00:37 -0700 Subject: [PATCH 32/32] address PR feedback --- .../OVM/execution/OVM_ExecutionManager.sol | 7 +++--- .../OVM/precompiles/OVM_Upgrader.sol | 20 ---------------- .../OVM/predeploys/OVM_Upgrader.sol | 13 +--------- .../Lib_SafeExecutionManagerWrapper.sol | 24 ++++++++++++++++--- test/helpers/constants.ts | 4 +--- 5 files changed, 27 insertions(+), 41 deletions(-) delete mode 100644 contracts/optimistic-ethereum/OVM/precompiles/OVM_Upgrader.sol diff --git a/contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol b/contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol index 59b91b2c7..df034e95d 100644 --- a/contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol +++ b/contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol @@ -35,6 +35,7 @@ import { OVM_DeployerWhitelist } from "../predeploys/OVM_DeployerWhitelist.sol"; * Runtime target: EVM */ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver { + /******************************** * External Contract References * ********************************/ @@ -142,7 +143,7 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver { } /** - * Only allows the given OVM contract to call the EM function. + * Modifies a function so that only a given address can call it. */ modifier onlyCallableBy( address _allowed @@ -1834,8 +1835,8 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver { /** * Sets the code of an ovm contract. - * @param _address Gas metadata key to set. - * @param _value Value to store at the given key. + * @param _address Address to update the code of. + * @param _code Bytecode to put into the ovm account. */ function ovmSETCODE( address _address, diff --git a/contracts/optimistic-ethereum/OVM/precompiles/OVM_Upgrader.sol b/contracts/optimistic-ethereum/OVM/precompiles/OVM_Upgrader.sol deleted file mode 100644 index 4407f6a4a..000000000 --- a/contracts/optimistic-ethereum/OVM/precompiles/OVM_Upgrader.sol +++ /dev/null @@ -1,20 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity >0.5.0 <0.8.0; - -/* Library Imports */ -import { Lib_BytesUtils } from "../../libraries/utils/Lib_BytesUtils.sol"; -import { Lib_OVMCodec } from "../../libraries/codec/Lib_OVMCodec.sol"; -import { Lib_ECDSAUtils } from "../../libraries/utils/Lib_ECDSAUtils.sol"; -import { Lib_SafeExecutionManagerWrapper } from "../../libraries/wrappers/Lib_SafeExecutionManagerWrapper.sol"; - -contract OVM_Upgrader { - // Account which can initiate upgrades on L1 by approving a hash onion. - address public l1UpgradeAuthorizer; - - function doUpgrade( - address _address, - bytes memory _code - ) external { - Lib_SafeExecutionManagerWrapper.safeUPGRADE(_address, _code); - } -} diff --git a/contracts/optimistic-ethereum/OVM/predeploys/OVM_Upgrader.sol b/contracts/optimistic-ethereum/OVM/predeploys/OVM_Upgrader.sol index 4407f6a4a..8eda6ee3a 100644 --- a/contracts/optimistic-ethereum/OVM/predeploys/OVM_Upgrader.sol +++ b/contracts/optimistic-ethereum/OVM/predeploys/OVM_Upgrader.sol @@ -2,19 +2,8 @@ pragma solidity >0.5.0 <0.8.0; /* Library Imports */ -import { Lib_BytesUtils } from "../../libraries/utils/Lib_BytesUtils.sol"; -import { Lib_OVMCodec } from "../../libraries/codec/Lib_OVMCodec.sol"; -import { Lib_ECDSAUtils } from "../../libraries/utils/Lib_ECDSAUtils.sol"; import { Lib_SafeExecutionManagerWrapper } from "../../libraries/wrappers/Lib_SafeExecutionManagerWrapper.sol"; contract OVM_Upgrader { - // Account which can initiate upgrades on L1 by approving a hash onion. - address public l1UpgradeAuthorizer; - - function doUpgrade( - address _address, - bytes memory _code - ) external { - Lib_SafeExecutionManagerWrapper.safeUPGRADE(_address, _code); - } + // TODO: implement me } diff --git a/contracts/optimistic-ethereum/libraries/wrappers/Lib_SafeExecutionManagerWrapper.sol b/contracts/optimistic-ethereum/libraries/wrappers/Lib_SafeExecutionManagerWrapper.sol index 8e8b509f2..8fa117730 100644 --- a/contracts/optimistic-ethereum/libraries/wrappers/Lib_SafeExecutionManagerWrapper.sol +++ b/contracts/optimistic-ethereum/libraries/wrappers/Lib_SafeExecutionManagerWrapper.sol @@ -323,9 +323,9 @@ library Lib_SafeExecutionManagerWrapper { } /** - * Performs a safe asdfasdfasdfasdf call. + * Performs a safe ovmSETCODE call. */ - function safeUPGRADE( + function safeSETCODE( address _address, bytes memory _code ) @@ -333,7 +333,25 @@ library Lib_SafeExecutionManagerWrapper { { _safeExecutionManagerInteraction( abi.encodeWithSignature( - "ovmUPGRADECODE(address,bytes)", + "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 ) diff --git a/test/helpers/constants.ts b/test/helpers/constants.ts index 29717b947..ad995056b 100644 --- a/test/helpers/constants.ts +++ b/test/helpers/constants.ts @@ -45,9 +45,7 @@ try { len = fromHexString( getContractDefinition('Helper_TestRunner').deployedBytecode ).byteLength -} catch { - 1 -} +} catch {} export const Helper_TestRunner_BYTELEN = len