Skip to content

Commit

Permalink
Setup gradual process for migrating to Typescript (#495)
Browse files Browse the repository at this point in the history
* Drop babel deps, add initial typescript config

* Add prettier as dep, add format scripts

* Mv bloom/index.js to bloom/index.ts

* Migrate bloom to ts, limit its api to buffers

* Mv lib/evm/stack.js to lib/evm/stack.ts

* Migrate stack to ts, limit it to BN

* Mv lib/evm/memory.js to lib/evm/memory.ts

* Migrate evm memory to ts

* Build:dist before coverage and api browser tests

* Use tester dist flag for coverage tests
  • Loading branch information
s1na authored Apr 12, 2019
1 parent 484a339 commit affe8b4
Show file tree
Hide file tree
Showing 24 changed files with 124 additions and 110 deletions.
6 changes: 6 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
node_modules
.vscode
package.json
dist
.nyc_output
*.json
36 changes: 20 additions & 16 deletions lib/bloom/index.js → lib/bloom/index.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
const assert = require('assert')
const utils = require('ethereumjs-util')
import * as assert from 'assert'
import { zeros, keccak256 } from 'ethereumjs-util'

const BYTE_SIZE = 256

module.exports = class Bloom {
export default class Bloom {
bitvector: Buffer

/**
* Represents a Bloom
* @constructor
* @param {Buffer} bitvector
*/
constructor (bitvector) {
constructor (bitvector: Buffer) {
if (!bitvector) {
this.bitvector = utils.zeros(BYTE_SIZE)
this.bitvector = zeros(BYTE_SIZE)
} else {
assert(bitvector.length === BYTE_SIZE, 'bitvectors must be 2048 bits long')
this.bitvector = bitvector
Expand All @@ -21,10 +23,11 @@ module.exports = class Bloom {
/**
* adds an element to a bit vector of a 64 byte bloom filter
* @method add
* @param {Buffer|Array|String|Number} e the element to add
* @param {Buffer} e the element to add
*/
add (e) {
e = utils.keccak256(e)
add (e: Buffer) {
assert(Buffer.isBuffer(e), 'Element should be buffer')
e = keccak256(e)
const mask = 2047 // binary 11111111111

for (let i = 0; i < 3; i++) {
Expand All @@ -39,11 +42,12 @@ module.exports = class Bloom {
/**
* checks if an element is in the bloom
* @method check
* @param {Buffer|Array|String|Number} e the element to check
* @param {Buffer} e the element to check
* @returns {boolean} Returns {@code true} if the element is in the bloom
*/
check (e) {
e = utils.keccak256(e)
check (e: Buffer): boolean {
assert(Buffer.isBuffer(e), 'Element should be Buffer')
e = keccak256(e)
const mask = 2047 // binary 11111111111
let match = true

Expand All @@ -52,7 +56,7 @@ module.exports = class Bloom {
const loc = mask & first2bytes
const byteLoc = loc >> 3
const bitLoc = 1 << loc % 8
match = (this.bitvector[BYTE_SIZE - byteLoc - 1] & bitLoc)
match = (this.bitvector[BYTE_SIZE - byteLoc - 1] & bitLoc) !== 0
}

return Boolean(match)
Expand All @@ -61,19 +65,19 @@ module.exports = class Bloom {
/**
* checks if multiple topics are in a bloom
* @method multiCheck
* @param {Buffer[]|Array[]|String[]|Number[]} topics
* @param {Buffer[]} topics
* @returns {boolean} Returns {@code true} if every topic is in the bloom
*/
multiCheck (topics) {
return topics.every((t) => this.check(t))
multiCheck (topics: Buffer[]): boolean {
return topics.every((t: Buffer) => this.check(t))
}

/**
* bitwise or blooms together
* @method or
* @param {Bloom} bloom
*/
or (bloom) {
or (bloom: Bloom) {
if (bloom) {
for (let i = 0; i <= BYTE_SIZE; i++) {
this.bitvector[i] = this.bitvector[i] | bloom.bitvector[i]
Expand Down
4 changes: 2 additions & 2 deletions lib/evm/loop.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ const utils = require('ethereumjs-util')
const { StorageReader } = require('../state')
const PStateManager = require('../state/promisified')
const { ERROR, VmError } = require('../exceptions')
const Memory = require('./memory')
const Stack = require('./stack')
const Memory = require('./memory').default
const Stack = require('./stack').default
const EEI = require('./eei')
const lookupOpInfo = require('./opcodes.js')
const opFns = require('./opFns.js')
Expand Down
26 changes: 13 additions & 13 deletions lib/evm/memory.js → lib/evm/memory.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
import * as assert from 'assert'

/**
* Memory implements a simple memory model
* for the ethereum virtual machine.
*/
module.exports = class Memory {
export default class Memory {
_store: number[]

constructor () {
this._store = []
}
Expand All @@ -11,9 +15,9 @@ module.exports = class Memory {
* Extends the memory given an offset and size. Rounds extended
* memory to word-size.
* @param {Number} offset
* @param {size} size
* @param {Number} size
*/
extend (offset, size) {
extend (offset: number, size: number) {
if (size === 0) {
return
}
Expand All @@ -31,18 +35,14 @@ module.exports = class Memory {
* @param {Number} size - How many bytes to write
* @param {Buffer} value - Value
*/
write (offset, size, value) {
write (offset: number, size: number, value: Buffer) {
if (size === 0) {
return
}

if (value.length !== size) {
throw new Error('Invalid value size')
}

if (offset + size > this._store.length) {
throw new Error('Value exceeds memory capacity')
}
assert(value.length === size, 'Invalid value size')
assert(offset + size <= this._store.length, 'Value exceeds memory capacity')
assert(Buffer.isBuffer(value), 'Invalid value type')

for (let i = 0; i < size; i++) {
this._store[offset + i] = value[i]
Expand All @@ -56,7 +56,7 @@ module.exports = class Memory {
* @param {Number} size - How many bytes to read
* @returns {Buffer}
*/
read (offset, size) {
read (offset: number, size: number): Buffer {
const loaded = this._store.slice(offset, offset + size)
// Fill the remaining length with zeros
for (let i = loaded.length; i < size; i++) {
Expand All @@ -66,7 +66,7 @@ module.exports = class Memory {
}
}

const ceil = (value, ceiling) => {
const ceil = (value: number, ceiling: number): number => {
const r = value % ceiling
if (r === 0) {
return value
Expand Down
45 changes: 19 additions & 26 deletions lib/evm/stack.js → lib/evm/stack.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
const BN = require('bn.js')
const ethUtil = require('ethereumjs-util')
import BN = require('bn.js')
import { MAX_INTEGER } from 'ethereumjs-util'
const { ERROR, VmError } = require('../exceptions')

/**
* Implementation of the stack used in evm.
*/
module.exports = class Stack {
export default class Stack {
_store: BN[]

constructor () {
this._store = []
}
Expand All @@ -14,24 +16,29 @@ module.exports = class Stack {
return this._store.length
}

push (value) {
if (this._store.length > 1023) {
throw new VmError(ERROR.STACK_OVERFLOW)
push (value: BN) {
if (!BN.isBN(value)) {
throw new VmError(ERROR.INTERNAL_ERROR)
}

if (!this._isValidValue(value)) {
if (value.gt(MAX_INTEGER)) {
throw new VmError(ERROR.OUT_OF_RANGE)
}

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

this._store.push(value)
}

pop () {
pop (): BN {
if (this._store.length < 1) {
throw new VmError(ERROR.STACK_UNDERFLOW)
}

return this._store.pop()
// Length is checked above, so pop shouldn't return undefined
return this._store.pop()!
}

/**
Expand All @@ -40,7 +47,7 @@ module.exports = class Stack {
* @param {Number} num - Number of items to pop
* @returns {Array}
*/
popN (num = 1) {
popN (num: number = 1): BN[] {
if (this._store.length < num) {
throw new VmError(ERROR.STACK_UNDERFLOW)
}
Expand All @@ -56,7 +63,7 @@ module.exports = class Stack {
* Swap top of stack with an item in the stack.
* @param {Number} position - Index of item from top of the stack (0-indexed)
*/
swap (position) {
swap (position: number) {
if (this._store.length <= position) {
throw new VmError(ERROR.STACK_UNDERFLOW)
}
Expand All @@ -73,26 +80,12 @@ module.exports = class Stack {
* Pushes a copy of an item in the stack.
* @param {Number} position - Index of item to be copied (1-indexed)
*/
dup (position) {
dup (position: number) {
if (this._store.length < position) {
throw new VmError(ERROR.STACK_UNDERFLOW)
}

const i = this._store.length - position
this.push(this._store[i])
}

_isValidValue (value) {
if (BN.isBN(value)) {
if (value.lte(ethUtil.MAX_INTEGER)) {
return true
}
} else if (Buffer.isBuffer(value)) {
if (value.length <= 32) {
return true
}
}

return false
}
}
2 changes: 1 addition & 1 deletion lib/runBlock.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
const promisify = require('util.promisify')
const ethUtil = require('ethereumjs-util')
const Bloom = require('./bloom')
const Bloom = require('./bloom').default
const rlp = ethUtil.rlp
const Trie = require('merkle-patricia-tree')
const BN = ethUtil.BN
Expand Down
2 changes: 1 addition & 1 deletion lib/runTx.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
const utils = require('ethereumjs-util')
const BN = utils.BN
const Bloom = require('./bloom')
const Bloom = require('./bloom').default
const Block = require('ethereumjs-block')
const Account = require('ethereumjs-account')
const Interpreter = require('./evm/interpreter')
Expand Down
4 changes: 2 additions & 2 deletions lib/state/stateManager.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const Buffer = require('safe-buffer').Buffer
const Set = require('core-js-pure/es/set')
const Trie = require('merkle-patricia-tree/secure.js')
const Common = require('ethereumjs-common').default
const { genesisStateByName } = require('ethereumjs-common/dist/genesisStates')
Expand Down Expand Up @@ -281,7 +281,7 @@ module.exports = class StateManager {
checkpoint (cb) {
this._trie.checkpoint()
this._cache.checkpoint()
this._touchedStack.push(new Set([...this._touched]))
this._touchedStack.push(new Set(Array.from(this._touched)))
this._checkpointCount++
cb()
}
Expand Down
26 changes: 15 additions & 11 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
],
"scripts": {
"coverage": "nyc npm run coverageTests && nyc report --reporter=text-lcov > .nyc_output/lcov.info",
"coverageTests": "tape './tests/api/**/*.js' ./tests/tester.js -s",
"coverageTests": "npm run build:dist && tape './tests/api/**/*.js' ./tests/tester.js -s --dist",
"coveralls": "npm run coverage && if [ -n \"$COVERALLS_REPO_TOKEN\" ]; then coveralls <.nyc_output/lcov.info; fi",
"testVM": "node ./tests/tester -v",
"testStateByzantium": "npm run build:dist && node ./tests/tester -s --fork='Byzantium' --dist",
Expand All @@ -17,12 +17,14 @@
"testBuildIntegrity": "npm run build:dist && node ./tests/tester -s --dist --test='stackOverflow'",
"testBlockchain": "npm run build:dist && node --stack-size=1500 ./tests/tester -b --fork='Petersburg' --dist --excludeDir='GeneralStateTests'",
"testBlockchainGeneralStateTests": "npm run build:dist && node --stack-size=1500 ./tests/tester -b --dist --dir='GeneralStateTests'",
"testAPI": "tape './tests/api/**/*.js'",
"testAPI:browser": "karma start karma.conf.js",
"testAPI": "npm run build:dist && tape './tests/api/**/*.js'",
"testAPI:browser": "npm run build:dist && karma start karma.conf.js",
"test": "echo \"[INFO] Generic test cmd not used. See package.json for more specific test run cmds.\"",
"lint": "standard",
"format": "ethereumjs-config-format",
"format-fix": "ethereumjs-config-format-fix",
"prepublishOnly": "npm run lint && npm run build:dist && npm run testBuildIntegrity",
"build:dist": "babel lib/ -d dist/",
"build:dist": "tsc",
"build:docs": "documentation build ./lib/index.js ./lib/runBlockchain.js ./lib/runBlock.js ./lib/runTx.js ./lib/runCode.js ./lib/runCall.js --format md --shallow > ./docs/index.md",
"formatTest": "node ./scripts/formatTest"
},
Expand All @@ -35,14 +37,14 @@
"VM"
],
"dependencies": {
"@babel/runtime": "^7.4.0",
"async": "^2.1.2",
"async-eventemitter": "^0.2.2",
"core-js-pure": "^3.0.1",

This comment has been minimized.

Copy link
@codler

codler Oct 25, 2019

You can use care-js-pure to get less noise during npm install

This comment has been minimized.

Copy link
@s1na

s1na Oct 28, 2019

Author Contributor

Hm, I'm not sure if we even need core-js-pure any more. Will have to check

"ethereumjs-account": "^2.0.3",
"ethereumjs-block": "~2.2.0",
"ethereumjs-blockchain": "^3.4.0",
"ethereumjs-common": "^1.1.0",
"ethereumjs-util": "^6.0.0",
"ethereumjs-util": "^6.1.0",
"fake-merkle-patricia-tree": "^1.0.1",
"functional-red-black-tree": "^1.0.1",
"merkle-patricia-tree": "^2.3.2",
Expand All @@ -51,10 +53,9 @@
"util.promisify": "^1.0.0"
},
"devDependencies": {
"@babel/cli": "^7.2.3",
"@babel/core": "^7.4.0",
"@babel/plugin-transform-runtime": "^7.4.0",
"@babel/preset-env": "^7.4.1",
"@ethereumjs/config-prettier": "^1.1.1",
"@types/bn.js": "^4.11.5",
"@types/node": "^11.13.4",
"browserify": "^16.2.3",
"coveralls": "^3.0.0",
"documentation": "^8.1.2",
Expand All @@ -70,9 +71,12 @@
"level-mem": "^3.0.1",
"minimist": "^1.1.1",
"nyc": "^12.0.2",
"prettier": "^1.16.4",
"rlp": "^2.2.3",
"standard": "^10.0.0",
"tap-spec": "^5.0.0",
"tape": "4.6.3"
"tape": "4.6.3",
"typescript": "^3.4.3"
},
"author": "mjbecze <[email protected]>",
"contributors": [
Expand Down
Loading

0 comments on commit affe8b4

Please sign in to comment.