diff --git a/app/controllers/socket.js b/app/controllers/socket.js index fa78f55..d996f4c 100644 --- a/app/controllers/socket.js +++ b/app/controllers/socket.js @@ -1,27 +1,32 @@ 'use strict'; // server-side socket behaviour - // io is a variable already taken in express var ios = null; module.exports.init = function(app, io_ext) { ios = io_ext; ios.set('log level', 1); // reduce logging - ios.sockets.on('connection', function() { + ios.sockets.on('connection', function(socket) { + socket.on('subscribe', function(topic) { + console.log('subscribe to '+topic); + socket.join(topic); + }); }); }; - module.exports.broadcast_tx = function(tx) { - ios.sockets.emit('tx', tx); + ios.sockets.in('inv').emit('tx', tx); }; - module.exports.broadcast_block = function(block) { - ios.sockets.emit('block', block); + ios.sockets.in('inv').emit('block', block); +}; + +module.exports.broadcast_address_tx = function(address, tx) { + ios.sockets.in(address).emit('atx', tx); }; module.exports.broadcastSyncInfo = function(syncInfo) { - ios.sockets.emit('block', syncInfo); + ios.sockets.emit('status', syncInfo); }; diff --git a/app/models/Transaction.js b/app/models/Transaction.js index fdad11e..2ecfd59 100644 --- a/app/models/Transaction.js +++ b/app/models/Transaction.js @@ -4,17 +4,18 @@ * Module dependencies. */ -var mongoose = require('mongoose'), - Schema = mongoose.Schema, - async = require('async'), - RpcClient = require('bitcore/RpcClient').class(), - Transaction = require('bitcore/Transaction').class(), - Address = require('bitcore/Address').class(), - BitcoreBlock= require('bitcore/Block').class(), - networks = require('bitcore/networks'), - util = require('bitcore/util/util'), - bignum = require('bignum'), - config = require('../../config/config'), +var mongoose = require('mongoose'), + Schema = mongoose.Schema, + async = require('async'), + RpcClient = require('bitcore/RpcClient').class(), + Transaction = require('bitcore/Transaction').class(), + Address = require('bitcore/Address').class(), + BitcoreBlock = require('bitcore/Block').class(), + networks = require('bitcore/networks'), + util = require('bitcore/util/util'), + bignum = require('bignum'), + config = require('../../config/config'), + sockets = require('../controllers/socket.js'), TransactionItem = require('./TransactionItem'); var CONCURRENCY = 5; @@ -97,8 +98,14 @@ TransactionSchema.statics.createFromArray = function(txs, time, next) { async.forEachLimit(txs, CONCURRENCY, function(txid, cb) { - that.explodeTransactionItems( txid, time, function(err) { + that.explodeTransactionItems( txid, time, function(err, addrs) { if (err) return next(err); + if (addrs) { + async.each(addrs, function(addr){ + sockets.broadcast_address_tx(addr, {'txid': txid}); + }); + + } that.create({txid: txid, time: time}, function(err, new_tx) { if (err && ! err.toString().match(/E11000/)) return cb(err); @@ -115,6 +122,7 @@ TransactionSchema.statics.createFromArray = function(txs, time, next) { TransactionSchema.statics.explodeTransactionItems = function(txid, time, cb) { + var addrs = []; // Is it from genesis block? (testnet==livenet) // TODO: parse it from networks.genesisTX @@ -131,7 +139,6 @@ TransactionSchema.statics.explodeTransactionItems = function(txid, time, cb) { async.forEachLimit(info.vin, CONCURRENCY, function(i, next_in) { if (i.addr && i.value) { -//console.log("Creating IN %s %d", i.addr, i.valueSat); TransactionItem.create({ txid : txid, value_sat : -1 * i.valueSat, @@ -139,6 +146,9 @@ TransactionSchema.statics.explodeTransactionItems = function(txid, time, cb) { index : i.n, ts : time, }, next_in); + if (addrs.indexOf(i.addr) === -1) { + addrs.push(i.addr); + } } else { if ( !i.coinbase ) { @@ -155,7 +165,6 @@ TransactionSchema.statics.explodeTransactionItems = function(txid, time, cb) { * TODO Support multisigs */ if (o.value && o.scriptPubKey && o.scriptPubKey.addresses && o.scriptPubKey.addresses[0]) { -//console.log("Creating OUT %s %d", o.scriptPubKey.addresses[0], o.valueSat); TransactionItem.create({ txid : txid, value_sat : o.valueSat, @@ -171,7 +180,7 @@ TransactionSchema.statics.explodeTransactionItems = function(txid, time, cb) { }, function (err) { if (err && ! err.toString().match(/E11000/)) return cb(err); - return cb(); + return cb(null, addrs); }); }); }); diff --git a/public/js/controllers/address.js b/public/js/controllers/address.js index 46a441d..6276230 100644 --- a/public/js/controllers/address.js +++ b/public/js/controllers/address.js @@ -1,6 +1,14 @@ 'use strict'; -angular.module('insight.address').controller('AddressController', ['$scope', '$rootScope', '$routeParams', '$location', 'Global', 'Address', function ($scope, $rootScope, $routeParams, $location, Global, Address) { +angular.module('insight.address').controller('AddressController', + ['$scope', + '$rootScope', + '$routeParams', + '$location', + 'Global', + 'Address', + 'get_socket', + function ($scope, $rootScope, $routeParams, $location, Global, Address, get_socket) { $scope.global = Global; $scope.findOne = function() { @@ -13,6 +21,8 @@ angular.module('insight.address').controller('AddressController', ['$scope', '$r $location.path('/'); }); }; + var socket = get_socket($scope); + socket.emit('subscribe', $routeParams.addrStr); $scope.params = $routeParams; }]); diff --git a/public/js/controllers/footer.js b/public/js/controllers/footer.js index 56ee28f..611cfc8 100644 --- a/public/js/controllers/footer.js +++ b/public/js/controllers/footer.js @@ -1,13 +1,12 @@ 'use strict'; -angular.module('insight.system').controller('FooterController', ['$scope', 'Global', 'socket', 'Status', function ($scope, Global, socket, Status) { +angular.module('insight.system').controller('FooterController', + ['$scope', + 'Global', + 'Status', + function ($scope, Global, Status) { $scope.global = Global; - socket.on('block', function(block) { -console.log('[footer.js:14]',block); //TODO - console.log('Block received! ' + JSON.stringify(block)); - }); - $scope.getFooter = function() { Status.get({ q: 'getInfo' diff --git a/public/js/controllers/index.js b/public/js/controllers/index.js index 5c03de6..e733d75 100755 --- a/public/js/controllers/index.js +++ b/public/js/controllers/index.js @@ -2,9 +2,19 @@ var TRANSACTION_DISPLAYED = 5; var BLOCKS_DISPLAYED = 5; -angular.module('insight.system').controller('IndexController', ['$scope', '$rootScope', 'Global', 'socket', 'Blocks', 'Transactions', function($scope, $rootScope, Global, socket, Blocks, Transactions) { +angular.module('insight.system').controller('IndexController', + ['$scope', + '$rootScope', + 'Global', + 'get_socket', + 'Blocks', + 'Transactions', + function($scope, $rootScope, Global, get_socket, Blocks, Transactions) { $scope.global = Global; + var socket = get_socket($scope); + socket.emit('subscribe', 'inv'); + //show errors $scope.flashMessage = $rootScope.flashMessage || null; @@ -18,9 +28,9 @@ angular.module('insight.system').controller('IndexController', ['$scope', '$root socket.on('block', function(block) { console.log('Block received! ' + JSON.stringify(block)); - if ($scope.blocks.length === BLOCKS_DISPLAYED) { - $scope.blocks.pop(); - } + if ($scope.blocks.length === BLOCKS_DISPLAYED) { + $scope.blocks.pop(); + } $scope.blocks.unshift(block); }); diff --git a/public/js/controllers/transactions.js b/public/js/controllers/transactions.js index e1272e8..ccb3a92 100644 --- a/public/js/controllers/transactions.js +++ b/public/js/controllers/transactions.js @@ -1,13 +1,28 @@ 'use strict'; -angular.module('insight.transactions').controller('transactionsController', ['$scope', '$rootScope', '$routeParams', '$location', 'Global', 'Transaction', 'TransactionsByBlock', 'TransactionsByAddress', '$rootScope', function ($scope, $rootScope, $routeParams, $location, Global, Transaction, TransactionsByBlock, TransactionsByAddress) { +angular.module('insight.transactions').controller('transactionsController', + ['$scope', + '$rootScope', + '$routeParams', + '$location', + 'Global', + 'Transaction', + 'TransactionsByBlock', + 'TransactionsByAddress', + 'get_socket', + function ($scope, $rootScope, $routeParams, $location, Global, Transaction, TransactionsByBlock, TransactionsByAddress, get_socket) { $scope.global = Global; - $scope.findOne = function() { + $scope.findThis = function() { + $scope.findTx($routeParams.txId); + }; + + $scope.findTx = function(txid) { Transaction.get({ - txId: $routeParams.txId + txId: txid }, function(tx) { $scope.tx = tx; + $scope.txs.push(tx); }, function() { $rootScope.flashMessage = 'Transaction Not Found'; $location.path('/'); @@ -29,4 +44,13 @@ angular.module('insight.transactions').controller('transactionsController', ['$s $scope.txs = txs; }); }; + var socket = get_socket($scope); + console.log('transactions.js'); + socket.on('atx', function(tx) { + console.log('Incoming transaction for address!', tx); + $scope.findTx(tx.txid); + }); + + $scope.txs = []; + }]); diff --git a/public/js/services/socket.js b/public/js/services/socket.js index 2ffe741..98af5e1 100644 --- a/public/js/services/socket.js +++ b/public/js/services/socket.js @@ -1,26 +1,59 @@ 'use strict'; -angular.module('insight.socket').factory('socket', ['$rootScope', function($rootScope) { - var socket = io.connect(); - return { - on: function(eventName, callback) { - socket.on(eventName, function() { - var args = arguments; - $rootScope.$apply(function() { - callback.apply(socket, args); - }); - }); - }, - emit: function(eventName, data, callback) { - socket.emit(eventName, data, function() { - var args = arguments; - $rootScope.$apply(function() { - if (callback) { - callback.apply(socket, args); - } - }); - }); - } - }; +var ScopedSocket = function(socket, $rootScope) { + this.socket = socket; + this.$rootScope = $rootScope; + this.listeners = []; +}; + +ScopedSocket.prototype.removeAllListeners = function() { + for (var i = 0; i < this.listeners.length; i++) { + var details = this.listeners[i]; + this.socket.removeListener(details.event, details.fn); + } + this.listeners = []; +}; + +ScopedSocket.prototype.on = function(event, callback) { + var socket = this.socket; + var $rootScope = this.$rootScope; + + var wrapped_callback = function() { + var args = arguments; + $rootScope.$apply(function() { + callback.apply(socket, args); + }); + }; + socket.on(event, wrapped_callback); + + this.listeners.push({ + event: event, + fn: wrapped_callback + }); +}; + +ScopedSocket.prototype.emit = function(event, data, callback) { + var socket = this.socket; + var $rootScope = this.$rootScope; + + socket.emit(event, data, function() { + var args = arguments; + $rootScope.$apply(function() { + if (callback) { + callback.apply(socket, args); + } + }); + }); +}; + +angular.module('insight.socket').factory('get_socket', ['$rootScope', function($rootScope) { + var socket = io.connect(); + return function(scope) { + var scopedSocket = new ScopedSocket(socket, $rootScope); + scope.$on('$destroy', function() { + scopedSocket.removeAllListeners(); + }); + return scopedSocket; + }; }]); diff --git a/public/views/transaction.html b/public/views/transaction.html index 8bca73b..fe4afeb 100644 --- a/public/views/transaction.html +++ b/public/views/transaction.html @@ -1,4 +1,4 @@ -
+

Transaction View information about a bitcoin transaction