wip
This commit is contained in:
parent
f8df875c34
commit
2bd769cce0
@ -66,6 +66,9 @@ Logger.prototype._log = function(color) {
|
||||
args[0] = '[' + date.toISOString() + ']' + ' ' + typeString + ' ' + args[0];
|
||||
}
|
||||
var fn = console.log;
|
||||
if (level === 'error') {
|
||||
fn = console.error;
|
||||
}
|
||||
fn.apply(console, args);
|
||||
};
|
||||
|
||||
|
||||
22
lib/node.js
22
lib/node.js
@ -3,9 +3,8 @@
|
||||
var util = require('util');
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
var async = require('async');
|
||||
var assert = require('assert');
|
||||
var bitcore = require('bitcore-lib');
|
||||
var Networks = bitcore.Networks;
|
||||
var $ = bitcore.util.preconditions;
|
||||
var _ = bitcore.deps._;
|
||||
var index = require('./');
|
||||
var log = index.log;
|
||||
@ -25,7 +24,6 @@ function Node(config) {
|
||||
}
|
||||
|
||||
if (config.services) {
|
||||
$.checkArgument(Array.isArray(config.services));
|
||||
this._unloadedServices = config.services;
|
||||
}
|
||||
}
|
||||
@ -49,15 +47,7 @@ Node.prototype._init = function(config) {
|
||||
};
|
||||
|
||||
Node.prototype._setNetwork = function(config) {
|
||||
if (config.network === 'testnet') {
|
||||
this.network = Networks.get('testnet');
|
||||
} else if (config.network === 'regtest') {
|
||||
Networks.enableRegtest();
|
||||
this.network = Networks.get('regtest');
|
||||
} else {
|
||||
this.network = Networks.defaultNetwork;
|
||||
}
|
||||
$.checkState(this.network, 'Unrecognized network');
|
||||
this.network = config.network;
|
||||
};
|
||||
|
||||
Node.prototype.openBus = function(options) {
|
||||
@ -107,7 +97,7 @@ Node.prototype._getServiceOrder = function(services) {
|
||||
|
||||
var name = names[i];
|
||||
var service = servicesByName[name];
|
||||
$.checkState(service, 'Required dependency "' + name + '" not available.');
|
||||
assert(service, 'Required dependency "' + name + '" not available.');
|
||||
|
||||
addToStack(service.module.dependencies);
|
||||
|
||||
@ -131,9 +121,9 @@ Node.prototype._startService = function(serviceInfo, callback) {
|
||||
|
||||
var config;
|
||||
if (serviceInfo.config) {
|
||||
$.checkState(_.isObject(serviceInfo.config));
|
||||
$.checkState(!serviceInfo.config.node);
|
||||
$.checkState(!serviceInfo.config.name);
|
||||
assert(_.isObject(serviceInfo.config));
|
||||
assert(serviceInfo.config.node);
|
||||
assert(serviceInfo.config.name);
|
||||
config = serviceInfo.config;
|
||||
} else {
|
||||
config = {};
|
||||
|
||||
@ -371,7 +371,7 @@ AddressService.prototype._onReorg = function(commonAncestorHeader, oldBlockList)
|
||||
var address;
|
||||
for(var k = 0; k < tx.inputs.length; k++) {
|
||||
var input = tx.inputs[k];
|
||||
address = utils.getAddressString({ tx: tx, item: input, network: this._network });
|
||||
address = input.address;
|
||||
|
||||
if (!address) {
|
||||
continue;
|
||||
@ -386,7 +386,7 @@ AddressService.prototype._onReorg = function(commonAncestorHeader, oldBlockList)
|
||||
//outputs
|
||||
for(k = 0; k < tx.outputs.length; k++) {
|
||||
var output = tx.outputs[k];
|
||||
address = utils.getAddressString({ tx: tx, item: output, network: this._network });
|
||||
address = output.address;
|
||||
|
||||
if (!address) {
|
||||
continue;
|
||||
@ -431,7 +431,7 @@ AddressService.prototype.onBlock = function(block, callback) {
|
||||
|
||||
AddressService.prototype._processInput = function(tx, input, opts) {
|
||||
|
||||
var address = utils.getAddressString({ item: input });
|
||||
var address = input.address;
|
||||
|
||||
if(!address) {
|
||||
return;
|
||||
@ -464,7 +464,7 @@ AddressService.prototype._processInput = function(tx, input, opts) {
|
||||
|
||||
AddressService.prototype._processOutput = function(tx, output, index, opts) {
|
||||
|
||||
var address = utils.getAddressString({ item: output });
|
||||
var address = output.address;
|
||||
|
||||
if(!address) {
|
||||
return;
|
||||
|
||||
@ -317,6 +317,7 @@ BlockService.prototype._handleReorg = function(hash, allHeaders) {
|
||||
this._reorging = false;
|
||||
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
// get the blocks from our current tip to the given hash, non-inclusive
|
||||
|
||||
@ -6,9 +6,6 @@ var async = require('async');
|
||||
var levelup = require('levelup');
|
||||
var leveldown = require('leveldown');
|
||||
var mkdirp = require('mkdirp');
|
||||
var bitcore = require('bitcore-lib');
|
||||
var Networks = bitcore.Networks;
|
||||
var $ = bitcore.util.preconditions;
|
||||
var Service = require('../../service');
|
||||
var constants = require('../../constants');
|
||||
var log = require('../../index').log;
|
||||
@ -38,10 +35,10 @@ function DB(options) {
|
||||
|
||||
this.subscriptions = {};
|
||||
|
||||
this.GENESIS_HASH = constants.BITCOIN_GENESIS_HASH[this.node.getNetworkName()];
|
||||
this.GENESIS_HASH = constants.BITCOIN_GENESIS_HASH[this.node.network];
|
||||
|
||||
this.node.on('stopping', function() {
|
||||
log.warn('Node is stopping, gently closing the database.');
|
||||
log.warn('Node is stopping, gently closing the database. Please wait, this could take a while.');
|
||||
});
|
||||
}
|
||||
|
||||
@ -52,19 +49,16 @@ DB.dependencies = [];
|
||||
DB.prototype._onError = function(err) {
|
||||
log.error('Db Service: error: ' + err);
|
||||
this.node.stop();
|
||||
process.exit(-1);
|
||||
};
|
||||
|
||||
DB.prototype._setDataPath = function() {
|
||||
$.checkState(this.node.datadir, 'Node is expected to have a "datadir" property');
|
||||
if (this.node.network === Networks.livenet) {
|
||||
assert(fs.existsSync(this.node.datadir), 'Node is expected to have a "datadir" property');
|
||||
if (this.node.network === 'livenet' || this.node.network === 'mainnet') {
|
||||
this.dataPath = this.node.datadir + '/bitcore-node.db';
|
||||
} else if (this.node.network === Networks.testnet) {
|
||||
if (this.node.network.regtestEnabled) {
|
||||
} else if (this.node.network === 'regtest') {
|
||||
this.dataPath = this.node.datadir + '/regtest/bitcore-node.db';
|
||||
} else {
|
||||
this.dataPath = this.node.datadir + '/testnet3/bitcore-node.db';
|
||||
}
|
||||
} else if (this.node.network === 'testnet') {
|
||||
this.dataPath = this.node.datadir + '/testnet/bitcore-node.db';
|
||||
} else {
|
||||
throw new Error('Unknown network: ' + this.network);
|
||||
}
|
||||
@ -77,13 +71,12 @@ DB.prototype._setVersion = function(callback) {
|
||||
};
|
||||
|
||||
DB.prototype.start = function(callback) {
|
||||
var self = this;
|
||||
|
||||
if (!fs.existsSync(self.dataPath)) {
|
||||
if (!fs.existsSync(this.dataPath)) {
|
||||
mkdirp.sync(this.dataPath);
|
||||
}
|
||||
|
||||
self._store = levelup(self.dataPath, { db: self.levelupStore, keyEncoding: 'binary', valueEncoding: 'binary'});
|
||||
this._store = levelup(this.dataPath, { db: this.levelupStore, keyEncoding: 'binary', valueEncoding: 'binary'});
|
||||
|
||||
setImmediate(callback);
|
||||
|
||||
@ -135,7 +128,7 @@ DB.prototype.put = function(key, value, callback) {
|
||||
var self = this;
|
||||
|
||||
if (self._stopping) {
|
||||
return;
|
||||
callback();
|
||||
}
|
||||
|
||||
self._store.put(key, value, callback);
|
||||
@ -195,6 +188,7 @@ DB.prototype.close = function(callback) {
|
||||
if (this._store && this._store.isOpen()) {
|
||||
this._store.close(callback);
|
||||
}
|
||||
setImmediate(callback);
|
||||
};
|
||||
|
||||
DB.prototype.getAPIMethods = function() {
|
||||
|
||||
@ -16,7 +16,7 @@ MempoolService.dependencies = ['db', 'block'];
|
||||
|
||||
MempoolService.prototype.getAPIMethods = function() {
|
||||
var methods = [
|
||||
['getMempoolTransaction', this, this.getTransaction, 1]
|
||||
['getMempoolTransaction', this, this.getMempoolTransaction, 1]
|
||||
];
|
||||
return methods;
|
||||
};
|
||||
|
||||
@ -7,6 +7,7 @@ var utils = require('../../utils');
|
||||
var _ = require('lodash');
|
||||
var LRU = require('lru-cache');
|
||||
var Unit = require('bitcore-lib').Unit;
|
||||
var log = require('../../index').log;
|
||||
|
||||
function TransactionService(options) {
|
||||
BaseService.call(this, options);
|
||||
@ -93,7 +94,6 @@ TransactionService.prototype.sendTransaction = function(tx, callback) {
|
||||
TransactionService.prototype.start = function(callback) {
|
||||
|
||||
var self = this;
|
||||
self._setListeners();
|
||||
|
||||
self._db.getPrefix(self.name, function(err, prefix) {
|
||||
|
||||
@ -161,12 +161,6 @@ TransactionService.prototype.onBlock = function(block, callback) {
|
||||
return self._processTransaction(tx, { block: block });
|
||||
});
|
||||
|
||||
if (operations && operations.length > 0) {
|
||||
|
||||
self._db.batch(operations);
|
||||
|
||||
}
|
||||
|
||||
setImmediate(function() {
|
||||
callback(null, operations);
|
||||
});
|
||||
@ -175,13 +169,14 @@ TransactionService.prototype.onBlock = function(block, callback) {
|
||||
|
||||
TransactionService.prototype._onReorg = function(commonAncestorHeader, oldBlockList) {
|
||||
|
||||
var self = this;
|
||||
// if the common ancestor block height is greater than our own, then nothing to do for the reorg
|
||||
if (this._tip.height <= commonAncestorHeader.height) {
|
||||
if (self._tip.height <= commonAncestorHeader.height) {
|
||||
return;
|
||||
}
|
||||
|
||||
// set the tip to the common ancestor in case something goes wrong with the reorg
|
||||
var tipOps = utils.encodeTip({ hash: commonAncestorHeader.hash, height: commonAncestorHeader.height }, this.name);
|
||||
var tipOps = utils.encodeTip({ hash: commonAncestorHeader.hash, height: commonAncestorHeader.height }, self.name);
|
||||
|
||||
var removalOps = [{
|
||||
type: 'put',
|
||||
@ -195,12 +190,18 @@ TransactionService.prototype._onReorg = function(commonAncestorHeader, oldBlockL
|
||||
var tx = block.transactions[j];
|
||||
removalOps.push({
|
||||
type: 'del',
|
||||
key: this._encoding.encodeTransactionKey(tx.id)
|
||||
key: self._encoding.encodeTransactionKey(tx.id)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
this._db.batch(removalOps);
|
||||
self._db.batch(removalOps, function(err) {
|
||||
if (err) {
|
||||
log.error('Transaction Service: error removing operations during reorg.' + err);
|
||||
self.node.stop();
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
@ -227,14 +228,6 @@ TransactionService.prototype._processTransaction = function(tx, opts) {
|
||||
|
||||
};
|
||||
|
||||
TransactionService.prototype._setListeners = function() {
|
||||
|
||||
var self = this;
|
||||
|
||||
self.on('reorg', self._onReorg.bind(self));
|
||||
|
||||
};
|
||||
|
||||
TransactionService.prototype._startSubscriptions = function() {
|
||||
|
||||
if (this._subscribed) {
|
||||
|
||||
162
lib/utils.js
162
lib/utils.js
@ -1,36 +1,22 @@
|
||||
'use strict';
|
||||
|
||||
var bitcore = require('bitcore-lib');
|
||||
var BufferUtil = bitcore.util.buffer;
|
||||
var MAX_SAFE_INTEGER = 0x1fffffffffffff; // 2 ^ 53 - 1
|
||||
var crypto = require('crypto');
|
||||
var _ = require('lodash');
|
||||
var constants = require('./constants');
|
||||
var BN = require('bn.js');
|
||||
|
||||
var utils = {};
|
||||
utils.isHash = function(value) {
|
||||
return typeof value === 'string' && value.length === 64 && /^[0-9a-fA-F]+$/.test(value);
|
||||
};
|
||||
|
||||
utils.isSafeNatural = function(value) {
|
||||
return typeof value === 'number' &&
|
||||
isFinite(value) &&
|
||||
Math.floor(value) === value &&
|
||||
value >= 0 &&
|
||||
value <= MAX_SAFE_INTEGER;
|
||||
};
|
||||
|
||||
utils.startAtZero = function(obj, key) {
|
||||
if (!obj.hasOwnProperty(key)) {
|
||||
obj[key] = 0;
|
||||
}
|
||||
|
||||
utils.isHeight = function(blockArg) {
|
||||
return _.isNumber(blockArg) || (blockArg.length < 40 && /^[0-9]+$/.test(blockArg));
|
||||
};
|
||||
|
||||
//start
|
||||
utils.isAbsolutePath = require('path').isAbsolute;
|
||||
if (!utils.isAbsolutePath) {
|
||||
utils.isAbsolutePath = require('path-is-absolute');
|
||||
}
|
||||
|
||||
//main
|
||||
utils.parseParamsWithJSON = function(paramsArg) {
|
||||
var params = paramsArg.map(function(paramArg) {
|
||||
var param;
|
||||
@ -45,9 +31,12 @@ utils.parseParamsWithJSON = function(paramsArg) {
|
||||
};
|
||||
|
||||
utils.getTerminalKey = function(startKey) {
|
||||
var endKey = Buffer.from(startKey);
|
||||
endKey.writeUInt8(startKey.readUInt8(startKey.length - 1) + 1, startKey.length - 1);
|
||||
return endKey;
|
||||
if (!startKey || !Buffer.isBuffer(startKey)) {
|
||||
return;
|
||||
}
|
||||
var bn = new BN(startKey);
|
||||
var endBN = bn.iaddn(1);
|
||||
return endBN.toBuffer();
|
||||
};
|
||||
|
||||
utils.diffTime = function(time) {
|
||||
@ -55,27 +44,6 @@ utils.diffTime = function(time) {
|
||||
return (diff[0] * 1E9 + diff[1])/(1E9 * 1.0);
|
||||
};
|
||||
|
||||
utils.reverseBufferToString = function(buf) {
|
||||
if (_.isString(buf)) {
|
||||
buf = new Buffer(buf, 'hex');
|
||||
}
|
||||
return BufferUtil.reverse(buf).toString('hex');
|
||||
};
|
||||
|
||||
utils.getAddressString = function(opts) {
|
||||
|
||||
if (!opts.item) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: this does not handle P2PK correctly, it uses the address and not the
|
||||
// pubkey
|
||||
if (opts.address) {
|
||||
return address.toString();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
utils.sendError = function(err, res) {
|
||||
if (err.statusCode) {
|
||||
res.status(err.statusCode).send(err.message);
|
||||
@ -85,108 +53,6 @@ utils.sendError = function(err, res) {
|
||||
}
|
||||
};
|
||||
|
||||
utils.getWalletId = exports.generateJobId = function() {
|
||||
return crypto.randomBytes(16).toString('hex');
|
||||
};
|
||||
|
||||
utils.getWalletId = exports.generateJobId = function() {
|
||||
return crypto.randomBytes(16).toString('hex');
|
||||
};
|
||||
|
||||
utils.toJSONL = function(obj) {
|
||||
var str = JSON.stringify(obj);
|
||||
str = str.replace(/\n/g, '');
|
||||
return str + '\n';
|
||||
};
|
||||
|
||||
utils.normalizeTimeStamp = function(addressArg) {
|
||||
var addresses = [addressArg];
|
||||
if (Array.isArray(addressArg)) {
|
||||
addresses = addressArg;
|
||||
}
|
||||
return addresses;
|
||||
};
|
||||
|
||||
utils.normalizeTimeStamp = function(value) {
|
||||
if (value > 0xffffffff) {
|
||||
value = Math.round(value/1000);
|
||||
}
|
||||
return value;
|
||||
};
|
||||
|
||||
utils.delimitedStringParse = function(delim, str) {
|
||||
function tryJSONparse(str) {
|
||||
try {
|
||||
return JSON.parse(str);
|
||||
} catch(e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
var ret = [];
|
||||
|
||||
if (delim === null) {
|
||||
return tryJSONparse(str);
|
||||
}
|
||||
|
||||
var list = str.split(delim);
|
||||
for(var i = 0; i < list.length; i++) {
|
||||
ret.push(tryJSONparse(list[i]));
|
||||
}
|
||||
ret = _.compact(ret);
|
||||
return ret.length === 0 ? false : ret;
|
||||
|
||||
};
|
||||
|
||||
utils.toIntIfNumberLike = function(a) {
|
||||
if (!/[^\d]+/.test(a)) {
|
||||
return parseInt(a);
|
||||
}
|
||||
return a;
|
||||
};
|
||||
|
||||
utils.getBlockInfoString = function(tip, best) {
|
||||
|
||||
var diff = best - tip;
|
||||
var astr = diff + ' blocks behind.';
|
||||
|
||||
if (diff === -1) {
|
||||
astr = Math.abs(diff) + ' block ahead. Peer may be syncing or we may need to reorganize our chain after new blocks arrive.';
|
||||
} else if (diff < 1) {
|
||||
astr = Math.abs(diff) + ' blocks ahead. Peer may be syncing or we may need to reorganize our chain after new blocks arrive.';
|
||||
} else if (diff === 1) {
|
||||
astr = diff + ' block behind.';
|
||||
}
|
||||
|
||||
};
|
||||
utils.joinListsOnIndex = function(indexList, list1, list2, direction) {
|
||||
|
||||
var newChains = [];
|
||||
|
||||
indexList.forEach(function(index) {
|
||||
var otherList = list2[index];
|
||||
if (direction && direction === 'reverse') {
|
||||
newChains.push(otherList.concat(list1));
|
||||
} else {
|
||||
newChains.push(list1.concat(otherList));
|
||||
}
|
||||
});
|
||||
|
||||
return newChains;
|
||||
|
||||
|
||||
};
|
||||
|
||||
utils.removeItemsByIndexList = function(indexList, list) {
|
||||
var ret = [];
|
||||
for(var i = 0; i < list.length; i++) {
|
||||
var item = list[i];
|
||||
if (indexList.indexOf(i) === -1) {
|
||||
ret.push(item);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
};
|
||||
|
||||
utils.encodeTip = function(tip, name) {
|
||||
var key = Buffer.concat([ constants.DB_PREFIX,
|
||||
new Buffer('tip-' + name, 'utf8') ]);
|
||||
@ -199,10 +65,6 @@ utils.encodeTip = function(tip, name) {
|
||||
|
||||
};
|
||||
|
||||
utils.isHeight = function(blockArg) {
|
||||
return _.isNumber(blockArg) || (blockArg.length < 40 && /^[0-9]+$/.test(blockArg))
|
||||
};
|
||||
|
||||
utils.SimpleMap = function SimpleMap() {
|
||||
var object = {};
|
||||
var array = [];
|
||||
|
||||
@ -1,56 +1,154 @@
|
||||
'use strict';
|
||||
|
||||
var expect = require('chai').expect;
|
||||
var bitcore = require('bitcore-lib');
|
||||
var DB = require('../../../lib/services/db');
|
||||
var chai = require('chai');
|
||||
var should = chai.should();
|
||||
var assert = chai.assert;
|
||||
var expect = chai.expect;
|
||||
var DBService = require('../../../lib/services/db');
|
||||
var sinon = require('sinon');
|
||||
var Levelup = require('levelup');
|
||||
|
||||
describe('DB', function() {
|
||||
|
||||
describe('Reorg', function() {
|
||||
var dbService;
|
||||
|
||||
before(function() {
|
||||
this.db = new DB({
|
||||
node: {
|
||||
network: bitcore.Networks.testnet,
|
||||
datadir: '/tmp',
|
||||
services: ''
|
||||
}
|
||||
var sandbox;
|
||||
beforeEach(function() {
|
||||
sandbox = sinon.sandbox.create();
|
||||
dbService = new DBService({
|
||||
node: {
|
||||
services: [],
|
||||
datadir: '/tmp',
|
||||
network: 'regtest',
|
||||
on: sinon.stub()
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
describe('#start', function() {
|
||||
it('should start the db service by creating a db dir, ' +
|
||||
' if necessary, and setting the store', function(done) {
|
||||
dbService.start(function() {
|
||||
dbService._store.should.be.instanceOf(Levelup);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('#stop', function() {
|
||||
it('should stop if store not open', function(done) {
|
||||
dbService.stop(function() {
|
||||
var close = sandbox.stub().callsArg(0);
|
||||
dbService._store = { close: close };
|
||||
dbService._stopping.should.be.true;
|
||||
done();
|
||||
});
|
||||
this.db.tip = { hash: 'ff', height: 444 };
|
||||
});
|
||||
|
||||
it('should detect a reorg from a common ancenstor that is in our set', function() {
|
||||
var block1 = { hash: '11', header: { prevHash: new Buffer('ff', 'hex') } };
|
||||
var block2 = { hash: '22', header: { prevHash: new Buffer('11', 'hex') } };
|
||||
var block3 = { hash: '33', header: { prevHash: new Buffer('22', 'hex') } };
|
||||
var block4 = { hash: '44', header: { prevHash: new Buffer('22', 'hex') } };
|
||||
//blocks must be passed in the order that they are received.
|
||||
var blocks = [ block3, block2, block1, block4 ];
|
||||
expect(this.db.detectReorg(blocks)).to.deep.equal(block3);
|
||||
|
||||
it('should stop if store open', function(done) {
|
||||
dbService.stop(function() {
|
||||
var close = sandbox.stub().callsArg(0);
|
||||
dbService._store = { close: close, isOpen: sinon.stub().returns(true) };
|
||||
dbService._stopping.should.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should detect a reorg from a common ancenstor that is not in our set', function() {
|
||||
var block1 = { hash: '11', header: { prevHash: new Buffer('ff', 'hex') } };
|
||||
var block2 = { hash: '22', header: { prevHash: new Buffer('11', 'hex') } };
|
||||
var block3 = { hash: '33', header: { prevHash: new Buffer('22', 'hex') } };
|
||||
var block4 = { hash: '44', header: { prevHash: new Buffer('ee', 'hex') } };
|
||||
var blocks = [ block3, block2, block1, block4 ];
|
||||
expect(this.db.detectReorg(blocks)).to.deep.equal(block4);
|
||||
|
||||
describe('#_onError', function() {
|
||||
it('should stop the db', function() {
|
||||
var stop = sandbox.stub();
|
||||
dbService.node = { stop: stop };
|
||||
dbService._onError(new Error('some error'));
|
||||
stop.should.be.calledOnce;
|
||||
});
|
||||
});
|
||||
|
||||
it('should not detect a reorg', function() {
|
||||
this.db.reorgTipHash = null;
|
||||
var block1 = { hash: '11', header: { prevHash: new Buffer('ff', 'hex') } };
|
||||
var block2 = { hash: '22', header: { prevHash: new Buffer('11', 'hex') } };
|
||||
var block3 = { hash: '33', header: { prevHash: new Buffer('22', 'hex') } };
|
||||
var block4 = { hash: '44', header: { prevHash: new Buffer('33', 'hex') } };
|
||||
var blocks = [ block3, block2, block1, block4 ];
|
||||
var actual = this.db.detectReorg(blocks);
|
||||
expect(actual).to.be.undefined;
|
||||
describe('#_setDataPath', function() {
|
||||
|
||||
it('should set the data path', function() {
|
||||
dbService._setDataPath();
|
||||
dbService.dataPath.should.equal('/tmp/regtest/bitcore-node.db');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('#_setVersion', function() {
|
||||
it('should set the version', function(done) {
|
||||
var put = sandbox.stub(dbService, 'put').callsArgWith(2, null);
|
||||
dbService._setVersion(function(err) {
|
||||
put.should.be.calledOnce;
|
||||
put.args[0][0].toString('hex').should.deep.equal('ffff76657273696f6e');
|
||||
put.args[0][1].toString('hex').should.deep.equal('00000001');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('#get', function() {
|
||||
it('should get a value from the db', function(done) {
|
||||
var get = sandbox.stub().callsArgWith(2, null, 'data');
|
||||
dbService._store = { get: get };
|
||||
dbService.get('key', function(err, value) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
value.should.equal('data');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should not get a value while the node is shutting down', function(done) {
|
||||
dbService._stopping = true;
|
||||
dbService.get('key', function(err, value) {
|
||||
err.message.should.equal('Shutdown sequence underway, not able to complete the query');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('#put', function() {
|
||||
it('should put a value in the db', function(done) {
|
||||
var put = sandbox.stub().callsArgWith(2, null);
|
||||
dbService._store = { put: put };
|
||||
dbService.put(new Buffer('key'), new Buffer('value'), function(err) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
put.should.be.calledOnce;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should not allow an operation while the node is shutting down', function(done) {
|
||||
dbService._stopping = true;
|
||||
dbService.put(new Buffer('key'), new Buffer('value'), function(err) {
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('#batch', function() {
|
||||
});
|
||||
|
||||
describe('#createReadStream', function() {
|
||||
});
|
||||
|
||||
describe('#createKeyStream', function() {
|
||||
});
|
||||
|
||||
describe('#close', function() {
|
||||
});
|
||||
|
||||
describe('#getServiceTip', function() {
|
||||
});
|
||||
|
||||
describe('#getPrefix', function() {
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@ -1,56 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
var should = require('chai').should();
|
||||
var sinon = require('sinon');
|
||||
var bitcore = require('bitcore-lib');
|
||||
var BufferUtil = bitcore.util.buffer;
|
||||
var DB = require('../../../lib/services/db');
|
||||
var Networks = bitcore.Networks;
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
var rimraf = require('rimraf');
|
||||
var mkdirp = require('mkdirp');
|
||||
var blocks = require('../../data/blocks.json');
|
||||
|
||||
|
||||
describe('DB', function() {
|
||||
|
||||
var bitcoind = {
|
||||
on: function(event, callback) {
|
||||
},
|
||||
genesisBuffer: blocks.genesis
|
||||
};
|
||||
|
||||
var node = {
|
||||
network: Networks.testnet,
|
||||
datadir: '/tmp/datadir',
|
||||
services: { bitcoind: bitcoind },
|
||||
on: sinon.stub(),
|
||||
once: sinon.stub()
|
||||
};
|
||||
|
||||
before(function(done) {
|
||||
|
||||
var self = this;
|
||||
|
||||
rimraf(node.datadir, function(err) {
|
||||
if(err) {
|
||||
return done(err);
|
||||
}
|
||||
mkdirp(node.datadir, done);
|
||||
});
|
||||
|
||||
this.db = new DB({node: node});
|
||||
this.emitter = new EventEmitter();
|
||||
|
||||
});
|
||||
|
||||
|
||||
describe('Reorg', function() {
|
||||
|
||||
it('should start db service', function(done) {
|
||||
this.db.start(done);
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
@ -1,135 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
var should = require('chai').should();
|
||||
var sinon = require('sinon');
|
||||
var bitcore = require('bitcore-lib');
|
||||
var BufferUtil = bitcore.util.buffer;
|
||||
var Reorg = require('../../../lib/services/db/reorg');
|
||||
|
||||
describe('Reorg', function() {
|
||||
describe('full-test', function() {
|
||||
before(function() {
|
||||
sinon.stub(BufferUtil, 'reverse', function(input) {
|
||||
return {
|
||||
toString: function() {
|
||||
return input;
|
||||
}
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
after(function() {
|
||||
BufferUtil.reverse.restore();
|
||||
});
|
||||
|
||||
it('should handle a reorg correctly', function(done) {
|
||||
var tipBlocks = [
|
||||
{hash: 'main0', header: {prevHash: null}},
|
||||
{hash: 'main1', header: {prevHash: 'main0'}},
|
||||
{hash: 'fork1', header: {prevHash: 'main1'}},
|
||||
{hash: 'fork2', header: {prevHash: 'fork1'}}
|
||||
];
|
||||
|
||||
var concurrentBlocks = [
|
||||
{hash: 'main0', header: {prevHash: null}},
|
||||
{hash: 'main1', header: {prevHash: 'main0'}},
|
||||
{hash: 'fork1', header: {prevHash: 'main1'}},
|
||||
{hash: 'fork2', header: {prevHash: 'fork1'}},
|
||||
{hash: 'fork3', header: {prevHash: 'fork2'}}
|
||||
];
|
||||
|
||||
var bitcoindBlocks = [
|
||||
{hash: 'main0', header: {prevHash: null}},
|
||||
{hash: 'main1', header: {prevHash: 'main0'}},
|
||||
{hash: 'main2', header: {prevHash: 'main1'}},
|
||||
{hash: 'main3', header: {prevHash: 'main2'}}
|
||||
];
|
||||
|
||||
var allBlocks = tipBlocks.concat(concurrentBlocks, bitcoindBlocks);
|
||||
|
||||
var db = {
|
||||
tip: tipBlocks[3],
|
||||
concurrentTip: concurrentBlocks[4],
|
||||
store: {
|
||||
batch: sinon.stub().callsArg(1)
|
||||
},
|
||||
getConcurrentBlockOperations: sinon.stub().callsArgWith(2, null, []),
|
||||
getSerialBlockOperations: sinon.stub().callsArgWith(2, null, []),
|
||||
getConcurrentTipOperation: sinon.stub().returns(null),
|
||||
getTipOperation: sinon.stub().returns(null)
|
||||
};
|
||||
|
||||
var node = {
|
||||
services: {
|
||||
bitcoind: {
|
||||
getBlock: function(hash, callback) {
|
||||
var block;
|
||||
for(var i = 0; i < allBlocks.length; i++) {
|
||||
if(allBlocks[i].hash === hash) {
|
||||
block = allBlocks[i];
|
||||
}
|
||||
}
|
||||
|
||||
setImmediate(function() {
|
||||
if(!block) {
|
||||
return callback(new Error('Block not found: ' + hash));
|
||||
}
|
||||
|
||||
callback(null, block);
|
||||
});
|
||||
},
|
||||
getBlockHeader: function(hash, callback) {
|
||||
var header;
|
||||
for(var i = 0; i < allBlocks.length; i++) {
|
||||
if(allBlocks[i].hash === hash) {
|
||||
header = allBlocks[i].header;
|
||||
}
|
||||
}
|
||||
|
||||
setImmediate(function() {
|
||||
if(!header) {
|
||||
return callback(new Error('Block header not found: ' + hash));
|
||||
}
|
||||
|
||||
callback(null, header);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var reorg = new Reorg(node, db);
|
||||
|
||||
reorg.handleReorg(bitcoindBlocks[3].hash, function(err) {
|
||||
should.not.exist(err);
|
||||
|
||||
db.tip.hash.should.equal('main3');
|
||||
db.concurrentTip.hash.should.equal('main3');
|
||||
|
||||
db.getConcurrentBlockOperations.callCount.should.equal(5);
|
||||
db.getConcurrentBlockOperations.args[0][0].should.equal(concurrentBlocks[4]);
|
||||
db.getConcurrentBlockOperations.args[0][1].should.equal(false);
|
||||
db.getConcurrentBlockOperations.args[1][0].should.equal(concurrentBlocks[3]);
|
||||
db.getConcurrentBlockOperations.args[1][1].should.equal(false);
|
||||
db.getConcurrentBlockOperations.args[2][0].should.equal(concurrentBlocks[2]);
|
||||
db.getConcurrentBlockOperations.args[2][1].should.equal(false);
|
||||
db.getConcurrentBlockOperations.args[3][0].should.equal(bitcoindBlocks[2]);
|
||||
db.getConcurrentBlockOperations.args[3][1].should.equal(true);
|
||||
db.getConcurrentBlockOperations.args[4][0].should.equal(bitcoindBlocks[3]);
|
||||
db.getConcurrentBlockOperations.args[4][1].should.equal(true);
|
||||
|
||||
db.getSerialBlockOperations.callCount.should.equal(4);
|
||||
db.getSerialBlockOperations.args[0][0].should.deep.equal(tipBlocks[3]);
|
||||
db.getSerialBlockOperations.args[0][1].should.equal(false);
|
||||
db.getSerialBlockOperations.args[1][0].should.deep.equal(tipBlocks[2]);
|
||||
db.getSerialBlockOperations.args[1][1].should.equal(false);
|
||||
db.getSerialBlockOperations.args[2][0].should.deep.equal(bitcoindBlocks[2]);
|
||||
db.getSerialBlockOperations.args[2][1].should.equal(true);
|
||||
db.getSerialBlockOperations.args[3][0].should.deep.equal(bitcoindBlocks[3]);
|
||||
db.getSerialBlockOperations.args[3][1].should.equal(true);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
Loading…
Reference in New Issue
Block a user