wip
This commit is contained in:
parent
4a294f7058
commit
c91155cc35
@ -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() {
|
||||
|
||||
@ -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) {
|
||||
|
||||
|
||||
@ -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));
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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');
|
||||
|
||||
@ -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() {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user