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
......
...@@ -7,6 +7,7 @@ pragma experimental ABIEncoderV2; ...@@ -7,6 +7,7 @@ pragma experimental ABIEncoderV2;
import { Lib_OVMCodec } from "../../libraries/codec/Lib_OVMCodec.sol"; import { Lib_OVMCodec } from "../../libraries/codec/Lib_OVMCodec.sol";
import { Lib_AddressResolver } from "../../libraries/resolver/Lib_AddressResolver.sol"; import { Lib_AddressResolver } from "../../libraries/resolver/Lib_AddressResolver.sol";
import { Lib_EthUtils } from "../../libraries/utils/Lib_EthUtils.sol"; import { Lib_EthUtils } from "../../libraries/utils/Lib_EthUtils.sol";
import { Lib_ErrorUtils } from "../../libraries/utils/Lib_ErrorUtils.sol";
/* Interface Imports */ /* Interface Imports */
import { iOVM_ExecutionManager } from "../../iOVM/execution/iOVM_ExecutionManager.sol"; import { iOVM_ExecutionManager } from "../../iOVM/execution/iOVM_ExecutionManager.sol";
...@@ -364,7 +365,8 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver { ...@@ -364,7 +365,8 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver {
/** /**
* @notice Overrides CREATE. * @notice Overrides CREATE.
* @param _bytecode Code to be used to CREATE a new contract. * @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, if and only if the creation threw an exception.
*/ */
function ovmCREATE( function ovmCREATE(
bytes memory _bytecode bytes memory _bytecode
...@@ -374,7 +376,8 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver { ...@@ -374,7 +376,8 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver {
notStatic notStatic
fixedGasDiscount(40000) fixedGasDiscount(40000)
returns ( returns (
address _contract address,
bytes memory
) )
{ {
// Creator is always the current ADDRESS. // Creator is always the current ADDRESS.
...@@ -400,7 +403,8 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver { ...@@ -400,7 +403,8 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver {
* @notice Overrides CREATE2. * @notice Overrides CREATE2.
* @param _bytecode Code to be used to CREATE2 a new contract. * @param _bytecode Code to be used to CREATE2 a new contract.
* @param _salt Value used to determine the contract's address. * @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, if and only if the creation threw an exception.
*/ */
function ovmCREATE2( function ovmCREATE2(
bytes memory _bytecode, bytes memory _bytecode,
...@@ -411,7 +415,8 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver { ...@@ -411,7 +415,8 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver {
notStatic notStatic
fixedGasDiscount(40000) fixedGasDiscount(40000)
returns ( returns (
address _contract address,
bytes memory
) )
{ {
// Creator is always the current ADDRESS. // Creator is always the current ADDRESS.
...@@ -769,90 +774,6 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver { ...@@ -769,90 +774,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 * * Public Functions: Execution Context *
***************************************/ ***************************************/
...@@ -873,7 +794,7 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver { ...@@ -873,7 +794,7 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver {
********************************************/ ********************************************/
/** /**
* Checks whether the given address is on the whitelst to ovmCREATE/ovmCREATE2, and reverts if not. * Checks whether the given address is on the whitelist to ovmCREATE/ovmCREATE2, and reverts if not.
* @param _deployerAddress Address attempting to deploy a contract. * @param _deployerAddress Address attempting to deploy a contract.
*/ */
function _checkDeployerAllowed( function _checkDeployerAllowed(
...@@ -881,7 +802,7 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver { ...@@ -881,7 +802,7 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver {
) )
internal internal
{ {
// From an OVM semanitcs perspectibe, this will appear the identical to // From an OVM semantics perspective, this will appear identical to
// the deployer ovmCALLing the whitelist. This is fine--in a sense, we are forcing them to. // the deployer ovmCALLing the whitelist. This is fine--in a sense, we are forcing them to.
(bool success, bytes memory data) = ovmCALL( (bool success, bytes memory data) = ovmCALL(
gasleft(), gasleft(),
...@@ -903,7 +824,8 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver { ...@@ -903,7 +824,8 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver {
* Creates a new contract and associates it with some contract address. * Creates a new contract and associates it with some contract address.
* @param _contractAddress Address to associate the created contract with. * @param _contractAddress Address to associate the created contract with.
* @param _bytecode Bytecode to be used to create the contract. * @param _bytecode Bytecode to be used to create the contract.
* @return _created Final OVM contract address. * @return Final OVM contract address.
* @return Revertdata, if and only if the creation threw an exception.
*/ */
function _createContract( function _createContract(
address _contractAddress, address _contractAddress,
...@@ -911,7 +833,8 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver { ...@@ -911,7 +833,8 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver {
) )
internal internal
returns ( returns (
address _created address,
bytes memory
) )
{ {
// We always update the nonce of the creating account, even if the creation fails. // We always update the nonce of the creating account, even if the creation fails.
...@@ -923,25 +846,21 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver { ...@@ -923,25 +846,21 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver {
nextMessageContext.ovmCALLER = messageContext.ovmADDRESS; nextMessageContext.ovmCALLER = messageContext.ovmADDRESS;
nextMessageContext.ovmADDRESS = _contractAddress; nextMessageContext.ovmADDRESS = _contractAddress;
// Run `safeCREATE` in a new EVM message so that our changes can be reflected even if // Run the common logic which occurs between call-type and create-type messages,
// `safeCREATE` reverts. // passing in the creation bytecode and `true` to trigger create-specific logic.
(bool _success, ) = _handleExternalInteraction( (bool success, bytes memory data) = _handleExternalMessage(
nextMessageContext, nextMessageContext,
gasleft(), gasleft(),
address(this), _contractAddress,
abi.encodeWithSignature( _bytecode,
"safeCREATE(address,bytes)", true
_contractAddress,
_bytecode
)
); );
// 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. // Yellow paper requires that address returned is zero if the contract deployment fails.
return _success ? _contractAddress : address(0); return (
success ? _contractAddress : address(0),
data
);
} }
/** /**
...@@ -971,6 +890,7 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver { ...@@ -971,6 +890,7 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver {
(uint256(_contract) & uint256(0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000)) (uint256(_contract) & uint256(0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000))
== uint256(0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000) == uint256(0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000)
) { ) {
// EVM does not return data in the success case, see: https://github.com/ethereum/go-ethereum/blob/aae7660410f0ef90279e14afaaf2f429fdc2a186/core/vm/instructions.go#L600-L604
return (true, hex''); return (true, hex'');
} }
...@@ -980,33 +900,38 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver { ...@@ -980,33 +900,38 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver {
? _contract ? _contract
: _getAccountEthAddress(_contract); : _getAccountEthAddress(_contract);
return _handleExternalInteraction( return _handleExternalMessage(
_nextMessageContext, _nextMessageContext,
_gasLimit, _gasLimit,
codeContractAddress, codeContractAddress,
_calldata _calldata,
false
); );
} }
/** /**
* Handles the logic of making an external call and parsing revert information. * Handles all interactions which involve the execution manager calling out to untrusted code (both calls and creates).
* @param _nextMessageContext Message context to be used for the call. * Ensures that OVM-related measures are enforced, including L2 gas refunds, nuisance gas, and flagged reversions.
* @param _gasLimit Amount of gas to be passed into this call. *
* @param _target Address of the contract to call. * @param _nextMessageContext Message context to be used for the external message.
* @param _data Data to send along with the call. * @param _gasLimit Amount of gas to be passed into this message.
* @return _success Whether or not the call returned (rather than reverted). * @param _contract OVM address being called or deployed to
* @return _returndata Data returned by the call. * @param _data Data for the message (either calldata or creation code)
* @param _isCreate Whether this is a create-type message.
* @return Whether or not the message (either a call or deployment) succeeded.
* @return Data returned by the message.
*/ */
function _handleExternalInteraction( function _handleExternalMessage(
MessageContext memory _nextMessageContext, MessageContext memory _nextMessageContext,
uint256 _gasLimit, uint256 _gasLimit,
address _target, address _contract,
bytes memory _data bytes memory _data,
bool _isCreate
) )
internal internal
returns ( returns (
bool _success, bool,
bytes memory _returndata bytes memory
) )
{ {
// We need to switch over to our next message context for the duration of this call. // We need to switch over to our next message context for the duration of this call.
...@@ -1022,11 +947,15 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver { ...@@ -1022,11 +947,15 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver {
messageRecord.nuisanceGasLeft = nuisanceGasLimit; messageRecord.nuisanceGasLeft = nuisanceGasLimit;
// Make the call and make sure to pass in the gas limit. Another instance of hidden // 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 // complexity. `_contract` is guaranteed to be a safe contract, meaning its return/revert
// behavior can be controlled. In particular, we enforce that flags are passed through // 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 // revert data as to retrieve execution metadata that would normally be reverted out of
// existence. // existence.
(bool success, bytes memory returndata) = _target.call{gas: _gasLimit}(_data);
(bool success, bytes memory returndata) =
_isCreate
? _handleContractCreation(_gasLimit, _data, _contract)
: _contract.call{gas: _gasLimit}(_data);
// Switch back to the original message context now that we're out of the call. // Switch back to the original message context now that we're out of the call.
_switchMessageContext(_nextMessageContext, prevMessageContext); _switchMessageContext(_nextMessageContext, prevMessageContext);
...@@ -1066,7 +995,10 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver { ...@@ -1066,7 +995,10 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver {
// INTENTIONAL_REVERT needs to pass up the user-provided return data encoded into the // 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. // flag, *not* the full encoded flag. All other revert types return no data.
if (flag == RevertFlag.INTENTIONAL_REVERT) { if (
flag == RevertFlag.INTENTIONAL_REVERT
|| _isCreate
) {
returndata = returndataFromFlag; returndata = returndataFromFlag;
} else { } else {
returndata = hex''; returndata = hex'';
...@@ -1089,6 +1021,99 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver { ...@@ -1089,6 +1021,99 @@ 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 Whether or not the call succeeded.
* @return If creation fails: revert data. Otherwise: empty.
*/
function _handleContractCreation(
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) {
// Note: in the EVM, this case burns all allotted gas. For improved
// developer experience, we do return the remaining ones.
return (
false,
_encodeRevertData(
RevertFlag.CREATE_COLLISION,
Lib_ErrorUtils.encodeRevertString("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,
Lib_ErrorUtils.encodeRevertString("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,
Lib_ErrorUtils.encodeRevertString("Constructor attempted to deploy 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)
);
// Successful deployments will not give access to returndata, in both the EVM and the OVM.
return (true, hex'');
}
/****************************************** /******************************************
* Internal Functions: State Manipulation * * Internal Functions: State Manipulation *
...@@ -1426,7 +1451,6 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver { ...@@ -1426,7 +1451,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. // Out of gas and create exceptions will fundamentally return no data, so simulating it shouldn't either.
if ( if (
_flag == RevertFlag.OUT_OF_GAS _flag == RevertFlag.OUT_OF_GAS
|| _flag == RevertFlag.CREATE_EXCEPTION
) { ) {
return bytes(''); return bytes('');
} }
...@@ -1494,27 +1518,8 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver { ...@@ -1494,27 +1518,8 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver {
bytes memory _data bytes memory _data
) )
internal 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( bytes memory revertdata = _encodeRevertData(
_flag, _flag,
_data _data
...@@ -1805,7 +1810,6 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver { ...@@ -1805,7 +1810,6 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver {
messageContext.isStatic = false; messageContext.isStatic = false;
messageRecord.nuisanceGasLeft = 0; messageRecord.nuisanceGasLeft = 0;
messageRecord.revertFlag = RevertFlag.DID_NOT_REVERT;
} }
/***************************** /*****************************
...@@ -1834,17 +1838,18 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver { ...@@ -1834,17 +1838,18 @@ contract OVM_ExecutionManager is iOVM_ExecutionManager, Lib_AddressResolver {
ovmStateManager = _ovmStateManager; ovmStateManager = _ovmStateManager;
_initContext(_transaction); _initContext(_transaction);
messageRecord.nuisanceGasLeft = uint(-1); messageRecord.nuisanceGasLeft = uint(-1);
messageContext.ovmADDRESS = _from; messageContext.ovmADDRESS = _from;
bool isCreate = _transaction.entrypoint == address(0); bool isCreate = _transaction.entrypoint == address(0);
if (isCreate) { if (isCreate) {
address created = ovmCREATE(_transaction.data); (address created, bytes memory revertData) = ovmCREATE(_transaction.data);
if (created == address(0)) { if (created == address(0)) {
return (false, hex""); return (false, revertData);
} else { } else {
// The eth_call RPC endpoint for to = undefined will return the deployed bytecode
// in the success case, differing from standard create messages.
return (true, Lib_EthUtils.getCode(created)); return (true, Lib_EthUtils.getCode(created));
} }
} else { } else {
......
...@@ -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,
......
...@@ -7,11 +7,13 @@ import { ...@@ -7,11 +7,13 @@ import {
NON_NULL_BYTES32, NON_NULL_BYTES32,
REVERT_FLAGS, REVERT_FLAGS,
DUMMY_BYTECODE, DUMMY_BYTECODE,
UNSAFE_BYTECODE,
ZERO_ADDRESS, ZERO_ADDRESS,
VERIFIED_EMPTY_CONTRACT_HASH, VERIFIED_EMPTY_CONTRACT_HASH,
DUMMY_BYTECODE_BYTELEN, DUMMY_BYTECODE_BYTELEN,
DUMMY_BYTECODE_HASH, DUMMY_BYTECODE_HASH,
getStorageXOR, getStorageXOR,
encodeSolidityError,
} from '../../../../helpers' } from '../../../../helpers'
const CREATED_CONTRACT_1 = '0x2bda4a99d5be88609d23b1e4ab5d1d34fb1c2feb' const CREATED_CONTRACT_1 = '0x2bda4a99d5be88609d23b1e4ab5d1d34fb1c2feb'
...@@ -68,7 +70,7 @@ const test_ovmCREATE: TestDefinition = { ...@@ -68,7 +70,7 @@ const test_ovmCREATE: TestDefinition = {
ethAddress: '0x' + '00'.repeat(20), ethAddress: '0x' + '00'.repeat(20),
}, },
[CREATED_CONTRACT_BY_2_2]: { [CREATED_CONTRACT_BY_2_2]: {
codeHash: '0x' + '01'.repeat(32), codeHash: VERIFIED_EMPTY_CONTRACT_HASH,
ethAddress: '0x' + '00'.repeat(20), ethAddress: '0x' + '00'.repeat(20),
}, },
[NESTED_CREATED_CONTRACT]: { [NESTED_CREATED_CONTRACT]: {
...@@ -79,6 +81,7 @@ const test_ovmCREATE: TestDefinition = { ...@@ -79,6 +81,7 @@ const test_ovmCREATE: TestDefinition = {
contractStorage: { contractStorage: {
$DUMMY_OVM_ADDRESS_2: { $DUMMY_OVM_ADDRESS_2: {
[NULL_BYTES32]: getStorageXOR(NULL_BYTES32), [NULL_BYTES32]: getStorageXOR(NULL_BYTES32),
[NON_NULL_BYTES32]: getStorageXOR(NULL_BYTES32),
}, },
}, },
verifiedContractStorage: { verifiedContractStorage: {
...@@ -87,6 +90,7 @@ const test_ovmCREATE: TestDefinition = { ...@@ -87,6 +90,7 @@ const test_ovmCREATE: TestDefinition = {
}, },
$DUMMY_OVM_ADDRESS_2: { $DUMMY_OVM_ADDRESS_2: {
[NULL_BYTES32]: true, [NULL_BYTES32]: true,
[NON_NULL_BYTES32]: true,
}, },
}, },
}, },
...@@ -167,20 +171,24 @@ const test_ovmCREATE: TestDefinition = { ...@@ -167,20 +171,24 @@ const test_ovmCREATE: TestDefinition = {
{ {
functionName: 'ovmREVERT', functionName: 'ovmREVERT',
revertData: DUMMY_REVERT_DATA, revertData: DUMMY_REVERT_DATA,
expectedReturnStatus: true, expectedReturnStatus: false,
expectedReturnValue: '0x00', expectedReturnValue: {
flag: REVERT_FLAGS.INTENTIONAL_REVERT,
onlyValidateFlag: true,
},
}, },
], ],
}, },
expectedReturnStatus: true, expectedReturnStatus: true,
expectedReturnValue: ZERO_ADDRESS, expectedReturnValue: {
address: ZERO_ADDRESS,
revertData: DUMMY_REVERT_DATA,
},
}, },
], ],
}, },
{ {
name: 'ovmCREATE => ovmREVERT, ovmEXTCODESIZE(CREATED)', name: 'ovmCREATE => ovmREVERT, ovmEXTCODESIZE(CREATED)',
// TODO: Appears to be failing because of a bug in smock.
skip: true,
steps: [ steps: [
{ {
functionName: 'ovmCREATE', functionName: 'ovmCREATE',
...@@ -189,13 +197,19 @@ const test_ovmCREATE: TestDefinition = { ...@@ -189,13 +197,19 @@ const test_ovmCREATE: TestDefinition = {
{ {
functionName: 'ovmREVERT', functionName: 'ovmREVERT',
revertData: DUMMY_REVERT_DATA, revertData: DUMMY_REVERT_DATA,
expectedReturnStatus: true, expectedReturnStatus: false,
expectedReturnValue: '0x00', expectedReturnValue: {
flag: REVERT_FLAGS.INTENTIONAL_REVERT,
onlyValidateFlag: true,
},
}, },
], ],
}, },
expectedReturnStatus: true, expectedReturnStatus: true,
expectedReturnValue: ZERO_ADDRESS, expectedReturnValue: {
address: ZERO_ADDRESS,
revertData: DUMMY_REVERT_DATA,
},
}, },
{ {
functionName: 'ovmEXTCODESIZE', functionName: 'ovmEXTCODESIZE',
...@@ -209,8 +223,6 @@ const test_ovmCREATE: TestDefinition = { ...@@ -209,8 +223,6 @@ const test_ovmCREATE: TestDefinition = {
}, },
{ {
name: 'ovmCREATE => ovmREVERT, ovmEXTCODEHASH(CREATED)', name: 'ovmCREATE => ovmREVERT, ovmEXTCODEHASH(CREATED)',
// TODO: Appears to be failing because of a bug in smock.
skip: true,
steps: [ steps: [
{ {
functionName: 'ovmCREATE', functionName: 'ovmCREATE',
...@@ -219,13 +231,19 @@ const test_ovmCREATE: TestDefinition = { ...@@ -219,13 +231,19 @@ const test_ovmCREATE: TestDefinition = {
{ {
functionName: 'ovmREVERT', functionName: 'ovmREVERT',
revertData: DUMMY_REVERT_DATA, revertData: DUMMY_REVERT_DATA,
expectedReturnStatus: true, expectedReturnStatus: false,
expectedReturnValue: '0x00', expectedReturnValue: {
flag: REVERT_FLAGS.INTENTIONAL_REVERT,
onlyValidateFlag: true,
},
}, },
], ],
}, },
expectedReturnStatus: true, expectedReturnStatus: true,
expectedReturnValue: ZERO_ADDRESS, expectedReturnValue: {
address: ZERO_ADDRESS,
revertData: DUMMY_REVERT_DATA,
},
}, },
{ {
functionName: 'ovmEXTCODEHASH', functionName: 'ovmEXTCODEHASH',
...@@ -239,8 +257,6 @@ const test_ovmCREATE: TestDefinition = { ...@@ -239,8 +257,6 @@ const test_ovmCREATE: TestDefinition = {
}, },
{ {
name: 'ovmCREATE => ovmREVERT, ovmEXTCODECOPY(CREATED)', name: 'ovmCREATE => ovmREVERT, ovmEXTCODECOPY(CREATED)',
// TODO: Appears to be failing because of a bug in smock.
skip: true,
steps: [ steps: [
{ {
functionName: 'ovmCREATE', functionName: 'ovmCREATE',
...@@ -249,13 +265,19 @@ const test_ovmCREATE: TestDefinition = { ...@@ -249,13 +265,19 @@ const test_ovmCREATE: TestDefinition = {
{ {
functionName: 'ovmREVERT', functionName: 'ovmREVERT',
revertData: DUMMY_REVERT_DATA, revertData: DUMMY_REVERT_DATA,
expectedReturnStatus: true, expectedReturnStatus: false,
expectedReturnValue: '0x00', expectedReturnValue: {
flag: REVERT_FLAGS.INTENTIONAL_REVERT,
onlyValidateFlag: true,
},
}, },
], ],
}, },
expectedReturnStatus: true, expectedReturnStatus: true,
expectedReturnValue: ZERO_ADDRESS, expectedReturnValue: {
address: ZERO_ADDRESS,
revertData: DUMMY_REVERT_DATA,
},
}, },
{ {
functionName: 'ovmEXTCODECOPY', functionName: 'ovmEXTCODECOPY',
...@@ -472,10 +494,10 @@ const test_ovmCREATE: TestDefinition = { ...@@ -472,10 +494,10 @@ const test_ovmCREATE: TestDefinition = {
], ],
}, },
{ {
// TODO: appears to be failing due to a smoddit issue
skip: true,
name: name:
'ovmCREATE => (ovmCALL(ADDRESS_2) => ovmSSTORE) + ovmREVERT, ovmCALL(ADDRESS_2) => ovmSLOAD', 'ovmCREATE => (ovmCALL(ADDRESS_2) => ovmSSTORE) + ovmREVERT, ovmCALL(ADDRESS_2) => ovmSLOAD',
// TODO: Appears to be failing because of a bug in smock.
skip: true,
steps: [ steps: [
{ {
functionName: 'ovmCREATE', functionName: 'ovmCREATE',
...@@ -487,10 +509,18 @@ const test_ovmCREATE: TestDefinition = { ...@@ -487,10 +509,18 @@ const test_ovmCREATE: TestDefinition = {
gasLimit: OVM_TX_GAS_LIMIT, gasLimit: OVM_TX_GAS_LIMIT,
target: '$DUMMY_OVM_ADDRESS_2', target: '$DUMMY_OVM_ADDRESS_2',
subSteps: [ subSteps: [
{
functionName: 'ovmSLOAD',
functionParams: {
key: NON_NULL_BYTES32,
},
expectedReturnStatus: true,
expectedReturnValue: NON_NULL_BYTES32,
},
{ {
functionName: 'ovmSSTORE', functionName: 'ovmSSTORE',
functionParams: { functionParams: {
key: NULL_BYTES32, key: NON_NULL_BYTES32,
value: NON_NULL_BYTES32, value: NON_NULL_BYTES32,
}, },
expectedReturnStatus: true, expectedReturnStatus: true,
...@@ -502,13 +532,19 @@ const test_ovmCREATE: TestDefinition = { ...@@ -502,13 +532,19 @@ const test_ovmCREATE: TestDefinition = {
{ {
functionName: 'ovmREVERT', functionName: 'ovmREVERT',
revertData: DUMMY_REVERT_DATA, revertData: DUMMY_REVERT_DATA,
expectedReturnStatus: true, expectedReturnStatus: false,
expectedReturnValue: '0x00', expectedReturnValue: {
flag: REVERT_FLAGS.INTENTIONAL_REVERT,
onlyValidateFlag: true,
},
}, },
], ],
}, },
expectedReturnStatus: true, expectedReturnStatus: true,
expectedReturnValue: ZERO_ADDRESS, expectedReturnValue: {
address: ZERO_ADDRESS,
revertData: DUMMY_REVERT_DATA,
},
}, },
{ {
functionName: 'ovmCALL', functionName: 'ovmCALL',
...@@ -519,10 +555,10 @@ const test_ovmCREATE: TestDefinition = { ...@@ -519,10 +555,10 @@ const test_ovmCREATE: TestDefinition = {
{ {
functionName: 'ovmSLOAD', functionName: 'ovmSLOAD',
functionParams: { functionParams: {
key: NULL_BYTES32, key: NON_NULL_BYTES32,
}, },
expectedReturnStatus: true, expectedReturnStatus: true,
expectedReturnValue: NULL_BYTES32, expectedReturnValue: NON_NULL_BYTES32,
}, },
], ],
}, },
...@@ -545,8 +581,11 @@ const test_ovmCREATE: TestDefinition = { ...@@ -545,8 +581,11 @@ const test_ovmCREATE: TestDefinition = {
target: '$DUMMY_OVM_ADDRESS_3', target: '$DUMMY_OVM_ADDRESS_3',
calldata: '0x', calldata: '0x',
}, },
expectedReturnStatus: true, expectedReturnStatus: false,
expectedReturnValue: '0x00', expectedReturnValue: {
flag: REVERT_FLAGS.INVALID_STATE_ACCESS,
onlyValidateFlag: true,
},
}, },
], ],
}, },
...@@ -558,7 +597,7 @@ const test_ovmCREATE: TestDefinition = { ...@@ -558,7 +597,7 @@ const test_ovmCREATE: TestDefinition = {
], ],
}, },
{ {
name: 'ovmCREATE => ovmCREATE => ovmCALL(ADDRESS_NONEXIST)', name: 'ovmCALL => ovmCREATE => ovmCREATE',
steps: [ steps: [
{ {
functionName: 'ovmCALL', functionName: 'ovmCALL',
...@@ -573,10 +612,10 @@ const test_ovmCREATE: TestDefinition = { ...@@ -573,10 +612,10 @@ const test_ovmCREATE: TestDefinition = {
{ {
functionName: 'ovmCREATE', functionName: 'ovmCREATE',
functionParams: { functionParams: {
bytecode: '0x', bytecode: '0x', // this will still succeed with empty bytecode
}, },
expectedReturnStatus: true, expectedReturnStatus: true,
expectedReturnValue: ZERO_ADDRESS, expectedReturnValue: CREATED_CONTRACT_BY_2_2,
}, },
], ],
}, },
...@@ -608,19 +647,26 @@ const test_ovmCREATE: TestDefinition = { ...@@ -608,19 +647,26 @@ const test_ovmCREATE: TestDefinition = {
target: '$DUMMY_OVM_ADDRESS_3', target: '$DUMMY_OVM_ADDRESS_3',
calldata: '0x', calldata: '0x',
}, },
expectedReturnStatus: true, expectedReturnStatus: false,
expectedReturnValue: '0x00', expectedReturnValue: {
flag: REVERT_FLAGS.INVALID_STATE_ACCESS,
onlyValidateFlag: true,
},
}, },
], ],
}, },
expectedReturnStatus: true, expectedReturnStatus: false,
expectedReturnValue: '0x00', expectedReturnValue: {
flag: REVERT_FLAGS.INVALID_STATE_ACCESS,
onlyValidateFlag: true,
},
}, },
], ],
}, },
expectedReturnStatus: false, expectedReturnStatus: false,
expectedReturnValue: { expectedReturnValue: {
flag: REVERT_FLAGS.INVALID_STATE_ACCESS, flag: REVERT_FLAGS.INVALID_STATE_ACCESS,
onlyValidateFlag: true,
}, },
}, },
], ],
...@@ -648,13 +694,19 @@ const test_ovmCREATE: TestDefinition = { ...@@ -648,13 +694,19 @@ const test_ovmCREATE: TestDefinition = {
{ {
functionName: 'ovmREVERT', functionName: 'ovmREVERT',
revertData: DUMMY_REVERT_DATA, revertData: DUMMY_REVERT_DATA,
expectedReturnStatus: true, expectedReturnStatus: false,
expectedReturnValue: '0x00', expectedReturnValue: {
flag: REVERT_FLAGS.INTENTIONAL_REVERT,
onlyValidateFlag: true,
},
}, },
], ],
}, },
expectedReturnStatus: true, expectedReturnStatus: true,
expectedReturnValue: ZERO_ADDRESS, expectedReturnValue: {
address: ZERO_ADDRESS,
revertData: DUMMY_REVERT_DATA,
},
}, },
], ],
}, },
...@@ -675,6 +727,24 @@ const test_ovmCREATE: TestDefinition = { ...@@ -675,6 +727,24 @@ const test_ovmCREATE: TestDefinition = {
}, },
], ],
}, },
{
name: 'ovmCREATE(UNSAFE_CODE)',
steps: [
{
functionName: 'ovmCREATE',
functionParams: {
bytecode: UNSAFE_BYTECODE,
},
expectedReturnStatus: true,
expectedReturnValue: {
address: ZERO_ADDRESS,
revertData: encodeSolidityError(
'Constructor attempted to deploy unsafe bytecode.'
),
},
},
],
},
], ],
subTests: [ subTests: [
{ {
......
...@@ -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