Merge pull request #89 from maraoz/feature/address-socket-api
Feature/address socket api
This commit is contained in:
commit
bf03c951c7
|
@ -1,27 +1,32 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
// server-side socket behaviour
|
// server-side socket behaviour
|
||||||
|
|
||||||
// io is a variable already taken in express
|
// io is a variable already taken in express
|
||||||
var ios = null;
|
var ios = null;
|
||||||
|
|
||||||
module.exports.init = function(app, io_ext) {
|
module.exports.init = function(app, io_ext) {
|
||||||
ios = io_ext;
|
ios = io_ext;
|
||||||
ios.set('log level', 1); // reduce logging
|
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) {
|
module.exports.broadcast_tx = function(tx) {
|
||||||
ios.sockets.emit('tx', tx);
|
ios.sockets.in('inv').emit('tx', tx);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
module.exports.broadcast_block = function(block) {
|
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) {
|
module.exports.broadcastSyncInfo = function(syncInfo) {
|
||||||
ios.sockets.emit('block', syncInfo);
|
ios.sockets.emit('status', syncInfo);
|
||||||
};
|
};
|
||||||
|
|
|
@ -10,11 +10,12 @@ var mongoose = require('mongoose'),
|
||||||
RpcClient = require('bitcore/RpcClient').class(),
|
RpcClient = require('bitcore/RpcClient').class(),
|
||||||
Transaction = require('bitcore/Transaction').class(),
|
Transaction = require('bitcore/Transaction').class(),
|
||||||
Address = require('bitcore/Address').class(),
|
Address = require('bitcore/Address').class(),
|
||||||
BitcoreBlock= require('bitcore/Block').class(),
|
BitcoreBlock = require('bitcore/Block').class(),
|
||||||
networks = require('bitcore/networks'),
|
networks = require('bitcore/networks'),
|
||||||
util = require('bitcore/util/util'),
|
util = require('bitcore/util/util'),
|
||||||
bignum = require('bignum'),
|
bignum = require('bignum'),
|
||||||
config = require('../../config/config'),
|
config = require('../../config/config'),
|
||||||
|
sockets = require('../controllers/socket.js'),
|
||||||
TransactionItem = require('./TransactionItem');
|
TransactionItem = require('./TransactionItem');
|
||||||
|
|
||||||
var CONCURRENCY = 5;
|
var CONCURRENCY = 5;
|
||||||
|
@ -97,8 +98,14 @@ TransactionSchema.statics.createFromArray = function(txs, time, next) {
|
||||||
|
|
||||||
async.forEachLimit(txs, CONCURRENCY, function(txid, cb) {
|
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 (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) {
|
that.create({txid: txid, time: time}, function(err, new_tx) {
|
||||||
if (err && ! err.toString().match(/E11000/)) return cb(err);
|
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) {
|
TransactionSchema.statics.explodeTransactionItems = function(txid, time, cb) {
|
||||||
|
var addrs = [];
|
||||||
|
|
||||||
// Is it from genesis block? (testnet==livenet)
|
// Is it from genesis block? (testnet==livenet)
|
||||||
// TODO: parse it from networks.genesisTX
|
// 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) {
|
async.forEachLimit(info.vin, CONCURRENCY, function(i, next_in) {
|
||||||
if (i.addr && i.value) {
|
if (i.addr && i.value) {
|
||||||
|
|
||||||
//console.log("Creating IN %s %d", i.addr, i.valueSat);
|
|
||||||
TransactionItem.create({
|
TransactionItem.create({
|
||||||
txid : txid,
|
txid : txid,
|
||||||
value_sat : -1 * i.valueSat,
|
value_sat : -1 * i.valueSat,
|
||||||
|
@ -139,6 +146,9 @@ TransactionSchema.statics.explodeTransactionItems = function(txid, time, cb) {
|
||||||
index : i.n,
|
index : i.n,
|
||||||
ts : time,
|
ts : time,
|
||||||
}, next_in);
|
}, next_in);
|
||||||
|
if (addrs.indexOf(i.addr) === -1) {
|
||||||
|
addrs.push(i.addr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if ( !i.coinbase ) {
|
if ( !i.coinbase ) {
|
||||||
|
@ -155,7 +165,6 @@ TransactionSchema.statics.explodeTransactionItems = function(txid, time, cb) {
|
||||||
* TODO Support multisigs
|
* TODO Support multisigs
|
||||||
*/
|
*/
|
||||||
if (o.value && o.scriptPubKey && o.scriptPubKey.addresses && o.scriptPubKey.addresses[0]) {
|
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({
|
TransactionItem.create({
|
||||||
txid : txid,
|
txid : txid,
|
||||||
value_sat : o.valueSat,
|
value_sat : o.valueSat,
|
||||||
|
@ -171,7 +180,7 @@ TransactionSchema.statics.explodeTransactionItems = function(txid, time, cb) {
|
||||||
},
|
},
|
||||||
function (err) {
|
function (err) {
|
||||||
if (err && ! err.toString().match(/E11000/)) return cb(err);
|
if (err && ! err.toString().match(/E11000/)) return cb(err);
|
||||||
return cb();
|
return cb(null, addrs);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,6 +1,14 @@
|
||||||
'use strict';
|
'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.global = Global;
|
||||||
|
|
||||||
$scope.findOne = function() {
|
$scope.findOne = function() {
|
||||||
|
@ -13,6 +21,8 @@ angular.module('insight.address').controller('AddressController', ['$scope', '$r
|
||||||
$location.path('/');
|
$location.path('/');
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
var socket = get_socket($scope);
|
||||||
|
socket.emit('subscribe', $routeParams.addrStr);
|
||||||
|
|
||||||
$scope.params = $routeParams;
|
$scope.params = $routeParams;
|
||||||
}]);
|
}]);
|
||||||
|
|
|
@ -1,13 +1,12 @@
|
||||||
'use strict';
|
'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;
|
$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() {
|
$scope.getFooter = function() {
|
||||||
Status.get({
|
Status.get({
|
||||||
q: 'getInfo'
|
q: 'getInfo'
|
||||||
|
|
|
@ -2,9 +2,19 @@
|
||||||
|
|
||||||
var TRANSACTION_DISPLAYED = 5;
|
var TRANSACTION_DISPLAYED = 5;
|
||||||
var BLOCKS_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;
|
$scope.global = Global;
|
||||||
|
|
||||||
|
var socket = get_socket($scope);
|
||||||
|
socket.emit('subscribe', 'inv');
|
||||||
|
|
||||||
//show errors
|
//show errors
|
||||||
$scope.flashMessage = $rootScope.flashMessage || null;
|
$scope.flashMessage = $rootScope.flashMessage || null;
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,28 @@
|
||||||
'use strict';
|
'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.global = Global;
|
||||||
|
|
||||||
$scope.findOne = function() {
|
$scope.findThis = function() {
|
||||||
|
$scope.findTx($routeParams.txId);
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.findTx = function(txid) {
|
||||||
Transaction.get({
|
Transaction.get({
|
||||||
txId: $routeParams.txId
|
txId: txid
|
||||||
}, function(tx) {
|
}, function(tx) {
|
||||||
$scope.tx = tx;
|
$scope.tx = tx;
|
||||||
|
$scope.txs.push(tx);
|
||||||
}, function() {
|
}, function() {
|
||||||
$rootScope.flashMessage = 'Transaction Not Found';
|
$rootScope.flashMessage = 'Transaction Not Found';
|
||||||
$location.path('/');
|
$location.path('/');
|
||||||
|
@ -29,4 +44,13 @@ angular.module('insight.transactions').controller('transactionsController', ['$s
|
||||||
$scope.txs = txs;
|
$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 = [];
|
||||||
|
|
||||||
}]);
|
}]);
|
||||||
|
|
|
@ -1,18 +1,42 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
angular.module('insight.socket').factory('socket', ['$rootScope', function($rootScope) {
|
var ScopedSocket = function(socket, $rootScope) {
|
||||||
var socket = io.connect();
|
this.socket = socket;
|
||||||
return {
|
this.$rootScope = $rootScope;
|
||||||
on: function(eventName, callback) {
|
this.listeners = [];
|
||||||
socket.on(eventName, function() {
|
};
|
||||||
|
|
||||||
|
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;
|
var args = arguments;
|
||||||
$rootScope.$apply(function() {
|
$rootScope.$apply(function() {
|
||||||
callback.apply(socket, args);
|
callback.apply(socket, args);
|
||||||
});
|
});
|
||||||
|
};
|
||||||
|
socket.on(event, wrapped_callback);
|
||||||
|
|
||||||
|
this.listeners.push({
|
||||||
|
event: event,
|
||||||
|
fn: wrapped_callback
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
emit: function(eventName, data, callback) {
|
|
||||||
socket.emit(eventName, data, function() {
|
ScopedSocket.prototype.emit = function(event, data, callback) {
|
||||||
|
var socket = this.socket;
|
||||||
|
var $rootScope = this.$rootScope;
|
||||||
|
|
||||||
|
socket.emit(event, data, function() {
|
||||||
var args = arguments;
|
var args = arguments;
|
||||||
$rootScope.$apply(function() {
|
$rootScope.$apply(function() {
|
||||||
if (callback) {
|
if (callback) {
|
||||||
|
@ -20,7 +44,16 @@ angular.module('insight.socket').factory('socket', ['$rootScope', function($root
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
|
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;
|
||||||
};
|
};
|
||||||
}]);
|
}]);
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
<section data-ng-controller="transactionsController" data-ng-init="findOne()">
|
<section data-ng-controller="transactionsController" data-ng-init="findThis()">
|
||||||
<h1>
|
<h1>
|
||||||
Transaction
|
Transaction
|
||||||
<small>View information about a bitcoin transaction</small>
|
<small>View information about a bitcoin transaction</small>
|
||||||
|
|
Loading…
Reference in New Issue