diff --git a/bin/client.js b/bin/client.js deleted file mode 100644 index 30b61c88..00000000 --- a/bin/client.js +++ /dev/null @@ -1,24 +0,0 @@ -'use strict'; - -var socket = require('socket.io-client')('http://localhost:3000'); -socket.on('connect', function(){ - console.log('connected'); -}); - -socket.on('disconnect', function(){ - console.log('disconnected'); -}); - -var message = { - command: 'getOutputs', - params: ['1HTxCVrXuthad6YW5895K98XmVsdMvvBSw', true] -}; - -socket.emit('message', message, function(response) { - if(response.error) { - console.log('Error', response.error); - return; - } - - console.log(response.result); -}); \ No newline at end of file diff --git a/bin/start-node.js b/bin/start.js similarity index 77% rename from bin/start-node.js rename to bin/start.js index 3f18e8fc..0c4f6204 100644 --- a/bin/start-node.js +++ b/bin/start.js @@ -2,13 +2,14 @@ var BitcoinNode = require('..').Node; var chainlib = require('chainlib'); -var io = require('socket.io')(3000); +var socketio = require('socket.io'); var log = chainlib.log; log.debug = function() {}; var configuration = { datadir: process.env.BITCOINDJS_DIR || '~/.bitcoin', - network: process.env.BITCOINDJS_NETWORK || 'livenet' + network: process.env.BITCOINDJS_NETWORK || 'livenet', + port: 3000 }; var node = new BitcoinNode(configuration); @@ -18,6 +19,8 @@ var interval; node.on('ready', function() { + var io = socketio(configuration.port); + interval = setInterval(function() { log.info('Sync Status: Tip:', node.chain.tip.hash, 'Height:', node.chain.tip.__height, 'Rate:', count/10, 'blocks per second'); count = 0; @@ -58,15 +61,17 @@ node.on('ready', function() { } var callback = function(err, result) { - console.log('callback called'); - console.log(err, result); var response = {}; if(err) { response.error = err; } if(result) { - response.result = result; + if(result.toJSON) { + response.result = result.toJSON(); + } else { + response.result = result; + } } socketCallback(response); @@ -92,9 +97,20 @@ node.on('ready', function() { var events = node.getAllPublishEvents(); events.forEach(function(event) { - bus.on(event.name, function(data) { + bus.on(event.name, function() { if(socket.connected) { - socket.emit(event.name, data); + var results = []; + + for(var i = 0; i < arguments.length; i++) { + if(arguments[i].toJSON) { + results.push(arguments[i].toJSON()); + } else { + results.push(arguments[i]); + } + } + + var params = [event.name].concat(results); + socket.emit.apply(socket, params); } }); }); diff --git a/example/client.js b/example/client.js new file mode 100644 index 00000000..2acb35b5 --- /dev/null +++ b/example/client.js @@ -0,0 +1,44 @@ +'use strict'; + +var socket = require('socket.io-client')('http://localhost:3000'); +socket.on('connect', function(){ + console.log('connected'); +}); + +socket.on('disconnect', function(){ + console.log('disconnected'); +}); + +var message = { + command: 'getOutputs', + params: ['1HTxCVrXuthad6YW5895K98XmVsdMvvBSw', true] +}; + +socket.send(message, function(response) { + if(response.error) { + console.log('Error', response.error); + return; + } + + console.log(response.result); +}); + +var message2 = { + command: 'getTransaction', + params: ['4f793f67fc7465f14fa3a8d3727fa7d133cdb2f298234548b94a5f08b6f4103e', true] +}; + +socket.send(message2, function(response) { + if(response.error) { + console.log('Error', response.error); + return; + } + + console.log(response.result); +}); + +socket.on('transaction', function(address, block) { + console.log(address, block); +}); + +socket.emit('subscribe', 'transaction', ['13FMwCYz3hUhwPcaWuD2M1U2KzfTtvLM89']); \ No newline at end of file diff --git a/lib/block.js b/lib/block.js index 6e5e9c13..e8499c31 100644 --- a/lib/block.js +++ b/lib/block.js @@ -60,6 +60,10 @@ Block.prototype.toObject = function() { }; }; +Block.prototype.toJSON = function() { + return JSON.stringify(this.toObject()); +}; + Block.prototype.headerToBufferWriter = function(bw) { /* jshint maxstatements: 20 */ diff --git a/lib/bus.js b/lib/bus.js index d1d0ecca..8772f3f8 100644 --- a/lib/bus.js +++ b/lib/bus.js @@ -41,7 +41,15 @@ Bus.prototype.unsubscribe = function(name) { }; Bus.prototype.close = function() { - // TODO Unsubscribe from all events + // Unsubscribe from all events + for (var i = 0; i < this.db.modules.length; i++) { + var mod = this.db.modules[i]; + var events = mod.getPublishEvents(); + for (var j = 0; j < events.length; j++) { + var event = events[j]; + event.unsubscribe.call(event.scope, this); + } + } }; module.exports = Bus; diff --git a/lib/modules/address.js b/lib/modules/address.js index c4a582f8..93863b21 100644 --- a/lib/modules/address.js +++ b/lib/modules/address.js @@ -172,7 +172,10 @@ AddressModule.prototype.subscribe = function(name, emitter, addresses) { AddressModule.prototype.unsubscribe = function(name, emitter, addresses) { $.checkArgument(emitter instanceof EventEmitter, 'First argument is expected to be an EventEmitter'); - $.checkArgument(Array.isArray(addresses), 'Second argument is expected to be an Array of addresses'); + + if(!addresses) { + return this.unsubscribeAll(name, emitter); + } for(var i = 0; i < addresses.length; i++) { if(this.subscriptions[name][addresses[i]]) { @@ -185,6 +188,18 @@ AddressModule.prototype.unsubscribe = function(name, emitter, addresses) { } }; +AddressModule.prototype.unsubscribeAll = function(name, emitter) { + $.checkArgument(emitter instanceof EventEmitter, 'First argument is expected to be an EventEmitter'); + + for(var address in this.subscriptions[name]) { + var emitters = this.subscriptions[name][address]; + var index = emitters.indexOf(emitter); + if(index > -1) { + emitters.splice(index, 1); + } + } +}; + AddressModule.prototype.getBalance = function(address, queryMempool, callback) { this.getUnspentOutputs(address, queryMempool, function(err, outputs) { if(err) { diff --git a/lib/node.js b/lib/node.js index 7c82de52..16d9fb43 100644 --- a/lib/node.js +++ b/lib/node.js @@ -30,7 +30,7 @@ Node.prototype.openBus = function() { }; Node.prototype.getAllAPIMethods = function() { - var methods = []; + var methods = this.db.getAPIMethods(); for (var i = 0; i < this.db.modules.length; i++) { var mod = this.db.modules[i]; methods = methods.concat(mod.getAPIMethods()); diff --git a/test/bus.unit.js b/test/bus.unit.js index 3dc09dc5..a0d4f249 100644 --- a/test/bus.unit.js +++ b/test/bus.unit.js @@ -58,4 +58,30 @@ describe('Bus', function() { }); }); + describe('#close', function() { + it('will unsubscribe from all events', function() { + var unsubscribe = sinon.spy(); + var db = { + modules: [ + { + getPublishEvents: sinon.stub().returns([ + { + name: 'test', + scope: this, + unsubscribe: unsubscribe + } + ]) + } + ] + }; + + var bus = new Bus({db: db}); + bus.close(); + + unsubscribe.callCount.should.equal(1); + unsubscribe.args[0].length.should.equal(1); + unsubscribe.args[0][0].should.equal(bus); + }); + }); + }); diff --git a/test/modules/address.unit.js b/test/modules/address.unit.js index 71c32478..03482823 100644 --- a/test/modules/address.unit.js +++ b/test/modules/address.unit.js @@ -295,6 +295,20 @@ describe('AddressModule', function() { am.unsubscribe(name, emitter, [address]); am.subscriptions.balance[address].should.deep.equal([emitter2]); }); + it('should unsubscribe from all addresses if no addresses are specified', function() { + var am = new AddressModule({}); + var emitter = new EventEmitter(); + var emitter2 = new EventEmitter(); + am.subscriptions.balance = { + '1KiW1A4dx1oRgLHtDtBjcunUGkYtFgZ1W': [emitter, emitter2], + '1DzjESe6SLmAKVPLFMj6Sx1sWki3qt5i8N': [emitter2, emitter] + }; + am.unsubscribe('balance', emitter); + am.subscriptions.balance.should.deep.equal({ + '1KiW1A4dx1oRgLHtDtBjcunUGkYtFgZ1W': [emitter2], + '1DzjESe6SLmAKVPLFMj6Sx1sWki3qt5i8N': [emitter2] + }); + }); }); describe('#getBalance', function() { diff --git a/test/node.unit.js b/test/node.unit.js index 2417f1dc..efab912d 100644 --- a/test/node.unit.js +++ b/test/node.unit.js @@ -40,6 +40,45 @@ describe('Bitcoind Node', function() { bus.db.should.equal(db); }); }); + describe('#getAllAPIMethods', function() { + it('should return db methods and modules methods', function() { + var node = new Node({}); + var db = { + getAPIMethods: sinon.stub().returns(['db1', 'db2']), + modules: [ + { + getAPIMethods: sinon.stub().returns(['mda1', 'mda2']) + }, + { + getAPIMethods: sinon.stub().returns(['mdb1', 'mdb2']) + } + ] + }; + 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({}); + var db = { + modules: [ + { + getPublishEvents: sinon.stub().returns(['mda1', 'mda2']) + }, + { + getPublishEvents: sinon.stub().returns(['mdb1', 'mdb2']) + } + ] + }; + node.db = db; + + var events = node.getAllPublishEvents(); + events.should.deep.equal(['mda1', 'mda2', 'mdb1', 'mdb2']); + }); + }); describe('#_loadConfiguration', function() { it('should call the necessary methods', function() { var node = new Node({});