diff --git a/copay.js b/copay.js index 84ac2137c..5b2a2951c 100644 --- a/copay.js +++ b/copay.js @@ -1,5 +1,5 @@ module.exports.Storage = require('./js/models/Storage'); module.exports.PublicKeyRing = require('./js/models/PublicKeyRing'); - +module.exports.CopayPeer = require('./js/models/CopayPeer'); module.exports.FakeStorage = require('./test/FakeStorage'); diff --git a/js/config.js b/js/config.js index 1d866dd7f..4372631ec 100644 --- a/js/config.js +++ b/js/config.js @@ -2,4 +2,6 @@ var config = { networkName: 'testnet', + p2pApiKey: 'lwjd5qra8257b9', + p2pDebug: 3 }; diff --git a/js/controllers/signin.js b/js/controllers/signin.js index 42b4183a5..588b47e1d 100644 --- a/js/controllers/signin.js +++ b/js/controllers/signin.js @@ -12,6 +12,7 @@ angular.module('copay.signin').controller('SigninController', Network.init(function() { $location.path('peer'); + $rootScope.$digest(); }); }; @@ -21,7 +22,9 @@ angular.module('copay.signin').controller('SigninController', if (cid) { Network.init(function() { Network.connect(cid, function() { +console.log('[signin.js.26] REDIR'); //TODO $location.path('peer'); + $rootScope.$digest(); }); }); } diff --git a/js/models/CopayPeer.js b/js/models/CopayPeer.js new file mode 100644 index 000000000..e0e2a3aa7 --- /dev/null +++ b/js/models/CopayPeer.js @@ -0,0 +1,256 @@ + +var imports = require('soop').imports(); +var EventEmitter= imports.EventEmitter || require('events').EventEmitter; + + +function CopayPeer(opts) { + opts = opts || {}; + this.peerId = opts.peerId; + this.apiKey = opts.apiKey || 'lwjd5qra8257b9'; + this.debug = opts.debug || 3; + this.connectedPeers = []; +} + +CopayPeer.parent=EventEmitter; + +// Array helpers +CopayPeer._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; +}; + +CopayPeer._inArray = function(el, array) { + return array.indexOf(el) > -1; +}; + +CopayPeer._arrayPushOnce = function(el, array) { + var ret = false; + if (!CopayPeer._inArray(el, array)) { + array.push(el); + ret = true; + } + return ret; +}; + +CopayPeer._arrayRemove = function(el, array) { + var pos = array.indexOf(el); + if (pos >= 0) array.splice(pos, 1); + + return array; +}; + +// DEBUG +CopayPeer.prototype._showConnectedPeers = function() { + console.log("### CONNECTED PEERS", this.connectedPeers); +}; + +CopayPeer.prototype._onClose = function(peerId) { + this.connectedPeers = CopayPeer._arrayRemove(peerId, this.connectedPeers); + this._notify(); +}; + +CopayPeer.prototype._connectToPeers = function(peerIds) { + var self = this; + var ret = false; + var arrayDiff1= CopayPeer._arrayDiff(peerIds, this.connectedPeers); + var arrayDiff = CopayPeer._arrayDiff(arrayDiff1, [this.peerId]); +console.log('[CopayPeer.js.65:arrayDiff:] DIFFFFF',arrayDiff, this.connectedPeers); //TODO + arrayDiff.forEach(function(peerId) { + console.log('### CONNECTING TO:', peerId); + self.connectTo(peerId); + ret = true; + }); + return ret; +}; + +CopayPeer.prototype._onData = function(data, isInbound) { + var obj = JSON.parse(data); + console.log('### RECEIVED TYPE: %s FROM %s', obj.data.type, obj.sender); + + switch(obj.data.type) { + case 'peerList': + var hasChanged = this._connectToPeers(obj.data.peers); + // if (hasChanged && !obj.data.isBroadcast) { + // }; + this._notify(); + break; + case 'disconnect': + this._onClose(obj.sender); + break; + } +}; + +CopayPeer.prototype._sendPeers = function(peerIds) { + var isBroadcast = false; + if (!peerIds) { + peerIds = this.connectedPeers; + isBroadcast = true; + }; + + console.log('#### SENDING PEER LIST: ', this.connectedPeers, ' TO ', peerIds); + this.send(peerIds, { + type: 'peerList', + peers: this.connectedPeers, + isBroadcast: isBroadcast, + }); +}; + +CopayPeer.prototype._addPeer = function(peerId, isInbound) { + + var hasChanged = CopayPeer._arrayPushOnce(peerId, this.connectedPeers); + + + if (isInbound && hasChanged) { + this._sendPeers(); //broadcast peer list + } + else { + if (isInbound) { + this._sendPeers(peerId); + } + } +}; + + +CopayPeer.prototype._setupConnectionHandlers = function(dataConn, isInbound, openCallback) { + var self=this; + + dataConn.on('open', function() { + if (!CopayPeer._inArray(dataConn.peer, self.connectedPeers)) { + + console.log('### DATA CONNECTION READY TO: ADDING PEER: %s (inbound: %s)', + dataConn.peer, isInbound); + + self._addPeer(dataConn.peer, isInbound); + self._notify(); + if (typeof openCallback === 'function') openCallback(); + } + }); + + dataConn.on('data', function(data) { + self._onData(data, isInbound); + }); + + dataConn.on('error', function(e) { + console.log('### ## INBOUND DATA ERROR',e ); //TODO + }); + + dataConn.on('close', function() { + self._onClose(dataConn.peer); + }); +}; + +CopayPeer.prototype._notify = function() { + this._showConnectedPeers(); + this.emit('update'); +}; + +CopayPeer.prototype._setupPeerHandlers = function(openCallback) { + var self=this; + var p = this.peer; + + + p.on('open', function(peerId) { + console.log('### PEER OPEN. I AM:' + peerId); + self.peerId = peerId; + self.connectedPeers = [peerId]; + self._notify(); + return openCallback(peerId); + }); + + p.on('error', function(err) { + console.log('### PEER ERROR:', err); + }); + + p.on('connection', function(dataConn) { + console.log('### NEW INBOUND CONNECTION'); //TODO + self._setupConnectionHandlers(dataConn, true); + }); +}; + +CopayPeer.prototype.start = function(openCallback) { + // Start PeerJS Peer + this.peer = new Peer(this.peerId, { + key: this.apiKey, // TODO: we need our own PeerServer KEY (http://peerjs.com/peerserver) + debug: this.debug, + }); + + this._setupPeerHandlers(openCallback); +}; + +CopayPeer.prototype._sendToOne = function(peerId, data, cb) { + if (peerId !== this.peerId) { + var conns = this.peer.connections[peerId]; + + if (conns) { + var str = JSON.stringify({ + sender: this.peerId, + data: data + }); + + for (var i = 0; i < conns.length; i++) { + var conn = conns[i]; + conn.send(str); + } + } + } + if (typeof cb === 'function') cb(); +}; + + +CopayPeer.prototype.send = function(peerIds, data, cb) { + var self=this; + + if (Array.isArray(peerIds)) { + var l = peerIds.length; + var i = 0; + peerIds.forEach(function(peerId) { + self._sendToOne(peerId, data, function () { + if (++i === l && typeof cb === 'function') cb(); + }); + }); + } + else if (typeof peerIds === 'string') + self._sendToOne(peerIds, data, cb); +}; + +CopayPeer.prototype.connectTo = function(peerId, cb) { + var self = this; + + console.log('### STARTING TO CONNECT TO:' + peerId ); + + var dataConn = this.peer.connect(peerId, { +// label: 'wallet', + serialization: 'none', + reliable: true, + metadata: { message: 'hi copayer!' } + }); + + self._setupConnectionHandlers(dataConn, false, cb); +}; + +CopayPeer.prototype.disconnect = function(peerId, cb) { + var self = this; + + this.send(this.connectedPeers, { type: 'disconnect' }, function() { + self.connectedPeers = []; + self.peerId = null; + if (self.peer) { + self.peer.disconnect(); + self.peer.destroy(); + self.peer = null; + } + if (typeof cb === 'function') cb(); + }); +}; + + +module.exports = require('soop')(CopayPeer); diff --git a/js/models/Peer.js b/js/models/Peer.js new file mode 100644 index 000000000..2d76a7d15 --- /dev/null +++ b/js/models/Peer.js @@ -0,0 +1,8 @@ + + +function Peer(id) { + this.id = id; +}; + + +module.exports = require('soop')(Peer); diff --git a/js/models/Storage.js b/js/models/Storage.js index c6036747c..6f9ce6e59 100644 --- a/js/models/Storage.js +++ b/js/models/Storage.js @@ -7,12 +7,20 @@ function Storage() { } Storage.prototype.get = function(k) { - return this.data[k]; + return JSON.parse(localStorage.getItem(k)); }; Storage.prototype.set = function(k,v) { - this.data[k]=v; + localStorage.setItem(k, JSON.stringify(v)); +}; + +Storage.prototype.remove = function(k) { + localStorage.removeItem(k); }; +Storage.prototype.clearAll = function() { + localStorage.clear(); +}; + module.exports = require('soop')(Storage); diff --git a/js/services/network.js b/js/services/network.js index 6cea32267..11940fab4 100644 --- a/js/services/network.js +++ b/js/services/network.js @@ -3,273 +3,112 @@ angular.module('copay.network') .factory('Network', function($rootScope, Storage) { var peer; - $rootScope.connectedPeers = []; - $rootScope.peerId = null; +// $rootScope.connectedPeers = []; +// $rootScope.peerId = null; - // Array helpers - var _arrayDiff = function(a, b) { - var seen = []; - var diff = []; + // case 'publicKeyRing': + // console.log('### RECEIVED PKR FROM:', obj.sender); - for (var i = 0; i < b.length; i++) - seen[b[i]] = true; - - for (var i = 0; i < a.length; i++) - if (!seen[a[i]]) - diff.push(a[i]); - - return diff; - }; - - var _inArray = function(el, array) { - return array.indexOf(el) > -1; - }; - - var _arrayPushOnce = function(el, array) { - if (!_inArray(el, array)) array.push(el); - }; - - var _arrayRemove = function(el, array) { - var pos = array.indexOf(el); - if (pos >= 0) array.splice(pos, 1); - - return array; - }; - - // General helpers - var _saveDataStorage = function() { - Storage.save('peerData', { - peerId: $rootScope.peerId, - connectedPeers: $rootScope.connectedPeers - }); - }; - - var _sendToOne = function(pid, data, cb) { - if (pid !== $rootScope.peerId) { - var conns = peer.connections[pid]; - - if (conns) { - var str = JSON.stringify({ - sender: $rootScope.peerId, - data: data - }); - - for (var i = 0; i < conns.length; i++) { - var conn = conns[i]; - conn.send(str); - } - - if (typeof cb === 'function') cb(); - } - } - }; - - var _onData = function(data, isOutbound) { - var obj = JSON.parse(data); - console.log('### RECEIVED TYPE: %s FROM %s', obj.data.type, obj.sender); - switch(obj.data.type) { - case 'peerList': - if (_connectToPeers(obj.data.peers)) { - //TODO Remove log - console.log('### BROADCASTING PEER LIST'); - _send( $rootScope.connectedPeers, { - type: 'peerList', - peers: $rootScope.connectedPeers, - isBroadcast: 1, - }); - $rootScope.$digest(); - } - else if (!isOutbound && !obj.data.isBroadcast) { - // replying always to connecting peer - console.log('### REPLYING PEERLIST TO:', obj.sender ); - _send( obj.sender, { - type: 'peerList', - peers: $rootScope.connectedPeers - }); - } - break; - case 'disconnect': - _onClose(obj.sender); - break; - case 'publicKeyRing': - console.log('### RECEIVED PKR FROM:', obj.sender); - - if ($rootScope.publicKeyRing.merge(obj.data.publicKeyRing, true)) { - //TODO Remove log - console.log('### BROADCASTING PRK'); - _send( $rootScope.connectedPeers, { - type: 'publicKeyRing', - publicKeyRing: $rootScope.publicKeyRing.toObj(), - isBroadcast: 1, - }); - $rootScope.$digest(); - } - else if (!isOutbound && !obj.data.isBroadcast) { - // replying always to connecting peer - console.log('### REPLYING PRK TO:', obj.sender ); - _send( obj.sender, { - type: 'publicKeyRing', - publicKeyRing: $rootScope.publicKeyRing.toObj(), - }); + // if ($rootScope.publicKeyRing.merge(obj.data.publicKeyRing, true)) { + // //TODO Remove log + // console.log('### BROADCASTING PRK'); + // _send( $rootScope.connectedPeers, { + // type: 'publicKeyRing', + // publicKeyRing: $rootScope.publicKeyRing.toObj(), + // isBroadcast: 1, + // }); + // $rootScope.$digest(); + // } + // else if (!isOutbound && !obj.data.isBroadcast) { + // // replying always to connecting peer + // console.log('### REPLYING PRK TO:', obj.sender ); + // _send( obj.sender, { + // type: 'publicKeyRing', + // publicKeyRing: $rootScope.publicKeyRing.toObj(), + // }); - } + // } - //TODO Remove log - console.log('*** PRK:', $rootScope.publicKeyRing.toObj()); - break; - } - }; + // //TODO Remove log + // console.log('*** PRK:', $rootScope.publicKeyRing.toObj()); + // break; + // } + + // // TODO + // $rootScope.publicKeyRing = new copay.PublicKeyRing({ + // network: config.networkName, + // }); + // $rootScope.publicKeyRing.addCopayer(); + // console.log('### PublicKeyRing Initialized'); + // + // // + + // console.log('#### SENDING PKR '); + // _send(dataConn.peer, { + // type: 'publicKeyRing', + // publicKeyRing: $rootScope.publicKeyRing.toObj(), + // }); + + // if (typeof cb === 'function') cb(); + // + // $rootScope.$digest(); + // }); - var _onClose = function(pid) { - $rootScope.connectedPeers = _arrayRemove(pid, $rootScope.connectedPeers); - _saveDataStorage(); - $rootScope.$digest(); - }; - var _connectToPeers = function(peers) { - var ret = false; - var arrayDiff1= _arrayDiff(peers, $rootScope.connectedPeers); - var arrayDiff = _arrayDiff(arrayDiff1, [$rootScope.peerId]); - arrayDiff.forEach(function(pid) { - console.log('### CONNECTING TO:',pid); - ret = true; - connect(pid); - }); - return ret; - }; // public methods var init = function(cb) { - peer = new Peer($rootScope.peerId, { - key: 'lwjd5qra8257b9', // TODO: we need our own PeerServer KEY (http://peerjs.com/peerserver) - debug: 3 - }); + var opts = { + //peerId: + apiKey: config.p2pApiKey, + debug: config.p2pDebug, + }; + var cp = $rootScope.cp = new copay.CopayPeer(opts); - $rootScope.publicKeyRing = new copay.PublicKeyRing({ - network: config.networkName, - }); - $rootScope.publicKeyRing.addCopayer(); - console.log('### PublicKeyRing Initialized'); + cp.on('update', function() { + console.log('*** UPDATING UX'); //TODO - peer.on('open', function(pid) { - console.log('### PEER OPEN. I AM:' + pid); - $rootScope.peerId = pid; - _saveDataStorage(); + $rootScope.peerId = cp.peerId; + $rootScope.connectedPeers = cp.connectedPeers; + + Storage.set('peerData', { + peerId: $rootScope.peerId, + connectedPeers: $rootScope.connectedPeers + }); - cb(); $rootScope.$digest(); }); - peer.on('connection', function(dataConn) { - if (dataConn.label === 'wallet') { - console.log('### NEW INBOUND CONNECTION'); //TODO - dataConn.on('open', function() { - if (!_inArray(dataConn.peer, $rootScope.connectedPeers)) { - console.log('### INBOUND DATA CONNECTION READY TO:' + dataConn.peer); //TODO - _arrayPushOnce(dataConn.peer, $rootScope.connectedPeers); - _saveDataStorage(); - - $rootScope.$digest(); - } - }); - - dataConn.on('data', _onData); - dataConn.on('error', function(e) { - console.log('### ## INBOUND DATA ERROR',e ); //TODO - _onClose(dataConn.peer); - }); - dataConn.on('close', function() { - _onClose(dataConn.peer); - }); - } + // inicia session + cp.start(function(peerId) { + console.log('[kkkk.7] START: SOY', peerId); //TODO +// networkPubKeyRing.setUpHandlers(cp); +// networkTransactionProposal.setUpHandlers(cp); + return cb(); }); }; - var connect = function(pid, cb) { - if (pid !== $rootScope.peerId) { - console.log('### STARTING CONNECT TO:' + pid ); - - var dataConn = peer.connect(pid, { - label: 'wallet', - serialization: 'none', - reliable: true, - metadata: { message: 'hi copayer!' } - }); - - dataConn.on('open', function() { - - console.log('### OUTBOUND DATA CONN READY TO:' + pid ); - _arrayPushOnce(pid, $rootScope.connectedPeers); - _saveDataStorage(); - - console.log('#### SENDING PEER LIST: ' +$rootScope.connectedPeers); - _send(pid, { - type: 'peerList', - peers: $rootScope.connectedPeers - }); - - - console.log('#### SENDING PKR '); - _send(dataConn.peer, { - type: 'publicKeyRing', - publicKeyRing: $rootScope.publicKeyRing.toObj(), - }); - - if (typeof cb === 'function') cb(); - - $rootScope.$digest(); - }); - - dataConn.on('data', function(data) { - _onData(data,true); - }); - - dataConn.on('error', function(e) { - console.log('### ## INBOUND DATA ERROR',e ); //TODO - _onClose(dataConn.peer); - }); - - dataConn.on('close', function() { - _onClose(dataConn.peer); - }); - } - }; - - var _send = function(pids, data, cb) { - if (Array.isArray(pids)) - pids.forEach(function(pid) { - _sendToOne(pid, data, cb); - }); - else if (typeof pids === 'string') - _sendToOne(pids, data, cb); + var connect = function(peerId, cb) { + $rootScope.cp.connectTo(peerId, function(id) { + console.log('CONNECTTO CALLBACK SOY:', id); //TODO + return cb(); + }); }; var disconnect = function(cb) { - Storage.remove('peerData'); - var conns = $rootScope.connectedPeers.length; - var i = 1; - _send($rootScope.connectedPeers, { type: 'disconnect' }, function() { - i += 1; - - if (i === conns) { - - $rootScope.connectedPeers = []; - $rootScope.peerId = null; - peer.disconnect(); - peer.destroy(); - if (typeof cb === 'function') cb(); - } - }); - } + if ($rootScope.cp) { + $rootScope.cp.disconnect(); + } + Storage.remove('peerData'); + }; return { init: init, connect: connect, - send: _send, disconnect: disconnect } });