Skip to content

Commit

Permalink
Transpiling Hardcoded Constructor Values (#169)
Browse files Browse the repository at this point in the history
* unable to replicate bug

* tests passing with new files

* linting, renaming

* Update packages/rollup-dev-tools/test/transpiler/abi-encoded-constants-transpilation.spec.ts

Co-authored-by: Kevin Ho <kevinjho1996@gmail.com>

* incorporate minor PR feedback

Co-authored-by: Kevin Ho <kevinjho1996@gmail.com>
  • Loading branch information
ben-chain and K-Ho authored Jun 30, 2020
1 parent 3c564fc commit 7c08e11
Show file tree
Hide file tree
Showing 7 changed files with 179 additions and 24 deletions.
16 changes: 11 additions & 5 deletions packages/rollup-dev-tools/src/tools/transpiler/transpiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -293,9 +293,9 @@ export class TranspilerImpl implements Transpiler {
`fixing CODECOPY(constant) at PC 0x${getPCOfEVMBytecodeIndex(
index,
taggedBytecode
).toString(16)}. Setting new index to 0x${bufToHexString(
newConstantOffsetBuf
)}`
).toString(16)}. The constant's value is: ${bufToHexString(
theConstant
)} Setting new index to ${bufToHexString(newConstantOffsetBuf)}`
)
bytecodeToReturn[index].consumedBytes = newConstantOffsetBuf
}
Expand Down Expand Up @@ -510,7 +510,7 @@ export class TranspilerImpl implements Transpiler {
TranspilerImpl.createError(
index,
TranspilationErrors.DETECTED_CONSTANT_OOB,
`Thought we detected a CODECOP(a CODECOPY(constant) pattern at starting at PC: 0x${getPCOfEVMBytecodeIndex(
`Thought we detected a CODECOPY(constant) pattern at starting at PC: 0x${getPCOfEVMBytecodeIndex(
index,
bytecode
).toString(
Expand All @@ -527,7 +527,13 @@ export class TranspilerImpl implements Transpiler {
`detected a CODECOPY(constant) pattern at starting at PC: 0x${getPCOfEVMBytecodeIndex(
index,
bytecode
).toString(16)}. Its val: ${bufToHexString(theConstant)}`
).toString(
16
)}. The original offset is: 0x${offsetForCODECOPY.toString(
16
)} and original length is: 0x${lengthforCODECOPY.toString(
16
)} which produced this value: ${bufToHexString(theConstant)}`
)
taggedBytecode[index] = {
opcode: op.opcode,
Expand Down
44 changes: 44 additions & 0 deletions packages/rollup-dev-tools/src/tools/transpiler/util.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
import { TranspilationError } from '../../types/transpiler'
import {
bufToHexString,
getLogger,
hexStrToBuf,
Logger,
} from '@eth-optimism/core-utils'

export const BIG_ENOUGH_GAS_LIMIT: number = 100000000

const log: Logger = getLogger('transpiler-util')

/**
* Util function to create TranspilationErrors.
*
Expand All @@ -21,3 +29,39 @@ export const createError = (
message,
}
}

export const stripAuxData = (
bytecode: Buffer,
buildJSON: any,
isDeployedBytecode: boolean
): Buffer => {
const auxDataObject = buildJSON.evm.legacyAssembly['.data']
const auxData = auxDataObject['0']['.auxdata']
let bytecodeWithoutAuxdata: Buffer
const auxdataObjectKeys = Object.keys(auxDataObject)
// deployed bytecode always has auxdata at the end, but constuction code may not.
if (auxdataObjectKeys.length <= 1 || isDeployedBytecode) {
log.debug(`Auxdata is at EOF, removing entirely from bytecode...`)
const split = bufToHexString(bytecode).split(auxData)
bytecodeWithoutAuxdata = hexStrToBuf(split[0])
} else {
log.debug(
`Auxdata is not at EOF, replacing it with 0s to preserve remaining data...`
)
const auxDataBuf: Buffer = hexStrToBuf(auxData)
const auxDataPosition = bytecode.indexOf(auxDataBuf)
log.debug(
`buf: ${bufToHexString(
auxDataBuf
)}, position: ${auxDataPosition}, length: ${auxDataBuf.byteLength}`
)
bytecodeWithoutAuxdata = Buffer.from(bytecode)
bytecodeWithoutAuxdata.fill(
0,
auxDataPosition,
auxDataPosition + auxDataBuf.byteLength
)
}

return bytecodeWithoutAuxdata
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
pragma solidity ^0.5.16;

contract AbiEncodedConstantInConstructor {
bytes32 public hash;

constructor() public {
hash = keccak256(
abi.encode(
keccak256('EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)'),
1
)
);
}

function getConstant() external returns(bytes32) {
return hash;
}
}
15 changes: 5 additions & 10 deletions packages/rollup-dev-tools/test/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import {
TranspilerImpl,
OpcodeReplacer,
OpcodeReplacerImpl,
stripAuxData,
} from '../src/'

import { getPUSHBuffer, getPUSHIntegerOp } from '../src'
Expand Down Expand Up @@ -444,14 +445,6 @@ export const setMemory = (toSet: Buffer): EVMBytecode => {
return op
}

export const stripAuxData = (bytecode: Buffer, buildJSON: any): Buffer => {
const auxData = buildJSON.evm.legacyAssembly['.data']['0']['.auxdata']
const bytecodeWithoutAuxdata: Buffer = hexStrToBuf(
bufToHexString(bytecode).split(auxData)[0]
)
return bytecodeWithoutAuxdata
}

export const transpileAndDeployInitcode = async (
contractBuildJSON: any,
constructorParams: any[],
Expand All @@ -466,11 +459,13 @@ export const transpileAndDeployInitcode = async (

const bytecodeStripped: Buffer = stripAuxData(
hexStrToBuf(contractBuildJSON.bytecode),
contractBuildJSON
contractBuildJSON,
false
)
const deployedBytecodeStripped: Buffer = stripAuxData(
hexStrToBuf(contractBuildJSON.evm.deployedBytecode.object),
contractBuildJSON
contractBuildJSON,
true
)

const initcodeTranspilationResult: TranspilationResult = transpiler.transpile(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
/* External Imports */
import { ethers } from 'ethers'
import { bufToHexString, getLogger } from '@eth-optimism/core-utils'
import {
formatBytecode,
Opcode,
bufferToBytecode,
} from '@eth-optimism/rollup-core'
import * as AbiEncodedConstantInConstructor from '../contracts/build/AbiEncodedConstantInConstructor.json'

/* Internal Imports */
import {
EvmIntrospectionUtil,
ExecutionResult,
EvmIntrospectionUtilImpl,
} from '../../src'

import { TranspilerImpl, OpcodeWhitelistImpl } from '../../src/tools/transpiler'
import { transpileAndDeployInitcode, mockSSTOREReplacer } from '../helpers'

const log = getLogger(`test-constructor-params-new`)

const getGetterReturnedVal = async (
deployedAddress: Buffer,
methodId: string,
evmUtil: EvmIntrospectionUtil
): Promise<Buffer> => {
const callRes: ExecutionResult = await evmUtil.callContract(
bufToHexString(deployedAddress),
methodId
)
if (!!callRes.error) {
throw new Error(
`call to ${methodId} failed with evmUtil Error: ${callRes.error}`
)
}
return callRes.result
}

describe('Solitity contracts should have hardcoded values correctly accessible in transpiled initcode', () => {
let evmUtil: EvmIntrospectionUtil

const opcodeWhitelist = new OpcodeWhitelistImpl(Opcode.ALL_OP_CODES)
const transpiler = new TranspilerImpl(opcodeWhitelist, mockSSTOREReplacer)
let deployedGetterAddress: Buffer
beforeEach(async () => {
evmUtil = await EvmIntrospectionUtilImpl.create()
log.debug(
`transpiling and deploying initcode which should store hash in constructor`
)
deployedGetterAddress = await transpileAndDeployInitcode(
AbiEncodedConstantInConstructor,
[],
[],
transpiler,
evmUtil
)
const code: Buffer = await evmUtil.getContractDeployedBytecode(
deployedGetterAddress
)
log.debug(
`Initcode transpiled and deployed. The code is:\n${formatBytecode(
bufferToBytecode(code)
)}`
)
})

it(`The hash of an abi.encode(hardcoded) should be correct and retrievable if stored during constructor()`, async () => {
const expectedStoredVal = ethers.utils.keccak256(
ethers.utils.defaultAbiCoder.encode(
['bytes32', 'uint256'],
[
ethers.utils.keccak256(
Buffer.from(
ethers.utils.toUtf8Bytes(
'EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)'
)
)
),
1,
]
)
)
const res = await getGetterReturnedVal(
deployedGetterAddress,
'getConstant',
evmUtil
)
bufToHexString(res).should.eq(expectedStoredVal)
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import {
EvmIntrospectionUtilImpl,
getPUSHBuffer,
getPUSHIntegerOp,
stripAuxData,
} from '../../src'
import {
ErroredTranspilation,
Expand All @@ -50,11 +51,7 @@ import {
OpcodeReplacerImpl,
OpcodeWhitelistImpl,
} from '../../src/tools/transpiler'
import {
transpileAndDeployInitcode,
stripAuxData,
mockSSTOREReplacer,
} from '../helpers'
import { transpileAndDeployInitcode, mockSSTOREReplacer } from '../helpers'

const abi = new ethers.utils.AbiCoder()
const log = getLogger(`constructor-transpilation`)
Expand Down Expand Up @@ -263,7 +260,7 @@ const getManuallyTranspiledAndInitcodeTranspiledDeployedBytecode = async (

// pad because this is currently done by the transpiler
const deployedBytecodeTranspilationResult: TranspilationResult = transpiler.transpileRawBytecode(
stripAuxData(deployedBytecode, contractBuildJSON)
stripAuxData(deployedBytecode, contractBuildJSON, true)
)
if (!deployedBytecodeTranspilationResult.succeeded) {
throw new Error(
Expand Down
10 changes: 7 additions & 3 deletions packages/solc-transpiler/src/compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
TranspilationError,
Transpiler,
TranspilerImpl,
stripAuxData,
} from '@eth-optimism/rollup-dev-tools'
import {
bufToHexString,
Expand Down Expand Up @@ -393,9 +394,12 @@ const getBytecode = (
const code: string = isDeployedBytecode
? contractSolcOutput.evm.deployedBytecode.object
: contractSolcOutput.evm.bytecode.object

const auxData = getAuxData(contractSolcOutput)
return !!auxData ? code.split(auxData)[0] : code
const strippedCode: Buffer = stripAuxData(
hexStrToBuf(code),
contractSolcOutput,
isDeployedBytecode
)
return bufToHexString(strippedCode)
} catch (e) {
return undefined
}
Expand Down

0 comments on commit 7c08e11

Please sign in to comment.