2015-07-16 12:53:44 -07:00
|
|
|
'use strict';
|
|
|
|
|
|
|
|
var should = require('chai').should();
|
|
|
|
var sinon = require('sinon');
|
|
|
|
var EventEmitter = require('events').EventEmitter;
|
|
|
|
var bitcore = require('bitcore');
|
|
|
|
var Networks = bitcore.Networks;
|
2015-08-27 07:10:07 -07:00
|
|
|
var BufferUtil = bitcore.util.buffer;
|
|
|
|
var Block = bitcore.Block;
|
2015-07-16 12:53:44 -07:00
|
|
|
var blockData = require('./data/livenet-345003.json');
|
|
|
|
var proxyquire = require('proxyquire');
|
2015-08-26 12:18:58 -07:00
|
|
|
var index = require('..');
|
2015-07-21 09:17:28 -07:00
|
|
|
var fs = require('fs');
|
2015-07-23 20:02:31 -07:00
|
|
|
var bitcoinConfBuffer = fs.readFileSync(__dirname + '/data/bitcoin.conf');
|
|
|
|
var chainHashes = require('./data/hashes.json');
|
2015-08-27 13:09:27 -07:00
|
|
|
var util = require('util');
|
|
|
|
var BaseModule = require('../lib/module');
|
2015-07-16 12:53:44 -07:00
|
|
|
|
2015-08-27 13:09:27 -07:00
|
|
|
describe('Bitcore Node', function() {
|
2015-08-26 12:18:58 -07:00
|
|
|
|
|
|
|
var Node;
|
|
|
|
var BadNode;
|
|
|
|
|
2015-08-27 07:10:07 -07:00
|
|
|
function hexlebuf(hexString){
|
|
|
|
return BufferUtil.reverse(new Buffer(hexString, 'hex'));
|
|
|
|
}
|
|
|
|
|
|
|
|
function lebufhex(buf) {
|
|
|
|
return BufferUtil.reverse(buf).toString('hex');
|
|
|
|
}
|
|
|
|
|
2015-08-26 12:18:58 -07:00
|
|
|
before(function() {
|
|
|
|
|
|
|
|
BadNode = proxyquire('../lib/node', {
|
|
|
|
fs: {
|
|
|
|
readFileSync: sinon.stub().returns(fs.readFileSync(__dirname + '/data/badbitcoin.conf'))
|
|
|
|
}
|
|
|
|
});
|
|
|
|
BadNode.prototype._loadConfiguration = sinon.spy();
|
|
|
|
BadNode.prototype._initialize = sinon.spy();
|
|
|
|
|
|
|
|
Node = proxyquire('../lib/node', {
|
|
|
|
fs: {
|
|
|
|
readFileSync: sinon.stub().returns(bitcoinConfBuffer)
|
|
|
|
}
|
|
|
|
});
|
|
|
|
Node.prototype._loadConfiguration = sinon.spy();
|
|
|
|
Node.prototype._initialize = sinon.spy();
|
|
|
|
|
|
|
|
});
|
|
|
|
|
2015-08-27 13:09:27 -07:00
|
|
|
describe('@constructor', function() {
|
|
|
|
it('will set properties', function() {
|
|
|
|
function TestModule() {}
|
|
|
|
util.inherits(TestModule, BaseModule);
|
|
|
|
TestModule.prototype.getData = function() {};
|
|
|
|
TestModule.prototype.getAPIMethods = function() {
|
|
|
|
return [
|
|
|
|
['getData', this, this.getData, 1]
|
|
|
|
];
|
|
|
|
};
|
|
|
|
var config = {
|
|
|
|
modules: [
|
|
|
|
{
|
|
|
|
name: 'test1',
|
|
|
|
module: TestModule
|
|
|
|
}
|
|
|
|
],
|
|
|
|
};
|
|
|
|
var TestNode = proxyquire('../lib/node', {});
|
|
|
|
TestNode.prototype._loadConfiguration = sinon.spy();
|
|
|
|
TestNode.prototype._initialize = sinon.spy();
|
|
|
|
var node = new TestNode(config);
|
|
|
|
TestNode.prototype._loadConfiguration.callCount.should.equal(1);
|
|
|
|
TestNode.prototype._initialize.callCount.should.equal(1);
|
|
|
|
node._unloadedModules.length.should.equal(1);
|
|
|
|
node._unloadedModules[0].name.should.equal('test1');
|
|
|
|
node._unloadedModules[0].module.should.equal(TestModule);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2015-07-29 14:22:34 -07:00
|
|
|
describe('#openBus', function() {
|
|
|
|
it('will create a new bus', function() {
|
|
|
|
var node = new Node({});
|
|
|
|
var bus = node.openBus();
|
2015-08-28 13:16:51 -07:00
|
|
|
bus.node.should.equal(node);
|
2015-07-29 14:22:34 -07:00
|
|
|
});
|
|
|
|
});
|
2015-08-27 13:09:27 -07:00
|
|
|
|
|
|
|
describe('#addModule', function() {
|
|
|
|
it('will instantiate an instance and load api methods', function() {
|
|
|
|
var node = new Node({});
|
|
|
|
function TestModule() {}
|
|
|
|
util.inherits(TestModule, BaseModule);
|
|
|
|
TestModule.prototype.getData = function() {};
|
|
|
|
TestModule.prototype.getAPIMethods = function() {
|
|
|
|
return [
|
|
|
|
['getData', this, this.getData, 1]
|
|
|
|
];
|
|
|
|
};
|
|
|
|
var service = {
|
|
|
|
name: 'testmodule',
|
|
|
|
module: TestModule
|
|
|
|
};
|
|
|
|
node.addModule(service);
|
|
|
|
should.exist(node.modules.testmodule);
|
|
|
|
should.exist(node.getData);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2015-08-04 13:34:53 -07:00
|
|
|
describe('#getAllAPIMethods', function() {
|
|
|
|
it('should return db methods and modules methods', function() {
|
|
|
|
var node = new Node({});
|
2015-08-28 08:43:38 -07:00
|
|
|
node.modules = {
|
|
|
|
module1: {
|
2015-08-27 13:09:27 -07:00
|
|
|
getAPIMethods: sinon.stub().returns(['mda1', 'mda2'])
|
|
|
|
},
|
2015-08-28 08:43:38 -07:00
|
|
|
module2: {
|
2015-08-27 13:09:27 -07:00
|
|
|
getAPIMethods: sinon.stub().returns(['mdb1', 'mdb2'])
|
|
|
|
}
|
2015-08-28 08:43:38 -07:00
|
|
|
};
|
2015-08-04 13:34:53 -07:00
|
|
|
var db = {
|
|
|
|
getAPIMethods: sinon.stub().returns(['db1', 'db2']),
|
|
|
|
};
|
|
|
|
node.db = db;
|
|
|
|
|
|
|
|
var methods = node.getAllAPIMethods();
|
|
|
|
methods.should.deep.equal(['db1', 'db2', 'mda1', 'mda2', 'mdb1', 'mdb2']);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
describe('#getAllPublishEvents', function() {
|
|
|
|
it('should return modules publish events', function() {
|
|
|
|
var node = new Node({});
|
2015-08-28 08:43:38 -07:00
|
|
|
node.modules = {
|
|
|
|
module1: {
|
2015-08-27 13:09:27 -07:00
|
|
|
getPublishEvents: sinon.stub().returns(['mda1', 'mda2'])
|
|
|
|
},
|
2015-08-28 08:43:38 -07:00
|
|
|
module2: {
|
2015-08-27 13:09:27 -07:00
|
|
|
getPublishEvents: sinon.stub().returns(['mdb1', 'mdb2'])
|
|
|
|
}
|
2015-08-28 08:43:38 -07:00
|
|
|
};
|
2015-08-04 13:34:53 -07:00
|
|
|
var db = {
|
2015-08-06 13:19:36 -07:00
|
|
|
getPublishEvents: sinon.stub().returns(['db1', 'db2']),
|
2015-08-04 13:34:53 -07:00
|
|
|
};
|
|
|
|
node.db = db;
|
|
|
|
|
|
|
|
var events = node.getAllPublishEvents();
|
2015-08-06 13:19:36 -07:00
|
|
|
events.should.deep.equal(['db1', 'db2', 'mda1', 'mda2', 'mdb1', 'mdb2']);
|
2015-08-04 13:34:53 -07:00
|
|
|
});
|
|
|
|
});
|
2015-07-16 12:53:44 -07:00
|
|
|
describe('#_loadConfiguration', function() {
|
|
|
|
it('should call the necessary methods', function() {
|
2015-08-26 12:18:58 -07:00
|
|
|
var TestNode = proxyquire('../lib/node', {
|
|
|
|
fs: {
|
|
|
|
readFileSync: sinon.stub().returns(bitcoinConfBuffer)
|
|
|
|
}
|
|
|
|
});
|
|
|
|
TestNode.prototype._initialize = sinon.spy();
|
|
|
|
TestNode.prototype._loadBitcoinConf = sinon.spy();
|
|
|
|
TestNode.prototype._loadBitcoind = sinon.spy();
|
|
|
|
TestNode.prototype._loadDB = sinon.spy();
|
|
|
|
TestNode.prototype._loadAPI = sinon.spy();
|
|
|
|
TestNode.prototype._loadConsensus = sinon.spy();
|
|
|
|
var node = new TestNode({});
|
|
|
|
node._loadBitcoind.callCount.should.equal(1);
|
|
|
|
node._loadBitcoinConf.callCount.should.equal(1);
|
|
|
|
node._loadDB.callCount.should.equal(1);
|
|
|
|
node._loadAPI.callCount.should.equal(1);
|
|
|
|
node._loadConsensus.callCount.should.equal(1);
|
2015-07-16 12:53:44 -07:00
|
|
|
});
|
|
|
|
});
|
2015-07-21 09:17:28 -07:00
|
|
|
describe('#_loadBitcoinConf', function() {
|
|
|
|
it('will parse a bitcoin.conf file', function() {
|
|
|
|
var node = new Node({});
|
2015-08-20 15:41:41 -07:00
|
|
|
node._loadBitcoinConf({datadir: process.env.HOME + '/.bitcoin'});
|
2015-07-21 09:17:28 -07:00
|
|
|
should.exist(node.bitcoinConfiguration);
|
|
|
|
node.bitcoinConfiguration.should.deep.equal({
|
|
|
|
server: 1,
|
|
|
|
whitelist: '127.0.0.1',
|
|
|
|
txindex: 1,
|
|
|
|
port: 20000,
|
|
|
|
rpcallowip: '127.0.0.1',
|
|
|
|
rpcuser: 'bitcoin',
|
|
|
|
rpcpassword: 'local321'
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
2015-07-16 12:53:44 -07:00
|
|
|
describe('#_loadBitcoind', function() {
|
|
|
|
it('should initialize', function() {
|
|
|
|
var node = new Node({});
|
2015-07-23 06:32:46 -07:00
|
|
|
node._loadBitcoind({datadir: './test'});
|
2015-07-16 12:53:44 -07:00
|
|
|
should.exist(node.bitcoind);
|
|
|
|
});
|
|
|
|
it('should initialize with testnet', function() {
|
|
|
|
var node = new Node({});
|
2015-07-23 06:32:46 -07:00
|
|
|
node._loadBitcoind({datadir: './test', testnet: true});
|
2015-07-16 12:53:44 -07:00
|
|
|
should.exist(node.bitcoind);
|
|
|
|
});
|
2015-08-10 11:05:05 -07:00
|
|
|
it('should throw an exception if txindex isn\'t enabled in the configuration', function() {
|
|
|
|
var node = new BadNode({});
|
|
|
|
(function() {
|
|
|
|
node._loadBitcoinConf({datadir: './test'});
|
|
|
|
}).should.throw('Txindex option');
|
|
|
|
});
|
2015-07-16 12:53:44 -07:00
|
|
|
});
|
2015-07-23 20:02:31 -07:00
|
|
|
describe('#_syncBitcoindAncestor', function() {
|
|
|
|
it('will find an ancestor 6 deep', function() {
|
|
|
|
var node = new Node({});
|
|
|
|
node.chain = {
|
|
|
|
getHashes: function(tipHash, callback) {
|
|
|
|
callback(null, chainHashes);
|
|
|
|
},
|
|
|
|
tip: {
|
|
|
|
hash: chainHashes[chainHashes.length]
|
|
|
|
}
|
|
|
|
};
|
|
|
|
var expectedAncestor = chainHashes[chainHashes.length - 6];
|
2015-08-27 07:10:07 -07:00
|
|
|
|
2015-07-23 20:02:31 -07:00
|
|
|
var forkedBlocks = {
|
|
|
|
'd7fa6f3d5b2fe35d711e6aca5530d311b8c6e45f588a65c642b8baf4b4441d82': {
|
2015-08-27 07:10:07 -07:00
|
|
|
header: {
|
|
|
|
prevHash: hexlebuf('76d920dbd83beca9fa8b2f346d5c5a81fe4a350f4b355873008229b1e6f8701a')
|
|
|
|
}
|
2015-07-23 20:02:31 -07:00
|
|
|
},
|
|
|
|
'76d920dbd83beca9fa8b2f346d5c5a81fe4a350f4b355873008229b1e6f8701a': {
|
2015-08-27 07:10:07 -07:00
|
|
|
header: {
|
|
|
|
prevHash: hexlebuf('f0a0d76a628525243c8af7606ee364741ccd5881f0191bbe646c8a4b2853e60c')
|
|
|
|
}
|
2015-07-23 20:02:31 -07:00
|
|
|
},
|
|
|
|
'f0a0d76a628525243c8af7606ee364741ccd5881f0191bbe646c8a4b2853e60c': {
|
2015-08-27 07:10:07 -07:00
|
|
|
header: {
|
|
|
|
prevHash: hexlebuf('2f72b809d5ccb750c501abfdfa8c4c4fad46b0b66c088f0568d4870d6f509c31')
|
|
|
|
}
|
2015-07-23 20:02:31 -07:00
|
|
|
},
|
|
|
|
'2f72b809d5ccb750c501abfdfa8c4c4fad46b0b66c088f0568d4870d6f509c31': {
|
2015-08-27 07:10:07 -07:00
|
|
|
header: {
|
|
|
|
prevHash: hexlebuf('adf66e6ae10bc28fc22bc963bf43e6b53ef4429269bdb65038927acfe66c5453')
|
|
|
|
}
|
2015-07-23 20:02:31 -07:00
|
|
|
},
|
|
|
|
'adf66e6ae10bc28fc22bc963bf43e6b53ef4429269bdb65038927acfe66c5453': {
|
2015-08-27 07:10:07 -07:00
|
|
|
header: {
|
|
|
|
prevHash: hexlebuf('3ea12707e92eed024acf97c6680918acc72560ec7112cf70ac213fb8bb4fa618')
|
|
|
|
}
|
2015-07-23 20:02:31 -07:00
|
|
|
},
|
|
|
|
'3ea12707e92eed024acf97c6680918acc72560ec7112cf70ac213fb8bb4fa618': {
|
2015-08-27 07:10:07 -07:00
|
|
|
header: {
|
|
|
|
prevHash: hexlebuf(expectedAncestor)
|
|
|
|
}
|
2015-07-23 20:02:31 -07:00
|
|
|
},
|
|
|
|
};
|
|
|
|
node.bitcoind = {
|
|
|
|
getBlockIndex: function(hash) {
|
2015-08-27 07:10:07 -07:00
|
|
|
var block = forkedBlocks[hash];
|
|
|
|
return {
|
|
|
|
prevHash: BufferUtil.reverse(block.header.prevHash).toString('hex')
|
|
|
|
};
|
2015-07-23 20:02:31 -07:00
|
|
|
}
|
|
|
|
};
|
|
|
|
var block = forkedBlocks['d7fa6f3d5b2fe35d711e6aca5530d311b8c6e45f588a65c642b8baf4b4441d82'];
|
|
|
|
node._syncBitcoindAncestor(block, function(err, ancestorHash) {
|
|
|
|
if (err) {
|
|
|
|
throw err;
|
|
|
|
}
|
|
|
|
ancestorHash.should.equal(expectedAncestor);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
2015-07-24 10:45:31 -07:00
|
|
|
describe('#_syncBitcoindRewind', function() {
|
|
|
|
it('will undo blocks 6 deep', function() {
|
|
|
|
var node = new Node({});
|
|
|
|
var ancestorHash = chainHashes[chainHashes.length - 6];
|
|
|
|
node.chain = {
|
|
|
|
tip: {
|
|
|
|
__height: 10,
|
|
|
|
hash: chainHashes[chainHashes.length],
|
2015-08-27 07:10:07 -07:00
|
|
|
header: {
|
|
|
|
prevHash: hexlebuf(chainHashes[chainHashes.length - 1])
|
|
|
|
}
|
2015-07-24 10:45:31 -07:00
|
|
|
},
|
|
|
|
saveMetadata: sinon.stub(),
|
|
|
|
emit: sinon.stub()
|
|
|
|
};
|
|
|
|
node.getBlock = function(hash, callback) {
|
|
|
|
setImmediate(function() {
|
|
|
|
for(var i = chainHashes.length; i > 0; i--) {
|
2015-08-27 07:10:07 -07:00
|
|
|
var block = {
|
|
|
|
hash: chainHashes[i],
|
|
|
|
header: {
|
|
|
|
prevHash: hexlebuf(chainHashes[i - 1])
|
|
|
|
}
|
|
|
|
};
|
2015-07-24 10:45:31 -07:00
|
|
|
if (chainHashes[i] === hash) {
|
2015-08-27 07:10:07 -07:00
|
|
|
callback(null, block);
|
2015-07-24 10:45:31 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
};
|
|
|
|
node.db = {
|
|
|
|
_onChainRemoveBlock: function(block, callback) {
|
|
|
|
setImmediate(callback);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
node._syncBitcoindAncestor = function(block, callback) {
|
|
|
|
setImmediate(function() {
|
|
|
|
callback(null, ancestorHash);
|
|
|
|
});
|
|
|
|
};
|
|
|
|
var forkedBlock = {};
|
|
|
|
node._syncBitcoindRewind(forkedBlock, function(err) {
|
|
|
|
if (err) {
|
|
|
|
throw err;
|
|
|
|
}
|
|
|
|
node.chain.tip.__height.should.equal(4);
|
|
|
|
node.chain.tip.hash.should.equal(ancestorHash);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
2015-07-16 12:53:44 -07:00
|
|
|
describe('#_syncBitcoind', function() {
|
|
|
|
it('will get and add block up to the tip height', function(done) {
|
|
|
|
var node = new Node({});
|
2015-08-27 07:10:07 -07:00
|
|
|
var blockBuffer = new Buffer(blockData, 'hex');
|
2015-07-23 06:32:46 -07:00
|
|
|
var block = Block.fromBuffer(blockBuffer);
|
2015-07-16 12:53:44 -07:00
|
|
|
node.bitcoind = {
|
2015-08-11 14:38:04 -07:00
|
|
|
getBlock: sinon.stub().callsArgWith(1, null, blockBuffer),
|
2015-08-24 13:33:44 -07:00
|
|
|
isSynced: sinon.stub().returns(true),
|
|
|
|
height: 1
|
2015-07-16 12:53:44 -07:00
|
|
|
};
|
|
|
|
node.chain = {
|
|
|
|
tip: {
|
2015-07-23 06:32:46 -07:00
|
|
|
__height: 0,
|
2015-08-27 07:10:07 -07:00
|
|
|
hash: lebufhex(block.header.prevHash)
|
2015-07-16 12:53:44 -07:00
|
|
|
},
|
2015-08-21 11:56:32 -07:00
|
|
|
getHashes: sinon.stub().callsArgWith(1, null),
|
2015-07-23 06:32:46 -07:00
|
|
|
saveMetadata: sinon.stub(),
|
2015-07-24 20:18:14 -07:00
|
|
|
emit: sinon.stub(),
|
|
|
|
cache: {
|
|
|
|
hashes: {}
|
|
|
|
}
|
2015-07-23 06:32:46 -07:00
|
|
|
};
|
|
|
|
node.db = {
|
|
|
|
_onChainAddBlock: function(block, callback) {
|
2015-07-16 12:53:44 -07:00
|
|
|
node.chain.tip.__height += 1;
|
|
|
|
callback();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
node.on('synced', function() {
|
|
|
|
done();
|
|
|
|
});
|
|
|
|
node._syncBitcoind();
|
|
|
|
});
|
|
|
|
it('will exit and emit error with error from bitcoind.getBlock', function(done) {
|
|
|
|
var node = new Node({});
|
|
|
|
node.bitcoind = {
|
2015-08-24 13:33:44 -07:00
|
|
|
getBlock: sinon.stub().callsArgWith(1, new Error('test error')),
|
|
|
|
height: 1
|
2015-07-16 12:53:44 -07:00
|
|
|
};
|
|
|
|
node.chain = {
|
|
|
|
tip: {
|
|
|
|
__height: 0
|
|
|
|
}
|
|
|
|
};
|
|
|
|
node.on('error', function(err) {
|
|
|
|
err.message.should.equal('test error');
|
|
|
|
done();
|
|
|
|
});
|
|
|
|
node._syncBitcoind();
|
|
|
|
});
|
2015-08-21 13:13:11 -07:00
|
|
|
it('will stop syncing when the node is stopping', function(done) {
|
|
|
|
var node = new Node({});
|
2015-08-27 07:10:07 -07:00
|
|
|
var blockBuffer = new Buffer(blockData, 'hex');
|
2015-08-21 13:13:11 -07:00
|
|
|
var block = Block.fromBuffer(blockBuffer);
|
|
|
|
node.bitcoind = {
|
|
|
|
getBlock: sinon.stub().callsArgWith(1, null, blockBuffer),
|
2015-08-24 13:33:44 -07:00
|
|
|
isSynced: sinon.stub().returns(true),
|
|
|
|
height: 1
|
2015-08-21 13:13:11 -07:00
|
|
|
};
|
|
|
|
node.chain = {
|
|
|
|
tip: {
|
|
|
|
__height: 0,
|
|
|
|
hash: block.prevHash
|
|
|
|
},
|
|
|
|
saveMetadata: sinon.stub(),
|
|
|
|
emit: sinon.stub(),
|
|
|
|
cache: {
|
|
|
|
hashes: {}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
node.db = {
|
|
|
|
_onChainAddBlock: function(block, callback) {
|
|
|
|
node.chain.tip.__height += 1;
|
|
|
|
callback();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
node.stopping = true;
|
|
|
|
|
|
|
|
var synced = false;
|
|
|
|
|
|
|
|
node.on('synced', function() {
|
|
|
|
synced = true;
|
|
|
|
});
|
|
|
|
|
|
|
|
node._syncBitcoind();
|
|
|
|
|
|
|
|
setTimeout(function() {
|
|
|
|
synced.should.equal(false);
|
|
|
|
done();
|
|
|
|
}, 10);
|
|
|
|
});
|
2015-07-16 12:53:44 -07:00
|
|
|
});
|
2015-08-21 13:13:11 -07:00
|
|
|
|
2015-07-16 12:53:44 -07:00
|
|
|
describe('#_loadNetwork', function() {
|
2015-07-21 10:47:25 -07:00
|
|
|
it('should use the testnet network if testnet is specified', function() {
|
2015-07-16 12:53:44 -07:00
|
|
|
var config = {
|
2015-07-21 10:47:25 -07:00
|
|
|
network: 'testnet'
|
2015-07-16 12:53:44 -07:00
|
|
|
};
|
|
|
|
var node = new Node(config);
|
|
|
|
node._loadNetwork(config);
|
2015-07-21 10:47:25 -07:00
|
|
|
node.network.name.should.equal('testnet');
|
2015-07-16 12:53:44 -07:00
|
|
|
});
|
2015-07-21 10:47:25 -07:00
|
|
|
it('should use the regtest network if regtest is specified', function() {
|
2015-07-16 12:53:44 -07:00
|
|
|
var config = {
|
2015-07-21 10:47:25 -07:00
|
|
|
network: 'regtest'
|
2015-07-16 12:53:44 -07:00
|
|
|
};
|
|
|
|
var node = new Node(config);
|
|
|
|
node._loadNetwork(config);
|
2015-07-21 10:47:25 -07:00
|
|
|
node.network.name.should.equal('regtest');
|
2015-07-16 12:53:44 -07:00
|
|
|
});
|
|
|
|
it('should use the livenet network if nothing is specified', function() {
|
|
|
|
var config = {};
|
|
|
|
var node = new Node(config);
|
|
|
|
node._loadNetwork(config);
|
|
|
|
node.network.name.should.equal('livenet');
|
|
|
|
});
|
|
|
|
});
|
|
|
|
describe('#_loadDB', function() {
|
|
|
|
it('should load the db', function() {
|
2015-07-21 08:52:08 -07:00
|
|
|
var DB = function(config) {
|
2015-08-04 12:06:00 -07:00
|
|
|
config.path.should.equal(process.env.HOME + '/.bitcoin/bitcore-node.db');
|
2015-07-21 08:52:08 -07:00
|
|
|
};
|
|
|
|
var config = {
|
|
|
|
DB: DB,
|
2015-08-20 15:41:41 -07:00
|
|
|
datadir: process.env.HOME + '/.bitcoin'
|
2015-07-21 08:52:08 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
var node = new Node(config);
|
|
|
|
node.network = Networks.livenet;
|
|
|
|
node._loadDB(config);
|
|
|
|
node.db.should.be.instanceof(DB);
|
|
|
|
});
|
|
|
|
it('should load the db for testnet', function() {
|
|
|
|
var DB = function(config) {
|
2015-08-04 12:06:00 -07:00
|
|
|
config.path.should.equal(process.env.HOME + '/.bitcoin/testnet3/bitcore-node.db');
|
2015-07-21 08:52:08 -07:00
|
|
|
};
|
2015-07-16 12:53:44 -07:00
|
|
|
var config = {
|
2015-07-21 08:52:08 -07:00
|
|
|
DB: DB,
|
2015-08-20 15:41:41 -07:00
|
|
|
datadir: process.env.HOME + '/.bitcoin'
|
2015-07-16 12:53:44 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
var node = new Node(config);
|
2015-07-21 08:52:08 -07:00
|
|
|
node.network = Networks.testnet;
|
2015-07-16 12:53:44 -07:00
|
|
|
node._loadDB(config);
|
|
|
|
node.db.should.be.instanceof(DB);
|
|
|
|
});
|
2015-07-21 08:52:08 -07:00
|
|
|
it('error with unknown network', function() {
|
|
|
|
var config = {
|
2015-08-20 15:41:41 -07:00
|
|
|
datadir: process.env.HOME + '/.bitcoin'
|
2015-07-21 08:52:08 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
var node = new Node(config);
|
|
|
|
node.network = 'not a network';
|
|
|
|
(function() {
|
|
|
|
node._loadDB(config);
|
|
|
|
}).should.throw('Unknown network');
|
|
|
|
});
|
2015-07-21 10:47:25 -07:00
|
|
|
it('should load the db with regtest', function() {
|
|
|
|
var DB = function(config) {
|
2015-08-04 12:06:00 -07:00
|
|
|
config.path.should.equal(process.env.HOME + '/.bitcoin/regtest/bitcore-node.db');
|
2015-07-21 10:47:25 -07:00
|
|
|
};
|
|
|
|
var config = {
|
|
|
|
DB: DB,
|
2015-08-20 15:41:41 -07:00
|
|
|
datadir: process.env.HOME + '/.bitcoin'
|
2015-07-21 10:47:25 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
var node = new Node(config);
|
|
|
|
// Switch to use regtest
|
|
|
|
Networks.remove(Networks.testnet);
|
|
|
|
Networks.add({
|
|
|
|
name: 'regtest',
|
|
|
|
alias: 'regtest',
|
|
|
|
pubkeyhash: 0x6f,
|
|
|
|
privatekey: 0xef,
|
|
|
|
scripthash: 0xc4,
|
|
|
|
xpubkey: 0x043587cf,
|
|
|
|
xprivkey: 0x04358394,
|
|
|
|
networkMagic: 0xfabfb5da,
|
|
|
|
port: 18444,
|
|
|
|
dnsSeeds: [ ]
|
|
|
|
});
|
|
|
|
var regtest = Networks.get('regtest');
|
|
|
|
node.network = regtest;
|
|
|
|
node._loadDB(config);
|
|
|
|
node.db.should.be.instanceof(DB);
|
|
|
|
Networks.remove(regtest);
|
|
|
|
// Add testnet back
|
|
|
|
Networks.add({
|
|
|
|
name: 'testnet',
|
|
|
|
alias: 'testnet',
|
|
|
|
pubkeyhash: 0x6f,
|
|
|
|
privatekey: 0xef,
|
|
|
|
scripthash: 0xc4,
|
|
|
|
xpubkey: 0x043587cf,
|
|
|
|
xprivkey: 0x04358394,
|
|
|
|
networkMagic: 0x0b110907,
|
|
|
|
port: 18333,
|
|
|
|
dnsSeeds: [
|
|
|
|
'testnet-seed.bitcoin.petertodd.org',
|
|
|
|
'testnet-seed.bluematt.me',
|
|
|
|
'testnet-seed.alexykot.me',
|
|
|
|
'testnet-seed.bitcoin.schildbach.de'
|
|
|
|
]
|
|
|
|
});
|
|
|
|
});
|
2015-07-16 12:53:44 -07:00
|
|
|
});
|
|
|
|
describe('#_loadConsensus', function() {
|
2015-08-26 12:18:58 -07:00
|
|
|
|
|
|
|
var node;
|
|
|
|
|
|
|
|
before(function() {
|
|
|
|
node = new Node({});
|
|
|
|
});
|
2015-07-16 12:53:44 -07:00
|
|
|
|
2015-08-24 13:33:44 -07:00
|
|
|
it('will set properties', function() {
|
|
|
|
node._loadConsensus();
|
2015-07-16 12:53:44 -07:00
|
|
|
should.exist(node.chain);
|
|
|
|
});
|
2015-08-24 13:33:44 -07:00
|
|
|
|
2015-07-16 12:53:44 -07:00
|
|
|
});
|
|
|
|
|
2015-08-21 13:13:11 -07:00
|
|
|
describe('#_initialize', function() {
|
2015-07-16 12:53:44 -07:00
|
|
|
|
2015-08-24 11:46:48 -07:00
|
|
|
var node;
|
2015-08-21 11:56:32 -07:00
|
|
|
|
2015-08-24 11:46:48 -07:00
|
|
|
before(function() {
|
2015-08-26 12:18:58 -07:00
|
|
|
var TestNode = proxyquire('../lib/node', {
|
|
|
|
fs: {
|
|
|
|
readFileSync: sinon.stub().returns(bitcoinConfBuffer)
|
|
|
|
}
|
|
|
|
});
|
|
|
|
TestNode.prototype._loadConfiguration = sinon.spy();
|
|
|
|
TestNode.prototype._initializeBitcoind = sinon.spy();
|
|
|
|
TestNode.prototype._initializeDatabase = sinon.spy();
|
|
|
|
TestNode.prototype._initializeChain = sinon.spy();
|
|
|
|
|
|
|
|
// mock the _initialize during construction
|
|
|
|
var _initialize = TestNode.prototype._initialize;
|
|
|
|
TestNode.prototype._initialize = sinon.spy();
|
|
|
|
|
|
|
|
node = new TestNode({});
|
2015-08-24 11:46:48 -07:00
|
|
|
node.chain = {
|
|
|
|
on: sinon.spy()
|
|
|
|
};
|
|
|
|
node.Block = 'Block';
|
|
|
|
node.bitcoind = {
|
|
|
|
on: sinon.spy()
|
|
|
|
};
|
|
|
|
node.db = {
|
|
|
|
on: sinon.spy()
|
|
|
|
};
|
2015-08-26 12:18:58 -07:00
|
|
|
|
|
|
|
// restore the original method
|
|
|
|
node._initialize = _initialize;
|
2015-08-24 11:46:48 -07:00
|
|
|
});
|
2015-08-21 11:56:32 -07:00
|
|
|
|
2015-08-24 11:46:48 -07:00
|
|
|
it('should initialize', function(done) {
|
|
|
|
node.once('ready', function() {
|
|
|
|
done();
|
|
|
|
});
|
|
|
|
|
|
|
|
node.start = sinon.stub().callsArg(0);
|
|
|
|
|
|
|
|
node._initialize();
|
|
|
|
|
|
|
|
// event handlers
|
|
|
|
node._initializeBitcoind.callCount.should.equal(1);
|
|
|
|
node._initializeDatabase.callCount.should.equal(1);
|
|
|
|
node._initializeChain.callCount.should.equal(1);
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should emit an error if an error occurred starting services', function(done) {
|
|
|
|
node.once('error', function(err) {
|
|
|
|
should.exist(err);
|
|
|
|
err.message.should.equal('error');
|
|
|
|
done();
|
|
|
|
});
|
|
|
|
node.start = sinon.stub().callsArgWith(0, new Error('error'));
|
|
|
|
node._initialize();
|
|
|
|
});
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('#_initalizeBitcoind', function() {
|
|
|
|
|
2015-08-21 11:56:32 -07:00
|
|
|
it('will call emit an error from libbitcoind', function(done) {
|
|
|
|
var node = new Node({});
|
|
|
|
node.bitcoind = new EventEmitter();
|
|
|
|
node.on('error', function(err) {
|
|
|
|
should.exist(err);
|
|
|
|
err.message.should.equal('test error');
|
|
|
|
done();
|
|
|
|
});
|
|
|
|
node._initializeBitcoind();
|
|
|
|
node.bitcoind.emit('error', new Error('test error'));
|
|
|
|
});
|
2015-08-24 11:46:48 -07:00
|
|
|
it('will call sync when there is a new tip', function(done) {
|
|
|
|
var node = new Node({});
|
|
|
|
node.bitcoind = new EventEmitter();
|
|
|
|
node.bitcoind.syncPercentage = sinon.spy();
|
|
|
|
node._syncBitcoind = function() {
|
|
|
|
node.bitcoind.syncPercentage.callCount.should.equal(1);
|
|
|
|
done();
|
|
|
|
};
|
|
|
|
node._initializeBitcoind();
|
|
|
|
node.bitcoind.emit('tip', 10);
|
|
|
|
});
|
|
|
|
it('will not call sync when there is a new tip and shutting down', function(done) {
|
|
|
|
var node = new Node({});
|
|
|
|
node.bitcoind = new EventEmitter();
|
|
|
|
node._syncBitcoind = sinon.spy();
|
|
|
|
node.bitcoind.syncPercentage = sinon.spy();
|
|
|
|
node.stopping = true;
|
|
|
|
node.bitcoind.on('tip', function() {
|
|
|
|
setImmediate(function() {
|
|
|
|
node.bitcoind.syncPercentage.callCount.should.equal(0);
|
|
|
|
node._syncBitcoind.callCount.should.equal(0);
|
|
|
|
done();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
node._initializeBitcoind();
|
|
|
|
node.bitcoind.emit('tip', 10);
|
|
|
|
});
|
|
|
|
});
|
2015-08-21 11:56:32 -07:00
|
|
|
|
2015-08-24 11:46:48 -07:00
|
|
|
describe('#_initializeDatabase', function() {
|
|
|
|
it('will log on ready event', function(done) {
|
2015-08-21 11:56:32 -07:00
|
|
|
var node = new Node({});
|
|
|
|
node.db = new EventEmitter();
|
2015-08-26 12:18:58 -07:00
|
|
|
sinon.stub(index.log, 'info');
|
2015-08-21 11:56:32 -07:00
|
|
|
node.db.on('ready', function() {
|
|
|
|
setImmediate(function() {
|
2015-08-26 12:18:58 -07:00
|
|
|
index.log.info.callCount.should.equal(1);
|
|
|
|
index.log.info.restore();
|
2015-08-21 11:56:32 -07:00
|
|
|
done();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
node._initializeDatabase();
|
|
|
|
node.db.emit('ready');
|
|
|
|
});
|
|
|
|
it('will call emit an error from db', function(done) {
|
|
|
|
var node = new Node({});
|
|
|
|
node.db = new EventEmitter();
|
|
|
|
node.on('error', function(err) {
|
|
|
|
should.exist(err);
|
|
|
|
err.message.should.equal('test error');
|
|
|
|
done();
|
|
|
|
});
|
|
|
|
node._initializeDatabase();
|
|
|
|
node.db.emit('error', new Error('test error'));
|
|
|
|
});
|
2015-08-24 11:46:48 -07:00
|
|
|
});
|
2015-08-21 11:56:32 -07:00
|
|
|
|
2015-08-24 11:46:48 -07:00
|
|
|
describe('#_initializeChain', function() {
|
|
|
|
it('will call _syncBitcoind on ready', function(done) {
|
|
|
|
var node = new Node({});
|
|
|
|
node._syncBitcoind = sinon.spy();
|
|
|
|
node.chain = new EventEmitter();
|
|
|
|
node.chain.on('ready', function(err) {
|
|
|
|
setImmediate(function() {
|
|
|
|
node._syncBitcoind.callCount.should.equal(1);
|
|
|
|
done();
|
|
|
|
});
|
2015-07-16 12:53:44 -07:00
|
|
|
});
|
2015-08-24 11:46:48 -07:00
|
|
|
node._initializeChain();
|
|
|
|
node.chain.emit('ready');
|
2015-08-21 13:13:11 -07:00
|
|
|
});
|
2015-08-24 11:46:48 -07:00
|
|
|
it('will emit an error from the chain', function(done) {
|
|
|
|
var node = new Node({});
|
|
|
|
node.chain = new EventEmitter();
|
|
|
|
node.on('error', function(err) {
|
2015-08-21 13:13:11 -07:00
|
|
|
should.exist(err);
|
2015-08-24 11:46:48 -07:00
|
|
|
err.message.should.equal('test error');
|
2015-07-16 12:53:44 -07:00
|
|
|
done();
|
|
|
|
});
|
2015-08-24 11:46:48 -07:00
|
|
|
node._initializeChain();
|
|
|
|
node.chain.emit('error', new Error('test error'));
|
2015-07-16 12:53:44 -07:00
|
|
|
});
|
|
|
|
});
|
2015-08-20 14:50:14 -07:00
|
|
|
|
|
|
|
describe('#getServiceOrder', function() {
|
|
|
|
it('should return the services in the correct order', function() {
|
|
|
|
var node = new Node({});
|
2015-08-24 13:33:44 -07:00
|
|
|
node.getServices = function() {
|
2015-08-27 13:09:27 -07:00
|
|
|
return [
|
|
|
|
{
|
|
|
|
name: 'chain',
|
|
|
|
dependencies: ['db']
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'db',
|
|
|
|
dependencies: ['daemon', 'p2p']
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name:'daemon',
|
|
|
|
dependencies: []
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'p2p',
|
|
|
|
dependencies: []
|
|
|
|
}
|
|
|
|
];
|
2015-08-24 13:33:44 -07:00
|
|
|
};
|
|
|
|
var order = node.getServiceOrder();
|
2015-08-27 13:09:27 -07:00
|
|
|
order[0].name.should.equal('daemon');
|
|
|
|
order[1].name.should.equal('p2p');
|
|
|
|
order[2].name.should.equal('db');
|
|
|
|
order[3].name.should.equal('chain');
|
2015-08-20 14:50:14 -07:00
|
|
|
});
|
|
|
|
});
|
2015-08-27 13:09:27 -07:00
|
|
|
|
|
|
|
describe('#start', function() {
|
|
|
|
it('will call start for each module', function(done) {
|
|
|
|
var node = new Node({});
|
|
|
|
function TestModule() {}
|
|
|
|
util.inherits(TestModule, BaseModule);
|
|
|
|
TestModule.prototype.start = sinon.stub().callsArg(0);
|
|
|
|
TestModule.prototype.getData = function() {};
|
|
|
|
TestModule.prototype.getAPIMethods = function() {
|
|
|
|
return [
|
|
|
|
['getData', this, this.getData, 1]
|
|
|
|
];
|
|
|
|
};
|
|
|
|
node.test2 = {};
|
|
|
|
node.test2.start = sinon.stub().callsArg(0);
|
|
|
|
node.getServiceOrder = sinon.stub().returns([
|
|
|
|
{
|
|
|
|
name: 'test1',
|
|
|
|
module: TestModule
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'test2'
|
|
|
|
}
|
|
|
|
]);
|
|
|
|
node.start(function() {
|
|
|
|
node.test2.start.callCount.should.equal(1);
|
|
|
|
TestModule.prototype.start.callCount.should.equal(1);
|
|
|
|
done();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('#stop', function() {
|
|
|
|
it('will call stop for each module', function(done) {
|
|
|
|
var node = new Node({});
|
|
|
|
function TestModule() {}
|
|
|
|
util.inherits(TestModule, BaseModule);
|
|
|
|
TestModule.prototype.stop = sinon.stub().callsArg(0);
|
|
|
|
TestModule.prototype.getData = function() {};
|
|
|
|
TestModule.prototype.getAPIMethods = function() {
|
|
|
|
return [
|
|
|
|
['getData', this, this.getData, 1]
|
|
|
|
];
|
|
|
|
};
|
|
|
|
node.modules = {
|
|
|
|
'test1': new TestModule({node: node})
|
|
|
|
};
|
|
|
|
node.test2 = {};
|
|
|
|
node.test2.stop = sinon.stub().callsArg(0);
|
|
|
|
node.getServiceOrder = sinon.stub().returns([
|
|
|
|
{
|
|
|
|
name: 'test2'
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'test1',
|
|
|
|
module: TestModule
|
|
|
|
}
|
|
|
|
]);
|
|
|
|
node.stop(function() {
|
|
|
|
node.test2.stop.callCount.should.equal(1);
|
|
|
|
TestModule.prototype.stop.callCount.should.equal(1);
|
|
|
|
done();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2015-07-16 12:53:44 -07:00
|
|
|
});
|