@polygon-hermez/vm
Version:
An Ethereum VM implementation
1,416 lines (1,369 loc) • 40.4 kB
text/typescript
import Common from '@ethereumjs/common'
import { Address, BN, keccak256, setLengthRight, TWO_POW256, MAX_INTEGER } from 'ethereumjs-util'
import {
addressToBuffer,
describeLocation,
getDataSlice,
jumpIsValid,
jumpSubIsValid,
trap,
writeCallOutput,
} from './util'
import { ERROR } from '../../exceptions'
import { RunState } from './../interpreter'
const { smtUtils } = require('@polygon-hermez/zkevm-commonjs')
export interface SyncOpHandler {
(runState: RunState, common: Common): void
}
export interface AsyncOpHandler {
(runState: RunState, common: Common): Promise<void>
}
export type OpHandler = SyncOpHandler | AsyncOpHandler
// the opcode functions
export const handlers: Map<number, OpHandler> = new Map([
// 0x00: STOP
[
0x00,
function (runState) {
runState.vcm.computeFunctionCounters('opStop', {
isCreate: runState.eei._env.isCreate,
isDeploy: runState.eei._env.isDeploy,
})
trap(ERROR.STOP)
},
],
// 0x01: ADD
[
0x01,
function (runState) {
runState.vcm.computeFunctionCounters('opAdd', {
isCreate: runState.eei._env.isCreate,
isDeploy: runState.eei._env.isDeploy,
})
const [a, b] = runState.stack.popN(2)
const r = a.add(b).mod(TWO_POW256)
runState.stack.push(r)
},
],
// 0x02: MUL
[
0x02,
function (runState) {
runState.vcm.computeFunctionCounters('opMul', {
isCreate: runState.eei._env.isCreate,
isDeploy: runState.eei._env.isDeploy,
})
const [a, b] = runState.stack.popN(2)
const r = a.mul(b).mod(TWO_POW256)
runState.stack.push(r)
},
],
// 0x03: SUB
[
0x03,
function (runState) {
runState.vcm.computeFunctionCounters('opSub', {
isCreate: runState.eei._env.isCreate,
isDeploy: runState.eei._env.isDeploy,
})
const [a, b] = runState.stack.popN(2)
const r = a.sub(b).toTwos(256)
runState.stack.push(r)
},
],
// 0x04: DIV
[
0x04,
function (runState) {
runState.vcm.computeFunctionCounters('opDiv', {
isCreate: runState.eei._env.isCreate,
isDeploy: runState.eei._env.isDeploy,
})
const [a, b] = runState.stack.popN(2)
let r
if (b.isZero()) {
r = new BN(b)
} else {
r = a.div(b)
}
runState.stack.push(r)
},
],
// 0x05: SDIV
[
0x05,
function (runState) {
runState.vcm.computeFunctionCounters('opSDiv', {
isCreate: runState.eei._env.isCreate,
isDeploy: runState.eei._env.isDeploy,
})
let [a, b] = runState.stack.popN(2)
let r
if (b.isZero()) {
r = new BN(b)
} else {
a = a.fromTwos(256)
b = b.fromTwos(256)
r = a.div(b).toTwos(256)
}
runState.stack.push(r)
},
],
// 0x06: MOD
[
0x06,
function (runState) {
runState.vcm.computeFunctionCounters('opMod', {
isCreate: runState.eei._env.isCreate,
isDeploy: runState.eei._env.isDeploy,
})
const [a, b] = runState.stack.popN(2)
let r
if (b.isZero()) {
r = new BN(b)
} else {
r = a.mod(b)
}
runState.stack.push(r)
},
],
// 0x07: SMOD
[
0x07,
function (runState) {
runState.vcm.computeFunctionCounters('opSMod', {
isCreate: runState.eei._env.isCreate,
isDeploy: runState.eei._env.isDeploy,
})
let [a, b] = runState.stack.popN(2)
let r
if (b.isZero()) {
r = new BN(b)
} else {
a = a.fromTwos(256)
b = b.fromTwos(256)
r = a.abs().mod(b.abs())
if (a.isNeg()) {
r = r.ineg()
}
r = r.toTwos(256)
}
runState.stack.push(r)
},
],
// 0x08: ADDMOD
[
0x08,
function (runState) {
runState.vcm.computeFunctionCounters('opAddMod', {
isCreate: runState.eei._env.isCreate,
isDeploy: runState.eei._env.isDeploy,
})
const [a, b, c] = runState.stack.popN(3)
let r
if (c.isZero()) {
r = new BN(c)
} else {
r = a.add(b).mod(c)
}
runState.stack.push(r)
},
],
// 0x09: MULMOD
[
0x09,
function (runState) {
runState.vcm.computeFunctionCounters('opMulMod', {
isCreate: runState.eei._env.isCreate,
isDeploy: runState.eei._env.isDeploy,
})
const [a, b, c] = runState.stack.popN(3)
let r
if (c.isZero()) {
r = new BN(c)
} else {
r = a.mul(b).mod(c)
}
runState.stack.push(r)
},
],
// 0x0a: EXP
[
0x0a,
function (runState, common) {
const [base, exponent] = runState.stack.popN(2)
runState.vcm.computeFunctionCounters('opExp', {
bytesExponentLength: exponent.toArrayLike(Buffer).length,
isCreate: runState.eei._env.isCreate,
isDeploy: runState.eei._env.isDeploy,
})
if (exponent.isZero()) {
runState.stack.push(new BN(1))
return
}
const byteLength = exponent.byteLength()
if (byteLength < 1 || byteLength > 32) {
trap(ERROR.OUT_OF_RANGE)
}
const gasPrice = common.param('gasPrices', 'expByte')
const amount = new BN(byteLength).muln(gasPrice)
runState.eei.useGas(amount, 'EXP opcode')
if (base.isZero()) {
runState.stack.push(new BN(0))
return
}
const m = BN.red(TWO_POW256)
const redBase = base.toRed(m)
const r = redBase.redPow(exponent)
runState.stack.push(r.fromRed())
},
],
// 0x0b: SIGNEXTEND
[
0x0b,
function (runState) {
runState.vcm.computeFunctionCounters('opSignExtend', {
isCreate: runState.eei._env.isCreate,
isDeploy: runState.eei._env.isDeploy,
})
/* eslint-disable-next-line prefer-const */
let [k, val] = runState.stack.popN(2)
if (k.ltn(31)) {
const signBit = k.muln(8).iaddn(7).toNumber()
const mask = new BN(1).ishln(signBit).isubn(1)
if (val.testn(signBit)) {
val = val.or(mask.notn(256))
} else {
val = val.and(mask)
}
} else {
// return the same value
val = new BN(val)
}
runState.stack.push(val)
},
],
// 0x10 range - bit ops
// 0x10: LT
[
0x10,
function (runState) {
runState.vcm.computeFunctionCounters('opLT', {
isCreate: runState.eei._env.isCreate,
isDeploy: runState.eei._env.isDeploy,
})
const [a, b] = runState.stack.popN(2)
const r = new BN(a.lt(b) ? 1 : 0)
runState.stack.push(r)
},
],
// 0x11: GT
[
0x11,
function (runState) {
runState.vcm.computeFunctionCounters('opGT', {
isCreate: runState.eei._env.isCreate,
isDeploy: runState.eei._env.isDeploy,
})
const [a, b] = runState.stack.popN(2)
const r = new BN(a.gt(b) ? 1 : 0)
runState.stack.push(r)
},
],
// 0x12: SLT
[
0x12,
function (runState) {
runState.vcm.computeFunctionCounters('opSLT', {
isCreate: runState.eei._env.isCreate,
isDeploy: runState.eei._env.isDeploy,
})
const [a, b] = runState.stack.popN(2)
const r = new BN(a.fromTwos(256).lt(b.fromTwos(256)) ? 1 : 0)
runState.stack.push(r)
},
],
// 0x13: SGT
[
0x13,
function (runState) {
runState.vcm.computeFunctionCounters('opSGT', {
isCreate: runState.eei._env.isCreate,
isDeploy: runState.eei._env.isDeploy,
})
const [a, b] = runState.stack.popN(2)
const r = new BN(a.fromTwos(256).gt(b.fromTwos(256)) ? 1 : 0)
runState.stack.push(r)
},
],
// 0x14: EQ
[
0x14,
function (runState) {
runState.vcm.computeFunctionCounters('opEq', {
isCreate: runState.eei._env.isCreate,
isDeploy: runState.eei._env.isDeploy,
})
const [a, b] = runState.stack.popN(2)
const r = new BN(a.eq(b) ? 1 : 0)
runState.stack.push(r)
},
],
// 0x15: ISZERO
[
0x15,
function (runState) {
runState.vcm.computeFunctionCounters('opIsZero', {
isCreate: runState.eei._env.isCreate,
isDeploy: runState.eei._env.isDeploy,
})
const a = runState.stack.pop()
const r = new BN(a.isZero() ? 1 : 0)
runState.stack.push(r)
},
],
// 0x16: AND
[
0x16,
function (runState) {
runState.vcm.computeFunctionCounters('opAnd', {
isCreate: runState.eei._env.isCreate,
isDeploy: runState.eei._env.isDeploy,
})
const [a, b] = runState.stack.popN(2)
const r = a.and(b)
runState.stack.push(r)
},
],
// 0x17: OR
[
0x17,
function (runState) {
runState.vcm.computeFunctionCounters('opOr', {
isCreate: runState.eei._env.isCreate,
isDeploy: runState.eei._env.isDeploy,
})
const [a, b] = runState.stack.popN(2)
const r = a.or(b)
runState.stack.push(r)
},
],
// 0x18: XOR
[
0x18,
function (runState) {
runState.vcm.computeFunctionCounters('opXor', {
isCreate: runState.eei._env.isCreate,
isDeploy: runState.eei._env.isDeploy,
})
const [a, b] = runState.stack.popN(2)
const r = a.xor(b)
runState.stack.push(r)
},
],
// 0x19: NOT
[
0x19,
function (runState) {
runState.vcm.computeFunctionCounters('opNot', {
isCreate: runState.eei._env.isCreate,
isDeploy: runState.eei._env.isDeploy,
})
const a = runState.stack.pop()
const r = a.notn(256)
runState.stack.push(r)
},
],
// 0x1a: BYTE
[
0x1a,
function (runState) {
runState.vcm.computeFunctionCounters('opByte', {
isCreate: runState.eei._env.isCreate,
isDeploy: runState.eei._env.isDeploy,
})
const [pos, word] = runState.stack.popN(2)
if (pos.gten(32)) {
runState.stack.push(new BN(0))
return
}
const r = new BN(word.shrn((31 - pos.toNumber()) * 8).andln(0xff))
runState.stack.push(r)
},
],
// 0x1b: SHL
[
0x1b,
function (runState) {
runState.vcm.computeFunctionCounters('opSHL', {
isCreate: runState.eei._env.isCreate,
isDeploy: runState.eei._env.isDeploy,
})
const [a, b] = runState.stack.popN(2)
if (a.gten(256)) {
runState.stack.push(new BN(0))
return
}
const r = b.shln(a.toNumber()).iand(MAX_INTEGER)
runState.stack.push(r)
},
],
// 0x1c: SHR
[
0x1c,
function (runState) {
runState.vcm.computeFunctionCounters('opSHR', {
isCreate: runState.eei._env.isCreate,
isDeploy: runState.eei._env.isDeploy,
})
const [a, b] = runState.stack.popN(2)
if (a.gten(256)) {
runState.stack.push(new BN(0))
return
}
const r = b.shrn(a.toNumber())
runState.stack.push(r)
},
],
// 0x1d: SAR
[
0x1d,
function (runState) {
runState.vcm.computeFunctionCounters('opSAR', {
isCreate: runState.eei._env.isCreate,
isDeploy: runState.eei._env.isDeploy,
})
const [a, b] = runState.stack.popN(2)
let r
const isSigned = b.testn(255)
if (a.gten(256)) {
if (isSigned) {
r = new BN(MAX_INTEGER)
} else {
r = new BN(0)
}
runState.stack.push(r)
return
}
const c = b.shrn(a.toNumber())
if (isSigned) {
const shiftedOutWidth = 255 - a.toNumber()
const mask = MAX_INTEGER.shrn(shiftedOutWidth).shln(shiftedOutWidth)
r = c.ior(mask)
} else {
r = c
}
runState.stack.push(r)
},
],
// 0x20 range - crypto
// 0x20: SHA3
[
0x20,
function (runState) {
const [offset, length] = runState.stack.popN(2)
runState.vcm.computeFunctionCounters('opSha3', {
inputSize: length.toNumber(),
isCreate: runState.eei._env.isCreate,
isDeploy: runState.eei._env.isDeploy,
})
let data = Buffer.alloc(0)
if (!length.isZero()) {
data = runState.memory.read(offset.toNumber(), length.toNumber())
}
const r = new BN(keccak256(data))
runState.stack.push(r)
},
],
// 0x30 range - closure state
// 0x30: ADDRESS
[
0x30,
function (runState) {
runState.vcm.computeFunctionCounters('opAddress', {
isCreate: runState.eei._env.isCreate,
isDeploy: runState.eei._env.isDeploy,
})
const address = new BN(runState.eei.getAddress().buf)
runState.stack.push(address)
},
],
// 0x31: BALANCE
[
0x31,
async function (runState) {
runState.vcm.computeFunctionCounters('opBalance', {
isCreate: runState.eei._env.isCreate,
isDeploy: runState.eei._env.isDeploy,
})
const addressBN = runState.stack.pop()
const address = new Address(addressToBuffer(addressBN))
const balance = await runState.eei.getExternalBalance(address)
runState.stack.push(balance)
},
],
// 0x32: ORIGIN
[
0x32,
function (runState) {
runState.vcm.computeFunctionCounters('opOrigin', {
isCreate: runState.eei._env.isCreate,
isDeploy: runState.eei._env.isDeploy,
})
runState.stack.push(runState.eei.getTxOrigin())
},
],
// 0x33: CALLER
[
0x33,
function (runState) {
runState.vcm.computeFunctionCounters('opCaller', {
isCreate: runState.eei._env.isCreate,
isDeploy: runState.eei._env.isDeploy,
})
runState.stack.push(runState.eei.getCaller())
},
],
// 0x34: CALLVALUE
[
0x34,
function (runState) {
runState.vcm.computeFunctionCounters('opCallValue', {
isCreate: runState.eei._env.isCreate,
isDeploy: runState.eei._env.isDeploy,
})
runState.stack.push(runState.eei.getCallValue())
},
],
// 0x35: CALLDATALOAD
[
0x35,
function (runState) {
runState.vcm.computeFunctionCounters('opCalldataLoad', {
isCreate: runState.eei._env.isCreate,
isDeploy: runState.eei._env.isDeploy,
})
const pos = runState.stack.pop()
if (pos.gt(runState.eei.getCallDataSize())) {
runState.stack.push(new BN(0))
return
}
const i = pos.toNumber()
let loaded = runState.eei.getCallData().slice(i, i + 32)
loaded = loaded.length ? loaded : Buffer.from([0])
const r = new BN(setLengthRight(loaded, 32))
runState.stack.push(r)
},
],
// 0x36: CALLDATASIZE
[
0x36,
function (runState) {
runState.vcm.computeFunctionCounters('opCalldataSize', {
isCreate: runState.eei._env.isCreate,
isDeploy: runState.eei._env.isDeploy,
})
const r = runState.eei.getCallDataSize()
runState.stack.push(r)
},
],
// 0x37: CALLDATACOPY
[
0x37,
function (runState) {
const [memOffset, dataOffset, dataLength] = runState.stack.popN(3)
if (!dataLength.eqn(0)) {
const data = getDataSlice(runState.eei.getCallData(), dataOffset, dataLength)
runState.vcm.computeFunctionCounters('opCalldataCopy', {
inputSize: data.length,
isCreate: runState.eei._env.isCreate,
isDeploy: runState.eei._env.isDeploy,
})
const memOffsetNum = memOffset.toNumber()
const dataLengthNum = dataLength.toNumber()
runState.memory.extend(memOffsetNum, dataLengthNum)
runState.memory.write(memOffsetNum, dataLengthNum, data)
}
},
],
// 0x38: CODESIZE
[
0x38,
function (runState) {
runState.vcm.computeFunctionCounters('opCodeSize', {
isCreate: runState.eei._env.isCreate,
isDeploy: runState.eei._env.isDeploy,
})
runState.stack.push(runState.eei.getCodeSize())
},
],
// 0x39: CODECOPY
[
0x39,
function (runState) {
const [memOffset, codeOffset, dataLength] = runState.stack.popN(3)
if (!dataLength.eqn(0)) {
const data = getDataSlice(runState.eei.getCode(), codeOffset, dataLength)
runState.vcm.computeFunctionCounters('opCodeCopy', {
inputSize: data.length,
isCreate: runState.eei._env.isCreate,
isDeploy: runState.eei._env.isDeploy,
})
const memOffsetNum = memOffset.toNumber()
const lengthNum = dataLength.toNumber()
runState.memory.extend(memOffsetNum, lengthNum)
runState.memory.write(memOffsetNum, lengthNum, data)
}
},
],
// 0x3b: EXTCODESIZE
[
0x3b,
async function (runState) {
runState.vcm.computeFunctionCounters('opExtCodeSize', {
isCreate: runState.eei._env.isCreate,
isDeploy: runState.eei._env.isDeploy,
})
const addressBN = runState.stack.pop()
const size = await runState.eei.getExternalCodeSize(addressBN)
runState.stack.push(size)
},
],
// 0x3c: EXTCODECOPY
[
0x3c,
async function (runState) {
const [addressBN, memOffset, codeOffset, dataLength] = runState.stack.popN(4)
if (!dataLength.eqn(0)) {
const code = await runState.eei.getExternalCode(addressBN)
const data = getDataSlice(code, codeOffset, dataLength)
runState.vcm.computeFunctionCounters('opExtCodeCopy', {
inputSize: data.length,
bytecodeLen: code.length,
isCreate: runState.eei._env.isCreate,
isDeploy: runState.eei._env.isDeploy,
})
const memOffsetNum = memOffset.toNumber()
const lengthNum = dataLength.toNumber()
runState.memory.extend(memOffsetNum, lengthNum)
runState.memory.write(memOffsetNum, lengthNum, data)
}
},
],
// 0x3f: EXTCODEHASH
[
0x3f,
async function (runState) {
runState.vcm.computeFunctionCounters('opExtCodeHash', {
isCreate: runState.eei._env.isCreate,
isDeploy: runState.eei._env.isDeploy,
})
const addressBN = runState.stack.pop()
const code = await runState.eei.getExternalCode(addressBN)
if (code.length === 0) {
runState.stack.push(new BN(0))
return
}
// Use linear poseidon hash
const lpCode = await smtUtils.hashContractBytecode(code.toString('hex'))
runState.stack.push(new BN(lpCode.slice(2), 16))
},
],
// 0x3d: RETURNDATASIZE
[
0x3d,
function (runState) {
runState.vcm.computeFunctionCounters('opReturnDataSize', {
isCreate: runState.eei._env.isCreate,
isDeploy: runState.eei._env.isDeploy,
})
runState.stack.push(runState.eei.getReturnDataSize())
},
],
// 0x3e: RETURNDATACOPY
[
0x3e,
function (runState) {
const [memOffset, returnDataOffset, dataLength] = runState.stack.popN(3)
if (!dataLength.eqn(0)) {
const data = getDataSlice(runState.eei.getReturnData(), returnDataOffset, dataLength)
runState.vcm.computeFunctionCounters('opReturnDataCopy', {
inputSize: data.length,
isCreate: runState.eei._env.isCreate,
isDeploy: runState.eei._env.isDeploy,
})
const memOffsetNum = memOffset.toNumber()
const lengthNum = dataLength.toNumber()
runState.memory.extend(memOffsetNum, lengthNum)
runState.memory.write(memOffsetNum, lengthNum, data)
}
},
],
// 0x3a: GASPRICE
[
0x3a,
function (runState) {
runState.vcm.computeFunctionCounters('opGasPrice', {
isCreate: runState.eei._env.isCreate,
isDeploy: runState.eei._env.isDeploy,
})
runState.stack.push(runState.eei.getTxGasPrice())
},
],
// '0x40' range - block operations
// 0x40: BLOCKHASH
[
0x40,
async function (runState) {
runState.vcm.computeFunctionCounters('opBlockHash', {
isCreate: runState.eei._env.isCreate,
isDeploy: runState.eei._env.isDeploy,
})
const number = runState.stack.pop()
const hash = await runState.eei.getBatchHash(number)
runState.stack.push(hash)
},
],
// 0x41: COINBASE
[
0x41,
function (runState) {
runState.vcm.computeFunctionCounters('opCoinbase', {
isCreate: runState.eei._env.isCreate,
isDeploy: runState.eei._env.isDeploy,
})
runState.stack.push(runState.eei.getBlockCoinbase())
},
],
// 0x42: TIMESTAMP
[
0x42,
function (runState) {
runState.vcm.computeFunctionCounters('opTimestamp', {
isCreate: runState.eei._env.isCreate,
isDeploy: runState.eei._env.isDeploy,
})
runState.stack.push(runState.eei.getBlockTimestamp())
},
],
// 0x43: NUMBER
[
0x43,
async function (runState) {
runState.vcm.computeFunctionCounters('opNumber', {
isCreate: runState.eei._env.isCreate,
isDeploy: runState.eei._env.isDeploy,
})
const num = await runState.eei.getBlockNum()
runState.stack.push(num)
},
],
// 0x44: DIFFICULTY
[
0x44,
function (runState) {
runState.vcm.computeFunctionCounters('opDifficulty', {
isCreate: runState.eei._env.isCreate,
isDeploy: runState.eei._env.isDeploy,
})
runState.stack.push(runState.eei.getBlockDifficulty())
},
],
// 0x45: GASLIMIT
[
0x45,
function (runState) {
runState.vcm.computeFunctionCounters('opGasLimit', {
isCreate: runState.eei._env.isCreate,
isDeploy: runState.eei._env.isDeploy,
})
runState.stack.push(runState.eei.getBlockGasLimit())
},
],
// 0x46: CHAINID
[
0x46,
function (runState) {
runState.vcm.computeFunctionCounters('opChainId', {
isCreate: runState.eei._env.isCreate,
isDeploy: runState.eei._env.isDeploy,
})
runState.stack.push(runState.eei.getChainId())
},
],
// 0x47: SELFBALANCE
[
0x47,
function (runState) {
runState.vcm.computeFunctionCounters('opSelfBalance', {
isCreate: runState.eei._env.isCreate,
isDeploy: runState.eei._env.isDeploy,
})
runState.stack.push(runState.eei.getSelfBalance())
},
],
// 0x48: BASEFEE
[
0x48,
function (runState) {
runState.vcm.computeFunctionCounters('opBaseFee', {
isCreate: runState.eei._env.isCreate,
isDeploy: runState.eei._env.isDeploy,
})
runState.stack.push(runState.eei.getBlockBaseFee())
//runState.stack.push(new BN(0))
},
],
// 0x50 range - 'storage' and execution
// 0x50: POP
[
0x50,
function (runState) {
runState.vcm.computeFunctionCounters('opPop', {
isCreate: runState.eei._env.isCreate,
isDeploy: runState.eei._env.isDeploy,
})
runState.stack.pop()
},
],
// 0x51: MLOAD
[
0x51,
function (runState) {
runState.vcm.computeFunctionCounters('opMLoad', {
isCreate: runState.eei._env.isCreate,
isDeploy: runState.eei._env.isDeploy,
})
const pos = runState.stack.pop()
const word = runState.memory.read(pos.toNumber(), 32)
runState.stack.push(new BN(word))
},
],
// 0x52: MSTORE
[
0x52,
function (runState) {
runState.vcm.computeFunctionCounters('opMStore', {
isCreate: runState.eei._env.isCreate,
isDeploy: runState.eei._env.isDeploy,
})
const [offset, word] = runState.stack.popN(2)
const buf = word.toArrayLike(Buffer, 'be', 32)
const offsetNum = offset.toNumber()
runState.memory.extend(offsetNum, 32)
runState.memory.write(offsetNum, 32, buf)
},
],
// 0x53: MSTORE8
[
0x53,
function (runState) {
runState.vcm.computeFunctionCounters('opMStore8', {
isCreate: runState.eei._env.isCreate,
isDeploy: runState.eei._env.isDeploy,
})
const [offset, byte] = runState.stack.popN(2)
// NOTE: we're using a 'trick' here to get the least significant byte
// NOTE: force cast necessary because `BN.andln` returns number but
// the types are wrong
const buf = Buffer.from([byte.andln(0xff) as unknown as number])
const offsetNum = offset.toNumber()
runState.memory.extend(offsetNum, 1)
runState.memory.write(offsetNum, 1, buf)
},
],
// 0x54: SLOAD
[
0x54,
async function (runState) {
runState.vcm.computeFunctionCounters('opSLoad', {
isCreate: runState.eei._env.isCreate,
isDeploy: runState.eei._env.isDeploy,
})
const key = runState.stack.pop()
const keyBuf = key.toArrayLike(Buffer, 'be', 32)
const value = await runState.eei.storageLoad(keyBuf)
const valueBN = value.length ? new BN(value) : new BN(0)
runState.stack.push(valueBN)
},
],
// 0x55: SSTORE
[
0x55,
async function (runState) {
runState.vcm.computeFunctionCounters('opSStore', {
isCreate: runState.eei._env.isCreate,
isDeploy: runState.eei._env.isDeploy,
})
const [key, val] = runState.stack.popN(2)
const keyBuf = key.toArrayLike(Buffer, 'be', 32)
// NOTE: this should be the shortest representation
let value
if (val.isZero()) {
value = Buffer.from([])
} else {
value = val.toArrayLike(Buffer, 'be')
}
await runState.eei.storageStore(keyBuf, value)
},
],
// 0x56: JUMP
[
0x56,
function (runState) {
runState.vcm.computeFunctionCounters('opJump', {
isCreate: runState.eei._env.isCreate,
isDeploy: runState.eei._env.isDeploy,
})
const dest = runState.stack.pop()
if (dest.gt(runState.eei.getCodeSize())) {
trap(ERROR.INVALID_JUMP + ' at ' + describeLocation(runState))
}
const destNum = dest.toNumber()
if (!jumpIsValid(runState, destNum)) {
trap(ERROR.INVALID_JUMP + ' at ' + describeLocation(runState))
}
runState.programCounter = destNum
},
],
// 0x57: JUMPI
[
0x57,
function (runState) {
runState.vcm.computeFunctionCounters('opJumpI', {
isCreate: runState.eei._env.isCreate,
isDeploy: runState.eei._env.isDeploy,
})
const [dest, cond] = runState.stack.popN(2)
if (!cond.isZero()) {
if (dest.gt(runState.eei.getCodeSize())) {
trap(ERROR.INVALID_JUMP + ' at ' + describeLocation(runState))
}
const destNum = dest.toNumber()
if (!jumpIsValid(runState, destNum)) {
trap(ERROR.INVALID_JUMP + ' at ' + describeLocation(runState))
}
runState.programCounter = destNum
}
},
],
// 0x58: PC
[
0x58,
function (runState) {
runState.vcm.computeFunctionCounters('opPC', {
isCreate: runState.eei._env.isCreate,
isDeploy: runState.eei._env.isDeploy,
})
runState.stack.push(new BN(runState.programCounter - 1))
},
],
// 0x59: MSIZE
[
0x59,
function (runState) {
runState.vcm.computeFunctionCounters('opMSize', {
isCreate: runState.eei._env.isCreate,
isDeploy: runState.eei._env.isDeploy,
})
runState.stack.push(runState.memoryWordCount.muln(32))
},
],
// 0x5a: GAS
[
0x5a,
function (runState) {
runState.vcm.computeFunctionCounters('opGas', {
isCreate: runState.eei._env.isCreate,
isDeploy: runState.eei._env.isDeploy,
})
runState.stack.push(new BN(runState.eei.getGasLeft()))
},
],
// 0x5b: JUMPDEST
[
0x5b,
function (runState) {
runState.vcm.computeFunctionCounters('opJumpDest', {
isCreate: runState.eei._env.isCreate,
isDeploy: runState.eei._env.isDeploy,
})
},
],
// 0x5c: BEGINSUB
[
0x5c,
function (runState) {
trap(ERROR.INVALID_BEGINSUB + ' at ' + describeLocation(runState))
},
],
// 0x5d: RETURNSUB
[
0x5d,
function (runState) {
if (runState.returnStack.length < 1) {
trap(ERROR.INVALID_RETURNSUB)
}
const dest = runState.returnStack.pop()
runState.programCounter = dest.toNumber()
},
],
// 0x5e: JUMPSUB
[
0x5e,
function (runState) {
const dest = runState.stack.pop()
if (dest.gt(runState.eei.getCodeSize())) {
trap(ERROR.INVALID_JUMPSUB + ' at ' + describeLocation(runState))
}
const destNum = dest.toNumber()
if (!jumpSubIsValid(runState, destNum)) {
trap(ERROR.INVALID_JUMPSUB + ' at ' + describeLocation(runState))
}
runState.returnStack.push(new BN(runState.programCounter))
runState.programCounter = destNum + 1
},
],
// 0x5f: PUSH0
[
0x5f,
function (runState) {
runState.vcm.computeFunctionCounters('opPush0', {
isCreate: runState.eei._env.isCreate,
isDeploy: runState.eei._env.isDeploy,
})
runState.stack.push(new BN(0))
},
],
// 0x60: PUSH
[
0x60,
function (runState) {
const numToPush = runState.opCode - 0x5f
runState.vcm.computeFunctionCounters('_opPush', {
pushBytes: numToPush,
isCreate: runState.eei._env.isCreate,
isDeploy: runState.eei._env.isDeploy,
})
const loaded = new BN(
runState.eei.getCode().slice(runState.programCounter, runState.programCounter + numToPush)
)
runState.programCounter += numToPush
runState.stack.push(loaded)
},
],
// 0x80: DUP
[
0x80,
function (runState) {
runState.vcm.computeFunctionCounters('_opDup', {
isCreate: runState.eei._env.isCreate,
isDeploy: runState.eei._env.isDeploy,
})
const stackPos = runState.opCode - 0x7f
runState.stack.dup(stackPos)
},
],
// 0x90: SWAP
[
0x90,
function (runState) {
runState.vcm.computeFunctionCounters('_opSwap', {
isCreate: runState.eei._env.isCreate,
isDeploy: runState.eei._env.isDeploy,
})
const stackPos = runState.opCode - 0x8f
runState.stack.swap(stackPos)
},
],
// 0xa0: LOG
[
0xa0,
function (runState) {
const [memOffset, memLength] = runState.stack.popN(2)
runState.vcm.computeFunctionCounters('_opLog', {
inputSize: memLength.toNumber(),
isCreate: runState.eei._env.isCreate,
isDeploy: runState.eei._env.isDeploy,
})
const topicsCount = runState.opCode - 0xa0
const topics = runState.stack.popN(topicsCount)
const topicsBuf = topics.map(function (a: BN) {
return a.toArrayLike(Buffer, 'be', 32)
})
let mem = Buffer.alloc(0)
if (!memLength.isZero()) {
mem = runState.memory.read(memOffset.toNumber(), memLength.toNumber())
}
runState.eei.log(mem, topicsCount, topicsBuf)
},
],
// '0xf0' range - closures
// 0xf0: CREATE
[
0xf0,
async function (runState) {
const [value, offset, length] = runState.stack.popN(3)
runState.vcm.computeFunctionCounters('opCreate', {
bytesNonceLength: runState.eei._env.contract.nonce.addn(1).toArrayLike(Buffer).length,
isCreate: runState.eei._env.isCreate,
isDeploy: runState.eei._env.isDeploy,
inLength: length.toNumber(),
})
const gasLimit = runState.messageGasLimit!
runState.messageGasLimit = undefined
let data = Buffer.alloc(0)
if (!length.isZero()) {
data = runState.memory.read(offset.toNumber(), length.toNumber())
}
const ret = await runState.eei.create(gasLimit, value, data)
runState.stack.push(ret.returnCode)
if (typeof ret.results !== 'undefined') {
// If there is execution result (the create has been executed), we need to compute _processContractCall counters
runState.vcm.computeFunctionCounters('_processContractCall', {
bytecodeLength: length.toNumber(),
isDeploy: false,
isCreate: true,
isCreate2: false,
})
}
return ret.results
},
],
// 0xf5: CREATE2
[
0xf5,
async function (runState) {
if (runState.eei.isStatic()) {
trap(ERROR.STATIC_STATE_CHANGE)
}
const [value, offset, length, salt] = runState.stack.popN(4)
runState.vcm.computeFunctionCounters('opCreate2', {
bytesNonceLength: runState.eei._env.contract.nonce.addn(1).toArrayLike(Buffer).length,
isCreate: runState.eei._env.isCreate,
isDeploy: runState.eei._env.isDeploy,
inLength: length.toNumber(),
})
runState.vcm.computeFunctionCounters('_processContractCall', {
bytecodeLength: length.toNumber(),
isDeploy: false,
isCreate: false,
isCreate2: true,
})
const gasLimit = runState.messageGasLimit!
runState.messageGasLimit = undefined
let data = Buffer.alloc(0)
if (!length.isZero()) {
data = runState.memory.read(offset.toNumber(), length.toNumber())
}
const ret = await runState.eei.create2(
gasLimit,
value,
data,
salt.toArrayLike(Buffer, 'be', 32)
)
runState.stack.push(ret.returnCode)
return ret.results
},
],
// 0xf1: CALL
[
0xf1,
async function (runState: RunState) {
const [_currentGasLimit, toAddr, value, inOffset, inLength, outOffset, outLength] =
runState.stack.popN(7)
const toAddress = new Address(addressToBuffer(toAddr))
const bytecodeLength = await runState.eei.getExternalCodeSize(toAddr)
runState.vcm.computeFunctionCounters('opCall', {
isCreate: runState.eei._env.isCreate,
isDeploy: runState.eei._env.isDeploy,
inLength: inLength.toNumber(),
outLength: outLength.toNumber(),
})
runState.vcm.computeFunctionCounters('_processContractCall', {
bytecodeLength: bytecodeLength.toNumber(),
isDeploy: false,
isCreate: false,
isCreate2: false,
})
let data = Buffer.alloc(0)
if (!inLength.isZero()) {
data = runState.memory.read(inOffset.toNumber(), inLength.toNumber())
}
const gasLimit = runState.messageGasLimit!
runState.messageGasLimit = undefined
const ret = await runState.eei.call(gasLimit, toAddress, value, data, outLength)
// Write return data to memory
writeCallOutput(runState, outOffset, outLength)
runState.stack.push(ret.returnCode)
return ret.results
},
],
// 0xf2: CALLCODE
[
0xf2,
async function (runState: RunState) {
const [_currentGasLimit, toAddr, value, inOffset, inLength, outOffset, outLength] =
runState.stack.popN(7)
const toAddress = new Address(addressToBuffer(toAddr))
runState.vcm.computeFunctionCounters('opCallCode', {
isCreate: runState.eei._env.isCreate,
isDeploy: runState.eei._env.isDeploy,
inLength: inLength.toNumber(),
outLength: outLength.toNumber(),
})
const bytecodeLength = await runState.eei.getExternalCodeSize(toAddr)
runState.vcm.computeFunctionCounters('_processContractCall', {
bytecodeLength: bytecodeLength.toNumber(),
isDeploy: false,
isCreate: false,
isCreate2: false,
})
const gasLimit = runState.messageGasLimit!
runState.messageGasLimit = undefined
let data = Buffer.alloc(0)
if (!inLength.isZero()) {
data = runState.memory.read(inOffset.toNumber(), inLength.toNumber())
}
const ret = await runState.eei.callCode(gasLimit, toAddress, value, data, outLength)
// Write return data to memory
writeCallOutput(runState, outOffset, outLength)
runState.stack.push(ret.returnCode)
return ret.results
},
],
// 0xf4: DELEGATECALL
[
0xf4,
async function (runState) {
const value = runState.eei.getCallValue()
const [_currentGasLimit, toAddr, inOffset, inLength, outOffset, outLength] =
runState.stack.popN(6)
const toAddress = new Address(addressToBuffer(toAddr))
const bytecodeLength = await runState.eei.getExternalCodeSize(toAddr)
runState.vcm.computeFunctionCounters('opDelegateCall', {
isCreate: runState.eei._env.isCreate,
isDeploy: runState.eei._env.isDeploy,
inLength: inLength.toNumber(),
outLength: outLength.toNumber(),
})
runState.vcm.computeFunctionCounters('_processContractCall', {
bytecodeLength: bytecodeLength.toNumber(),
isDeploy: false,
isCreate: false,
isCreate2: false,
})
let data = Buffer.alloc(0)
if (!inLength.isZero()) {
data = runState.memory.read(inOffset.toNumber(), inLength.toNumber())
}
const gasLimit = runState.messageGasLimit!
runState.messageGasLimit = undefined
const ret = await runState.eei.callDelegate(gasLimit, toAddress, value, data, outLength)
// Write return data to memory
writeCallOutput(runState, outOffset, outLength)
runState.stack.push(ret.returnCode)
return ret.results
},
],
// 0x06: STATICCALL
[
0xfa,
async function (runState) {
const value = new BN(0)
const [_currentGasLimit, toAddr, inOffset, inLength, outOffset, outLength] =
runState.stack.popN(6)
const toAddress = new Address(addressToBuffer(toAddr))
const bytecodeLength = await runState.eei.getExternalCodeSize(toAddr)
runState.vcm.computeFunctionCounters('opStaticCall', {
isCreate: runState.eei._env.isCreate,
isDeploy: runState.eei._env.isDeploy,
inLength: inLength.toNumber(),
outLength: outLength.toNumber(),
})
runState.vcm.computeFunctionCounters('_processContractCall', {
bytecodeLength: bytecodeLength.toNumber(),
isDeploy: false,
isCreate: false,
isCreate2: false,
})
const gasLimit = runState.messageGasLimit!
runState.messageGasLimit = undefined
let data = Buffer.alloc(0)
if (!inLength.isZero()) {
data = runState.memory.read(inOffset.toNumber(), inLength.toNumber())
}
const ret = await runState.eei.callStatic(gasLimit, toAddress, value, data, outLength)
// Write return data to memory
writeCallOutput(runState, outOffset, outLength)
runState.stack.push(ret.returnCode)
return ret.results
},
],
// 0xf3: RETURN
[
0xf3,
function (runState) {
const [offset, length] = runState.stack.popN(2)
runState.vcm.computeFunctionCounters('opReturn', {
returnLength: length.toNumber(),
isCreate: runState.eei._env.isCreate,
isDeploy: runState.eei._env.isDeploy,
depth: runState.eei._env.depth,
})
let returnData = Buffer.alloc(0)
if (!length.isZero()) {
returnData = runState.memory.read(offset.toNumber(), length.toNumber())
}
runState.eei.finish(returnData)
},
],
// 0xfd: REVERT
[
0xfd,
function (runState) {
const [offset, length] = runState.stack.popN(2)
runState.vcm.computeFunctionCounters('opRevert', {
revertSize: length.toNumber(),
isCreate: runState.eei._env.isCreate,
isDeploy: runState.eei._env.isDeploy,
})
let returnData = Buffer.alloc(0)
if (!length.isZero()) {
returnData = runState.memory.read(offset.toNumber(), length.toNumber())
}
runState.eei.revert(returnData)
},
],
// '0x70', range - other
// 0xff: SELFDESTRUCT
[
0xff,
async function (runState) {
runState.vcm.computeFunctionCounters('opSendAll', {
isCreate: runState.eei._env.isCreate,
isDeploy: runState.eei._env.isDeploy,
})
const selfdestructToAddressBN = runState.stack.pop()
const selfdestructToAddress = new Address(addressToBuffer(selfdestructToAddressBN))
return runState.eei.selfDestruct(selfdestructToAddress)
},
],
])
// Fill in rest of PUSHn, DUPn, SWAPn, LOGn for handlers
const pushFn = handlers.get(0x60)!
for (let i = 0x61; i <= 0x7f; i++) {
handlers.set(i, pushFn)
}
const dupFn = handlers.get(0x80)!
for (let i = 0x81; i <= 0x8f; i++) {
handlers.set(i, dupFn)
}
const swapFn = handlers.get(0x90)!
for (let i = 0x91; i <= 0x9f; i++) {
handlers.set(i, swapFn)
}
const logFn = handlers.get(0xa0)!
for (let i = 0xa1; i <= 0xa4; i++) {
handlers.set(i, logFn)
}