Skip to content

Commit 8faa894

Browse files
authored
Migrate interpreter & exceptions to typescript (ethereumjs#504)
* Change interpreter filetype to ts * Change exceptions filetype to ts * Migrate exceptions to ts * Fix exceptions import path * Migrate interpreter to ts
1 parent bcc0f69 commit 8faa894

18 files changed

+87
-81
lines changed

lib/evm/interpreter.js renamed to lib/evm/interpreter.ts

Lines changed: 55 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,37 @@
1-
const BN = require('bn.js')
2-
const ethUtil = require('ethereumjs-util')
3-
const { ERROR } = require('../exceptions')
1+
import BN = require('bn.js')
2+
import { toBuffer, generateAddress, generateAddress2, KECCAK256_NULL, MAX_INTEGER } from 'ethereumjs-util'
3+
import Account from 'ethereumjs-account'
4+
import { ERROR } from '../exceptions'
5+
import { StorageReader } from '../state'
6+
import PStateManager from '../state/promisified'
7+
import { getPrecompile, PrecompileFunc, PrecompileResult } from './precompiles'
8+
import { OOGResult } from './precompiles/types'
9+
import TxContext from './txContext'
10+
import Message from './message'
411
const Loop = require('./loop')
5-
const { StorageReader } = require('../state')
6-
const PStateManager = require('../state/promisified').default
7-
const { getPrecompile } = require('./precompiles')
8-
const { OOGResult } = require('./precompiles/types')
912

10-
module.exports = class Interpreter {
11-
constructor (vm, txContext, block, storageReader) {
13+
export interface InterpreterResult {
14+
gasUsed: BN
15+
createdAddress?: Buffer
16+
vm: any // TODO
17+
}
18+
19+
export default class Interpreter {
20+
_vm: any
21+
_state: PStateManager
22+
_storageReader: StorageReader
23+
_tx: TxContext
24+
_block: any
25+
26+
constructor (vm: any, txContext: TxContext, block: any, storageReader: StorageReader) {
1227
this._vm = vm
1328
this._state = new PStateManager(this._vm.stateManager)
1429
this._storageReader = storageReader || new StorageReader(this._state._wrapped)
1530
this._tx = txContext
1631
this._block = block
1732
}
1833

19-
async executeMessage (message) {
34+
async executeMessage (message: Message): Promise<InterpreterResult> {
2035
await this._state.checkpoint()
2136

2237
let result
@@ -46,14 +61,18 @@ module.exports = class Interpreter {
4661
return result
4762
}
4863

49-
async _executeCall (message) {
64+
async _executeCall (message: Message): Promise<InterpreterResult> {
5065
const account = await this._state.getAccount(message.caller)
5166
// Reduce tx value from sender
52-
await this._reduceSenderBalance(account, message)
67+
if (!message.delegatecall) {
68+
await this._reduceSenderBalance(account, message)
69+
}
5370
// Load `to` account
5471
const toAccount = await this._state.getAccount(message.to)
5572
// Add tx value to the `to` account
56-
await this._addToBalance(toAccount, message)
73+
if (!message.delegatecall) {
74+
await this._addToBalance(toAccount, message)
75+
}
5776

5877
// Load code
5978
await this._loadCode(message)
@@ -73,17 +92,17 @@ module.exports = class Interpreter {
7392
}
7493
}
7594

76-
async _executeCreate (message) {
95+
async _executeCreate (message: Message): Promise<InterpreterResult> {
7796
const account = await this._state.getAccount(message.caller)
7897
// Reduce tx value from sender
7998
await this._reduceSenderBalance(account, message)
8099

81100
message.code = message.data
82-
message.data = undefined
101+
message.data = Buffer.alloc(0)
83102
message.to = await this._generateAddress(message)
84103
let toAccount = await this._state.getAccount(message.to)
85104
// Check for collision
86-
if ((toAccount.nonce && new BN(toAccount.nonce) > 0) || toAccount.codeHash.compare(ethUtil.KECCAK256_NULL) !== 0) {
105+
if ((toAccount.nonce && new BN(toAccount.nonce).gtn(0)) || toAccount.codeHash.compare(KECCAK256_NULL) !== 0) {
87106
return {
88107
gasUsed: message.gasLimit,
89108
createdAddress: message.to,
@@ -145,7 +164,7 @@ module.exports = class Interpreter {
145164
}
146165
}
147166

148-
async runLoop (message) {
167+
async runLoop (message: Message): Promise<any> {
149168
const opts = {
150169
storageReader: this._storageReader,
151170
block: this._block,
@@ -157,7 +176,7 @@ module.exports = class Interpreter {
157176
let results
158177
const loop = new Loop(this._vm, this)
159178
if (message.isCompiled) {
160-
results = this.runPrecompile(message.code, message.data, message.gasLimit)
179+
results = this.runPrecompile(message.code as PrecompileFunc, message.data, message.gasLimit)
161180
} else {
162181
results = await loop.run(opts)
163182
}
@@ -170,11 +189,11 @@ module.exports = class Interpreter {
170189
* if no such precompile exists.
171190
* @param {Buffer} address
172191
*/
173-
getPrecompile (address) {
192+
getPrecompile (address: Buffer): PrecompileFunc {
174193
return getPrecompile(address.toString('hex'))
175194
}
176195

177-
runPrecompile (code, data, gasLimit) {
196+
runPrecompile (code: PrecompileFunc, data: Buffer, gasLimit: BN): PrecompileResult {
178197
if (typeof code !== 'function') {
179198
throw new Error('Invalid precompile')
180199
}
@@ -188,7 +207,7 @@ module.exports = class Interpreter {
188207
return code(opts)
189208
}
190209

191-
async _loadCode (message) {
210+
async _loadCode (message: Message): Promise<void> {
192211
if (!message.code) {
193212
const precompile = this.getPrecompile(message.codeAddress)
194213
if (precompile) {
@@ -201,39 +220,35 @@ module.exports = class Interpreter {
201220
}
202221
}
203222

204-
async _generateAddress (message) {
223+
async _generateAddress (message: Message): Promise<Buffer> {
205224
let addr
206225
if (message.salt) {
207-
addr = ethUtil.generateAddress2(message.caller, message.salt, message.code)
226+
addr = generateAddress2(message.caller, message.salt, message.code as Buffer)
208227
} else {
209228
const acc = await this._state.getAccount(message.caller)
210229
const newNonce = new BN(acc.nonce).subn(1)
211-
addr = ethUtil.generateAddress(message.caller, newNonce.toArray())
230+
addr = generateAddress(message.caller, newNonce.toArrayLike(Buffer))
212231
}
213232
return addr
214233
}
215234

216-
async _reduceSenderBalance (account, message) {
217-
if (!message.delegatecall) {
218-
const newBalance = new BN(account.balance).sub(message.value)
219-
account.balance = newBalance
220-
await this._state.putAccount(ethUtil.toBuffer(message.caller), account)
221-
}
235+
async _reduceSenderBalance (account: Account, message: Message): Promise<void> {
236+
const newBalance = new BN(account.balance).sub(message.value)
237+
account.balance = toBuffer(newBalance)
238+
return this._state.putAccount(toBuffer(message.caller), account)
222239
}
223240

224-
async _addToBalance (toAccount, message) {
225-
if (!message.delegatecall) {
226-
const newBalance = new BN(toAccount.balance).add(message.value)
227-
if (newBalance.gt(ethUtil.MAX_INTEGER)) {
228-
throw new Error('Value overflow')
229-
}
230-
toAccount.balance = newBalance
231-
// putAccount as the nonce may have changed for contract creation
232-
this._state.putAccount(ethUtil.toBuffer(message.to), toAccount)
241+
async _addToBalance (toAccount: Account, message: Message): Promise<void> {
242+
const newBalance = new BN(toAccount.balance).add(message.value)
243+
if (newBalance.gt(MAX_INTEGER)) {
244+
throw new Error('Value overflow')
233245
}
246+
toAccount.balance = toBuffer(newBalance)
247+
// putAccount as the nonce may have changed for contract creation
248+
return this._state.putAccount(toBuffer(message.to), toAccount)
234249
}
235250

236-
async _touchAccount (address) {
251+
async _touchAccount (address: Buffer): Promise<void> {
237252
const acc = await this._state.getAccount(address)
238253
return this._state.putAccount(address, acc)
239254
}

lib/evm/message.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import BN = require('bn.js')
2+
import { PrecompileFunc } from './precompiles'
23

34
export default class Message {
45
to: Buffer
@@ -7,7 +8,7 @@ export default class Message {
78
gasLimit: BN
89
data: Buffer
910
depth: number
10-
code: Buffer
11+
code: Buffer | PrecompileFunc
1112
_codeAddress: Buffer
1213
isStatic: boolean
1314
isCompiled: boolean
@@ -20,7 +21,7 @@ export default class Message {
2021
this.value = opts.value ? new BN(opts.value) : new BN(0)
2122
this.caller = opts.caller
2223
this.gasLimit = opts.gasLimit
23-
this.data = opts.data
24+
this.data = opts.data || Buffer.alloc(0)
2425
this.depth = opts.depth || 0
2526
this.code = opts.code
2627
this._codeAddress = opts.codeAddress

lib/evm/opFns.js

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
const Buffer = require('safe-buffer').Buffer
22
const utils = require('ethereumjs-util')
33
const BN = utils.BN
4-
const exceptions = require('../exceptions.js')
5-
const ERROR = exceptions.ERROR
6-
const VmError = exceptions.VmError
4+
const { ERROR, VmError } = require('../exceptions')
75
const MASK_160 = new BN(1).shln(160).subn(1)
86

97
// Find Ceil(`this` / `num`)

lib/evm/precompiles/01-ecrecover.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import BN = require('bn.js')
22
import { setLengthLeft, setLengthRight, ecrecover, publicToAddress } from 'ethereumjs-util'
33
import { PrecompileInput, PrecompileResult, OOGResult } from './types'
4-
const error = require('../../exceptions.js').ERROR
54
const assert = require('assert')
65

76
export default function (opts: PrecompileInput): PrecompileResult {

lib/evm/precompiles/02-sha256.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import BN = require('bn.js')
22
import { sha256 } from 'ethereumjs-util'
33
import { PrecompileInput, PrecompileResult, OOGResult } from './types'
4-
const error = require('../../exceptions.js').ERROR
54
const assert = require('assert')
65

76
export default function (opts: PrecompileInput): PrecompileResult {

lib/evm/precompiles/03-ripemd160.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import BN = require('bn.js')
22
import { ripemd160 } from 'ethereumjs-util'
33
import { PrecompileInput, PrecompileResult, OOGResult } from './types'
4-
const error = require('../../exceptions.js').ERROR
54
const assert = require('assert')
65

76
export default function (opts: PrecompileInput): PrecompileResult {

lib/evm/precompiles/04-identity.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import BN = require('bn.js')
22
import { PrecompileInput, PrecompileResult, OOGResult } from './types'
3-
const error = require('../../exceptions.js').ERROR
43
const assert = require('assert')
54

65
export default function (opts: PrecompileInput): PrecompileResult {

lib/evm/precompiles/05-modexp.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import BN = require('bn.js')
22
import { setLengthRight } from 'ethereumjs-util'
33
import { PrecompileInput, PrecompileResult, OOGResult } from './types'
4-
const error = require('../../exceptions.js').ERROR
54
const assert = require('assert')
65

76
function multComplexity (x: BN): BN {

lib/evm/precompiles/06-ecadd.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import BN = require('bn.js')
22
import { PrecompileInput, PrecompileResult, OOGResult } from './types'
3-
const error = require('../../exceptions.js').ERROR
43
const assert = require('assert')
54
const bn128 = require('rustbn.js')
65

lib/evm/precompiles/07-ecmul.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import BN = require('bn.js')
22
import { PrecompileInput, PrecompileResult, OOGResult } from './types'
3-
const ERROR = require('../../exceptions.js').ERROR
43
const assert = require('assert')
54
const bn128 = require('rustbn.js')
65

lib/evm/precompiles/08-ecpairing.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import BN = require('bn.js')
22
import { PrecompileInput, PrecompileResult, OOGResult } from './types'
3-
const error = require('../../exceptions.js').ERROR
43
const assert = require('assert')
54
const bn128 = require('rustbn.js')
65

lib/evm/precompiles/types.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import BN = require('bn.js')
22
import Common from 'ethereumjs-common'
3-
const { ERROR } = require('../../exceptions.js')
3+
import { ERROR } from '../../exceptions'
44

55
export interface PrecompileFunc {
66
(opts: PrecompileInput): PrecompileResult
@@ -16,7 +16,7 @@ export interface PrecompileResult {
1616
gasUsed: BN;
1717
return: Buffer;
1818
exception: 0 | 1;
19-
exceptionError?: string;
19+
exceptionError?: ERROR;
2020
}
2121

2222
export function OOGResult(gasLimit: BN): PrecompileResult {

lib/exceptions.js

Lines changed: 0 additions & 22 deletions
This file was deleted.

lib/exceptions.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
export enum ERROR {
2+
OUT_OF_GAS = 'out of gas',
3+
STACK_UNDERFLOW = 'stack underflow',
4+
STACK_OVERFLOW = 'stack overflow',
5+
INVALID_JUMP = 'invalid JUMP',
6+
INVALID_OPCODE = 'invalid opcode',
7+
OUT_OF_RANGE = 'value out of range',
8+
REVERT = 'revert',
9+
STATIC_STATE_CHANGE = 'static state change',
10+
INTERNAL_ERROR = 'internal error',
11+
CREATE_COLLISION = 'create collision'
12+
}
13+
14+
export class VmError {
15+
error: ERROR
16+
errorType: string
17+
18+
constructor (error: ERROR) {
19+
this.error = error
20+
this.errorType = 'VmError'
21+
}
22+
}

lib/runCall.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ const BN = ethUtil.BN
44
const { StorageReader } = require('./state')
55
const TxContext = require('./evm/txContext').default
66
const Message = require('./evm/message').default
7-
const Interpreter = require('./evm/interpreter')
7+
const Interpreter = require('./evm/interpreter').default
88

99
/**
1010
* runs a CALL operation

lib/runCode.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ const Block = require('ethereumjs-block')
1515
const Loop = require('./evm/loop')
1616
const TxContext = require('./evm/txContext').default
1717
const Message = require('./evm/message').default
18-
const Interpreter = require('./evm/interpreter')
18+
const Interpreter = require('./evm/interpreter').default
1919
const { StorageReader } = require('./state')
2020

2121
/**

lib/runTx.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ const BN = utils.BN
33
const Bloom = require('./bloom').default
44
const Block = require('ethereumjs-block')
55
const Account = require('ethereumjs-account').default
6-
const Interpreter = require('./evm/interpreter')
6+
const Interpreter = require('./evm/interpreter').default
77
const Message = require('./evm/message').default
88
const TxContext = require('./evm/txContext').default
99
const { StorageReader } = require('./state')

tests/api/runJit.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
const tape = require('tape')
22
const runJit = require('../../dist/runJit')
3-
const exceptions = require('../../dist/exceptions.js')
3+
const exceptions = require('../../dist/exceptions')
44

55
tape('Should run code with func type', (t) => {
66
// TODO: Determine if account is still necessary for runJit

0 commit comments

Comments
 (0)