diff --git a/bitcore.js b/bitcore.js index d9d8cdf..f50fd26 100644 --- a/bitcore.js +++ b/bitcore.js @@ -81,4 +81,5 @@ requireWhenAccessed('PeerManager', './lib/PeerManager'); requireWhenAccessed('Message', './lib/Message'); requireWhenAccessed('Electrum', './lib/Electrum'); requireWhenAccessed('Armory', './lib/Armory'); +requireWhenAccessed('NetworkMonitor', './lib/NetworkMonitor'); module.exports.Buffer = Buffer; diff --git a/lib/Address.js b/lib/Address.js index 6dafa0c..17118b8 100644 --- a/lib/Address.js +++ b/lib/Address.js @@ -182,5 +182,9 @@ Address.prototype.getScriptPubKey = function() { return script; }; +Address.validate = function(s) { + return new Address(s).isValid(); +}; + module.exports = Address; diff --git a/lib/NetworkMonitor.js b/lib/NetworkMonitor.js new file mode 100644 index 0000000..b112642 --- /dev/null +++ b/lib/NetworkMonitor.js @@ -0,0 +1,80 @@ +var log = require('../util/log'); +var networks = require('../networks'); +var Address = require('./Address'); +var Peer = require('./Peer'); +var PeerManager = require('./PeerManager'); +var util = require('util'); +var EventEmitter = require('events').EventEmitter; +var preconditions = require('preconditions').singleton(); + +var NetworkMonitor = function(peerman) { + preconditions.checkArgument(peerman); + this.peerman = peerman; + this.init(); +} + +util.inherits(NetworkMonitor, EventEmitter); + +NetworkMonitor.create = function(config) { + var peerman = new PeerManager({ + network: config.networkName + }); + + peerman.addPeer(new Peer(config.host, config.port)); + return new NetworkMonitor(peerman); +}; + +NetworkMonitor.prototype.init = function() { + var self = this; + var handleInv = function(info) { + var invs = info.message.invs; + info.conn.sendGetData(invs); + }; + + var handleBlock = function(info) { + self.emit('block', info.message); + }; + + var handleTx = function(info) { + var tx = info.message.tx; + + var from = tx.getSendingAddresses(); + for (var i = 0; i < from.length; i++) { + var addr = from[i]; + self.emit('out:'+addr, tx); + } + var to = tx.getReceivingAddresses(); + for (var i = 0; i < to.length; i++) { + var addr = to[i]; + self.emit('in:'+addr, tx); + } + }; + + this.peerman.on('connection', function(conn) { + if (self.connection) throw new Error('Cant handle more than one connection'); + self.connection = conn; + conn.on('inv', handleInv); + conn.on('block', handleBlock); + conn.on('tx', handleTx); + }); +}; + +NetworkMonitor.prototype.incoming = function(addrStr, callback) { + preconditions.checkArgument(Address.validate(addrStr)); + this.on('in:'+addrStr, callback); +}; + +NetworkMonitor.prototype.outgoing = function(addrStr, callback) { + preconditions.checkArgument(Address.validate(addrStr)); + this.on('out:'+addrStr, callback); +}; + +NetworkMonitor.prototype.start = function() { + this.peerman.start(); +}; + +NetworkMonitor.prototype.stop = function() { + this.peerman.stop(); +}; + +module.exports = NetworkMonitor; diff --git a/package.json b/package.json index 0a64dc8..cd5ca05 100644 --- a/package.json +++ b/package.json @@ -84,7 +84,8 @@ "async": "~0.2.10", "event-stream": "~3.1.5", "gulp-concat": "~2.2.0", - "gulp": "~3.8.2" + "gulp": "~3.8.2", + "preconditions": "^1.0.7" }, "testling": { "harness": "mocha-bdd", diff --git a/test/test.Address.js b/test/test.Address.js index ab7e4c7..0c4d359 100644 --- a/test/test.Address.js +++ b/test/test.Address.js @@ -50,6 +50,7 @@ describe('Address', function() { var s = a.toString(); a.isValid().should.equal(result); + Address.validate(address).should.equal(result); s.should.equal(a.toString()); // check that validation doesn't change data }); }); diff --git a/test/test.NetworkMonitor.js b/test/test.NetworkMonitor.js new file mode 100644 index 0000000..6eb6b17 --- /dev/null +++ b/test/test.NetworkMonitor.js @@ -0,0 +1,97 @@ +'use strict'; + +var chai = chai || require('chai'); +var bitcore = bitcore || require('../bitcore'); +var NetworkMonitor = bitcore.NetworkMonitor; +var EventEmitter = require('events').EventEmitter; + +var should = chai.should(); + +var nop = function() {}; + +describe('NetworkMonitor', function() { + var config = { + networkName: 'testnet', + host: 'localhost', + port: 18333 + }; + var fakePM = {}; + fakePM.on = nop; + it('should initialze the main object', function() { + should.exist(NetworkMonitor); + }); + it('should be able to instanciate', function() { + var nm = new NetworkMonitor(fakePM); + should.exist(nm); + }); + it('should be able to create instance', function() { + var nm = new NetworkMonitor.create(config); + should.exist(nm); + }); + it('should be able to start instance', function() { + var nm = new NetworkMonitor.create(config); + nm.start.bind(nm).should.not.throw(); + }); + it('should be able to stop instance', function() { + var nm = new NetworkMonitor.create(config); + nm.start(); + nm.stop.bind(nm).should.not.throw(); + }); + it('should be able to register listeners', function() { + var nm = new NetworkMonitor.create(config); + (function() { + nm.on('block', nop); + }).should.not.throw(); + (function() { + nm.incoming('n2tTCgsJPJBZZEKLiJx9KoU4idJQB37j9E', nop); + }).should.not.throw(); + (function() { + nm.outgoing('n2tTCgsJPJBZZEKLiJx9KoU4idJQB37j9E', nop); + }).should.not.throw(); + }); + var createConnectedNM = function() { + var nm = new NetworkMonitor.create(config); + var fakeConnection = new EventEmitter(); + nm.peerman.emit('connection', fakeConnection); + return nm; + }; + it('should store connection', function() { + var nm = createConnectedNM(); + should.exist(nm.connection); + }); + describe('block event', function() { + it('should be called on blocks', function(done) { + var nm = createConnectedNM(); + nm.on('block', function(m) { + should.exist(m); + done(); + }); + nm.connection.emit('block', { + message: 'test' + }); + }); + }); + describe('incoming tx event', function() { + it('should be called on incoming transactions', function(done) { + var nm = createConnectedNM(); + nm.incoming('n2tTCgsJPJBZZEKLiJx9KoU4idJQB37j9E', function(tx) { + should.exist(tx); + done(); + }); + var fakeTX = null; + nm.connection.emit('tx', { + message: { + tx: fakeTX + } + }); + }); + it('should not be called on outgoing transactions', function() { + }); + }); + describe('outgoing tx event', function() { + it('should be called on outgoing transactions', function() { + }); + it('should not be called on incoming transactions', function() { + }); + }); +}); diff --git a/test/test.PeerManager.js b/test/test.PeerManager.js index d2c4ec8..f0fc2fe 100644 --- a/test/test.PeerManager.js +++ b/test/test.PeerManager.js @@ -5,16 +5,10 @@ var bitcore = bitcore || require('../bitcore'); var should = chai.should(); -var PeerManagerModule = bitcore.PeerManager; - -var PeerManager; +var PeerManager = bitcore.PeerManager; describe('PeerManager', function() { - it('should initialze the main object', function() { - should.exist(PeerManagerModule); - }); it('should be able to create class', function() { - PeerManager = PeerManagerModule; should.exist(PeerManager); }); it('should be able to create instance', function() { diff --git a/test/test.Transaction.js b/test/test.Transaction.js index df68753..fa8e076 100644 --- a/test/test.Transaction.js +++ b/test/test.Transaction.js @@ -48,7 +48,7 @@ function parse_test_transaction(entry) { } describe('Transaction', function() { - it('should initialze the main object', function() { + it('should initialize the main objects', function() { should.exist(Transaction); In = Transaction.In; Out = Transaction.Out;