Added fixes for reorg and added time since last block.
This commit is contained in:
parent
f5ad8b89fb
commit
ee97cb5b12
135
:
135
:
@ -1,135 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
var BaseService = require('../../service');
|
|
||||||
var util = require('util');
|
|
||||||
var Encoding = require('./encoding');
|
|
||||||
var log = require('../..').log;
|
|
||||||
|
|
||||||
var MempoolService = function(options) {
|
|
||||||
BaseService.call(this, options);
|
|
||||||
this._subscriptions = {};
|
|
||||||
this._subscriptions.transaction = [];
|
|
||||||
this._db = this.node.services.db;
|
|
||||||
this._p2p = this.node.services.p2p;
|
|
||||||
};
|
|
||||||
|
|
||||||
util.inherits(MempoolService, BaseService);
|
|
||||||
|
|
||||||
MempoolService.dependencies = ['db'];
|
|
||||||
|
|
||||||
MempoolService.prototype.getAPIMethods = function() {
|
|
||||||
var methods = [
|
|
||||||
['getMempoolTransaction', this, this.getMempoolTransaction, 1]
|
|
||||||
];
|
|
||||||
return methods;
|
|
||||||
};
|
|
||||||
|
|
||||||
MempoolService.prototype.start = function(callback) {
|
|
||||||
var self = this;
|
|
||||||
|
|
||||||
self._db.getPrefix(self.name, function(err, prefix) {
|
|
||||||
if(err) {
|
|
||||||
return callback(err);
|
|
||||||
}
|
|
||||||
self._encoding = new Encoding(prefix);
|
|
||||||
self._startSubscriptions();
|
|
||||||
|
|
||||||
callback();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
MempoolService.prototype.onReorg = function(args, callback) {
|
|
||||||
|
|
||||||
var oldBlockList = args[1];
|
|
||||||
|
|
||||||
var removalOps = [];
|
|
||||||
|
|
||||||
for(var i = 0; i < oldBlockList.length; i++) {
|
|
||||||
|
|
||||||
var block = oldBlockList[i];
|
|
||||||
|
|
||||||
for(var j = 0; j < block.txs.length; j++) {
|
|
||||||
|
|
||||||
var tx = block.txs[j];
|
|
||||||
var key = this._encoding.encodeMempoolTransactionKey(tx.txid());
|
|
||||||
var value = this._encoding.encodeMempoolTransactionValue(tx);
|
|
||||||
|
|
||||||
removalOps.push({
|
|
||||||
type: 'put',
|
|
||||||
key: key,
|
|
||||||
value: value
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setImmediate(function() {
|
|
||||||
callback(null, removalOps);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
MempoolService.prototype._startSubscriptions = function() {
|
|
||||||
|
|
||||||
var self = this;
|
|
||||||
if (self._subscribed) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
self._subscribed = true;
|
|
||||||
if (!self._bus) {
|
|
||||||
self._bus = self.node.openBus({remoteAddress: 'localhost-mempool'});
|
|
||||||
}
|
|
||||||
|
|
||||||
self._bus.on('p2p/transaction', self._onTransaction.bind(self));
|
|
||||||
self._bus.subscribe('p2p/transaction');
|
|
||||||
|
|
||||||
self._p2p.on('bestHeight', function() {
|
|
||||||
log.info('Mempool Service: Geting mempool from peer.');
|
|
||||||
self._p2p.getMempool();
|
|
||||||
});
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
MempoolService.prototype.onBlock = function(block, callback) {
|
|
||||||
|
|
||||||
// remove this block's txs from mempool
|
|
||||||
var self = this;
|
|
||||||
var ops = block.txs.map(function(tx) {
|
|
||||||
return {
|
|
||||||
type: 'del',
|
|
||||||
key: self._encoding.encodeMempoolTransactionKey(tx.txid())
|
|
||||||
};
|
|
||||||
});
|
|
||||||
callback(null, ops);
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
MempoolService.prototype._onTransaction = function(tx) {
|
|
||||||
this._db.put(this._encoding.encodeMempoolTransactionKey(tx.txid()),
|
|
||||||
this._encoding.encodeMempoolTransactionValue(tx));
|
|
||||||
};
|
|
||||||
|
|
||||||
MempoolService.prototype.getMempoolTransaction = function(txid, callback) {
|
|
||||||
|
|
||||||
var self = this;
|
|
||||||
|
|
||||||
self._db.get(self._encoding.encodeMempoolTransactionKey(txid), function(err, tx) {
|
|
||||||
|
|
||||||
if (err) {
|
|
||||||
return callback(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!tx) {
|
|
||||||
return callback();
|
|
||||||
}
|
|
||||||
|
|
||||||
callback(null, self._encoding.decodeMempoolTransactionValue(tx));
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
MempoolService.prototype.stop = function(callback) {
|
|
||||||
callback();
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = MempoolService;
|
|
||||||
@ -188,6 +188,7 @@ BlockService.prototype._checkTip = function(callback) {
|
|||||||
return callback();
|
return callback();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.warn('Block Service: reorganization spotted...');
|
||||||
self._findCommonAncestor(function(err, commonAncestorHash) {
|
self._findCommonAncestor(function(err, commonAncestorHash) {
|
||||||
if(err) {
|
if(err) {
|
||||||
return callback(err);
|
return callback(err);
|
||||||
@ -366,11 +367,9 @@ BlockService.prototype._queueBlock = function(block) {
|
|||||||
return self._handleError(err);
|
return self._handleError(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Date.now() - self._timeOfLastBlockReport > 30000) { // 30 seconds
|
log.info('Block Service: The best block hash is: ' + self._tip.hash +
|
||||||
self._timeOfLastBlockReport = Date.now();
|
' at height: ' + self._tip.height + ' Time since last block: ' + utils.convertMillisecondsToHumanReadable(Date.now() - self._timeOfLastBlockReport));
|
||||||
log.info('Block Service: The best block hash is: ' + self._tip.hash +
|
self._timeOfLastBlockReport = Date.now();
|
||||||
' at height: ' + self._tip.height);
|
|
||||||
}
|
|
||||||
|
|
||||||
log.debug('Block Service: completed processing block: ' + block.rhash() +
|
log.debug('Block Service: completed processing block: ' + block.rhash() +
|
||||||
' prev hash: ' + bcoin.util.revHex(block.prevBlock) + ' height: ' + self._tip.height);
|
' prev hash: ' + bcoin.util.revHex(block.prevBlock) + ' height: ' + self._tip.height);
|
||||||
|
|||||||
@ -67,7 +67,7 @@ Encoding.prototype.decodeHeaderValue = function(buffer) {
|
|||||||
var bits = buffer.readUInt32BE(104);
|
var bits = buffer.readUInt32BE(104);
|
||||||
var nonce = buffer.readUInt32BE(108);
|
var nonce = buffer.readUInt32BE(108);
|
||||||
var height = buffer.readUInt32BE(112);
|
var height = buffer.readUInt32BE(112);
|
||||||
var chainwork = buffer.slice(116).toString('hex');
|
var chainwork = buffer.slice(116, 116 + 32).toString('hex');
|
||||||
var nextHash = buffer.slice(116 + 32).toString('hex');
|
var nextHash = buffer.slice(116 + 32).toString('hex');
|
||||||
return {
|
return {
|
||||||
hash: hash,
|
hash: hash,
|
||||||
|
|||||||
@ -395,7 +395,10 @@ HeaderService.prototype._getDBOpForLastHeader = function(nextHeader) {
|
|||||||
// then put operation for the updated last header (with the next hash)
|
// then put operation for the updated last header (with the next hash)
|
||||||
this._lastHeader.nextHash = nextHeader.hash;
|
this._lastHeader.nextHash = nextHeader.hash;
|
||||||
var keyHash = this._encoding.encodeHeaderHashKey(this._lastHeader.hash);
|
var keyHash = this._encoding.encodeHeaderHashKey(this._lastHeader.hash);
|
||||||
var keyHeight = this._encoding.encodeHeaderHeightKey(this._lastHeader.hash);
|
|
||||||
|
assert(this._lastHeader.height >= 0, 'Trying to save a header with incorrect height.');
|
||||||
|
|
||||||
|
var keyHeight = this._encoding.encodeHeaderHeightKey(this._lastHeader.height);
|
||||||
var value = this._encoding.encodeHeaderValue(this._lastHeader);
|
var value = this._encoding.encodeHeaderValue(this._lastHeader);
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
@ -596,7 +599,7 @@ HeaderService.prototype._detectReorg = function(block, callback) {
|
|||||||
return callback(null, header);
|
return callback(null, header);
|
||||||
}
|
}
|
||||||
|
|
||||||
log.warn('Block: ' + block.rhash() + 'references: ' + prevHash +
|
log.warn('Block: ' + block.rhash() + ' references: ' + prevHash +
|
||||||
' as its previous block, yet we have not stored this block in our data set, thus ignoring this block.');
|
' as its previous block, yet we have not stored this block in our data set, thus ignoring this block.');
|
||||||
callback(null, false);
|
callback(null, false);
|
||||||
|
|
||||||
|
|||||||
30
lib/utils.js
30
lib/utils.js
@ -103,4 +103,34 @@ utils.SimpleMap = function SimpleMap() {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
utils.convertMillisecondsToHumanReadable = function(ms) {
|
||||||
|
var ret = '';
|
||||||
|
|
||||||
|
if (!ms && ms !== 0) {
|
||||||
|
return 'invalid number of ms.';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ms >= 60000) {
|
||||||
|
var minutes = Math.floor(ms / 60000);
|
||||||
|
var ms = ms % 60000;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ms >= 1000) {
|
||||||
|
var seconds = Math.floor(ms / 1000);
|
||||||
|
var ms = ms % 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (minutes) {
|
||||||
|
ret = minutes + ' minute(s). ';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (seconds) {
|
||||||
|
ret += seconds + ' second(s). ';
|
||||||
|
}
|
||||||
|
|
||||||
|
ret += ms + ' millisecond(s).';
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
module.exports = utils;
|
module.exports = utils;
|
||||||
|
|||||||
@ -22,10 +22,12 @@ describe('Header service encoding', function() {
|
|||||||
bits: 400000,
|
bits: 400000,
|
||||||
nonce: 123456,
|
nonce: 123456,
|
||||||
height: 123,
|
height: 123,
|
||||||
chainwork: '0000000000000000000000000000000000000000000000000000000200020002'
|
chainwork: '0000000000000000000000000000000000000000000000000000000200020002',
|
||||||
|
nextHash: '91b58f19b6eecba94ed0f6e463e8e334ec0bcda7880e2985c82a8f32e4d03ade'
|
||||||
};
|
};
|
||||||
var versionBuf = new Buffer(4);
|
var versionBuf = new Buffer(4);
|
||||||
var prevHashBuf = new Buffer(header.prevHash, 'hex');
|
var prevHashBuf = new Buffer(header.prevHash, 'hex');
|
||||||
|
var nextHashBuf = new Buffer(header.nextHash, 'hex');
|
||||||
var merkleRootBuf = new Buffer(header.merkleRoot, 'hex');
|
var merkleRootBuf = new Buffer(header.merkleRoot, 'hex');
|
||||||
var tsBuf = new Buffer(4);
|
var tsBuf = new Buffer(4);
|
||||||
var bitsBuf = new Buffer(4);
|
var bitsBuf = new Buffer(4);
|
||||||
@ -67,7 +69,8 @@ describe('Header service encoding', function() {
|
|||||||
bitsBuf,
|
bitsBuf,
|
||||||
nonceBuf,
|
nonceBuf,
|
||||||
heightBuf,
|
heightBuf,
|
||||||
chainBuf
|
chainBuf,
|
||||||
|
nextHashBuf
|
||||||
]));
|
]));
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -81,7 +84,8 @@ describe('Header service encoding', function() {
|
|||||||
bitsBuf,
|
bitsBuf,
|
||||||
nonceBuf,
|
nonceBuf,
|
||||||
heightBuf,
|
heightBuf,
|
||||||
chainBuf
|
chainBuf,
|
||||||
|
nextHashBuf
|
||||||
])).should.deep.equal(header);
|
])).should.deep.equal(header);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -134,7 +134,8 @@ describe('Header Service', function() {
|
|||||||
var saveHeaders = sandbox.stub(headerService, '_saveHeaders');
|
var saveHeaders = sandbox.stub(headerService, '_saveHeaders');
|
||||||
headerService._tip = { height: 123, hash: 'aa' };
|
headerService._tip = { height: 123, hash: 'aa' };
|
||||||
|
|
||||||
headerService._lastHeader = { hash: header.prevHash };
|
var lastHeader = Object.assign({ height: 1, chainwork: new Array(65).join('0') }, prevHeader);
|
||||||
|
headerService._lastHeader = lastHeader;
|
||||||
|
|
||||||
headerService._onHeaders(headers);
|
headerService._onHeaders(headers);
|
||||||
|
|
||||||
|
|||||||
@ -122,4 +122,15 @@ describe('Utils', function() {
|
|||||||
map.getLastIndex().should.equal('last value');
|
map.getLastIndex().should.equal('last value');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('#convertMillisecondsToHumanReadable', function() {
|
||||||
|
it('should convert a number of milliseconds to human readable format', function() {
|
||||||
|
var actual1 = utils.convertMillisecondsToHumanReadable(164532);
|
||||||
|
actual1.should.equal('2 minute(s). 44 second(s). 532 millisecond(s).');
|
||||||
|
var actual2 = utils.convertMillisecondsToHumanReadable(1);
|
||||||
|
actual2.should.equal('1 millisecond(s).');
|
||||||
|
var actual3 = utils.convertMillisecondsToHumanReadable(null);
|
||||||
|
actual3.should.equal('invalid number of ms.');
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user