Skip to content

Commit

Permalink
Merge pull request #754 from ethereumjs/eip-2315
Browse files Browse the repository at this point in the history
Implement EIP-2315: subroutines
  • Loading branch information
holgerd77 authored Aug 14, 2020
2 parents 25254da + 10f4186 commit 546736d
Show file tree
Hide file tree
Showing 10 changed files with 296 additions and 11 deletions.
15 changes: 14 additions & 1 deletion packages/common/src/hardforks/berlin.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,20 @@
"status": "Draft"
},
"gasConfig": {},
"gasPrices": {},
"gasPrices": {
"beginsub": {
"v": 2,
"d": "Base fee of the BEGINSUB opcode"
},
"returnsub": {
"v": 5,
"d": "Base fee of the RETURNSUB opcode"
},
"jumpsub": {
"v": 10,
"d": "Base fee of the JUMPSUB opcode"
}
},
"vm": {},
"pow": {}
}
27 changes: 23 additions & 4 deletions packages/vm/lib/evm/interpreter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@ export interface RunState {
memoryWordCount: BN
highestMemCost: BN
stack: Stack
returnStack: Stack
code: Buffer
validJumps: number[]
validJumpSubs: number[]
_common: Common
stateManager: StateManager
eei: EEI
Expand All @@ -36,6 +38,7 @@ export interface InterpreterStep {
gasLeft: BN
stateManager: StateManager
stack: BN[]
returnStack: BN[]
pc: number
depth: number
address: Buffer
Expand All @@ -50,6 +53,11 @@ export interface InterpreterStep {
codeAddress: Buffer
}

interface JumpDests {
jumps: number[]
jumpSubs: number[]
}

/**
* Parses and executes EVM bytecode.
*/
Expand All @@ -70,8 +78,10 @@ export default class Interpreter {
memoryWordCount: new BN(0),
highestMemCost: new BN(0),
stack: new Stack(),
returnStack: new Stack(1023), // 1023 return stack height limit per EIP 2315 spec
code: Buffer.alloc(0),
validJumps: [],
validJumpSubs: [],
// TODO: Replace with EEI methods
_common: this._vm._common,
stateManager: this._state,
Expand All @@ -82,7 +92,10 @@ export default class Interpreter {
async run(code: Buffer, opts: InterpreterOpts = {}): Promise<InterpreterResult> {
this._runState.code = code
this._runState.programCounter = opts.pc || this._runState.programCounter
this._runState.validJumps = this._getValidJumpDests(code)

const valid = this._getValidJumpDests(code)
this._runState.validJumps = valid.jumps
this._runState.validJumpSubs = valid.jumpSubs

// Check that the programCounter is in range
const pc = this._runState.programCounter
Expand Down Expand Up @@ -167,6 +180,7 @@ export default class Interpreter {
isAsync: opcode.isAsync,
},
stack: this._runState.stack._store,
returnStack: this._runState.returnStack._store,
depth: this._eei._env.depth,
address: this._eei._env.address,
account: this._eei._env.contract,
Expand Down Expand Up @@ -194,9 +208,10 @@ export default class Interpreter {
return this._vm._emit('step', eventObj)
}

// Returns all valid jump destinations.
_getValidJumpDests(code: Buffer): number[] {
// Returns all valid jump and jumpsub destinations.
_getValidJumpDests(code: Buffer): JumpDests {
const jumps = []
const jumpSubs = []

for (let i = 0; i < code.length; i++) {
const curOpCode = this.lookupOpInfo(code[i]).name
Expand All @@ -209,8 +224,12 @@ export default class Interpreter {
if (curOpCode === 'JUMPDEST') {
jumps.push(i)
}

if (curOpCode === 'BEGINSUB') {
jumpSubs.push(i)
}
}

return jumps
return { jumps, jumpSubs }
}
}
32 changes: 32 additions & 0 deletions packages/vm/lib/evm/opFns.ts
Original file line number Diff line number Diff line change
Expand Up @@ -546,6 +546,33 @@ export const handlers: { [k: string]: OpHandler } = {
runState.stack.push(new BN(runState.eei.getGasLeft()))
},
JUMPDEST: function (runState: RunState) {},
BEGINSUB: function (runState: RunState) {
trap(ERROR.INVALID_BEGINSUB + ' at ' + describeLocation(runState))
},
JUMPSUB: function (runState: 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
},
RETURNSUB: function (runState: RunState) {
if (runState.returnStack.length < 1) {
trap(ERROR.INVALID_RETURNSUB)
}

const dest = runState.returnStack.pop()
runState.programCounter = dest.toNumber()
},
PUSH: function (runState: RunState) {
const numToPush = runState.opCode - 0x5f
const loaded = new BN(
Expand Down Expand Up @@ -927,6 +954,11 @@ function maxCallGas(gasLimit: BN, gasLeft: BN, runState: RunState): BN {
}
}

// checks if a jumpsub is valid given a destination
function jumpSubIsValid(runState: RunState, dest: number): boolean {
return runState.validJumpSubs.indexOf(dest) !== -1
}

async function getContractStorage(runState: RunState, address: Buffer, key: Buffer) {
const current = await runState.stateManager.getContractStorage(address, key)
if (
Expand Down
8 changes: 8 additions & 0 deletions packages/vm/lib/evm/opcodes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,14 @@ const hardforkOpcodes = [
0x47: { name: 'SELFBALANCE', isAsync: false }, // EIP 1884
},
},
{
hardforkName: 'berlin',
opcodes: {
0x5c: { name: 'BEGINSUB', isAsync: false }, // EIP 2315
0x5d: { name: 'RETURNSUB', isAsync: false }, // EIP 2315
0x5e: { name: 'JUMPSUB', isAsync: false }, // EIP 2315
},
},
]

/**
Expand Down
6 changes: 4 additions & 2 deletions packages/vm/lib/evm/stack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@ const { ERROR, VmError } = require('../exceptions')
*/
export default class Stack {
_store: BN[]
_maxHeight: number

constructor() {
constructor(maxHeight?: number) {
this._store = []
this._maxHeight = maxHeight || 1024
}

get length() {
Expand All @@ -25,7 +27,7 @@ export default class Stack {
throw new VmError(ERROR.OUT_OF_RANGE)
}

if (this._store.length > 1023) {
if (this._store.length >= this._maxHeight) {
throw new VmError(ERROR.STACK_OVERFLOW)
}

Expand Down
3 changes: 3 additions & 0 deletions packages/vm/lib/exceptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ export enum ERROR {
STOP = 'stop',
REFUND_EXHAUSTED = 'refund exhausted',
VALUE_OVERFLOW = 'value overflow',
INVALID_BEGINSUB = 'invalid BEGINSUB',
INVALID_RETURNSUB = 'invalid RETURNSUB',
INVALID_JUMPSUB = 'invalid JUMPSUB',
}

export class VmError {
Expand Down
1 change: 1 addition & 0 deletions packages/vm/lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ export default class VM extends AsyncEventEmitter {
'petersburg',
'istanbul',
'muirGlacier',
'berlin',
]

this._common = new Common(chain, hardfork, supportedHardforks)
Expand Down
2 changes: 1 addition & 1 deletion packages/vm/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@
"@types/node": "^11.13.4",
"@types/tape": "^4.13.0",
"browserify": "^16.5.1",
"ethereumjs-testing": "git+https://github.com/ethereumjs/ethereumjs-testing.git#v1.3.1",
"ethereumjs-testing": "git+https://github.com/ethereumjs/ethereumjs-testing.git#v1.3.3",
"karma": "^4.1.0",
"karma-browserify": "^6.0.0",
"karma-chrome-launcher": "^2.2.0",
Expand Down
Loading

0 comments on commit 546736d

Please sign in to comment.