From c91155cc35c263c70800522f1dd45ee0efb55a21 Mon Sep 17 00:00:00 2001 From: Chris Kleeschulte Date: Fri, 21 Jul 2017 18:47:50 -0400 Subject: [PATCH] wip --- lib/services/block/index.js | 2 +- lib/services/db/index.js | 57 ++++++------- lib/services/header/index.js | 35 +++----- lib/services/mempool/index.js | 5 +- test/services/block/index.unit.js | 130 ++++++++++++----------------- test/services/header/index.unit.js | 13 +++ 6 files changed, 103 insertions(+), 139 deletions(-) diff --git a/lib/services/block/index.js b/lib/services/block/index.js index b73a1ec5..072721d1 100644 --- a/lib/services/block/index.js +++ b/lib/services/block/index.js @@ -48,7 +48,7 @@ inherits(BlockService, BaseService); BlockService.dependencies = [ 'p2p', 'db', 'header' ]; -BlockService.MAX_BLOCKS = 250; +BlockService.MAX_BLOCKS = 10; // --- public prototype functions BlockService.prototype.getAPIMethods = function() { diff --git a/lib/services/db/index.js b/lib/services/db/index.js index e0ef62e7..9f0c2f4b 100644 --- a/lib/services/db/index.js +++ b/lib/services/db/index.js @@ -12,6 +12,7 @@ var $ = bitcore.util.preconditions; var Service = require('../../service'); var constants = require('../../constants'); var log = require('../../index').log; +var assert = require('assert'); function DB(options) { @@ -70,38 +71,10 @@ DB.prototype._setDataPath = function() { } }; -// _checkVersion only governs db versions from bitcore-node >= 4.0 -DB.prototype._checkVersion = function(callback) { - - var self = this; - - // presupposition is that IF there is a database to open -and- there is a version key - // in the form below, it will be related to us and it must be equal to this service's version. - - var versionBuf = Buffer.concat([ self._dbPrefix, new Buffer('version', 'utf8') ]); - self.get(versionBuf, self.dbOptions, function(err, buffer) { - - if (err) { - return callback(err); - } - - var version; - - if (buffer) { - version = buffer.readUInt32BE(); - } - - if (self.version !== version) { - return callback(new Error('The version of the database "' + version + '" does not match the expected version "')); - } - callback(); - }); -}; - DB.prototype._setVersion = function(callback) { var versionBuffer = new Buffer(new Array(4)); versionBuffer.writeUInt32BE(this.version); - this.put(this._dbPrefix + 'version', versionBuffer, callback); + this.put(Buffer.concat([ this._dbPrefix, new Buffer('version', 'utf8') ]), versionBuffer, callback); }; DB.prototype.start = function(callback) { @@ -113,10 +86,7 @@ DB.prototype.start = function(callback) { self._store = levelup(self.dataPath, { db: self.levelupStore, keyEncoding: 'binary', valueEncoding: 'binary'}); - - setImmediate(function() { - self._checkVersion(self._setVersion.bind(self, callback)); - }); + setImmediate(callback); }; @@ -154,6 +124,15 @@ DB.prototype.get = function(key, options, callback) { }; DB.prototype.put = function(key, value, options) { + + assert(Buffer.isBuffer(key), 'key NOT a buffer as expected.'); + + if (value) { + + assert(Buffer.isBuffer(value), 'value exists but NOT a buffer as expected.'); + + } + var self = this; if (self._stopping) { @@ -174,12 +153,24 @@ DB.prototype.put = function(key, value, options) { }; DB.prototype.batch = function(ops, options) { + var self = this; if (self._stopping) { return; } + for(var i = 0; i < ops.length; i++) { + + assert(Buffer.isBuffer(ops[i].key), 'key NOT a buffer as expected.'); + + if (ops[i].value) { + + assert(Buffer.isBuffer(ops[i].value), 'value exists but NOT a buffer as expected.'); + + } + } + self._operationsCount += ops.length; self._store.batch(ops, options, function(err) { diff --git a/lib/services/header/index.js b/lib/services/header/index.js index bf8e1aa4..6630fb5b 100644 --- a/lib/services/header/index.js +++ b/lib/services/header/index.js @@ -132,39 +132,24 @@ HeaderService.prototype._onHeaders = function(headers, convert) { }); } - var operations = this._getHeaderOperations(newHeaders); + var runningHeight = this._tip.height; + var prevHeader = Array.from(this._headers)[this._headers.length - 1]; + + for(var i = 0; i < headers.length; i++) { + var header = headers[i]; + header.height = ++runningHeight; + header.chainwork = this._getChainwork(header, prevHeader).toString(16, 32); + prevHeader = header; + this._headers.set(header.hash, header); + } this._tip.hash = newHeaders[newHeaders.length - 1].hash; this._tip.height = this._tip.height + newHeaders.length; - this._db.batch(operations); - this._sync(); }; -HeaderService.prototype._getHeaderOperations = function(headers) { - - var self = this; - var runningHeight = this._tip.height; - // get the last header placed in the map - var prevHeader = Array.from(this._headers)[this._headers.length - 1]; - - return headers.map(function(header) { - header.height = ++runningHeight; - header.chainwork = self._getChainwork(header, prevHeader).toString(16, 32); - prevHeader = header; - // set the header in the in-memory map - self._headers.set(header.hash, header); - return { - type: 'put', - key: self._encoding.encodeHeaderKey(header.height, header.hash), - value: self._encoding.encodeHeaderValue(header) - }; - }); - -}; - HeaderService.prototype._setListeners = function() { this._p2p.once('bestHeight', this._onBestHeight.bind(this)); diff --git a/lib/services/mempool/index.js b/lib/services/mempool/index.js index 99a86794..9aa6d180 100644 --- a/lib/services/mempool/index.js +++ b/lib/services/mempool/index.js @@ -54,13 +54,14 @@ MempoolService.prototype._startSubscriptions = function() { MempoolService.prototype._onBlock = function(block) { // remove this block's txs from mempool + var self = this; var ops = block.txs.map(function(tx) { return { type: 'del', - key: tx.txid() + key: self._encoding.encodeMempoolTransactionKey(tx.txid()) }; }); - this._db.batch(ops); + self._db.batch(ops); }; MempoolService.prototype._onTransaction = function(tx) { diff --git a/test/services/block/index.unit.js b/test/services/block/index.unit.js index d08c1a6a..ecec03a1 100644 --- a/test/services/block/index.unit.js +++ b/test/services/block/index.unit.js @@ -6,7 +6,7 @@ var BN = require('bn.js'); var assert = require('chai').assert; var crypto = require('crypto'); var sinon = require('sinon'); -var Block = require('bitcore-lib').Block; +var Block = require('bcoin').block; var Encoding = require('../../../lib/services/block/encoding'); var LRU = require('lru-cache'); var constants = require('../../../lib/constants'); @@ -105,16 +105,18 @@ describe('Block Service', function() { sandbox.restore(); }); - it('should find the common ancestor between the current chain and the new chain', function() { - var block = { hash: 'cc' }; - blockService._tip = { hash: 'bb' } - blockService._blockQueue = new LRU(5); - blockService._blockQueue.set('aa', { prevHash: '00' }); - blockService._blockQueue.set('bb', { prevHash: 'aa' }); - blockService._blockQueue.set('cc', { prevHash: 'aa' }); - blockService._chainTips = [ 'cc', 'bb' ]; - var commonAncestor = blockService._findCommonAncestor(block); - expect(commonAncestor).to.equal('aa'); + it('should find the common ancestor between the current chain and the new chain', function(done) { + + blockService._header = { getAllHeaders: sinon.stub().returns({}) }; + blockService._db = { get: sinon.stub() }; + blockService._chainTips = ['aa', 'bb']; + blockService._tip = { hash: 'aa' }; + sandbox.stub(blockService, '_getOldBlocks').returns([]); + + blockService._findCommonAncestor('cc'); + blockService.on('common ancestor', function() { + done(); + }); }); }); @@ -142,19 +144,19 @@ describe('Block Service', function() { describe('#_getBlockOperations', function() { it('should get block operations when given one block', function() { - var block = new Block(new Buffer('0100000095194b8567fe2e8bbda931afd01a7acd399b9325cb54683e64129bcd00000000660802c98f18fd34fd16d61c63cf447568370124ac5f3be626c2e1c3c9f0052d19a76949ffff001d33f3c25d0101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0704ffff001d014dffffffff0100f2052a01000000434104e70a02f5af48a1989bf630d92523c9d14c45c75f7d1b998e962bff6ff9995fc5bdb44f1793b37495d80324acba7c8f537caaf8432b8d47987313060cc82d8a93ac00000000', 'hex')); + var block = Block.fromRaw('0100000095194b8567fe2e8bbda931afd01a7acd399b9325cb54683e64129bcd00000000660802c98f18fd34fd16d61c63cf447568370124ac5f3be626c2e1c3c9f0052d19a76949ffff001d33f3c25d0101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0704ffff001d014dffffffff0100f2052a01000000434104e70a02f5af48a1989bf630d92523c9d14c45c75f7d1b998e962bff6ff9995fc5bdb44f1793b37495d80324acba7c8f537caaf8432b8d47987313060cc82d8a93ac00000000', 'hex'); var ops = blockService._getBlockOperations(block); - expect(ops[0]).to.deep.equal({ type: 'put', key: blockService._encoding.encodeBlockKey(block.hash), value: blockService._encoding.encodeBlockValue(block) }); + expect(ops[0]).to.deep.equal({ type: 'put', key: blockService._encoding.encodeBlockKey(block.rhash()), value: blockService._encoding.encodeBlockValue(block) }); }); it('should get block operations when given more than one block', function() { - var block = new Block(new Buffer('0100000095194b8567fe2e8bbda931afd01a7acd399b9325cb54683e64129bcd00000000660802c98f18fd34fd16d61c63cf447568370124ac5f3be626c2e1c3c9f0052d19a76949ffff001d33f3c25d0101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0704ffff001d014dffffffff0100f2052a01000000434104e70a02f5af48a1989bf630d92523c9d14c45c75f7d1b998e962bff6ff9995fc5bdb44f1793b37495d80324acba7c8f537caaf8432b8d47987313060cc82d8a93ac00000000', 'hex')); + var block = Block.fromRaw('0100000095194b8567fe2e8bbda931afd01a7acd399b9325cb54683e64129bcd00000000660802c98f18fd34fd16d61c63cf447568370124ac5f3be626c2e1c3c9f0052d19a76949ffff001d33f3c25d0101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0704ffff001d014dffffffff0100f2052a01000000434104e70a02f5af48a1989bf630d92523c9d14c45c75f7d1b998e962bff6ff9995fc5bdb44f1793b37495d80324acba7c8f537caaf8432b8d47987313060cc82d8a93ac00000000', 'hex'); var ops = blockService._getBlockOperations([block, block]); - expect(ops[0]).to.deep.equal({ type: 'put', key: blockService._encoding.encodeBlockKey(block.hash), value: blockService._encoding.encodeBlockValue(block) }); - expect(ops[1]).to.deep.equal({ type: 'put', key: blockService._encoding.encodeBlockKey(block.hash), value: blockService._encoding.encodeBlockValue(block) }); + expect(ops[0]).to.deep.equal({ type: 'put', key: blockService._encoding.encodeBlockKey(block.rhash()), value: blockService._encoding.encodeBlockValue(block) }); + expect(ops[1]).to.deep.equal({ type: 'put', key: blockService._encoding.encodeBlockKey(block.rhash()), value: blockService._encoding.encodeBlockValue(block) }); }); @@ -165,19 +167,6 @@ describe('Block Service', function() { }); }); - describe('#_getChainwork', function() { - - it('should get chainwork', function() { - var expected = new BN(new Buffer('000000000000000000000000000000000000000000677c7b8122f9902c79f4e0', 'hex')); - blockService._meta = [ { chainwork: '000000000000000000000000000000000000000000677bd68118a98f8779ea90', hash: 'aa' } ]; - blockService._blockQueue = LRU(1); - blockService._blockQueue.set('bb', { header: { bits: 0x18018d30 }}); - var actual = blockService._getChainwork('bb'); - assert(actual.eq(expected), 'not equal: actual: ' + actual + ' expected: ' + expected); - }); - - }); - describe('#_getDelta', function() { var sandbox; @@ -191,17 +180,25 @@ describe('Block Service', function() { it('should get all unsent blocks for the active chain', function() { - var block1 = { header: { prevHash: new Buffer('00', 'hex') }}; - var block2 = { header: { prevHash: new Buffer('aa', 'hex') }}; - var block3 = { header: { prevHash: new Buffer('bb', 'hex') }}; - var expected = [ block2, block3 ]; + var toJSON1 = sinon.stub().returns({ prevBlock: 'bb' }); + var toJSON2 = sinon.stub().returns({ prevBlock: 'aa' }); + var toJSON3 = sinon.stub().returns({ prevBlock: '00' }); + var blocks = [ + { toHeaders: sinon.stub().returns({ toJSON: toJSON3 }) }, + { toHeaders: sinon.stub().returns({ toJSON: toJSON2 }) }, + { toHeaders: sinon.stub().returns({ toJSON: toJSON1 }) } + ]; + var get = sandbox.stub(); + get.onCall(0).returns(blocks[2]); + get.onCall(1).returns(blocks[1]); + blockService._blockQueue = { get: get }; + + + var expected = [ blocks[1], blocks[2] ]; blockService._tip = { hash: 'aa' }; - blockService._blockQueue = LRU(3); - blockService._blockQueue.set('aa', block1); - blockService._blockQueue.set('bb', block2); - blockService._blockQueue.set('cc', block3); var actual = blockService._getDelta('cc'); expect(actual).to.deep.equal(expected); + }); }); @@ -211,14 +208,16 @@ describe('Block Service', function() { describe('#_isChainReorganizing', function() { it('should decide that chain is reorging', function() { + var toJSON = sinon.stub().returns({ prevBlock: 'bb' }); + var block = { toHeaders: sinon.stub().returns({ toJSON: toJSON }) }; blockService._tip = { hash: 'aa' }; - var block = { header: { prevHash: new Buffer('00', 'hex') }}; expect(blockService._isChainReorganizing(block)).to.be.true; }); it('should decide that chain is not reorging', function() { + var toJSON = sinon.stub().returns({ prevBlock: 'aa' }); + var block = { toHeaders: sinon.stub().returns({ toJSON: toJSON }) }; blockService._tip = { hash: 'aa' }; - var block = { header: { prevHash: new Buffer('aa', 'hex') }}; expect(blockService._isChainReorganizing(block)).to.be.false; }); @@ -226,67 +225,39 @@ describe('Block Service', function() { describe('#_isOutOfOrder', function() { - beforeEach(function() { - var prevHash = '00000000'; - for(var i = 0; i < 110; i++) { - var newHash = crypto.randomBytes(4); - var mock = { toBuffer: function() { return new Buffer(new Array(10), 'hex') } }; - blockService._blockQueue.set(newHash, mock); - prevHash = newHash; - } - }); - it('should detect an orphaned block', function() { - var block = { hash: 'ee', header: { prevHash: new Buffer('aa', 'hex') }}; + blockService._chainTips = [ 'cc', 'dd' ]; + var toJSON = sinon.stub().returns({ prevBlock: 'aa' }); + var block = { toHeaders: sinon.stub().returns({ toJSON: toJSON }) }; expect(blockService._isOutOfOrder(block)).to.be.true; }); it('should not detect an orphaned block', function() { - var block = { hash: 'new', header: { prevHash: '00' }}; + var toJSON = sinon.stub().returns({ prevBlock: 'cc' }); + var block = { toHeaders: sinon.stub().returns({ toJSON: toJSON }) }; expect(blockService._isOutOfOrder(block)).to.be.true; }); }); - - describe('#_onBestHeight', function() { - var sandbox; - beforeEach(function() { - sandbox = sinon.sandbox.create(); - }); - - after(function() { - sandbox.restore(); - }); - - it('should set best height', function() { - var startSync = sandbox.stub(blockService, '_startSync'); - blockService._onBestHeight(123); - expect(blockService._bestHeight).to.equal(123); - expect(startSync.calledOnce).to.be.true; - }); - - }); - - describe('#_onBlock', function() { - + describe.only('#_onBlock', function() { it('should perform all the steps for onBlock handler (normal)', function() { - var sandbox = sinon.sandbox.create(); var alreadyProcessed = sandbox.stub(blockService, '_blockAlreadyProcessed').returns(false); var cacheBlock = sandbox.stub(blockService, '_cacheBlock'); var blockState = sandbox.stub(blockService, '_determineBlockState').returns('normal'); var updateChainTips = sandbox.stub(blockService, '_updateChainInfo'); var sendAllUnsent = sandbox.stub(blockService, '_sendDelta'); - var saveMetaData = sandbox.stub(blockService, '_saveMetaData'); - blockService._onBlock({ hash: 'aa' }); + var rhash = sinon.stub().returns('aa'); + var block = { rhash: rhash }; + + blockService._onBlock(block); expect(alreadyProcessed.callCount).to.equal(1); expect(cacheBlock.callCount).to.equal(1); expect(blockState.callCount).to.equal(1); expect(updateChainTips.callCount).to.equal(1); expect(sendAllUnsent.callCount).to.equal(1); - expect(saveMetaData.callCount).to.equal(1); sandbox.restore(); @@ -295,7 +266,10 @@ describe('Block Service', function() { it('should perform all the steps for onBlock handler (reorg)', function() { var sandbox = sinon.sandbox.create(); - var block = { hash: 'aa' }; + + var rhash = sinon.stub().returns('aa'); + var block = { rhash: rhash }; + var alreadyProcessed = sandbox.stub(blockService, '_blockAlreadyProcessed').returns(false); var cacheBlock = sandbox.stub(blockService, '_cacheBlock'); var blockState = sandbox.stub(blockService, '_determineBlockState').returns('reorg'); diff --git a/test/services/header/index.unit.js b/test/services/header/index.unit.js index 03d2e158..66750e31 100644 --- a/test/services/header/index.unit.js +++ b/test/services/header/index.unit.js @@ -134,6 +134,19 @@ describe('Header Service', function() { }); }); + describe('#_getChainwork', function() { + + it('should get chainwork', function() { + var expected = new BN(new Buffer('000000000000000000000000000000000000000000677c7b8122f9902c79f4e0', 'hex')); + headerService._meta = [ { chainwork: '000000000000000000000000000000000000000000677bd68118a98f8779ea90', hash: 'aa' } ]; + headerService._blockQueue = LRU(1); + headerService._blockQueue.set('bb', { header: { bits: 0x18018d30 }}); + var actual = headerService._getChainwork('bb'); + assert(actual.eq(expected), 'not equal: actual: ' + actual + ' expected: ' + expected); + }); + + }); + describe('#_computeChainwork', function() { it('should calculate chain work correctly', function() {