diff --git a/packages/core-utils/src/app/misc.ts b/packages/core-utils/src/app/misc.ts index 40ed85a1591b..4cc7bc6d7c22 100644 --- a/packages/core-utils/src/app/misc.ts +++ b/packages/core-utils/src/app/misc.ts @@ -181,6 +181,15 @@ export const hexStrToBuf = (hexString: string): Buffer => { return Buffer.from(remove0x(hexString), 'hex') } +/** + * Converts a hex string to a JavaScript Number + * @param hexString the hex string to be converted + * @returns the hexString as a JavaScript Number. + */ +export const hexStrToNumber = (hexString: string): number => { + return parseInt(remove0x(hexString), 16) +} + /** * Converts the provided buffer into a hex string. * @param buff The buffer. diff --git a/packages/ovm/package.json b/packages/ovm/package.json index 5f028cad1f87..5df8cc69ada3 100644 --- a/packages/ovm/package.json +++ b/packages/ovm/package.json @@ -43,6 +43,7 @@ "chai": "^4.2.0", "chai-bignumber": "^3.0.0", "ethereumjs-abi": "^0.6.8", + "lodash": "^4.17.15", "memdown": "^5.0.0", "mocha": "^6.0.2", "rimraf": "^2.6.3", diff --git a/packages/ovm/src/app/ovm.ts b/packages/ovm/src/app/ovm.ts index d4c73cd8f4f9..69b8e1f0f50a 100644 --- a/packages/ovm/src/app/ovm.ts +++ b/packages/ovm/src/app/ovm.ts @@ -14,7 +14,6 @@ import { StorageElement, StorageSlot, StorageValue, - Transaction, TransactionLog, TransactionReceipt, TransactionResult, diff --git a/packages/ovm/src/app/utils.ts b/packages/ovm/src/app/utils.ts index 879732888d20..abd27088d19d 100644 --- a/packages/ovm/src/app/utils.ts +++ b/packages/ovm/src/app/utils.ts @@ -1,6 +1,6 @@ /* External Imports */ import { getLogger, ZERO_ADDRESS } from '@eth-optimism/core-utils' -import { Contract, ethers } from 'ethers' +import { ethers } from 'ethers' import { Log, TransactionReceipt } from 'ethers/providers' /* Contract Imports */ diff --git a/packages/ovm/src/types/ovm.ts b/packages/ovm/src/types/ovm.ts index 64cc2509d333..2a4062855657 100644 --- a/packages/ovm/src/types/ovm.ts +++ b/packages/ovm/src/types/ovm.ts @@ -4,7 +4,6 @@ import { Address, StorageSlot, StorageValue, - Transaction, TransactionResult, } from '@eth-optimism/rollup-core' diff --git a/packages/ovm/test/app/utils.spec.ts b/packages/ovm/test/app/utils.spec.ts index b6b9c5c5d8a0..87d5e6221724 100644 --- a/packages/ovm/test/app/utils.spec.ts +++ b/packages/ovm/test/app/utils.spec.ts @@ -1,8 +1,6 @@ -import { Contract, ethers } from 'ethers' -import { add0x, ZERO_ADDRESS, TestUtils } from '@eth-optimism/core-utils' +import { add0x, ZERO_ADDRESS } from '@eth-optimism/core-utils' /* Contract Imports */ -import { getWallets } from 'ethereum-waffle' -import { TransactionReceipt, JsonRpcProvider, Log } from 'ethers/providers' +import { TransactionReceipt } from 'ethers/providers' import { convertInternalLogsToOvmLogs, getOvmTransactionMetadata, diff --git a/packages/ovm/test/contracts/contract-address-generator.spec.ts b/packages/ovm/test/contracts/contract-address-generator.spec.ts index 32445026f24b..db35a42ef2db 100644 --- a/packages/ovm/test/contracts/contract-address-generator.spec.ts +++ b/packages/ovm/test/contracts/contract-address-generator.spec.ts @@ -3,7 +3,6 @@ import '../setup' /* External Imports */ import { createMockProvider, deployContract, getWallets } from 'ethereum-waffle' import { getLogger } from '@eth-optimism/core-utils' -import { Address } from '@eth-optimism/rollup-core' import { utils } from 'ethers' /* Contract Imports */ diff --git a/packages/ovm/test/contracts/execution-manager.call-opcodes.spec.ts b/packages/ovm/test/contracts/execution-manager.call-opcodes.spec.ts index d8c9ffb76957..95707ad46b35 100644 --- a/packages/ovm/test/contracts/execution-manager.call-opcodes.spec.ts +++ b/packages/ovm/test/contracts/execution-manager.call-opcodes.spec.ts @@ -2,17 +2,10 @@ import '../setup' /* External Imports */ import { Address } from '@eth-optimism/rollup-core' -import { - getLogger, - BigNumber, - remove0x, - add0x, - TestUtils, -} from '@eth-optimism/core-utils' +import { getLogger, remove0x, add0x, TestUtils } from '@eth-optimism/core-utils' import { Contract, ContractFactory, ethers } from 'ethers' import { createMockProvider, deployContract, getWallets } from 'ethereum-waffle' -import * as ethereumjsAbi from 'ethereumjs-abi' /* Contract Imports */ import * as ExecutionManager from '../../build/contracts/ExecutionManager.json' @@ -26,9 +19,12 @@ import { DEFAULT_ETHNODE_GAS_LIMIT, didCreateSucceed, gasLimit, + executeOVMCall, + encodeMethodId, + encodeRawArguments, } from '../helpers' import { GAS_LIMIT, OPCODE_WHITELIST_MASK } from '../../src/app' -import { TransactionReceipt } from 'ethers/providers' +import { fromPairs } from 'lodash' export const abi = new ethers.utils.AbiCoder() @@ -38,56 +34,35 @@ const log = getLogger('execution-manager-calls', true) * TESTS * *********/ -const executeCallMethodId: string = ethereumjsAbi - .methodID('executeCall', []) - .toString('hex') - -const sstoreMethodId: string = ethereumjsAbi - .methodID('notStaticFriendlySSTORE', []) - .toString('hex') - -const createMethodId: string = ethereumjsAbi - .methodID('notStaticFriendlyCREATE', []) - .toString('hex') - -const create2MethodId: string = ethereumjsAbi - .methodID('notStaticFriendlyCREATE2', []) - .toString('hex') - -const sloadMethodId: string = ethereumjsAbi - .methodID('staticFriendlySLOAD', []) - .toString('hex') - -const staticCallThenCallMethodId: string = ethereumjsAbi - .methodID('makeStaticCallThenCall', []) - .toString('hex') +const methodIds = fromPairs( + [ + 'executeCall', + 'makeCall', + 'makeStaticCall', + 'makeStaticCallThenCall', + 'staticFriendlySLOAD', + 'notStaticFriendlySSTORE', + 'notStaticFriendlyCREATE', + 'notStaticFriendlyCREATE2', + 'staticFriendlySLOAD', + 'makeDelegateCall', + ].map((methodId) => [methodId, encodeMethodId(methodId)]) +) const sloadKey: string = '11'.repeat(32) const unpopultedSLOADResult: string = '00'.repeat(32) const populatedSLOADResult: string = '22'.repeat(32) -const sstoreMethodIdAndParams: string = `${sstoreMethodId}${sloadKey}${populatedSLOADResult}` -const sloadMethodIdAndParams: string = `${sloadMethodId}${sloadKey}` - -const timestampAndQueueOrigin: string = '00'.repeat(64) - describe('Execution Manager -- Call opcodes', () => { const provider = createMockProvider({ gasLimit: DEFAULT_ETHNODE_GAS_LIMIT }) const [wallet] = getWallets(provider) // Create pointers to our execution manager & simple copier contract let executionManager: Contract let dummyContract: Contract - let callContract: ContractFactory let callContractAddress: Address let callContract2Address: Address let callContract3Address: Address - let callContractAddress32: string - let callContract2Address32: string - let callContract3Address32: string - let executeCallToCallContractData: string - - let createMethodIdAndData: string - let create2MethodIdAndData: string + let deployTx: any /* Link libraries before tests */ before(async () => { @@ -95,15 +70,10 @@ describe('Execution Manager -- Call opcodes', () => { gasLimit: DEFAULT_ETHNODE_GAS_LIMIT, }) - const deployTx: any = new ContractFactory( + deployTx = new ContractFactory( SimpleCall.abi, SimpleCall.bytecode ).getDeployTransaction(dummyContract.address) - - createMethodIdAndData = `${createMethodId}${remove0x(deployTx.data)}` - create2MethodIdAndData = `${create2MethodId}${'00'.repeat(32)}${remove0x( - deployTx.data - )}` }) beforeEach(async () => { // Deploy ExecutionManager the normal way @@ -144,62 +114,46 @@ describe('Execution Manager -- Call opcodes', () => { ) log.debug(`Contract address: [${callContractAddress}]`) - - // Also set our simple copier Ethers contract so we can generate unsigned transactions - callContract = new ContractFactory( - SimpleCall.abi as any, - SimpleCall.bytecode - ) - - callContractAddress32 = remove0x( - addressToBytes32Address(callContractAddress) - ) - callContract2Address32 = remove0x( - addressToBytes32Address(callContract2Address) - ) - callContract3Address32 = remove0x( - addressToBytes32Address(callContract2Address) - ) - const encodedParams = `${timestampAndQueueOrigin}${callContractAddress32}` - executeCallToCallContractData = `0x${executeCallMethodId}${encodedParams}` }) describe('ovmCALL', async () => { - const callMethodId: string = ethereumjsAbi - .methodID('makeCall', []) - .toString('hex') - it('properly executes ovmCALL to SLOAD', async () => { - const data: string = `${executeCallToCallContractData}${callMethodId}${callContract2Address32}${sloadMethodIdAndParams}` - - const result = await executionManager.provider.call({ - to: executionManager.address, - data, - gasLimit, - }) - + const result: string = await executeCall([ + addressToBytes32Address(callContract2Address), + methodIds.staticFriendlySLOAD, + sloadKey, + ]) log.debug(`Result: [${result}]`) remove0x(result).should.equal(unpopultedSLOADResult, 'Result mismatch!') }) it('properly executes ovmCALL to SSTORE', async () => { - const data: string = `${executeCallToCallContractData}${callMethodId}${callContract2Address32}${sstoreMethodIdAndParams}` + const data: string = + encodeMethodId('executeCall') + + encodeRawArguments([ + 0, + 0, + addressToBytes32Address(callContractAddress), + methodIds.makeCall, + addressToBytes32Address(callContract2Address), + methodIds.notStaticFriendlySSTORE, + sloadKey, + populatedSLOADResult, + ]) // Note: Send transaction vs call so it is persisted await wallet.sendTransaction({ to: executionManager.address, - data, + data: add0x(data), gasLimit, }) - const fetchData: string = `${executeCallToCallContractData}${callMethodId}${callContract2Address32}${sloadMethodIdAndParams}` - - const result = await executionManager.provider.call({ - to: executionManager.address, - data: fetchData, - gasLimit, - }) + const result: string = await executeCall([ + addressToBytes32Address(callContract2Address), + methodIds.staticFriendlySLOAD, + sloadKey, + ]) log.debug(`Result: [${result}]`) @@ -208,13 +162,11 @@ describe('Execution Manager -- Call opcodes', () => { }) it('properly executes ovmCALL to CREATE', async () => { - const data: string = `${executeCallToCallContractData}${callMethodId}${callContract2Address32}${createMethodIdAndData}` - - const result = await executionManager.provider.call({ - to: executionManager.address, - data, - gasLimit, - }) + const result: string = await executeCall([ + addressToBytes32Address(callContract2Address), + methodIds.notStaticFriendlyCREATE, + deployTx.data, + ]) log.debug(`RESULT: ${result}`) @@ -227,13 +179,12 @@ describe('Execution Manager -- Call opcodes', () => { }) it('properly executes ovmCALL to CREATE2', async () => { - const data: string = `${executeCallToCallContractData}${callMethodId}${callContract2Address32}${create2MethodIdAndData}` - - const result = await executionManager.provider.call({ - to: executionManager.address, - data, - gasLimit, - }) + const result: string = await executeCall([ + addressToBytes32Address(callContract2Address), + methodIds.notStaticFriendlyCREATE2, + 0, + deployTx.data, + ]) log.debug(`RESULT: ${result}`) @@ -247,32 +198,33 @@ describe('Execution Manager -- Call opcodes', () => { }) describe('ovmDELEGATECALL', async () => { - const delegateCallMethodId: string = ethereumjsAbi - .methodID('makeDelegateCall', []) - .toString('hex') - - const callMethodId: string = ethereumjsAbi - .methodID('makeCall', []) - .toString('hex') - it('properly executes ovmDELEGATECALL to SSTORE', async () => { - const data: string = `${executeCallToCallContractData}${delegateCallMethodId}${callContract2Address32}${sstoreMethodIdAndParams}` + const data: string = + methodIds.executeCall + + encodeRawArguments([ + 0, + 0, + addressToBytes32Address(callContractAddress), + methodIds.makeDelegateCall, + addressToBytes32Address(callContract2Address), + methodIds.notStaticFriendlySSTORE, + sloadKey, + populatedSLOADResult, + ]) // Note: Send transaction vs call so it is persisted await wallet.sendTransaction({ to: executionManager.address, - data, + data: add0x(data), gasLimit, }) // Stored in contract 2 via delegate call but accessed via contract 1 - const fetchData: string = `${executeCallToCallContractData}${callMethodId}${callContractAddress32}${sloadMethodIdAndParams}` - - const result = await executionManager.provider.call({ - to: executionManager.address, - data: fetchData, - gasLimit, - }) + const result: string = await executeCall([ + addressToBytes32Address(callContractAddress), + methodIds.staticFriendlySLOAD, + sloadKey, + ]) log.debug(`Result: [${result}]`) // Should have stored result @@ -281,12 +233,11 @@ describe('Execution Manager -- Call opcodes', () => { 'SLOAD should yield stored result!' ) - const contract2FetchData: string = `${executeCallToCallContractData}${callMethodId}${callContract2Address32}${sloadMethodIdAndParams}` - const contract2Result = await executionManager.provider.call({ - to: executionManager.address, - data: contract2FetchData, - gasLimit, - }) + const contract2Result: string = await executeCall([ + addressToBytes32Address(callContract2Address), + methodIds.staticFriendlySLOAD, + sloadKey, + ]) log.debug(`Result: [${contract2Result}]`) @@ -299,21 +250,33 @@ describe('Execution Manager -- Call opcodes', () => { it('properly executes nested ovmDELEGATECALLs to SSTORE', async () => { // contract 1 delegate calls contract 2 delegate calls contract 3 - const data: string = `${executeCallToCallContractData}${delegateCallMethodId}${callContract2Address32}${delegateCallMethodId}${callContract3Address32}${sstoreMethodIdAndParams}` + const data: string = + methodIds.executeCall + + encodeRawArguments([ + 0, + 0, + addressToBytes32Address(callContractAddress), + methodIds.makeDelegateCall, + addressToBytes32Address(callContract2Address), + methodIds.makeDelegateCall, + addressToBytes32Address(callContract3Address), + methodIds.notStaticFriendlySSTORE, + sloadKey, + populatedSLOADResult, + ]) // Note: Send transaction vs call so it is persisted await wallet.sendTransaction({ to: executionManager.address, - data, + data: add0x(data), gasLimit, }) - const contract1FetchData: string = `${executeCallToCallContractData}${callMethodId}${callContractAddress32}${sloadMethodIdAndParams}` - const contract1Result = await executionManager.provider.call({ - to: executionManager.address, - data: contract1FetchData, - gasLimit, - }) + const contract1Result: string = await executeCall([ + addressToBytes32Address(callContractAddress), + methodIds.staticFriendlySLOAD, + sloadKey, + ]) log.debug(`Result 1: [${contract1Result}]`) @@ -323,12 +286,11 @@ describe('Execution Manager -- Call opcodes', () => { 'SLOAD should yield stored data!' ) - const contract2FetchData: string = `${executeCallToCallContractData}${callMethodId}${callContract2Address32}${sloadMethodIdAndParams}` - const contract2Result = await executionManager.provider.call({ - to: executionManager.address, - data: contract2FetchData, - gasLimit, - }) + const contract2Result: string = await executeCall([ + addressToBytes32Address(callContract2Address), + methodIds.staticFriendlySLOAD, + sloadKey, + ]) log.debug(`Result 2: [${contract2Result}]`) @@ -338,12 +300,11 @@ describe('Execution Manager -- Call opcodes', () => { 'SLOAD should not yield any data (0 x 32 bytes)!' ) - const contract3FetchData: string = `${executeCallToCallContractData}${callMethodId}${callContract3Address32}${sloadMethodIdAndParams}` - const contract3Result = await executionManager.provider.call({ - to: executionManager.address, - data: contract3FetchData, - gasLimit, - }) + const contract3Result: string = await executeCall([ + addressToBytes32Address(callContract3Address), + methodIds.staticFriendlySLOAD, + sloadKey, + ]) log.debug(`Result 3: [${contract3Result}]`) @@ -356,18 +317,16 @@ describe('Execution Manager -- Call opcodes', () => { }) describe('ovmSTATICCALL', async () => { - const staticCallMethodId: string = ethereumjsAbi - .methodID('makeStaticCall', []) - .toString('hex') - it('properly executes ovmSTATICCALL to SLOAD', async () => { - const data: string = `${executeCallToCallContractData}${staticCallMethodId}${callContract2Address32}${sloadMethodIdAndParams}` - - const result = await executionManager.provider.call({ - to: executionManager.address, - data, - gasLimit, - }) + const result = await executeOVMCall(executionManager, 'executeCall', [ + 0, + 0, + addressToBytes32Address(callContractAddress), + methodIds.makeStaticCall, + addressToBytes32Address(callContract2Address), + methodIds.staticFriendlySLOAD, + sloadKey, + ]) log.debug(`Result: [${result}]`) @@ -375,13 +334,17 @@ describe('Execution Manager -- Call opcodes', () => { }) it('properly executes nested ovmSTATICCALL to SLOAD', async () => { - const data: string = `${executeCallToCallContractData}${staticCallMethodId}${callContract2Address32}${staticCallMethodId}${callContract2Address32}${sloadMethodIdAndParams}` - - const result = await executionManager.provider.call({ - to: executionManager.address, - data, - gasLimit, - }) + const result = await executeOVMCall(executionManager, 'executeCall', [ + 0, + 0, + addressToBytes32Address(callContractAddress), + methodIds.makeStaticCall, + addressToBytes32Address(callContract2Address), + methodIds.makeStaticCall, + addressToBytes32Address(callContract2Address), + methodIds.staticFriendlySLOAD, + sloadKey, + ]) log.debug(`Result: [${result}]`) @@ -389,21 +352,39 @@ describe('Execution Manager -- Call opcodes', () => { }) it('successfully makes static call then call', async () => { - const data: string = `${executeCallToCallContractData}${staticCallThenCallMethodId}${callContractAddress32}` + const data: string = + methodIds.executeCall + + encodeRawArguments([ + 0, + 0, + addressToBytes32Address(callContractAddress), + methodIds.makeStaticCallThenCall, + addressToBytes32Address(callContractAddress), + ]) // Should not throw await executionManager.provider.call({ to: executionManager.address, - data, + data: add0x(data), gasLimit, }) }) it('remains in static context when exiting nested static context', async () => { - const data: string = `${executeCallToCallContractData}${staticCallMethodId}${callContractAddress32}${staticCallThenCallMethodId}${callContractAddress32}` + const data: string = + methodIds.executeCall + + encodeRawArguments([ + 0, + 0, + addressToBytes32Address(callContractAddress), + methodIds.makeStaticCall, + addressToBytes32Address(callContractAddress), + methodIds.makeStaticCallThenCall, + addressToBytes32Address(callContractAddress), + ]) await TestUtils.assertThrowsAsync(async () => { - const res = await executionManager.provider.call({ + await executionManager.provider.call({ to: executionManager.address, data, gasLimit, @@ -412,7 +393,18 @@ describe('Execution Manager -- Call opcodes', () => { }) it('fails on ovmSTATICCALL to SSTORE', async () => { - const data: string = `${executeCallToCallContractData}${staticCallMethodId}${callContract2Address32}${sstoreMethodIdAndParams}` + const data: string = + methodIds.executeCall + + encodeRawArguments([ + 0, + 0, + addressToBytes32Address(callContractAddress), + methodIds.makeStaticCall, + addressToBytes32Address(callContractAddress), + methodIds.notStaticFriendlySSTORE, + sloadKey, + populatedSLOADResult, + ]) await TestUtils.assertThrowsAsync(async () => { // Note: Send transaction vs call so it is persisted @@ -425,12 +417,22 @@ describe('Execution Manager -- Call opcodes', () => { }) it('Fails to create on ovmSTATICCALL to CREATE -- tx', async () => { - const data: string = `${executeCallToCallContractData}${staticCallMethodId}${callContract2Address32}${createMethodIdAndData}` + const data: string = + methodIds.executeCall + + encodeRawArguments([ + 0, + 0, + addressToBytes32Address(callContractAddress), + methodIds.makeStaticCall, + addressToBytes32Address(callContractAddress), + methodIds.notStaticFriendlyCREATE, + deployTx.data, + ]) // Note: Send transaction vs call so it is persisted const receipt = await wallet.sendTransaction({ to: executionManager.address, - data, + data: add0x(data), gasLimit, }) @@ -442,11 +444,21 @@ describe('Execution Manager -- Call opcodes', () => { }) it('Fails to create on ovmSTATICCALL to CREATE -- call', async () => { - const data: string = `${executeCallToCallContractData}${staticCallMethodId}${callContract2Address32}${createMethodIdAndData}` + const data: string = + methodIds.executeCall + + encodeRawArguments([ + 0, + 0, + addressToBytes32Address(callContractAddress), + methodIds.makeStaticCall, + addressToBytes32Address(callContractAddress), + methodIds.notStaticFriendlyCREATE, + deployTx.data, + ]) const res = await wallet.provider.call({ to: executionManager.address, - data, + data: add0x(data), gasLimit, }) @@ -455,12 +467,23 @@ describe('Execution Manager -- Call opcodes', () => { }) it('fails on ovmSTATICCALL to CREATE2 -- tx', async () => { - const data: string = `${executeCallToCallContractData}${staticCallMethodId}${callContract2Address32}${create2MethodIdAndData}` + const data: string = + methodIds.executeCall + + encodeRawArguments([ + 0, + 0, + addressToBytes32Address(callContractAddress), + methodIds.makeStaticCall, + addressToBytes32Address(callContractAddress), + methodIds.notStaticFriendlyCREATE2, + 0, + deployTx.data, + ]) // Note: Send transaction vs call so it is persisted const receipt = await wallet.sendTransaction({ to: executionManager.address, - data, + data: add0x(data), gasLimit, }) @@ -472,11 +495,22 @@ describe('Execution Manager -- Call opcodes', () => { }) it('fails on ovmSTATICCALL to CREATE2 -- call', async () => { - const data: string = `${executeCallToCallContractData}${staticCallMethodId}${callContract2Address32}${create2MethodIdAndData}` + const data: string = + methodIds.executeCall + + encodeRawArguments([ + 0, + 0, + addressToBytes32Address(callContractAddress), + methodIds.makeStaticCall, + addressToBytes32Address(callContractAddress), + methodIds.notStaticFriendlyCREATE2, + 0, + deployTx.data, + ]) const res = await wallet.provider.call({ to: executionManager.address, - data, + data: add0x(data), gasLimit, }) @@ -484,4 +518,16 @@ describe('Execution Manager -- Call opcodes', () => { address.should.equal('00'.repeat(32), 'Should be 0 address!') }) }) + + const executeCall = (args: any[]): Promise => { + return executeOVMCall(executionManager, 'executeCall', [ + encodeRawArguments([ + 0, + 0, + addressToBytes32Address(callContractAddress), + methodIds.makeCall, + ...args, + ]), + ]) + } }) diff --git a/packages/ovm/test/contracts/execution-manager.code-opcodes.spec.ts b/packages/ovm/test/contracts/execution-manager.code-opcodes.spec.ts index bcbb0f5b40a9..2def4202d293 100644 --- a/packages/ovm/test/contracts/execution-manager.code-opcodes.spec.ts +++ b/packages/ovm/test/contracts/execution-manager.code-opcodes.spec.ts @@ -4,18 +4,14 @@ import '../setup' import { Address } from '@eth-optimism/rollup-core' import { getLogger, - add0x, BigNumber, hexStrToBuf, remove0x, keccak256, - bufferUtils, - bufToHexString, } from '@eth-optimism/core-utils' import { Contract, ContractFactory, ethers } from 'ethers' import { createMockProvider, deployContract, getWallets } from 'ethereum-waffle' -import * as ethereumjsAbi from 'ethereumjs-abi' /* Contract Imports */ import * as ExecutionManager from '../../build/contracts/ExecutionManager.json' @@ -24,9 +20,9 @@ import * as DummyContract from '../../build/contracts/DummyContract.json' /* Internal Imports */ import { manuallyDeployOvmContract, - getUnsignedTransactionCalldata, DEFAULT_ETHNODE_GAS_LIMIT, - gasLimit, + executeOVMCall, + addressToBytes32Address, } from '../helpers' import { GAS_LIMIT, OPCODE_WHITELIST_MASK } from '../../src/app' @@ -81,19 +77,11 @@ describe('Execution Manager -- Code-related opcodes', () => { describe('getContractCodeSize', async () => { it('properly gets contract code size for the contract we expect', async () => { - const methodId: string = ethereumjsAbi - .methodID('ovmEXTCODESIZE', []) - .toString('hex') - - const encodedParams: string = - '00'.repeat(12) + remove0x(dummyContractAddress) - const data: string = `0x${methodId}${encodedParams}` - - const result: string = await executionManager.provider.call({ - to: add0x(executionManager.address), - data, - gasLimit, - }) + const result: string = await executeOVMCall( + executionManager, + 'ovmEXTCODESIZE', + [addressToBytes32Address(dummyContractAddress)] + ) log.debug(`Resulting size: [${result}]`) const codeSize: number = new BigNumber(remove0x(result), 'hex').toNumber() @@ -106,19 +94,11 @@ describe('Execution Manager -- Code-related opcodes', () => { describe('getContractCodeHash', async () => { it('properly gets contract code hash for the contract we expect', async () => { - const methodId: string = ethereumjsAbi - .methodID('ovmEXTCODEHASH', []) - .toString('hex') - - const encodedParams: string = - '00'.repeat(12) + remove0x(dummyContractAddress) - const data: string = `0x${methodId}${encodedParams}` - - const codeHash: string = await executionManager.provider.call({ - to: add0x(executionManager.address), - data, - gasLimit, - }) + const codeHash: string = await executeOVMCall( + executionManager, + 'ovmEXTCODEHASH', + [addressToBytes32Address(dummyContractAddress)] + ) log.debug(`Resulting hash: [${codeHash}]`) const hash: string = keccak256(dummyContractBytecode.toString('hex')) @@ -129,28 +109,53 @@ describe('Execution Manager -- Code-related opcodes', () => { describe('ovmEXTCODECOPY', async () => { it('properly gets all contract code via EXTCODECOPY', async () => { - const methodId: string = ethereumjsAbi - .methodID('ovmEXTCODECOPY', []) - .toString('hex') - - const address: string = '00'.repeat(12) + remove0x(dummyContractAddress) - const index: string = '00'.repeat(32) - const length: string = bufferUtils - .numberToBuffer(dummyContractBytecode.length) - .toString('hex') - const encodedParams: string = `${address}${index}${length}` - - const data: string = `0x${methodId}${remove0x(encodedParams)}` - - const code: string = await executionManager.provider.call({ - to: executionManager.address, - data, - gasLimit, - }) + const code: string = await executeOVMCall( + executionManager, + 'ovmEXTCODECOPY', + [ + addressToBytes32Address(dummyContractAddress), + 0, + dummyContractBytecode.length, + ] + ) log.debug(`Resulting code: [${code}]`) const codeBuff: Buffer = hexStrToBuf(code) codeBuff.should.eql(dummyContractBytecode, 'Incorrect code!') }) + + it('returns zeroed bytes if the range is out of bounds', async () => { + const code: string = await executeOVMCall( + executionManager, + 'ovmEXTCODECOPY', + [ + addressToBytes32Address(dummyContractAddress), + 0, + dummyContractBytecode.length + 3, + ] + ) + log.debug(`Resulting code: [${code}]`) + + const codeBuff: Buffer = hexStrToBuf(code) + const bytecodeWithZeroedBytes = Buffer.concat([ + dummyContractBytecode, + Buffer.alloc(3), + ]) + codeBuff.should.eql(bytecodeWithZeroedBytes, 'Incorrect code!') + }) + + it('returns zeroed bytes if the provided address is invalid', async () => { + const offset = 0 + const length = 99 + const code: string = await executeOVMCall( + executionManager, + 'ovmEXTCODECOPY', + [addressToBytes32Address('11'.repeat(20)), 0, length] + ) + log.debug(`Resulting code: [${code}]`) + + const codeBuff: Buffer = hexStrToBuf(code) + codeBuff.should.eql(Buffer.alloc(length), 'Incorrect code!') + }) }) }) diff --git a/packages/ovm/test/contracts/execution-manager.context-opcodes.spec.ts b/packages/ovm/test/contracts/execution-manager.context-opcodes.spec.ts index b461caa91927..cd150df16b74 100644 --- a/packages/ovm/test/contracts/execution-manager.context-opcodes.spec.ts +++ b/packages/ovm/test/contracts/execution-manager.context-opcodes.spec.ts @@ -3,16 +3,14 @@ import { should } from '../setup' /* External Imports */ import { Address } from '@eth-optimism/rollup-core' import { - bufferUtils, - bufToHexString, getLogger, + hexStrToNumber, remove0x, TestUtils, } from '@eth-optimism/core-utils' import { Contract, ContractFactory, ethers } from 'ethers' import { createMockProvider, deployContract, getWallets } from 'ethereum-waffle' -import * as ethereumjsAbi from 'ethereumjs-abi' /* Contract Imports */ import * as ExecutionManager from '../../build/contracts/ExecutionManager.json' @@ -21,25 +19,32 @@ import * as ContextContract from '../../build/contracts/ContextContract.json' /* Internal Imports */ import { manuallyDeployOvmContract, - getUnsignedTransactionCalldata, - bytes32AddressToAddress, addressToBytes32Address, DEFAULT_ETHNODE_GAS_LIMIT, - gasLimit, + executeOVMCall, + encodeRawArguments, + encodeMethodId, } from '../helpers' import { GAS_LIMIT, OPCODE_WHITELIST_MASK } from '../../src/app' +import { fromPairs } from 'lodash' export const abi = new ethers.utils.AbiCoder() const log = getLogger('execution-manager-context', true) -const executeCallMethodId: string = ethereumjsAbi - .methodID('executeCall', []) - .toString('hex') - -const callThroughEMMethodId: string = ethereumjsAbi - .methodID('callThroughExecutionManager', []) - .toString('hex') +const methodIds = fromPairs( + [ + 'callThroughExecutionManager', + 'executeCall', + 'getADDRESS', + 'getCALLER', + 'getGASLIMIT', + 'getQueueOrigin', + 'getTIMESTAMP', + 'ovmADDRESS', + 'ovmCALLER', + ].map((methodId) => [methodId, encodeMethodId(methodId)]) +) /********* * TESTS * @@ -48,12 +53,9 @@ const callThroughEMMethodId: string = ethereumjsAbi describe('Execution Manager -- Context opcodes', () => { const provider = createMockProvider({ gasLimit: DEFAULT_ETHNODE_GAS_LIMIT }) const [wallet] = getWallets(provider) - const defaultTimestampAndQueueOrigin: string = '00'.repeat(64) // Create pointers to our execution manager & simple copier contract let executionManager: Contract - let contract: ContractFactory - let contract2: ContractFactory let contractAddress: Address let contract2Address: Address let contractAddress32: string @@ -80,7 +82,7 @@ describe('Execution Manager -- Context opcodes', () => { log.debug(`Contract address: [${contractAddress}]`) // Also set our simple copier Ethers contract so we can generate unsigned transactions - contract = new ContractFactory( + const contract = new ContractFactory( ContextContract.abi as any, ContextContract.bytecode ) @@ -97,7 +99,7 @@ describe('Execution Manager -- Context opcodes', () => { log.debug(`Contract address: [${contractAddress}]`) // Also set our simple copier Ethers contract so we can generate unsigned transactions - contract2 = new ContractFactory( + const contract2 = new ContractFactory( ContextContract.abi as any, ContextContract.bytecode ) @@ -108,42 +110,18 @@ describe('Execution Manager -- Context opcodes', () => { describe('ovmCALLER', async () => { it('reverts when CALLER is not set', async () => { - const callerMethodId: string = ethereumjsAbi - .methodID('ovmCALLER', []) - .toString('hex') - - const data = `0x${executeCallMethodId}${defaultTimestampAndQueueOrigin}${remove0x( - contractAddress32 - )}${callerMethodId}` - await TestUtils.assertThrowsAsync(async () => { - await executionManager.provider.call({ - to: executionManager.address, - data, - gasLimit, - }) + await executeCall([contractAddress32, methodIds.ovmCALLER]) }) }) it('properly retrieves CALLER when caller is set', async () => { - const callerMethodId: string = ethereumjsAbi - .methodID('getCALLER', []) - .toString('hex') - - const internalCall: string = `${callThroughEMMethodId}${remove0x( - contract2Address32 - )}${callerMethodId}` - - const data = `0x${executeCallMethodId}${defaultTimestampAndQueueOrigin}${remove0x( - contractAddress32 - )}${internalCall}` - - const result = await executionManager.provider.call({ - to: executionManager.address, - data, - gasLimit, - }) - + const result = await executeCall([ + contractAddress32, + methodIds.callThroughExecutionManager, + contract2Address32, + methodIds.getCALLER, + ]) log.debug(`CALLER result: ${result}`) should.exist(result, 'Result should exist!') @@ -153,41 +131,18 @@ describe('Execution Manager -- Context opcodes', () => { describe('ovmADDRESS', async () => { it('reverts when ADDRESS is not set', async () => { - const addressMethodId: string = ethereumjsAbi - .methodID('ovmADDRESS', []) - .toString('hex') - - const data = `0x${executeCallMethodId}${defaultTimestampAndQueueOrigin}${remove0x( - contractAddress32 - )}${addressMethodId}` - await TestUtils.assertThrowsAsync(async () => { - await executionManager.provider.call({ - to: executionManager.address, - data, - gasLimit, - }) + await executeCall([contractAddress32, methodIds.ovmADDRESS]) }) }) it('properly retrieves ADDRESS when address is set', async () => { - const addressMethodId: string = ethereumjsAbi - .methodID('getADDRESS', []) - .toString('hex') - - const internalCall: string = `${callThroughEMMethodId}${remove0x( - contract2Address32 - )}${addressMethodId}` - - const data = `0x${executeCallMethodId}${defaultTimestampAndQueueOrigin}${remove0x( - contractAddress32 - )}${internalCall}` - - const result = await executionManager.provider.call({ - to: executionManager.address, - data, - gasLimit, - }) + const result = await executeCall([ + contractAddress32, + methodIds.callThroughExecutionManager, + contract2Address32, + methodIds.getADDRESS, + ]) log.debug(`ADDRESS result: ${result}`) @@ -198,136 +153,78 @@ describe('Execution Manager -- Context opcodes', () => { describe('ovmTIMESTAMP', async () => { it('reverts when TIMESTAMP is not set', async () => { - const timestampMethodId: string = ethereumjsAbi - .methodID('getTIMESTAMP', []) - .toString('hex') - - const internalCall: string = `${callThroughEMMethodId}${remove0x( - contract2Address32 - )}${timestampMethodId}` - - const data = `0x${executeCallMethodId}${defaultTimestampAndQueueOrigin}${remove0x( - contractAddress32 - )}${internalCall}` - await TestUtils.assertThrowsAsync(async () => { - await executionManager.provider.call({ - to: executionManager.address, - data, - gasLimit, - }) + await executeCall([ + contractAddress32, + methodIds.callThroughExecutionManager, + contract2Address32, + methodIds.getTIMESTAMP, + ]) }) }) it('properly retrieves TIMESTAMP when timestamp is set', async () => { - const timestampMethodId: string = ethereumjsAbi - .methodID('getTIMESTAMP', []) - .toString('hex') - - const internalCall: string = `${callThroughEMMethodId}${remove0x( - contractAddress32 - )}${timestampMethodId}` - - const timestamp: string = '00'.repeat(30) + '1111' - const queueOrigin: string = '00'.repeat(32) - - const data = `0x${executeCallMethodId}${timestamp}${queueOrigin}${remove0x( - contract2Address32 - )}${internalCall}` - - const result = await executionManager.provider.call({ - to: executionManager.address, - data, - gasLimit, - }) + const timestamp: number = 1582890922 + const result = await executeOVMCall(executionManager, 'executeCall', [ + timestamp, + 0, + contractAddress32, + methodIds.callThroughExecutionManager, + contract2Address32, + methodIds.getTIMESTAMP, + ]) log.debug(`TIMESTAMP result: ${result}`) should.exist(result, 'Result should exist!') - remove0x(result).should.equal(timestamp, 'Timestamps do not match.') + hexStrToNumber(result).should.equal(timestamp, 'Timestamps do not match.') }) }) describe('ovmGASLIMIT', async () => { it('properly retrieves GASLIMIT', async () => { - const gasLimitMethodId: string = ethereumjsAbi - .methodID('getGASLIMIT', []) - .toString('hex') - - const internalCall: string = `${callThroughEMMethodId}${remove0x( - contractAddress32 - )}${gasLimitMethodId}` - - const data = `0x${executeCallMethodId}${defaultTimestampAndQueueOrigin}${remove0x( - contract2Address32 - )}${internalCall}` - - const result = await executionManager.provider.call({ - to: executionManager.address, - data, - gasLimit, - }) + const result = await executeCall([ + contractAddress32, + methodIds.callThroughExecutionManager, + contract2Address32, + methodIds.getGASLIMIT, + ]) log.debug(`GASLIMIT result: ${result}`) should.exist(result, 'Result should exist!') - const expected: string = bufToHexString( - bufferUtils.numberToBuffer(GAS_LIMIT) - ) - result.should.equal(expected, 'Gas limits do not match.') + hexStrToNumber(result).should.equal(GAS_LIMIT, 'Gas limits do not match.') }) }) describe('ovmQueueOrigin', async () => { it('gets Queue Origin when it is 0', async () => { - const timestampMethodId: string = ethereumjsAbi - .methodID('getQueueOrigin', []) - .toString('hex') - - const internalCall: string = `${callThroughEMMethodId}${remove0x( - contract2Address32 - )}${timestampMethodId}` - - const data = `0x${executeCallMethodId}${defaultTimestampAndQueueOrigin}${remove0x( - contractAddress32 - )}${internalCall}` - - const result = await executionManager.provider.call({ - to: executionManager.address, - data, - gasLimit, - }) + const queueOrigin: string = '00'.repeat(32) + const result = await executeOVMCall(executionManager, 'executeCall', [ + 0, + queueOrigin, + contractAddress32, + methodIds.callThroughExecutionManager, + contract2Address32, + methodIds.getQueueOrigin, + ]) log.debug(`QUEUE ORIGIN result: ${result}`) should.exist(result, 'Result should exist!') - remove0x(result).should.equal( - defaultTimestampAndQueueOrigin.substr(64), - 'Queue origins do not match.' - ) + remove0x(result).should.equal(queueOrigin, 'Queue origins do not match.') }) it('properly retrieves Queue Origin when queue origin is set', async () => { - const timestampMethodId: string = ethereumjsAbi - .methodID('getQueueOrigin', []) - .toString('hex') - - const internalCall: string = `${callThroughEMMethodId}${remove0x( - contractAddress32 - )}${timestampMethodId}` - - const timestamp: string = '00'.repeat(32) const queueOrigin: string = '00'.repeat(30) + '1111' - - const data = `0x${executeCallMethodId}${timestamp}${queueOrigin}${remove0x( - contract2Address32 - )}${internalCall}` - - const result = await executionManager.provider.call({ - to: executionManager.address, - data, - gasLimit, - }) + const result = await executeOVMCall(executionManager, 'executeCall', [ + 0, + queueOrigin, + contractAddress32, + methodIds.callThroughExecutionManager, + contract2Address32, + methodIds.getQueueOrigin, + ]) log.debug(`QUEUE ORIGIN result: ${result}`) @@ -335,4 +232,10 @@ describe('Execution Manager -- Context opcodes', () => { remove0x(result).should.equal(queueOrigin, 'Queue origins do not match.') }) }) + + const executeCall = (args: any[]): Promise => { + return executeOVMCall(executionManager, 'executeCall', [ + encodeRawArguments([0, 0, ...args]), + ]) + } }) diff --git a/packages/ovm/test/contracts/execution-manager.create-opcodes.spec.ts b/packages/ovm/test/contracts/execution-manager.create-opcodes.spec.ts index 8e791e305c02..24009d8a12f6 100644 --- a/packages/ovm/test/contracts/execution-manager.create-opcodes.spec.ts +++ b/packages/ovm/test/contracts/execution-manager.create-opcodes.spec.ts @@ -2,8 +2,7 @@ import '../setup' /* External Imports */ import { createMockProvider, deployContract, getWallets } from 'ethereum-waffle' -import { getLogger, remove0x } from '@eth-optimism/core-utils' -import * as ethereumjsAbi from 'ethereumjs-abi' +import { getLogger, remove0x, add0x } from '@eth-optimism/core-utils' import { Contract, ContractFactory } from 'ethers' /* Contract Imports */ @@ -15,7 +14,21 @@ const log = getLogger('execution-manager-create', true) /* Internal Imports */ import { OPCODE_WHITELIST_MASK, GAS_LIMIT } from '../../src/app' -import { DEFAULT_ETHNODE_GAS_LIMIT, gasLimit } from '../helpers' +import { + DEFAULT_ETHNODE_GAS_LIMIT, + gasLimit, + executeOVMCall, + encodeMethodId, + encodeRawArguments, +} from '../helpers' +import { fromPairs } from 'lodash' + +const methodIds = fromPairs( + ['ovmCREATE', 'ovmCREATE2'].map((methodId) => [ + methodId, + encodeMethodId(methodId), + ]) +) /********* * TESTS * @@ -59,18 +72,9 @@ describe('ExecutionManager -- Create opcodes', () => { describe('ovmCREATE', async () => { it('returns created address when passed valid bytecode', async () => { - const methodId: string = ethereumjsAbi - .methodID('ovmCREATE', []) - .toString('hex') - - const data = `0x${methodId}${remove0x(deployTx.data)}` - - // Now actually apply it to our execution manager - const result = await executionManager.provider.call({ - to: executionManager.address, - data, - gasLimit, - }) + const result = await executeOVMCall(executionManager, 'ovmCREATE', [ + deployTx.data, + ]) log.debug(`Result: [${result}]`) @@ -80,11 +84,9 @@ describe('ExecutionManager -- Create opcodes', () => { }) it('returns 0 address when passed invalid bytecode', async () => { - const methodId: string = ethereumjsAbi - .methodID('ovmCREATE', []) - .toString('hex') - - const data = `0x${methodId}${remove0x(deployInvalidTx.data)}` + const data = add0x( + methodIds.ovmCREATE + encodeRawArguments([deployInvalidTx.data]) + ) // Now actually apply it to our execution manager const result = await executionManager.provider.call({ @@ -103,11 +105,9 @@ describe('ExecutionManager -- Create opcodes', () => { describe('ovmCREATE2', async () => { it('returns created address when passed salt and bytecode', async () => { - const methodId: string = ethereumjsAbi - .methodID('ovmCREATE2', []) - .toString('hex') - - const data = `0x${methodId}${'00'.repeat(32)}${remove0x(deployTx.data)}` + const data = add0x( + methodIds.ovmCREATE2 + encodeRawArguments([0, deployTx.data]) + ) // Now actually apply it to our execution manager const result = await executionManager.provider.call({ @@ -124,13 +124,9 @@ describe('ExecutionManager -- Create opcodes', () => { }) it('returns 0 address when passed salt and invalid bytecode', async () => { - const methodId: string = ethereumjsAbi - .methodID('ovmCREATE2', []) - .toString('hex') - - const data = `0x${methodId}${'00'.repeat(32)}${remove0x( - deployInvalidTx.data - )}` + const data = add0x( + methodIds.ovmCREATE2 + encodeRawArguments([0, deployInvalidTx.data]) + ) // Now actually apply it to our execution manager const result = await executionManager.provider.call({ diff --git a/packages/ovm/test/contracts/execution-manager.executeCall.spec.ts b/packages/ovm/test/contracts/execution-manager.executeCall.spec.ts index 0515ca7d82b4..d4673f935cf2 100644 --- a/packages/ovm/test/contracts/execution-manager.executeCall.spec.ts +++ b/packages/ovm/test/contracts/execution-manager.executeCall.spec.ts @@ -2,13 +2,7 @@ import '../setup' /* External Imports */ import { Address } from '@eth-optimism/rollup-core' -import { - add0x, - getLogger, - padToLength, - remove0x, - ZERO_ADDRESS, -} from '@eth-optimism/core-utils' +import { getLogger, padToLength, ZERO_ADDRESS } from '@eth-optimism/core-utils' import { Contract, ContractFactory, ethers } from 'ethers' import { createMockProvider, deployContract, getWallets } from 'ethereum-waffle' @@ -22,8 +16,6 @@ import * as DummyContract from '../../build/contracts/DummyContract.json' import { manuallyDeployOvmContract, getUnsignedTransactionCalldata, - getTransactionResult, - numberToHexWord, DEFAULT_ETHNODE_GAS_LIMIT, } from '../helpers' import { GAS_LIMIT, CHAIN_ID, OPCODE_WHITELIST_MASK } from '../../src/app' diff --git a/packages/ovm/test/contracts/execution-manager.purity-checking.spec.ts b/packages/ovm/test/contracts/execution-manager.purity-checking.spec.ts index 60e93a770f45..c8fd660fba5e 100644 --- a/packages/ovm/test/contracts/execution-manager.purity-checking.spec.ts +++ b/packages/ovm/test/contracts/execution-manager.purity-checking.spec.ts @@ -13,7 +13,6 @@ import * as DummyContract from '../../build/contracts/DummyContract.json' /* Internal Imports */ import { OPCODE_WHITELIST_MASK, GAS_LIMIT } from '../../src/app' import { - manuallyDeployOvmContract, DEFAULT_ETHNODE_GAS_LIMIT, manuallyDeployOvmContractReturnReceipt, didCreateSucceed, diff --git a/packages/ovm/test/contracts/execution-manager.recover-eoa-address.spec.ts b/packages/ovm/test/contracts/execution-manager.recover-eoa-address.spec.ts index af7fae4a5e66..206e25e7d873 100644 --- a/packages/ovm/test/contracts/execution-manager.recover-eoa-address.spec.ts +++ b/packages/ovm/test/contracts/execution-manager.recover-eoa-address.spec.ts @@ -7,7 +7,6 @@ import { createMockProvider, deployContract, getWallets } from 'ethereum-waffle' /* Contract Imports */ import * as ExecutionManager from '../../build/contracts/ExecutionManager.json' -import * as DummyContract from '../../build/contracts/DummyContract.json' /* Internal Imports */ import { GAS_LIMIT, CHAIN_ID, OPCODE_WHITELIST_MASK } from '../../src/app' diff --git a/packages/ovm/test/contracts/execution-manager.storage-opcodes.spec.ts b/packages/ovm/test/contracts/execution-manager.storage-opcodes.spec.ts index 29eac8145b6f..de30062cb2b7 100644 --- a/packages/ovm/test/contracts/execution-manager.storage-opcodes.spec.ts +++ b/packages/ovm/test/contracts/execution-manager.storage-opcodes.spec.ts @@ -2,19 +2,29 @@ import '../setup' /* External Imports */ import { createMockProvider, deployContract, getWallets } from 'ethereum-waffle' -import { abi, getLogger, remove0x } from '@eth-optimism/core-utils' -import * as ethereumjsAbi from 'ethereumjs-abi' -import { Contract, ContractFactory } from 'ethers' +import { abi, getLogger, add0x } from '@eth-optimism/core-utils' +import { Contract } from 'ethers' /* Contract Imports */ import * as ExecutionManager from '../../build/contracts/ExecutionManager.json' -import * as SimpleStorage from '../../build/contracts/SimpleStorage.json' /* Internal Imports */ import { OPCODE_WHITELIST_MASK, GAS_LIMIT } from '../../src/app' -import { DEFAULT_ETHNODE_GAS_LIMIT, gasLimit } from '../helpers' +import { + DEFAULT_ETHNODE_GAS_LIMIT, + gasLimit, + encodeMethodId, + encodeRawArguments, +} from '../helpers' +import { fromPairs } from 'lodash' const log = getLogger('execution-manager-storage', true) +const methodIds = fromPairs( + ['ovmSSTORE', 'ovmSLOAD'].map((methodId) => [ + methodId, + encodeMethodId(methodId), + ]) +) /********* * TESTS * @@ -40,14 +50,10 @@ describe('ExecutionManager -- Storage opcodes', () => { }) const sstore = async (): Promise => { - const methodId: string = ethereumjsAbi - .methodID('ovmSSTORE', []) - .toString('hex') - - const data = `0x${methodId}${remove0x(ONE_FILLED_BYTES_32)}${remove0x( - TWO_FILLED_BYTES_32 - )}` - + const data = add0x( + encodeMethodId('ovmSSTORE') + + encodeRawArguments([ONE_FILLED_BYTES_32, TWO_FILLED_BYTES_32]) + ) // Now actually apply it to our execution manager const tx = await wallet.sendTransaction({ to: executionManager.address, @@ -84,11 +90,10 @@ describe('ExecutionManager -- Storage opcodes', () => { it('loads a value immediately after it is stored', async () => { await sstore() - const methodId: string = ethereumjsAbi - .methodID('ovmSLOAD', []) - .toString('hex') - - const data = `0x${methodId}${remove0x(ONE_FILLED_BYTES_32)}` + const data = add0x( + encodeMethodId('ovmSLOAD') + + encodeRawArguments([ONE_FILLED_BYTES_32, TWO_FILLED_BYTES_32]) + ) // Now actually apply it to our execution manager const result = await executionManager.provider.call({ diff --git a/packages/ovm/test/contracts/rlp-encode.spec.ts b/packages/ovm/test/contracts/rlp-encode.spec.ts index 6a4c1adf6084..08b38985a1b3 100644 --- a/packages/ovm/test/contracts/rlp-encode.spec.ts +++ b/packages/ovm/test/contracts/rlp-encode.spec.ts @@ -9,7 +9,6 @@ const log = getLogger('rlp-encode', true) /* Contract Imports */ import * as RLPEncode from '../../build/contracts/RLPEncode.json' -import { Contract, ContractFactory, Wallet, utils } from 'ethers' /* Begin tests */ describe('RLP Encoder', () => { diff --git a/packages/ovm/test/contracts/state-manager.spec.ts b/packages/ovm/test/contracts/state-manager.spec.ts index 621ad387c84b..5761695ea32e 100644 --- a/packages/ovm/test/contracts/state-manager.spec.ts +++ b/packages/ovm/test/contracts/state-manager.spec.ts @@ -2,13 +2,11 @@ import '../setup' /* External Imports */ import { getLogger } from '@eth-optimism/core-utils' -import { newInMemoryDB, SparseMerkleTreeImpl } from '@eth-optimism/core-db' import { createMockProvider, deployContract, getWallets } from 'ethereum-waffle' /* Contract Imports */ import * as ExecutionManager from '../../build/contracts/ExecutionManager.json' -import * as PurityChecker from '../../build/contracts/PurityChecker.json' -import { Contract, ContractFactory, Wallet, utils } from 'ethers' +import { Contract } from 'ethers' /* Internal Imports */ import { GAS_LIMIT, OPCODE_WHITELIST_MASK } from '../../src/app' diff --git a/packages/ovm/test/contracts/tx-origin.spec.ts b/packages/ovm/test/contracts/tx-origin.spec.ts index 3e675d4dc583..56f512adcf87 100644 --- a/packages/ovm/test/contracts/tx-origin.spec.ts +++ b/packages/ovm/test/contracts/tx-origin.spec.ts @@ -4,8 +4,7 @@ import '../setup' import { Address } from '@eth-optimism/rollup-core' import { createMockProvider, deployContract, getWallets } from 'ethereum-waffle' import { getLogger, add0x } from '@eth-optimism/core-utils' -import { Contract, ContractFactory, ethers } from 'ethers' -import { TransactionReceipt } from 'ethers/providers' +import { Contract, ContractFactory } from 'ethers' import * as ethereumjsAbi from 'ethereumjs-abi' /* Contract Imports */ @@ -19,7 +18,6 @@ import { getUnsignedTransactionCalldata, signTransation, DEFAULT_ETHNODE_GAS_LIMIT, - executeEOACall, } from '../helpers' import { CHAIN_ID, GAS_LIMIT, OPCODE_WHITELIST_MASK } from '../../src/app' diff --git a/packages/ovm/test/helpers.ts b/packages/ovm/test/helpers.ts index 52f0f244f611..af0c75bd3f72 100644 --- a/packages/ovm/test/helpers.ts +++ b/packages/ovm/test/helpers.ts @@ -23,9 +23,7 @@ import { import { Transaction } from 'ethers/utils' /* Contract Imports */ -import * as SimpleStorage from '../build/contracts/SimpleStorage.json' import { - convertInternalLogsToOvmLogs, GAS_LIMIT, CHAIN_ID, internalTxReceiptToOvmTxReceipt, @@ -322,3 +320,54 @@ export const buildLog = ( data: encodedData, } } + +/** + * Executes a call in the OVM + * @param The name of the function to call + * @param The function arguments + * @returns The return value of the function executed + */ +export const executeOVMCall = async ( + executionManager: Contract, + functionName: string, + args: any[] +): Promise => { + const data: string = add0x( + encodeMethodId(functionName) + encodeRawArguments(args) + ) + + return executionManager.provider.call({ + to: executionManager.address, + data, + gasLimit, + }) +} + +/** + * Computes the method id of a function name and encodes it as + * a hexidecimal string. + * @param The name of the function + * @returns The hex-encoded methodId + */ +export const encodeMethodId = (functionName: string): string => { + return ethereumjsAbi.methodID(functionName, []).toString('hex') +} + +/** + * Encodes an array of function arguments into a hex string. + * @param any[] An array of arguments + * @returns The hex-encoded function arguments + */ +export const encodeRawArguments = (args: any[]): string => { + return args + .map((arg) => { + if (Number.isInteger(arg)) { + return bufferUtils.numberToBuffer(arg).toString('hex') + } else if (arg && arg.startsWith('0x')) { + return remove0x(arg) + } else { + return arg + } + }) + .join('') +} diff --git a/yarn.lock b/yarn.lock index abd6145327a3..c875a150402e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3251,7 +3251,7 @@ debug@3.1.0, debug@=3.1.0: dependencies: ms "2.0.0" -debug@3.2.6, debug@^3.1.0, debug@^3.2.6: +debug@3.2.6, debug@^3.1.0: version "3.2.6" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== @@ -3377,11 +3377,6 @@ deep-equal@~1.1.1: object-keys "^1.1.1" regexp.prototype.flags "^1.2.0" -deep-extend@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" - integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== - default-compare@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/default-compare/-/default-compare-1.0.0.tgz#cb61131844ad84d84788fb68fd01681ca7781a2f" @@ -3513,11 +3508,6 @@ detect-indent@^5.0.0: resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-5.0.0.tgz#3871cc0a6a002e8c3e5b3cf7f336264675f06b9d" integrity sha1-OHHMCmoALow+Wzz38zYmRnXwa50= -detect-libc@^1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" - integrity sha1-+hN8S9aY7fVc1c0CrFWfkaTEups= - dezalgo@^1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/dezalgo/-/dezalgo-1.0.3.tgz#7f742de066fc748bc8db820569dddce49bf0d456" @@ -5669,7 +5659,7 @@ humanize-ms@^1.2.1: dependencies: ms "^2.0.0" -iconv-lite@0.4.24, iconv-lite@^0.4.24, iconv-lite@^0.4.4, iconv-lite@~0.4.13: +iconv-lite@0.4.24, iconv-lite@^0.4.24, iconv-lite@~0.4.13: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== @@ -5766,7 +5756,7 @@ inherits@2.0.3: resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= -ini@^1.3.2, ini@^1.3.4, ini@~1.3.0: +ini@^1.3.2, ini@^1.3.4: version "1.3.5" resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw== @@ -7529,15 +7519,6 @@ napi-macros@~2.0.0: resolved "https://registry.yarnpkg.com/napi-macros/-/napi-macros-2.0.0.tgz#2b6bae421e7b96eb687aa6c77a7858640670001b" integrity sha512-A0xLykHtARfueITVDernsAWdtIMbOJgKgcluwENp3AlsKN/PloyO10HtmoqnFAQAcxPkgZN7wdfPfEd0zNGxbg== -needle@^2.2.1: - version "2.3.2" - resolved "https://registry.yarnpkg.com/needle/-/needle-2.3.2.tgz#3342dea100b7160960a450dc8c22160ac712a528" - integrity sha512-DUzITvPVDUy6vczKKYTnWc/pBZ0EnjMJnQ3y+Jo5zfKFimJs7S3HFCxCRZYB9FUZcrzUQr3WsmvZgddMEIZv6w== - dependencies: - debug "^3.2.6" - iconv-lite "^0.4.4" - sax "^1.2.4" - negotiator@0.6.2: version "0.6.2" resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" @@ -7615,22 +7596,6 @@ node-gyp@^5.0.2: tar "^4.4.12" which "^1.3.1" -node-pre-gyp@*: - version "0.14.0" - resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.14.0.tgz#9a0596533b877289bcad4e143982ca3d904ddc83" - integrity sha512-+CvDC7ZttU/sSt9rFjix/P05iS43qHCOOGzcr3Ry99bXG7VX953+vFyEuph/tfqoYu8dttBkE86JSKBO2OzcxA== - dependencies: - detect-libc "^1.0.2" - mkdirp "^0.5.1" - needle "^2.2.1" - nopt "^4.0.1" - npm-packlist "^1.1.6" - npmlog "^4.0.2" - rc "^1.2.7" - rimraf "^2.6.1" - semver "^5.3.0" - tar "^4.4.2" - nopt@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.1.tgz#d0d4685afd5415193c8c7505602d0d17cd64474d" @@ -7714,7 +7679,7 @@ npm-normalize-package-bin@^1.0.0, npm-normalize-package-bin@^1.0.1: semver "^5.6.0" validate-npm-package-name "^3.0.0" -npm-packlist@^1.1.6, npm-packlist@^1.4.4: +npm-packlist@^1.4.4: version "1.4.8" resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.4.8.tgz#56ee6cc135b9f98ad3d51c1c95da22bbb9b2ef3e" integrity sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A== @@ -7739,7 +7704,7 @@ npm-run-path@^2.0.0: dependencies: path-key "^2.0.0" -npmlog@^4.0.2, npmlog@^4.1.2: +npmlog@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg== @@ -8604,16 +8569,6 @@ raw-body@2.4.0: iconv-lite "0.4.24" unpipe "1.0.0" -rc@^1.2.7: - version "1.2.8" - resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" - integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== - dependencies: - deep-extend "^0.6.0" - ini "~1.3.0" - minimist "^1.2.0" - strip-json-comments "~2.0.1" - read-cmd-shim@^1.0.1: version "1.0.5" resolved "https://registry.yarnpkg.com/read-cmd-shim/-/read-cmd-shim-1.0.5.tgz#87e43eba50098ba5a32d0ceb583ab8e43b961c16" @@ -9012,7 +8967,7 @@ retry@^0.10.0: resolved "https://registry.yarnpkg.com/retry/-/retry-0.10.1.tgz#e76388d217992c252750241d3d3956fed98d8ff4" integrity sha1-52OI0heZLCUnUCQdPTlW/tmNj/Q= -rimraf@2, rimraf@^2.2.8, rimraf@^2.5.4, rimraf@^2.6.1, rimraf@^2.6.2, rimraf@^2.6.3: +rimraf@2, rimraf@^2.2.8, rimraf@^2.5.4, rimraf@^2.6.2, rimraf@^2.6.3: version "2.7.1" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== @@ -9089,11 +9044,6 @@ safe-regex@^1.1.0: resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== -sax@^1.2.4: - version "1.2.4" - resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" - integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== - scrypt-js@2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/scrypt-js/-/scrypt-js-2.0.3.tgz#bb0040be03043da9a012a2cea9fc9f852cfc87d4" @@ -9748,7 +9698,7 @@ strip-indent@^2.0.0: resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-2.0.0.tgz#5ef8db295d01e6ed6cbf7aab96998d7822527b68" integrity sha1-XvjbKV0B5u1sv3qrlpmNeCJSe2g= -strip-json-comments@2.0.1, strip-json-comments@~2.0.1: +strip-json-comments@2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= @@ -9887,7 +9837,7 @@ tar@^2.1.1: fstream "^1.0.12" inherits "2" -tar@^4.0.2, tar@^4.4.10, tar@^4.4.12, tar@^4.4.2, tar@^4.4.8: +tar@^4.0.2, tar@^4.4.10, tar@^4.4.12, tar@^4.4.8: version "4.4.13" resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.13.tgz#43b364bc52888d555298637b10d60790254ab525" integrity sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==