Merge pull request #1065 from maraoz/ref/broker

Refactor networking, use insight message broker
This commit is contained in:
Matias Alejo Garcia 2014-08-26 15:07:55 -03:00
commit f63e17eaf3
23 changed files with 1148 additions and 1886 deletions

View File

@ -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,
},

View File

@ -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');

View File

@ -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,

View File

@ -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() {

View File

@ -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;

View File

@ -1,6 +1,5 @@
'use strict';
var preconditions = require('preconditions').instance();
var bitcore = require('bitcore');
var HK = bitcore.HierarchicalKey;

View File

@ -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() {

View File

@ -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);
}
});

382
js/models/network/Async.js Normal file
View File

@ -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;

View File

@ -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;

View File

@ -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;
});

View File

@ -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"
},

View File

@ -15,8 +15,7 @@
<script src="../lib/bitcore.js"></script>
<script src="../js/copayBundle.js"></script>
<script src="test.blockchain.Insight.js"></script>
<script src="test.Message.js"></script>
<script src="test.network.WebRTC.js"></script>
<script src="test.network.Async.js"></script>
<script src="test.PayPro.js"></script>
<script src="test.PrivateKey.js"></script>
<script src="test.PublicKeyRing.js"></script>

View File

@ -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;

View File

@ -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);
});
});
});

View File

@ -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;
});
});

View File

@ -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';

285
test/test.network.Async.js Normal file
View File

@ -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]);
});
});
});

View File

@ -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]);
});
});
});

View File

@ -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');
});
});

View File

@ -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'
});

View File

@ -3,13 +3,13 @@
<video
ng-if="hasVideo(copayer)"
peer="{{copayer}}" avatar autoplay
ng-class="isConnected(copayer) ? 'online' : 'offline'"
ng-class="true || isConnected(copayer) ? 'online' : 'offline'"
ng-src="{{getVideoURL(copayer)}}"></video>
<img
class="br100 no-video"
ng-if="!hasVideo(copayer)"
ng-class="isConnected(copayer) ? 'online' : 'offline'"
ng-class="true || isConnected(copayer) ? 'online' : 'offline'"
src="./img/satoshi.gif"
alt="{{copayer}}"
width="70">

View File

@ -47,22 +47,6 @@
</fieldset>
<fieldset>
<legend>PeerJS server</legend>
<label for="peerjs-key">Key</label>
<input type="text" ng-model="networkKey" class="form-control" name="peerjs-key">
<label for="peerjs-host">Host</label>
<input type="text" ng-model="networkHost" class="form-control" name="peerjs-host">
<label for="peerjs-port">Port</label>
<input type="number" ng-model="networkPort" class="form-control" name="peerjs-port">
<input id="peerjs-secure" type="checkbox" ng-model="networkSecure" class="form-control">
<label for="peerjs-secure">Use SSL</label>
<p class="small">
PeerJS Server is open-source software. You can run your own instance, or use PeerJS Server cloud. Check <a href="http://peerjs.com" target="_blank">PeerJS Server</a>
</p>
</fieldset>
<div class="text-right">
<a class="back-button text-white m20r" href="#!/">&laquo; Back</a>
<button type="submit" class="button primary m0 ng-binding" ng-disabled="setupForm.$invalid || loading" disabled="disabled" ng-click="save()">