Commit 8575b629 authored by Kelvin Fichter's avatar Kelvin Fichter

Added latest tests

parent 10667c03
......@@ -228,6 +228,7 @@ contract OVM_StateManager is iOVM_StateManager {
Lib_OVMCodec.Account storage account = accounts[_address];
account.nonce = 1;
account.codeHash = keccak256(hex'80');
account.isFresh = true;
}
/**
......@@ -405,9 +406,9 @@ contract OVM_StateManager is iOVM_StateManager {
bool _exists
)
{
return verifiedContractStorage[_contract][_key];
return verifiedContractStorage[_contract][_key] || accounts[_contract].isFresh;
}
/**
* Checks whether a storage slot has already been retrieved, and marks it as retrieved if not.
* @param _contract Address of the contract to check.
......
......@@ -17,7 +17,9 @@ interface iOVM_ExecutionManager {
EXCEEDS_NUISANCE_GAS,
INVALID_STATE_ACCESS,
UNSAFE_BYTECODE,
CREATE_COLLISION
CREATE_COLLISION,
STATIC_VIOLATION,
CREATE_EXCEPTION
}
enum GasMetadataKey {
......@@ -65,6 +67,7 @@ interface iOVM_ExecutionManager {
address ovmCALLER;
address ovmADDRESS;
bool isStatic;
bool isCreation;
}
struct MessageRecord {
......
......@@ -20,6 +20,7 @@ library Lib_OVMCodec {
bytes32 storageRoot;
bytes32 codeHash;
address ethAddress;
bool isFresh;
}
struct EVMAccount {
......
pragma solidity >=0.7.0;
pragma experimental ABIEncoderV2;
// note: this pattern breaks if an action leads to reversion, since the result will not be stored.
// Thus, only the final action in deployedActions can be an ovmREVERT, and the result must be checked via the callee.
contract MockOvmCodeContract {
bytes[] public EMReturnValuesInConstructor;
bytes[][] public EMReturnValuesInDeployed;
bytes[][] public callDatasForEMInDeployed;
bytes[] public returnDataForCodeContractInDeployed;
uint public callIndex = 0;
bytes public constrRet0;
bytes public constrRet1;
bytes public constCall0;
constructor(bytes[] memory _callsToEMInConstructor, bytes[][] memory _calldatasToEMInDeployed, bytes[] memory _returnDataForCodeContractInDeployed) {
require(_calldatasToEMInDeployed.length == _returnDataForCodeContractInDeployed.length, "Invalid behavior requested for mock code contract: mismatch between number of calldata batches and returndata for post-deployment behavior.");
callDatasForEMInDeployed = _calldatasToEMInDeployed;
returnDataForCodeContractInDeployed = _returnDataForCodeContractInDeployed;
bytes[] memory callsToDoNow = _callsToEMInConstructor;
bytes[] memory returnVals = doEMCalls(callsToDoNow);
constCall0 = callsToDoNow[0];
constrRet0 = returnVals[0];
constrRet1 = returnVals[1];
for (uint i = 0; i < returnVals.length; i++) {
EMReturnValuesInConstructor.push(returnVals[i]);
}
}
fallback() external {
bytes[] memory calldatas = callDatasForEMInDeployed[callIndex];
bytes[] memory returndatas = doEMCalls(calldatas);
EMReturnValuesInDeployed.push();
for (uint i = 0; i < returndatas.length; i++) {
EMReturnValuesInDeployed[callIndex].push(returndatas[i]);
}
bytes memory dataToReturn = returnDataForCodeContractInDeployed[callIndex];
callIndex++;
uint returnLength = dataToReturn.length;
assembly {
return(add(dataToReturn, 0x20), returnLength)
}
}
function doEMCalls(bytes[] memory _calldatas) internal returns(bytes[] memory) {
bytes[] memory calldatas = _calldatas;
bytes[] memory results = new bytes[](calldatas.length);
for (uint i = 0; i < calldatas.length; i++) {
bytes memory data = calldatas[i];
bytes memory result = callExecutionManager(data);
results[i] = result;
}
return results;
}
function callExecutionManager (bytes memory _data) internal returns (bytes memory actionResult) {
uint dataLength = _data.length;
uint returnedLength;
assembly {
function isContextCREATE() -> isCREATE {
isCREATE := iszero(extcodesize(address()))
}
// Note: this function is the only way that the opcodes REVERT, CALLER, EXTCODESIZE, ADDRESS can appear in a code contract which passes SafetyChecker.isBytecodeSafe().
// The static analysis enforces that the EXACT functionality below is implemented by comparing to a reference bytestring.
function doSafeExecutionManagerCall(argOff, argLen, retOffset, retLen) {
let success := call(gas(), caller(), 0, argOff, argLen, retOffset, retLen)
if iszero(success) {
mstore(0, 0x2a2a7adb00000000000000000000000000000000000000000000000000000000) // ovmREVERT(bytes) methodId
returndatacopy(4, 0, 32)
let secondsuccess := call(gas(), caller(), 0, 0, 36, 0, 0)
if iszero(secondsuccess) {
returndatacopy(0, 0, 32)
revert(0, 32)
}
// ovmREVERT will only succeed if we are in a CREATE context, in which case we abort by deploying a single byte contract.
mstore(0,0)
return(0, 1)
}
}
doSafeExecutionManagerCall(add(_data, 0x20), dataLength, 0, 0)
returnedLength := returndatasize()
}
bytes memory returned = new bytes(returnedLength);
assembly {
returndatacopy(add(returned, 0x20), 0, returndatasize())
}
return returned;
}
}
\ No newline at end of file
pragma solidity >=0.7.0;
pragma experimental ABIEncoderV2;
struct MessageSteps {
bytes[] callsToEM;
bool shouldRevert;
}
struct EMResponse {
bool success;
bytes data;
}
contract Helper_CodeContractForCalls {
function runSteps(
MessageSteps calldata _stepsToRun
) external returns(EMResponse[] memory) {
uint numSteps = _stepsToRun.callsToEM.length;
EMResponse[] memory EMResponses = new EMResponse[](numSteps);
for (uint i = 0; i < numSteps; i++) {
bytes memory dataToSend = _stepsToRun.callsToEM[i];
(bool success, bytes memory responseData) = address(msg.sender).call(dataToSend);
EMResponses[i].success = success;
EMResponses[i].data = responseData;
}
return EMResponses; // TODO: revert with this data in case of !shouldRevert
}
}
pragma solidity >=0.7.0;
pragma experimental ABIEncoderV2;
import {console} from "@nomiclabs/buidler/console.sol";
interface Helper_CodeContractDataTypes {
struct CALLResponse {
bool success;
bytes data;
}
}
contract Helper_CodeContractForCalls is Helper_CodeContractDataTypes {
bytes constant sampleCREATEData = abi.encodeWithSignature("ovmCREATE(bytes)", hex"");
bytes constant sampleCREATE2Data = abi.encodeWithSignature("ovmCREATE2(bytes,bytes32)", hex"", 0x4242424242424242424242424242424242424242424242424242424242424242);
function runSteps(
bytes[] memory callsToEM,
bool _shouldRevert,
address _createEMResponsesStorer
) public returns(CALLResponse[] memory) {
console.log("in runSteps()");
uint numSteps = callsToEM.length;
CALLResponse[] memory EMResponses = new CALLResponse[](numSteps);
for (uint i = 0; i < numSteps; i++) {
bytes memory dataToSend = callsToEM[i];
console.log("calling EM with data:");
console.logBytes(dataToSend);
(bool success, bytes memory responseData) = address(msg.sender).call(dataToSend);
console.log("step to EM had result:");
console.logBool(success);
EMResponses[i].success = success;
if (_isOVMCreateCall(dataToSend)) {
console.log("step to EM returned data:");
console.logBytes(responseData);
EMResponses[i].data = abi.encode(
responseData,
_getStoredEMREsponsesInCreate(_createEMResponsesStorer)
);
console.log("since this is create step, stored concatenation:");
console.logBytes(EMResponses[i].data);
} else {
console.log("step to EM returned data:");
console.logBytes(responseData);
EMResponses[i].data = responseData;
}
}
return EMResponses;
}
function _getStoredEMREsponsesInCreate(address _createEMResponsesStorer) internal returns(bytes memory) {
(bool success, bytes memory data) = _createEMResponsesStorer.call(abi.encodeWithSignature("getLastResponses()"));
return data;
}
function _isOVMCreateCall(bytes memory _calldata) public returns(bool) {
return (
_doMethodIdsMatch(_calldata, sampleCREATEData) || _doMethodIdsMatch(_calldata, sampleCREATE2Data)
);
}
function _doMethodIdsMatch(bytes memory _calldata1, bytes memory _calldata2) internal returns(bool) {
return (
_calldata1[0] == _calldata2[0] &&
_calldata1[1] == _calldata2[1] &&
_calldata1[2] == _calldata2[2] &&
_calldata1[3] == _calldata2[3]
);
}
}
contract Helper_CodeContractForCreates is Helper_CodeContractForCalls {
constructor(
bytes[] memory callsToEM,
bool _shouldRevert,
bytes memory _codeToDeploy,
address _createEMResponsesStorer
) {
console.log("In CREATE helper (deployment)");
CALLResponse[] memory responses = runSteps(callsToEM, _shouldRevert, _createEMResponsesStorer);
Helper_CreateEMResponsesStorer(_createEMResponsesStorer).store(responses);
uint lengthToDeploy = _codeToDeploy.length;
// todo revert if _shouldrevert
assembly {
return(add(_codeToDeploy, 0x20), lengthToDeploy)
}
}
}
contract Helper_CreateEMResponsesStorer is Helper_CodeContractDataTypes {
CALLResponse[] responses;
function store(
CALLResponse[] memory _responses
) public {
console.log("create storer helper is storing responses...");
for (uint i = 0; i < _responses.length; i++) {
responses.push();
responses[i] = _responses[i];
}
console.log("helper successfully stored this many responses:");
console.logUint(responses.length);
}
function getLastResponses() public returns(CALLResponse[] memory) {
console.log("helper is retreiving last stored responses. It has this many responses stored:");
console.logUint(responses.length);
CALLResponse[] memory toReturn = responses;
delete responses;
return toReturn;
}
}
contract Helper_CodeContractForReverts {
function doRevert(
bytes memory _revertdata
) public {
uint revertLength = _revertdata.length;
assembly {
revert(add(_revertdata, 0x20), revertLength)
}
}
}
// note: behavior of this contract covers all 3 cases of EVM message exceptions:
// - out of gas
// - INVALID opcode
// - invalid JUMPDEST
contract Helper_CodeContractForInvalid {
function doInvalid() public {
assembly {
invalid()
}
}
}
// note: behavior of this contract covers all 3 cases of EVM message exceptions:
// - out of gas
// - INVALID opcode
// - invalid JUMPDEST
contract Helper_CodeContractForInvalidInCreation {
constructor() public {
assembly {
invalid()
}
}
}
......@@ -2,18 +2,18 @@
pragma solidity ^0.7.0;
contract Helper_ModifiableStorage {
address private target;
mapping (address => address) private target;
constructor(
address _target
) {
target = _target;
target[address(this)] = _target;
}
fallback()
external
{
(bool success, bytes memory returndata) = target.delegatecall(msg.data);
(bool success, bytes memory returndata) = target[address(this)].delegatecall(msg.data);
if (success) {
assembly {
......
......@@ -7,7 +7,7 @@
"build": "yarn run build:contracts",
"build:contracts": "buidler compile",
"test": "yarn run test:contracts",
"test:contracts": "buidler test \"test/contracts/OVM/verification/OVM_FraudVerifier.spec.ts\""
"test:contracts": "buidler test \"test/contracts/OVM/execution/OVM_ExecutionManager/ovmSTATICCALL.spec.ts\""
},
"devDependencies": {
"@nomiclabs/buidler": "^1.4.4",
......
/* Internal Imports */
import {
runExecutionManagerTest,
TestDefinition,
GAS_LIMIT,
NULL_BYTES32,
NON_NULL_BYTES32,
REVERT_FLAGS,
DUMMY_BYTECODE,
VERIFIED_EMPTY_CONTRACT_HASH
} from '../../../../helpers'
const DUMMY_REVERT_DATA = "0xdeadbeef1e5420deadbeef1e5420deadbeef1e5420deadbeef1e5420deadbeef1e5420"
const test_ovmCALL: TestDefinition = {
name: "Basic tests for ovmCALL",
preState: {
ExecutionManager: {
ovmStateManager: "$OVM_STATE_MANAGER",
ovmSafetyChecker: "$OVM_SAFETY_CHECKER",
messageRecord: {
nuisanceGasLeft: GAS_LIMIT
}
},
StateManager: {
owner: "$OVM_EXECUTION_MANAGER",
accounts: {
"$DUMMY_OVM_ADDRESS_1": {
codeHash: NON_NULL_BYTES32,
ethAddress: "$OVM_CALL_HELPER"
},
"$DUMMY_OVM_ADDRESS_2": {
codeHash: NON_NULL_BYTES32,
ethAddress: "$OVM_CALL_HELPER"
},
"$DUMMY_OVM_ADDRESS_3": {
codeHash: VERIFIED_EMPTY_CONTRACT_HASH,
ethAddress: '0x' + '00'.repeat(20)
}
}
}
},
parameters: [
{
steps: [
{
functionName: 'ovmCALL',
functionParams: [
GAS_LIMIT / 2,
"$DUMMY_OVM_ADDRESS_1",
[
{
functionName: 'ovmCALL',
functionParams: [
GAS_LIMIT / 2,
"$DUMMY_OVM_ADDRESS_2",
[
{
functionName: 'ovmCALLER',
functionParams: [],
expectedReturnStatus: true,
expectedReturnValues: ["$DUMMY_OVM_ADDRESS_1"]
},
{
functionName: 'ovmADDRESS',
functionParams: [],
expectedReturnStatus: true,
expectedReturnValues: ["$DUMMY_OVM_ADDRESS_2"]
},
{
functionName: 'ovmSSTORE',
functionParams: [ NON_NULL_BYTES32, NON_NULL_BYTES32 ],
expectedReturnStatus: true,
expectedReturnValues: []
},
{
functionName: 'ovmSLOAD',
functionParams: [ NON_NULL_BYTES32 ],
expectedReturnStatus: true,
expectedReturnValues: [NON_NULL_BYTES32]
}
]
],
expectedReturnStatus: true,
expectedReturnValues: []
},
{
functionName: 'ovmCALL',
functionParams: [
GAS_LIMIT / 2,
"$DUMMY_OVM_ADDRESS_2",
[
{
functionName: 'ovmSLOAD',
functionParams: [ NON_NULL_BYTES32 ],
expectedReturnStatus: true,
expectedReturnValues: [NON_NULL_BYTES32]
}
]
],
expectedReturnStatus: true,
expectedReturnValues: []
},
]
],
expectedReturnStatus: true,
expectedReturnValues: []
}
]
},
{
steps: [
{
functionName: 'ovmCALL',
functionParams: [
GAS_LIMIT / 2,
"$DUMMY_OVM_ADDRESS_1",
[
{
functionName: 'ovmCALL',
functionParams: [
GAS_LIMIT / 2,
"$DUMMY_OVM_ADDRESS_2",
[
{
functionName: 'ovmCALLER',
functionParams: [],
expectedReturnStatus: true,
expectedReturnValues: ["$DUMMY_OVM_ADDRESS_1"]
},
{
functionName: 'ovmADDRESS',
functionParams: [],
expectedReturnStatus: true,
expectedReturnValues: ["$DUMMY_OVM_ADDRESS_2"]
},
{
functionName: 'ovmSSTORE',
functionParams: [ NON_NULL_BYTES32, NON_NULL_BYTES32 ],
expectedReturnStatus: true,
expectedReturnValues: []
},
{
functionName: 'ovmSLOAD',
functionParams: [ NON_NULL_BYTES32 ],
expectedReturnStatus: true,
expectedReturnValues: [NON_NULL_BYTES32]
}
]
],
expectedReturnStatus: true,
expectedReturnValues: []
},
]
],
expectedReturnStatus: true,
expectedReturnValues: []
}
]
},
{
steps: [
{
functionName: 'ovmCALL',
functionParams: [
GAS_LIMIT / 2,
"$DUMMY_OVM_ADDRESS_1",
[
{
functionName: 'ovmCALL',
functionParams: [
GAS_LIMIT / 2,
"$DUMMY_OVM_ADDRESS_3",
[]
],
expectedReturnStatus: true,
expectedReturnValues: [true, "0x"]
},
]
],
expectedReturnStatus: true,
expectedReturnValues: []
}
]
}
]
}
const test_ovmCALL_revert: TestDefinition = {
name: "Basic reverts in a code contract called via ovmCALL",
preState: {
ExecutionManager: {
ovmStateManager: "$OVM_STATE_MANAGER",
ovmSafetyChecker: "$OVM_SAFETY_CHECKER",
messageRecord: {
nuisanceGasLeft: GAS_LIMIT
}
},
StateManager: {
owner: "$OVM_EXECUTION_MANAGER",
accounts: {
"$DUMMY_OVM_ADDRESS_1": {
codeHash: NON_NULL_BYTES32,
ethAddress: "$OVM_CALL_HELPER"
},
"$DUMMY_OVM_ADDRESS_2": {
codeHash: NON_NULL_BYTES32,
ethAddress: "$OVM_REVERT_HELPER"
},
"$DUMMY_OVM_ADDRESS_3": {
codeHash: NON_NULL_BYTES32,
ethAddress: "$OVM_REVERT_HELPER"
},
"$DUMMY_OVM_ADDRESS_4": {
codeHash: VERIFIED_EMPTY_CONTRACT_HASH,
ethAddress: '0x' + '00'.repeat(20)
}
}
}
},
parameters: [
{
steps: [
{
functionName: 'ovmCALL',
functionParams: [
GAS_LIMIT / 2,
"$DUMMY_OVM_ADDRESS_1",
[
{
functionName: 'ovmCALLToRevert',
functionParams: [
GAS_LIMIT / 2,
"$DUMMY_OVM_ADDRESS_2",
[
REVERT_FLAGS.INTENTIONAL_REVERT,
DUMMY_REVERT_DATA,
GAS_LIMIT / 2,
0
]
],
expectedReturnStatus: true,
expectedReturnValues: [false, DUMMY_REVERT_DATA]
},
]
],
expectedReturnStatus: true,
expectedReturnValues: []
}
]
},
{
steps: [
{
functionName: 'ovmCALL',
functionParams: [
GAS_LIMIT / 2,
"$DUMMY_OVM_ADDRESS_1",
[
{
functionName: 'ovmCALLToRevert',
functionParams: [
GAS_LIMIT / 2,
"$DUMMY_OVM_ADDRESS_2",
[
REVERT_FLAGS.EXCEEDS_NUISANCE_GAS,
"0x",
0,
0
]
],
expectedReturnStatus: true,
expectedReturnValues: [false, "0x"]
},
]
],
expectedReturnStatus: true,
expectedReturnValues: []
},
]
},
]
}
runExecutionManagerTest(test_ovmCALL)
runExecutionManagerTest(test_ovmCALL_revert)
/* Internal Imports */
import {
runExecutionManagerTest,
TestDefinition,
GAS_LIMIT,
NULL_BYTES32,
NON_NULL_BYTES32,
REVERT_FLAGS,
DUMMY_BYTECODE
} from '../../../../helpers'
const CREATED_CONTRACT_1 = "0x2bda4a99d5be88609d23b1e4ab5d1d34fb1c2feb"
const CREATED_CONTRACT_2 = "0xe0d8be8101f36ebe6b01abacec884422c39a1f62"
const test_ovmDELEGATECALL: TestDefinition = {
name: "Basic tests for ovmDELEGATECALL",
preState: {
ExecutionManager: {
ovmStateManager: "$OVM_STATE_MANAGER",
ovmSafetyChecker: "$OVM_SAFETY_CHECKER",
messageRecord: {
nuisanceGasLeft: GAS_LIMIT
}
},
StateManager: {
owner: "$OVM_EXECUTION_MANAGER",
accounts: {
"$DUMMY_OVM_ADDRESS_1": {
codeHash: NON_NULL_BYTES32,
ethAddress: "$OVM_CALL_HELPER"
},
"$DUMMY_OVM_ADDRESS_2": {
codeHash: NON_NULL_BYTES32,
ethAddress: "$OVM_CALL_HELPER"
},
"$DUMMY_OVM_ADDRESS_3": {
codeHash: NON_NULL_BYTES32,
ethAddress: "$OVM_CALL_HELPER"
},
"$DUMMY_OVM_ADDRESS_4": {
codeHash: NON_NULL_BYTES32,
ethAddress: "$OVM_CALL_HELPER"
},
}
}
},
parameters: [
{
steps: [
{
functionName: 'ovmCALL',
functionParams: [
GAS_LIMIT,
"$DUMMY_OVM_ADDRESS_1",
[
{
functionName: 'ovmCALL',
functionParams: [
GAS_LIMIT,
"$DUMMY_OVM_ADDRESS_2",
[
{
functionName: 'ovmDELEGATECALL',
functionParams: [
GAS_LIMIT,
"$DUMMY_OVM_ADDRESS_3",
[
{
functionName: 'ovmCALLER',
functionParams: [],
expectedReturnStatus: true,
expectedReturnValues: ["$DUMMY_OVM_ADDRESS_1"]
},
{
functionName: 'ovmADDRESS',
functionParams: [],
expectedReturnStatus: true,
expectedReturnValues: ["$DUMMY_OVM_ADDRESS_2"]
},
{
functionName: 'ovmSSTORE',
functionParams: [ NON_NULL_BYTES32, NON_NULL_BYTES32 ],
expectedReturnStatus: true,
expectedReturnValues: []
},
{
functionName: 'ovmSLOAD',
functionParams: [ NON_NULL_BYTES32 ],
expectedReturnStatus: true,
expectedReturnValues: [NON_NULL_BYTES32]
}
]
],
expectedReturnStatus: true,
expectedReturnValues: ["$DUMMY_OVM_ADDRESS_1"]
},
]
],
expectedReturnStatus: true,
expectedReturnValues: []
},
{
functionName: 'ovmCALL',
functionParams: [
GAS_LIMIT / 2,
"$DUMMY_OVM_ADDRESS_2",
[
{
functionName: 'ovmSLOAD',
functionParams: [ NON_NULL_BYTES32 ],
expectedReturnStatus: true,
expectedReturnValues: [NON_NULL_BYTES32]
}
]
],
expectedReturnStatus: true,
expectedReturnValues: []
},
]
],
expectedReturnStatus: true,
expectedReturnValues: []
}
]
},
{
steps: [
{
functionName: 'ovmCALL',
functionParams: [
GAS_LIMIT,
"$DUMMY_OVM_ADDRESS_1",
[
{
functionName: 'ovmDELEGATECALL',
functionParams: [
GAS_LIMIT,
"$DUMMY_OVM_ADDRESS_2",
[
{
functionName: 'ovmCREATE',
functionParams: [
DUMMY_BYTECODE,
true,
[]
],
expectedReturnStatus: true,
expectedReturnValues: [CREATED_CONTRACT_1]
},
]
],
expectedReturnStatus: true,
expectedReturnValues: []
},
]
],
expectedReturnStatus: true,
expectedReturnValues: []
}
]
},
{
steps: [
{
functionName: 'ovmCALL',
functionParams: [
GAS_LIMIT,
"$DUMMY_OVM_ADDRESS_1",
[
{
functionName: 'ovmCALL',
functionParams: [
GAS_LIMIT,
"$DUMMY_OVM_ADDRESS_2",
[
{
functionName: 'ovmDELEGATECALL',
functionParams: [
GAS_LIMIT,
"$DUMMY_OVM_ADDRESS_3",
[
{
functionName: "ovmDELEGATECALL",
functionParams: [
GAS_LIMIT,
"$DUMMY_OVM_ADDRESS_4",
[
{
functionName: "ovmCALLER",
functionParams: [],
expectedReturnStatus: true,
expectedReturnValues: [ "$DUMMY_OVM_ADDRESS_1" ]
},
{
functionName: "ovmADDRESS",
functionParams: [],
expectedReturnStatus: true,
expectedReturnValues: [ "$DUMMY_OVM_ADDRESS_2" ]
},
{
functionName: 'ovmCREATE',
functionParams: [
DUMMY_BYTECODE,
true,
[]
],
expectedReturnStatus: true,
expectedReturnValues: [CREATED_CONTRACT_2]
}
]
],
expectedReturnStatus: true,
expectedReturnValues: []
}
]
],
expectedReturnStatus: true,
expectedReturnValues: []
},
]
],
expectedReturnStatus: true,
expectedReturnValues: []
},
{
functionName: "ovmADDRESS",
functionParams: [],
expectedReturnStatus: true,
expectedReturnValues: [ "$DUMMY_OVM_ADDRESS_1" ]
}
]
],
expectedReturnStatus: true,
expectedReturnValues: []
}
]
}
]
}
runExecutionManagerTest(test_ovmDELEGATECALL)
/* Internal Imports */
import {
runExecutionManagerTest,
TestDefinition,
GAS_LIMIT,
NULL_BYTES32,
NON_NULL_BYTES32,
REVERT_FLAGS,
DUMMY_BYTECODE
} from '../../../../helpers'
const test_ovmREVERT: TestDefinition = {
name: "basic ovmREVERT unit tests",
preState: {
ExecutionManager: {
ovmStateManager: "$OVM_STATE_MANAGER",
ovmSafetyChecker: "$OVM_SAFETY_CHECKER",
messageRecord: {
nuisanceGasLeft: GAS_LIMIT
}
},
StateManager: {
owner: "$OVM_EXECUTION_MANAGER",
accounts: {
"$DUMMY_OVM_ADDRESS_1": {
codeHash: NON_NULL_BYTES32,
ethAddress: "$OVM_CALL_HELPER"
},
}
}
},
parameters: [
{
name: "ovmREVERT inside ovmCALL should cause EM to revert",
parameters: [
{
steps: [
{
functionName: "ovmCALL",
functionParams: [
GAS_LIMIT / 2,
"$DUMMY_OVM_ADDRESS_1",
[
{
functionName: "ovmREVERT",
functionParams: [ "0xdeadbeef" ],
expectedReturnStatus: false,
expectedReturnValues: [
REVERT_FLAGS.INTENTIONAL_REVERT,
"0xdeadbeef",
GAS_LIMIT / 2,
0
]
}
]
],
expectedReturnStatus: true,
expectedReturnValues: []
}
]
}
]
},
// TODO: fix this. only way to do it is manually set up and call ovmREVERT directly inside a context which mirrors that during creation.
// {
// name: "ovmREVERT inside ovmCREATE ?",
// parameters: [
// {
// steps: [
// {
// functionName: "ovmCALL",
// functionParams: [
// GAS_LIMIT / 2,
// "$DUMMY_OVM_ADDRESS_1",
// [
// {
// functionName: "ovmCREATE",
// functionParams: [
// USELESS_BYTECODE,
// false, // "create will be successful?"
// [
// {
// functionName: "ovmREVERT",
// functionParams: [ "0xdeadbeef" ],
// expectedReturnStatus: false,
// expectedReturnValues: [ "0x00" ] // no return values for reversion in constructor
// },
// // TODO: check internally flagged storage here
// ]
// ],
// expectedReturnStatus: true,
// expectedReturnValues: [ CREATED_CONTRACT_1 ]
// }
// ],
// ],
// expectedReturnStatus: true,
// expectedReturnValues: []
// }
// ]
// }
// ]
// }
]
}
runExecutionManagerTest(test_ovmREVERT)
/* Internal Imports */
import {
runExecutionManagerTest,
TestDefinition,
GAS_LIMIT,
NULL_BYTES32,
NON_NULL_BYTES32,
REVERT_FLAGS,
DUMMY_BYTECODE
} from '../../../../helpers'
const test_ovmSLOAD: TestDefinition = {
name: "External storage manipulation during initcode subcalls should correctly NOT be persisted if ovmREVERTed",
preState: {
ExecutionManager: {
ovmStateManager: "$OVM_STATE_MANAGER",
ovmSafetyChecker: "$OVM_SAFETY_CHECKER",
messageRecord: {
nuisanceGasLeft: GAS_LIMIT
}
},
StateManager: {
owner: "$OVM_EXECUTION_MANAGER",
accounts: {
"$DUMMY_OVM_ADDRESS_1": {
codeHash: NON_NULL_BYTES32,
ethAddress: "$OVM_CALL_HELPER"
},
"$DUMMY_OVM_ADDRESS_2": {
codeHash: NON_NULL_BYTES32,
ethAddress: "$OVM_CALL_HELPER"
},
},
verifiedContractStorage: {
"$DUMMY_OVM_ADDRESS_1": {
[NON_NULL_BYTES32]: true
}
}
}
},
parameters: [
{
steps: [
{
functionName: 'ovmCALL',
functionParams: [
GAS_LIMIT,
"$DUMMY_OVM_ADDRESS_1",
[
{
functionName: 'ovmSLOAD',
functionParams: [NON_NULL_BYTES32],
expectedReturnStatus: true,
expectedReturnValues: [NULL_BYTES32]
},
]
],
expectedReturnStatus: true,
expectedReturnValues: []
}
]
}
]
}
runExecutionManagerTest(test_ovmSLOAD)
/* Internal Imports */
import {
runExecutionManagerTest,
TestDefinition,
GAS_LIMIT,
NULL_BYTES32,
NON_NULL_BYTES32,
REVERT_FLAGS,
DUMMY_BYTECODE
} from '../../../../helpers'
const test_ovmSTATICCALL: TestDefinition = {
name: "Basic checks on staticcall",
preState: {
ExecutionManager: {
ovmStateManager: "$OVM_STATE_MANAGER",
ovmSafetyChecker: "$OVM_SAFETY_CHECKER",
messageRecord: {
nuisanceGasLeft: GAS_LIMIT
}
},
StateManager: {
owner: "$OVM_EXECUTION_MANAGER",
accounts: {
"$DUMMY_OVM_ADDRESS_1": {
codeHash: NON_NULL_BYTES32,
ethAddress: "$OVM_CALL_HELPER"
},
"$DUMMY_OVM_ADDRESS_2": {
codeHash: NON_NULL_BYTES32,
ethAddress: "$OVM_CALL_HELPER"
},
},
verifiedContractStorage: {
"$DUMMY_OVM_ADDRESS_2": {
[NON_NULL_BYTES32]: true
}
}
}
},
parameters: [
{
steps: [
{
functionName: 'ovmCALL',
functionParams: [
GAS_LIMIT,
"$DUMMY_OVM_ADDRESS_1",
[
{
functionName: 'ovmSTATICCALL',
functionParams: [
GAS_LIMIT / 2,
"$DUMMY_OVM_ADDRESS_2",
[
{
functionName: 'ovmSSTORE',
functionParams: [NULL_BYTES32, NULL_BYTES32],
expectedReturnStatus: false,
expectedReturnValues: [REVERT_FLAGS.STATIC_VIOLATION, "0x", GAS_LIMIT / 2, 0]
},
{
functionName: 'ovmCREATE',
functionParams: [
DUMMY_BYTECODE,
false,
[]
],
expectedReturnStatus: false,
expectedReturnValues: [REVERT_FLAGS.STATIC_VIOLATION, "0x", GAS_LIMIT / 2, 0]
},
{
functionName: 'ovmSLOAD',
functionParams: [NON_NULL_BYTES32],
expectedReturnStatus: true,
expectedReturnValues: [NULL_BYTES32]
},
{
functionName: 'ovmCALLER',
functionParams: [],
expectedReturnStatus: true,
expectedReturnValues: ["$DUMMY_OVM_ADDRESS_1"]
},
{
functionName: 'ovmADDRESS',
functionParams: [],
expectedReturnStatus: true,
expectedReturnValues: ["$DUMMY_OVM_ADDRESS_2"]
},
]
],
expectedReturnStatus: true,
expectedReturnValues: []
},
]
],
expectedReturnStatus: true,
expectedReturnValues: []
}
]
},
{
steps: [
{
functionName: 'ovmCALL',
functionParams: [
GAS_LIMIT,
"$DUMMY_OVM_ADDRESS_1",
[
{
functionName: 'ovmSTATICCALL',
functionParams: [
GAS_LIMIT,
"$DUMMY_OVM_ADDRESS_2",
[
{
functionName: 'ovmCALL',
functionParams: [
GAS_LIMIT,
"$DUMMY_OVM_ADDRESS_2",
[
{
functionName: 'ovmSLOAD',
functionParams: [NON_NULL_BYTES32],
expectedReturnStatus: true,
expectedReturnValues: [NULL_BYTES32]
},
{
functionName: 'ovmSSTORE',
functionParams: [NULL_BYTES32, NULL_BYTES32],
expectedReturnStatus: false,
expectedReturnValues: [REVERT_FLAGS.STATIC_VIOLATION, "0x", 867484476, 2906]
},
{
functionName: 'ovmCREATE',
functionParams: [
DUMMY_BYTECODE,
false,
[]
],
expectedReturnStatus: false,
expectedReturnValues: [REVERT_FLAGS.STATIC_VIOLATION, "0x", 867484476, 2906]
}
]
],
expectedReturnStatus: true,
expectedReturnValues: []
}
]
],
expectedReturnStatus: true,
expectedReturnValues: []
},
]
],
expectedReturnStatus: true,
expectedReturnValues: []
}
]
},
{
steps: [
{
functionName: 'ovmCALL',
functionParams: [
GAS_LIMIT,
"$DUMMY_OVM_ADDRESS_1",
[
{
functionName: 'ovmSTATICCALL',
functionParams: [
GAS_LIMIT / 2,
"$DUMMY_OVM_ADDRESS_2",
[
{
functionName: 'ovmSTATICCALL',
functionParams: [
GAS_LIMIT,
"$DUMMY_OVM_ADDRESS_2",
[]
],
expectedReturnStatus: true,
expectedReturnValues: []
},
{
functionName: 'ovmSSTORE',
functionParams: [NULL_BYTES32, NULL_BYTES32],
expectedReturnStatus: false,
expectedReturnValues: [REVERT_FLAGS.STATIC_VIOLATION, "0x", GAS_LIMIT / 2, 33806]
}
]
],
expectedReturnStatus: true,
expectedReturnValues: []
},
]
],
expectedReturnStatus: true,
expectedReturnValues: []
}
]
},
{
steps: [
{
functionName: 'ovmCALL',
functionParams: [
GAS_LIMIT,
"$DUMMY_OVM_ADDRESS_1",
[
{
functionName: 'ovmSTATICCALLToRevert',
functionParams: [
GAS_LIMIT / 2,
"$DUMMY_OVM_ADDRESS_2",
[
REVERT_FLAGS.STATIC_VIOLATION,
"0x",
GAS_LIMIT / 2,
0
]
],
expectedReturnStatus: true,
expectedReturnValues: [false, "0x"]
},
]
],
expectedReturnStatus: true,
expectedReturnValues: []
}
]
},
{
steps: [
{
functionName: 'ovmCALL',
functionParams: [
GAS_LIMIT,
"$DUMMY_OVM_ADDRESS_1",
[
{
functionName: 'ovmSTATICCALL',
functionParams: [
GAS_LIMIT,
"$DUMMY_OVM_ADDRESS_1",
[
{
functionName: 'ovmSTATICCALLToRevert',
functionParams: [
GAS_LIMIT / 2,
"$DUMMY_OVM_ADDRESS_2",
[
REVERT_FLAGS.STATIC_VIOLATION,
"0x",
GAS_LIMIT / 2,
0
]
],
expectedReturnStatus: true,
expectedReturnValues: [false, "0x"]
},
]
],
expectedReturnStatus: true,
expectedReturnValues: []
}
]
],
expectedReturnStatus: true,
expectedReturnValues: []
}
]
}
]
}
runExecutionManagerTest(test_ovmSTATICCALL)
import { ethers } from '@nomiclabs/buidler'
import { hexZeroPad } from 'ethers/lib/utils'
export const encodeRevertData = (
flag: number,
......@@ -6,10 +7,25 @@ export const encodeRevertData = (
nuisanceGasLeft: number = 0,
ovmGasRefund: number = 0
): string => {
return ethers.utils.defaultAbiCoder.encode(
const abiEncoded: string = ethers.utils.defaultAbiCoder.encode(
['uint256','uint256','uint256','bytes'],
[flag, nuisanceGasLeft, ovmGasRefund, data]
)
return abiEncoded
}
export const decodeRevertData = (
revertData: string
): any => {
// const length: number = revertData.length/2 - 1
// const reencodedRevertData = '0x' + hexZeroPad('0x' + length.toString(16), 32) + revertData.slice(2)
const decoded = ethers.utils.defaultAbiCoder.decode(
['uint256','uint256','uint256','bytes'],
revertData
)
console.log(`flag is: ${decoded[0].toNumber()}`)
return '[revertFlag:' + Object.keys(REVERT_FLAGS)[decoded[0]] + ', nuisanceGasLeft:' + decoded[1] + ', ovmGasRefund: ' + decoded[2] + ', data: ' + decoded[3] + ']'
}
export const REVERT_FLAGS = {
......@@ -18,5 +34,8 @@ export const REVERT_FLAGS = {
INTENTIONAL_REVERT: 2,
EXCEEDS_NUISANCE_GAS: 3,
INVALID_STATE_ACCESS: 4,
UNSAFE_BYTECODE: 5
UNSAFE_BYTECODE: 5,
CREATE_COLLISION: 6,
STATIC_VIOLATION: 7,
CREATE_EXCEPTION: 8
}
......@@ -20,4 +20,4 @@ export const NON_NULL_BYTES32 = makeHexString('11', 32)
export const ZERO_ADDRESS = makeAddress('00')
export const NON_ZERO_ADDRESS = makeAddress('11')
export const FORCE_INCLUSION_PERIOD_SECONDS = 600
export const VERIFIED_EMPTY_CONTRACT_HASH = makeHexString('69', 32)
/* External Imports */
import { keccak256 } from 'ethers/lib/utils'
export const DUMMY_BYTECODE = '0x123412341234'
export const DUMMY_BYTECODE_BYTELEN = 6
export const DUMMY_BYTECODE_HASH = keccak256(DUMMY_BYTECODE)
export * from './accounts'
export * from './bytes32'
export * from './context'
export * from './bytecode'
......@@ -6,4 +6,5 @@ export * from './mocks'
export * from './buffer-utils'
export * from './byte-utils'
export * from './codec'
export * from './data'
\ No newline at end of file
export * from './data'
export * from './test-utils'
export * from './test-generation'
export * from './test-parsing'
export * from './test.types'
......@@ -3,18 +3,26 @@ import { expect } from '../../setup'
/* External Imports */
import { Contract } from 'ethers'
import { cloneDeep } from 'lodash'
import { ethers } from '@nomiclabs/buidler'
/* Internal Imports */
import { getModifiableStorageFactory } from '../storage/contract-storage'
import { GAS_LIMIT } from '../constants'
import { getTestGenerator } from './test-generation'
import { GAS_LIMIT, NON_NULL_BYTES32 } from '../constants'
import { getTestGenerator, getInitcode, getBytecode } from './test-generation'
import { TestParameters, TestDefinition, isTestDefinition } from './test.types'
import { int } from '@nomiclabs/buidler/internal/core/params/argumentTypes'
const getDummyOVMAddress = (kv: string): string => {
return '0x' + (kv.split('$DUMMY_OVM_ADDRESS_')[1] + '0').repeat(20)
}
const setPlaceholderStrings = (
test: any,
ovmExecutionManager: Contract,
ovmStateManager: Contract,
ovmCallHelper: Contract
ovmSafetyChecker: Contract,
ovmCallHelper: Contract,
ovmRevertHelper: Contract,
): any => {
const setPlaceholder = (
kv: string
......@@ -23,10 +31,16 @@ const setPlaceholderStrings = (
return ovmExecutionManager.address
} else if (kv === '$OVM_STATE_MANAGER') {
return ovmStateManager.address
} else if (kv === '$OVM_SAFETY_CHECKER') {
return ovmSafetyChecker.address
} else if (kv === '$OVM_CALL_HELPER_CODE') {
return getBytecode('Helper_CodeContractForCalls')
} else if (kv === '$OVM_CALL_HELPER') {
return ovmCallHelper.address
} else if (kv === '$OVM_REVERT_HELPER') {
return ovmRevertHelper.address
} else if (kv.startsWith('$DUMMY_OVM_ADDRESS_')) {
return '0x' + kv.split('$DUMMY_OVM_ADDRESS_')[1].padStart(40, '0')
return getDummyOVMAddress(kv)
} else {
return kv
}
......@@ -38,7 +52,9 @@ const setPlaceholderStrings = (
element,
ovmExecutionManager,
ovmStateManager,
ovmCallHelper
ovmSafetyChecker,
ovmCallHelper,
ovmRevertHelper
)
})
} else if (typeof test === 'object' && test !== null) {
......@@ -54,7 +70,9 @@ const setPlaceholderStrings = (
test[replacedKey],
ovmExecutionManager,
ovmStateManager,
ovmCallHelper
ovmSafetyChecker,
ovmCallHelper,
ovmRevertHelper
)
}
} else if (typeof test === 'string') {
......@@ -68,9 +86,12 @@ const fixtureDeployContracts = async (): Promise <{
OVM_SafetyChecker: Contract,
OVM_StateManager: Contract,
OVM_ExecutionManager: Contract,
OVM_CallHelper: Contract
OVM_CallHelper: Contract,
OVM_RevertHelper: Contract,
OVM_CreateStorer: Contract,
OVM_InvalidHelper: Contract
}> => {
const Factory__OVM_SafetyChecker = await getModifiableStorageFactory(
const Factory__OVM_SafetyChecker = await ethers.getContractFactory(
'OVM_SafetyChecker'
)
const Factory__OVM_StateManager = await getModifiableStorageFactory(
......@@ -82,17 +103,33 @@ const fixtureDeployContracts = async (): Promise <{
const Factory__Helper_CodeContractForCalls = await getModifiableStorageFactory(
'Helper_CodeContractForCalls'
)
const Factory__Helper_CodeContractForReverts = await ethers.getContractFactory(
'Helper_CodeContractForReverts'
)
const Factory__Helper_CreateEMResponsesStorer = await ethers.getContractFactory(
'Helper_CreateEMResponsesStorer'
)
const Helper_CodeContractForInvalid = await ethers.getContractFactory(
'Helper_CodeContractForInvalid'
)
const OVM_SafetyChecker = await Factory__OVM_SafetyChecker.deploy()
const OVM_StateManager = await Factory__OVM_StateManager.deploy()
const OVM_ExecutionManager = await Factory__OVM_ExecutionManager.deploy(OVM_SafetyChecker.address)
const OVM_StateManager = await Factory__OVM_StateManager.deploy(OVM_ExecutionManager.address)
const OVM_CallHelper = await Factory__Helper_CodeContractForCalls.deploy()
const OVM_RevertHelper = await Factory__Helper_CodeContractForReverts.deploy()
const OVM_CreateStorer = await Factory__Helper_CreateEMResponsesStorer.deploy()
const OVM_InvalidHelper = await Helper_CodeContractForInvalid.deploy()
return {
OVM_SafetyChecker,
OVM_StateManager,
OVM_ExecutionManager,
OVM_CallHelper,
OVM_RevertHelper,
OVM_CreateStorer,
OVM_InvalidHelper
}
}
......@@ -119,14 +156,22 @@ export const runExecutionManagerTest = (
}
)
} else {
let OVM_SafetyChecker: Contract
let OVM_StateManager: Contract
let OVM_ExecutionManager: Contract
let OVM_CallHelper: Contract
let OVM_RevertHelper: Contract
let OVM_CreateStorer: Contract
let OVM_InvalidHelper: Contract
beforeEach(async () => {
const contracts = await fixtureDeployContracts()
OVM_SafetyChecker = contracts.OVM_SafetyChecker
OVM_StateManager = contracts.OVM_StateManager
OVM_ExecutionManager = contracts.OVM_ExecutionManager
OVM_CallHelper = contracts.OVM_CallHelper
OVM_RevertHelper = contracts.OVM_RevertHelper
OVM_CreateStorer = contracts.OVM_CreateStorer
OVM_InvalidHelper = contracts.OVM_InvalidHelper
})
let replacedParams: TestParameters
......@@ -136,13 +181,17 @@ export const runExecutionManagerTest = (
cloneDeep(parameters),
OVM_ExecutionManager,
OVM_StateManager,
OVM_CallHelper
OVM_SafetyChecker,
OVM_CallHelper,
OVM_RevertHelper
)
replacedTest = setPlaceholderStrings(
cloneDeep(test),
OVM_ExecutionManager,
OVM_StateManager,
OVM_CallHelper
OVM_SafetyChecker,
OVM_CallHelper,
OVM_RevertHelper
)
})
......@@ -153,21 +202,24 @@ export const runExecutionManagerTest = (
afterEach(async () => {
await OVM_ExecutionManager.__checkContractStorage({
...replacedTest.preState.ExecutionManager,
...replacedTest.postState.ExecutionManager
})
await OVM_StateManager.__checkContractStorage({
...replacedTest.preState.StateManager,
...replacedTest.postState.StateManager
})
})
parameters.steps.map((step, idx) => {
it(`should run test: ${test.name} ${idx}`, async () => {
const scopedFunction = (!!test.focus) ? it.only : it
scopedFunction(`should run test: ${test.name} ${idx}`, async () => {
const testGenerator = getTestGenerator(
replacedParams.steps[idx],
OVM_ExecutionManager,
OVM_CallHelper
OVM_CallHelper,
OVM_CreateStorer,
(await ethers.getContractFactory('Helper_CodeContractForCreates')).interface,
OVM_RevertHelper,
OVM_InvalidHelper
)
const callResult = await OVM_ExecutionManager.provider.call({
......@@ -182,7 +234,14 @@ export const runExecutionManagerTest = (
gasLimit: GAS_LIMIT
})
expect(callResult).to.equal(testGenerator.getReturnData())
const interpretation = testGenerator.interpretActualReturnData(callResult, true)
console.log('interpretation of actual results:\n' + interpretation) // in future we can add conditional here but for now always assume succeed
const interpretationOfExpected = testGenerator.interpretActualReturnData(testGenerator.getReturnData(), true)
console.log('interpretation of expected: \n' + interpretationOfExpected)
expect(callResult).to.equal(testGenerator.getReturnData()) //, 'got bad response, looks like it did:\n' + testGenerator.interpretActualReturnData(callResult))
})
})
}
......
......@@ -5,9 +5,9 @@ export type SolidityFunctionParameter = string | number | BigNumber
export interface TestStep {
functionName: string
functionParams: Array<SolidityFunctionParameter | TestStep[]>
returnStatus: boolean
returnValues: any[]
functionParams: Array<SolidityFunctionParameter | SolidityFunctionParameter[] | TestStep[] | boolean>
expectedReturnStatus: boolean
expectedReturnValues: any[]
}
export interface TestParameters {
......@@ -16,6 +16,7 @@ export interface TestParameters {
export interface TestDefinition {
name: string
focus?: boolean
preState?: {
ExecutionManager?: any,
StateManager?: any
......
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