Commit 82390b3a authored by Kelvin Fichter's avatar Kelvin Fichter

Linting and storage mod cleanup

parent a7760482
node_modules/
artifacts/
cache/
yarn-error.log
\ No newline at end of file
......@@ -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/execution/OVM_ExecutionManager/context-opcodes.spec.ts\"",
"test:contracts": "buidler test",
"lint": "tslint --format stylish --project .",
"fix": "prettier --config prettier-config.json --write \"buidler.config.ts\" \"{src,test}/**/*.ts\""
},
......@@ -16,6 +16,7 @@
"@nomiclabs/buidler-ethers": "^2.0.0",
"@nomiclabs/buidler-waffle": "^2.0.0",
"@types/chai": "^4.2.12",
"@types/lodash": "^4.14.161",
"@types/mocha": "^8.0.3",
"@types/node": "^14.6.0",
"assert": "^2.0.0",
......
......@@ -4,13 +4,10 @@ import {
TestDefinition,
OVM_TX_GAS_LIMIT,
NON_NULL_BYTES32,
REVERT_FLAGS,
ZERO_ADDRESS,
VERIFIED_EMPTY_CONTRACT_HASH,
} from '../../../../helpers'
const DUMMY_REVERT_DATA =
'0xdeadbeef1e5420deadbeef1e5420deadbeef1e5420deadbeef1e5420deadbeef1e5420'
const GAS_METADATA_ADDRESS = '0x06a506a506a506a506a506a506a506a506a506a5'
enum GasMetadataKey {
......
......@@ -5,7 +5,7 @@ import { ethers } from '@nomiclabs/buidler'
import { Contract } from 'ethers'
/* Internal Imports */
import { SAFETY_CHECKER_TEST_JSON } from '../../../helpers'
import { SAFETY_CHECKER_TEST_JSON } from '../../../data'
describe('OVM_SafetyChecker', () => {
let OVM_SafetyChecker: Contract
......
/* External Imports */
import { ethers } from '@nomiclabs/buidler'
import { hexZeroPad } from 'ethers/lib/utils'
export const encodeRevertData = (
flag: number,
......@@ -15,8 +15,6 @@ export const encodeRevertData = (
}
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
......
......@@ -3,7 +3,7 @@ import { ethers } from 'ethers'
import { defaultAccounts } from 'ethereum-waffle'
/* Internal Imports */
import { makeHexString, makeAddress } from './byte-utils'
import { makeHexString, makeAddress } from './utils'
export const DEFAULT_ACCOUNTS = defaultAccounts
export const DEFAULT_ACCOUNTS_BUIDLER = defaultAccounts.map((account) => {
......
/* Internal Imports */
import { DUMMY_BYTES32 } from './bytes32'
import { ZERO_ADDRESS, NON_ZERO_ADDRESS } from '../constants'
import { makeAddress } from '../byte-utils'
import { makeAddress } from '../utils'
import { OVMAccount } from '../types/ovm-types'
export const DUMMY_ACCOUNTS: Array<{
......
......@@ -3,8 +3,8 @@ export * from './types'
export * from './constants'
export * from './proxy'
export * from './mocks'
export * from './buffer-utils'
export * from './byte-utils'
export * from './utils'
export * from './codec'
export * from './data'
export * from './test-utils'
export * from './test-runner'
export * from './storage'
export * from './solidity'
......@@ -3,7 +3,7 @@ import bre from '@nomiclabs/buidler'
import { ethers, Contract, ContractFactory } from 'ethers'
/* Internal Imports */
import { toHexString, fromHexString } from '../buffer-utils'
import { toHexString, fromHexString } from '../utils'
import { MockContract, MockContractFunction } from './mock-contract.types'
/**
......
......@@ -6,7 +6,7 @@ import { FunctionFragment, ParamType } from 'ethers/lib/utils'
/* Internal Imports */
import { MockContract, MockContractFunction } from './mock-contract.types'
import { bindMockContractToVM, bindMockWatcherToVM } from './mock-binding'
import { SolidityCompiler, getDefaultCompiler, compile } from '../compilation'
import { SolidityCompiler, getDefaultCompiler, compile } from '../solidity'
/**
* Generates contract code for a mock contract.
......
/* External Imports */
import bre, { ethers } from '@nomiclabs/buidler'
import { readArtifact } from '@nomiclabs/buidler/internal/artifacts'
import { Contract, BigNumber, ContractFactory } from 'ethers'
import { keccak256, defaultAbiCoder } from 'ethers/lib/utils'
import { keccak256 } from 'ethers/lib/utils'
import _ from 'lodash'
import { remove0x } from '../byte-utils'
import { readArtifact } from '@nomiclabs/buidler/internal/artifacts'
/* Internal Imports */
import { remove0x } from '../utils'
const getFlattenedKeys = (depth: number, value: any): string[] => {
if (depth === 0) {
return []
}
const getStorageLayout = async (name: string): Promise<any> => {
const artifact: any = await readArtifact(bre.config.paths.artifacts, name)
return artifact.storageLayout
}
let keys = Object.keys(value)
if (depth > 1) {
keys = keys.concat(getFlattenedKeys(depth - 1, Object.values(value)[0]))
}
export const getModifiableStorageFactory = async (
name: string
): Promise<ContractFactory> => {
const contractFactory = await ethers.getContractFactory(name)
const proxyFactory = await ethers.getContractFactory(
'Helper_ModifiableStorage'
)
return keys
}
const originalDeployFn = contractFactory.deploy.bind(contractFactory)
contractFactory.deploy = async (...args: any[]): Promise<Contract> => {
const originalDefinePropertyFn = Object.defineProperty
Object.defineProperty = (obj: any, pname: string, prop: any): void => {
if (prop.writable === false) {
prop.writable = true
}
const toHexString32 = (
value: string | number | BigNumber | boolean
): string => {
if (typeof value === 'string') {
return '0x' + remove0x(value).padStart(64, '0').toLowerCase()
} else if (typeof value === 'boolean') {
return toHexString32(value ? 1 : 0)
} else {
return toHexString32(BigNumber.from(value).toHexString())
}
}
originalDefinePropertyFn(obj, pname, prop)
}
const getFlattenedValues = (depth: number, value: any): any[] => {
if (depth > 0) {
return getFlattenedValues(depth - 1, Object.values(value)[0])
}
const contract: any = await originalDeployFn(...args)
const proxy = await proxyFactory.deploy(contract.address)
if (typeof value === 'object' && value !== null) {
return Object.keys(value).map((key) => {
return {
label: key,
value: toHexString32(value[key]),
}
})
} else {
return [
{
label: 'default',
value: toHexString32(value),
},
]
}
}
Object.defineProperty = originalDefinePropertyFn
const getStorageSlotHash = (
slot: number,
depth: number,
value: any
): string => {
let keys = []
if (typeof value === 'object' && value !== null) {
keys = getFlattenedKeys(depth, value)
contract.address = proxy.address
contract.resolvedAddress = proxy.address
contract.__setStorageSlot = proxy.__setStorageSlot.bind(proxy)
contract.__getStorageSlot = proxy.__getStorageSlot.bind(proxy)
contract.__setContractStorage = async (obj: any) => {
await setContractStorage(contract, await getStorageLayout(name), obj)
}
contract.__checkContractStorage = async (obj: any) => {
await checkContractStorage(contract, await getStorageLayout(name), obj)
}
return contract
}
if (keys.length === 0) {
return defaultAbiCoder.encode(['uint256'], [slot])
} else {
let slotHash = toHexString32(slot)
for (const key of keys) {
slotHash = keccak256(toHexString32(key) + remove0x(slotHash))
return contractFactory
}
const flattenObject = (
obj: Object,
prefix: string = '',
res: Object = {}
): Object => {
if (_.isString(obj) || _.isNumber(obj) || _.isBoolean(obj)) {
res[prefix] = obj
return res
} else if (_.isArray(obj)) {
for (let i = 0; i < obj.length; i++) {
const pre = _.isEmpty(prefix) ? `${i}` : `${prefix}.${i}`
flattenObject(obj[i], pre, res)
}
return slotHash
return res
} else if (_.isPlainObject(obj)) {
for (const key of Object.keys(obj)) {
const pre = _.isEmpty(prefix) ? key : `${prefix}.${key}`
flattenObject(obj[key], pre, res)
}
return res
} else {
throw new Error('Cannot flatten unsupported object type.')
}
}
const parseInputSlots = (layout: any, inputTypeName: string): any[] => {
const inputType = layout.types[inputTypeName]
interface InputSlot {
label: string
slot: number
}
const getInputSlots = (
storageLayout: any,
inputTypeName: string
): InputSlot[] => {
const inputType = storageLayout.types[inputTypeName]
if (inputType.encoding === 'mapping') {
return parseInputSlots(layout, inputType.value)
return getInputSlots(storageLayout, inputType.value)
} else if (inputType.encoding === 'inplace') {
if (inputType.members) {
return inputType.members.map((member: any) => {
......@@ -99,217 +116,112 @@ const parseInputSlots = (layout: any, inputTypeName: string): any[] => {
}
}
export const getModifiableStorageFactory = async (
name: string
): Promise<ContractFactory> => {
const contractFactory = await ethers.getContractFactory(name)
const proxyFactory = await ethers.getContractFactory(
'Helper_ModifiableStorage'
)
interface StorageSlot {
label: string
hash: string
value: string
}
const originalDeploy = contractFactory.deploy.bind(contractFactory)
contractFactory.deploy = async (...args: any[]): Promise<Contract> => {
const originalDefinePropertyFn = Object.defineProperty
Object.defineProperty = (
object: any,
propName: string,
props: any
): void => {
if (props.writable === false) {
props.writable = true
}
const toHexString32 = (
value: string | number | BigNumber | boolean
): string => {
if (typeof value === 'string') {
return '0x' + remove0x(value).padStart(64, '0').toLowerCase()
} else if (typeof value === 'boolean') {
return toHexString32(value ? 1 : 0)
} else {
return toHexString32(BigNumber.from(value).toHexString())
}
}
originalDefinePropertyFn(object, propName, props)
}
const getStorageSlots = (storageLayout: any, obj: any): StorageSlot[] => {
const slots: StorageSlot[] = []
const flat = flattenObject(obj)
const contract = await originalDeploy(...args)
const proxy = await proxyFactory.deploy(contract.address)
;(contract as any).address = proxy.address
;(contract as any).resolvedAddress = proxy.address
;(contract as any).__setStorageSlot = proxy.__setStorageSlot.bind(proxy)
;(contract as any).__getStorageSlot = proxy.__getStorageSlot.bind(proxy)
;(contract as any).__setContractStorage = async (value: any) => {
await setContractStorage(
contract,
((await readArtifact(bre.config.paths.artifacts, name)) as any)
.storageLayout,
value
for (const key of Object.keys(flat)) {
const path = key.split('.')
const variableLabel = path[0]
const variableDef = storageLayout.storage.find((vDef: any) => {
return vDef.label === variableLabel
})
if (!variableDef) {
throw new Error(
`Could not find a matching variable definition for ${variableLabel}`
)
}
;(contract as any).__checkContractStorage = async (value: any) => {
await checkContractStorage(
contract,
((await readArtifact(bre.config.paths.artifacts, name)) as any)
.storageLayout,
value
const baseSlot = parseInt(variableDef.slot, 10)
const baseDepth = (variableDef.type.match(/t_mapping/g) || []).length
const slotLabel =
path.length > 1 + baseDepth ? path[path.length - 1] : 'default'
const inputSlot = getInputSlots(storageLayout, variableDef.type).find(
(iSlot) => {
return iSlot.label === slotLabel
}
)
if (!inputSlot) {
throw new Error(
`Could not find a matching slot definition for ${slotLabel}`
)
}
Object.defineProperty = originalDefinePropertyFn
return contract
let slotHash = toHexString32(baseSlot)
for (let i = 0; i < baseDepth; i++) {
slotHash = keccak256(toHexString32(path[i + 1]) + remove0x(slotHash))
}
slotHash = toHexString32(BigNumber.from(slotHash).add(inputSlot.slot))
slots.push({
label: key,
hash: slotHash,
value: toHexString32(flat[key]),
})
}
return contractFactory
return slots
}
export const setContractStorage = async (
const setContractStorage = async (
contract: Contract,
layout: any,
storage: any
obj: any
): Promise<void> => {
storage = storage || {}
for (const [key, value] of Object.entries(storage)) {
const layoutMap = layout.storage.find((lmap: any) => {
return lmap.label === key
})
const inputSlots = parseInputSlots(layout, layoutMap.type)
obj = obj || {}
const slot = parseInt(layoutMap.slot, 10)
let depth = (layoutMap.type.match(/t_mapping/g) || []).length
if (!obj) {
return
}
if (typeof value !== 'object') {
const slotHash = getStorageSlotHash(slot, depth, value)
await contract.__setStorageSlot(slotHash, toHexString32(value as string))
} else {
if (key === 'contractStorage' || key === 'verifiedContractStorage') {
for (const [subKey1, subValue1] of Object.entries(value)) {
for (const [subKey, subValue] of Object.entries(subValue1)) {
const baseSlotHash = getStorageSlotHash(slot, depth, {
[subKey1]: {
[subKey]: subValue,
},
})
const slotValues = getFlattenedValues(depth, {
[subKey1]: {
[subKey]: subValue,
},
})
for (const slotValue of slotValues) {
const slotIndex = inputSlots.find((inputSlot) => {
return inputSlot.label === slotValue.label
}).slot
const slotHash = toHexString32(
BigNumber.from(baseSlotHash).add(slotIndex)
)
await contract.__setStorageSlot(slotHash, slotValue.value)
}
}
}
} else {
for (const [subKey, subValue] of Object.entries(value)) {
const baseSlotHash = getStorageSlotHash(slot, depth, {
[subKey]: subValue,
})
const slotValues = getFlattenedValues(depth, {
[subKey]: subValue,
})
for (const slotValue of slotValues) {
const slotIndex = inputSlots.find((inputSlot) => {
return inputSlot.label === slotValue.label
}).slot
const slotHash = toHexString32(
BigNumber.from(baseSlotHash).add(slotIndex)
)
await contract.__setStorageSlot(slotHash, slotValue.value)
}
}
}
}
const slots = getStorageSlots(layout, obj)
for (const slot of slots) {
contract.__setStorageSlot(slot.hash, slot.value)
}
}
export const checkContractStorage = async (
const checkContractStorage = async (
contract: Contract,
layout: any,
storage: any
obj: any
): Promise<void> => {
storage = storage || {}
obj = obj || {}
for (const [key, value] of Object.entries(storage)) {
const layoutMap = layout.storage.find((lmap: any) => {
return lmap.label === key
})
const inputSlots = parseInputSlots(layout, layoutMap.type)
const slot = parseInt(layoutMap.slot, 10)
const depth = (layoutMap.type.match(/t_mapping/g) || []).length
if (!obj) {
return
}
if (typeof value !== 'object') {
const slotHash = getStorageSlotHash(slot, depth, value)
const retSlotValue = await contract.__getStorageSlot(slotHash)
const slots = getStorageSlots(layout, obj)
for (const slot of slots) {
const value = contract.__getStorageSlot(slot.hash)
if (retSlotValue !== toHexString32(value as string)) {
throw new Error(
`Resulting state of ${key} (${retSlotValue}) did not match expected state (${toHexString32(
value as string
)})`
)
}
} else {
if (key === 'contractStorage' || key === 'verifiedContractStorage') {
for (const [subKey1, subValue1] of Object.entries(value)) {
for (const [subKey, subValue] of Object.entries(subValue1)) {
const baseSlotHash = getStorageSlotHash(slot, depth, {
[subKey1]: {
[subKey]: subValue,
},
})
const slotValues = getFlattenedValues(depth, {
[subKey1]: {
[subKey]: subValue,
},
})
for (const slotValue of slotValues) {
const slotIndex = inputSlots.find((inputSlot) => {
return inputSlot.label === slotValue.label
}).slot
const slotHash = toHexString32(
BigNumber.from(baseSlotHash).add(slotIndex)
)
const retSlotValue = await contract.__getStorageSlot(slotHash)
if (retSlotValue !== slotValue.value) {
throw new Error(
`Resulting state of ${slotValue.label} (${retSlotValue}) did not match expected state (${slotValue.value}).`
)
}
}
}
}
} else {
for (const [subKey, subValue] of Object.entries(value)) {
const baseSlotHash = getStorageSlotHash(slot, depth, {
[subKey]: subValue,
})
const slotValues = getFlattenedValues(depth, {
[subKey]: subValue,
})
for (const slotValue of slotValues) {
const slotIndex = inputSlots.find((inputSlot) => {
return inputSlot.label === slotValue.label
}).slot
const slotHash = toHexString32(
BigNumber.from(baseSlotHash).add(slotIndex)
)
const retSlotValue = await contract.__getStorageSlot(slotHash)
if (retSlotValue !== slotValue.value) {
throw new Error(
`Resulting state of ${slotValue.label} (${retSlotValue}) did not match expected state (${slotValue.value}).`
)
}
}
}
}
if (value !== slot.value) {
throw new Error(
`Resulting state of ${slot.label} (${value}) did not match expected state (${slot.value}).`
)
}
}
}
export * from './contract-storage'
......@@ -28,7 +28,7 @@ import {
isTestStep_REVERT,
} from './test.types'
import { encodeRevertData } from '../codec'
import { getModifiableStorageFactory } from '../storage/contract-storage'
import { getModifiableStorageFactory } from '../storage'
import {
OVM_TX_GAS_LIMIT,
RUN_OVM_TEST_GAS,
......@@ -226,10 +226,10 @@ export class ExecutionManagerTestRunner {
await this.contracts.OVM_ExecutionManager.run(
{
timestamp: step.functionParams.timestamp,
queueOrigin: step.functionParams.queueOrigin,
number: 0,
l1QueueOrigin: step.functionParams.queueOrigin,
l1Txorigin: step.functionParams.origin,
entrypoint: step.functionParams.entrypoint,
origin: step.functionParams.origin,
msgSender: step.functionParams.msgSender,
gasLimit: step.functionParams.gasLimit,
data: calldata,
},
......
......@@ -6,6 +6,11 @@ export interface OVMAccount {
ethAddress: string
}
/**
* Converts a raw ethers result to an OVM account.
* @param result Raw ethers transaction result.
* @returns Converted OVM account.
*/
export const toOVMAccount = (result: any[]): OVMAccount => {
return {
nonce: result[0].toNumber(),
......
/**
* Converts a string or buffer to a '0x'-prefixed hex string.
* @param buf String or buffer to convert.
* @returns '0x'-prefixed string.
*/
export const toHexString = (buf: Buffer | string): string => {
return '0x' + fromHexString(buf).toString('hex')
}
/**
* Converts a '0x'-prefixed string to a buffer.
* @param str '0x'-prefixed string to convert.
* @returns Hex buffer.
*/
export const fromHexString = (str: string | Buffer): Buffer => {
if (typeof str === 'string' && str.startsWith('0x')) {
return Buffer.from(str.slice(2), 'hex')
......
/**
* Generates a hex string of repeated bytes.
* @param byte Byte to repeat.
* @param len Number of times to repeat the byte.
* @return '0x'-prefixed hex string filled with the provided byte.
*/
export const makeHexString = (byte: string, len: number): string => {
return '0x' + byte.repeat(len)
}
/**
* Genereates an address with a repeated byte.
* @param byte Byte to repeat in the address.
* @return Address filled with the repeated byte.
*/
export const makeAddress = (byte: string): string => {
return makeHexString(byte, 20)
}
/**
* Removes '0x' from a hex string.
* @param str Hex string to remove '0x' from.
* @returns String without the '0x' prefix.
*/
export const remove0x = (str: string): string => {
if (str.startsWith('0x')) {
return str.slice(2)
......
export * from './buffer-utils'
export * from './byte-utils'
......@@ -655,6 +655,11 @@
resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0"
integrity sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==
"@types/lodash@^4.14.161":
version "4.14.161"
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.161.tgz#a21ca0777dabc6e4f44f3d07f37b765f54188b18"
integrity sha512-EP6O3Jkr7bXvZZSZYlsgt5DIjiGr0dXP1/jVEwVLTFgg0d+3lWVQkRavYVQszV7dYUwvg0B8R0MBDpcmXg7XIA==
"@types/lru-cache@^5.1.0":
version "5.1.0"
resolved "https://registry.yarnpkg.com/@types/lru-cache/-/lru-cache-5.1.0.tgz#57f228f2b80c046b4a1bd5cac031f81f207f4f03"
......
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