diff --git a/packages/rollup-dev-tools/src/tools/transpiler/opcode-whitelist.ts b/packages/rollup-dev-tools/src/tools/transpiler/opcode-whitelist.ts index 6cf93aee8c5..6e7b934565f 100644 --- a/packages/rollup-dev-tools/src/tools/transpiler/opcode-whitelist.ts +++ b/packages/rollup-dev-tools/src/tools/transpiler/opcode-whitelist.ts @@ -71,6 +71,7 @@ const defaultWhitelist: EVMOpcode[] = [ Opcode.EXP, Opcode.EXTCODECOPY, Opcode.EXTCODESIZE, + Opcode.EXTCODEHASH, Opcode.GAS, Opcode.GT, Opcode.INVALID, @@ -93,6 +94,7 @@ const defaultWhitelist: EVMOpcode[] = [ Opcode.MULMOD, Opcode.NOT, Opcode.OR, + Opcode.ORIGIN, Opcode.PC, Opcode.POP, Opcode.PUSH1, diff --git a/packages/rollup-dev-tools/src/tools/transpiler/transpiler.ts b/packages/rollup-dev-tools/src/tools/transpiler/transpiler.ts index 35d36d5d4be..36c03d853de 100644 --- a/packages/rollup-dev-tools/src/tools/transpiler/transpiler.ts +++ b/packages/rollup-dev-tools/src/tools/transpiler/transpiler.ts @@ -135,16 +135,21 @@ export class TranspilerImpl implements Transpiler { const deployedBytecodeTranspilationResult: TaggedTranspilationResult = this.transpileBytecodePreservingTags( taggedDeployedEVMBytecode ) - // todo error handle however above + if (!deployedBytecodeTranspilationResult.succeeded) { errors.push( ...(deployedBytecodeTranspilationResult as ErroredTranspilation).errors ) + return { + succeeded: false, + errors, + } } const transpiledDeployedBytecode: EVMBytecode = deployedBytecodeTranspilationResult.bytecodeWithTags - log.debug(`Fixing constant indices for transpiled deployed bytecode...`) + log.debug(`Fixing the constant indices for transpiled deployed bytecode...`) + log.debug(`errors are: ${JSON.stringify(errors)}`) // Note that fixTaggedConstantOffsets() scrubs all fixed tags, so we do not re-fix when we use finalTranspiledDeployedBytecode to reconstruct the returned initcode const finalTranspiledDeployedBytecode: EVMBytecode = this.fixTaggedConstantOffsets( transpiledDeployedBytecode as EVMBytecode, @@ -176,6 +181,10 @@ export class TranspilerImpl implements Transpiler { ...(constructorInitLogicTranspilationResult as ErroredTranspilation) .errors ) + return { + succeeded: false, + errors, + } } const transpiledConstructorInitLogic: EVMBytecode = constructorInitLogicTranspilationResult.bytecodeWithTags @@ -241,6 +250,7 @@ export class TranspilerImpl implements Transpiler { taggedBytecode: EVMBytecode, errors ): EVMBytecode { + log.debug(`tagged input: ${formatBytecode(taggedBytecode)}`) const inputAsBuf: Buffer = bytecodeToBuffer(taggedBytecode) const bytecodeToReturn: EVMBytecode = [] for (const [index, op] of taggedBytecode.entries()) { diff --git a/packages/rollup-dev-tools/test/transpiler/constants-transpilation.spec.ts b/packages/rollup-dev-tools/test/transpiler/constants-transpilation.spec.ts index a1cb8ac61c3..cf5bf32838a 100644 --- a/packages/rollup-dev-tools/test/transpiler/constants-transpilation.spec.ts +++ b/packages/rollup-dev-tools/test/transpiler/constants-transpilation.spec.ts @@ -148,7 +148,7 @@ describe('Solitity contracts should have constants correctly accessible when usi const bytesMemoryConstB: Buffer = hexStrToBuf( 'BBBbeedfeedBBBBBBbeedfeedBBBBBBbeedfeedBBBBBBbeedfeedBBBBBBbeedfeedBBBBBBbeedfeedBBBBBBbeedfeedBBB' ) - it('should work for the first bytes memory constant', async () => { + it('should work for the second bytes memory constant', async () => { const retrievedBytesMemoryBVal: Buffer = await getGetterReturnedVal( deployedGetterAddress, 'getBytesMemoryConstantB', diff --git a/packages/rollup-full-node/package.json b/packages/rollup-full-node/package.json index 43d13e42f1c..edfcedfc8f5 100644 --- a/packages/rollup-full-node/package.json +++ b/packages/rollup-full-node/package.json @@ -8,11 +8,13 @@ ], "scripts": { "all": "yarn clean && yarn build && yarn test && yarn fix && yarn lint", - "build": "waffle waffle-config.json && tsc -p .", - "clean": "rimraf build/ && rimraf test/contracts/build", + "build": "waffle waffle-config-untranspiled.json && waffle waffle-config-transpiled.json && tsc -p .", + "clean": "rimraf build/ && yarn clean-contracts", "fix": "prettier --config ../../prettier-config.json --write 'index.ts' '{src,test}/**/*.ts'", "lint": "tslint --format stylish --project .", - "test": "waffle waffle-config.json && mocha --require source-map-support/register --require ts-node/register 'test/**/*.spec.ts' --timeout 8000 --exit", + "test": "yarn build-contracts && mocha --require source-map-support/register --require ts-node/register 'test/**/*.spec.ts' --timeout 8000 --exit", + "clean-contracts": "rimraf ./test/contracts/build && mkdir ./test/contracts/build && mkdir ./test/contracts/build/transpiled && mkdir ./test/contracts/build/untranspiled", + "build-contracts": "yarn clean-contracts && waffle waffle-config-untranspiled.json && waffle waffle-config-transpiled.json", "server:aggregator": "env DEBUG=\"info:*,error:*\" node ./exec/aggregator.js", "server:fullnode": "env DEBUG=\"info:*,error:*\" node ./exec/fullnode.js", "server:fullnode:debug": "env DEBUG=\"info:*,error:*,debug:*\" node ./exec/fullnode.js" @@ -45,6 +47,7 @@ "fastpriorityqueue": "^0.6.3" }, "devDependencies": { + "@eth-optimism/solc-transpiler": "^0.0.1-alpha.12", "@types/abstract-leveldown": "^5.0.1", "@types/chai": "^4.1.7", "@types/mocha": "^5.2.7", diff --git a/packages/rollup-full-node/test/app/handler.spec.ts b/packages/rollup-full-node/test/app/handler.spec.ts index c6ff0c15840..5656d9e8efb 100644 --- a/packages/rollup-full-node/test/app/handler.spec.ts +++ b/packages/rollup-full-node/test/app/handler.spec.ts @@ -3,8 +3,12 @@ import '../setup' import { getLogger } from '@eth-optimism/core-utils' /* Internal Imports */ -import { FullnodeRpcServer, DefaultWeb3Handler } from '../../src/app' -import * as SimpleStorage from '../contracts/build/SimpleStorage.json' +import { + FullnodeRpcServer, + deployOvmContract, + DefaultWeb3Handler, +} from '../../src/app' +import * as SimpleStorage from '../contracts/build/untranspiled/SimpleStorage.json' import { ethers, ContractFactory } from 'ethers' import { FullnodeHandler } from '../../src/types' diff --git a/packages/rollup-full-node/test/contracts/transpiled/CallerGetter.sol b/packages/rollup-full-node/test/contracts/transpiled/CallerGetter.sol new file mode 100644 index 00000000000..544b8c3e3a5 --- /dev/null +++ b/packages/rollup-full-node/test/contracts/transpiled/CallerGetter.sol @@ -0,0 +1,8 @@ +pragma solidity ^0.5.0; +import "./CallerReturner.sol"; + +contract CallerGetter { + function getMsgSenderFrom(address _callerReturner) public view returns(address) { + return CallerReturner(_callerReturner).getMsgSender(); + } +} \ No newline at end of file diff --git a/packages/rollup-full-node/test/contracts/transpiled/CallerReturner.sol b/packages/rollup-full-node/test/contracts/transpiled/CallerReturner.sol new file mode 100644 index 00000000000..64ae5c4a46f --- /dev/null +++ b/packages/rollup-full-node/test/contracts/transpiled/CallerReturner.sol @@ -0,0 +1,7 @@ +pragma solidity ^0.5.0; + +contract CallerReturner { + function getMsgSender() public view returns(address) { + return msg.sender; + } +} \ No newline at end of file diff --git a/packages/rollup-full-node/test/contracts/transpiled/ExtCode.sol b/packages/rollup-full-node/test/contracts/transpiled/ExtCode.sol new file mode 100644 index 00000000000..14aeb4978c1 --- /dev/null +++ b/packages/rollup-full-node/test/contracts/transpiled/ExtCode.sol @@ -0,0 +1,19 @@ +pragma solidity ^0.5.0; + +contract ExtCode { + function getExtCodeSizeOf(address _addr) public returns(uint) { + uint toReturn; + assembly { + toReturn:= extcodesize(_addr) + } + return toReturn; + } + + function getExtCodeHashOf(address _addr) public returns(bytes32) { + bytes32 toReturn; + assembly { + toReturn:= extcodehash(_addr) + } + return toReturn; + } +} \ No newline at end of file diff --git a/packages/rollup-full-node/test/contracts/transpiled/OriginGetter.sol b/packages/rollup-full-node/test/contracts/transpiled/OriginGetter.sol new file mode 100644 index 00000000000..15ee4b4fbba --- /dev/null +++ b/packages/rollup-full-node/test/contracts/transpiled/OriginGetter.sol @@ -0,0 +1,7 @@ +pragma solidity ^0.5.0; + +contract OriginGetter { + function getTxOrigin() public view returns(address) { + return tx.origin; + } +} \ No newline at end of file diff --git a/packages/rollup-full-node/test/contracts/transpiled/SelfAware.sol b/packages/rollup-full-node/test/contracts/transpiled/SelfAware.sol new file mode 100644 index 00000000000..6c1b5dbdb77 --- /dev/null +++ b/packages/rollup-full-node/test/contracts/transpiled/SelfAware.sol @@ -0,0 +1,7 @@ +pragma solidity ^0.5.0; + +contract SelfAware { + function getMyAddress() public view returns(address) { + return address(this); + } +} \ No newline at end of file diff --git a/packages/rollup-full-node/test/contracts/transpiled/SimpleCaller.sol b/packages/rollup-full-node/test/contracts/transpiled/SimpleCaller.sol new file mode 100644 index 00000000000..2215e51fc04 --- /dev/null +++ b/packages/rollup-full-node/test/contracts/transpiled/SimpleCaller.sol @@ -0,0 +1,8 @@ +pragma solidity ^0.5.0; +import "./SimpleStorage.sol"; + +contract SimpleCaller { + function doGetStorageCall(address _target, bytes32 key) public view returns(bytes32) { + return SimpleStorage(_target).getStorage(key); + } +} \ No newline at end of file diff --git a/packages/rollup-full-node/test/contracts/transpiled/SimpleStorage.sol b/packages/rollup-full-node/test/contracts/transpiled/SimpleStorage.sol new file mode 100644 index 00000000000..823562b6049 --- /dev/null +++ b/packages/rollup-full-node/test/contracts/transpiled/SimpleStorage.sol @@ -0,0 +1,12 @@ +pragma solidity ^0.5.0; + +contract SimpleStorage { + mapping(bytes32 => bytes32) public builtInStorage; + function setStorage(bytes32 key, bytes32 value) public { + builtInStorage[key] = value; + } + + function getStorage(bytes32 key) public view returns (bytes32) { + return builtInStorage[key]; + } +} diff --git a/packages/rollup-full-node/test/contracts/transpiled/TimeGetter.sol b/packages/rollup-full-node/test/contracts/transpiled/TimeGetter.sol new file mode 100644 index 00000000000..00500c0457d --- /dev/null +++ b/packages/rollup-full-node/test/contracts/transpiled/TimeGetter.sol @@ -0,0 +1,7 @@ +pragma solidity ^0.5.0; + +contract TimeGetter { + function getTimestamp() public view returns(uint256) { + return block.timestamp; + } +} \ No newline at end of file diff --git a/packages/rollup-full-node/test/contracts/SimpleStorage.sol b/packages/rollup-full-node/test/contracts/untranspiled/SimpleStorage.sol similarity index 100% rename from packages/rollup-full-node/test/contracts/SimpleStorage.sol rename to packages/rollup-full-node/test/contracts/untranspiled/SimpleStorage.sol diff --git a/packages/rollup-full-node/test/integration/transpiler-integration.spec.ts b/packages/rollup-full-node/test/integration/transpiler-integration.spec.ts new file mode 100644 index 00000000000..78a0405fbbd --- /dev/null +++ b/packages/rollup-full-node/test/integration/transpiler-integration.spec.ts @@ -0,0 +1,95 @@ +import '../../../rollup-dev-tools/test/setup' +/* External Imports */ +import { + getLogger, + remove0x, + bufToHexString, + hexStrToBuf, +} from '@eth-optimism/core-utils' +import { + Address, + formatBytecode, + bufferToBytecode, +} from '@eth-optimism/rollup-core' + +/* Contract Imports */ + +import * as SimpleStorage from '../contracts/build/transpiled/SimpleStorage.json' +import * as SimpleCaller from '../contracts/build/transpiled/SimpleCaller.json' +import * as SelfAware from '../contracts/build/transpiled/SelfAware.json' +import * as CallerGetter from '../contracts/build/transpiled/CallerGetter.json' +import * as OriginGetter from '../contracts/build/transpiled/OriginGetter.json' +import * as CallerReturner from '../contracts/build/transpiled/CallerReturner.json' +import * as TimeGetter from '../contracts/build/transpiled/TimeGetter.json' + +import { createMockProvider, getWallets, deployContract } from '../../' + +describe(`Various opcodes should be usable in combination with transpiler and full node`, () => { + let provider + let wallet + + beforeEach(async () => { + provider = await createMockProvider() + const wallets = getWallets(provider) + wallet = wallets[0] + }) + + afterEach(() => { + provider.closeOVM() + }) + + // TEST BASIC FUNCTIONALITIES + + it('should process cross-ovm-contract calls', async () => { + const simpleStorage = await deployContract(wallet, SimpleStorage, [], []) + const simpleCaller = await deployContract(wallet, SimpleCaller, [], []) + + const storageKey = '0x' + '01'.repeat(32) + const storageValue = '0x' + '02'.repeat(32) + + await simpleStorage.setStorage(storageKey, storageValue) + + const res = await simpleCaller.doGetStorageCall( + simpleStorage.address, + storageKey + ) + res.should.equal(storageValue) + }) + it('should work for address(this)', async () => { + const selfAware = await deployContract(wallet, SelfAware, [], []) + const deployedAddress: Address = selfAware.address + const returnedAddress: Address = await selfAware.getMyAddress() + deployedAddress.should.equal(returnedAddress) + }) + it.skip('should work for block.timestamp', async () => { + // todo, once we handle timestamps, unskip this test + const timeGetter = await deployContract(wallet, TimeGetter, [], []) + const time = await timeGetter.getTimestamp() + time._hex.should.equal('???') + }) + it('should work for msg.sender', async () => { + const callerReturner = await deployContract(wallet, CallerReturner, [], []) + const callerGetter = await deployContract(wallet, CallerGetter, [], []) + const result = await callerGetter.getMsgSenderFrom(callerReturner.address) + result.should.equal(callerGetter.address) + }) + it('should work for tx.origin', async () => { + const originGetter = await deployContract(wallet, OriginGetter, [], []) + const result = await originGetter.getTxOrigin() + result.should.equal(wallet.address) + }) + + // SIMPLE STORAGE TEST + it('should set storage & retrieve the value', async () => { + const simpleStorage = await deployContract(wallet, SimpleStorage, [], []) + // Create some constants we will use for storage + const storageKey = '0x' + '01'.repeat(32) + const storageValue = '0x' + '02'.repeat(32) + // Set storage with our new storage elements + await simpleStorage.setStorage(storageKey, storageValue) + // Get the storage + const res = await simpleStorage.getStorage(storageKey) + // Verify we got the value! + res.should.equal(storageValue) + }) +}) diff --git a/packages/rollup-full-node/waffle-config-transpiled.json b/packages/rollup-full-node/waffle-config-transpiled.json new file mode 100644 index 00000000000..f46bd52e831 --- /dev/null +++ b/packages/rollup-full-node/waffle-config-transpiled.json @@ -0,0 +1,14 @@ +{ + "sourcesPath": "./test/contracts/transpiled", + "targetPath": "./test/contracts/build/transpiled", + "npmPath": "../../node_modules", + "solcVersion": "../../node_modules/@eth-optimism/solc-transpiler", + "compilerOptions": { + "outputSelection": { + "*": { + "*": ["*"] + } + }, + "executionManagerAddress": "0xA193E42526F1FEA8C99AF609dcEabf30C1c29fAA" + } +} diff --git a/packages/rollup-full-node/waffle-config-untranspiled.json b/packages/rollup-full-node/waffle-config-untranspiled.json new file mode 100644 index 00000000000..dc9e37200bd --- /dev/null +++ b/packages/rollup-full-node/waffle-config-untranspiled.json @@ -0,0 +1,12 @@ +{ + "sourcesPath": "./test/contracts/untranspiled", + "targetPath": "./test/contracts/build/untranspiled", + "npmPath": "../../node_modules", + "compilerOptions": { + "outputSelection": { + "*": { + "*": ["*"] + } + } + } +} diff --git a/packages/rollup-full-node/waffle-config.json b/packages/rollup-full-node/waffle-config.json deleted file mode 100644 index 31feed29fcb..00000000000 --- a/packages/rollup-full-node/waffle-config.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "sourcesPath": "./test/contracts", - "targetPath": "./test/contracts/build", - "npmPath": "../../node_modules" -}