diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..b60b88d --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,43 @@ +name: Build +on: + push: + branches: + - master + tags: + - '*' + pull_request: + types: [opened, reopened, synchronize] +jobs: + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/setup-node@v1 + - uses: actions/checkout@v1 + - run: npm install + - run: npm run lint + + coverage: + runs-on: ubuntu-latest + steps: + - uses: actions/setup-node@v1 + - uses: actions/checkout@v1 + - run: npm install + - run: npm run coverage + - name: Upload coverage to Coveralls + uses: coverallsapp/github-action@master + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + + test: + runs-on: ubuntu-latest + strategy: + matrix: + node-version: [10.x, 12.x, 13.x, 14.x] + steps: + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v1 + with: + node-version: ${{ matrix.node-version }} + - uses: actions/checkout@v1 + - run: npm install + - run: npm run test diff --git a/.gitignore b/.gitignore index 25c8fdb..c507f8f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ node_modules -package-lock.json \ No newline at end of file +package-lock.json +.nyc_output +coverage \ No newline at end of file diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..48f1b50 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,3 @@ +semi: false +singleQuote: true +trailingComma: none \ No newline at end of file diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 9b3c74f..0000000 --- a/.travis.yml +++ /dev/null @@ -1,25 +0,0 @@ -language: node_js -node_js: - - "4" - - "5" -env: - - CXX=g++-4.8 -addons: - apt: - sources: - - ubuntu-toolchain-r-test - packages: - - g++-4.8 -env: - matrix: - - CXX=g++-4.8 TEST_SUITE=test -matrix: - fast_finish: true - include: - - os: linux - node_js: "4" - env: CXX=g++-4.8 TEST_SUITE=coveralls - - os: linux - node_js: "4" - env: CXX=g++-4.8 TEST_SUITE=lint -script: npm run $TEST_SUITE diff --git a/README.md b/README.md index 1eb2c3c..914871a 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,18 @@ # SYNOPSIS -[![NPM Package](https://img.shields.io/npm/v/ethashjs.svg?style=flat-square)](https://www.npmjs.org/package/ethashjs) -[![Build Status](https://travis-ci.org/ethereumjs/ethashjs.svg?branch=master&style=flat-square)](https://travis-ci.org/ethereumjs/ethashjs) -[![Coverage Status](https://img.shields.io/coveralls/ethereumjs/ethashjs.svg?style=flat-square)](https://coveralls.io/r/ethereumjs/ethashjs) -[![Gitter](https://img.shields.io/gitter/room/ethereum/ethereumjs-lib.svg?style=flat-square)](https://gitter.im/ethereum/ethereumjs-lib) or #ethereumjs on freenode -Implements [Ethash](https://github.com/ethereum/wiki/wiki/Ethash) +[![NPM Package](https://img.shields.io/npm/v/ethashjs.svg)](https://www.npmjs.org/package/ethashjs) +[![Actions Status](https://github.com/ethereumjs/ethashjs/workflows/Build/badge.svg)](https://github.com/ethereumjs/ethashjs/actions) +[![Coverage Status](https://img.shields.io/coveralls/ethereumjs/ethashjs.svg)](https://coveralls.io/r/ethereumjs/ethashjs) +[![Gitter](https://img.shields.io/gitter/room/ethereum/ethereumjs-lib.svg)](https://gitter.im/ethereum/ethereumjs-lib) -# CONTACT - [Scrollback](https://scrollback.io/ethereumjs/all/all-messages) or #ethereumjs on freenode +Implements [Ethash](https://github.com/ethereum/wiki/wiki/Ethash). # INSTALL + `npm install ethashjs` # USAGE + ```javascript const Ethash = require('ethashjs') const Block = require('ethereumjs-block') @@ -23,61 +23,75 @@ var cacheDB = levelup('', { db: memdown }) -var ethash = new Ethash(cacheDB); -var validblockRlp = "f90667f905fba0a8d5b7a4793baaede98b5236954f634a0051842df6a252f6a80492fd888678bda01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a0f93c8db1e931daa2e22e39b5d2da6fb4074e3d544094857608536155e3521bc1a0bb7495628f9160ddbcf6354380ee32c300d594e833caec3a428041a66e7bade1a0c7778a7376099ee2e5c455791c1885b5c361b95713fddcbe32d97fd01334d296b90100000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000400000000000000000000000000000000000000000000000000000008302000001832fefd882560b84559c17b9b9040001020304050607080910111213141516171819202122232410000000000000000000200000000000000000003000000000000000000040000000000000000000500000000000000000006000000000000000000070000000000000000000800000000000000000009000000000000000000010000000000000000000100000000000000000002000000000000000000030000000000000000000400000000000000000005000000000000000000060000000000000000000700000000000000000008000000000000000000090000000000000000000100000000000000000001000000000000000000020000000000000000000300000000000000000004000000000000000000050000000000000000000600000000000000000007000000000000000000080000000000000000000900000000000000000001000000000000000000010000000000000000000200000000000000000003000000000000000000040000000000000000000500000000000000000006000000000000000000070000000000000000000800000000000000000009000000000000000000010000000000000000000100000000000000000002000000000000000000030000000000000000000400000000000000000005000000000000000000060000000000000000000700000000000000000008000000000000000000090000000000000000000100000000000000000001000000000000000000020000000000000000000300000000000000000004000000000000000000050000000000000000000600000000000000000007000000000000000000080000000000000000000900000000000000000001000000000000000000010000000000000000000200000000000000000003000000000000000000040000000000000000000500000000000000000006000000000000000000070000000000000000000800000000000000000009000000000000000000010000000000000000000100000000000000000002000000000000000000030000000000000000000400000000000000000005000000000000000000060000000000000000000700000000000000000008000000000000000000090000000000000000000100000000000000000001000000000000000000020000000000000000000300000000000000000004000000000000000000050000000000000000000600000000000000000007000000000000000000080000000000000000000900000000000000000001000000000000000000010000000000000000000200000000000000000003000000000000000000040000000000000000000500000000000000000006000000000000000000070000000000000000000800000000000000000009000000000000000000010000000000000000000a09c7b47112a3afb385c12924bf6280d273c106eea7caeaf5131d8776f61056c148876ae05d46b58d1fff866f864800a82c35094095e7baea6a6c7c4c2dfeb977efac326af552d8785012a05f200801ba01d2c92cfaeb04e53acdff2b5d42005ff6aacdb0105e64eb8c30c273f445d2782a01e7d50ffce57840360c57d94977b8cdebde614da23e8d1e77dc07928763cfe21c0" +var ethash = new Ethash(cacheDB) +var validblockRlp = + 'f90667f905fba0a8d5b7a4793baaede98b5236954f634a0051842df6a252f6a80492fd888678bda01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a0f93c8db1e931daa2e22e39b5d2da6fb4074e3d544094857608536155e3521bc1a0bb7495628f9160ddbcf6354380ee32c300d594e833caec3a428041a66e7bade1a0c7778a7376099ee2e5c455791c1885b5c361b95713fddcbe32d97fd01334d296b90100000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000400000000000000000000000000000000000000000000000000000008302000001832fefd882560b84559c17b9b9040001020304050607080910111213141516171819202122232410000000000000000000200000000000000000003000000000000000000040000000000000000000500000000000000000006000000000000000000070000000000000000000800000000000000000009000000000000000000010000000000000000000100000000000000000002000000000000000000030000000000000000000400000000000000000005000000000000000000060000000000000000000700000000000000000008000000000000000000090000000000000000000100000000000000000001000000000000000000020000000000000000000300000000000000000004000000000000000000050000000000000000000600000000000000000007000000000000000000080000000000000000000900000000000000000001000000000000000000010000000000000000000200000000000000000003000000000000000000040000000000000000000500000000000000000006000000000000000000070000000000000000000800000000000000000009000000000000000000010000000000000000000100000000000000000002000000000000000000030000000000000000000400000000000000000005000000000000000000060000000000000000000700000000000000000008000000000000000000090000000000000000000100000000000000000001000000000000000000020000000000000000000300000000000000000004000000000000000000050000000000000000000600000000000000000007000000000000000000080000000000000000000900000000000000000001000000000000000000010000000000000000000200000000000000000003000000000000000000040000000000000000000500000000000000000006000000000000000000070000000000000000000800000000000000000009000000000000000000010000000000000000000100000000000000000002000000000000000000030000000000000000000400000000000000000005000000000000000000060000000000000000000700000000000000000008000000000000000000090000000000000000000100000000000000000001000000000000000000020000000000000000000300000000000000000004000000000000000000050000000000000000000600000000000000000007000000000000000000080000000000000000000900000000000000000001000000000000000000010000000000000000000200000000000000000003000000000000000000040000000000000000000500000000000000000006000000000000000000070000000000000000000800000000000000000009000000000000000000010000000000000000000a09c7b47112a3afb385c12924bf6280d273c106eea7caeaf5131d8776f61056c148876ae05d46b58d1fff866f864800a82c35094095e7baea6a6c7c4c2dfeb977efac326af552d8785012a05f200801ba01d2c92cfaeb04e53acdff2b5d42005ff6aacdb0105e64eb8c30c273f445d2782a01e7d50ffce57840360c57d94977b8cdebde614da23e8d1e77dc07928763cfe21c0' -var validblock = new Block(new Buffer(validblockRlp, 'hex')) +var validblock = new Block(Buffer.from(validblockRlp, 'hex')) ethash.verifyPOW(validblock, function (result) { console.log(result) }) ``` -# BROWSER -Yep, You can [browserify](http://browserify.org/) it. +# BROWSER + +Yep, you can [browserify](http://browserify.org/) it. # API + - [`new Ethash([cacheDB])`](#newethashcachedb) - [`ethash.verifyPOW(block, cb)`](#ethashverifypowblock-cb) - [`ethash.mkcache(cacheSize, seed)`](#ethashmkcachecachesize-seed) - [`ethash.run(val, nonce, fullsize)`](#ethashrunval-nonce-fullsize) ### `new Ethash([cacheDB])` + Creates a new instance of `Ethash`. **Parameters** + - `cacheDB` - an instance of a levelup db which is used to store the cache(s) ### `ethash.verifyPOW(block, cb)` + Verifies the POW on a block and its uncles -**Parameters** +**Parameters** + - `block` - the [block](https://github.com/ethereum/ethereumjs-block) to verify -- `cb` - the callback which is given a `Boolean` determining the validaty of the block +- `cb` - the callback which is given a `Boolean` determining the validaty of the block ### `ethash.mkcache(cacheSize, seed)` -Creates a cache. NOTE: this is automatically done for in - [`ethash.verifyPOW(block, cb)`](#ethashverifypowblock-cb) so you do not need to use this function if you are just validating blocks -**Parameters** +Creates a cache. NOTE: this is automatically done for in - [`ethash.verifyPOW(block, cb)`](#ethashverifypowblock-cb) so you do not need to use this function if you are just validating blocks + +**Parameters** + - `cachSize` - the size of the cach - `seed` - the seed as a `Buffer` - ### `ethash.run(val, nonce, fullsize)` + Runs ethash on a give val/nonce pair. NOTE: you need to run [`ethash.mkcache(cacheSize, seed)`](#ethashverifypowcachesize-seed) first before using this function. -**Parameters** +**Parameters** + - `val` - value to run ethash on e.g. the header hash - `nonce` - the nonce used for this hash attempt - `fullsize` - the fullsize of the cache. -**Return** -and `Object` containing -- `hash` - the hash of the value +**Returns** + +An `Object` containing: + +- `hash` - the hash of the value - `mix` - the mis result # TESTS + `npm test` # LICENSE + [MPL-2.0](https://www.mozilla.org/MPL/2.0/) diff --git a/example/example.js b/example/example.js index 5f19349..5d6117b 100644 --- a/example/example.js +++ b/example/example.js @@ -2,7 +2,7 @@ const Ethash = require('../index.js') var ethash = new Ethash() // make the 1000 cache items with a seed of 0 * 32 -ethash.mkcache(1000, new Buffer(32).fill(0)) +ethash.mkcache(1000, Buffer.alloc(32).fill(0)) -var result = ethash.run(new Buffer('test'), new Buffer([0]), 1000) +var result = ethash.run(Buffer.from('test'), Buffer.from([0]), 1000) console.log(result.hash.toString('hex')) diff --git a/example/rawExample.js b/example/rawExample.js index 2c31cd0..3ba8dc8 100644 --- a/example/rawExample.js +++ b/example/rawExample.js @@ -8,7 +8,7 @@ Ethash.prototype.verifySubmit = function (number, headerHash, nonce, cb) { this.loadEpoc(number, function () { console.log('EPOC set') console.log(self.seed.toString('hex')) - var a = self.run(headerHash, new Buffer(nonce, 'hex')) + var a = self.run(headerHash, Buffer.from(nonce, 'hex')) cb(a.hash) }) } @@ -19,7 +19,10 @@ var cacheDB = levelup('', { var ethash = new Ethash(cacheDB) -var header = Buffer('0e2887aa1a0668bf8254d1a6ae518927de99e3e5d7f30fd1f16096e2608fe05e', 'hex') +var header = Buffer.from( + '0e2887aa1a0668bf8254d1a6ae518927de99e3e5d7f30fd1f16096e2608fe05e', + 'hex' +) ethash.verifySubmit(35414, header, 'e360b6170c229d15', function (result) { console.log(result.toString('hex')) diff --git a/index.js b/index.js index de8acf4..28e4c82 100644 --- a/index.js +++ b/index.js @@ -1,33 +1,40 @@ -const ethUtil = require('ethereumjs-util') +const { + BN, + keccak, + keccak256, + rlphash, + zeros, + bufferToInt, + TWO_POW256 +} = require('ethereumjs-util') const ethHashUtil = require('./util.js') const xor = require('buffer-xor') -const BN = ethUtil.BN const async = require('async') -var Ethash = module.exports = function (cacheDB) { +var Ethash = (module.exports = function (cacheDB) { this.dbOpts = { valueEncoding: 'json' } this.cacheDB = cacheDB this.cache = false -} +}) Ethash.prototype.mkcache = function (cacheSize, seed) { // console.log('generating cache') // console.log('size: ' + cacheSize) // console.log('seed: ' + seed.toString('hex')) const n = Math.floor(cacheSize / ethHashUtil.params.HASH_BYTES) - var o = [ethUtil.keccak(seed, 512)] + var o = [keccak(seed, 512)] var i for (i = 1; i < n; i++) { - o.push(ethUtil.keccak(o[o.length - 1], 512)) + o.push(keccak(o[o.length - 1], 512)) } for (var _ = 0; _ < ethHashUtil.params.CACHE_ROUNDS; _++) { for (i = 0; i < n; i++) { var v = o[i].readUInt32LE(0) % n - o[i] = ethUtil.keccak(xor(o[(i - 1 + n) % n], o[v]), 512) + o[i] = keccak(xor(o[(i - 1 + n) % n], o[v]), 512) } } @@ -37,28 +44,37 @@ Ethash.prototype.mkcache = function (cacheSize, seed) { Ethash.prototype.calcDatasetItem = function (i) { const n = this.cache.length - const r = Math.floor(ethHashUtil.params.HASH_BYTES / ethHashUtil.params.WORD_BYTES) - var mix = new Buffer(this.cache[i % n]) + const r = Math.floor( + ethHashUtil.params.HASH_BYTES / ethHashUtil.params.WORD_BYTES + ) + var mix = Buffer.from(this.cache[i % n]) mix.writeInt32LE(mix.readUInt32LE(0) ^ i, 0) - mix = ethUtil.keccak(mix, 512) + mix = keccak(mix, 512) for (var j = 0; j < ethHashUtil.params.DATASET_PARENTS; j++) { - var cacheIndex = ethHashUtil.fnv(i ^ j, mix.readUInt32LE(j % r * 4)) + var cacheIndex = ethHashUtil.fnv(i ^ j, mix.readUInt32LE((j % r) * 4)) mix = ethHashUtil.fnvBuffer(mix, this.cache[cacheIndex % n]) } - return ethUtil.keccak(mix, 512) + return keccak(mix, 512) } Ethash.prototype.run = function (val, nonce, fullSize) { fullSize = fullSize || this.fullSize const n = Math.floor(fullSize / ethHashUtil.params.HASH_BYTES) - const w = Math.floor(ethHashUtil.params.MIX_BYTES / ethHashUtil.params.WORD_BYTES) - const s = ethUtil.keccak(Buffer.concat([val, ethHashUtil.bufReverse(nonce)]), 512) - const mixhashes = Math.floor(ethHashUtil.params.MIX_BYTES / ethHashUtil.params.HASH_BYTES) + const w = Math.floor( + ethHashUtil.params.MIX_BYTES / ethHashUtil.params.WORD_BYTES + ) + const s = keccak(Buffer.concat([val, ethHashUtil.bufReverse(nonce)]), 512) + const mixhashes = Math.floor( + ethHashUtil.params.MIX_BYTES / ethHashUtil.params.HASH_BYTES + ) var mix = Buffer.concat(Array(mixhashes).fill(s)) var i for (i = 0; i < ethHashUtil.params.ACCESSES; i++) { - var p = ethHashUtil.fnv(i ^ s.readUInt32LE(0), mix.readUInt32LE(i % w * 4)) % Math.floor(n / mixhashes) * mixhashes + var p = + (ethHashUtil.fnv(i ^ s.readUInt32LE(0), mix.readUInt32LE((i % w) * 4)) % + Math.floor(n / mixhashes)) * + mixhashes var newdata = [] for (var j = 0; j < mixhashes; j++) { newdata.push(this.calcDatasetItem(p + j)) @@ -68,9 +84,12 @@ Ethash.prototype.run = function (val, nonce, fullSize) { mix = ethHashUtil.fnvBuffer(mix, newdata) } - var cmix = new Buffer(mix.length / 4) + var cmix = Buffer.alloc(mix.length / 4) for (i = 0; i < mix.length / 4; i = i + 4) { - var a = ethHashUtil.fnv(mix.readUInt32LE(i * 4), mix.readUInt32LE((i + 1) * 4)) + var a = ethHashUtil.fnv( + mix.readUInt32LE(i * 4), + mix.readUInt32LE((i + 1) * 4) + ) var b = ethHashUtil.fnv(a, mix.readUInt32LE((i + 2) * 4)) var c = ethHashUtil.fnv(b, mix.readUInt32LE((i + 3) * 4)) cmix.writeUInt32LE(c, i) @@ -78,16 +97,16 @@ Ethash.prototype.run = function (val, nonce, fullSize) { return { mix: cmix, - hash: ethUtil.keccak256(Buffer.concat([s, cmix])) + hash: keccak256(Buffer.concat([s, cmix])) } } Ethash.prototype.cacheHash = function () { - return ethUtil.keccak256(Buffer.concat(this.cache)) + return keccak256(Buffer.concat(this.cache)) } Ethash.prototype.headerHash = function (header) { - return ethUtil.rlphash(header.slice(0, -2)) + return rlphash(header.slice(0, -2)) } /** @@ -109,7 +128,7 @@ Ethash.prototype.loadEpoc = function (number, cb) { // gives the seed the first epoc found function findLastSeed (epoc, cb2) { if (epoc === 0) { - return cb2(ethUtil.zeros(32), 0) + return cb2(zeros(32), 0) } self.cacheDB.get(epoc, self.dbOpts, function (err, data) { @@ -131,21 +150,26 @@ Ethash.prototype.loadEpoc = function (number, cb) { self.seed = ethHashUtil.getSeed(seed, foundEpoc, epoc) var cache = self.mkcache(self.cacheSize, self.seed) // store the generated cache - self.cacheDB.put(epoc, { - cacheSize: self.cacheSize, - fullSize: self.fullSize, - seed: self.seed, - cache: cache - }, self.dbOpts, cb) + self.cacheDB.put( + epoc, + { + cacheSize: self.cacheSize, + fullSize: self.fullSize, + seed: self.seed, + cache: cache + }, + self.dbOpts, + cb + ) }) } else { // Object.assign(self, data) self.cache = data.cache.map(function (a) { - return new Buffer(a) + return Buffer.from(a) }) self.cacheSize = data.cacheSize self.fullSize = data.fullSize - self.seed = new Buffer(data.seed) + self.seed = Buffer.from(data.seed) cb() } }) @@ -155,12 +179,17 @@ Ethash.prototype.loadEpoc = function (number, cb) { Ethash.prototype._verifyPOW = function (header, cb) { var self = this var headerHash = this.headerHash(header.raw) - var number = ethUtil.bufferToInt(header.number) + var number = bufferToInt(header.number) this.loadEpoc(number, function () { - var a = self.run(headerHash, new Buffer(header.nonce, 'hex')) + var a = self.run(headerHash, Buffer.from(header.nonce, 'hex')) var result = new BN(a.hash) - cb(a.mix.toString('hex') === header.mixHash.toString('hex') && (ethUtil.TWO_POW256.div(new BN(header.difficulty)).cmp(result) === 1)) + /* eslint-disable standard/no-callback-literal */ + cb( + a.mix.toString('hex') === header.mixHash.toString('hex') && + TWO_POW256.div(new BN(header.difficulty)).cmp(result) === 1 + ) + /* eslint-enable standard/no-callback-literal */ }) } @@ -170,7 +199,7 @@ Ethash.prototype.verifyPOW = function (block, cb) { // don't validate genesis blocks if (block.header.isGenesis()) { - cb(true) + cb(true) // eslint-disable-line standard/no-callback-literal return } @@ -181,17 +210,21 @@ Ethash.prototype.verifyPOW = function (block, cb) { return cb(valid) } - async.eachSeries(block.uncleHeaders, function (uheader, cb2) { - self._verifyPOW(uheader, function (valid3) { - valid &= valid3 - if (!valid) { - cb2(Boolean(valid)) - } else { - cb2() - } - }) - }, function () { - cb(Boolean(valid)) - }) + async.eachSeries( + block.uncleHeaders, + function (uheader, cb2) { + self._verifyPOW(uheader, function (valid3) { + valid &= valid3 + if (!valid) { + cb2(Boolean(valid)) + } else { + cb2() + } + }) + }, + function () { + cb(Boolean(valid)) + } + ) }) } diff --git a/package.json b/package.json index d9503a2..cd865e3 100644 --- a/package.json +++ b/package.json @@ -7,8 +7,7 @@ "test": "tests" }, "scripts": { - "coverage": "istanbul cover ./tests/", - "coveralls": "npm run coverage && coveralls >> 0 -} +var fnv = (exports.fnv = function (x, y) { + return ((((x * 0x01000000) | 0) + ((x * 0x193) | 0)) ^ y) >>> 0 +}) exports.fnvBuffer = function (a, b) { - var r = new Buffer(a.length) + var r = Buffer.alloc(a.length) for (var i = 0; i < a.length; i = i + 4) { r.writeUInt32LE(fnv(a.readUInt32LE(i), b.readUInt32LE(i)), i) } @@ -70,7 +73,7 @@ exports.fnvBuffer = function (a, b) { exports.bufReverse = function (a) { const length = a.length - var b = new Buffer(length) + var b = Buffer.alloc(length) for (var i = 0; i < length; i++) { b[i] = a[length - i - 1] }