Refactored onHeaders.
This commit is contained in:
parent
c7ee57f224
commit
a1fbf20317
@ -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" }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -11,7 +11,6 @@ var BN = require('bn.js');
|
|||||||
var consensus = require('bcoin').consensus;
|
var consensus = require('bcoin').consensus;
|
||||||
var assert = require('assert');
|
var assert = require('assert');
|
||||||
var constants = require('../../constants');
|
var constants = require('../../constants');
|
||||||
var Header = require('bitcore-lib').BlockHeader;
|
|
||||||
|
|
||||||
var HeaderService = function(options) {
|
var HeaderService = function(options) {
|
||||||
|
|
||||||
@ -26,6 +25,7 @@ var HeaderService = function(options) {
|
|||||||
this.subscriptions.block = [];
|
this.subscriptions.block = [];
|
||||||
this._checkpoint = options.checkpoint || 2000;
|
this._checkpoint = options.checkpoint || 2000;
|
||||||
this.GENESIS_HASH = constants.BITCOIN_GENESIS_HASH[this.node.network];
|
this.GENESIS_HASH = constants.BITCOIN_GENESIS_HASH[this.node.network];
|
||||||
|
this._initiallySynced = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
inherits(HeaderService, BaseService);
|
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 height is: ' + self._tip.height);
|
||||||
log.debug('Header Service: original tip hash is: ' + self._tip.hash);
|
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) {
|
if (self._tip.height === 0) {
|
||||||
|
|
||||||
@ -134,7 +134,8 @@ HeaderService.prototype.start = function(callback) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self._setListeners();
|
self._setListeners();
|
||||||
self._startSubscriptions();
|
self._bus = self.node.openBus({remoteAddress: 'localhost-header'});
|
||||||
|
self._startHeaderSubscription();
|
||||||
callback();
|
callback();
|
||||||
|
|
||||||
});
|
});
|
||||||
@ -145,23 +146,11 @@ HeaderService.prototype.stop = function(callback) {
|
|||||||
setImmediate(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/headers', this._onHeaders.bind(this));
|
||||||
this._bus.on('p2p/block', this._onBlock.bind(this));
|
|
||||||
|
|
||||||
this._bus.subscribe('p2p/headers');
|
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);
|
log.debug('Header Service: new block: ' + hash);
|
||||||
|
|
||||||
var header = this._headers.get(hash);
|
var header = block.toHeaders().toJSON();
|
||||||
if (!header) {
|
header.timestamp = header.ts;
|
||||||
header = block.toHeaders().toJSON();
|
header.prevHash = header.prevBlock;
|
||||||
header.timestamp = header.ts;
|
this._saveHeaders([this._onHeader(header)]);
|
||||||
header.prevHash = header.prevBlock;
|
|
||||||
this._onHeaders([header]);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var i = 0; i < this.subscriptions.block.length; i++) {
|
for (var i = 0; i < this.subscriptions.block.length; i++) {
|
||||||
var prevHeader = this._headers.get(header.prevHash);
|
var prevHeader = this._headers.get(header.prevHash);
|
||||||
@ -198,54 +184,59 @@ HeaderService.prototype._onBlock = function(block) {
|
|||||||
block.height = prevHeader.height + 1;
|
block.height = prevHeader.height + 1;
|
||||||
this.subscriptions.block[i].emit('header/block', block, header);
|
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) {
|
HeaderService.prototype._onHeaders = function(headers) {
|
||||||
|
|
||||||
var self = this;
|
|
||||||
|
|
||||||
log.debug('Header Service: Received: ' + headers.length + ' header(s).');
|
log.debug('Header Service: Received: ' + headers.length + ' header(s).');
|
||||||
|
|
||||||
var dbOps = [];
|
var dbOps = [];
|
||||||
var headerListLength = 0;
|
|
||||||
|
|
||||||
for(var i = 0; i < headers.length; i++) {
|
for(var i = 0; i < headers.length; i++) {
|
||||||
|
|
||||||
var header = headers[i];
|
var header = headers[i];
|
||||||
|
|
||||||
// headers that come from a call to getheaders and not a new block comoing in
|
header = header.toObject();
|
||||||
if (header instanceof Header) {
|
|
||||||
header = header.toObject();
|
|
||||||
self._tip.height++;
|
|
||||||
self._tip.hash = header.hash;
|
|
||||||
headerListLength = headers.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
var prevHeader = self._headers.get(header.prevHash);
|
dbOps.push(this._onHeader(header));
|
||||||
assert(prevHeader, 'We must have a previous header in order to calculate this header\'s data.');
|
|
||||||
|
|
||||||
header.height = prevHeader.height + 1;
|
this._tip.height = header.height;
|
||||||
header.chainwork = self._getChainwork(header, prevHeader).toString(16, 64);
|
this._tip.hash = header.hash;
|
||||||
|
|
||||||
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)
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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({
|
dbOps.push({
|
||||||
type: 'put',
|
type: 'put',
|
||||||
@ -253,47 +244,71 @@ HeaderService.prototype._onHeaders = function(headers) {
|
|||||||
value: tipOps.value
|
value: tipOps.value
|
||||||
});
|
});
|
||||||
|
|
||||||
self._db.batch(dbOps, function(err) {
|
this._db.batch(dbOps, this._onHeadersSave.bind(this));
|
||||||
|
};
|
||||||
|
|
||||||
|
HeaderService.prototype._onHeadersSave = function(err) {
|
||||||
|
|
||||||
if(err) {
|
if(err) {
|
||||||
log.error(err);
|
log.error(err);
|
||||||
self.node.stop();
|
this.node.stop();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// once we've received less than 2000 headers, we know we are fully sync as far as headers
|
if (!this._syncComplete()) {
|
||||||
// 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 (headerListLength === 2000) {
|
this._sync();
|
||||||
self._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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// at this point, we should have no empty items in this._headers, everything should be filled in
|
this._populateNextHashes();
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
log.debug('Header Service: emitting headers to block service.');
|
log.debug('Header Service: emitting headers to block service.');
|
||||||
// populate next hash fields on each header
|
this._populateNextHashes();
|
||||||
self._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() {
|
HeaderService.prototype._populateNextHashes = function() {
|
||||||
|
|||||||
32
package-lock.json
generated
32
package-lock.json
generated
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "bitcore-node",
|
"name": "bitcore-node",
|
||||||
"version": "4.0.0",
|
"version": "5.0.0",
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@ -828,21 +828,11 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"deferred-leveldown": {
|
"deferred-leveldown": {
|
||||||
"version": "1.2.1",
|
"version": "1.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/deferred-leveldown/-/deferred-leveldown-1.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/deferred-leveldown/-/deferred-leveldown-1.2.2.tgz",
|
||||||
"integrity": "sha1-XSXDMQ9f6QmUb2JA3J+Q3RCace8=",
|
"integrity": "sha512-uukrWD2bguRtXilKt6cAWKyoXrTSMo5m7crUdLfWQmu8kIm88w3QZoUL+6nhpfKVmhHANER6Re3sKoNoZ3IKMA==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"abstract-leveldown": "2.4.1"
|
"abstract-leveldown": "2.6.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"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"delayed-stream": {
|
"delayed-stream": {
|
||||||
@ -2148,7 +2138,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/levelup/-/levelup-1.3.9.tgz",
|
"resolved": "https://registry.npmjs.org/levelup/-/levelup-1.3.9.tgz",
|
||||||
"integrity": "sha512-VVGHfKIlmw8w1XqpGOAGwq6sZm2WwWLmlDcULkKWQXEA5EopA8OBNJ2Ck2v6bdk8HeEZSbCSEgzXadyQFm76sQ==",
|
"integrity": "sha512-VVGHfKIlmw8w1XqpGOAGwq6sZm2WwWLmlDcULkKWQXEA5EopA8OBNJ2Ck2v6bdk8HeEZSbCSEgzXadyQFm76sQ==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"deferred-leveldown": "1.2.1",
|
"deferred-leveldown": "1.2.2",
|
||||||
"level-codec": "7.0.0",
|
"level-codec": "7.0.0",
|
||||||
"level-errors": "1.0.4",
|
"level-errors": "1.0.4",
|
||||||
"level-iterator-stream": "1.3.1",
|
"level-iterator-stream": "1.3.1",
|
||||||
@ -2161,11 +2151,6 @@
|
|||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz",
|
||||||
"integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY="
|
"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"
|
"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": {
|
"send": {
|
||||||
"version": "0.15.3",
|
"version": "0.15.3",
|
||||||
"resolved": "https://registry.npmjs.org/send/-/send-0.15.3.tgz",
|
"resolved": "https://registry.npmjs.org/send/-/send-0.15.3.tgz",
|
||||||
|
|||||||
@ -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');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
Loading…
Reference in New Issue
Block a user