Merge pull request #188 from pnagurny/feature/more-changes

More changes
This commit is contained in:
Braydon Fuller 2015-09-03 17:39:18 -04:00
commit d068681283
8 changed files with 131 additions and 36 deletions

1
.gitignore vendored
View File

@ -29,3 +29,4 @@ libbitcoind
libbitcoind* libbitcoind*
libbitcoind.includes libbitcoind.includes
*.log *.log
.DS_Store

View File

@ -145,6 +145,7 @@ Node.prototype._instantiateService = function(service) {
var config = service.config; var config = service.config;
config.node = this; config.node = this;
config.name = service.name;
var mod = new service.module(config); var mod = new service.module(config);
// include in loaded services // include in loaded services

View File

@ -9,16 +9,7 @@ var path = require('path');
var packageFile = require('../../package.json'); var packageFile = require('../../package.json');
var mkdirp = require('mkdirp'); var mkdirp = require('mkdirp');
var fs = require('fs'); var fs = require('fs');
var defaultConfig = require('./default-config');
var BASE_CONFIG = {
name: 'My Node',
services: [
'address'
],
datadir: './data',
network: 'livenet',
port: 3001
};
var version; var version;
if (packageFile.version.match('-dev')) { if (packageFile.version.match('-dev')) {
@ -59,19 +50,19 @@ function createBitcoinDirectory(datadir, done) {
/** /**
* Will create a base Bitcore Node configuration directory and files. * Will create a base Bitcore Node configuration directory and files.
* @param {String} configDir - The absolute path * @param {String} configDir - The absolute path
* @param {String} name - The name of the node
* @param {String} datadir - The bitcoin database directory * @param {String} datadir - The bitcoin database directory
* @param {Boolean} isGlobal - If the configuration depends on globally installed node services. * @param {Boolean} isGlobal - If the configuration depends on globally installed node services.
* @param {Function} done - The callback function called when finished * @param {Function} done - The callback function called when finished
*/ */
function createConfigDirectory(configDir, name, datadir, isGlobal, done) { function createConfigDirectory(configDir, datadir, isGlobal, done) {
mkdirp(configDir, function(err) { mkdirp(configDir, function(err) {
if (err) { if (err) {
throw err; throw err;
} }
var config = BASE_CONFIG; var configInfo = defaultConfig();
config.name = name || 'Bitcore Node'; var config = configInfo.config;
config.datadir = datadir; config.datadir = datadir;
var configJSON = JSON.stringify(config, null, 2); var configJSON = JSON.stringify(config, null, 2);
var packageJSON = JSON.stringify(BASE_PACKAGE, null, 2); var packageJSON = JSON.stringify(BASE_PACKAGE, null, 2);
@ -95,7 +86,6 @@ function createConfigDirectory(configDir, name, datadir, isGlobal, done) {
* @param {Object} options * @param {Object} options
* @param {String} options.cwd - The current working directory * @param {String} options.cwd - The current working directory
* @param {String} options.dirname - The name of the bitcore node configuration directory * @param {String} options.dirname - The name of the bitcore node configuration directory
* @param {String} options.name - The name of the bitcore node
* @param {String} options.datadir - The path to the bitcoin datadir * @param {String} options.datadir - The path to the bitcoin datadir
* @param {Function} done - A callback function called when finished * @param {Function} done - A callback function called when finished
*/ */
@ -106,13 +96,11 @@ function create(options, done) {
$.checkArgument(_.isFunction(done)); $.checkArgument(_.isFunction(done));
$.checkArgument(_.isString(options.cwd)); $.checkArgument(_.isString(options.cwd));
$.checkArgument(_.isString(options.dirname)); $.checkArgument(_.isString(options.dirname));
$.checkArgument(_.isString(options.name) || _.isUndefined(options.name));
$.checkArgument(_.isBoolean(options.isGlobal)); $.checkArgument(_.isBoolean(options.isGlobal));
$.checkArgument(_.isString(options.datadir)); $.checkArgument(_.isString(options.datadir));
var cwd = options.cwd; var cwd = options.cwd;
var dirname = options.dirname; var dirname = options.dirname;
var name = options.name;
var datadir = options.datadir; var datadir = options.datadir;
var isGlobal = options.isGlobal; var isGlobal = options.isGlobal;
@ -123,7 +111,7 @@ function create(options, done) {
function(next) { function(next) {
// Setup the the bitcore-node directory and configuration // Setup the the bitcore-node directory and configuration
if (!fs.existsSync(absConfigDir)) { if (!fs.existsSync(absConfigDir)) {
createConfigDirectory(absConfigDir, name, datadir, isGlobal, next); createConfigDirectory(absConfigDir, datadir, isGlobal, next);
} else { } else {
next(new Error('Directory "' + absConfigDir+ '" already exists.')); next(new Error('Directory "' + absConfigDir+ '" already exists.'));
} }

View File

@ -7,6 +7,7 @@ var Service = function(options) {
EventEmitter.call(this); EventEmitter.call(this);
this.node = options.node; this.node = options.node;
this.name = options.name;
}; };
util.inherits(Service, EventEmitter); util.inherits(Service, EventEmitter);
@ -81,4 +82,10 @@ Service.prototype.setupRoutes = function(app) {
// Setup express routes here // Setup express routes here
}; };
Service.prototype.getRoutePrefix = function() {
return this.name;
};
module.exports = Service; module.exports = Service;

View File

@ -247,13 +247,13 @@ DB.prototype.estimateFee = function(blocks, callback) {
DB.prototype.getPublishEvents = function() { DB.prototype.getPublishEvents = function() {
return [ return [
{ {
name: 'transaction', name: 'db/transaction',
scope: this, scope: this,
subscribe: this.subscribe.bind(this, 'transaction'), subscribe: this.subscribe.bind(this, 'transaction'),
unsubscribe: this.unsubscribe.bind(this, 'transaction') unsubscribe: this.unsubscribe.bind(this, 'transaction')
}, },
{ {
name: 'block', name: 'db/block',
scope: this, scope: this,
subscribe: this.subscribe.bind(this, 'block'), subscribe: this.subscribe.bind(this, 'block'),
unsubscribe: this.unsubscribe.bind(this, 'block') unsubscribe: this.unsubscribe.bind(this, 'block')
@ -435,12 +435,15 @@ DB.prototype.getHashes = function getHashes(tipHash, callback) {
if (hash === self.genesis.hash) { if (hash === self.genesis.hash) {
// Stop at the genesis block // Stop at the genesis block
self.cache.chainHashes[tipHash] = hashes; self.cache.chainHashes[tipHash] = hashes;
callback(null, hashes); callback(null, hashes);
} else if(self.cache.chainHashes[hash]) { } else if(self.cache.chainHashes[hash]) {
hashes.shift(); hashes.shift();
hashes = self.cache.chainHashes[hash].concat(hashes); hashes = self.cache.chainHashes[hash].concat(hashes);
delete self.cache.chainHashes[hash];
self.cache.chainHashes[tipHash] = hashes; self.cache.chainHashes[tipHash] = hashes;
if(hash !== tipHash) {
delete self.cache.chainHashes[hash];
}
callback(null, hashes); callback(null, hashes);
} else { } else {
// Continue with the previous hash // Continue with the previous hash
@ -643,8 +646,15 @@ DB.prototype.sync = function() {
// This block doesn't progress the current tip, so we'll attempt // This block doesn't progress the current tip, so we'll attempt
// to rewind the chain to the common ancestor of the block and // to rewind the chain to the common ancestor of the block and
// then we can resume syncing. // then we can resume syncing.
self.syncRewind(block, done); log.warn('Beginning reorg! Current tip: ' + self.tip.hash + '; New tip: ' + block.hash);
self.syncRewind(block, function(err) {
if(err) {
return done(err);
}
log.warn('Reorg complete. New tip is ' + self.tip.hash);
done();
});
} }
}); });
}, function(err) { }, function(err) {

View File

@ -6,6 +6,8 @@ var bodyParser = require('body-parser');
var socketio = require('socket.io'); var socketio = require('socket.io');
var BaseService = require('../service'); var BaseService = require('../service');
var inherits = require('util').inherits; var inherits = require('util').inherits;
var index = require('../');
var log = index.log;
var WebService = function(options) { var WebService = function(options) {
var self = this; var self = this;
@ -13,6 +15,7 @@ var WebService = function(options) {
this.port = options.port || this.node.port || 3456; this.port = options.port || this.node.port || 3456;
this.node.on('ready', function() { this.node.on('ready', function() {
self.eventNames = self.getEventNames();
self.setupAllRoutes(); self.setupAllRoutes();
self.server.listen(self.port); self.server.listen(self.port);
self.createMethodsMap(); self.createMethodsMap();
@ -50,7 +53,15 @@ WebService.prototype.stop = function(callback) {
WebService.prototype.setupAllRoutes = function() { WebService.prototype.setupAllRoutes = function() {
for(var key in this.node.services) { for(var key in this.node.services) {
this.node.services[key].setupRoutes(this.app); var subApp = new express.Router();
var service = this.node.services[key];
if(service.getRoutePrefix && service.setupRoutes) {
this.app.use('/' + this.node.services[key].getRoutePrefix(), subApp);
this.node.services[key].setupRoutes(subApp, express);
} else {
log.info('Not setting up routes for ' + service.name);
}
} }
}; };
@ -71,7 +82,32 @@ WebService.prototype.createMethodsMap = function() {
args: args args: args
}; };
}); });
} };
WebService.prototype.getEventNames = function() {
var events = this.node.getAllPublishEvents();
var eventNames = [];
function addEventName(name) {
if(eventNames.indexOf(name) > -1) {
throw new Error('Duplicate event ' + name);
}
eventNames.push(name);
};
events.forEach(function(event) {
addEventName(event.name);
if(event.extraEvents) {
event.extraEvents.forEach(function(name) {
addEventName(name);
});
}
});
return eventNames;
};
WebService.prototype.socketHandler = function(socket) { WebService.prototype.socketHandler = function(socket) {
var self = this; var self = this;
@ -88,10 +124,8 @@ WebService.prototype.socketHandler = function(socket) {
bus.unsubscribe(name, params); bus.unsubscribe(name, params);
}); });
var events = self.node.getAllPublishEvents(); this.eventNames.forEach(function(eventName) {
bus.on(eventName, function() {
events.forEach(function(event) {
bus.on(event.name, function() {
if(socket.connected) { if(socket.connected) {
var results = []; var results = [];
@ -99,7 +133,7 @@ WebService.prototype.socketHandler = function(socket) {
results.push(arguments[i]); results.push(arguments[i]);
} }
var params = [event.name].concat(results); var params = [eventName].concat(results);
socket.emit.apply(socket, params); socket.emit.apply(socket, params);
} }
}); });

View File

@ -74,8 +74,7 @@ describe('#create', function() {
should.equal(fs.existsSync(bitcoinConfig), true); should.equal(fs.existsSync(bitcoinConfig), true);
var config = JSON.parse(fs.readFileSync(configPath)); var config = JSON.parse(fs.readFileSync(configPath));
config.name.should.equal('My Node 1'); config.services.should.deep.equal(['bitcoind', 'db', 'address', 'web']);
config.services.should.deep.equal(['address']);
config.datadir.should.equal('./data'); config.datadir.should.equal('./data');
config.network.should.equal('livenet'); config.network.should.equal('livenet');

View File

@ -39,15 +39,20 @@ describe('WebService', function() {
on: sinon.spy(), on: sinon.spy(),
services: { services: {
one: { one: {
setupRoutes: sinon.spy() setupRoutes: sinon.spy(),
getRoutePrefix: sinon.stub().returns('one')
}, },
two: { two: {
setupRoutes: sinon.spy() setupRoutes: sinon.spy(),
getRoutePrefix: sinon.stub().returns('two')
} }
} }
}; };
var web = new WebService({node: node}); var web = new WebService({node: node});
web.app = {
use: sinon.spy()
};
web.setupAllRoutes(); web.setupAllRoutes();
node.services.one.setupRoutes.callCount.should.equal(1); node.services.one.setupRoutes.callCount.should.equal(1);
@ -97,6 +102,54 @@ describe('WebService', function() {
}); });
}); });
describe('#getEventNames', function() {
it('should get event names', function() {
var Module1 = function() {};
Module1.prototype.getPublishEvents = function() {
return [
{
name: 'event1',
extraEvents: ['event2']
}
];
};
var module1 = new Module1();
var node = {
on: sinon.spy(),
getAllPublishEvents: sinon.stub().returns(module1.getPublishEvents())
};
var web = new WebService({node: node});
var events = web.getEventNames();
events.should.deep.equal(['event1', 'event2']);
});
it('should throw an error if there is a duplicate event', function() {
var Module1 = function() {};
Module1.prototype.getPublishEvents = function() {
return [
{
name: 'event1',
extraEvents: ['event1']
}
];
};
var module1 = new Module1();
var node = {
on: sinon.spy(),
getAllPublishEvents: sinon.stub().returns(module1.getPublishEvents())
};
var web = new WebService({node: node});
(function() {
var events = web.getEventNames();
}).should.throw('Duplicate event event1');
});
});
describe('#socketHandler', function() { describe('#socketHandler', function() {
var bus = new EventEmitter(); var bus = new EventEmitter();
@ -104,7 +157,8 @@ describe('WebService', function() {
Module1.prototype.getPublishEvents = function() { Module1.prototype.getPublishEvents = function() {
return [ return [
{ {
name: 'event1' name: 'event1',
extraEvents: ['event2']
} }
]; ];
}; };
@ -121,6 +175,7 @@ describe('WebService', function() {
it('on message should call socketMessageHandler', function(done) { it('on message should call socketMessageHandler', function(done) {
web = new WebService({node: node}); web = new WebService({node: node});
web.eventNames = web.getEventNames();
web.socketMessageHandler = function(param1) { web.socketMessageHandler = function(param1) {
param1.should.equal('data'); param1.should.equal('data');
done(); done();
@ -149,13 +204,13 @@ describe('WebService', function() {
}); });
it('publish events from bus should be emitted from socket', function(done) { it('publish events from bus should be emitted from socket', function(done) {
socket.once('event1', function(param1, param2) { socket.once('event2', function(param1, param2) {
param1.should.equal('param1'); param1.should.equal('param1');
param2.should.equal('param2'); param2.should.equal('param2');
done(); done();
}); });
socket.connected = true; socket.connected = true;
bus.emit('event1', 'param1', 'param2'); bus.emit('event2', 'param1', 'param2');
}); });
it('on disconnect should close bus', function(done) { it('on disconnect should close bus', function(done) {