From 7459df2bf8146f09b4efc80ff49f90f855948567 Mon Sep 17 00:00:00 2001 From: Vinay Pulim Date: Sat, 28 Jul 2018 05:30:47 -0400 Subject: [PATCH 1/5] Fix: Cached objects should not be mutable --- index.js | 62 ++++++++++++++++++++++++++------------------------- package.json | 4 ++-- test/index.js | 21 ++++++++++++++++- 3 files changed, 54 insertions(+), 33 deletions(-) diff --git a/index.js b/index.js index 5273453..76ff4f7 100644 --- a/index.js +++ b/index.js @@ -331,38 +331,41 @@ Blockchain.prototype._putBlock = function (block, cb, isGenesis) { function rebuildInfo (next) { // save block and total difficulty to the database var key = tdKey(number, hash) + var value = rlp.encode(td) dbOps.push({ type: 'put', key: key, keyEncoding: 'binary', valueEncoding: 'binary', - value: rlp.encode(td) + value: value }) - self._cache.td.set(key, td) + self._cache.td.set(key, value) // save header key = headerKey(number, hash) + value = rlp.encode(header.raw) dbOps.push({ type: 'put', key: key, keyEncoding: 'binary', valueEncoding: 'binary', - value: rlp.encode(header.raw) + value: value }) - self._cache.header.set(key, header) + self._cache.header.set(key, value) // store body if not empty if (block.transactions.length || block.uncleHeaders.length) { var body = block.serialize(false).slice(1) key = bodyKey(number, hash) + value = rlp.encode(body) dbOps.push({ type: 'put', key: key, keyEncoding: 'binary', valueEncoding: 'binary', - value: rlp.encode(body) + value: value }) - self._cache.body.set(key, body) + self._cache.body.set(key, value) } // if total difficulty is higher than current, add it to canonical chain @@ -383,14 +386,15 @@ Blockchain.prototype._putBlock = function (block, cb, isGenesis) { } else { // save hash to number lookup info even if rebuild not needed key = hashToNumberKey(hash) + value = bufBE8(number) dbOps.push({ type: 'put', key: key, keyEncoding: 'binary', valueEncoding: 'binary', - value: bufBE8(number) + value: value }) - self._cache.hashToNumber.set(key, number) + self._cache.hashToNumber.set(key, value) next() } } @@ -603,6 +607,7 @@ Blockchain.prototype._rebuildCanonical = function (header, ops, cb) { function saveLookups (hash, number) { var key = numberToHashKey(number) + var value ops.push({ type: 'put', key: key, @@ -613,14 +618,15 @@ Blockchain.prototype._rebuildCanonical = function (header, ops, cb) { self._cache.numberToHash.set(key, hash) key = hashToNumberKey(hash) + value = bufBE8(number) ops.push({ type: 'put', key: key, keyEncoding: 'binary', valueEncoding: 'binary', - value: bufBE8(number) + value: value }) - self._cache.hashToNumber.set(key, number) + self._cache.hashToNumber.set(key, value) } // handle genesis block @@ -885,16 +891,15 @@ Blockchain.prototype._hashToNumber = function (hash, cb) { var key = hashToNumberKey(hash) var number = self._cache.hashToNumber.get(key) if (number) { - return cb(null, number) + return cb(null, new BN(number)) } self.db.get(key, { keyEncoding: 'binary', valueEncoding: 'binary' }, (err, number) => { if (err) return cb(err) - number = new BN(number) self._cache.hashToNumber.set(key, number) - cb(null, number) + cb(null, new BN(number)) }) } @@ -931,7 +936,7 @@ Blockchain.prototype._lookupByHashNumber = function (hash, number, cb, next) { if (typeof number === 'function') { cb = number return this._hashToNumber(hash, (err, number) => { - if (err) return next(err) + if (err) return next(err, hash, null, cb) next(null, hash, number, cb) }) } @@ -948,18 +953,17 @@ Blockchain.prototype._getHeader = function (hash, number, cb) { self._lookupByHashNumber(hash, number, cb, (err, hash, number, cb) => { if (err) return cb(err) var key = headerKey(number, hash) - var header = self._cache.header.get(key) - if (header) { - return cb(null, header) + var encodedHeader = self._cache.header.get(key) + if (encodedHeader) { + return cb(null, new Block.Header(rlp.decode(encodedHeader))) } - self.db.get(headerKey(number, hash), { + self.db.get(key, { keyEncoding: 'binary', valueEncoding: 'binary' }, (err, encodedHeader) => { if (err) return cb(err) - header = new Block.Header(rlp.decode(encodedHeader)) - self._cache.header.set(key, header) - cb(null, header) + self._cache.header.set(key, encodedHeader) + cb(null, new Block.Header(rlp.decode(encodedHeader))) }) }) } @@ -987,18 +991,17 @@ Blockchain.prototype._getBody = function (hash, number, cb) { self._lookupByHashNumber(hash, number, cb, (err, hash, number, cb) => { if (err) return cb(err) var key = bodyKey(number, hash) - var body = self._cache.body.get(key) - if (body) { - return cb(null, body) + var encodedBody = self._cache.body.get(key) + if (encodedBody) { + return cb(null, rlp.decode(encodedBody)) } self.db.get(key, { keyEncoding: 'binary', valueEncoding: 'binary' }, (err, encodedBody) => { if (err) return cb(err) - body = rlp.decode(encodedBody) - self._cache.body.set(key, body) - cb(null, body) + self._cache.body.set(key, encodedBody) + cb(null, rlp.decode(encodedBody)) }) }) } @@ -1015,16 +1018,15 @@ Blockchain.prototype._getTd = function (hash, number, cb) { var key = tdKey(number, hash) var td = self._cache.td.get(key) if (td) { - return cb(null, td) + return cb(null, new BN(rlp.decode(td))) } self.db.get(key, { keyEncoding: 'binary', valueEncoding: 'binary' }, (err, td) => { if (err) return cb(err) - td = new BN(rlp.decode(td)) self._cache.td.set(key, td) - cb(null, td) + cb(null, new BN(rlp.decode(td))) }) }) } diff --git a/package.json b/package.json index 36e6093..f3ff120 100644 --- a/package.json +++ b/package.json @@ -30,10 +30,10 @@ "ethereumjs-util": "~5.2.0", "flow-stoplight": "^1.0.0", "levelup": "^1.3.0", + "lru-cache": "^4.1.2", "memdown": "^1.1.0", "safe-buffer": "^5.1.1", - "semaphore": "^1.0.3", - "lru-cache": "^4.1.2" + "semaphore": "^1.0.3" }, "devDependencies": { "coveralls": "^3.0.0", diff --git a/test/index.js b/test/index.js index ced9fa9..8f0832f 100644 --- a/test/index.js +++ b/test/index.js @@ -11,7 +11,7 @@ const testData = require('./testdata.json') const BN = require('bn.js') test('blockchain test', function (t) { - t.plan(59) + t.plan(60) var blockchain = new Blockchain() var genesisBlock var blocks = [] @@ -364,6 +364,25 @@ test('blockchain test', function (t) { }) ], done) }) + }, + function immutableCachedObjects (done) { + var blockchain = new Blockchain({validate: false}) + var cachedHash + async.series([ + (cb) => blockchain.putBlock(blocks[1], (err) => { + if (err) return done(err) + cachedHash = blocks[1].hash() + cb() + }), + (cb) => { + blocks[1].header.extraData = Buffer.from([1]) + blockchain.getBlock(1, (err, block) => { + if (err) return done(err) + t.equals(cachedHash.toString('hex'), block.hash().toString('hex'), 'should not modify cached objects') + cb() + }) + } + ], done) } ], function (err) { if (err) { From 3a944980335b825c03313ba3197164eb217dd066 Mon Sep 17 00:00:00 2001 From: Vinay Pulim Date: Sat, 28 Jul 2018 06:27:55 -0400 Subject: [PATCH 2/5] Added putHeader()/putHeaders() to support header chains --- README.md | 15 ++++++++ index.js | 102 ++++++++++++++++++++++++++++++++++++++++++-------- test/index.js | 96 +++++++++++++++++++++++++++++++++++++---------- 3 files changed, 177 insertions(+), 36 deletions(-) diff --git a/README.md b/README.md index 3c77750..98669f1 100644 --- a/README.md +++ b/README.md @@ -44,6 +44,8 @@ new Blockchain({db: db}).iterator('i', (block, reorg, cb) => { - [`blockchain.putBlock(block, [cb])`](#blockchainputblockblock-cb) - [`blockchain.getBlock(hash, [cb])`](#blockchaingetblockhash-cb) - [`blockchain.getBlocks(blockId, maxBlocks, skip, reverse, [cb])`](#blockchaingetblocksblockid-maxblocks-skip-reverse-cb) + - [`blockchain.putHeaders(headers, [cb])`](#blockchainputheadersheaders-cb) + - [`blockchain.putHeader(header, [cb])`](#blockchainputheaderheader-cb) - [`blockchain.selectNeededHashes(hashes, [cb])`](#blockchainselectneededhasheshashes-cb) - [`blockchain.delBlock(blockHash, [cb])`](#blockchaindelblockblockhash-cb) - [`blockchain.iterator(name, onBlock, [cb])`](#blockchainiteratorname-onblock-cb) @@ -119,6 +121,19 @@ Looks up many blocks relative to blockId. -------------------------------------------------------- +#### `blockchain.putHeaders(headers, cb)` +Adds many headers to the blockchain. +- `headers` - the headers to be added to the blockchain +- `cb` - the callback. It is given two parameters `err` and the last of the saved `headers` +-------------------------------------------------------- + +#### `blockchain.putHeader(header, cb)` +Adds a header to the blockchain. +- `header` - the header to be added to the blockchain +- `cb` - the callback. It is given two parameters `err` and the saved `header` + +-------------------------------------------------------- + #### `blockchain.getDetails(hash, cb)` [DEPRECATED] Returns an empty object diff --git a/index.js b/index.js index 76ff4f7..a6e3210 100644 --- a/index.js +++ b/index.js @@ -146,7 +146,7 @@ Blockchain.prototype._setCanonicalGenesisBlock = function (cb) { const self = this var genesisBlock = new Block() genesisBlock.setGenesisParams() - self._putBlock(genesisBlock, cb, true) + self._putBlockOrHeader(genesisBlock, cb, true) } /** @@ -243,7 +243,7 @@ Blockchain.prototype.putBlock = function (block, cb, isGenesis) { self._initLock.await(() => { // perform put with mutex dance lockUnlock(function (done) { - self._putBlock(block, done, isGenesis) + self._putBlockOrHeader(block, done, isGenesis) }, cb) }) @@ -262,13 +262,61 @@ Blockchain.prototype.putBlock = function (block, cb, isGenesis) { } } -Blockchain.prototype._putBlock = function (block, cb, isGenesis) { +/** + * Adds many headers to the blockchain + * @method putHeaders + * @param {array} headers - the headers to be added to the blockchain + * @param {function} cb - a callback function + */ + +Blockchain.prototype.putHeaders = function (headers, cb) { + const self = this + async.eachSeries(headers, function (header, done) { + self.putHeader(header, done) + }, cb) +} + +/** + * Adds a header to the blockchain + * @method putHeader + * @param {object} header -the header to be added to the block chain + * @param {function} cb - a callback function + */ +Blockchain.prototype.putHeader = function (header, cb) { const self = this + + // make sure init has completed + self._initLock.await(() => { + // perform put with mutex dance + lockUnlock(function (done) { + self._putBlockOrHeader(header, done) + }, cb) + }) + + // lock, call fn, unlock + function lockUnlock (fn, cb) { + // take lock + self._putSemaphore.take(function () { + // call fn + fn(function () { + // leave lock + self._putSemaphore.leave() + // exit + cb.apply(null, arguments) + }) + }) + } +} + +Blockchain.prototype._putBlockOrHeader = function (item, cb, isGenesis) { + const self = this + var isHeader = item instanceof Block.Header + var block = isHeader ? new Block([item.raw, [], []]) : item var header = block.header var hash = block.hash() var number = new BN(header.number) var td = new BN(header.difficulty) - var currentTd = null + var currentTd = { header: null, block: null } var dbOps = [] if (block.constructor !== Block) { @@ -305,14 +353,20 @@ Blockchain.prototype._putBlock = function (block, cb, isGenesis) { function getCurrentTd (next) { if (isGenesis) { - currentTd = 0 + currentTd.header = new BN(0) + currentTd.block = new BN(0) return next() } - self._getTd(self._headHeader, (err, td) => { - if (err) return next(err) - currentTd = td - next() - }) + async.parallel([ + (cb) => self._getTd(self._headHeader, (err, td) => { + currentTd.header = td + cb(err) + }), + (cb) => self._getTd(self._headBlock, (err, td) => { + currentTd.block = td + cb(err) + }) + ], next) } function getBlockTd (next) { @@ -353,8 +407,8 @@ Blockchain.prototype._putBlock = function (block, cb, isGenesis) { }) self._cache.header.set(key, value) - // store body if not empty - if (block.transactions.length || block.uncleHeaders.length) { + // store body if it exists + if (isGenesis || block.transactions.length || block.uncleHeaders.length) { var body = block.serialize(false).slice(1) key = bodyKey(number, hash) value = rlp.encode(body) @@ -369,11 +423,11 @@ Blockchain.prototype._putBlock = function (block, cb, isGenesis) { } // if total difficulty is higher than current, add it to canonical chain - // second if clause is to reduce selfish mining vulnerability - if (block.isGenesis() || td.cmp(currentTd) > 0 || - (td.cmp(currentTd) === 0 && Math.random() < 0.5)) { + if (block.isGenesis() || td.gt(currentTd.header)) { self._headHeader = hash - self._headBlock = hash + if (!isHeader) { + self._headBlock = hash + } if (block.isGenesis()) { self._genesis = hash } @@ -384,6 +438,9 @@ Blockchain.prototype._putBlock = function (block, cb, isGenesis) { (cb) => self._rebuildCanonical(header, dbOps, cb) ], next) } else { + if (td.gt(currentTd.block) && !isHeader) { + self._headBlock = hash + } // save hash to number lookup info even if rebuild not needed key = hashToNumberKey(hash) value = bufBE8(number) @@ -594,6 +651,10 @@ Blockchain.prototype._deleteStaleAssignments = function (number, headHash, ops, self._heads[name] = headHash } }) + // reset stale headBlock to current canonical + if (self._headBlock.equals(hash)) { + self._headBlock = headHash + } self._deleteStaleAssignments(number.addn(1), headHash, ops, cb) }) @@ -647,6 +708,10 @@ Blockchain.prototype._rebuildCanonical = function (header, ops, cb) { self._staleHeads.push(name) } }) + // flag stale headBlock for reset + if (staleHash && self._headBlock.equals(staleHash)) { + self._staleHeadBlock = true + } self._getHeader(header.parentHash, number.subn(1), (err, header) => { if (err) { @@ -661,6 +726,11 @@ Blockchain.prototype._rebuildCanonical = function (header, ops, cb) { self._heads[name] = hash }) delete self._staleHeads + // set stale headBlock to last previously valid canonical block + if (self._staleHeadBlock) { + self._headBlock = hash + delete self._staleHeadBlock + } cb() } }) diff --git a/test/index.js b/test/index.js index 8f0832f..e89ac26 100644 --- a/test/index.js +++ b/test/index.js @@ -9,13 +9,14 @@ const levelup = require('levelup') const memdown = require('memdown') const testData = require('./testdata.json') const BN = require('bn.js') +const rlp = ethUtil.rlp test('blockchain test', function (t) { - t.plan(60) + t.plan(66) var blockchain = new Blockchain() var genesisBlock var blocks = [] - var forkBlock + var forkHeader blockchain.validate = false async.series([ @@ -237,22 +238,24 @@ test('blockchain test', function (t) { t.ok(blockchain.meta.heads['test'], 'should get meta.heads') done() }, - function addForkBlockAndResetStaleHeads (done) { - forkBlock = new Block() - forkBlock.header.number = ethUtil.toBuffer(9) - forkBlock.header.difficulty = '0xffffffff' - forkBlock.header.parentHash = blocks[8].hash() + function addForkHeaderAndResetStaleHeads (done) { + forkHeader = new Block.Header() + forkHeader.number = ethUtil.toBuffer(9) + forkHeader.difficulty = '0xffffffff' + forkHeader.parentHash = blocks[8].hash() blockchain._heads['staletest'] = blockchain._headHeader - blockchain.putBlock(forkBlock, function (err) { + blockchain.putHeader(forkHeader, function (err) { t.equals(blockchain._heads['staletest'].toString('hex'), blocks[8].hash().toString('hex'), 'should update stale head') + t.equals(blockchain._headBlock.toString('hex'), blocks[8].hash().toString('hex'), 'should update stale headBlock') t.notOk(err, 'should add new block in fork') done() }) }, - function delForkBlock (done) { - blockchain.delBlock(forkBlock.hash(), (err) => { + function delForkHeader (done) { + blockchain.delBlock(forkHeader.hash(), (err) => { t.ok(!err, 'should delete fork block') - t.equals(blockchain._headHeader.toString('hex'), blocks[8].hash().toString('hex'), 'should not change head') + t.equals(blockchain._headHeader.toString('hex'), blocks[8].hash().toString('hex'), 'should reset headHeader') + t.equals(blockchain._headBlock.toString('hex'), blocks[8].hash().toString('hex'), 'should not change headBlock') done() }) }, @@ -347,19 +350,22 @@ test('blockchain test', function (t) { function saveHeads (done) { var db = levelup('', { db: memdown }) var blockchain = new Blockchain({db: db, validate: false}) - - blockchain.putBlock(blocks[1], (err) => { + var header = new Block.Header() + header.number = ethUtil.toBuffer(1) + header.difficulty = '0xfffffff' + header.parentHash = blocks[0].hash() + blockchain.putHeader(header, (err) => { if (err) return done(err) blockchain = new Blockchain({db: db, validate: false}) async.series([ - (cb) => blockchain.getLatestHeader((err, header) => { + (cb) => blockchain.getLatestHeader((err, latest) => { if (err) return done(err) - t.equals(header.hash().toString('hex'), blocks[1].hash().toString('hex'), 'should get latest header') + t.equals(latest.hash().toString('hex'), header.hash().toString('hex'), 'should save headHeader') cb() }), - (cb) => blockchain.getLatestBlock((err, headBlock) => { + (cb) => blockchain.getLatestBlock((err, latest) => { if (err) return done(err) - t.equals(headBlock.hash().toString('hex'), blocks[1].hash().toString('hex'), 'should get latest block') + t.equals(latest.hash().toString('hex'), blocks[0].hash().toString('hex'), 'should save headBlock') cb() }) ], done) @@ -367,15 +373,18 @@ test('blockchain test', function (t) { }, function immutableCachedObjects (done) { var blockchain = new Blockchain({validate: false}) + // clone blocks[1] + var testBlock = new Block(rlp.decode(rlp.encode(blocks[1].raw))) var cachedHash async.series([ - (cb) => blockchain.putBlock(blocks[1], (err) => { + (cb) => blockchain.putBlock(testBlock, (err) => { if (err) return done(err) - cachedHash = blocks[1].hash() + cachedHash = testBlock.hash() cb() }), (cb) => { - blocks[1].header.extraData = Buffer.from([1]) + // change testBlock's extraData in order to modify its hash + testBlock.header.extraData = Buffer.from([1]) blockchain.getBlock(1, (err, block) => { if (err) return done(err) t.equals(cachedHash.toString('hex'), block.hash().toString('hex'), 'should not modify cached objects') @@ -383,6 +392,53 @@ test('blockchain test', function (t) { }) } ], done) + }, + function getLatest (done) { + var blockchain = new Blockchain({validate: false}) + var headers = [new Block.Header(), new Block.Header()] + + headers[0].number = ethUtil.toBuffer(1) + headers[0].difficulty = '0xfffffff' + headers[0].parentHash = blocks[0].hash() + + headers[1].number = ethUtil.toBuffer(2) + headers[1].difficulty = '0xfffffff' + headers[1].parentHash = headers[0].hash() + + async.series([ + // first, add some headers and make sure the latest block remains the same + (cb) => blockchain.putHeaders(headers, (err) => { + if (err) return cb(err) + async.series([ + (cb) => blockchain.getLatestHeader((err, header) => { + if (err) return done(err) + t.equals(header.hash().toString('hex'), headers[1].hash().toString('hex'), 'should update latest header') + cb() + }), + (cb) => blockchain.getLatestBlock((err, block) => { + if (err) return done(err) + t.equals(block.hash().toString('hex'), blocks[0].hash().toString('hex'), 'should not change latest block') + cb() + }) + ], cb) + }), + // then, add a full block and make sure the latest header remains the same + (cb) => blockchain.putBlock(blocks[1], (err) => { + if (err) return cb(err) + async.series([ + (cb) => blockchain.getLatestHeader((err, header) => { + if (err) return done(err) + t.equals(header.hash().toString('hex'), headers[1].hash().toString('hex'), 'should not change latest header') + cb() + }), + (cb) => blockchain.getLatestBlock((err, block) => { + if (err) return done(err) + t.equals(block.hash().toString('hex'), blocks[1].hash().toString('hex'), 'should update latest block') + cb() + }) + ], cb) + }) + ], done) } ], function (err) { if (err) { From 3fc3320ba95b6cdb939d62557848437f217f25de Mon Sep 17 00:00:00 2001 From: Vinay Pulim Date: Sat, 28 Jul 2018 11:13:32 -0400 Subject: [PATCH 3/5] Added support for setting network and hardfork-specific validation --- index.js | 28 ++++++++++++++++++++++------ package.json | 19 ++++++++++--------- test/index.js | 51 ++++++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 82 insertions(+), 16 deletions(-) diff --git a/index.js b/index.js index a6e3210..7e1c596 100644 --- a/index.js +++ b/index.js @@ -6,6 +6,7 @@ const semaphore = require('semaphore') const levelup = require('levelup') const memdown = require('memdown') const Block = require('ethereumjs-block') +const Common = require('ethereumjs-common') const ethUtil = require('ethereumjs-util') const Ethash = require('ethashjs') const Buffer = require('safe-buffer').Buffer @@ -36,6 +37,17 @@ function Blockchain (opts) { opts = opts || {} const self = this + if (opts.common) { + if (opts.chain) { + throw new Error('Instantiation with both opts.common and opts.chain parameter not allowed!') + } + this._common = opts.common + } else { + let chain = opts.chain ? opts.chain : 'mainnet' + let hardfork = opts.hardfork ? opts.hardfork : null + this._common = new Common(chain, hardfork) + } + // backwards compatibilty with older constructor interfaces if (opts.constructor.name === 'LevelUP') { opts = { db: opts } @@ -144,7 +156,7 @@ Blockchain.prototype._init = function (cb) { */ Blockchain.prototype._setCanonicalGenesisBlock = function (cb) { const self = this - var genesisBlock = new Block() + var genesisBlock = new Block(null, {common: self._common}) genesisBlock.setGenesisParams() self._putBlockOrHeader(genesisBlock, cb, true) } @@ -311,7 +323,7 @@ Blockchain.prototype.putHeader = function (header, cb) { Blockchain.prototype._putBlockOrHeader = function (item, cb, isGenesis) { const self = this var isHeader = item instanceof Block.Header - var block = isHeader ? new Block([item.raw, [], []]) : item + var block = isHeader ? new Block([item.raw, [], []], {common: item._common}) : item var header = block.header var hash = block.hash() var number = new BN(header.number) @@ -320,7 +332,11 @@ Blockchain.prototype._putBlockOrHeader = function (item, cb, isGenesis) { var dbOps = [] if (block.constructor !== Block) { - block = new Block(block) + block = new Block(block, {common: self._common}) + } + + if (block._common.chainId() !== self._common.chainId()) { + return cb(new Error('Chain mismatch while trying to put block or header')) } async.series([ @@ -514,7 +530,7 @@ Blockchain.prototype._getBlock = function (blockTag, cb) { } }, (err, parts) => { if (err) return cb(err) - cb(null, new Block([parts.header].concat(parts.body))) + cb(null, new Block([parts.header].concat(parts.body), {common: self._common})) }) } } @@ -1025,7 +1041,7 @@ Blockchain.prototype._getHeader = function (hash, number, cb) { var key = headerKey(number, hash) var encodedHeader = self._cache.header.get(key) if (encodedHeader) { - return cb(null, new Block.Header(rlp.decode(encodedHeader))) + return cb(null, new Block.Header(rlp.decode(encodedHeader), {common: self._common})) } self.db.get(key, { keyEncoding: 'binary', @@ -1033,7 +1049,7 @@ Blockchain.prototype._getHeader = function (hash, number, cb) { }, (err, encodedHeader) => { if (err) return cb(err) self._cache.header.set(key, encodedHeader) - cb(null, new Block.Header(rlp.decode(encodedHeader))) + cb(null, new Block.Header(rlp.decode(encodedHeader), {common: self._common})) }) }) } diff --git a/package.json b/package.json index f3ff120..785bcfb 100644 --- a/package.json +++ b/package.json @@ -24,21 +24,22 @@ }, "homepage": "https://github.com/ethereumjs/ethereumjs-blockchain#readme", "dependencies": { - "async": "^2.1.2", + "async": "^2.6.1", "ethashjs": "~0.0.7", - "ethereumjs-block": "~1.7.0", + "ethereumjs-block": "~2.0.1", + "ethereumjs-common": "~0.4.0", "ethereumjs-util": "~5.2.0", "flow-stoplight": "^1.0.0", - "levelup": "^1.3.0", - "lru-cache": "^4.1.2", + "levelup": "^1.3.2", + "lru-cache": "^4.1.3", "memdown": "^1.1.0", - "safe-buffer": "^5.1.1", - "semaphore": "^1.0.3" + "safe-buffer": "^5.1.2", + "semaphore": "^1.1.0" }, "devDependencies": { - "coveralls": "^3.0.0", - "nyc": "^11.8.0", + "coveralls": "^3.0.2", + "nyc": "^13.0.1", "standard": "^11.0.1", - "tape": "^4.2.1" + "tape": "^4.9.1" } } diff --git a/test/index.js b/test/index.js index e89ac26..0459002 100644 --- a/test/index.js +++ b/test/index.js @@ -3,6 +3,7 @@ const test = require('tape') const Blockchain = require('..') const Block = require('ethereumjs-block') +const Common = require('ethereumjs-common') const async = require('async') const ethUtil = require('ethereumjs-util') const levelup = require('levelup') @@ -12,7 +13,7 @@ const BN = require('bn.js') const rlp = ethUtil.rlp test('blockchain test', function (t) { - t.plan(66) + t.plan(70) var blockchain = new Blockchain() var genesisBlock var blocks = [] @@ -27,6 +28,22 @@ test('blockchain test', function (t) { done() }) }, + function initialization (done) { + const common = new Common('ropsten') + t.throws(function () { new Blockchain({ chain: 'ropsten', common: common }) }, /not allowed!$/, 'should throw on initialization with chain and common parameter') // eslint-disable-line + + const bc0 = new Blockchain({ chain: 'ropsten' }) + const bc1 = new Blockchain({ common: common }) + async.parallel([ + (cb) => bc0.getHead(cb), + (cb) => bc1.getHead(cb) + ], (err, heads) => { + if (err) return done(err) + t.equals(heads[0].hash().toString('hex'), common.genesis().hash.slice(2), 'correct genesis hash') + t.equals(heads[0].hash().toString('hex'), heads[1].hash().toString('hex'), 'genesis blocks match') + done() + }) + }, function alternateConstructors (done) { var db = levelup('', { db: memdown }) var blockchain = new Blockchain(db) @@ -439,6 +456,38 @@ test('blockchain test', function (t) { ], cb) }) ], done) + }, + function mismatchedChains (done) { + var common = new Common('rinkeby') + var blockchain = new Blockchain({common: common, validate: false}) + var blocks = [ + new Block(null, {common: common}), + new Block(null, {chain: 'rinkeby'}), + new Block(null, {chain: 'ropsten'}) + ] + + blocks[0].setGenesisParams() + + blocks[1].header.number = 1 + blocks[1].header.parentHash = blocks[0].hash() + + blocks[2].header.number = 2 + blocks[2].header.parentHash = blocks[1].hash() + + async.eachOfSeries(blocks, (block, i, cb) => { + if (i === 0) { + blockchain.putGenesis(block, cb) + } else { + blockchain.putBlock(block, (err) => { + if (i === 2) { + t.ok(err.message.match('Chain mismatch'), 'should return chain mismatch error') + } else { + t.error(err, 'should not return mismatch error') + } + cb() + }) + } + }) } ], function (err) { if (err) { From 4db9b218ef378d3f62ce71a331221b01a6e91047 Mon Sep 17 00:00:00 2001 From: Holger Drewes Date: Wed, 8 Aug 2018 23:55:16 +0200 Subject: [PATCH 4/5] Updated number of planned tests --- test/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/index.js b/test/index.js index 2df94a1..3536b6e 100644 --- a/test/index.js +++ b/test/index.js @@ -13,7 +13,7 @@ const BN = require('bn.js') const rlp = ethUtil.rlp test('blockchain test', function (t) { - t.plan(70) + t.plan(73) var blockchain = new Blockchain() var genesisBlock var blocks = [] From a4b16962169aaf7968655ae8034106e5545d812e Mon Sep 17 00:00:00 2001 From: Holger Drewes Date: Thu, 9 Aug 2018 00:06:31 +0200 Subject: [PATCH 5/5] Another update --- test/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/index.js b/test/index.js index 3536b6e..079cf8d 100644 --- a/test/index.js +++ b/test/index.js @@ -13,7 +13,7 @@ const BN = require('bn.js') const rlp = ethUtil.rlp test('blockchain test', function (t) { - t.plan(73) + t.plan(72) var blockchain = new Blockchain() var genesisBlock var blocks = []