From a1fbf2031757fc9aec62fd485298f1969c613ece Mon Sep 17 00:00:00 2001 From: Chris Kleeschulte Date: Fri, 11 Aug 2017 11:49:57 -0400 Subject: [PATCH] Refactored onHeaders. --- bitcore-node.json.connect | 22 --- lib/services/header/index.js | 177 ++++++++++-------- package-lock.json | 32 ++-- .../default-base-config.integration.js | 28 --- 4 files changed, 107 insertions(+), 152 deletions(-) delete mode 100644 bitcore-node.json.connect delete mode 100644 test/scaffold/default-base-config.integration.js diff --git a/bitcore-node.json.connect b/bitcore-node.json.connect deleted file mode 100644 index 925c217e..00000000 --- a/bitcore-node.json.connect +++ /dev/null @@ -1,22 +0,0 @@ -{ - "network": "regtest", - "port": 3001, - "datadir": "/Users/chrisk", - "services": [ - "p2p", - "db", - "transaction", - "timestamp", - "address", - "mempool", - "wallet-api", - "web" - ], - "servicesConfig": { - "p2p": { - "connect": [ - { "rpchost": "127.0.0.1", "rpcport": 58332, "rpcuser": "bitcoin", "rpcpassword": "local321", "zmqpubrawtx": "tcp://127.0.0.1:28332" } - ] - } - } -} diff --git a/lib/services/header/index.js b/lib/services/header/index.js index deb24669..351831c1 100644 --- a/lib/services/header/index.js +++ b/lib/services/header/index.js @@ -11,7 +11,6 @@ var BN = require('bn.js'); var consensus = require('bcoin').consensus; var assert = require('assert'); var constants = require('../../constants'); -var Header = require('bitcore-lib').BlockHeader; var HeaderService = function(options) { @@ -26,6 +25,7 @@ var HeaderService = function(options) { this.subscriptions.block = []; this._checkpoint = options.checkpoint || 2000; this.GENESIS_HASH = constants.BITCOIN_GENESIS_HASH[this.node.network]; + this._initiallySynced = false; }; inherits(HeaderService, BaseService); @@ -97,7 +97,7 @@ HeaderService.prototype.start = function(callback) { log.debug('Header Service: original tip height is: ' + self._tip.height); log.debug('Header Service: original tip hash is: ' + self._tip.hash); - self._originalTip = Object.assign(self._tip, {}); + self._originalTip = { height: self._tip.height, hash: self._tip.hash }; if (self._tip.height === 0) { @@ -134,7 +134,8 @@ HeaderService.prototype.start = function(callback) { } self._setListeners(); - self._startSubscriptions(); + self._bus = self.node.openBus({remoteAddress: 'localhost-header'}); + self._startHeaderSubscription(); callback(); }); @@ -145,23 +146,11 @@ HeaderService.prototype.stop = function(callback) { setImmediate(callback); }; -HeaderService.prototype._startSubscriptions = function() { +HeaderService.prototype._startHeaderSubscription = function() { - if (this._subscribed) { - return; - } - - this._subscribed = true; - - if (!this._bus) { - this._bus = this.node.openBus({remoteAddress: 'localhost-header'}); - } this._bus.on('p2p/headers', this._onHeaders.bind(this)); - this._bus.on('p2p/block', this._onBlock.bind(this)); - this._bus.subscribe('p2p/headers'); - this._bus.subscribe('p2p/block'); }; @@ -184,13 +173,10 @@ HeaderService.prototype._onBlock = function(block) { log.debug('Header Service: new block: ' + hash); - var header = this._headers.get(hash); - if (!header) { - header = block.toHeaders().toJSON(); - header.timestamp = header.ts; - header.prevHash = header.prevBlock; - this._onHeaders([header]); - } + var header = block.toHeaders().toJSON(); + header.timestamp = header.ts; + header.prevHash = header.prevBlock; + this._saveHeaders([this._onHeader(header)]); for (var i = 0; i < this.subscriptions.block.length; i++) { var prevHeader = this._headers.get(header.prevHash); @@ -198,54 +184,59 @@ HeaderService.prototype._onBlock = function(block) { block.height = prevHeader.height + 1; this.subscriptions.block[i].emit('header/block', block, header); } + +}; + +HeaderService.prototype._onHeader = function(header) { + + var prevHeader = this._headers.get(header.prevHash); + assert(prevHeader, 'We must have a previous header in order to calculate this header\'s data, current header is: ' + header.hash); + + header.height = prevHeader.height + 1; + header.chainwork = this._getChainwork(header, prevHeader).toString(16, 64); + + var newHdr = { + hash: header.hash, + prevHash: header.prevHash, + height: header.height, + chainwork: header.chainwork + }; + + this._headers.set(header.hash, newHdr, header.height); + + return { + type: 'put', + key: this._encoding.encodeHeaderKey(header.height, header.hash), + value: this._encoding.encodeHeaderValue(header) + }; + }; HeaderService.prototype._onHeaders = function(headers) { - var self = this; - log.debug('Header Service: Received: ' + headers.length + ' header(s).'); var dbOps = []; - var headerListLength = 0; for(var i = 0; i < headers.length; i++) { var header = headers[i]; - // headers that come from a call to getheaders and not a new block comoing in - if (header instanceof Header) { - header = header.toObject(); - self._tip.height++; - self._tip.hash = header.hash; - headerListLength = headers.length; - } + header = header.toObject(); - var prevHeader = self._headers.get(header.prevHash); - assert(prevHeader, 'We must have a previous header in order to calculate this header\'s data.'); + dbOps.push(this._onHeader(header)); - header.height = prevHeader.height + 1; - header.chainwork = self._getChainwork(header, prevHeader).toString(16, 64); - - var newHdr = { - hash: header.hash, - prevHash: header.prevHash, - height: header.height, - chainwork: header.chainwork - }; - - // note: this could lead to nulls in positions we don't yet have headers for - // that should be ok because eventually all the missing headers will fill in - self._headers.set(header.hash, newHdr, header.height); - - dbOps.push({ - type: 'put', - key: self._encoding.encodeHeaderKey(header.height, header.hash), - value: self._encoding.encodeHeaderValue(header) - }); + this._tip.height = header.height; + this._tip.hash = header.hash; } - var tipOps = utils.encodeTip(self._tip, self.name); + this._saveHeaders(dbOps); + +}; + +HeaderService.prototype._saveHeaders = function(dbOps) { + + var tipOps = utils.encodeTip(this._tip, this.name); dbOps.push({ type: 'put', @@ -253,47 +244,71 @@ HeaderService.prototype._onHeaders = function(headers) { value: tipOps.value }); - self._db.batch(dbOps, function(err) { + this._db.batch(dbOps, this._onHeadersSave.bind(this)); +}; + +HeaderService.prototype._onHeadersSave = function(err) { if(err) { log.error(err); - self.node.stop(); + this.node.stop(); return; } - // once we've received less than 2000 headers, we know we are fully sync as far as headers - // if we reveive 2000 headers and there are no more after that, (the best height is a multiple of 2000), - // then we'll just wait for the next block to come in. This should be pretty rare occurrence + if (!this._syncComplete()) { - if (headerListLength === 2000) { - self._sync(); + this._sync(); + return; + + } + + assert(!this._headers.hasNullItems(), 'Header list is not complete yet peer has sent all available headers.'); + + this._startBlockSubscription(); + + this._setBestHeader(); + + if (this._detectReorg()) { + this._handleReorg(); return; } - // at this point, we should have no empty items in this._headers, everything should be filled in - assert(!self._headers.hasNullItems(), 'Header list is not complete yet peer has sent all available headers.'); - - var bestHeader = self._headers.getLastIndex(); - self._tip.height = bestHeader.height; - self._tip.hash = bestHeader.hash; - - log.debug('Header Service: ' + bestHeader.hash + ' is the best block hash.'); - - // at this point, we can check our header list to see if our starting tip diverged from the tip - // that we have now - if (self._detectReorg()) { - self._handleReorg(); - return; - } + this._populateNextHashes(); log.debug('Header Service: emitting headers to block service.'); - // populate next hash fields on each header - self._populateNextHashes(); + this._populateNextHashes(); - self.emit('headers', self._headers); + this.emit('headers', this._headers); - }); +}; +HeaderService.prototype._startBlockSubscription = function() { + + if (this._subscribedBlock) { + return; + } + + this._subscribedBlock = true; + + this._bus.on('p2p/block', this._onBlock.bind(this)); + this._bus.subscribe('p2p/block'); + +}; + +HeaderService.prototype._syncComplete = function() { + + return this._tip.height >= this._bestHeight; + +}; + + +HeaderService.prototype._setBestHeader = function() { + + var bestHeader = this._headers.getLastIndex(); + this._tip.height = bestHeader.height; + this._tip.hash = bestHeader.hash; + + log.debug('Header Service: ' + bestHeader.hash + ' is the best block hash.'); }; HeaderService.prototype._populateNextHashes = function() { diff --git a/package-lock.json b/package-lock.json index 92e178aa..8b8bb6b7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "bitcore-node", - "version": "4.0.0", + "version": "5.0.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -828,21 +828,11 @@ "dev": true }, "deferred-leveldown": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/deferred-leveldown/-/deferred-leveldown-1.2.1.tgz", - "integrity": "sha1-XSXDMQ9f6QmUb2JA3J+Q3RCace8=", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/deferred-leveldown/-/deferred-leveldown-1.2.2.tgz", + "integrity": "sha512-uukrWD2bguRtXilKt6cAWKyoXrTSMo5m7crUdLfWQmu8kIm88w3QZoUL+6nhpfKVmhHANER6Re3sKoNoZ3IKMA==", "requires": { - "abstract-leveldown": "2.4.1" - }, - "dependencies": { - "abstract-leveldown": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/abstract-leveldown/-/abstract-leveldown-2.4.1.tgz", - "integrity": "sha1-s7/tuITraToSd18MVenwpCDM7mQ=", - "requires": { - "xtend": "4.0.1" - } - } + "abstract-leveldown": "2.6.1" } }, "delayed-stream": { @@ -2148,7 +2138,7 @@ "resolved": "https://registry.npmjs.org/levelup/-/levelup-1.3.9.tgz", "integrity": "sha512-VVGHfKIlmw8w1XqpGOAGwq6sZm2WwWLmlDcULkKWQXEA5EopA8OBNJ2Ck2v6bdk8HeEZSbCSEgzXadyQFm76sQ==", "requires": { - "deferred-leveldown": "1.2.1", + "deferred-leveldown": "1.2.2", "level-codec": "7.0.0", "level-errors": "1.0.4", "level-iterator-stream": "1.3.1", @@ -2161,11 +2151,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=" - }, - "semver": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", - "integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==" } } }, @@ -3120,6 +3105,11 @@ "prebuild-install": "2.2.0" } }, + "semver": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", + "integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==" + }, "send": { "version": "0.15.3", "resolved": "https://registry.npmjs.org/send/-/send-0.15.3.tgz", diff --git a/test/scaffold/default-base-config.integration.js b/test/scaffold/default-base-config.integration.js deleted file mode 100644 index b622c742..00000000 --- a/test/scaffold/default-base-config.integration.js +++ /dev/null @@ -1,28 +0,0 @@ -'use strict'; - -var should = require('chai').should(); -var path = require('path'); -var defaultBaseConfig = require('../../lib/scaffold/default-base-config'); - -describe('#defaultBaseConfig', function() { - it('will return expected configuration', function() { - var cwd = process.cwd(); - var home = process.env.HOME; - var info = defaultBaseConfig(); - info.path.should.equal(cwd); - info.config.network.should.equal('livenet'); - info.config.port.should.equal(3001); - info.config.services.should.deep.equal(['bitcoind', 'web']); - var bitcoind = info.config.servicesConfig.bitcoind; - bitcoind.spawn.datadir.should.equal(home + '/.bitcoin'); - bitcoind.spawn.exec.should.equal(path.resolve(__dirname, '../../bin/bitcoind')); - }); - it('be able to specify a network', function() { - var info = defaultBaseConfig({network: 'testnet'}); - info.config.network.should.equal('testnet'); - }); - it('be able to specify a datadir', function() { - var info = defaultBaseConfig({datadir: './data2', network: 'testnet'}); - info.config.servicesConfig.bitcoind.spawn.datadir.should.equal('./data2'); - }); -});