Commit 7a3d3ad7 authored by ben-chain's avatar ben-chain Committed by GitHub

Eliminate EM.safeCREATE logic (#308)

* new create codepath working

* get calls ported over to new function

* remove from interface

* clean up, add comments

* remove unused commented code

* fix nuisance gas

* add return type to ovmCREATE

* focus on final failing test

* finish up tests with new contract account revert type

* remove test focus

* linting

* cleanup

* add explicit unsafe bytecode test

* linting

* remove pointless ternary

* fix docstring

* fix eth_call for creates and fix contract account reversions

* use if statement instead

* nits

* Update contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol
Co-authored-by: default avatarsmartcontracts <kelvinfichter@gmail.com>

* Update contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol
Co-authored-by: default avatarsmartcontracts <kelvinfichter@gmail.com>

* Update contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol
Co-authored-by: default avatarsmartcontracts <kelvinfichter@gmail.com>

* Update contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol
Co-authored-by: default avatarsmartcontracts <kelvinfichter@gmail.com>

* Update contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol
Co-authored-by: default avatarsmartcontracts <kelvinfichter@gmail.com>

* Fix nits on comments

* More small comment fixes

* fix capitalization, add ref URL

* PR feedback

* opcodes->bytecode

* doc: explain eth_call create return type

* Update contracts/optimistic-ethereum/OVM/execution/OVM_ExecutionManager.sol
Co-authored-by: default avatarsmartcontracts <kelvinfichter@gmail.com>
Co-authored-by: default avatarMaurelian <maurelian@protonmail.ch>
parent 6693d3a8
...@@ -113,14 +113,17 @@ contract OVM_ECDSAContractAccount is iOVM_ECDSAContractAccount { ...@@ -113,14 +113,17 @@ contract OVM_ECDSAContractAccount is iOVM_ECDSAContractAccount {
// Contract creations are signalled by sending a transaction to the zero address. // Contract creations are signalled by sending a transaction to the zero address.
if (decodedTx.to == address(0)) { if (decodedTx.to == address(0)) {
address created = Lib_SafeExecutionManagerWrapper.safeCREATE( (address created, bytes memory revertData) = Lib_SafeExecutionManagerWrapper.safeCREATE(
decodedTx.gasLimit, decodedTx.gasLimit,
decodedTx.data decodedTx.data
); );
// EVM doesn't tell us whether a contract creation failed, even if it reverted during // Return true if the contract creation succeeded, false w/ revertData otherwise.
// initialization. Always return `true` for our success value here. if (created != address(0)) {
return (true, abi.encode(created)); return (true, abi.encode(created));
} else {
return (false, revertData);
}
} else { } else {
// We only want to bump the nonce for `ovmCALL` because `ovmCREATE` automatically bumps // 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 // the nonce of the calling account. Normally an EOA would bump the nonce for both
......
...@@ -11,7 +11,6 @@ interface iOVM_ExecutionManager { ...@@ -11,7 +11,6 @@ interface iOVM_ExecutionManager {
*********/ *********/
enum RevertFlag { enum RevertFlag {
DID_NOT_REVERT,
OUT_OF_GAS, OUT_OF_GAS,
INTENTIONAL_REVERT, INTENTIONAL_REVERT,
EXCEEDS_NUISANCE_GAS, EXCEEDS_NUISANCE_GAS,
...@@ -19,7 +18,6 @@ interface iOVM_ExecutionManager { ...@@ -19,7 +18,6 @@ interface iOVM_ExecutionManager {
UNSAFE_BYTECODE, UNSAFE_BYTECODE,
CREATE_COLLISION, CREATE_COLLISION,
STATIC_VIOLATION, STATIC_VIOLATION,
CREATE_EXCEPTION,
CREATOR_NOT_ALLOWED CREATOR_NOT_ALLOWED
} }
...@@ -67,7 +65,6 @@ interface iOVM_ExecutionManager { ...@@ -67,7 +65,6 @@ interface iOVM_ExecutionManager {
struct MessageRecord { struct MessageRecord {
uint256 nuisanceGasLeft; uint256 nuisanceGasLeft;
RevertFlag revertFlag;
} }
...@@ -112,8 +109,8 @@ interface iOVM_ExecutionManager { ...@@ -112,8 +109,8 @@ interface iOVM_ExecutionManager {
* Contract Creation Opcodes * * Contract Creation Opcodes *
*****************************/ *****************************/
function ovmCREATE(bytes memory _bytecode) 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); function ovmCREATE2(bytes memory _bytecode, bytes32 _salt) external returns (address _contract, bytes memory _revertdata);
/******************************* /*******************************
...@@ -151,12 +148,6 @@ interface iOVM_ExecutionManager { ...@@ -151,12 +148,6 @@ interface iOVM_ExecutionManager {
function ovmEXTCODEHASH(address _contract) external returns (bytes32 _hash); function ovmEXTCODEHASH(address _contract) external returns (bytes32 _hash);
/**************************************
* Public Functions: Execution Safety *
**************************************/
function safeCREATE(address _address, bytes memory _bytecode) external;
/*************************************** /***************************************
* Public Functions: Execution Context * * Public Functions: Execution Context *
***************************************/ ***************************************/
......
// SPDX-License-Identifier: MIT
pragma solidity >0.5.0 <0.8.0;
pragma experimental ABIEncoderV2;
/**
* @title Lib_ErrorUtils
*/
library Lib_ErrorUtils {
/**********************
* Internal Functions *
**********************/
/**
* Encodes an error string into raw solidity-style revert data.
* (i.e. ascii bytes, prefixed with bytes4(keccak("Error(string))"))
* Ref: https://docs.soliditylang.org/en/v0.8.2/control-structures.html?highlight=Error(string)#panic-via-assert-and-error-via-require
* @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
);
}
}
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity >0.5.0 <0.8.0; pragma solidity >0.5.0 <0.8.0;
/* Library Imports */
import { Lib_ErrorUtils } from "../utils/Lib_ErrorUtils.sol";
/** /**
* @title Lib_SafeExecutionManagerWrapper * @title Lib_SafeExecutionManagerWrapper
* @dev The Safe Execution Manager Wrapper provides functions which facilitate writing OVM safe * @dev The Safe Execution Manager Wrapper provides functions which facilitate writing OVM safe
...@@ -90,7 +93,8 @@ library Lib_SafeExecutionManagerWrapper { ...@@ -90,7 +93,8 @@ library Lib_SafeExecutionManagerWrapper {
) )
internal internal
returns ( returns (
address _contract address,
bytes memory
) )
{ {
bytes memory returndata = _safeExecutionManagerInteraction( bytes memory returndata = _safeExecutionManagerInteraction(
...@@ -101,7 +105,7 @@ library Lib_SafeExecutionManagerWrapper { ...@@ -101,7 +105,7 @@ library Lib_SafeExecutionManagerWrapper {
) )
); );
return abi.decode(returndata, (address)); return abi.decode(returndata, (address, bytes));
} }
/** /**
...@@ -258,8 +262,7 @@ library Lib_SafeExecutionManagerWrapper { ...@@ -258,8 +262,7 @@ library Lib_SafeExecutionManagerWrapper {
_safeExecutionManagerInteraction( _safeExecutionManagerInteraction(
abi.encodeWithSignature( abi.encodeWithSignature(
"ovmREVERT(bytes)", "ovmREVERT(bytes)",
abi.encodeWithSignature( Lib_ErrorUtils.encodeRevertString(
"Error(string)",
_reason _reason
) )
) )
......
...@@ -54,7 +54,7 @@ contract mockOVM_ECDSAContractAccount is iOVM_ECDSAContractAccount { ...@@ -54,7 +54,7 @@ contract mockOVM_ECDSAContractAccount is iOVM_ECDSAContractAccount {
// Contract creations are signalled by sending a transaction to the zero address. // Contract creations are signalled by sending a transaction to the zero address.
if (decodedTx.to == address(0)) { if (decodedTx.to == address(0)) {
address created = Lib_SafeExecutionManagerWrapper.safeCREATE( (address created, ) = Lib_SafeExecutionManagerWrapper.safeCREATE(
decodedTx.gasLimit, decodedTx.gasLimit,
decodedTx.data decodedTx.data
); );
......
...@@ -128,12 +128,8 @@ contract Helper_TestRunner { ...@@ -128,12 +128,8 @@ contract Helper_TestRunner {
} }
} }
if (success == false || (success == true && returndata.length == 1)) { if (success == false) {
assembly { assembly {
if eq(extcodesize(address()), 0) {
return(0, 1)
}
revert(add(returndata, 0x20), mload(returndata)) revert(add(returndata, 0x20), mload(returndata))
} }
} }
......
...@@ -77,9 +77,10 @@ describe('OVM_ECDSAContractAccount', () => { ...@@ -77,9 +77,10 @@ describe('OVM_ECDSAContractAccount', () => {
Mock__OVM_ExecutionManager.smocked.ovmCHAINID.will.return.with(420) Mock__OVM_ExecutionManager.smocked.ovmCHAINID.will.return.with(420)
Mock__OVM_ExecutionManager.smocked.ovmGETNONCE.will.return.with(100) Mock__OVM_ExecutionManager.smocked.ovmGETNONCE.will.return.with(100)
Mock__OVM_ExecutionManager.smocked.ovmCALL.will.return.with([true, '0x']) Mock__OVM_ExecutionManager.smocked.ovmCALL.will.return.with([true, '0x'])
Mock__OVM_ExecutionManager.smocked.ovmCREATE.will.return.with( Mock__OVM_ExecutionManager.smocked.ovmCREATE.will.return.with([
NON_ZERO_ADDRESS NON_ZERO_ADDRESS,
) '0x',
])
Mock__OVM_ExecutionManager.smocked.ovmCALLER.will.return.with( Mock__OVM_ExecutionManager.smocked.ovmCALLER.will.return.with(
NON_ZERO_ADDRESS NON_ZERO_ADDRESS
) )
......
...@@ -110,7 +110,7 @@ describe('OVM_ExecutionManager gas consumption', () => { ...@@ -110,7 +110,7 @@ describe('OVM_ExecutionManager gas consumption', () => {
) )
console.log(`calculated gas cost of ${gasCost}`) console.log(`calculated gas cost of ${gasCost}`)
const benchmark: number = 226_516 const benchmark: number = 225_500
expect(gasCost).to.be.lte(benchmark) expect(gasCost).to.be.lte(benchmark)
expect(gasCost).to.be.gte( expect(gasCost).to.be.gte(
benchmark - 1_000, benchmark - 1_000,
......
...@@ -34,14 +34,12 @@ export const decodeRevertData = (revertData: string): any => { ...@@ -34,14 +34,12 @@ export const decodeRevertData = (revertData: string): any => {
} }
export const REVERT_FLAGS = { export const REVERT_FLAGS = {
DID_NOT_REVERT: 0, OUT_OF_GAS: 0,
OUT_OF_GAS: 1, INTENTIONAL_REVERT: 1,
INTENTIONAL_REVERT: 2, EXCEEDS_NUISANCE_GAS: 2,
EXCEEDS_NUISANCE_GAS: 3, INVALID_STATE_ACCESS: 3,
INVALID_STATE_ACCESS: 4, UNSAFE_BYTECODE: 4,
UNSAFE_BYTECODE: 5, CREATE_COLLISION: 5,
CREATE_COLLISION: 6, STATIC_VIOLATION: 6,
STATIC_VIOLATION: 7, CREATOR_NOT_ALLOWED: 7,
CREATE_EXCEPTION: 8,
CREATOR_NOT_ALLOWED: 9,
} }
...@@ -3,4 +3,5 @@ import { keccak256 } from 'ethers/lib/utils' ...@@ -3,4 +3,5 @@ import { keccak256 } from 'ethers/lib/utils'
export const DUMMY_BYTECODE = '0x123412341234' export const DUMMY_BYTECODE = '0x123412341234'
export const DUMMY_BYTECODE_BYTELEN = 6 export const DUMMY_BYTECODE_BYTELEN = 6
export const UNSAFE_BYTECODE = '0x6069606955'
export const DUMMY_BYTECODE_HASH = keccak256(DUMMY_BYTECODE) export const DUMMY_BYTECODE_HASH = keccak256(DUMMY_BYTECODE)
...@@ -38,6 +38,7 @@ import { ...@@ -38,6 +38,7 @@ import {
NULL_BYTES32, NULL_BYTES32,
} from '../constants' } from '../constants'
import { getStorageXOR } from '../' import { getStorageXOR } from '../'
import { UNSAFE_BYTECODE } from '../dummy'
export class ExecutionManagerTestRunner { export class ExecutionManagerTestRunner {
private snapshot: string private snapshot: string
...@@ -194,7 +195,11 @@ export class ExecutionManagerTestRunner { ...@@ -194,7 +195,11 @@ export class ExecutionManagerTestRunner {
).deploy() ).deploy()
const MockSafetyChecker = await smockit(SafetyChecker) 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 this.contracts.OVM_SafetyChecker = MockSafetyChecker
...@@ -494,6 +499,19 @@ export class ExecutionManagerTestRunner { ...@@ -494,6 +499,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( return this.contracts.OVM_ExecutionManager.interface.encodeFunctionResult(
step.functionName, step.functionName,
returnData returnData
......
...@@ -118,7 +118,13 @@ interface TestStep_CREATE { ...@@ -118,7 +118,13 @@ interface TestStep_CREATE {
subSteps?: TestStep[] subSteps?: TestStep[]
} }
expectedReturnStatus: boolean expectedReturnStatus: boolean
expectedReturnValue: string | RevertFlagError expectedReturnValue:
| string
| {
address: string
revertData: string
}
| RevertFlagError
} }
interface TestStep_CREATE2 { interface TestStep_CREATE2 {
...@@ -129,7 +135,13 @@ interface TestStep_CREATE2 { ...@@ -129,7 +135,13 @@ interface TestStep_CREATE2 {
subSteps?: TestStep[] subSteps?: TestStep[]
} }
expectedReturnStatus: boolean expectedReturnStatus: boolean
expectedReturnValue: string | RevertFlagError expectedReturnValue:
| string
| {
address: string
revertData: string
}
| RevertFlagError
} }
interface TestStep_CREATEEOA { interface TestStep_CREATEEOA {
......
...@@ -16,3 +16,7 @@ const errorABI = new ethers.utils.Interface([ ...@@ -16,3 +16,7 @@ const errorABI = new ethers.utils.Interface([
export const decodeSolidityError = (err: string): string => { export const decodeSolidityError = (err: string): string => {
return errorABI.decodeFunctionData('Error', err)[0] return errorABI.decodeFunctionData('Error', err)[0]
} }
export const encodeSolidityError = (message: string): string => {
return errorABI.encodeFunctionData('Error', [message])
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment