diff --git a/config.js b/config.js
index 4da6227cc..5c658ea68 100644
--- a/config.js
+++ b/config.js
@@ -1,7 +1,7 @@
'use strict';
var defaultConfig = {
// DEFAULT network (livenet or testnet)
- networkName: 'livenet',
+ networkName: 'testnet',
forceNetwork: false,
// DEFAULT unit: Bit
@@ -15,71 +15,11 @@ var defaultConfig = {
minAmountSatoshi: 5400,
},
- // network layer (PeerJS) config
+ // network layer config
network: {
- // Use this to run your own local PeerJS server
- // with params: ./peerjs -p 10009 -k '6d6d751ea61e26f2'
- /*
-key: '6d6d751ea61e26f2',
-host: 'localhost',
-port: 10009,
-path: '/',
-*/
-
- // Use this to connect to bitpay's PeerJS server
- key: 'satoshirocks',
- // host: '162.242.219.26',
- // port: 10000,
- // secure: false,
- host: 'live.copay.io',
- port: 9000,
- secure: true,
- path: '/',
-
- // other PeerJS config
- maxPeers: 15,
- debug: 2,
-
- // PeerJS internal config object
- config: {
- 'iceServers': [
- // Pass in STUN and TURN servers for maximum network compatibility
- {
- url: 'stun:162.242.219.26'
- }, {
- url: 'turn:162.242.219.26',
- username: 'bitcore',
- credential: 'bitcore',
- }
- // {
- // url: 'stun:stun.l.google.com:19302'
- // }, {
- // url: 'stun:stun1.l.google.com:19302'
- // }, {
- // url: 'stun:stun2.l.google.com:19302'
- // }, {
- // url: 'stun:stun3.l.google.com:19302'
- // }, {
- // url: 'stun:stun4.l.google.com:19302'
- // }, {
- // url: 'stun:stunserver.org'
- // }
- // // Options fot TURN servers with p2p communications are not possible.
- // {
- // url: 'turn:numb.viagenie.ca',
- // credential: 'muazkh',
- // username: 'webrtc@live.com'
- // }, {
- // url: 'turn:192.158.29.39:3478?transport=udp',
- // credential: 'JZEOEt2V3Qb0y27GRntt2u2PAYA=',
- // username: '28224511:1379330808'
- // }, {
- // url: 'turn:192.158.29.39:3478?transport=tcp',
- // credential: 'JZEOEt2V3Qb0y27GRntt2u2PAYA=',
- // username: '28224511:1379330808'
- // }
- ]
- }
+ host: 'test-insight.bitpay.com',
+ port: 443,
+ schema: 'https'
},
// wallet default config
@@ -96,14 +36,14 @@ path: '/',
// blockchain service API config
blockchain: {
schema: 'https',
- host: 'insight.bitpay.com',
+ host: 'test-insight.bitpay.com',
port: 443,
retryDelay: 1000,
},
// socket service API config
socket: {
schema: 'https',
- host: 'insight.bitpay.com',
+ host: 'test-insight.bitpay.com',
port: 443,
reconnectDelay: 1000,
},
diff --git a/copay.js b/copay.js
index d9c855cce..35178e9a3 100644
--- a/copay.js
+++ b/copay.js
@@ -9,7 +9,7 @@ module.exports.HDParams = require('./js/models/core/HDParams');
// components
-var WebRTC = module.exports.WebRTC = require('./js/models/network/WebRTC');
+var Async = module.exports.Async = require('./js/models/network/Async');
var Insight = module.exports.Insight = require('./js/models/blockchain/Insight');
var StorageLocalEncrypted = module.exports.StorageLocalEncrypted = require('./js/models/storage/LocalEncrypted');
diff --git a/js/controllers/settings.js b/js/controllers/settings.js
index 6ff26a035..c8761aa17 100644
--- a/js/controllers/settings.js
+++ b/js/controllers/settings.js
@@ -52,10 +52,9 @@ angular.module('copayApp.controllers').controller('SettingsController', function
$scope.save = function() {
var network = config.network;
- network.key = $scope.networkKey;
- network.host = $scope.networkHost;
- network.port = $scope.networkPort;
- network.secure = $scope.networkSecure;
+ network.host = $scope.insightHost;
+ network.port = $scope.insightPort;
+ network.schema = $scope.insightSecure ? 'https' : 'http';
localStorage.setItem('config', JSON.stringify({
networkName: $scope.networkName,
diff --git a/js/models/blockchain/Insight.js b/js/models/blockchain/Insight.js
index b0a0b7654..610be2ebe 100644
--- a/js/models/blockchain/Insight.js
+++ b/js/models/blockchain/Insight.js
@@ -256,7 +256,7 @@ Insight.prototype._requestBrowser = function(options, callback) {
errTxt = 'CRITICAL: Wrong response from insight' + e2;
}
} else if (request.status >= 400 && request.status < 499) {
- errTxt = 'CRITICAL: Bad request to insight. Probably wrong transaction to broadcast?.';
+ errTxt = 'CRITICAL: Bad request to insight: '+request.status;
} else {
errTxt = 'Error code: ' + request.status + ' - Status: ' + request.statusText + ' - Description: ' + request.responseText;
setTimeout(function() {
diff --git a/js/models/core/Message.js b/js/models/core/Message.js
deleted file mode 100644
index ad6324440..000000000
--- a/js/models/core/Message.js
+++ /dev/null
@@ -1,148 +0,0 @@
-'use strict';
-
-var bitcore = require('bitcore');
-
-/* Encrypted, authenticated messages to be shared between copayers */
-var Message = function() {
-};
-
-Message.encode = function(topubkey, fromkey, payload, opts) {
- var version1 = new Buffer([1]); //peers will reject messges containing not-understood version1
- //i.e., increment version1 to prevent communications with old clients
- var version2 = new Buffer([0]); //peers will not reject messages containing not-understood version2
- //i.e., increment version2 to allow communication with old clients, but signal new clients
- var nonce;
- if (opts && opts.nonce && Buffer.isBuffer(opts.nonce) && opts.nonce.length == 8) {
- nonce = opts.nonce;
- } else {
- nonce = new Buffer(8);
- nonce.fill(0); //nonce is a big endian 8 byte number
- }
-
- var toencrypt = Buffer.concat([version1, version2, nonce, payload]);
- var toencrypthexbuf = new Buffer(toencrypt.toString('hex')); //due to bug in sjcl/bitcore, must use hex string
- var encrypted = Message._encrypt(topubkey, toencrypthexbuf);
- var sig = Message._sign(fromkey, encrypted);
- var encoded = {
- pubkey: fromkey.public.toString('hex'),
- sig: sig.toString('hex'),
- encrypted: encrypted.toString('hex')
- };
- return encoded;
-};
-
-Message.decode = function(key, encoded, opts) {
- var prevnonce;
- if (opts && opts.prevnonce && Buffer.isBuffer(opts.prevnonce) && opts.prevnonce.length == 8) {
- prevnonce = opts.prevnonce;
- } else {
- prevnonce = new Buffer(8);
- prevnonce.fill(0); //nonce is a big endian 8 byte number
- }
-
- try {
- var frompubkey = new Buffer(encoded.pubkey, 'hex');
- } catch (e) {
- throw new Error('Error decoding public key: ' + e);
- }
-
- try {
- var sig = new Buffer(encoded.sig, 'hex');
- var encrypted = new Buffer(encoded.encrypted, 'hex');
- } catch (e) {
- throw new Error('Error decoding data: ' + e);
- }
-
- try {
- var v = Message._verify(frompubkey, sig, encrypted);
- } catch (e) {
- throw new Error('Error verifying signature: ' + e);
- }
-
- if (!v) {
- throw new Error('Invalid signature');
- }
-
- try {
- var decryptedhexbuf = Message._decrypt(key.private, encrypted);
- var decrypted = new Buffer(decryptedhexbuf.toString(), 'hex'); //workaround for bug in bitcore/sjcl
- } catch (e) {
- throw new Error('Cannot decrypt data: ' + e);
- }
-
- try {
- var version1 = decrypted[0];
- var version2 = decrypted[1];
- var nonce = decrypted.slice(2, 10);
- var payload = decrypted.slice(10);
- } catch (e) {
- throw new Error('Cannot parse decrypted data: ' + e);
- }
-
- if (payload.length === 0) {
- throw new Error('No data present');
- }
-
- if (version1 !== 1) {
- throw new Error('Invalid version number');
- }
-
- if (version2 !== 0) {
- //put special version2 handling code here, if ever needed
- }
-
- if (!Message._noncegt(nonce, prevnonce) && prevnonce.toString('hex') !== '0000000000000000') {
- throw new Error('Nonce not equal to zero and not greater than the previous nonce');
- }
-
- var decoded = {
- version1: version1,
- version2: version2,
- nonce: nonce,
- payload: payload
- };
-
- return decoded;
-};
-
-//return true if nonce > prevnonce; false otherwise
-Message._noncegt = function(nonce, prevnonce) {
- var noncep1 = nonce.slice(0, 4).readUInt32BE(0);
- var prevnoncep1 = prevnonce.slice(0, 4).readUInt32BE(0);
-
- if (noncep1 > prevnoncep1)
- return true;
-
- if (noncep1 < prevnoncep1)
- return false;
-
- var noncep2 = nonce.slice(4, 8).readUInt32BE(0);
- var prevnoncep2 = prevnonce.slice(4, 8).readUInt32BE(0);
-
- if (noncep2 > prevnoncep2)
- return true;
-
- return false;
-};
-
-Message._encrypt = function(topubkey, payload, r, iv) {
- var encrypted = bitcore.ECIES.encrypt(topubkey, payload, r, iv);
- return encrypted;
-};
-
-Message._decrypt = function(privkey, encrypted) {
- var decrypted = bitcore.ECIES.decrypt(privkey, encrypted);
- return decrypted;
-};
-
-Message._sign = function(key, payload) {
- var sig = bitcore.Message.sign(payload, key);
- return sig;
-};
-
-Message._verify = function(pubkey, signature, payload) {
- var v = bitcore.Message.verifyWithPubKey(pubkey, payload, signature);
- return v;
-};
-
-module.exports = Message;
diff --git a/js/models/core/PublicKeyRing.js b/js/models/core/PublicKeyRing.js
index b86f18277..9a734e5ac 100644
--- a/js/models/core/PublicKeyRing.js
+++ b/js/models/core/PublicKeyRing.js
@@ -1,6 +1,5 @@
'use strict';
-
var preconditions = require('preconditions').instance();
var bitcore = require('bitcore');
var HK = bitcore.HierarchicalKey;
diff --git a/js/models/core/Wallet.js b/js/models/core/Wallet.js
index f2bb28c6b..a85aadc43 100644
--- a/js/models/core/Wallet.js
+++ b/js/models/core/Wallet.js
@@ -33,20 +33,16 @@ function Wallet(opts) {
'publicKeyRing', 'txProposals', 'privateKey', 'version',
'reconnectDelay'
].forEach(function(k) {
- if (typeof opts[k] === 'undefined')
- throw new Error('missing required option for Wallet: ' + k);
+ preconditions.checkArgument(typeof opts[k] !== 'undefined',
+ 'missing required option for Wallet: ' + k);
self[k] = opts[k];
});
- if (copayConfig.forceNetwork && this.getNetworkName() !== copayConfig.networkName)
- throw new Error('Network forced to ' + copayConfig.networkName +
- ' and tried to create a Wallet with network ' + this.getNetworkName());
-
- this.log('creating ' + opts.requiredCopayers + ' of ' + opts.totalCopayers + ' wallet');
+ preconditions.checkArgument(!copayConfig.forceNetwork || this.getNetworkName() === copayConfig.networkName,
+ 'Network forced to ' + copayConfig.networkName +
+ ' and tried to create a Wallet with network ' + this.getNetworkName());
this.id = opts.id || Wallet.getRandomId();
this.lock = new WalletLock(this.storage, this.id, opts.lockTimeOutMin);
-
-
this.name = opts.name;
this.verbose = opts.verbose;
@@ -56,6 +52,7 @@ function Wallet(opts) {
this.registeredPeerIds = [];
this.addressBook = opts.addressBook || {};
this.publicKey = this.privateKey.publicHex;
+ this.lastTimestamp = opts.lastTimestamp || undefined;
this.paymentRequests = opts.paymentRequests || {};
@@ -89,7 +86,10 @@ Wallet.prototype.seedCopayer = function(pubKey) {
this.seededCopayerId = pubKey;
};
+// not being used now
Wallet.prototype.connectToAll = function() {
+ // not being used now
+ return;
var all = this.publicKeyRing.getAllCopayerIds();
this.network.connectToCopayers(all);
@@ -99,7 +99,7 @@ Wallet.prototype.connectToAll = function() {
}
};
-Wallet.prototype._handleIndexes = function(senderId, data, isInbound) {
+Wallet.prototype._onIndexes = function(senderId, data) {
this.log('RECV INDEXES:', data);
var inIndexes = HDParams.fromList(data.indexes);
var hasChanged = this.publicKeyRing.mergeIndexes(inIndexes);
@@ -109,7 +109,7 @@ Wallet.prototype._handleIndexes = function(senderId, data, isInbound) {
}
};
-Wallet.prototype._handlePublicKeyRing = function(senderId, data, isInbound) {
+Wallet.prototype._onPublicKeyRing = function(senderId, data) {
this.log('RECV PUBLICKEYRING:', data);
var inPKR = PublicKeyRing.fromObj(data.publicKeyRing);
@@ -217,7 +217,7 @@ Wallet.prototype._checkSentTx = function(ntxid, cb) {
};
-Wallet.prototype._handleTxProposal = function(senderId, data) {
+Wallet.prototype._onTxProposal = function(senderId, data) {
var self = this;
this.log('RECV TXPROPOSAL: ', data);
var m;
@@ -254,7 +254,7 @@ Wallet.prototype._handleTxProposal = function(senderId, data) {
};
-Wallet.prototype._handleReject = function(senderId, data, isInbound) {
+Wallet.prototype._onReject = function(senderId, data) {
preconditions.checkState(data.ntxid);
this.log('RECV REJECT:', data);
@@ -277,7 +277,7 @@ Wallet.prototype._handleReject = function(senderId, data, isInbound) {
});
};
-Wallet.prototype._handleSeen = function(senderId, data, isInbound) {
+Wallet.prototype._onSeen = function(senderId, data) {
preconditions.checkState(data.ntxid);
this.log('RECV SEEN:', data);
@@ -295,7 +295,7 @@ Wallet.prototype._handleSeen = function(senderId, data, isInbound) {
-Wallet.prototype._handleAddressBook = function(senderId, data, isInbound) {
+Wallet.prototype._onAddressBook = function(senderId, data) {
preconditions.checkState(data.addressBook);
this.log('RECV ADDRESSBOOK:', data);
var rcv = data.addressBook;
@@ -315,13 +315,25 @@ Wallet.prototype._handleAddressBook = function(senderId, data, isInbound) {
}
};
-Wallet.prototype._handleData = function(senderId, data, isInbound) {
- // TODO check message signature
+Wallet.prototype.updateTimestamp = function(ts) {
+ preconditions.checkArgument(ts);
+ preconditions.checkArgument(typeof ts === 'number');
+ this.lastTimestamp = ts;
+ this.store();
+};
+
+Wallet.prototype._onData = function(senderId, data, ts) {
+ preconditions.checkArgument(senderId);
+ preconditions.checkArgument(data);
+ preconditions.checkArgument(data.type);
+ preconditions.checkArgument(ts);
+ preconditions.checkArgument(typeof ts === 'number');
+
+ this.updateTimestamp(ts);
if (data.type !== 'walletId' && this.id !== data.walletId) {
- this.emit('badMessage', senderId);
- this.log('badMessage FROM:', senderId);
+ this.emit('corrupt', senderId);
return;
}
@@ -336,27 +348,31 @@ Wallet.prototype._handleData = function(senderId, data, isInbound) {
this.sendAllTxProposals(senderId); // send old txps
break;
case 'publicKeyRing':
- this._handlePublicKeyRing(senderId, data, isInbound);
+ this._onPublicKeyRing(senderId, data);
break;
case 'reject':
- this._handleReject(senderId, data, isInbound);
+ this._onReject(senderId, data);
break;
case 'seen':
- this._handleSeen(senderId, data, isInbound);
+ this._onSeen(senderId, data);
break;
case 'txProposal':
- this._handleTxProposal(senderId, data, isInbound);
+ this._onTxProposal(senderId, data);
break;
case 'indexes':
- this._handleIndexes(senderId, data, isInbound);
+ this._onIndexes(senderId, data);
break;
case 'addressbook':
- this._handleAddressBook(senderId, data, isInbound);
+ this._onAddressBook(senderId, data);
+ break;
+ case 'disconnect':
+ this._onDisconnect(senderId, data);
break;
}
+
};
-Wallet.prototype._handleConnect = function(newCopayerId) {
+Wallet.prototype._onConnect = function(newCopayerId) {
if (newCopayerId) {
this.log('#### Setting new COPAYER:', newCopayerId);
this.sendWalletId(newCopayerId);
@@ -365,7 +381,7 @@ Wallet.prototype._handleConnect = function(newCopayerId) {
this.emit('connect', peerID);
};
-Wallet.prototype._handleDisconnect = function(peerID) {
+Wallet.prototype._onDisconnect = function(peerID) {
this.currentDelay = null;
this.emit('disconnect', peerID);
};
@@ -427,15 +443,8 @@ Wallet.prototype.netStart = function(callback) {
var net = this.network;
net.removeAllListeners();
- net.on('connect', self._handleConnect.bind(self));
- net.on('disconnect', self._handleDisconnect.bind(self));
- net.on('data', self._handleData.bind(self));
- net.on('close', function() {
- self.emit('close');
- });
- net.on('serverError', function(msg) {
- self.emit('serverError', msg);
- });
+ net.on('connect', self._onConnect.bind(self));
+ net.on('data', self._onData.bind(self));
var myId = self.getMyCopayerId();
var myIdPriv = self.getMyCopayerIdPriv();
@@ -443,7 +452,8 @@ Wallet.prototype.netStart = function(callback) {
var startOpts = {
copayerId: myId,
privkey: myIdPriv,
- maxPeers: self.totalCopayers
+ maxPeers: self.totalCopayers,
+ lastTimestamp: this.lastTimestamp,
};
if (this.publicKeyRing.isComplete()) {
@@ -454,12 +464,15 @@ Wallet.prototype.netStart = function(callback) {
self.emit('ready', net.getPeer());
setTimeout(function() {
self.emit('publicKeyRingUpdated', true);
- self.scheduleConnect();
+ //self.scheduleConnect();
+ // no connection logic for now
self.emit('txProposalsUpdated');
}, 10);
});
};
+
+// not being used now
Wallet.prototype.scheduleConnect = function() {
var self = this;
if (self.network.isOnline()) {
@@ -533,6 +546,7 @@ Wallet.prototype.toObj = function() {
txProposals: this.txProposals.toObj(),
privateKey: this.privateKey ? this.privateKey.toObj() : undefined,
addressBook: this.addressBook,
+ lastTimestamp: this.lastTimestamp,
};
return walletObj;
@@ -544,16 +558,17 @@ Wallet.fromObj = function(o, storage, network, blockchain) {
opts.addressBook = o.addressBook;
- if (o.privateKey)
+ if (o.privateKey) {
opts.privateKey = PrivateKey.fromObj(o.privateKey);
- else
+ } else {
opts.privateKey = new PrivateKey({
networkName: opts.networkName
});
+ }
- if (o.publicKeyRing)
+ if (o.publicKeyRing) {
opts.publicKeyRing = PublicKeyRing.fromObj(o.publicKeyRing);
- else {
+ } else {
opts.publicKeyRing = new PublicKeyRing({
networkName: opts.networkName,
requiredCopayers: opts.requiredCopayers,
@@ -565,12 +580,15 @@ Wallet.fromObj = function(o, storage, network, blockchain) {
);
}
- if (o.txProposals)
+ if (o.txProposals) {
opts.txProposals = TxProposals.fromObj(o.txProposals, Wallet.builderOpts);
- else
+ } else {
opts.txProposals = new TxProposals({
networkName: this.networkName,
});
+ }
+
+ opts.lastTimestamp = o.lastTimestamp;
opts.storage = storage;
opts.network = network;
@@ -627,7 +645,9 @@ Wallet.prototype.sendReject = function(ntxid) {
});
};
+
Wallet.prototype.sendWalletReady = function(recipients) {
+ preconditions.checkArgument(recipients);
this.log('### SENDING WalletReady TO:', recipients);
this.send(recipients, {
@@ -707,7 +727,7 @@ Wallet.prototype.getTxProposals = function() {
txp.finallyRejected = true;
}
- if (txp.readonly && !txp.finallyRejected && !txp.sentTs) {} else {
+ if (!txp.readonly || txp.finallyRejected || txp.sentTs) {
ret.push(txp);
}
}
@@ -1724,7 +1744,7 @@ Wallet.prototype.indexDiscovery = function(start, change, copayerIndex, gap, cb)
function _while() {
return hasActivity;
},
- function _finnaly(err) {
+ function _finally(err) {
if (err) return cb(err);
cb(null, lastActive);
}
@@ -1735,7 +1755,12 @@ Wallet.prototype.indexDiscovery = function(start, change, copayerIndex, gap, cb)
Wallet.prototype.disconnect = function() {
this.log('## DISCONNECTING');
this.lock.release();
- this.network.disconnect();
+ var self = this;
+ self.send(null, {
+ type: 'disconnect',
+ walletId: this.id,
+ });
+ self.network.cleanUp();
};
Wallet.prototype.getNetwork = function() {
diff --git a/js/models/core/WalletFactory.js b/js/models/core/WalletFactory.js
index 909fba68f..3b4d9d77e 100644
--- a/js/models/core/WalletFactory.js
+++ b/js/models/core/WalletFactory.js
@@ -5,7 +5,7 @@ var PublicKeyRing = require('./PublicKeyRing');
var PrivateKey = require('./PrivateKey');
var Wallet = require('./Wallet');
-var WebRTC = module.exports.WebRTC = require('../network/WebRTC');
+var Async = module.exports.Async = require('../network/Async');
var Insight = module.exports.Insight = require('../blockchain/Insight');
var StorageLocalEncrypted = module.exports.StorageLocalEncrypted = require('../storage/LocalEncrypted');
@@ -19,7 +19,7 @@ function WalletFactory(config, version) {
config = config || {};
this.Storage = config.Storage || StorageLocalEncrypted;
- this.Network = config.Network || WebRTC;
+ this.Network = config.Network || Async;
this.Blockchain = config.Blockchain || Insight;
this.storage = new this.Storage(config.storage);
@@ -104,6 +104,7 @@ WalletFactory.prototype.read = function(walletId, skipFields) {
obj.privateKey = s.get(walletId, 'privateKey');
obj.addressBook = s.get(walletId, 'addressBook');
obj.backupOffered = s.get(walletId, 'backupOffered');
+ obj.lastTimestamp = s.get(walletId, 'lastTimestamp');
var w = this.fromObj(obj, skipFields);
return w;
@@ -249,16 +250,13 @@ WalletFactory.prototype.joinCreateSession = function(secret, nickname, passphras
self.network.on('connected', function(sender, data) {
connectedOnce = true;
});
- self.network.on('onlyYou', function(sender, data) {
- return cb(connectedOnce ? 'walletFull' : 'joinError');
- });
self.network.on('serverError', function() {
return cb('joinError');
});
self.network.start(opts, function() {
- self.network.connectTo(s.pubKey);
+ self.network.greet(s.pubKey);
self.network.on('data', function(sender, data) {
if (data.type === 'walletId') {
@@ -271,7 +269,8 @@ WalletFactory.prototype.joinCreateSession = function(secret, nickname, passphras
data.opts.passphrase = passphrase;
data.opts.id = data.walletId;
var w = self.create(data.opts);
- w.seedCopayer(s.pubKey);
+ w.sendWalletReady(s.pubKey);
+ //w.seedCopayer(s.pubKey);
return cb(null, w);
}
});
diff --git a/js/models/network/Async.js b/js/models/network/Async.js
new file mode 100644
index 000000000..e63b2ecfc
--- /dev/null
+++ b/js/models/network/Async.js
@@ -0,0 +1,382 @@
+'use strict';
+
+var EventEmitter = require('events').EventEmitter;
+var bitcore = require('bitcore');
+var AuthMessage = bitcore.AuthMessage;
+var util = bitcore.util;
+var nodeUtil = require('util');
+var extend = nodeUtil._extend;
+var io = require('socket.io-client');
+var preconditions = require('preconditions').singleton();
+
+/*
+ * Emits
+ * 'connect'
+ * when network layout has change (new/lost peers, etc)
+ *
+ * 'data'
+ * when an unknown data type arrives
+ *
+ * Provides
+ * send(toPeerIds, {data}, cb?)
+ *
+ */
+
+function Network(opts) {
+ var self = this;
+ opts = opts || {};
+ this.maxPeers = opts.maxPeers || 12;
+ this.host = opts.host || 'localhost';
+ this.port = opts.port || 3001;
+ this.schema = opts.schema || 'https';
+ this.cleanUp();
+}
+
+nodeUtil.inherits(Network, EventEmitter);
+
+Network.prototype.cleanUp = function() {
+ this.started = false;
+ this.connectedPeers = [];
+ this.peerId = null;
+ this.privkey = null;
+ this.key = null;
+ this.copayerId = null;
+ this.allowedCopayerIds = null;
+ this.isInboundPeerAuth = [];
+ this.copayerForPeer = {};
+ this.connections = {};
+ this.criticalErr = '';
+ this.removeAllListeners();
+ if (this.socket) {
+ this.socket.disconnect();
+ this.socket = null;
+ }
+};
+
+Network.parent = EventEmitter;
+
+// Array helpers
+Network._inArray = function(el, array) {
+ return array.indexOf(el) > -1;
+};
+
+Network._arrayPushOnce = function(el, array) {
+ var ret = false;
+ if (!Network._inArray(el, array)) {
+ array.push(el);
+ ret = true;
+ }
+ return ret;
+};
+
+Network._arrayRemove = function(el, array) {
+ var pos = array.indexOf(el);
+ if (pos >= 0) array.splice(pos, 1);
+ return array;
+};
+
+Network.prototype.connectedCopayers = function() {
+ var ret = [];
+ for (var i in this.connectedPeers) {
+ var copayerId = this.copayerForPeer[this.connectedPeers[i]];
+ if (copayerId) ret.push(copayerId);
+ }
+ return ret;
+};
+
+Network.prototype._sendHello = function(copayerId) {
+ this.send(copayerId, {
+ type: 'hello',
+ copayerId: this.copayerId,
+ });
+};
+
+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);
+};
+
+Network.prototype._addConnectedCopayer = function(copayerId) {
+ var peerId = this.peerFromCopayer(copayerId);
+ this._addCopayerMap(peerId, copayerId);
+ Network._arrayPushOnce(peerId, this.connectedPeers);
+ this.emit('connect', copayerId);
+};
+
+Network.prototype.getKey = function() {
+ preconditions.checkState(this.privkey || this.key);
+ if (!this.key) {
+ var key = new bitcore.Key();
+ key.private = new Buffer(this.privkey, 'hex');
+ key.regenerateSync();
+ this.key = key;
+ }
+ return this.key;
+};
+
+//hex version of one's own nonce
+Network.prototype.setHexNonce = function(networkNonce) {
+ if (networkNonce) {
+ if (networkNonce.length !== 16)
+ throw new Error('incorrect length of hex nonce');
+ this.networkNonce = new Buffer(networkNonce, 'hex');
+ } else
+ this.iterateNonce();
+};
+
+//hex version of copayers' nonces
+Network.prototype.setHexNonces = function(networkNonces) {
+ for (var i in networkNonces) {
+ if (!this.networkNonces)
+ this.networkNonces = {};
+ if (networkNonces[i].length === 16)
+ this.networkNonces[i] = new Buffer(networkNonces[i], 'hex');
+ }
+};
+
+//for oneself
+Network.prototype.getHexNonce = function() {
+ return this.networkNonce.toString('hex');
+};
+
+//for copayers
+Network.prototype.getHexNonces = function() {
+ var networkNoncesHex = [];
+ for (var i in this.networkNonces) {
+ networkNoncesHex[i] = this.networkNonces[i].toString('hex');
+ }
+ return networkNoncesHex;
+};
+
+Network.prototype.iterateNonce = function() {
+ if (!this.networkNonce || this.networkNonce.length !== 8) {
+ this.networkNonce = new Buffer(8);
+ this.networkNonce.fill(0);
+ }
+ //the first 4 bytes of a nonce is a unix timestamp in seconds
+ //the second 4 bytes is just an iterated "sub" nonce
+ //the whole thing is interpreted as one big endian number
+ var noncep1 = this.networkNonce.slice(0, 4);
+ noncep1.writeUInt32BE(Math.floor(Date.now() / 1000), 0);
+ var noncep2uint = this.networkNonce.slice(4, 8).readUInt32BE(0);
+ var noncep2 = this.networkNonce.slice(4, 8);
+ noncep2.writeUInt32BE(noncep2uint + 1, 0);
+ return this.networkNonce;
+};
+
+Network.prototype.decode = function(enc) {
+ var sender = enc.pubkey;
+ var key = this.getKey();
+ var prevnonce = this.networkNonces ? this.networkNonces[sender] : undefined;
+ var opts = {
+ prevnonce: prevnonce
+ };
+ var decoded = AuthMessage.decode(key, enc, opts);
+
+ //if no error thrown in the last step, we can set the copayer's nonce
+ if (!this.networkNonces)
+ this.networkNonces = {};
+ this.networkNonces[sender] = decoded.nonce;
+
+ var payload = decoded.payload;
+ return payload;
+};
+
+Network.prototype._onMessage = function(enc) {
+ var sender = enc.pubkey;
+ try {
+ var payload = this.decode(enc);
+ } catch (e) {
+ this._deletePeer(sender);
+ return;
+ }
+
+ //console.log('receiving ' + JSON.stringify(payload));
+
+ var self = this;
+ switch (payload.type) {
+ case 'hello':
+ // if we locked allowed copayers, check if it belongs
+ if (this.allowedCopayerIds && !this.allowedCopayerIds[payload.copayerId]) {
+ this._deletePeer(sender);
+ return;
+ }
+ //ensure claimed public key is actually the public key of the peer
+ //e.g., their public key should hash to be their peerId
+ if (sender !== payload.copayerId) {
+ this._deletePeer(enc.pubkey, 'incorrect pubkey for peerId');
+ return;
+ }
+ this._addConnectedCopayer(payload.copayerId);
+ break;
+ default:
+ this.emit('data', sender, payload, enc.ts);
+ }
+};
+
+Network.prototype._setupConnectionHandlers = function(cb) {
+ preconditions.checkState(this.socket);
+ var self = this;
+
+ self.socket.on('connect', function() {
+ self.socket.on('disconnect', function() {
+ self.cleanUp();
+ });
+ if (typeof cb === 'function') cb();
+ });
+ self.socket.on('message', function(m) {
+ // delay execution, to improve error handling
+ setTimeout(function() {
+ self._onMessage(m);
+ }, 1);
+ });
+ self.socket.on('error', self._onError.bind(self));
+
+};
+
+Network.prototype._onError = function(err) {
+ console.log('RECV ERROR: ', err);
+ console.log(err.stack);
+ this.criticalError = err.message;
+};
+
+Network.prototype.greet = function(copayerId) {
+ this._sendHello(copayerId);
+ var peerId = this.peerFromCopayer(copayerId);
+ this._addCopayerMap(peerId, copayerId);
+};
+
+Network.prototype._addCopayerMap = function(peerId, copayerId) {
+ if (!this.copayerForPeer[peerId]) {
+ if (Object.keys(this.copayerForPeer).length < this.maxPeers) {
+ this.copayerForPeer[peerId] = copayerId;
+ }
+ }
+};
+
+Network.prototype._setInboundPeerAuth = function(peerId) {
+ this.isInboundPeerAuth[peerId] = true;
+};
+
+Network.prototype.setCopayerId = function(copayerId) {
+ preconditions.checkState(!this.started, 'network already started: can not change peerId');
+
+ this.copayerId = copayerId;
+ this.copayerIdBuf = new Buffer(copayerId, 'hex');
+ this.peerId = this.peerFromCopayer(this.copayerId);
+ this._addCopayerMap(this.peerId, copayerId);
+};
+
+
+// TODO cache this.
+Network.prototype.peerFromCopayer = function(hex) {
+ var SIN = bitcore.SIN;
+ return new SIN(new Buffer(hex, 'hex')).toString();
+};
+
+Network.prototype.start = function(opts, openCallback) {
+ preconditions.checkArgument(opts);
+ preconditions.checkArgument(opts.privkey);
+ preconditions.checkArgument(opts.copayerId);
+
+ preconditions.checkState(this.connectedPeers && this.connectedPeers.length === 0);
+
+ 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);
+ this.socket.emit('sync', opts.lastTimestamp);
+ this.started = true;
+
+};
+
+Network.prototype.createSocket = function() {
+ var hostPort = this.schema + '://' + this.host + ':' + this.port;
+ return io.connect(hostPort, {
+ reconnection: true,
+ 'force new connection': true,
+ 'secure': this.schema === 'https',
+ });
+};
+
+Network.prototype.getOnlinePeerIDs = function() {
+ return this.connectedPeers;
+};
+
+Network.prototype.getPeer = function() {
+ return this.peer;
+};
+
+
+Network.prototype.getCopayerIds = function() {
+ if (this.allowedCopayerIds) {
+ return Object.keys(this.allowedCopayerIds);
+ } else {
+ var copayerIds = [];
+ for (var peerId in this.copayerForPeer) {
+ copayerIds.push(this.copayerForPeer[peerId]);
+ }
+ return copayerIds;
+ }
+};
+
+
+Network.prototype.send = function(dest, payload, cb) {
+ preconditions.checkArgument(payload);
+
+ var self = this;
+ if (!dest) {
+ dest = this.getCopayerIds();
+ payload.isBroadcast = 1;
+ }
+
+ if (typeof dest === 'string')
+ dest = [dest];
+
+ var l = dest.length;
+ var i = 0;
+ //console.log('sending ' + JSON.stringify(payload));
+ dest.forEach(function(to) {
+ //console.log('\t to ' + to);
+ var message = self.encode(to, payload);
+ self.socket.emit('message', message);
+ });
+ if (typeof cb === 'function') cb();
+};
+
+
+Network.prototype.encode = function(copayerId, payload, nonce) {
+ this.iterateNonce();
+ var opts = {
+ nonce: nonce || this.networkNonce
+ };
+ var copayerIdBuf = new Buffer(copayerId, 'hex');
+ var message = AuthMessage.encode(copayerIdBuf, this.getKey(), payload, opts);
+ return message;
+};
+
+Network.prototype.isOnline = function() {
+ return !!this.socket;
+};
+
+
+Network.prototype.lockIncommingConnections = function(allowedCopayerIdsArray) {
+ this.allowedCopayerIds = {};
+ for (var i in allowedCopayerIdsArray) {
+ this.allowedCopayerIds[allowedCopayerIdsArray[i]] = true;
+ }
+};
+
+module.exports = Network;
diff --git a/js/models/network/WebRTC.js b/js/models/network/WebRTC.js
deleted file mode 100644
index 256bc3ec7..000000000
--- a/js/models/network/WebRTC.js
+++ /dev/null
@@ -1,525 +0,0 @@
-'use strict';
-
-var EventEmitter = require('events').EventEmitter;
-var bitcore = require('bitcore');
-var util = bitcore.util;
-var nodeUtil = require('util');
-var Message = require('../core/Message');
-/*
- * Emits
- * 'connect'
- * when network layout has change (new/lost peers, etc)
- *
- * 'data'
- * when an unknown data type arrives
- *
- * Provides
- * send(toPeerIds, {data}, cb?)
- *
- */
-
-function Network(opts) {
- var self = this;
- opts = opts || {};
- this.apiKey = opts.apiKey || 'lwjd5qra8257b9';
- this.debug = opts.debug || 3;
- this.maxPeers = opts.maxPeers || 10;
- this.reconnectAttempts = opts.reconnectAttempts || 3;
- this.sjclParams = opts.sjclParams || {
- salt: 'f28bfb49ef70573c',
- iter: 500,
- mode: 'ccm',
- ts: parseInt(64),
- };
- this.opts = {};
- ['config', 'port', 'host', 'path', 'debug', 'key', 'secure'].forEach(function(k) {
- if (opts.hasOwnProperty(k)) self.opts[k] = opts[k];
- });
- this.cleanUp();
-}
-
-nodeUtil.inherits(Network, EventEmitter);
-
-Network.prototype.cleanUp = function() {
- this.started = false;
- this.connectedPeers = [];
- this.peerId = null;
- this.privkey = null; //TODO: hide privkey in a closure
- this.key = null;
- this.copayerId = null;
- this.allowedCopayerIds = null;
- this.isInboundPeerAuth = [];
- this.copayerForPeer = {};
- this.connections = {};
- this.criticalErr = '';
- if (this.peer) {
- this.peer.disconnect();
- this.peer.destroy();
- this.peer.removeAllListeners();
- this.peer = null;
- }
- this.closing = 0;
- this.tries = 0;
- this.removeAllListeners();
-};
-
-
-// Array helpers
-Network._arrayDiff = function(a, b) {
- var seen = [];
- var diff = [];
-
- for (var i = 0; i < b.length; i++)
- seen[b[i]] = true;
-
- for (var j = 0; j < a.length; j++)
- if (!seen[a[j]])
- diff.push(a[j]);
-
- return diff;
-};
-
-Network._inArray = function(el, array) {
- return array.indexOf(el) > -1;
-};
-
-Network._arrayPushOnce = function(el, array) {
- var ret = false;
- if (!Network._inArray(el, array)) {
- array.push(el);
- ret = true;
- }
- return ret;
-};
-
-Network._arrayRemove = function(el, array) {
- var pos = array.indexOf(el);
- if (pos >= 0) array.splice(pos, 1);
- return array;
-};
-
-Network.prototype.connectedCopayers = function() {
- var ret = [];
- for (var i in this.connectedPeers) {
- var copayerId = this.copayerForPeer[this.connectedPeers[i]];
- if (copayerId) ret.push(copayerId);
- }
- return ret;
-};
-
-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);
-};
-
-Network.prototype._onClose = function(peerID) {
- this._deletePeer(peerID);
- this.emit('disconnect', peerID);
-};
-
-Network.prototype.connectToCopayers = function(copayerIds) {
- var self = this;
- var arrayDiff = Network._arrayDiff(copayerIds, self.connectedCopayers());
-
- arrayDiff.forEach(function(copayerId) {
- if (self.allowedCopayerIds && !self.allowedCopayerIds[copayerId]) {
- self._deletePeer(self.peerFromCopayer(copayerId));
- } else {
- self.connectTo(copayerId);
- }
- });
-};
-
-Network.prototype._sendHello = function(copayerId) {
- this.send(copayerId, {
- type: 'hello',
- copayerId: this.copayerId,
- });
-};
-
-Network.prototype._addConnectedCopayer = function(copayerId, isInbound) {
- var peerId = this.peerFromCopayer(copayerId);
- this._addCopayerMap(peerId, copayerId);
- Network._arrayPushOnce(peerId, this.connectedPeers);
- this.emit('connect', copayerId);
-};
-
-Network.prototype.getKey = function() {
- if (!this.key) {
- var key = new bitcore.Key();
- key.private = new Buffer(this.privkey, 'hex');
- key.regenerateSync();
- this.key = key;
- }
- return this.key;
-};
-
-//hex version of one's own nonce
-Network.prototype.setHexNonce = function(networkNonce) {
- if (networkNonce) {
- if (networkNonce.length !== 16)
- throw new Error('incorrect length of hex nonce');
- this.networkNonce = new Buffer(networkNonce, 'hex');
- }
- else
- this.iterateNonce();
-};
-
-//hex version of copayers' nonces
-Network.prototype.setHexNonces = function(networkNonces) {
- for (var i in networkNonces) {
- if (!this.networkNonces)
- this.networkNonces = {};
- if (networkNonces[i].length === 16)
- this.networkNonces[i] = new Buffer(networkNonces[i], 'hex');
- }
-};
-
-//for oneself
-Network.prototype.getHexNonce = function() {
- return this.networkNonce.toString('hex');
-};
-
-//for copayers
-Network.prototype.getHexNonces = function() {
- var networkNoncesHex = [];
- for (var i in this.networkNonces) {
- networkNoncesHex[i] = this.networkNonces[i].toString('hex');
- }
- return networkNoncesHex;
-};
-
-Network.prototype.iterateNonce = function() {
- if (!this.networkNonce || this.networkNonce.length !== 8) {
- this.networkNonce = new Buffer(8);
- this.networkNonce.fill(0);
- }
- //the first 4 bytes of a nonce is a unix timestamp in seconds
- //the second 4 bytes is just an iterated "sub" nonce
- //the whole thing is interpreted as one big endian number
- var noncep1 = this.networkNonce.slice(0, 4);
- noncep1.writeUInt32BE(Math.floor(Date.now()/1000), 0);
- var noncep2uint = this.networkNonce.slice(4, 8).readUInt32BE(0);
- var noncep2 = this.networkNonce.slice(4, 8);
- noncep2.writeUInt32BE(noncep2uint + 1, 0);
- return this.networkNonce;
-};
-
-Network.prototype._onData = function(enc, isInbound, peerId) {
- var encUint8Array = new Uint8Array(enc);
- var encbuf = new Buffer(encUint8Array);
- var encstr = encbuf.toString();
-
- var privkey = this.privkey;
- var key = this.getKey();
-
- try {
- var encoded = JSON.parse(encstr);
- var prevnonce = this.networkNonces ? this.networkNonces[peerId] : undefined;
- var opts = {prevnonce: prevnonce};
- var decoded = this._decode(key, encoded, opts);
-
- //if no error thrown in the last step, we can set the copayer's nonce
- if (!this.networkNonces)
- this.networkNonces = {};
- this.networkNonces[peerId] = decoded.nonce;
-
- var databuf = decoded.payload;
- var datastr = databuf.toString();
- var payload = JSON.parse(datastr);
- } catch (e) {
- this._deletePeer(peerId);
- return;
- }
-
- if (isInbound && payload.type === 'hello') {
- var payloadStr = JSON.stringify(payload);
-
- //ensure claimed public key is actually the public key of the peer
- //e.g., their public key should hash to be their peerId
- if (peerId.toString() !== this.peerFromCopayer(payload.copayerId) || peerId.toString() !== this.peerFromCopayer(encoded.pubkey)) {
- this._deletePeer(peerId, 'incorrect pubkey for peerId');
- return;
- }
-
- if (this.allowedCopayerIds && !this.allowedCopayerIds[payload.copayerId]) {
- this._deletePeer(peerId);
- return;
- }
-
- this._addConnectedCopayer(payload.copayerId, isInbound);
- this._setInboundPeerAuth(peerId, true);
- return;
- }
-
- if (!this.copayerForPeer[peerId] || (isInbound && !this.isInboundPeerAuth[peerId])) {
- this._deletePeer(peerId);
- return;
- }
-
- var self = this;
- switch (payload.type) {
- case 'disconnect':
- this._onClose(peerId);
- break;
- default:
- this.emit('data', self.copayerForPeer[peerId], payload, isInbound);
- }
-};
-
-Network.prototype._checkAnyPeer = function(msg) {
- if (this.connectedPeers.length === 1) {
- this.emit('onlyYou');
- }
-};
-
-Network.prototype._setupConnectionHandlers = function(dataConn, toCopayerId) {
- var self = this;
-
- var isInbound = toCopayerId ? false : true;
-
- dataConn.on('open', function() {
- if (!Network._inArray(dataConn.peer, self.connectedPeers) &&
- !self.connections[dataConn.peer]) {
-
- self.connections[dataConn.peer] = dataConn;
-
- // The connecting peer send hello
- if (toCopayerId) {
- self.emit('connected');
- self._sendHello(toCopayerId);
- self._addConnectedCopayer(toCopayerId);
- }
- }
- });
-
- dataConn.on('data', function(data) {
- self._onData(data, isInbound, dataConn.peer);
- });
-
- dataConn.on('error', function(e) {
- self._onClose(dataConn.peer);
- self._checkAnyPeer();
- self.emit('dataError');
- });
-
- dataConn.on('close', function() {
- if (self.closing) return;
-
- self._onClose(dataConn.peer);
- self._checkAnyPeer();
- });
-};
-
-Network.prototype._handlePeerOpen = function(openCallback) {
- this.connectedPeers = [this.peerId];
- this.copayerForPeer[this.peerId] = this.copayerId;
- return openCallback();
-};
-
-Network.prototype._handlePeerError = function(err) {
- console.log('RECV ERROR: ', err);
- if (err.message.match(/Could\snot\sconnect\sto peer/)) {
- this._checkAnyPeer();
- } else {
- this.criticalError = err.message;
- }
-};
-
-Network.prototype._handlePeerConnection = function(dataConn) {
- if (this.connectedPeers.length >= self.maxPeers) {
- dataConn.on('open', function() {
- dataConn.close();
- });
- } else {
- this._setInboundPeerAuth(dataConn.peer, false);
- this._setupConnectionHandlers(dataConn);
- }
-};
-
-Network.prototype._setupPeerHandlers = function(openCallback) {
- var p = this.peer;
- p.on('open', this._handlePeerOpen.bind(this, openCallback));
- p.on('error', this._handlePeerError.bind(this));
- p.on('connection', this._handlePeerConnection.bind(this));
-};
-
-Network.prototype._addCopayerMap = function(peerId, copayerId) {
- if (!this.copayerForPeer[peerId]) {
- if (Object.keys(this.copayerForPeer).length < this.maxPeers) {
- this.copayerForPeer[peerId] = copayerId;
- } else {}
- }
-};
-
-Network.prototype._setInboundPeerAuth = function(peerId, isAuthenticated) {
- this.isInboundPeerAuth[peerId] = isAuthenticated;
-};
-
-Network.prototype.setCopayerId = function(copayerId) {
- if (this.started) {
- throw new Error('network already started: can not change peerId')
- }
- this.copayerId = copayerId;
- this.copayerIdBuf = new Buffer(copayerId, 'hex');
- this.peerId = this.peerFromCopayer(this.copayerId);
- this._addCopayerMap(this.peerId, copayerId);
-};
-
-
-// TODO cache this.
-Network.prototype.peerFromCopayer = function(hex) {
- var SIN = bitcore.SIN;
- return new SIN(new Buffer(hex, 'hex')).toString();
-};
-
-Network.prototype.start = function(opts, openCallback) {
- opts = opts || {};
-
- if (this.started) return openCallback();
-
- if (!this.privkey)
- this.privkey = opts.privkey;
-
- this.maxPeers = opts.maxPeers || this.maxPeers;
-
- if (opts.token)
- this.opts.token = opts.token;
-
- if (!this.copayerId)
- this.setCopayerId(opts.copayerId);
-
- var self = this;
- var setupPeer = function() {
- if (self.connectedPeers.length > 0) return; // Already connected!
- if (self.peer) {
- self.peer.destroy();
- self.peer.removeAllListeners();
- }
-
- if (!self.criticalError && self.tries < self.reconnectAttempts) {
- self.tries++;
- self.opts.token = util.sha256(self.peerId).toString('hex');
- self.peer = new Peer(self.peerId, self.opts);
- self.started = true;
- self._setupPeerHandlers(openCallback);
-
- setTimeout(setupPeer, 3000); // Schedule retry
- return;
- }
- if (self.criticalError && self.criticalError.match(/taken/)) {
- self.criticalError = ' Looks like you are already connected to this wallet please close all other Copay Wallets '
- }
-
- self.emit('serverError', self.criticalError);
- self.cleanUp();
- }
-
- this.tries = 0;
- setupPeer();
-};
-
-Network.prototype.getOnlinePeerIDs = function() {
- return this.connectedPeers;
-};
-
-Network.prototype.getPeer = function() {
- return this.peer;
-};
-
-Network.prototype._encode = function(topubkey, fromkey, payload, opts) {
- var encoded = Message.encode(topubkey, fromkey, payload, opts);
- return encoded;
-};
-
-
-Network.prototype._decode = function(key, encoded, opts) {
- var decoded = Message.decode(key, encoded, opts);
- return decoded;
-};
-
-Network.prototype._sendToOne = function(copayerId, payload, cb) {
- if (!Buffer.isBuffer(payload))
- throw new Error('payload must be a buffer');
-
- var peerId = this.peerFromCopayer(copayerId);
- if (peerId !== this.peerId) {
- var dataConn = this.connections[peerId];
- if (dataConn) {
- dataConn.send(payload);
- }
- }
- if (typeof cb === 'function') cb();
-};
-
-Network.prototype.send = function(copayerIds, payload, cb) {
- if (!payload) return cb();
-
- var self = this;
- if (!copayerIds) {
- copayerIds = this.connectedCopayers();
- payload.isBroadcast = 1;
- }
-
- if (typeof copayerIds === 'string')
- copayerIds = [copayerIds];
-
- var payloadStr = JSON.stringify(payload);
- var payloadBuf = new Buffer(payloadStr);
-
- var l = copayerIds.length;
- var i = 0;
- copayerIds.forEach(function(copayerId) {
- self.iterateNonce();
- var opts = {nonce: self.networkNonce};
- var copayerIdBuf = new Buffer(copayerId, 'hex');
- var encPayload = self._encode(copayerIdBuf, self.getKey(), payloadBuf, opts);
- var enc = new Buffer(JSON.stringify(encPayload));
- self._sendToOne(copayerId, enc, function() {
- if (++i === l && typeof cb === 'function') cb();
- });
- });
-};
-
-
-Network.prototype.isOnline = function() {
- return !!this.peer;
-};
-
-Network.prototype.connectTo = function(copayerId) {
- var self = this;
-
- var peerId = this.peerFromCopayer(copayerId);
- var dataConn = this.peer.connect(peerId, {
- serialization: 'none',
- reliable: true,
- });
- self._setupConnectionHandlers(dataConn, copayerId);
-};
-
-Network.prototype.lockIncommingConnections = function(allowedCopayerIdsArray) {
- this.allowedCopayerIds = {};
- for (var i in allowedCopayerIdsArray) {
- this.allowedCopayerIds[allowedCopayerIdsArray[i]] = true;
- }
-};
-
-Network.prototype.disconnect = function(cb, forced) {
- var self = this;
- self.closing = 1;
- self.send(null, {
- type: 'disconnect'
- }, function() {
- self.cleanUp();
- if (typeof cb === 'function') cb();
- });
-};
-
-module.exports = Network;
diff --git a/js/services/controllerUtils.js b/js/services/controllerUtils.js
index dfbe4bb45..197b53d11 100644
--- a/js/services/controllerUtils.js
+++ b/js/services/controllerUtils.js
@@ -2,382 +2,380 @@
var bitcore = require('bitcore');
angular.module('copayApp.services')
-.factory('controllerUtils', function($rootScope, $sce, $location, notification, $timeout, Socket, video, uriHandler) {
- var root = {};
- root.getVideoMutedStatus = function(copayer) {
- if (!$rootScope.videoInfo) return;
+ .factory('controllerUtils', function($rootScope, $sce, $location, notification, $timeout, Socket, video, uriHandler) {
+ var root = {};
+ root.getVideoMutedStatus = function(copayer) {
+ if (!$rootScope.videoInfo) return;
- var vi = $rootScope.videoInfo[copayer]
- if (!vi) {
- return;
- }
- return vi.muted;
- };
-
- root.redirIfLogged = function() {
- if ($rootScope.wallet) {
- $rootScope.wallet.path('receive');
- }
- };
-
- root.logout = function() {
- if ($rootScope.wallet)
- $rootScope.wallet.disconnect();
-
- Socket.removeAllListeners();
-
- $rootScope.wallet = null;
- delete $rootScope['wallet'];
-
- video.close();
- // Clear rootScope
- for (var i in $rootScope) {
- if (i.charAt(0) != '$') {
- delete $rootScope[i];
- }
- }
-
- $location.path('/');
- };
-
- root.onError = function(scope) {
- if (scope) scope.loading = false;
- root.logout();
- }
-
- root.onErrorDigest = function(scope, msg) {
- root.onError(scope);
- if (msg) {
- notification.error('Error', msg);
- }
- $rootScope.$digest();
- };
-
- 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.onErrorDigest($scope);
- });
- wallet.on('serverError', function(m) {
- var message = m || 'The PeerJS server is not responding, please try again';
- $location.path('receive');
- root.onErrorDigest($scope, message);
- });
- wallet.on('ready', function() {
- $scope.loading = false;
- });
- };
-
- root.setupRootVariables = function() {
- uriHandler.register();
- $rootScope.unitName = config.unitName;
- $rootScope.txAlertCount = 0;
- $rootScope.insightError = 0;
- $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) {
- Socket.removeAllListeners();
-
- root.setupRootVariables();
- root.installStartupHandlers(w, $scope);
- root.setSocketHandlers();
-
- var handlePeerVideo = function(err, peerID, url) {
- if (err) {
- delete $rootScope.videoInfo[peerID];
+ var vi = $rootScope.videoInfo[copayer]
+ if (!vi) {
return;
}
- $rootScope.videoInfo[peerID] = {
- url: encodeURI(url),
- muted: peerID === w.network.peerId
- };
+ return vi.muted;
+ };
+
+ root.redirIfLogged = function() {
+ if ($rootScope.wallet) {
+ $rootScope.wallet.path('receive');
+ }
+ };
+
+ root.logout = function() {
+ if ($rootScope.wallet)
+ $rootScope.wallet.disconnect();
+
+ Socket.removeAllListeners();
+
+ $rootScope.wallet = null;
+ delete $rootScope['wallet'];
+
+ video.close();
+ // Clear rootScope
+ for (var i in $rootScope) {
+ if (i.charAt(0) != '$') {
+ delete $rootScope[i];
+ }
+ }
+
+ $location.path('/');
+ };
+
+ root.onError = function(scope) {
+ if (scope) scope.loading = false;
+ root.logout();
+ }
+
+ root.onErrorDigest = function(scope, msg) {
+ root.onError(scope);
+ if (msg) {
+ notification.error('Error', msg);
+ }
$rootScope.$digest();
};
- notification.enableHtml5Mode(); // for chrome: if support, enable it
+ 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.onErrorDigest($scope);
+ });
+ wallet.on('ready', function() {
+ $scope.loading = false;
+ });
+ };
- w.on('badMessage', function(peerId) {
- notification.error('Error', 'Received wrong message from peer ' + peerId);
- });
- w.on('ready', function(myPeerID) {
- $rootScope.wallet = w;
- if ($rootScope.pendingPayment) {
- $location.path('send');
- } else {
- $location.path('receive');
- }
- if (!config.disableVideo)
- video.setOwnPeer(myPeerID, w, handlePeerVideo);
- });
+ root.setupRootVariables = function() {
+ uriHandler.register();
+ $rootScope.unitName = config.unitName;
+ $rootScope.txAlertCount = 0;
+ $rootScope.insightError = 0;
+ $rootScope.isCollapsed = true;
+ $rootScope.$watch('txAlertCount', function(txAlertCount) {
+ if (txAlertCount && txAlertCount > 0) {
- w.on('publicKeyRingUpdated', function(dontDigest) {
- root.setSocketHandlers();
- if (!dontDigest) {
- $rootScope.$digest();
- }
- });
- w.on('txProposalsUpdated', function(dontDigest) {
- root.updateTxs();
- // give sometime to the tx to propagate.
- $timeout(function() {
- root.updateBalance(function() {
- if (!dontDigest) {
- $rootScope.$digest();
+ 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;
+ }
}
- });
- }, 3000);
- });
- w.on('txProposalEvent', function(e) {
- var user = w.publicKeyRing.nicknameForCopayer(e.cId);
- switch (e.type) {
- case 'signed':
- notification.info('Transaction Update', 'A transaction was signed by ' + user);
- break;
- case 'rejected':
- notification.info('Transaction Update', 'A transaction was rejected by ' + user);
- break;
- case 'corrupt':
- notification.error('Transaction Error', 'Received corrupt transaction from '+user);
- break;
- }
- });
- w.on('addressBookUpdated', function(dontDigest) {
- if (!dontDigest) {
+ if (currentAddr) {
+ //var beep = new Audio('sound/transaction.mp3');
+ notification.funds('Received fund', currentAddr, receivedFund);
+ //beep.play();
+ }
+ }
+ });
+
+ };
+
+
+ root.startNetwork = function(w, $scope) {
+ Socket.removeAllListeners();
+
+ root.setupRootVariables();
+ root.installStartupHandlers(w, $scope);
+ root.setSocketHandlers();
+
+ var handlePeerVideo = function(err, peerID, url) {
+ if (err) {
+ delete $rootScope.videoInfo[peerID];
+ return;
+ }
+ $rootScope.videoInfo[peerID] = {
+ url: encodeURI(url),
+ muted: peerID === w.network.peerId
+ };
$rootScope.$digest();
- }
- });
- w.on('connectionError', function(msg) {
- root.onErrorDigest(null, msg);
- });
- w.on('connect', function(peerID) {
- if (peerID && !config.disableVideo) {
- video.callPeer(peerID, handlePeerVideo);
- }
- $rootScope.$digest();
- });
- w.on('disconnect', function(peerID) {
- $rootScope.$digest();
- });
- w.on('close', root.onErrorDigest);
- w.on('locked', root.onErrorDigest.bind(this));
- w.netStart();
- };
+ };
- root.updateAddressList = function() {
- var w = $rootScope.wallet;
- if (w && w.isReady())
- $rootScope.addrInfos = w.getAddressesInfo();
- };
+ notification.enableHtml5Mode(); // for chrome: if support, enable it
- root.updateBalance = function(cb) {
- var w = $rootScope.wallet;
- if (!w) return root.onErrorDigest();
- if (!w.isReady()) return;
+ w.on('corrupt', function(peerId) {
+ notification.error('Error', 'Received corrupt message from ' + peerId);
+ });
+ w.on('ready', function(myPeerID) {
+ $rootScope.wallet = w;
+ if ($rootScope.pendingPayment) {
+ $location.path('send');
+ } else {
+ $location.path('receive');
+ }
+ if (!config.disableVideo)
+ video.setOwnPeer(myPeerID, w, handlePeerVideo);
+ });
- $rootScope.balanceByAddr = {};
- $rootScope.updatingBalance = true;
+ w.on('publicKeyRingUpdated', function(dontDigest) {
+ root.setSocketHandlers();
+ if (!dontDigest) {
+ $rootScope.$digest();
+ }
+ });
+ w.on('txProposalsUpdated', function(dontDigest) {
+ root.updateTxs();
+ // give sometime to the tx to propagate.
+ $timeout(function() {
+ root.updateBalance(function() {
+ if (!dontDigest) {
+ $rootScope.$digest();
+ }
+ });
+ }, 3000);
+ });
+ w.on('txProposalEvent', function(e) {
+ var user = w.publicKeyRing.nicknameForCopayer(e.cId);
+ switch (e.type) {
+ case 'signed':
+ notification.info('Transaction Update', 'A transaction was signed by ' + user);
+ break;
+ case 'rejected':
+ notification.info('Transaction Update', 'A transaction was rejected by ' + user);
+ break;
+ case 'corrupt':
+ notification.error('Transaction Error', 'Received corrupt transaction from ' + user);
+ break;
+ }
+ });
+ w.on('addressBookUpdated', function(dontDigest) {
+ if (!dontDigest) {
+ $rootScope.$digest();
+ }
+ });
+ w.on('connectionError', function(msg) {
+ root.onErrorDigest(null, msg);
+ });
+ w.on('connect', function(peerID) {
+ if (peerID && !config.disableVideo) {
+ video.callPeer(peerID, handlePeerVideo);
+ }
+ $rootScope.$digest();
+ });
+ w.on('disconnect', function(peerID) {
+ $rootScope.$digest();
+ });
+ w.on('close', root.onErrorDigest);
+ w.on('locked', root.onErrorDigest.bind(this));
+ w.netStart();
+ };
- w.getBalance(function(err, balanceSat, balanceByAddrSat, safeBalanceSat) {
- if (err) {
- console.error('Error: ' + err.message); //TODO
- root._setCommError();
- return null;
- } else {
- root._clearCommError();
- }
+ root.updateAddressList = function() {
+ var w = $rootScope.wallet;
+ if (w && w.isReady())
+ $rootScope.addrInfos = w.getAddressesInfo();
+ };
+
+ root.updateBalance = function(cb) {
+ var w = $rootScope.wallet;
+ if (!w) return root.onErrorDigest();
+ if (!w.isReady()) return;
+
+ $rootScope.balanceByAddr = {};
+ $rootScope.updatingBalance = true;
+
+ w.getBalance(function(err, balanceSat, balanceByAddrSat, safeBalanceSat) {
+ if (err) {
+ console.error('Error: ' + err.message); //TODO
+ root._setCommError();
+ return null;
+ } else {
+ root._clearCommError();
+ }
+
+ var satToUnit = 1 / config.unitToSatoshi;
+ var COIN = bitcore.util.COIN;
+
+ $rootScope.totalBalance = balanceSat * satToUnit;
+ $rootScope.totalBalanceBTC = (balanceSat / COIN);
+ $rootScope.availableBalance = safeBalanceSat * satToUnit;
+ $rootScope.availableBalanceBTC = (safeBalanceSat / COIN);
+
+ $rootScope.lockedBalance = (balanceSat - safeBalanceSat) * satToUnit;
+ $rootScope.lockedBalanceBTC = (balanceSat - safeBalanceSat) / COIN;
+
+ var balanceByAddr = {};
+ for (var ii in balanceByAddrSat) {
+ balanceByAddr[ii] = balanceByAddrSat[ii] * satToUnit;
+ }
+ $rootScope.balanceByAddr = balanceByAddr;
+ root.updateAddressList();
+ $rootScope.updatingBalance = false;
+ return cb ? cb() : null;
+ });
+ };
+
+ root.updateTxs = function(opts) {
+ var w = $rootScope.wallet;
+ if (!w) return;
+ opts = opts || $rootScope.txsOpts || {};
var satToUnit = 1 / config.unitToSatoshi;
- var COIN = bitcore.util.COIN;
+ var myCopayerId = w.getMyCopayerId();
+ var pendingForUs = 0;
+ var inT = w.getTxProposals().sort(function(t1, t2) {
+ return t2.createdTs - t1.createdTs
+ });
+ var txs = [];
- $rootScope.totalBalance = balanceSat * satToUnit;
- $rootScope.totalBalanceBTC = (balanceSat / COIN);
- $rootScope.availableBalance = safeBalanceSat * satToUnit;
- $rootScope.availableBalanceBTC = (safeBalanceSat / COIN);
+ inT.forEach(function(i, index) {
+ if (opts.skip && (index < opts.skip[0] || index >= opts.skip[1])) {
+ return txs.push(null);
+ }
- $rootScope.lockedBalance = (balanceSat - safeBalanceSat) * satToUnit;
- $rootScope.lockedBalanceBTC = (balanceSat - safeBalanceSat) / COIN;
+ if (myCopayerId != i.creator && !i.finallyRejected && !i.sentTs && !i.rejectedByUs && !i.signedByUs) {
+ pendingForUs++;
+ }
+ if (!i.finallyRejected && !i.sentTs) {
+ i.isPending = 1;
+ }
- var balanceByAddr = {};
- for (var ii in balanceByAddrSat) {
- balanceByAddr[ii] = balanceByAddrSat[ii] * satToUnit;
+ if (!!opts.pending == !!i.isPending) {
+ var tx = i.builder.build();
+ var outs = [];
+ tx.outs.forEach(function(o) {
+ var addr = bitcore.Address.fromScriptPubKey(o.getScript(), config.networkName)[0].toString();
+ if (!w.addressIsOwn(addr, {
+ excludeMain: true
+ })) {
+ outs.push({
+ address: addr,
+ value: bitcore.util.valueToBigInt(o.getValue()) * satToUnit,
+ });
+ }
+ });
+ // extra fields
+ i.outs = outs;
+ i.fee = i.builder.feeSat * satToUnit;
+ i.missingSignatures = tx.countInputMissingSignatures(0);
+ i.actionList = getActionList(i.peerActions);
+ txs.push(i);
+ }
+ });
+
+ $rootScope.txs = txs;
+ $rootScope.txsOpts = opts;
+ if ($rootScope.pendingTxCount < pendingForUs) {
+ $rootScope.txAlertCount = pendingForUs;
}
- $rootScope.balanceByAddr = balanceByAddr;
+ $rootScope.pendingTxCount = pendingForUs;
+ };
+
+ function getActionList(actions) {
+ var peers = Object.keys(actions).map(function(i) {
+ return {
+ cId: i,
+ actions: actions[i]
+ }
+ });
+
+ return peers.sort(function(a, b) {
+ return !!b.actions.create - !!a.actions.create;
+ });
+ }
+
+ var connectionLost = false;
+ $rootScope.$watch('insightError', function(status) {
+ if (!status) return;
+
+ // Reconnected
+ if (status === -1) {
+ if (!connectionLost) return; // Skip on first reconnect
+ connectionLost = false;
+ notification.success('Networking restored', 'Connection to Insight re-established');
+ return;
+ }
+
+ // Retry
+ if (status == 1) return; // Skip the first try
+ connectionLost = true;
+ notification.error('Networking problem', 'Connection to Insight lost, reconnecting (attempt number ' + (status - 1) + ')');
+ });
+
+ root._setCommError = function(e) {
+ if ($rootScope.insightError < 0)
+ $rootScope.insightError = 0;
+ $rootScope.insightError++;
+ };
+
+ root._clearCommError = function(e) {
+ if ($rootScope.insightError > 0)
+ $rootScope.insightError = -1;
+ else
+ $rootScope.insightError = 0;
+ };
+
+ root.setSocketHandlers = function() {
root.updateAddressList();
- $rootScope.updatingBalance = false;
- return cb ? cb() : null;
- });
- };
-
- root.updateTxs = function(opts) {
- var w = $rootScope.wallet;
- if (!w) return;
- opts = opts || $rootScope.txsOpts || {};
-
- var satToUnit = 1 / config.unitToSatoshi;
- var myCopayerId = w.getMyCopayerId();
- var pendingForUs = 0;
- var inT = w.getTxProposals().sort(function(t1, t2) {
- return t2.createdTs - t1.createdTs
- });
- var txs = [];
-
- inT.forEach(function(i, index) {
- if (opts.skip && (index < opts.skip[0] || index >= opts.skip[1])) {
- return txs.push(null);
+ if (!Socket.sysEventsSet) {
+ Socket.sysOn('error', root._setCommError);
+ Socket.sysOn('reconnect_error', root._setCommError);
+ Socket.sysOn('reconnect_failed', root._setCommError);
+ Socket.sysOn('connect', root._clearCommError);
+ Socket.sysOn('reconnect', root._clearCommError);
+ Socket.sysEventsSet = true;
}
+ if (!$rootScope.wallet) return;
- if (myCopayerId != i.creator && !i.finallyRejected && !i.sentTs && !i.rejectedByUs && !i.signedByUs) {
- pendingForUs++;
- }
- if (!i.finallyRejected && !i.sentTs) {
- i.isPending = 1;
- }
+ var currentAddrs = Socket.getListeners();
+ var allAddrs = $rootScope.addrInfos;
- if (!!opts.pending == !!i.isPending) {
- var tx = i.builder.build();
- var outs = [];
- tx.outs.forEach(function(o) {
- var addr = bitcore.Address.fromScriptPubKey(o.getScript(), config.networkName)[0].toString();
- if (!w.addressIsOwn(addr, {
- excludeMain: true
- })) {
- outs.push({
- address: addr,
- value: bitcore.util.valueToBigInt(o.getValue()) * satToUnit,
- });
- }
+ var newAddrs = [];
+ for (var i in allAddrs) {
+ var a = allAddrs[i];
+ if (!currentAddrs[a.addressStr])
+ newAddrs.push(a);
+ }
+ for (var i = 0; i < newAddrs.length; i++) {
+ Socket.emit('subscribe', newAddrs[i].addressStr);
+ }
+ newAddrs.forEach(function(a) {
+ Socket.on(a.addressStr, function(txid) {
+
+ if (!a.isChange)
+ notification.funds('Funds received!', a.addressStr);
+
+ root.updateBalance(function() {
+ $rootScope.$digest();
+ });
+ });
+ });
+
+ if (!$rootScope.wallet.spendUnconfirmed && !Socket.isListeningBlocks()) {
+ Socket.emit('subscribe', 'inv');
+ Socket.on('block', function(block) {
+ root.updateBalance(function() {
+ $rootScope.$digest();
+ });
});
- // extra fields
- i.outs = outs;
- i.fee = i.builder.feeSat * satToUnit;
- i.missingSignatures = tx.countInputMissingSignatures(0);
- i.actionList = getActionList(i.peerActions);
- txs.push(i);
}
- });
-
- $rootScope.txs = txs;
- $rootScope.txsOpts = opts;
- if ($rootScope.pendingTxCount < pendingForUs) {
- $rootScope.txAlertCount = pendingForUs;
- }
- $rootScope.pendingTxCount = pendingForUs;
- };
-
- function getActionList(actions) {
- var peers = Object.keys(actions).map(function(i) {
- return {cId: i, actions: actions[i] }
- });
-
- return peers.sort(function(a, b) {
- return !!b.actions.create - !!a.actions.create;
- });
- }
-
- var connectionLost = false;
- $rootScope.$watch('insightError', function(status) {
- if (!status) return;
-
- // Reconnected
- if (status === -1) {
- if (!connectionLost) return; // Skip on first reconnect
- connectionLost = false;
- notification.success('Networking restored', 'Connection to Insight re-established');
- return;
- }
-
- // Retry
- if (status == 1) return; // Skip the first try
- connectionLost = true;
- notification.error('Networking problem', 'Connection to Insight lost, reconnecting (attempt number ' + (status-1) + ')');
+ };
+ return root;
});
-
- root._setCommError = function(e) {
- if ($rootScope.insightError < 0)
- $rootScope.insightError = 0;
- $rootScope.insightError++;
- };
-
- root._clearCommError = function(e) {
- if ($rootScope.insightError > 0)
- $rootScope.insightError = -1;
- else
- $rootScope.insightError = 0;
- };
-
- root.setSocketHandlers = function() {
- root.updateAddressList();
- if (!Socket.sysEventsSet) {
- Socket.sysOn('error', root._setCommError);
- Socket.sysOn('reconnect_error', root._setCommError);
- Socket.sysOn('reconnect_failed', root._setCommError);
- Socket.sysOn('connect', root._clearCommError);
- Socket.sysOn('reconnect', root._clearCommError);
- Socket.sysEventsSet = true;
- }
- if (!$rootScope.wallet) return;
-
- var currentAddrs = Socket.getListeners();
- var allAddrs = $rootScope.addrInfos;
-
- var newAddrs = [];
- for (var i in allAddrs) {
- var a = allAddrs[i];
- if (!currentAddrs[a.addressStr])
- newAddrs.push(a);
- }
- for (var i = 0; i < newAddrs.length; i++) {
- Socket.emit('subscribe', newAddrs[i].addressStr);
- }
- newAddrs.forEach(function(a) {
- Socket.on(a.addressStr, function(txid) {
-
- if (!a.isChange)
- notification.funds('Funds received!', a.addressStr);
-
- root.updateBalance(function() {
- $rootScope.$digest();
- });
- });
- });
-
- if (!$rootScope.wallet.spendUnconfirmed && !Socket.isListeningBlocks()) {
- Socket.emit('subscribe', 'inv');
- Socket.on('block', function(block) {
- root.updateBalance(function() {
- $rootScope.$digest();
- });
- });
- }
- };
- return root;
-});
diff --git a/package.json b/package.json
index 61b315609..8e9e91402 100644
--- a/package.json
+++ b/package.json
@@ -53,10 +53,7 @@
"grunt-contrib-uglify": "^0.5.1",
"grunt-contrib-watch": "0.5.3",
"grunt-markdown": "0.5.0",
- "browser-pack": "2.0.1",
"bitcore": "0.1.35",
- "node-cryptojs-aes": "0.4.0",
- "blanket": "1.1.6",
"grunt-mocha-test": "0.8.2",
"grunt-shell": "0.6.4",
"istanbul": "0.2.10",
@@ -69,6 +66,7 @@
"mocha-lcov-reporter": "0.0.1",
"mock-fs": "^2.3.1",
"node-cryptojs-aes": "0.4.0",
+ "socket.io-client": "1.0.6",
"travis-cov": "0.2.5",
"uglifyify": "1.2.3"
},
diff --git a/test/index.html b/test/index.html
index 6d75f29c7..e5aa76a6c 100644
--- a/test/index.html
+++ b/test/index.html
@@ -15,8 +15,7 @@
-
-
+
diff --git a/test/mocks/FakeNetwork.js b/test/mocks/FakeNetwork.js
index 4368e8de5..d1b8fee6c 100644
--- a/test/mocks/FakeNetwork.js
+++ b/test/mocks/FakeNetwork.js
@@ -6,7 +6,6 @@ function Network(opts) {}
util.inherits(Network, EventEmitter);
Network.prototype.start = function(opts, cb) {
- // start! :D
this.peer = {
options: {
token: "asd"
@@ -15,18 +14,12 @@ Network.prototype.start = function(opts, cb) {
if (cb) cb();
};
-Network.prototype.send = function(peerIds, data, cb) {
- // send! c:
-};
+Network.prototype.send = function(peerIds, data, cb) {};
-Network.prototype.connectTo = function(peerId) {
- // connect C:
-};
+Network.prototype.connectTo = function(peerId) {};
-Network.prototype.disconnect = function(cb) {
- // disconect :c
-};
+Network.prototype.disconnect = function(cb) {};
Network.prototype.lockIncommingConnections = function() {
@@ -43,7 +36,7 @@ Network.prototype.peerFromCopayer = function(copayerId) {
//hex version of one's own nonce
Network.prototype.setHexNonce = function(networkNonce) {
- if (networkNonce && networkNonce.length === 16)
+ if (networkNonce && networkNonce.length === 16)
this.networkNonce = new Buffer(networkNonce, 'hex');
else
this.iterateNonce();
@@ -84,7 +77,7 @@ Network.prototype.iterateNonce = function() {
//the second 4 bytes is just an iterated "sub" nonce
//the whole thing is interpreted as one big endian number
var noncep1 = this.networkNonce.slice(0, 4);
- noncep1.writeUInt32BE(Math.floor(Date.now()/1000), 0);
+ noncep1.writeUInt32BE(Math.floor(Date.now() / 1000), 0);
var noncep2uint = this.networkNonce.slice(4, 8).readUInt32BE(0);
var noncep2 = this.networkNonce.slice(4, 8);
noncep2.writeUInt32BE(noncep2uint + 1, 0);
@@ -92,4 +85,9 @@ Network.prototype.iterateNonce = function() {
};
+Network.prototype.cleanUp = function() {
+ return;
+};
+
+
module.exports = Network;
diff --git a/test/test.Message.js b/test/test.Message.js
deleted file mode 100644
index 9bad687d1..000000000
--- a/test/test.Message.js
+++ /dev/null
@@ -1,159 +0,0 @@
-'use strict';
-
-var chai = chai || require('chai');
-var should = chai.should();
-var sinon = require('sinon');
-var Message = require('../js/models/core/Message');
-var bitcore = bitcore || require('bitcore');
-var Buffer = bitcore.Buffer;
-
-describe('Message model', function() {
- var key = new bitcore.Key();
- key.private = bitcore.util.sha256(new Buffer('test'));
- key.regenerateSync();
-
- var key2 = new bitcore.Key();
- key2.private = bitcore.util.sha256(new Buffer('test 2'));
- key2.regenerateSync();
-
- describe('#encode', function() {
-
- it('should encode a message', function() {
- var message = new Buffer('message');
- var encoded = Message.encode(key2.public, key, message);
- should.exist(encoded.pubkey);
- should.exist(encoded.sig);
- should.exist(encoded.encrypted);
- });
-
- });
-
- describe('#decode', function() {
-
- it('should decode an encoded message', function() {
- var message = new Buffer('message');
- var messagehex = message.toString('hex');
- var encoded = Message.encode(key2.public, key, message);
-
- var decoded = Message.decode(key2, encoded);
- var payload = decoded.payload;
- payload.toString('hex').should.equal(messagehex);
- });
-
- it('should decode an encoded message with proper prevnonce', function() {
- var message = new Buffer('message');
- var messagehex = message.toString('hex');
- var nonce = new Buffer([0, 0, 0, 0, 0, 0, 0, 2]);
- var opts = {nonce: nonce};
- var encoded = Message.encode(key2.public, key, message, opts);
-
- var prevnonce = new Buffer([0, 0, 0, 0, 0, 0, 0, 1]);
- opts = {prevnonce: prevnonce};
- var decoded = Message.decode(key2, encoded, opts);
- var payload = decoded.payload;
- payload.toString('hex').should.equal(messagehex);
- });
-
- it('should decode an encoded message with proper prevnonce - for first part', function() {
- var message = new Buffer('message');
- var messagehex = message.toString('hex');
- var nonce = new Buffer([0, 0, 0, 2, 0, 0, 0, 0]);
- var opts = {nonce: nonce};
- var encoded = Message.encode(key2.public, key, message, opts);
-
- var prevnonce = new Buffer([0, 0, 0, 1, 0, 0, 0, 0]);
- opts = {prevnonce: prevnonce};
- var decoded = Message.decode(key2, encoded, opts);
- var payload = decoded.payload;
- payload.toString('hex').should.equal(messagehex);
- });
-
- it('should fail if prevnonce is too high', function() {
- var message = new Buffer('message');
- var messagehex = message.toString('hex');
- var nonce = new Buffer([0, 0, 0, 0, 0, 0, 0, 1]);
- var opts = {nonce: nonce};
- var encoded = Message.encode(key2.public, key, message, opts);
-
- var prevnonce = new Buffer([0, 0, 0, 0, 0, 0, 0, 1]);
- opts = {prevnonce: prevnonce};
- (function() {Message.decode(key2, encoded, opts)}).should.throw('Nonce not equal to zero and not greater than the previous nonce');
- });
-
- it('should fail if prevnonce is too high - for first part', function() {
- var message = new Buffer('message');
- var messagehex = message.toString('hex');
- var nonce = new Buffer([0, 0, 0, 1, 0, 0, 0, 0]);
- var opts = {nonce: nonce};
- var encoded = Message.encode(key2.public, key, message, opts);
-
- var prevnonce = new Buffer([0, 0, 0, 1, 0, 0, 0, 0]);
- opts = {prevnonce: prevnonce};
- (function() {Message.decode(key2, encoded, opts)}).should.throw('Nonce not equal to zero and not greater than the previous nonce');
- });
-
- it('should fail if the version number is incorrect', function() {
- var payload = new Buffer('message');
- var fromkey = key;
- var topubkey = key2.public;
- var version1 = new Buffer([2]);
- var version2 = new Buffer([0]);
- var nonce = new Buffer([0, 0, 0, 0, 0, 0, 0, 0]);
- var toencrypt = Buffer.concat([version1, version2, nonce, payload]);
- var toencrypt_workaround = new Buffer(toencrypt.toString('hex'));
- var encrypted = Message._encrypt(topubkey, toencrypt_workaround);
- var sig = Message._sign(fromkey, encrypted);
- var encoded = {
- pubkey: fromkey.public.toString('hex'),
- sig: sig.toString('hex'),
- encrypted: encrypted.toString('hex')
- };
-
- (function() {Message.decode(key2, encoded);}).should.throw('Invalid version number');
- });
-
- });
-
- describe('#_encrypt', function() {
-
- it('should encrypt data', function() {
- var payload = new Buffer('payload');
- var encrypted = Message._encrypt(key.public, payload);
- encrypted.length.should.equal(129);
- });
-
- });
-
- describe('#_decrypt', function() {
- var payload = new Buffer('payload');
- var payloadhex = payload.toString('hex');
-
- it('should decrypt encrypted data', function() {
- var encrypted = Message._encrypt(key.public, payload);
- var decrypted = Message._decrypt(key.private, encrypted);
- decrypted.toString('hex').should.equal(payloadhex);
- });
-
- });
-
- describe('#_sign', function() {
-
- it('should sign data', function() {
- var payload = new Buffer('payload');
- var sig = Message._sign(key, payload);
- sig.length.should.be.greaterThan(60);
- });
-
- });
-
- describe('#_verify', function() {
- var payload = new Buffer('payload');
- var sig = Message._sign(key, payload);
-
- it('should verify signed data', function() {
- Message._verify(key.public, sig, payload).should.equal(true);
- });
-
- });
-
-});
diff --git a/test/test.Wallet.js b/test/test.Wallet.js
index e46c1c3c3..3406cf42c 100644
--- a/test/test.Wallet.js
+++ b/test/test.Wallet.js
@@ -441,7 +441,7 @@ describe('Wallet model', function() {
receiveIndex: 2
}]
};
- w._handleIndexes('senderID', aiObj, true);
+ w._onIndexes('senderID', aiObj, true);
w.publicKeyRing.getHDParams(0).getReceiveIndex(2);
w.publicKeyRing.getHDParams(0).getChangeIndex(3);
});
@@ -467,7 +467,7 @@ describe('Wallet model', function() {
copayersExtPubKeys: cepk,
nicknameFor: {},
};
- w._handlePublicKeyRing('senderID', {
+ w._onPublicKeyRing('senderID', {
publicKeyRing: pkrObj
}, true);
w.publicKeyRing.getHDParams(0).getReceiveIndex(2);
@@ -512,7 +512,7 @@ describe('Wallet model', function() {
var stub = sinon.stub(w.publicKeyRing, 'copayersForPubkeys').returns({
'027445ab3a935dce7aee1dadb0d103ed6147a0f83deb80474a04538b2c5bc4d509': 'pepe'
});
- w._handleTxProposal('senderID', txp, true);
+ w._onTxProposal('senderID', txp, true);
Object.keys(w.txProposals.txps).length.should.equal(1);
w.getTxProposals().length.should.equal(1);
//stub.restore();
@@ -525,7 +525,7 @@ describe('Wallet model', function() {
id.should.equal(newId);
done();
});
- w._handleConnect(newId);
+ w._onConnect(newId);
});
it('handle disconnections', function(done) {
@@ -534,7 +534,7 @@ describe('Wallet model', function() {
id.should.equal(newId);
done();
});
- w._handleDisconnect(newId);
+ w._onDisconnect(newId);
});
it('should register new copayers correctly', function() {
@@ -993,7 +993,7 @@ describe('Wallet model', function() {
var senderId = "03baa45498fee1045fa8f91a2913f638dc3979b455498924d3cf1a11303c679cdb";
Object.keys(w.addressBook).length.should.equal(2);
- w._handleAddressBook(senderId, data, true);
+ w._onAddressBook(senderId, data, true);
Object.keys(w.addressBook).length.should.equal(3);
});
@@ -1094,7 +1094,7 @@ describe('Wallet model', function() {
var backup = copayConfig.forceNetwork;
copayConfig.forceNetwork = true;
config.networkName = 'livenet';
- cachedCreateW2.should.throw(Error);
+ createW2.should.throw(Error);
copayConfig.forceNetwork = backup;
});
});
@@ -1252,7 +1252,7 @@ describe('Wallet model', function() {
- describe('_handleTxProposal', function() {
+ describe('_onTxProposal', function() {
var testValidate = function(response, result, done) {
var w = cachedCreateW();
@@ -1278,7 +1278,7 @@ describe('Wallet model', function() {
};
});
- w._handleTxProposal('senderID', txp);
+ w._onTxProposal('senderID', txp);
spy.callCount.should.equal(1);
merge.restore();
};
@@ -1299,11 +1299,11 @@ describe('Wallet model', function() {
});
- describe('_handleReject', function() {
+ describe('_onReject', function() {
it('should fails if unknown tx', function() {
var w = cachedCreateW();
(function() {
- w._handleReject(1, {
+ w._onReject(1, {
ntxid: 1
}, 1);
}).should.throw('Unknown TXP');
@@ -1316,7 +1316,7 @@ describe('Wallet model', function() {
}
};
(function() {
- w._handleReject('john', {
+ w._onReject('john', {
ntxid: 'qwerty'
}, 1);
}).should.throw('already signed');
@@ -1337,7 +1337,7 @@ describe('Wallet model', function() {
var spy2 = sinon.spy(w, 'emit');
w.txProposals.txps['qwerty'] = new txp();
w.txProposals.txps['qwerty'].ok.should.equal(0);
- w._handleReject('john', {
+ w._onReject('john', {
ntxid: 'qwerty'
}, 1);
w.txProposals.txps['qwerty'].ok.should.equal(1);
@@ -1353,11 +1353,11 @@ describe('Wallet model', function() {
});
- describe('_handleSeen', function() {
+ describe('_onSeen', function() {
it('should fails if unknown tx', function() {
var w = cachedCreateW();
(function() {
- w._handleReject(1, {
+ w._onReject(1, {
ntxid: 1
}, 1);
}).should.throw('Unknown TXP');
@@ -1378,7 +1378,7 @@ describe('Wallet model', function() {
var spy2 = sinon.spy(w, 'emit');
w.txProposals.txps['qwerty'] = new txp();
w.txProposals.txps['qwerty'].ok.should.equal(0);
- w._handleSeen('john', {
+ w._onSeen('john', {
ntxid: 'qwerty'
}, 1);
w.txProposals.txps['qwerty'].ok.should.equal(1);
@@ -1402,9 +1402,9 @@ describe('Wallet model', function() {
it('#disconnect', function() {
var w = cachedCreateW();
- var spy1 = sinon.spy(w.network, 'disconnect');
+ var spy1 = sinon.spy(w, 'send');
w.disconnect();
- spy1.callCount.should.equal(1);
+ spy1.calledOnce.should.be.true;
});
});
diff --git a/test/test.blockchain.Insight.js b/test/test.blockchain.Insight.js
index e80d628f8..9eb9af6f1 100644
--- a/test/test.blockchain.Insight.js
+++ b/test/test.blockchain.Insight.js
@@ -9,6 +9,7 @@ try {
} catch (e) {
var copay = require('../copay'); //node
}
+var Buffer = bitcore.Buffer;
var Insight = copay.Insight || require('../js/models/blockchain/Insight');
var ID = '933bf321393459b7';
diff --git a/test/test.network.Async.js b/test/test.network.Async.js
new file mode 100644
index 000000000..4d46dabb3
--- /dev/null
+++ b/test/test.network.Async.js
@@ -0,0 +1,285 @@
+'use strict';
+
+var chai = chai || require('chai');
+var should = chai.should();
+var expect = chai.expect;
+var sinon = sinon || require('sinon');
+var bitcore = bitcore || require('bitcore');
+var copay = copay || require('../copay');
+var Async = copay.Async;
+var EventEmitter = require('events').EventEmitter;
+
+describe('Network / Async', function() {
+
+
+ var createN = function(pk) {
+ var n = new Async();
+ var fakeSocket = {};
+ fakeSocket.emit = function() {};
+ fakeSocket.on = function() {};
+ fakeSocket.disconnect = function() {};
+ n.createSocket = function() {
+ return fakeSocket;
+ };
+ var opts = {
+ copayerId: '03b51d01d798522cf61211b4dfcdd6db219ee33cf166e1cb7f43d836ab00ccddee',
+ privkey: pk || '31701118abde096d166607115ed00ce74a2231f68f43144406c863f5ebf06c32',
+ lastTimestamp: 1,
+ };
+ n.start(opts);
+ return n;
+ };
+
+ it('should create an instance', function() {
+ var n = createN();
+ should.exist(n);
+ });
+
+ describe('#cleanUp', function() {
+
+ it('should not set netKey', function() {
+ var n = createN();
+ (n.netKey === undefined).should.equal(true);
+ });
+
+ it('should set privkey to null', function() {
+ var n = createN();
+ n.cleanUp();
+ expect(n.privkey).to.equal(null);
+ });
+
+ it('should remove handlers', function() {
+ var n = createN();
+ var save = Async.prototype.removeAllListeners;
+ var spy = Async.prototype.removeAllListeners = sinon.spy();
+ n.cleanUp();
+ spy.calledOnce.should.equal(true);
+ Async.prototype.removeAllListeners = save;
+ });
+ });
+
+ describe('#send', function() {
+
+ it('should be able to broadcast', function() {
+ var n = createN('9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08');
+ var spy = sinon.spy();
+ n.send(null, 'hello', spy);
+ spy.calledOnce.should.be.true;
+ });
+ it('should call _sendToOne for a copayer', function(done) {
+ var n = createN('9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08');
+ var data = 'my data to send';
+
+ var copayerId = '03b51d01d798522cf61211b4dfcdd6d01020304cf166e1cb7f43d836abc5c18b23';
+ n._sendToOne = function(a, b, cb) {
+ cb();
+ };
+ var opts = {};
+ n.send(copayerId, data, done);
+
+ });
+
+ it('should call _sendToOne with encrypted data for a copayer', function(done) {
+ var n = createN('9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08');
+ var data = new bitcore.Buffer('my data to send');
+
+ var copayerId = '03b51d01d798522cf61001b4dfcdd6db219ee33cf166e1cb7f43d836abc5c18b23';
+ n._sendToOne = function(a1, enc, cb) {
+ var encPayload = JSON.parse(enc.toString());
+ encPayload.sig.length.should.be.greaterThan(0);
+ cb();
+ };
+ var opts = {};
+ n.send(copayerId, data, function() {
+ done();
+ });
+
+ });
+
+ it('should call _sendToOne for a list of copayers', function(done) {
+ var n = createN('9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08');
+ var data = new bitcore.Buffer('my data to send');
+ var copayerIds = ['03b51d01d798522cf61211b4dfcdd6db219ee33cf166e1cb7f43d836abc5c18b23'];
+ n._sendToOne = function(a1, a2, cb) {
+ cb();
+ };
+ var opts = {};
+ n.send(copayerIds, data, function() {
+ done();
+ });
+
+ });
+ });
+
+ describe('#_onMessage', function() {
+ var pk1 = 'fb23b9074ca5e7163719b86b41c7ce8348cf3d2839bb5f6125ef6efd5d40d7d3';
+ var cid1 = '0311a10109320efb3646c832d3e140c6d9c4f69b16e73fc3f0c23b3d014ec77828';
+
+ var pk2 = '89073fe4d3fdef2c5f2909bcda92e4470633f08640d1a62acc464327d611577e';
+ var cid2 = '03ceefb9dbcf7410411e5c1268d9d8e850ffd3a55da764a8377f3212571a52c01b';
+
+ var pk3 = 'a2ae2c7029c6a4136d7fe60c4d078a2e9d5af8a246bf2d5fee3410e273a5d430';
+ var cid3 = '034d3dd2054234737c1cff9d973c9c7e0fb5902c8e56c9d57a699b7842cedfe984';
+
+ it('should not reject data sent from a peer with hijacked pubkey', function() {
+ var n1 = createN(pk1);
+ var n2 = createN(pk2);
+ n2._deletePeer = sinon.spy();
+
+ var message = {
+ type: 'hello',
+ copayerId: cid1
+ };
+ var enc = n1.encode(cid2, message);
+ n2._onMessage(enc);
+ n2._deletePeer.calledOnce.should.equal(false);
+ });
+
+ it('should reject data sent from a peer with hijacked pubkey', function() {
+ var n = createN(pk2);
+
+ var message = {
+ type: 'hello',
+ copayerId: cid3 // MITM
+ };
+
+ var enc = n.encode(cid2, message);
+
+ n._deletePeer = sinon.spy();
+
+ n._onMessage(enc);
+ n._deletePeer.calledOnce.should.equal(true);
+ n._deletePeer.getCall(0).args[1].should.equal('incorrect pubkey for peerId');
+ });
+
+ it('should not reject data sent from a peer with no previously set nonce but who is setting one now', function() {
+ var n1 = createN(pk1);
+ var n2 = createN(pk2);
+ n2._deletePeer = sinon.spy();
+
+ var message = {
+ type: 'hello',
+ copayerId: cid1
+ };
+ var nonce = new Buffer('0000000000000001', 'hex');
+ var enc = n1.encode(cid2, message, nonce);
+ n2._onMessage(enc);
+ n2._deletePeer.calledOnce.should.equal(false);
+ n2.getHexNonces()[cid1].toString('hex').should.equal('0000000000000001');
+ });
+
+ it('should not reject data sent from a peer with a really big new nonce', function() {
+ var n1 = createN(pk1);
+ var n2 = createN(pk2);
+ n2._deletePeer = sinon.spy();
+
+ var message = {
+ type: 'hello',
+ copayerId: cid1
+ };
+ n2.networkNonces = {};
+ n2.networkNonces[cid1] = new Buffer('5000000000000001', 'hex');
+ var nonce = new Buffer('5000000000000002', 'hex')
+ var enc = n1.encode(cid2, message, nonce);
+ n2._onMessage(enc);
+ n2._deletePeer.calledOnce.should.equal(false);
+ n2.getHexNonces()[cid1].toString('hex').should.equal('5000000000000002');
+ n2._deletePeer.calledOnce.should.equal(false);
+ });
+
+ it('should reject data sent from a peer with an outdated nonce', function() {
+ var n1 = createN(pk1);
+ var n2 = createN(pk2);
+ n2._deletePeer = sinon.spy();
+
+ var message = {
+ type: 'hello',
+ copayerId: cid1
+ };
+ n2.networkNonces = {};
+ n2.networkNonces[cid1] = new Buffer('0000000000000002', 'hex');
+ var nonce = new Buffer('0000000000000001', 'hex');
+ var enc = n1.encode(cid2, message, nonce);
+ n2._onMessage(enc);
+ n2._deletePeer.calledOnce.should.equal(true);
+ });
+
+ });
+
+ describe('#setHexNonce', function() {
+
+ it('should set a nonce from a hex value', function() {
+ var hex = '0000000000000000';
+ var n = createN();
+ n.setHexNonce(hex);
+ n.getHexNonce().should.equal(hex);
+ n.networkNonce.toString('hex').should.equal(hex);
+ });
+
+ });
+
+ describe('#setHexNonces', function() {
+
+ it('should set a nonce from a hex value', function() {
+ var hex = '0000000000000000';
+ var n = createN();
+ n.setHexNonces({
+ fakeid: hex
+ });
+ n.getHexNonces().fakeid.should.equal(hex);
+ });
+
+ });
+
+ describe('#getHexNonce', function() {
+
+ it('should get a nonce hex value', function() {
+ var hex = '0000000000000000';
+ var n = createN();
+ n.setHexNonce(hex);
+ n.getHexNonce().should.equal(hex);
+ });
+
+ });
+
+ describe('#getHexNonces', function() {
+
+ it('should get a nonce from a hex value', function() {
+ var hex = '0000000000000000';
+ var n = createN();
+ n.setHexNonces({
+ fakeid: hex
+ });
+ n.getHexNonces().fakeid.should.equal(hex);
+ });
+
+ });
+
+ describe('#iterateNonce', function() {
+
+ it('should set a nonce not already set', function() {
+ var n = createN();
+ n.iterateNonce();
+ n.networkNonce.slice(4, 8).toString('hex').should.equal('00000001');
+ n.networkNonce.slice(0, 4).toString('hex').should.not.equal('00000000');
+ });
+
+ it('called twice should increment', function() {
+ var n = createN();
+ n.iterateNonce();
+ n.networkNonce.slice(4, 8).toString('hex').should.equal('00000001');
+ n.iterateNonce();
+ n.networkNonce.slice(4, 8).toString('hex').should.equal('00000002');
+ });
+
+ it('should set the first byte to the most significant "now" digit', function() {
+ var n = createN();
+ n.iterateNonce();
+ var buf = new Buffer(4);
+ buf.writeUInt32BE(Math.floor(Date.now() / 1000), 0);
+ n.networkNonce[0].should.equal(buf[0]);
+ });
+
+ });
+
+});
diff --git a/test/test.network.WebRTC.js b/test/test.network.WebRTC.js
deleted file mode 100644
index 8231991dc..000000000
--- a/test/test.network.WebRTC.js
+++ /dev/null
@@ -1,507 +0,0 @@
-'use strict';
-
-var chai = chai || require('chai');
-var should = chai.should();
-var expect = chai.expect;
-var sinon = sinon || require('sinon');
-var bitcore = bitcore || require('bitcore');
-var WebRTC = require('../js/models/network/WebRTC');
-
-describe('Network / WebRTC', function() {
-
- it('should create an instance', function() {
- var n = new WebRTC();
- should.exist(n);
- });
-
- describe('#WebRTC constructor', function() {
-
- it('should set reconnect attempts', function() {
- var n = new WebRTC();
- n.reconnectAttempts.should.equal(3);
- });
-
- it('should call cleanUp', function() {
- var save = WebRTC.prototype.cleanUp;
- WebRTC.prototype.cleanUp = sinon.spy();
- var n = new WebRTC();
- n.cleanUp.calledOnce.should.equal(true);
- WebRTC.prototype.cleanUp = save;
- });
- });
-
- describe('#cleanUp', function() {
-
- it('should not set netKey', function() {
- var n = new WebRTC();
- (n.netKey === undefined).should.equal(true);
- });
-
- it('should set privkey to null', function() {
- var n = new WebRTC();
- n.cleanUp();
- expect(n.privkey).to.equal(null);
- });
-
- it('should remove handlers', function() {
- var n = new WebRTC();
- var save = WebRTC.prototype.removeAllListeners;
- var spy = WebRTC.prototype.removeAllListeners = sinon.spy();
- n.cleanUp();
- spy.calledOnce.should.equal(true);
- WebRTC.prototype.removeAllListeners = save;
- });
- });
-
-
- describe('#_setupPeerHandlers', function() {
- var n = new WebRTC();
- n.peer = {};
- var spy = n.peer.on = sinon.spy();
- it('should setup handlers', function() {
- n._setupPeerHandlers();
- spy.calledWith('connection').should.equal(true);
- spy.calledWith('open').should.equal(true);
- spy.calledWith('error').should.equal(true);
- });
- });
-
- describe('#_handlePeerOpen', function() {
- var n = new WebRTC();
- it('should call openCallback handler', function(done) {
- n.peerId = 1;
- n.copayerId = 2;
- n._handlePeerOpen(function() {
- n.connectedPeers.should.deep.equal([1]);
- n.copayerForPeer.should.deep.equal({
- 1: 2
- });
- done();
- });
- });
- });
-
- describe('#_handlePeerError', function() {
- var log = console.log;
- var n = new WebRTC();
- it('should call _checkAnyPeer on could not connect error', function() {
- var save = n._checkAnyPeer;
- var spy = n._checkAnyPeer = sinon.spy();
- var logSpy = console.log = sinon.spy();
- n._handlePeerError({
- message: 'Could not connect to peer xxx'
- });
- console.log = log;
- spy.called.should.equal(true);
- logSpy.called.should.equal(true);
- n._checkAnyPeer = save;
- });
-
- it('should call not call _checkAnyPeer other error', function() {
- var save = n._checkAnyPeer;
- var spy = n._checkAnyPeer = sinon.spy();
- var otherMessage = 'Could connect to peer xxx';
- var logSpy = console.log = sinon.spy();
- n._handlePeerError({
- message: otherMessage,
- });
- console.log = log;
- spy.called.should.equal(false);
- n.criticalError.should.equal(otherMessage);
- logSpy.called.should.equal(true);
- n._checkAnyPeer = save;
- });
-
- });
-
-
-
- describe('#_encode', function() {
-
- it('should encode data successfully', function() {
- var n = new WebRTC();
- var data = new bitcore.Buffer('my data to encode');
- var privkeystr = new bitcore.Buffer('test privkey');
- var privkey = bitcore.util.sha256(privkeystr);
- var key = new bitcore.Key();
- key.private = privkey;
- key.regenerateSync();
- var encoded = n._encode(key.public, key, data);
- should.exist(encoded);
- encoded.sig.length.should.not.equal(0);
- encoded.pubkey.length.should.not.equal(0);
- encoded.encrypted.length.should.not.equal(0);
- });
-
- });
-
- describe('#_decode', function() {
-
- it('should decode that which was encoded', function() {
- var n = new WebRTC();
- var data = new bitcore.Buffer('my data to encrypt');
- var privkeystr = new bitcore.Buffer('test privkey');
- var privkey = bitcore.util.sha256(privkeystr);
- var key = new bitcore.Key();
- key.private = privkey;
- key.regenerateSync();
- var encoded = n._encode(key.public, key, data);
- var decoded = n._decode(key, encoded);
- encoded.sig.should.not.equal(0);
- decoded.payload.toString().should.equal('my data to encrypt');
- });
-
- });
-
- describe('#send', function() {
-
- it('should call _sendToOne for a copayer', function(done) {
- var n = new WebRTC();
- n.privkey = bitcore.util.sha256('test');
-
- var data = new bitcore.Buffer('my data to send');
-
- var privkeystr = new bitcore.Buffer('test privkey');
- var privkey = bitcore.util.sha256(privkeystr);
- var key = new bitcore.Key();
- key.private = privkey;
- key.regenerateSync();
-
- var copayerId = key.public.toString('hex');
- n._sendToOne = function(a1, a2, cb) {
- cb();
- };
- var opts = {};
- n.send(copayerId, data, function() {
- done();
- });
-
- });
-
- it('should call _sendToOne with encrypted data for a copayer', function(done) {
- var n = new WebRTC();
- n.privkey = bitcore.util.sha256('test');
-
- var data = new bitcore.Buffer('my data to send');
-
- var privkeystr = new bitcore.Buffer('test privkey');
- var privkey = bitcore.util.sha256(privkeystr);
- var key = new bitcore.Key();
- key.private = privkey;
- key.regenerateSync();
-
- var copayerId = key.public.toString('hex');
- n._sendToOne = function(a1, enc, cb) {
- var encPayload = JSON.parse(enc.toString());
- encPayload.sig.length.should.be.greaterThan(0);
- cb();
- };
- var opts = {};
- n.send(copayerId, data, function() {
- done();
- });
-
- });
-
- it('should call _sendToOne for a list of copayers', function(done) {
- var n = new WebRTC();
- n.privkey = bitcore.util.sha256('test');
-
- var data = new bitcore.Buffer('my data to send');
-
- var privkeystr = new bitcore.Buffer('test privkey');
- var privkey = bitcore.util.sha256(privkeystr);
- var key = new bitcore.Key();
- key.private = privkey;
- key.regenerateSync();
-
- var copayerIds = [key.public.toString('hex')];
- n._sendToOne = function(a1, a2, cb) {
- cb();
- };
- var opts = {};
- n.send(copayerIds, data, function() {
- done();
- });
-
- });
- });
-
- describe('#_onData', function() {
- var privkey1 = bitcore.util.sha256('test privkey 1');
- var privkey2 = bitcore.util.sha256('test privkey 2');
- var privkey3 = bitcore.util.sha256('test privkey 2');
-
- var key1 = new bitcore.Key();
- key1.private = privkey1;
- key1.regenerateSync();
-
- var key2 = new bitcore.Key();
- key2.private = privkey2;
- key2.regenerateSync();
-
- var key3 = new bitcore.Key();
- key3.private = privkey3;
- key3.regenerateSync();
-
- it('should not reject data sent from a peer with hijacked pubkey', function() {
- var n = new WebRTC();
- n.privkey = key2.private.toString('hex');
-
- var message = {
- type: 'hello',
- copayerId: key1.public.toString('hex')
- };
- var messagestr = JSON.stringify(message);
- var messagebuf = new Buffer(messagestr);
-
- var encoded = n._encode(key2.public, key1, messagebuf);
- var encodedstr = JSON.stringify(encoded);
- var encodeduint = new Buffer(encodedstr);
-
- var isInbound = true;
- var peerId = new bitcore.SIN(key1.public);
-
- n._deletePeer = sinon.spy();
-
- n._onData(encodeduint, isInbound, peerId);
- n._deletePeer.calledOnce.should.equal(false);
- });
-
- it('should reject data sent from a peer with hijacked pubkey', function() {
- var n = new WebRTC();
- n.privkey = key2.private.toString('hex');
-
- var message = {
- type: 'hello',
- copayerId: key3.public.toString('hex') //MITM pubkey 3
- };
- var messagestr = JSON.stringify(message);
- var messagebuf = new Buffer(messagestr);
-
- var encoded = n._encode(key2.public, key1, messagebuf);
- var encodedstr = JSON.stringify(encoded);
- var encodeduint = new Buffer(encodedstr);
-
- var isInbound = true;
- var peerId = new bitcore.SIN(key1.public);
-
- n._deletePeer = sinon.spy();
-
- n._onData(encodeduint, isInbound, peerId);
- n._deletePeer.calledOnce.should.equal(true);
- n._deletePeer.getCall(0).args[0].should.equal(peerId);
- n._deletePeer.getCall(0).args[1].should.equal('incorrect pubkey for peerId');
- });
-
- it('should not reject data sent from a peer with no previously set nonce but who is setting one now', function() {
- var n = new WebRTC();
- n.privkey = key2.private.toString('hex');
- //n.networkNonces = {};
- //n.networkNonces[(new bitcore.SIN(key1.public)).toString()] = new Buffer('0000000000000001', 'hex'); //previously used nonce
-
- var message = {
- type: 'hello',
- copayerId: key1.public.toString('hex')
- };
- var messagestr = JSON.stringify(message);
- var messagebuf = new Buffer(messagestr);
-
- var opts = {nonce: new Buffer('0000000000000001', 'hex')}; //message send with new nonce
- var encoded = n._encode(key2.public, key1, messagebuf, opts);
- var encodedstr = JSON.stringify(encoded);
- var encodeduint = new Buffer(encodedstr);
-
- var isInbound = true;
- var peerId = new bitcore.SIN(key1.public);
-
- n._deletePeer = sinon.spy();
-
- n._onData(encodeduint, isInbound, peerId);
- n._deletePeer.calledOnce.should.equal(false);
- n.getHexNonces()[(new bitcore.SIN(key1.public)).toString()].toString('hex').should.equal('0000000000000001');
- });
-
- it('should not reject data sent from a peer with a really big new nonce', function() {
- var n = new WebRTC();
- n.privkey = key2.private.toString('hex');
- n.networkNonces = {};
- n.networkNonces[(new bitcore.SIN(key1.public)).toString()] = new Buffer('5000000000000001', 'hex'); //previously used nonce
-
- var message = {
- type: 'hello',
- copayerId: key1.public.toString('hex')
- };
- var messagestr = JSON.stringify(message);
- var messagebuf = new Buffer(messagestr);
-
- var opts = {nonce: new Buffer('5000000000000002', 'hex')}; //message send with new nonce
- var encoded = n._encode(key2.public, key1, messagebuf, opts);
- var encodedstr = JSON.stringify(encoded);
- var encodeduint = new Buffer(encodedstr);
-
- var isInbound = true;
- var peerId = new bitcore.SIN(key1.public);
-
- n._deletePeer = sinon.spy();
-
- n._onData(encodeduint, isInbound, peerId);
- n._deletePeer.calledOnce.should.equal(false);
- });
-
- it('should not reject data sent from a peer with a really big new nonce', function() {
- var n = new WebRTC();
- n.privkey = key2.private.toString('hex');
- n.networkNonces = {};
- n.networkNonces[(new bitcore.SIN(key1.public)).toString()] = new Buffer('5000000000000001', 'hex'); //previously used nonce
-
- var message = {
- type: 'hello',
- copayerId: key1.public.toString('hex')
- };
- var messagestr = JSON.stringify(message);
- var messagebuf = new Buffer(messagestr);
-
- var opts = {nonce: new Buffer('5000000000000002', 'hex')}; //message send with new nonce
- var encoded = n._encode(key2.public, key1, messagebuf, opts);
- var encodedstr = JSON.stringify(encoded);
- var encodeduint = new Buffer(encodedstr);
-
- var isInbound = true;
- var peerId = new bitcore.SIN(key1.public);
-
- n._deletePeer = sinon.spy();
-
- n._onData(encodeduint, isInbound, peerId);
- n._deletePeer.calledOnce.should.equal(false);
- });
-
- it('should reject data sent from a peer with an outdated nonce', function() {
- var n = new WebRTC();
- n.privkey = key2.private.toString('hex');
- n.networkNonces = {};
- n.networkNonces[(new bitcore.SIN(key1.public)).toString()] = new Buffer('0000000000000002', 'hex'); //previously used nonce
-
- var message = {
- type: 'hello',
- copayerId: key1.public.toString('hex')
- };
- var messagestr = JSON.stringify(message);
- var messagebuf = new Buffer(messagestr);
-
- var opts = {nonce: new Buffer('0000000000000001', 'hex')}; //message send with old nonce
- var encoded = n._encode(key2.public, key1, messagebuf, opts);
- var encodedstr = JSON.stringify(encoded);
- var encodeduint = new Buffer(encodedstr);
-
- var isInbound = true;
- var peerId = new bitcore.SIN(key1.public);
-
- n._deletePeer = sinon.spy();
-
- n._onData(encodeduint, isInbound, peerId);
- n._deletePeer.calledOnce.should.equal(true);
- });
-
- it('should reject data sent from a peer with a really big outdated nonce', function() {
- var n = new WebRTC();
- n.privkey = key2.private.toString('hex');
- n.networkNonces = {};
- n.networkNonces[(new bitcore.SIN(key1.public)).toString()] = new Buffer('5000000000000002', 'hex'); //previously used nonce
-
- var message = {
- type: 'hello',
- copayerId: key1.public.toString('hex')
- };
- var messagestr = JSON.stringify(message);
- var messagebuf = new Buffer(messagestr);
-
- var opts = {nonce: new Buffer('5000000000000001', 'hex')}; //message send with old nonce
- var encoded = n._encode(key2.public, key1, messagebuf, opts);
- var encodedstr = JSON.stringify(encoded);
- var encodeduint = new Buffer(encodedstr);
-
- var isInbound = true;
- var peerId = new bitcore.SIN(key1.public);
-
- n._deletePeer = sinon.spy();
-
- n._onData(encodeduint, isInbound, peerId);
- n._deletePeer.calledOnce.should.equal(true);
- });
-
- });
-
- describe('#setHexNonce', function() {
-
- it('should set a nonce from a hex value', function() {
- var hex = '0000000000000000';
- var n = new WebRTC();
- n.setHexNonce(hex);
- n.getHexNonce().should.equal(hex);
- n.networkNonce.toString('hex').should.equal(hex);
- });
-
- });
-
- describe('#setHexNonces', function() {
-
- it('should set a nonce from a hex value', function() {
- var hex = '0000000000000000';
- var n = new WebRTC();
- n.setHexNonces({fakeid: hex});
- n.getHexNonces().fakeid.should.equal(hex);
- });
-
- });
-
- describe('#getHexNonce', function() {
-
- it('should get a nonce hex value', function() {
- var hex = '0000000000000000';
- var n = new WebRTC();
- n.setHexNonce(hex);
- n.getHexNonce().should.equal(hex);
- });
-
- });
-
- describe('#getHexNonces', function() {
-
- it('should get a nonce from a hex value', function() {
- var hex = '0000000000000000';
- var n = new WebRTC();
- n.setHexNonces({fakeid: hex});
- n.getHexNonces().fakeid.should.equal(hex);
- });
-
- });
-
- describe('#iterateNonce', function() {
-
- it('should set a nonce not already set', function() {
- var n = new WebRTC();
- n.iterateNonce();
- n.networkNonce.slice(4, 8).toString('hex').should.equal('00000001');
- n.networkNonce.slice(0, 4).toString('hex').should.not.equal('00000000');
- });
-
- it('called twice should increment', function() {
- var n = new WebRTC();
- n.iterateNonce();
- n.networkNonce.slice(4, 8).toString('hex').should.equal('00000001');
- n.iterateNonce();
- n.networkNonce.slice(4, 8).toString('hex').should.equal('00000002');
- });
-
- it('should set the first byte to the most significant "now" digit', function() {
- var n = new WebRTC();
- n.iterateNonce();
- var buf = new Buffer(4);
- buf.writeUInt32BE(Math.floor(Date.now()/1000), 0);
- n.networkNonce[0].should.equal(buf[0]);
- });
-
- });
-
-});
diff --git a/test/unit/controllers/controllersSpec.js b/test/unit/controllers/controllersSpec.js
index 02ae10eb9..c056246c8 100644
--- a/test/unit/controllers/controllersSpec.js
+++ b/test/unit/controllers/controllersSpec.js
@@ -175,12 +175,12 @@ describe("Unit: Controllers", function() {
});
it('should validate address with network', function() {
- form.newaddress.$setViewValue('1JqniWpWNA6Yvdivg3y9izLidETnurxRQm');
+ form.newaddress.$setViewValue('mkfTyEk7tfgV611Z4ESwDDSZwhsZdbMpVy');
expect(form.newaddress.$invalid).to.equal(false);
});
it('should not validate address with other network', function() {
- form.newaddress.$setViewValue('mkfTyEk7tfgV611Z4ESwDDSZwhsZdbMpVy');
+ form.newaddress.$setViewValue('1JqniWpWNA6Yvdivg3y9izLidETnurxRQm');
expect(form.newaddress.$invalid).to.equal(true);
});
@@ -199,7 +199,7 @@ describe("Unit: Controllers", function() {
});
it('should create a transaction proposal with given values', function() {
- sendForm.address.$setViewValue('1JqniWpWNA6Yvdivg3y9izLidETnurxRQm');
+ sendForm.address.$setViewValue('mkfTyEk7tfgV611Z4ESwDDSZwhsZdbMpVy');
sendForm.amount.$setViewValue(1000);
var spy = sinon.spy(scope.wallet, 'createTx');
@@ -210,7 +210,7 @@ describe("Unit: Controllers", function() {
sinon.assert.callCount(spy, 1);
sinon.assert.callCount(spy2, 0);
sinon.assert.callCount(scope.loadTxs, 1);
- spy.getCall(0).args[0].should.equal('1JqniWpWNA6Yvdivg3y9izLidETnurxRQm');
+ spy.getCall(0).args[0].should.equal('mkfTyEk7tfgV611Z4ESwDDSZwhsZdbMpVy');
spy.getCall(0).args[1].should.equal(1000 * config.unitToSatoshi);
(typeof spy.getCall(0).args[2]).should.equal('undefined');
});
@@ -219,7 +219,7 @@ describe("Unit: Controllers", function() {
it('should handle big values in 100 BTC', function() {
var old = config.unitToSatoshi;
config.unitToSatoshi = 100000000;;
- sendForm.address.$setViewValue('1JqniWpWNA6Yvdivg3y9izLidETnurxRQm');
+ sendForm.address.$setViewValue('mkfTyEk7tfgV611Z4ESwDDSZwhsZdbMpVy');
sendForm.amount.$setViewValue(100);
var spy = sinon.spy(scope.wallet, 'createTx');
scope.loadTxs = sinon.spy();
@@ -232,7 +232,7 @@ describe("Unit: Controllers", function() {
it('should handle big values in 5000 BTC', function() {
var old = config.unitToSatoshi;
config.unitToSatoshi = 100000000;;
- sendForm.address.$setViewValue('1JqniWpWNA6Yvdivg3y9izLidETnurxRQm');
+ sendForm.address.$setViewValue('mkfTyEk7tfgV611Z4ESwDDSZwhsZdbMpVy');
sendForm.amount.$setViewValue(5000);
var spy = sinon.spy(scope.wallet, 'createTx');
scope.loadTxs = sinon.spy();
@@ -245,7 +245,7 @@ describe("Unit: Controllers", function() {
it('should create and send a transaction proposal', function() {
- sendForm.address.$setViewValue('1JqniWpWNA6Yvdivg3y9izLidETnurxRQm');
+ sendForm.address.$setViewValue('mkfTyEk7tfgV611Z4ESwDDSZwhsZdbMpVy');
sendForm.amount.$setViewValue(1000);
scope.wallet.totalCopayers = scope.wallet.requiredCopayers = 1;
var spy = sinon.spy(scope.wallet, 'createTx');
@@ -322,7 +322,7 @@ describe("Unit: Controllers", function() {
it('should return networkName', function() {
$httpBackend.flush(); // need flush
var networkName = scope.networkName;
- expect(networkName).equal('livenet');
+ expect(networkName).equal('testnet');
});
});
diff --git a/util/build.js b/util/build.js
index 1a7f0a9c1..5495a04a2 100644
--- a/util/build.js
+++ b/util/build.js
@@ -44,9 +44,6 @@ var createBundle = function(opts) {
b.require('./js/models/core/WalletLock', {
expose: '../js/models/core/WalletLock'
});
- b.require('./js/models/network/WebRTC', {
- expose: '../js/models/network/WebRTC'
- });
b.require('./js/models/blockchain/Insight', {
expose: '../js/models/blockchain/Insight'
});
@@ -79,9 +76,6 @@ var createBundle = function(opts) {
b.require('./test/mocks/FakeLocalStorage', {
expose: './mocks/FakeLocalStorage'
});
- b.require('./js/models/core/Message', {
- expose: '../js/models/core/Message'
- });
b.require('./test/mocks/FakeBlockchain', {
expose: './mocks/FakeBlockchain'
});
diff --git a/views/includes/video.html b/views/includes/video.html
index 11e04ccb6..9c1b7e7b2 100644
--- a/views/includes/video.html
+++ b/views/includes/video.html
@@ -3,13 +3,13 @@
diff --git a/views/settings.html b/views/settings.html
index 47f92935a..2b231322b 100644
--- a/views/settings.html
+++ b/views/settings.html
@@ -47,22 +47,6 @@
-