Skip to content

Commit

Permalink
Merge pull request #664 from rumkin/f/opcodes
Browse files Browse the repository at this point in the history
Refactor opcodes
  • Loading branch information
holgerd77 committed Apr 20, 2020
2 parents e229b8e + ec3539d commit 36fde16
Show file tree
Hide file tree
Showing 2 changed files with 98 additions and 34 deletions.
39 changes: 13 additions & 26 deletions packages/vm/lib/evm/interpreter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,11 @@ export interface InterpreterStep {
address: Buffer
memory: number[]
memoryWordCount: BN
opcode: Opcode
opcode: {
name: string
fee: number
isAsync: boolean
}
account: Account
codeAddress: Buffer
}
Expand Down Expand Up @@ -147,39 +151,22 @@ export default class Interpreter {
/**
* Get info for an opcode from VM's list of opcodes.
*/
lookupOpInfo(op: number, full: boolean = false): Opcode {
const opcode = this._vm._opcodes[op]
? this._vm._opcodes[op]
: { name: 'INVALID', fee: 0, isAsync: false }

if (full) {
let name = opcode.name
if (name === 'LOG') {
name += op - 0xa0
}

if (name === 'PUSH') {
name += op - 0x5f
}

if (name === 'DUP') {
name += op - 0x7f
}

if (name === 'SWAP') {
name += op - 0x8f
}
return { ...opcode, ...{ name } }
}
lookupOpInfo(op: number): Opcode {
const opcode = this._vm._opcodes[op] ? this._vm._opcodes[op] : this._vm._opcodes[0xfe]

return opcode
}

async _runStepHook(): Promise<void> {
const opcode = this.lookupOpInfo(this._runState.opCode)
const eventObj: InterpreterStep = {
pc: this._runState.programCounter,
gasLeft: this._eei.getGasLeft(),
opcode: this.lookupOpInfo(this._runState.opCode, true),
opcode: {
name: opcode.fullName,
fee: opcode.fee,
isAsync: opcode.isAsync,
},
stack: this._runState.stack._store,
depth: this._eei._env.depth,
address: this._eei._env.address,
Expand Down
93 changes: 85 additions & 8 deletions packages/vm/lib/evm/opcodes.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,41 @@
import Common from 'ethereumjs-common'

export interface Opcode {
name: string
fee: number
isAsync: boolean
export class Opcode {
readonly code: number
readonly name: string
readonly fullName: string
readonly fee: number
readonly isAsync: boolean

constructor({
code,
name,
fullName,
fee,
isAsync,
}: {
code: number
name: string
fullName: string
fee: number
isAsync: boolean
}) {
this.code = code
this.name = name
this.fullName = fullName
this.fee = fee
this.isAsync = isAsync

// Opcode isn't subject to change, thus all futher modifications are prevented.
Object.freeze(this)
}
}

export interface OpcodeList {
[code: number]: Opcode
}

const opcodes: OpcodeList = {
const opcodes: OpcodeList = createOpcodes({
// 0x0 range - arithmetic ops
// name, baseCost, async
0x00: { name: 'STOP', fee: 0, isAsync: false },
Expand Down Expand Up @@ -172,17 +197,69 @@ const opcodes: OpcodeList = {
// '0x70', range - other
0xfe: { name: 'INVALID', fee: 0, isAsync: false },
0xff: { name: 'SELFDESTRUCT', fee: 5000, isAsync: true },
}
})

const istanbulOpcodes: OpcodeList = {
const istanbulOpcodes: OpcodeList = createOpcodes({
0x31: { name: 'BALANCE', fee: 700, isAsync: true },
0x3f: { name: 'EXTCODEHASH', fee: 700, isAsync: true },
0x46: { name: 'CHAINID', fee: 2, isAsync: false },
0x47: { name: 'SELFBALANCE', fee: 5, isAsync: false },
0x54: { name: 'SLOAD', fee: 800, isAsync: true },
})

/**
* Convert basic opcode info dictonary into complete OpcodeList instance.
*
* @param opcodes {Object} Receive basic opcodes info dictionary.
* @returns {OpcodeList} Complete Opcode list
*/
function createOpcodes(opcodes: {
[key: string]: { name: string; fee: number; isAsync: boolean }
}): OpcodeList {
const result: OpcodeList = {}
for (const [key, value] of Object.entries(opcodes)) {
const code = parseInt(key, 10)
result[<any>key] = new Opcode({
code,
fullName: getFullname(code, value.name),
...value,
})
}
return result
}

/**
* Get full opcode name from its name and code.
*
* @param code {number} Integer code of opcode.
* @param name {string} Short name of the opcode.
* @returns {string} Full opcode name
*/
function getFullname(code: number, name: string): string {
switch (name) {
case 'LOG':
name += code - 0xa0
break
case 'PUSH':
name += code - 0x5f
break
case 'DUP':
name += code - 0x7f
break
case 'SWAP':
name += code - 0x8f
break
}
return name
}

export function getOpcodesForHF(common: Common) {
/**
* Get suitable opcodes for the required hardfork.
*
* @param common {Common} Ethereumjs Common metadaata object.
* @returns {OpcodeList} Opcodes dictionary object.
*/
export function getOpcodesForHF(common: Common): OpcodeList {
if (common.gteHardfork('istanbul')) {
return { ...opcodes, ...istanbulOpcodes }
} else {
Expand Down

0 comments on commit 36fde16

Please sign in to comment.