Merge pull request #1332 from matiu/insight-reconnect

Insight reconnect
This commit is contained in:
Gustavo Maximiliano Cortez 2014-09-09 23:17:33 -03:00
commit 9d142cbff7
14 changed files with 322 additions and 258 deletions

View File

@ -15,7 +15,7 @@
<div class="inner-wrap">
<span class="status" ng-if="$root.reconnecting">
<i class="fi-loop icon-rotate m15r"></i>
Attempting to reconnect...
<span translate> Network Error. Attempting to reconnect...</span>
</span>
<nav class="tab-bar" ng-class="{'hide-tab-bar' : !$root.wallet ||
!$root.wallet.isReady() || $root.wallet.isLocked}">

View File

@ -9,7 +9,7 @@ angular.module('copayApp.controllers').controller('AddressesController',
$scope.loading = true;
w.generateAddress(null, function() {
$timeout(function() {
controllerUtils.updateGlobalAddresses();
controllerUtils.updateAddressList();
$scope.loading = false;
}, 1);
});

View File

@ -17,7 +17,7 @@ angular.module('copayApp.controllers').controller('CopayersController',
}
$scope.goToWallet = function() {
controllerUtils.updateGlobalAddresses();
controllerUtils.updateAddressList();
$location.path('/receive');
};

View File

@ -101,5 +101,5 @@ Logger.prototype.setLevel = function(level) {
var logger = new Logger('copay');
logger.setLevel(config.logLevel);
logger.log('Log level:' + config.logLevel);
module.exports = logger;

View File

@ -5,6 +5,7 @@ var async = require('async');
var request = require('request');
var bitcore = require('bitcore');
var io = require('socket.io-client');
var log = require('../../log');
var EventEmitter = require('events').EventEmitter;
var preconditions = require('preconditions').singleton();
@ -27,15 +28,15 @@ var preconditions = require('preconditions').singleton();
- disconnect: the connection with the blochckain is unavailable.
*/
var Insight = function (opts) {
var Insight = function(opts) {
this.status = this.STATUS.DISCONNECTED;
this.subscribed = {};
this.listeningBlocks = false;
preconditions.checkArgument(opts).shouldBeObject(opts)
.checkArgument(opts.host)
.checkArgument(opts.port)
.checkArgument(opts.schema);
.checkArgument(opts.host)
.checkArgument(opts.port)
.checkArgument(opts.schema);
this.url = opts.schema + '://' + opts.host + ':' + opts.port;
this.opts = {
@ -44,8 +45,36 @@ var Insight = function (opts) {
'secure': opts.schema === 'https'
};
this.socket = this.getSocket(this.url, this.opts);
}
util.inherits(Insight, EventEmitter);
Insight.prototype.STATUS = {
CONNECTED: 'connected',
DISCONNECTED: 'disconnected',
DESTROYED: 'destroyed'
}
/** @private */
Insight.prototype.subscribeToBlocks = function() {
var socket = this.getSocket();
if (this.listeningBlocks || !socket.connected) return;
var self = this;
socket.emit('subscribe', 'inv');
socket.on('block', function(blockHash) {
self.emit('block', blockHash);
});
this.listeningBlocks = true;
}
/** @private */
Insight.prototype._getSocketIO = function(url, opts) {
return io(this.url, this.opts);
};
Insight.prototype._setMainHandlers = function(url, opts) {
// Emmit connection events
var self = this;
this.socket.on('connect', function() {
@ -68,34 +97,21 @@ var Insight = function (opts) {
this.socket.on('reconnect', function(attempt) {
if (self.status != self.STATUS.DISCONNECTED) return;
self.emit('reconnect', attempt);
self.reSubscribe();
self.status = self.STATUS.CONNECTED;
self.emit('connect', attempt);
});
}
};
util.inherits(Insight, EventEmitter);
Insight.prototype.STATUS = {
CONNECTED: 'connected',
DISCONNECTED: 'disconnected',
DESTROYED: 'destroyed'
}
/** @private */
Insight.prototype.subscribeToBlocks = function() {
if (this.listeningBlocks || !this.socket.connected) return;
var self = this;
this.socket.emit('subscribe', 'inv');
this.socket.on('block', function(blockHash) {
self.emit('block', blockHash);
});
this.listeningBlocks = true;
}
/** @private */
Insight.prototype.getSocket = function(url, opts) {
return io(this.url, this.opts);
if (!this.socket) {
this.socket = this._getSocketIO(this.url, this.opts);
this._setMainHandlers();
}
return this.socket;
}
/** @private */
@ -107,11 +123,18 @@ Insight.prototype.request = function(path, cb) {
/** @private */
Insight.prototype.requestPost = function(path, data, cb) {
preconditions.checkArgument(path).checkArgument(data).shouldBeFunction(cb);
request({method: "POST", url: this.url + path, json: data}, cb);
request({
method: "POST",
url: this.url + path,
json: data
}, cb);
}
Insight.prototype.destroy = function() {
this.socket.destroy();
var socket = this.getSocket();
this.socket.disconnect();
this.socket.removeAllListeners();
this.socket = null;
this.subscribed = {};
this.status = this.STATUS.DESTROYED;
this.removeAllListeners();
@ -122,49 +145,61 @@ Insight.prototype.subscribe = function(addresses) {
var self = this;
function handlerFor(self, address) {
return function (txid) {
return function(txid) {
// verify the address is still subscribed
if (!self.subscribed[address]) return;
self.emit('tx', {address: address, txid: txid});
log.debug('insight tx event');
self.emit('tx', {
address: address,
txid: txid
});
}
}
var s = self.getSocket();
addresses.forEach(function(address) {
preconditions.checkArgument(new bitcore.Address(address).isValid());
// skip already subscibed
if (!self.subscribed[address]) {
self.subscribed[address] = true;
self.socket.emit('subscribe', address);
self.socket.on(address, handlerFor(self, address));
var handler = handlerFor(self, address);
self.subscribed[address] = handler;
log.debug('Subcribe to: ', address);
s.emit('subscribe', address);
s.on(address, handler);
}
});
};
Insight.prototype.getSubscriptions = function(addresses) {
return Object.keys(this.subscribed);
return this.subscribed;
}
Insight.prototype.unsubscribe = function(addresses) {
addresses = Array.isArray(addresses) ? addresses : [addresses];
var self = this;
addresses.forEach(function(address) {
preconditions.checkArgument(new bitcore.Address(address).isValid());
self.socket.removeEventListener(address);
delete self.subscribed[address];
});
Insight.prototype.reSubscribe = function() {
log.debug('insight reSubscribe');
var allAddresses = Object.keys(this.subscribed);
this.subscribed = {};
var s = this.socket;
if (s) {
s.removeAllListeners();
this._setMainHandlers();
this.subscribe(allAddresses);
this.subscribeToBlocks();
}
};
Insight.prototype.unsubscribeAll = function() {
this.unsubscribe(this.getSubscriptions());
};
Insight.prototype.broadcast = function(rawtx, cb) {
preconditions.checkArgument(rawtx);
preconditions.shouldBeFunction(cb);
this.requestPost('/api/tx/send', {rawtx: rawtx}, function(err, res, body) {
this.requestPost('/api/tx/send', {
rawtx: rawtx
}, function(err, res, body) {
if (err || res.statusCode != 200) cb(err || res);
cb(null, body.txid);
});
@ -218,7 +253,9 @@ Insight.prototype.getUnspent = function(addresses, cb) {
preconditions.shouldBeArray(addresses);
preconditions.shouldBeFunction(cb);
this.requestPost('/api/addrs/utxo', {addrs: addresses.join(',')}, function(err, res, body) {
this.requestPost('/api/addrs/utxo', {
addrs: addresses.join(',')
}, function(err, res, body) {
if (err || res.statusCode != 200) return cb(err || res);
cb(null, body);
});
@ -243,8 +280,8 @@ Insight.prototype.getActivity = function(addresses, cb) {
var getOutputs = function(t) {
return flatArray(
t.vout.map(function(vout) {
return vout.scriptPubKey.addresses;
})
return vout.scriptPubKey.addresses;
})
);
};

View File

@ -38,6 +38,22 @@ TxProposals.prototype.length = function() {
return Object.keys(this.txps).length;
};
TxProposals.prototype.getNtxidsSince = function(sinceTs) {
preconditions.checkArgument(sinceTs);
var ret = [];
for(var ii in this.txps){
var txp = this.txps[ii];
if (txp.createdTs >= sinceTs)
ret.push(ii);
}
console.log('[TxProposals.js.52:ret:]',ret); //TODO
return ret;
};
TxProposals.prototype.getNtxids = function() {
return Object.keys(this.txps);
};

View File

@ -222,12 +222,12 @@ Wallet.prototype._processProposalEvents = function(senderId, m) {
if (m.new) {
ev = {
type: 'new',
cid: senderId
cId: senderId
}
} else if (m.newCopayer) {
} else if (m.newCopayer.length) {
ev = {
type: 'signed',
cid: m.newCopayer
cId: m.newCopayer[0]
};
}
} else {
@ -236,7 +236,6 @@ Wallet.prototype._processProposalEvents = function(senderId, m) {
cId: senderId,
};
}
if (ev)
this.emit('txProposalEvent', ev);
};
@ -328,14 +327,13 @@ Wallet.prototype._onTxProposal = function(senderId, data) {
try {
m = this.txProposals.merge(data.txProposal, Wallet.builderOpts);
var keyMap = this._getKeyMap(m.txp);
ret.newCopayer = m.txp.setCopayers(senderId, keyMap);
m.newCopayer = m.txp.setCopayers(senderId, keyMap);
} catch (e) {
log.debug('Corrupt TX proposal received from:', senderId, e);
log.error('Corrupt TX proposal received from:', senderId, e.toString());
m = null;
}
if (m) {
if (m.hasChanged) {
m.txp.setSeen(this.getMyCopayerId());
this.sendSeen(m.ntxid);
@ -464,8 +462,8 @@ Wallet.prototype.updateTimestamp = function(ts) {
* Triggers a call to {@link Wallet#sendWalletReady}
*/
Wallet.prototype._onNoMessages = function() {
log.debug('No messages at the server. Requesting sync'); //TODO
this.sendWalletReady();
log.debug('No messages at the server. Requesting peer sync from: ' + this.lastTimestamp + 1); //TODO
this.sendWalletReady(null, parseInt((this.lastTimestamp + 1)/1000) ) ;
};
/**
@ -500,9 +498,10 @@ Wallet.prototype._onData = function(senderId, data, ts) {
break;
case 'walletReady':
if (this.lastMessageFrom[senderId] !== 'walletReady') {
log.debug('peer Sync received. since: ' + (data.sinceTs||0));
this.sendPublicKeyRing(senderId);
this.sendAddressBook(senderId);
this.sendAllTxProposals(senderId); // send old txps
this.sendAllTxProposals(senderId, data.sinceTs); // send old txps
}
break;
case 'publicKeyRing':
@ -633,7 +632,7 @@ Wallet.decodeSecret = function(secretB) {
var secretNumber = secret.slice(33, 38);
return {
pubKey: pubKeyBuf.toString('hex'),
secretNumber : secretNumber.toString('hex')
secretNumber: secretNumber.toString('hex')
}
};
@ -645,6 +644,31 @@ Wallet.prototype._lockIncomming = function() {
this.network.lockIncommingConnections(this.publicKeyRing.getAllCopayerIds());
};
Wallet.prototype._setBlockchainListeners = function() {
var self = this;
this.blockchain.removeAllListeners();
this.blockchain.on('reconnect', function(attempts) {
log.debug('blockchain reconnect event');
self.emit('insightReconnected');
});
this.blockchain.on('disconnect', function() {
log.debug('blockchain disconnect event');
self.emit('insightError');
});
this.blockchain.on('tx', function(tx) {
log.debug('blockchain tx event');
self.emit('tx', tx.address);
});
if (!self.spendUnconfirmed) {
self.blockchain.on('block', self.emit.bind(self, 'balanceUpdated'));
}
}
/**
* @desc Sets up the networking with other peers.
*
@ -682,7 +706,12 @@ Wallet.prototype.netStart = function() {
this._lockIncomming();
}
net.on('connect_error', function() {
self.emit('connectionError');
});
net.start(startOpts, function() {
self._setBlockchainListeners();
self.emit('ready', net.getPeer());
setTimeout(function() {
self.emit('publicKeyRingUpdated', true);
@ -864,11 +893,12 @@ Wallet.prototype.send = function(recipients, obj) {
* @desc Send the set of TxProposals to some peers
* @param {string[]} recipients - the pubkeys of the recipients
*/
Wallet.prototype.sendAllTxProposals = function(recipients) {
var ntxids = this.txProposals.getNtxids(),
that = this;
Wallet.prototype.sendAllTxProposals = function(recipients, sinceTs) {
var ntxids = sinceTs ? this.txProposals.getNtxidsSince(sinceTs) : this.txProposals.getNtxids();
var self = this;
_.each(ntxids, function(ntxid, key) {
that.sendTxProposal(ntxid, recipients);
self.sendTxProposal(ntxid, recipients);
});
};
@ -919,12 +949,13 @@ Wallet.prototype.sendReject = function(ntxid) {
* @desc Notify other peers that a wallet has been backed up and it's ready to be used
* @param {string[]=} recipients - the pubkeys of the recipients
*/
Wallet.prototype.sendWalletReady = function(recipients) {
Wallet.prototype.sendWalletReady = function(recipients, sinceTs) {
log.debug('### SENDING WalletReady TO:', recipients || 'All');
this.send(recipients, {
type: 'walletReady',
walletId: this.id,
sinceTs: sinceTs,
});
};
@ -1864,7 +1895,17 @@ Wallet.prototype.getAddressesStr = function(opts) {
* @desc Alias for {@link PublicKeyRing#getAddressesInfo}
*/
Wallet.prototype.getAddressesInfo = function(opts) {
return this.publicKeyRing.getAddressesInfo(opts, this.publicKey);
var addrInfo = this.publicKeyRing.getAddressesInfo(opts, this.publicKey);
var currentAddrs = this.blockchain.getSubscriptions();
var newAddrs = [];
for (var i in addrInfo) {
var a = addrInfo[i];
if (!currentAddrs[a.addressStr] && !a.isChange)
newAddrs.push(a.addressStr);
}
this.blockchain.subscribe(newAddrs);
return addrInfo;
};
/**
* @desc Returns true if a given address was generated by deriving our master public key
@ -1872,7 +1913,9 @@ Wallet.prototype.getAddressesInfo = function(opts) {
*/
Wallet.prototype.addressIsOwn = function(addrStr, opts) {
var addrList = this.getAddressesStr(opts);
return _.any(addrList, function(value) { return value === addrStr; });
return _.any(addrList, function(value) {
return value === addrStr;
});
};
@ -1973,7 +2016,7 @@ Wallet.prototype.getUnspent = function(cb) {
Wallet.prototype.removeTxWithSpentInputs = function(cb) {
var self = this;
cb = cb || function () {};
cb = cb || function() {};
var txps = [];
var maxRejectCount = this.maxRejectCount();
@ -1986,9 +2029,13 @@ Wallet.prototype.removeTxWithSpentInputs = function(cb) {
}
var inputs = [];
txps.forEach(function (txp) {
txp.builder.utxos.forEach(function (utxo) {
inputs.push({ ntxid: txp.ntxid, txid: utxo.txid, vout: utxo.vout });
txps.forEach(function(txp) {
txp.builder.utxos.forEach(function(utxo) {
inputs.push({
ntxid: txp.ntxid,
txid: utxo.txid,
vout: utxo.vout
});
});
});
if (inputs.length === 0)
@ -1999,13 +2046,13 @@ Wallet.prototype.removeTxWithSpentInputs = function(cb) {
this.blockchain.getUnspent(this.getAddressesStr(), function(err, unspentList) {
if (err) return cb(err);
unspentList.forEach(function (unspent) {
inputs.forEach(function (input) {
unspentList.forEach(function(unspent) {
inputs.forEach(function(input) {
input.unspent = input.unspent || (input.txid === unspent.txid && input.vout === unspent.vout);
});
});
inputs.forEach(function (input) {
inputs.forEach(function(input) {
if (!input.unspent) {
proposalsChanged = true;
self.txProposals.deleteOne(input.ntxid);

View File

@ -33,14 +33,14 @@ Network.prototype.cleanUp = function() {
this.allowedCopayerIds = null;
this.isInboundPeerAuth = [];
this.copayerForPeer = {};
this.connections = {};
this.criticalErr = '';
this.removeAllListeners();
if (this.socket) {
this.socket.removeAllListeners();
log.info('Async DISCONNECT');
this.socket.disconnect();
this.socket.removeAllListeners();
this.socket = null;
}
this.removeAllListeners();
};
Network.parent = EventEmitter;
@ -86,11 +86,6 @@ Network.prototype._sendHello = function(copayerId,secretNumber) {
Network.prototype._deletePeer = function(peerId) {
delete this.isInboundPeerAuth[peerId];
delete this.copayerForPeer[peerId];
if (this.connections[peerId]) {
this.connections[peerId].close();
}
delete this.connections[peerId];
this.connectedPeers = Network._arrayRemove(peerId, this.connectedPeers);
};
@ -219,9 +214,27 @@ Network.prototype._onMessage = function(enc) {
}
};
Network.prototype._setupConnectionHandlers = function(cb) {
Network.prototype._setupConnectionHandlers = function(opts, cb) {
preconditions.checkState(this.socket);
var self = this;
self.socket.on('connect_error', function(m) {
// If socket is not started, destroy it and emit and error
// If it is started, socket.io will try to reconnect.
if (!self.started) {
self.emit('connect_error');
self.cleanUp();
}
});
self.socket.on('subscribed', function(m) {
var fromTs = (opts.lastTimestamp||0) + 1;
self.socket.emit('sync', fromTs);
self.started = true;
});
self.socket.on('message', function(m) {
// delay execution, to improve error handling
setTimeout(function() {
@ -233,14 +246,17 @@ Network.prototype._setupConnectionHandlers = function(cb) {
self.socket.on('no messages', self.emit.bind(self, 'no messages'));
self.socket.on('connect', function() {
var pubkey = self.getKey().public.toString('hex');
self.socket.emit('subscribe', pubkey);
self.socket.on('disconnect', function() {
var pubKey = self.getKey().public.toString('hex');
self.socket.emit('subscribe', pubKey);
self.socket.emit('subscribe', pubkey);
});
if (typeof cb === 'function') cb();
});
};
Network.prototype._onError = function(err) {
@ -293,20 +309,11 @@ Network.prototype.start = function(opts, openCallback) {
if (this.started) return openCallback();
this.privkey = opts.privkey;
var pubkey = this.getKey().public.toString('hex');
this.setCopayerId(opts.copayerId);
this.maxPeers = opts.maxPeers || this.maxPeers;
this.socket = this.createSocket();
this._setupConnectionHandlers(openCallback);
this.socket.emit('subscribe', pubkey);
var fromTs = opts.lastTimestamp + 1;
var self = this;
self.socket.on('subscribed', function(m) {
self.socket.emit('sync', fromTs);
self.started = true;
});
this._setupConnectionHandlers(opts, openCallback);
};
Network.prototype.createSocket = function() {

View File

@ -40,66 +40,22 @@ angular.module('copayApp.services')
}
};
root.installStartupHandlers = function(wallet, $scope) {
wallet.on('connectionError', function() {
var message = "Looks like you are already connected to this wallet, please logout and try importing it again.";
notification.error('PeerJS Error', message);
root.installWalletHandlers = function(w, $scope) {
w.on('connectionError', function() {
var message = "Could not connect to the Insight server. Check your settings and network configuration";
notification.error('Networking Error', message);
root.onErrorDigest($scope);
});
wallet.on('ready', function() {
w.on('ready', function() {
$scope.loading = false;
});
};
root.setupRootVariables = function() {
uriHandler.register();
$rootScope.unitName = config.unitName;
$rootScope.txAlertCount = 0;
$rootScope.reconnecting = false;
$rootScope.isCollapsed = true;
$rootScope.$watch('txAlertCount', function(txAlertCount) {
if (txAlertCount && txAlertCount > 0) {
notification.info('New Transaction', ($rootScope.txAlertCount == 1) ? 'You have a pending transaction proposal' : 'You have ' + $rootScope.txAlertCount + ' pending transaction proposals', txAlertCount);
}
});
$rootScope.$watch('receivedFund', function(receivedFund) {
if (receivedFund) {
var currentAddr;
for (var i = 0; i < $rootScope.addrInfos.length; i++) {
var addrinfo = $rootScope.addrInfos[i];
if (addrinfo.address.toString() == receivedFund[1] && !addrinfo.isChange) {
currentAddr = addrinfo.address.toString();
break;
}
}
if (currentAddr) {
//var beep = new Audio('sound/transaction.mp3');
notification.funds('Received fund', currentAddr, receivedFund);
//beep.play();
}
}
});
};
root.startNetwork = function(w, $scope) {
root.setupRootVariables();
root.installStartupHandlers(w, $scope);
root.updateGlobalAddresses();
notification.enableHtml5Mode(); // for chrome: if support, enable it
w.on('corrupt', function(peerId) {
notification.error('Error', 'Received corrupt message from ' + peerId);
});
w.on('ready', function(myPeerID) {
$rootScope.wallet = w;
root.setConnectionListeners($rootScope.wallet);
if ($rootScope.pendingPayment) {
$location.path('send');
} else {
@ -108,11 +64,38 @@ angular.module('copayApp.services')
});
w.on('publicKeyRingUpdated', function(dontDigest) {
root.updateGlobalAddresses();
root.updateAddressList();
if (!dontDigest) {
$rootScope.$digest();
}
});
w.on('tx', function(address) {
notification.funds('Funds received!', address);
root.updateBalance(function() {
$rootScope.$digest();
});
});
w.on('balanceUpdated', function() {
root.updateBalance(function() {
$rootScope.$digest();
});
});
w.on('insightReconnected', function() {
$rootScope.reconnecting = false;
root.updateAddressList();
root.updateBalance(function() {
$rootScope.$digest();
});
});
w.on('insightError', function() {
$rootScope.reconnecting = true;
$rootScope.$digest();
});
w.on('txProposalsUpdated', function(dontDigest) {
root.updateTxs();
// give sometime to the tx to propagate.
@ -125,6 +108,7 @@ angular.module('copayApp.services')
}, 3000);
});
w.on('txProposalEvent', function(e) {
var user = w.publicKeyRing.nicknameForCopayer(e.cId);
switch (e.type) {
case 'signed':
@ -143,17 +127,36 @@ angular.module('copayApp.services')
$rootScope.$digest();
}
});
w.on('connectionError', function(msg) {
root.onErrorDigest(null, msg);
});
w.on('connect', function(peerID) {
$rootScope.$digest();
});
w.on('close', root.onErrorDigest);
w.on('locked', root.onErrorDigest.bind(this));
};
root.setupRootVariables = function() {
uriHandler.register();
$rootScope.unitName = config.unitName;
$rootScope.txAlertCount = 0;
$rootScope.reconnecting = false;
$rootScope.isCollapsed = true;
$rootScope.$watch('txAlertCount', function(txAlertCount) {
if (txAlertCount && txAlertCount > 0) {
notification.info('New Transaction', ($rootScope.txAlertCount == 1) ? 'You have a pending transaction proposal' : 'You have ' + $rootScope.txAlertCount + ' pending transaction proposals', txAlertCount);
}
});
};
root.startNetwork = function(w, $scope) {
root.setupRootVariables();
root.installWalletHandlers(w, $scope);
root.updateAddressList();
notification.enableHtml5Mode(); // for chrome: if support, enable it
w.netStart();
};
// TODO movie this to wallet
root.updateAddressList = function() {
var w = $rootScope.wallet;
if (w && w.isReady())
@ -272,53 +275,5 @@ angular.module('copayApp.services')
});
}
root.setConnectionListeners = function(wallet) {
wallet.blockchain.on('connect', function(attempts) {
if (attempts == 0) return;
notification.success('Networking restored', 'Connection to Insight re-established');
$rootScope.reconnecting = false;
root.updateBalance(function() {
$rootScope.$digest();
});
});
wallet.blockchain.on('disconnect', function() {
notification.error('Networking problem', 'Connection to Insight lost, trying to reconnect...');
$rootScope.reconnecting = true;
$rootScope.$digest();
});
wallet.blockchain.on('tx', function(tx) {
notification.funds('Funds received!', tx.address);
root.updateBalance(function() {
$rootScope.$digest();
});
});
if (!$rootScope.wallet.spendUnconfirmed) {
wallet.blockchain.on('block', function(block) {
root.updateBalance(function() {
$rootScope.$digest();
});
});
}
}
root.updateGlobalAddresses = function() {
if (!$rootScope.wallet) return;
root.updateAddressList();
var currentAddrs = $rootScope.wallet.blockchain.getSubscriptions();
var allAddrs = $rootScope.addrInfos;
var newAddrs = [];
for (var i in allAddrs) {
var a = allAddrs[i];
if (!currentAddrs[a.addressStr] && !a.isChange)
newAddrs.push(a.addressStr);
}
$rootScope.wallet.blockchain.subscribe(newAddrs);
};
return root;
});

View File

@ -30,9 +30,16 @@ FakeSocket.prototype.removeEventListener = function() {
return;
}
FakeSocket.prototype.destroy = function() {
this.connected = false;
this.removeAllListeners();
};
module.exports = FakeSocket;
FakeSocket.prototype.disconnect = function() {
this.destroy();
};
module.exports = FakeSocket;

View File

@ -1400,31 +1400,38 @@ describe('Wallet model', function() {
var txp = {
'txProposal': txp
};
var merge = sinon.stub(w.txProposals, 'merge', function() {
if (response == 0) throw new Error();
var s1 = sinon.stub(w, '_getKeyMap', function() {
return {1:2};
});
var s2 = sinon.stub(w.txProposals, 'merge', function() {
if (response == 0)
throw new Error('test error');
return {
newCopayer: ['juan'],
ntxid: 1,
txp: {
setCopayers: function() {return ['oeoe']; } ,
},
new: response == 1
};
});
w._onTxProposal('senderID', txp);
spy.callCount.should.equal(1);
merge.restore();
s1.restore();
s2.restore();
};
it('should handle corrupt', function(done) {
var result = 'corrupt';
testValidate(0, result, done);
testValidate(0, 'corrupt', done);
});
it('should handle new', function(done) {
var result = 'new';
testValidate(1, result, done);
testValidate(1, 'new', done);
});
it('should handle signed', function(done) {
var result = 'signed';
testValidate(2, result, done);
testValidate(2, 'signed', done);
});
});

View File

@ -48,13 +48,13 @@ var FAKE_OPTS = {
describe('Insight model', function() {
before(function() {
sinon.stub(Insight.prototype, "getSocket", function() {
sinon.stub(Insight.prototype, "_getSocketIO", function() {
return new FakeSocket();
});
});
after(function() {
Insight.prototype.getSocket.restore();
Insight.prototype._getSocketIO.restore();
});
it('should create an instance', function() {
@ -65,7 +65,8 @@ describe('Insight model', function() {
it('should subscribe to inventory', function(done) {
var blockchain = new Insight(FAKE_OPTS);
var emitSpy = sinon.spy(blockchain.socket, 'emit');
var socket = blockchain.getSocket();
var emitSpy = sinon.spy(socket, 'emit');
blockchain.on('connect', function() {
emitSpy.calledWith('subscribe', 'inv');
done();
@ -75,11 +76,12 @@ describe('Insight model', function() {
it('should be able to destroy the instance', function(done) {
var blockchain = new Insight(FAKE_OPTS);
blockchain.status.should.be.equal('disconnected');
var socket = blockchain.getSocket();
blockchain.on('connect', function() {
blockchain.subscribe('mg7UbtKgMvWAixTNMbC8soyUnwFk1qxEuM');
blockchain.getSubscriptions().length.should.equal(1);
Object.keys(blockchain.getSubscriptions()).length.should.equal(1);
blockchain.destroy();
blockchain.getSubscriptions().length.should.equal(0);
Object.keys(blockchain.getSubscriptions()).length.should.equal(0);
blockchain.status.should.be.equal('destroyed');
done();
});
@ -87,10 +89,11 @@ describe('Insight model', function() {
it('should subscribe to an address', function() {
var blockchain = new Insight(FAKE_OPTS);
var emitSpy = sinon.spy(blockchain.socket, 'emit');
var socket = blockchain.getSocket();
var emitSpy = sinon.spy(socket, 'emit');
blockchain.subscribe('mg7UbtKgMvWAixTNMbC8soyUnwFk1qxEuM');
blockchain.getSubscriptions().length.should.equal(1);
Object.keys(blockchain.getSubscriptions()).length.should.equal(1);
emitSpy.calledWith('subscribe', 'mg7UbtKgMvWAixTNMbC8soyUnwFk1qxEuM');
});
@ -99,38 +102,31 @@ describe('Insight model', function() {
blockchain.subscribe('mg7UbtKgMvWAixTNMbC8soyUnwFk1qxEuM');
blockchain.subscribe('mg7UbtKgMvWAixTNMbC8soyUnwFk1qxEuM');
blockchain.getSubscriptions().length.should.equal(1);
Object.keys(blockchain.getSubscriptions()).length.should.equal(1);
});
it('should subscribe to a list of addresses', function() {
var blockchain = new Insight(FAKE_OPTS);
var emitSpy = sinon.spy(blockchain.socket, 'emit');
var socket = blockchain.getSocket();
var emitSpy = sinon.spy(socket, 'emit');
blockchain.subscribe([
'mg7UbtKgMvWAixTNMbC8soyUnwFk1qxEuM',
'2NBBHBjB5sd7HFqKtout1L7d6dPhwJgP2j8'
]);
blockchain.getSubscriptions().length.should.equal(2);
Object.keys(blockchain.getSubscriptions()).length.should.equal(2);
emitSpy.calledWith('subscribe', 'mg7UbtKgMvWAixTNMbC8soyUnwFk1qxEuM');
emitSpy.calledWith('subscribe', '2NBBHBjB5sd7HFqKtout1L7d6dPhwJgP2j8');
});
it('should unsubscribe to an address', function() {
var blockchain = new Insight(FAKE_OPTS);
blockchain.subscribe('mg7UbtKgMvWAixTNMbC8soyUnwFk1qxEuM');
blockchain.getSubscriptions().length.should.equal(1);
blockchain.unsubscribe('mg7UbtKgMvWAixTNMbC8soyUnwFk1qxEuM');
blockchain.getSubscriptions().length.should.equal(0);
});
it('should unsubscribe to all addresses', function() {
it('should resubscribe to all addresses', function() {
var blockchain = new Insight(FAKE_OPTS);
blockchain.subscribe('mg7UbtKgMvWAixTNMbC8soyUnwFk1qxEuM');
blockchain.subscribe('2NBBHBjB5sd7HFqKtout1L7d6dPhwJgP2j8');
blockchain.getSubscriptions().length.should.equal(2);
Object.keys(blockchain.getSubscriptions()).length.should.equal(2);
blockchain.unsubscribeAll('mg7UbtKgMvWAixTNMbC8soyUnwFk1qxEuM');
blockchain.getSubscriptions().length.should.equal(0);
blockchain.reSubscribe();
Object.keys(blockchain.getSubscriptions()).length.should.equal(2);
});
it('should broadcast a raw transaction', function(done) {
@ -354,8 +350,10 @@ describe('Insight model', function() {
describe('Events', function() {
it('should emmit event on a new block', function(done) {
var blockchain = new Insight(FAKE_OPTS);
var socket = blockchain.getSocket();
blockchain.on('connect', function() {
blockchain.socket.emit('block', '12312312');
var socket = blockchain.getSocket();
socket.emit('block', '12312312');
});
blockchain.on('block', function(blockid) {
@ -364,11 +362,13 @@ describe('Insight model', function() {
});
});
it('should emmit event on a transaction for subscried addresses', function(done) {
it('should emmit event on a transaction for subscribed addresses', function(done) {
var blockchain = new Insight(FAKE_OPTS);
var socket = blockchain.getSocket();
blockchain.subscribe('2NFjCBFZSsxiwWAD7CKQ3hzWFtf9DcqTucY');
blockchain.on('connect', function() {
blockchain.socket.emit('2NFjCBFZSsxiwWAD7CKQ3hzWFtf9DcqTucY', '1123');
var socket = blockchain.getSocket();
socket.emit('2NFjCBFZSsxiwWAD7CKQ3hzWFtf9DcqTucY', '1123');
});
blockchain.on('tx', function(ev) {
@ -380,8 +380,10 @@ describe('Insight model', function() {
it('should\'t emmit event on a transaction for non subscribed addresses', function(done) {
var blockchain = new Insight(FAKE_OPTS);
var socket = blockchain.getSocket();
blockchain.on('connect', function() {
blockchain.socket.emit('2NFjCBFZSsxiwWAD7CKQ3hzWFtf9DcqTucY', '1123');
var socket = blockchain.getSocket();
socket.emit('2NFjCBFZSsxiwWAD7CKQ3hzWFtf9DcqTucY', '1123');
setTimeout(function() { done(); }, 20);
});
@ -392,6 +394,7 @@ describe('Insight model', function() {
it('should emmit event on connection', function(done) {
var blockchain = new Insight(FAKE_OPTS);
var socket = blockchain.getSocket();
blockchain.on('connect', function() {
done();
});
@ -399,8 +402,10 @@ describe('Insight model', function() {
it('should emmit event on disconnection', function(done) {
var blockchain = new Insight(FAKE_OPTS);
var socket = blockchain.getSocket();
blockchain.on('connect', function() {
blockchain.socket.emit('connect_error');
var socket = blockchain.getSocket();
socket.emit('connect_error');
});
blockchain.on('disconnect', function() {
done();

View File

@ -62,23 +62,6 @@ describe("Unit: controllerUtils", function() {
expect($rootScope.unitName).to.be.equal('bits');
});
}));
describe("Unit: controllerUtils #updateGlobalAddresses", function() {
it(' should call updateAddressList ', inject(function(controllerUtils, $rootScope) {
$rootScope.wallet = new FakeWallet();
var spy = sinon.spy(controllerUtils, 'updateAddressList');
controllerUtils.updateGlobalAddresses();
sinon.assert.callCount(spy, 1);
}));
it('should update addresses', inject(function(controllerUtils, $rootScope) {
$rootScope.wallet = new FakeWallet();
var Waddr = Object.keys($rootScope.wallet.balanceByAddr)[0];
controllerUtils.updateGlobalAddresses();
expect($rootScope.addrInfos[0].address).to.be.equal(Waddr);;
}));
});
});
describe("Unit: Notification Service", function() {

View File

@ -1,7 +1,7 @@
<div class="open" ng-controller="OpenController">
<div data-alert class="loading-screen" ng-show="loading && !failure">
<i class="size-60 fi-bitcoin-circle icon-rotate spinner"></i>
<span translate>Authenticating and looking for peers...</span>
<span translate>Connecting...</span>
</div>
<div class="row" ng-show="!loading">
<div class="large-4 columns logo-setup">