UNPKG

@polygon-hermez/vm

Version:
1,416 lines (1,369 loc) 40.4 kB
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) }