mirror of https://github.com/BTCPrivate/copay.git
Merge pull request #1332 from matiu/insight-reconnect
Insight reconnect
This commit is contained in:
commit
9d142cbff7
|
@ -15,7 +15,7 @@
|
||||||
<div class="inner-wrap">
|
<div class="inner-wrap">
|
||||||
<span class="status" ng-if="$root.reconnecting">
|
<span class="status" ng-if="$root.reconnecting">
|
||||||
<i class="fi-loop icon-rotate m15r"></i>
|
<i class="fi-loop icon-rotate m15r"></i>
|
||||||
Attempting to reconnect...
|
<span translate> Network Error. Attempting to reconnect...</span>
|
||||||
</span>
|
</span>
|
||||||
<nav class="tab-bar" ng-class="{'hide-tab-bar' : !$root.wallet ||
|
<nav class="tab-bar" ng-class="{'hide-tab-bar' : !$root.wallet ||
|
||||||
!$root.wallet.isReady() || $root.wallet.isLocked}">
|
!$root.wallet.isReady() || $root.wallet.isLocked}">
|
||||||
|
|
|
@ -9,7 +9,7 @@ angular.module('copayApp.controllers').controller('AddressesController',
|
||||||
$scope.loading = true;
|
$scope.loading = true;
|
||||||
w.generateAddress(null, function() {
|
w.generateAddress(null, function() {
|
||||||
$timeout(function() {
|
$timeout(function() {
|
||||||
controllerUtils.updateGlobalAddresses();
|
controllerUtils.updateAddressList();
|
||||||
$scope.loading = false;
|
$scope.loading = false;
|
||||||
}, 1);
|
}, 1);
|
||||||
});
|
});
|
||||||
|
|
|
@ -17,7 +17,7 @@ angular.module('copayApp.controllers').controller('CopayersController',
|
||||||
}
|
}
|
||||||
|
|
||||||
$scope.goToWallet = function() {
|
$scope.goToWallet = function() {
|
||||||
controllerUtils.updateGlobalAddresses();
|
controllerUtils.updateAddressList();
|
||||||
$location.path('/receive');
|
$location.path('/receive');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -101,5 +101,5 @@ Logger.prototype.setLevel = function(level) {
|
||||||
|
|
||||||
var logger = new Logger('copay');
|
var logger = new Logger('copay');
|
||||||
logger.setLevel(config.logLevel);
|
logger.setLevel(config.logLevel);
|
||||||
|
logger.log('Log level:' + config.logLevel);
|
||||||
module.exports = logger;
|
module.exports = logger;
|
||||||
|
|
|
@ -5,6 +5,7 @@ var async = require('async');
|
||||||
var request = require('request');
|
var request = require('request');
|
||||||
var bitcore = require('bitcore');
|
var bitcore = require('bitcore');
|
||||||
var io = require('socket.io-client');
|
var io = require('socket.io-client');
|
||||||
|
var log = require('../../log');
|
||||||
|
|
||||||
var EventEmitter = require('events').EventEmitter;
|
var EventEmitter = require('events').EventEmitter;
|
||||||
var preconditions = require('preconditions').singleton();
|
var preconditions = require('preconditions').singleton();
|
||||||
|
@ -27,15 +28,15 @@ var preconditions = require('preconditions').singleton();
|
||||||
- disconnect: the connection with the blochckain is unavailable.
|
- disconnect: the connection with the blochckain is unavailable.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var Insight = function (opts) {
|
var Insight = function(opts) {
|
||||||
this.status = this.STATUS.DISCONNECTED;
|
this.status = this.STATUS.DISCONNECTED;
|
||||||
this.subscribed = {};
|
this.subscribed = {};
|
||||||
this.listeningBlocks = false;
|
this.listeningBlocks = false;
|
||||||
|
|
||||||
preconditions.checkArgument(opts).shouldBeObject(opts)
|
preconditions.checkArgument(opts).shouldBeObject(opts)
|
||||||
.checkArgument(opts.host)
|
.checkArgument(opts.host)
|
||||||
.checkArgument(opts.port)
|
.checkArgument(opts.port)
|
||||||
.checkArgument(opts.schema);
|
.checkArgument(opts.schema);
|
||||||
|
|
||||||
this.url = opts.schema + '://' + opts.host + ':' + opts.port;
|
this.url = opts.schema + '://' + opts.host + ':' + opts.port;
|
||||||
this.opts = {
|
this.opts = {
|
||||||
|
@ -44,8 +45,36 @@ var Insight = function (opts) {
|
||||||
'secure': opts.schema === 'https'
|
'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
|
// Emmit connection events
|
||||||
var self = this;
|
var self = this;
|
||||||
this.socket.on('connect', function() {
|
this.socket.on('connect', function() {
|
||||||
|
@ -68,34 +97,21 @@ var Insight = function (opts) {
|
||||||
|
|
||||||
this.socket.on('reconnect', function(attempt) {
|
this.socket.on('reconnect', function(attempt) {
|
||||||
if (self.status != self.STATUS.DISCONNECTED) return;
|
if (self.status != self.STATUS.DISCONNECTED) return;
|
||||||
|
self.emit('reconnect', attempt);
|
||||||
|
self.reSubscribe();
|
||||||
self.status = self.STATUS.CONNECTED;
|
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 */
|
/** @private */
|
||||||
Insight.prototype.getSocket = function(url, opts) {
|
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 */
|
/** @private */
|
||||||
|
@ -107,11 +123,18 @@ Insight.prototype.request = function(path, cb) {
|
||||||
/** @private */
|
/** @private */
|
||||||
Insight.prototype.requestPost = function(path, data, cb) {
|
Insight.prototype.requestPost = function(path, data, cb) {
|
||||||
preconditions.checkArgument(path).checkArgument(data).shouldBeFunction(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() {
|
Insight.prototype.destroy = function() {
|
||||||
this.socket.destroy();
|
var socket = this.getSocket();
|
||||||
|
this.socket.disconnect();
|
||||||
|
this.socket.removeAllListeners();
|
||||||
|
this.socket = null;
|
||||||
this.subscribed = {};
|
this.subscribed = {};
|
||||||
this.status = this.STATUS.DESTROYED;
|
this.status = this.STATUS.DESTROYED;
|
||||||
this.removeAllListeners();
|
this.removeAllListeners();
|
||||||
|
@ -122,49 +145,61 @@ Insight.prototype.subscribe = function(addresses) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
function handlerFor(self, address) {
|
function handlerFor(self, address) {
|
||||||
return function (txid) {
|
return function(txid) {
|
||||||
// verify the address is still subscribed
|
// verify the address is still subscribed
|
||||||
if (!self.subscribed[address]) return;
|
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) {
|
addresses.forEach(function(address) {
|
||||||
preconditions.checkArgument(new bitcore.Address(address).isValid());
|
preconditions.checkArgument(new bitcore.Address(address).isValid());
|
||||||
|
|
||||||
// skip already subscibed
|
// skip already subscibed
|
||||||
if (!self.subscribed[address]) {
|
if (!self.subscribed[address]) {
|
||||||
self.subscribed[address] = true;
|
var handler = handlerFor(self, address);
|
||||||
self.socket.emit('subscribe', address);
|
self.subscribed[address] = handler;
|
||||||
self.socket.on(address, handlerFor(self, address));
|
log.debug('Subcribe to: ', address);
|
||||||
|
|
||||||
|
s.emit('subscribe', address);
|
||||||
|
s.on(address, handler);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
Insight.prototype.getSubscriptions = function(addresses) {
|
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) {
|
Insight.prototype.reSubscribe = function() {
|
||||||
preconditions.checkArgument(new bitcore.Address(address).isValid());
|
log.debug('insight reSubscribe');
|
||||||
self.socket.removeEventListener(address);
|
var allAddresses = Object.keys(this.subscribed);
|
||||||
delete self.subscribed[address];
|
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) {
|
Insight.prototype.broadcast = function(rawtx, cb) {
|
||||||
preconditions.checkArgument(rawtx);
|
preconditions.checkArgument(rawtx);
|
||||||
preconditions.shouldBeFunction(cb);
|
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);
|
if (err || res.statusCode != 200) cb(err || res);
|
||||||
cb(null, body.txid);
|
cb(null, body.txid);
|
||||||
});
|
});
|
||||||
|
@ -218,7 +253,9 @@ Insight.prototype.getUnspent = function(addresses, cb) {
|
||||||
preconditions.shouldBeArray(addresses);
|
preconditions.shouldBeArray(addresses);
|
||||||
preconditions.shouldBeFunction(cb);
|
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);
|
if (err || res.statusCode != 200) return cb(err || res);
|
||||||
cb(null, body);
|
cb(null, body);
|
||||||
});
|
});
|
||||||
|
@ -243,8 +280,8 @@ Insight.prototype.getActivity = function(addresses, cb) {
|
||||||
var getOutputs = function(t) {
|
var getOutputs = function(t) {
|
||||||
return flatArray(
|
return flatArray(
|
||||||
t.vout.map(function(vout) {
|
t.vout.map(function(vout) {
|
||||||
return vout.scriptPubKey.addresses;
|
return vout.scriptPubKey.addresses;
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -38,6 +38,22 @@ TxProposals.prototype.length = function() {
|
||||||
return Object.keys(this.txps).length;
|
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() {
|
TxProposals.prototype.getNtxids = function() {
|
||||||
return Object.keys(this.txps);
|
return Object.keys(this.txps);
|
||||||
};
|
};
|
||||||
|
|
|
@ -222,12 +222,12 @@ Wallet.prototype._processProposalEvents = function(senderId, m) {
|
||||||
if (m.new) {
|
if (m.new) {
|
||||||
ev = {
|
ev = {
|
||||||
type: 'new',
|
type: 'new',
|
||||||
cid: senderId
|
cId: senderId
|
||||||
}
|
}
|
||||||
} else if (m.newCopayer) {
|
} else if (m.newCopayer.length) {
|
||||||
ev = {
|
ev = {
|
||||||
type: 'signed',
|
type: 'signed',
|
||||||
cid: m.newCopayer
|
cId: m.newCopayer[0]
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -236,7 +236,6 @@ Wallet.prototype._processProposalEvents = function(senderId, m) {
|
||||||
cId: senderId,
|
cId: senderId,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ev)
|
if (ev)
|
||||||
this.emit('txProposalEvent', ev);
|
this.emit('txProposalEvent', ev);
|
||||||
};
|
};
|
||||||
|
@ -328,14 +327,13 @@ Wallet.prototype._onTxProposal = function(senderId, data) {
|
||||||
try {
|
try {
|
||||||
m = this.txProposals.merge(data.txProposal, Wallet.builderOpts);
|
m = this.txProposals.merge(data.txProposal, Wallet.builderOpts);
|
||||||
var keyMap = this._getKeyMap(m.txp);
|
var keyMap = this._getKeyMap(m.txp);
|
||||||
ret.newCopayer = m.txp.setCopayers(senderId, keyMap);
|
m.newCopayer = m.txp.setCopayers(senderId, keyMap);
|
||||||
|
|
||||||
} catch (e) {
|
} 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) {
|
||||||
|
|
||||||
if (m.hasChanged) {
|
if (m.hasChanged) {
|
||||||
m.txp.setSeen(this.getMyCopayerId());
|
m.txp.setSeen(this.getMyCopayerId());
|
||||||
this.sendSeen(m.ntxid);
|
this.sendSeen(m.ntxid);
|
||||||
|
@ -464,8 +462,8 @@ Wallet.prototype.updateTimestamp = function(ts) {
|
||||||
* Triggers a call to {@link Wallet#sendWalletReady}
|
* Triggers a call to {@link Wallet#sendWalletReady}
|
||||||
*/
|
*/
|
||||||
Wallet.prototype._onNoMessages = function() {
|
Wallet.prototype._onNoMessages = function() {
|
||||||
log.debug('No messages at the server. Requesting sync'); //TODO
|
log.debug('No messages at the server. Requesting peer sync from: ' + this.lastTimestamp + 1); //TODO
|
||||||
this.sendWalletReady();
|
this.sendWalletReady(null, parseInt((this.lastTimestamp + 1)/1000) ) ;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -500,9 +498,10 @@ Wallet.prototype._onData = function(senderId, data, ts) {
|
||||||
break;
|
break;
|
||||||
case 'walletReady':
|
case 'walletReady':
|
||||||
if (this.lastMessageFrom[senderId] !== 'walletReady') {
|
if (this.lastMessageFrom[senderId] !== 'walletReady') {
|
||||||
|
log.debug('peer Sync received. since: ' + (data.sinceTs||0));
|
||||||
this.sendPublicKeyRing(senderId);
|
this.sendPublicKeyRing(senderId);
|
||||||
this.sendAddressBook(senderId);
|
this.sendAddressBook(senderId);
|
||||||
this.sendAllTxProposals(senderId); // send old txps
|
this.sendAllTxProposals(senderId, data.sinceTs); // send old txps
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'publicKeyRing':
|
case 'publicKeyRing':
|
||||||
|
@ -633,7 +632,7 @@ Wallet.decodeSecret = function(secretB) {
|
||||||
var secretNumber = secret.slice(33, 38);
|
var secretNumber = secret.slice(33, 38);
|
||||||
return {
|
return {
|
||||||
pubKey: pubKeyBuf.toString('hex'),
|
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());
|
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.
|
* @desc Sets up the networking with other peers.
|
||||||
*
|
*
|
||||||
|
@ -682,7 +706,12 @@ Wallet.prototype.netStart = function() {
|
||||||
this._lockIncomming();
|
this._lockIncomming();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
net.on('connect_error', function() {
|
||||||
|
self.emit('connectionError');
|
||||||
|
});
|
||||||
|
|
||||||
net.start(startOpts, function() {
|
net.start(startOpts, function() {
|
||||||
|
self._setBlockchainListeners();
|
||||||
self.emit('ready', net.getPeer());
|
self.emit('ready', net.getPeer());
|
||||||
setTimeout(function() {
|
setTimeout(function() {
|
||||||
self.emit('publicKeyRingUpdated', true);
|
self.emit('publicKeyRingUpdated', true);
|
||||||
|
@ -864,11 +893,12 @@ Wallet.prototype.send = function(recipients, obj) {
|
||||||
* @desc Send the set of TxProposals to some peers
|
* @desc Send the set of TxProposals to some peers
|
||||||
* @param {string[]} recipients - the pubkeys of the recipients
|
* @param {string[]} recipients - the pubkeys of the recipients
|
||||||
*/
|
*/
|
||||||
Wallet.prototype.sendAllTxProposals = function(recipients) {
|
Wallet.prototype.sendAllTxProposals = function(recipients, sinceTs) {
|
||||||
var ntxids = this.txProposals.getNtxids(),
|
var ntxids = sinceTs ? this.txProposals.getNtxidsSince(sinceTs) : this.txProposals.getNtxids();
|
||||||
that = this;
|
var self = this;
|
||||||
|
|
||||||
_.each(ntxids, function(ntxid, key) {
|
_.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
|
* @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
|
* @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');
|
log.debug('### SENDING WalletReady TO:', recipients || 'All');
|
||||||
|
|
||||||
this.send(recipients, {
|
this.send(recipients, {
|
||||||
type: 'walletReady',
|
type: 'walletReady',
|
||||||
walletId: this.id,
|
walletId: this.id,
|
||||||
|
sinceTs: sinceTs,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1864,7 +1895,17 @@ Wallet.prototype.getAddressesStr = function(opts) {
|
||||||
* @desc Alias for {@link PublicKeyRing#getAddressesInfo}
|
* @desc Alias for {@link PublicKeyRing#getAddressesInfo}
|
||||||
*/
|
*/
|
||||||
Wallet.prototype.getAddressesInfo = function(opts) {
|
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
|
* @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) {
|
Wallet.prototype.addressIsOwn = function(addrStr, opts) {
|
||||||
var addrList = this.getAddressesStr(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) {
|
Wallet.prototype.removeTxWithSpentInputs = function(cb) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
cb = cb || function () {};
|
cb = cb || function() {};
|
||||||
|
|
||||||
var txps = [];
|
var txps = [];
|
||||||
var maxRejectCount = this.maxRejectCount();
|
var maxRejectCount = this.maxRejectCount();
|
||||||
|
@ -1986,9 +2029,13 @@ Wallet.prototype.removeTxWithSpentInputs = function(cb) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var inputs = [];
|
var inputs = [];
|
||||||
txps.forEach(function (txp) {
|
txps.forEach(function(txp) {
|
||||||
txp.builder.utxos.forEach(function (utxo) {
|
txp.builder.utxos.forEach(function(utxo) {
|
||||||
inputs.push({ ntxid: txp.ntxid, txid: utxo.txid, vout: utxo.vout });
|
inputs.push({
|
||||||
|
ntxid: txp.ntxid,
|
||||||
|
txid: utxo.txid,
|
||||||
|
vout: utxo.vout
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
if (inputs.length === 0)
|
if (inputs.length === 0)
|
||||||
|
@ -1999,13 +2046,13 @@ Wallet.prototype.removeTxWithSpentInputs = function(cb) {
|
||||||
this.blockchain.getUnspent(this.getAddressesStr(), function(err, unspentList) {
|
this.blockchain.getUnspent(this.getAddressesStr(), function(err, unspentList) {
|
||||||
if (err) return cb(err);
|
if (err) return cb(err);
|
||||||
|
|
||||||
unspentList.forEach(function (unspent) {
|
unspentList.forEach(function(unspent) {
|
||||||
inputs.forEach(function (input) {
|
inputs.forEach(function(input) {
|
||||||
input.unspent = input.unspent || (input.txid === unspent.txid && input.vout === unspent.vout);
|
input.unspent = input.unspent || (input.txid === unspent.txid && input.vout === unspent.vout);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
inputs.forEach(function (input) {
|
inputs.forEach(function(input) {
|
||||||
if (!input.unspent) {
|
if (!input.unspent) {
|
||||||
proposalsChanged = true;
|
proposalsChanged = true;
|
||||||
self.txProposals.deleteOne(input.ntxid);
|
self.txProposals.deleteOne(input.ntxid);
|
||||||
|
|
|
@ -33,14 +33,14 @@ Network.prototype.cleanUp = function() {
|
||||||
this.allowedCopayerIds = null;
|
this.allowedCopayerIds = null;
|
||||||
this.isInboundPeerAuth = [];
|
this.isInboundPeerAuth = [];
|
||||||
this.copayerForPeer = {};
|
this.copayerForPeer = {};
|
||||||
this.connections = {};
|
|
||||||
this.criticalErr = '';
|
this.criticalErr = '';
|
||||||
this.removeAllListeners();
|
|
||||||
if (this.socket) {
|
if (this.socket) {
|
||||||
this.socket.removeAllListeners();
|
log.info('Async DISCONNECT');
|
||||||
this.socket.disconnect();
|
this.socket.disconnect();
|
||||||
|
this.socket.removeAllListeners();
|
||||||
this.socket = null;
|
this.socket = null;
|
||||||
}
|
}
|
||||||
|
this.removeAllListeners();
|
||||||
};
|
};
|
||||||
|
|
||||||
Network.parent = EventEmitter;
|
Network.parent = EventEmitter;
|
||||||
|
@ -86,11 +86,6 @@ Network.prototype._sendHello = function(copayerId,secretNumber) {
|
||||||
Network.prototype._deletePeer = function(peerId) {
|
Network.prototype._deletePeer = function(peerId) {
|
||||||
delete this.isInboundPeerAuth[peerId];
|
delete this.isInboundPeerAuth[peerId];
|
||||||
delete this.copayerForPeer[peerId];
|
delete this.copayerForPeer[peerId];
|
||||||
|
|
||||||
if (this.connections[peerId]) {
|
|
||||||
this.connections[peerId].close();
|
|
||||||
}
|
|
||||||
delete this.connections[peerId];
|
|
||||||
this.connectedPeers = Network._arrayRemove(peerId, this.connectedPeers);
|
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);
|
preconditions.checkState(this.socket);
|
||||||
var self = this;
|
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) {
|
self.socket.on('message', function(m) {
|
||||||
// delay execution, to improve error handling
|
// delay execution, to improve error handling
|
||||||
setTimeout(function() {
|
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('no messages', self.emit.bind(self, 'no messages'));
|
||||||
|
|
||||||
self.socket.on('connect', function() {
|
self.socket.on('connect', function() {
|
||||||
|
var pubkey = self.getKey().public.toString('hex');
|
||||||
|
self.socket.emit('subscribe', pubkey);
|
||||||
|
|
||||||
self.socket.on('disconnect', function() {
|
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();
|
if (typeof cb === 'function') cb();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Network.prototype._onError = function(err) {
|
Network.prototype._onError = function(err) {
|
||||||
|
@ -293,20 +309,11 @@ Network.prototype.start = function(opts, openCallback) {
|
||||||
if (this.started) return openCallback();
|
if (this.started) return openCallback();
|
||||||
|
|
||||||
this.privkey = opts.privkey;
|
this.privkey = opts.privkey;
|
||||||
var pubkey = this.getKey().public.toString('hex');
|
|
||||||
this.setCopayerId(opts.copayerId);
|
this.setCopayerId(opts.copayerId);
|
||||||
this.maxPeers = opts.maxPeers || this.maxPeers;
|
this.maxPeers = opts.maxPeers || this.maxPeers;
|
||||||
|
|
||||||
this.socket = this.createSocket();
|
this.socket = this.createSocket();
|
||||||
this._setupConnectionHandlers(openCallback);
|
this._setupConnectionHandlers(opts, 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;
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Network.prototype.createSocket = function() {
|
Network.prototype.createSocket = function() {
|
||||||
|
|
|
@ -40,66 +40,22 @@ angular.module('copayApp.services')
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
root.installStartupHandlers = function(wallet, $scope) {
|
root.installWalletHandlers = function(w, $scope) {
|
||||||
wallet.on('connectionError', function() {
|
w.on('connectionError', function() {
|
||||||
var message = "Looks like you are already connected to this wallet, please logout and try importing it again.";
|
var message = "Could not connect to the Insight server. Check your settings and network configuration";
|
||||||
notification.error('PeerJS Error', message);
|
notification.error('Networking Error', message);
|
||||||
root.onErrorDigest($scope);
|
root.onErrorDigest($scope);
|
||||||
});
|
});
|
||||||
wallet.on('ready', function() {
|
w.on('ready', function() {
|
||||||
$scope.loading = false;
|
$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) {
|
w.on('corrupt', function(peerId) {
|
||||||
notification.error('Error', 'Received corrupt message from ' + peerId);
|
notification.error('Error', 'Received corrupt message from ' + peerId);
|
||||||
});
|
});
|
||||||
w.on('ready', function(myPeerID) {
|
w.on('ready', function(myPeerID) {
|
||||||
$rootScope.wallet = w;
|
$rootScope.wallet = w;
|
||||||
root.setConnectionListeners($rootScope.wallet);
|
|
||||||
|
|
||||||
if ($rootScope.pendingPayment) {
|
if ($rootScope.pendingPayment) {
|
||||||
$location.path('send');
|
$location.path('send');
|
||||||
} else {
|
} else {
|
||||||
|
@ -108,11 +64,38 @@ angular.module('copayApp.services')
|
||||||
});
|
});
|
||||||
|
|
||||||
w.on('publicKeyRingUpdated', function(dontDigest) {
|
w.on('publicKeyRingUpdated', function(dontDigest) {
|
||||||
root.updateGlobalAddresses();
|
root.updateAddressList();
|
||||||
if (!dontDigest) {
|
if (!dontDigest) {
|
||||||
$rootScope.$digest();
|
$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) {
|
w.on('txProposalsUpdated', function(dontDigest) {
|
||||||
root.updateTxs();
|
root.updateTxs();
|
||||||
// give sometime to the tx to propagate.
|
// give sometime to the tx to propagate.
|
||||||
|
@ -125,6 +108,7 @@ angular.module('copayApp.services')
|
||||||
}, 3000);
|
}, 3000);
|
||||||
});
|
});
|
||||||
w.on('txProposalEvent', function(e) {
|
w.on('txProposalEvent', function(e) {
|
||||||
|
|
||||||
var user = w.publicKeyRing.nicknameForCopayer(e.cId);
|
var user = w.publicKeyRing.nicknameForCopayer(e.cId);
|
||||||
switch (e.type) {
|
switch (e.type) {
|
||||||
case 'signed':
|
case 'signed':
|
||||||
|
@ -143,17 +127,36 @@ angular.module('copayApp.services')
|
||||||
$rootScope.$digest();
|
$rootScope.$digest();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
w.on('connectionError', function(msg) {
|
|
||||||
root.onErrorDigest(null, msg);
|
|
||||||
});
|
|
||||||
w.on('connect', function(peerID) {
|
w.on('connect', function(peerID) {
|
||||||
$rootScope.$digest();
|
$rootScope.$digest();
|
||||||
});
|
});
|
||||||
w.on('close', root.onErrorDigest);
|
w.on('close', root.onErrorDigest);
|
||||||
w.on('locked', root.onErrorDigest.bind(this));
|
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();
|
w.netStart();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// TODO movie this to wallet
|
||||||
root.updateAddressList = function() {
|
root.updateAddressList = function() {
|
||||||
var w = $rootScope.wallet;
|
var w = $rootScope.wallet;
|
||||||
if (w && w.isReady())
|
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;
|
return root;
|
||||||
});
|
});
|
||||||
|
|
|
@ -30,9 +30,16 @@ FakeSocket.prototype.removeEventListener = function() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
FakeSocket.prototype.destroy = function() {
|
FakeSocket.prototype.destroy = function() {
|
||||||
this.connected = false;
|
this.connected = false;
|
||||||
this.removeAllListeners();
|
this.removeAllListeners();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
FakeSocket.prototype.disconnect = function() {
|
||||||
|
this.destroy();
|
||||||
|
};
|
||||||
|
|
||||||
module.exports = FakeSocket;
|
module.exports = FakeSocket;
|
|
@ -1400,31 +1400,38 @@ describe('Wallet model', function() {
|
||||||
var txp = {
|
var txp = {
|
||||||
'txProposal': 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 {
|
return {
|
||||||
newCopayer: ['juan'],
|
|
||||||
ntxid: 1,
|
ntxid: 1,
|
||||||
|
txp: {
|
||||||
|
setCopayers: function() {return ['oeoe']; } ,
|
||||||
|
},
|
||||||
new: response == 1
|
new: response == 1
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
w._onTxProposal('senderID', txp);
|
w._onTxProposal('senderID', txp);
|
||||||
spy.callCount.should.equal(1);
|
spy.callCount.should.equal(1);
|
||||||
merge.restore();
|
s1.restore();
|
||||||
|
s2.restore();
|
||||||
};
|
};
|
||||||
|
|
||||||
it('should handle corrupt', function(done) {
|
it('should handle corrupt', function(done) {
|
||||||
var result = 'corrupt';
|
testValidate(0, 'corrupt', done);
|
||||||
testValidate(0, result, done);
|
|
||||||
});
|
});
|
||||||
it('should handle new', function(done) {
|
it('should handle new', function(done) {
|
||||||
var result = 'new';
|
testValidate(1, 'new', done);
|
||||||
testValidate(1, result, done);
|
|
||||||
});
|
});
|
||||||
it('should handle signed', function(done) {
|
it('should handle signed', function(done) {
|
||||||
var result = 'signed';
|
testValidate(2, 'signed', done);
|
||||||
testValidate(2, result, done);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -48,13 +48,13 @@ var FAKE_OPTS = {
|
||||||
describe('Insight model', function() {
|
describe('Insight model', function() {
|
||||||
|
|
||||||
before(function() {
|
before(function() {
|
||||||
sinon.stub(Insight.prototype, "getSocket", function() {
|
sinon.stub(Insight.prototype, "_getSocketIO", function() {
|
||||||
return new FakeSocket();
|
return new FakeSocket();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
after(function() {
|
after(function() {
|
||||||
Insight.prototype.getSocket.restore();
|
Insight.prototype._getSocketIO.restore();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create an instance', function() {
|
it('should create an instance', function() {
|
||||||
|
@ -65,7 +65,8 @@ describe('Insight model', function() {
|
||||||
|
|
||||||
it('should subscribe to inventory', function(done) {
|
it('should subscribe to inventory', function(done) {
|
||||||
var blockchain = new Insight(FAKE_OPTS);
|
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() {
|
blockchain.on('connect', function() {
|
||||||
emitSpy.calledWith('subscribe', 'inv');
|
emitSpy.calledWith('subscribe', 'inv');
|
||||||
done();
|
done();
|
||||||
|
@ -75,11 +76,12 @@ describe('Insight model', function() {
|
||||||
it('should be able to destroy the instance', function(done) {
|
it('should be able to destroy the instance', function(done) {
|
||||||
var blockchain = new Insight(FAKE_OPTS);
|
var blockchain = new Insight(FAKE_OPTS);
|
||||||
blockchain.status.should.be.equal('disconnected');
|
blockchain.status.should.be.equal('disconnected');
|
||||||
|
var socket = blockchain.getSocket();
|
||||||
blockchain.on('connect', function() {
|
blockchain.on('connect', function() {
|
||||||
blockchain.subscribe('mg7UbtKgMvWAixTNMbC8soyUnwFk1qxEuM');
|
blockchain.subscribe('mg7UbtKgMvWAixTNMbC8soyUnwFk1qxEuM');
|
||||||
blockchain.getSubscriptions().length.should.equal(1);
|
Object.keys(blockchain.getSubscriptions()).length.should.equal(1);
|
||||||
blockchain.destroy();
|
blockchain.destroy();
|
||||||
blockchain.getSubscriptions().length.should.equal(0);
|
Object.keys(blockchain.getSubscriptions()).length.should.equal(0);
|
||||||
blockchain.status.should.be.equal('destroyed');
|
blockchain.status.should.be.equal('destroyed');
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
@ -87,10 +89,11 @@ describe('Insight model', function() {
|
||||||
|
|
||||||
it('should subscribe to an address', function() {
|
it('should subscribe to an address', function() {
|
||||||
var blockchain = new Insight(FAKE_OPTS);
|
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.subscribe('mg7UbtKgMvWAixTNMbC8soyUnwFk1qxEuM');
|
||||||
blockchain.getSubscriptions().length.should.equal(1);
|
Object.keys(blockchain.getSubscriptions()).length.should.equal(1);
|
||||||
emitSpy.calledWith('subscribe', 'mg7UbtKgMvWAixTNMbC8soyUnwFk1qxEuM');
|
emitSpy.calledWith('subscribe', 'mg7UbtKgMvWAixTNMbC8soyUnwFk1qxEuM');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -99,38 +102,31 @@ describe('Insight model', function() {
|
||||||
|
|
||||||
blockchain.subscribe('mg7UbtKgMvWAixTNMbC8soyUnwFk1qxEuM');
|
blockchain.subscribe('mg7UbtKgMvWAixTNMbC8soyUnwFk1qxEuM');
|
||||||
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() {
|
it('should subscribe to a list of addresses', function() {
|
||||||
var blockchain = new Insight(FAKE_OPTS);
|
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([
|
blockchain.subscribe([
|
||||||
'mg7UbtKgMvWAixTNMbC8soyUnwFk1qxEuM',
|
'mg7UbtKgMvWAixTNMbC8soyUnwFk1qxEuM',
|
||||||
'2NBBHBjB5sd7HFqKtout1L7d6dPhwJgP2j8'
|
'2NBBHBjB5sd7HFqKtout1L7d6dPhwJgP2j8'
|
||||||
]);
|
]);
|
||||||
blockchain.getSubscriptions().length.should.equal(2);
|
Object.keys(blockchain.getSubscriptions()).length.should.equal(2);
|
||||||
emitSpy.calledWith('subscribe', 'mg7UbtKgMvWAixTNMbC8soyUnwFk1qxEuM');
|
emitSpy.calledWith('subscribe', 'mg7UbtKgMvWAixTNMbC8soyUnwFk1qxEuM');
|
||||||
emitSpy.calledWith('subscribe', '2NBBHBjB5sd7HFqKtout1L7d6dPhwJgP2j8');
|
emitSpy.calledWith('subscribe', '2NBBHBjB5sd7HFqKtout1L7d6dPhwJgP2j8');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should unsubscribe to an address', function() {
|
it('should resubscribe to all addresses', 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() {
|
|
||||||
var blockchain = new Insight(FAKE_OPTS);
|
var blockchain = new Insight(FAKE_OPTS);
|
||||||
blockchain.subscribe('mg7UbtKgMvWAixTNMbC8soyUnwFk1qxEuM');
|
blockchain.subscribe('mg7UbtKgMvWAixTNMbC8soyUnwFk1qxEuM');
|
||||||
blockchain.subscribe('2NBBHBjB5sd7HFqKtout1L7d6dPhwJgP2j8');
|
blockchain.subscribe('2NBBHBjB5sd7HFqKtout1L7d6dPhwJgP2j8');
|
||||||
blockchain.getSubscriptions().length.should.equal(2);
|
Object.keys(blockchain.getSubscriptions()).length.should.equal(2);
|
||||||
|
|
||||||
blockchain.unsubscribeAll('mg7UbtKgMvWAixTNMbC8soyUnwFk1qxEuM');
|
blockchain.reSubscribe();
|
||||||
blockchain.getSubscriptions().length.should.equal(0);
|
Object.keys(blockchain.getSubscriptions()).length.should.equal(2);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should broadcast a raw transaction', function(done) {
|
it('should broadcast a raw transaction', function(done) {
|
||||||
|
@ -354,8 +350,10 @@ describe('Insight model', function() {
|
||||||
describe('Events', function() {
|
describe('Events', function() {
|
||||||
it('should emmit event on a new block', function(done) {
|
it('should emmit event on a new block', function(done) {
|
||||||
var blockchain = new Insight(FAKE_OPTS);
|
var blockchain = new Insight(FAKE_OPTS);
|
||||||
|
var socket = blockchain.getSocket();
|
||||||
blockchain.on('connect', function() {
|
blockchain.on('connect', function() {
|
||||||
blockchain.socket.emit('block', '12312312');
|
var socket = blockchain.getSocket();
|
||||||
|
socket.emit('block', '12312312');
|
||||||
});
|
});
|
||||||
|
|
||||||
blockchain.on('block', function(blockid) {
|
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 blockchain = new Insight(FAKE_OPTS);
|
||||||
|
var socket = blockchain.getSocket();
|
||||||
blockchain.subscribe('2NFjCBFZSsxiwWAD7CKQ3hzWFtf9DcqTucY');
|
blockchain.subscribe('2NFjCBFZSsxiwWAD7CKQ3hzWFtf9DcqTucY');
|
||||||
blockchain.on('connect', function() {
|
blockchain.on('connect', function() {
|
||||||
blockchain.socket.emit('2NFjCBFZSsxiwWAD7CKQ3hzWFtf9DcqTucY', '1123');
|
var socket = blockchain.getSocket();
|
||||||
|
socket.emit('2NFjCBFZSsxiwWAD7CKQ3hzWFtf9DcqTucY', '1123');
|
||||||
});
|
});
|
||||||
|
|
||||||
blockchain.on('tx', function(ev) {
|
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) {
|
it('should\'t emmit event on a transaction for non subscribed addresses', function(done) {
|
||||||
var blockchain = new Insight(FAKE_OPTS);
|
var blockchain = new Insight(FAKE_OPTS);
|
||||||
|
var socket = blockchain.getSocket();
|
||||||
blockchain.on('connect', function() {
|
blockchain.on('connect', function() {
|
||||||
blockchain.socket.emit('2NFjCBFZSsxiwWAD7CKQ3hzWFtf9DcqTucY', '1123');
|
var socket = blockchain.getSocket();
|
||||||
|
socket.emit('2NFjCBFZSsxiwWAD7CKQ3hzWFtf9DcqTucY', '1123');
|
||||||
setTimeout(function() { done(); }, 20);
|
setTimeout(function() { done(); }, 20);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -392,6 +394,7 @@ describe('Insight model', function() {
|
||||||
|
|
||||||
it('should emmit event on connection', function(done) {
|
it('should emmit event on connection', function(done) {
|
||||||
var blockchain = new Insight(FAKE_OPTS);
|
var blockchain = new Insight(FAKE_OPTS);
|
||||||
|
var socket = blockchain.getSocket();
|
||||||
blockchain.on('connect', function() {
|
blockchain.on('connect', function() {
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
@ -399,8 +402,10 @@ describe('Insight model', function() {
|
||||||
|
|
||||||
it('should emmit event on disconnection', function(done) {
|
it('should emmit event on disconnection', function(done) {
|
||||||
var blockchain = new Insight(FAKE_OPTS);
|
var blockchain = new Insight(FAKE_OPTS);
|
||||||
|
var socket = blockchain.getSocket();
|
||||||
blockchain.on('connect', function() {
|
blockchain.on('connect', function() {
|
||||||
blockchain.socket.emit('connect_error');
|
var socket = blockchain.getSocket();
|
||||||
|
socket.emit('connect_error');
|
||||||
});
|
});
|
||||||
blockchain.on('disconnect', function() {
|
blockchain.on('disconnect', function() {
|
||||||
done();
|
done();
|
||||||
|
|
|
@ -62,23 +62,6 @@ describe("Unit: controllerUtils", function() {
|
||||||
expect($rootScope.unitName).to.be.equal('bits');
|
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() {
|
describe("Unit: Notification Service", function() {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<div class="open" ng-controller="OpenController">
|
<div class="open" ng-controller="OpenController">
|
||||||
<div data-alert class="loading-screen" ng-show="loading && !failure">
|
<div data-alert class="loading-screen" ng-show="loading && !failure">
|
||||||
<i class="size-60 fi-bitcoin-circle icon-rotate spinner"></i>
|
<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>
|
||||||
<div class="row" ng-show="!loading">
|
<div class="row" ng-show="!loading">
|
||||||
<div class="large-4 columns logo-setup">
|
<div class="large-4 columns logo-setup">
|
||||||
|
|
Loading…
Reference in New Issue