Fixed conflicts with 'master'.

Merge branch 'master' into feature/wallet

Conflicts:
	copay.js
This commit is contained in:
Gustavo Cortez 2014-04-11 15:08:00 -03:00
commit 6b33f0425a
15 changed files with 3637 additions and 323 deletions

View File

@ -2,5 +2,5 @@
module.exports.Storage = require('./js/models/Storage');
module.exports.PublicKeyRing = require('./js/models/PublicKeyRing');
module.exports.Wallet = require('./js/models/Wallet');
module.exports.CopayPeer = require('./js/models/CopayPeer');
module.exports.FakeStorage = require('./test/FakeStorage');

View File

@ -73,12 +73,16 @@ body {
.panel {
background: #FFFFFF;
padding: 0.25rem 1rem;
padding: 0.7rem 1rem;
border:0;
}
.panel:hover {
background: #efefef;
}
.box-backup {
margin: 2rem 0;
background: white;
padding: 2rem 1rem;
}
@ -87,22 +91,32 @@ a.box-backup {
color: #111;
}
a.box-backup:hover {
color:#666;
}
.box-backup i {
margin-bottom: 2rem;
display: block;
}
p {
margin-bottom: 0.5rem;
}
.pending button {
margin: 0;
}
.line {
border-top: 1px solid #ccc;
padding-bottom: 1rem;
}
button.primary { background-color: #111; }
button.secondary { background-color: #FAE448 !important; }
button.primary:hover { background-color: #333;}
button.secondary:hover { background-color: #D7C020 !important;}
.m0 {margin: 0;}
.db {display: block;}
.size-12 { font-size: 12px; }
.size-14 { font-size: 14px; }
.size-16 { font-size: 16px; }

View File

@ -10,7 +10,8 @@
<link rel="stylesheet" href="css/main.css">
</head>
<body>
<div class="header" data-ng-init="init()" data-ng-controller="HeaderController">
<div data-ng-init="init()" data-ng-controller="HeaderController">
<div class="header">
<div class="header-content">
<figure class="left">
<img src="./img/logo-negative.svg" alt="" width="130">
@ -38,8 +39,17 @@
</ul>
</section>
</nav>
</div>
<div ng-if='$root.flashMessage.message' class="panel callout radius" style="color:red" >
{{$root.flashMessage.type}}:
{{$root.flashMessage.message}}
<a href="#" ng-click="clearFlashMessage()" class="button tiny">Dismiss</a>
</div>
</div>
<div class="row">
<div class="large-12 columns" ng-view></div>
</div>
@ -61,7 +71,19 @@
<div ng-show="!loading">
<div class="row">
<div class="large-6 columns">
<h3>Join wallet</h3>
<h3>Open a Existing Wallet</h3>
<select class="form-control" >
<option data-ng-repeat="walletId in listWalletIds()" value="walletId">{{walletId}}</option>
</select>
</div>
<div class="large-3 columns">
<button class="button primary expand round" type="button" ng-click="open(walletId)">Open</button>
</div>
</div>
<hr>
<div class="row">
<div class="large-6 columns">
<h3>Join a Network Wallet</h3>
<input type="text" class="form-control" placeholder="Peer ID" ng-model="connectionId" autofocus>
</div>
<div class="large-3 columns">
@ -71,7 +93,7 @@
<hr>
<div class="row">
<div class="large-6 columns">
<h3>Create a new wallet</h3>
<h3>Create a New Wallet</h3>
</div>
<div class="large-3 columns">
<button class="button secondary expand round" ng-click="create()">Create</button>
@ -108,54 +130,91 @@
</div>
</div>
</script>
<!-- HOME -->
<script type="text/ng-template" id="home.html">
<div class="home" data-ng-controller="HomeController">
<h2>{{title}}</h2>
<h2>{{title}}</h2>
<accordion close-others="oneAtATime">
<accordion-group ng-repeat="addr in addrs">
<accordion-heading>
{{addr.addrStr}}
</accordion-heading>
<qrcode size="160" data="{{addr.addrStr}}"></qrcode>
</accordion-group>
</accordion>
<div class="row">
<div class="large-6 columns">
<a class="panel db"> Adress1 <span class="right"> &gt; </span> </a>
<a class="panel db"> Adress2 <span class="right"> &gt; </span> </a>
<a class="panel db"> Adress3 <span class="right"> &gt; </span> </a>
<a class="panel db"> Adress4 <span class="right"> &gt; </span> </a>
</div>
<div class="large-6 columns">
<qrcode size="160" data="n3zUqNR7Bbbc4zJhPVj1vG2Lx66K3Xhzvb"></qrcode>
<p> 2.5432 BTC </p>
</div>
</div>
</div>
</script>
<!-- TRANSACTIONS -->
<script type="text/ng-template" id="transactions.html">
<div class="transactions" data-ng-controller="TransactionsController">
<h2>{{title}}</h2>
<div class="row">
<div class="large-6 columns">
<h3>Input</h3>
<accordion close-others="oneAtATime">
<accordion-group ng-repeat="tx in txsinput">
<accordion-heading>
{{tx.fromAddr}} -> {{tx.toAddr}} : {{tx.amount}}
</accordion-heading>
<div class="row">
<div class="large-6 columns">
<h2>Output</h2>
<div class="panel pending">
<div class="row">
<div class="col-xs-6 col-md-4">ignore</div>
<div class="col-xs-6 col-md-4">(Required 3 copay)</div>
<div class="col-xs-6 col-md-4">sign</div>
<p class="large-5 columns"> Address 1</p>
<i class="large-2 columns fi-arrow-right size-16 text-center"></i>
<p class="large-5 columns"> Adress 2</p>
<div class="large-12 columns m0">
<div class="line"></div>
<button class="primary round large-4 columns"><i class="large-2 columns fi-x size-16 text-center"></i> Ignore</button>
<small class="large-4 columns text-center">Faltan 3 cosigners</small>
<button class="secondary round large-4 columns"><i class="large-2 columns fi-check size-16 text-center"></i> Sign</button>
</div>
</div> <!-- end of row -->
</div> <!-- end of pending -->
<div class="line"></div>
<div class="panel">
<div class="row">
<p class="large-5 columns"> Address 1</p>
<i class="large-2 columns fi-arrow-right size-16 text-center"></i>
<p class="large-5 columns"> Adress 2</p>
</div>
</accordion-group>
</accordion>
</div>
<div class="large-6 columns">
<h3>Output</h3>
<accordion close-others="oneAtATime">
<accordion-group ng-repeat="tx in txsoutput">
<accordion-heading>
{{tx.fromAddr}} <- {{tx.toAddr}} : {{tx.amount}}
</accordion-heading>
Details...
</accordion-group>
</accordion>
</div>
</div>
<div class="panel">
<div class="row">
<p class="large-5 columns"> Address 1</p>
<i class="large-2 columns fi-arrow-right size-16 text-center"></i>
<p class="large-5 columns"> Adress 2</p>
</div>
</div>
</div>
<div class="large-6 columns">
<h2>Input</h2>
<div class="panel">
<div class="row">
<p class="large-5 columns"> Address 1</p>
<i class="large-2 columns fi-arrow-left size-16 text-center"></i>
<p class="large-5 columns"> Adress 2</p>
</div>
</div>
<div class="panel">
<div class="row">
<p class="large-5 columns"> Address 1</p>
<i class="large-2 columns fi-arrow-left size-16 text-center"></i>
<p class="large-5 columns"> Adress 2</p>
</div>
</div>
<div class="panel">
<div class="row">
<p class="large-5 columns"> Address 1</p>
<i class="large-2 columns fi-arrow-left size-16 text-center"></i>
<p class="large-5 columns"> Adress 2</p>
</div>
</div>
</div>
</div>
</div>
</script>
@ -185,19 +244,19 @@
<h2>{{title}}</h2>
<div class="row text-center">
<div class="large-4 columns">
<a href="/" class="box-backup">
<a href="/" class="panel box-backup">
<i class="fi-download size-72"></i>
<p> Download File </p>
</a>
</div>
<div class="large-4 columns">
<a href="/" class="box-backup">
<a href="/" class="panel box-backup">
<i class="fi-social-dropbox size-72"></i>
<p> Backup to Dropbox </p>
</a>
</div>
<div class="large-4 columns">
<a href="/" class="box-backup">
<a href="/" class="panel box-backup">
<i class="fi-mail size-72"></i>
<p> Backup to email </p>
</a>

View File

@ -2,4 +2,7 @@
var config = {
networkName: 'testnet',
p2pApiKey: 'lwjd5qra8257b9',
p2pDebug: 3,
maxPeers: 5,
};

View File

@ -32,9 +32,12 @@ angular.module('copay.header').controller('HeaderController',
};
$scope.signout = function() {
$rootScope.isLogged = false;
Network.disconnect(function() {
$location.path('signin');
});
};
$scope.clearFlashMessage = function() {
$rootScope.flashMessage = {};
};
});

View File

@ -2,26 +2,52 @@
angular.module('copay.signin').controller('SigninController',
function($scope, $rootScope, $location, Network, Storage) {
var peerData = Storage.get('peerData');
var peerData = Storage.get($rootScope.walletId, 'peerData');
$scope.loading = false;
$rootScope.peerId = peerData ? peerData.peerId : null;
$scope.listWalletIds = function() {
return Storage.getWalletIds();
};
$scope.create = function() {
$scope.loading = true;
Network.createWallet();
Network.init(function() {
$location.path('peer');
$rootScope.$digest();
});
};
$scope.open = function(walletId) {
$scope.loading = true;
if (Network.openWallet(walletId)) {
Network.init(function() {
$location.path('peer');
$rootScope.$digest();
});
}
};
$scope.join = function(cid) {
$scope.loading = true;
if (cid) {
Network.init(function() {
Network.connect(cid, function() {
$location.path('peer');
Network.connect(cid,
function() {
$location.path('peer');
$rootScope.$digest();
}, function() {
console.log('[signin.js.46] SETTING MESSAGE'); //TODO
$rootScope.flashMessage = { message: 'Connection refussed', type: 'error'};
$location.path('home');
$rootScope.$digest();
});
});
}

2958
js/copayBundle.js Normal file

File diff suppressed because it is too large Load Diff

284
js/models/CopayPeer.js Normal file
View File

@ -0,0 +1,284 @@
var imports = require('soop').imports();
var EventEmitter= imports.EventEmitter || require('events').EventEmitter;
/*
* Emits
* 'networkChange'
* when network layout has change (new/lost peers, etc)
*
* 'data'
* when an unknown data type arrives
*
* Provides
* send(toPeerIds, {data}, cb?)
*
*/
function CopayPeer(opts) {
opts = opts || {};
this.peerId = opts.peerId;
this.apiKey = opts.apiKey || 'lwjd5qra8257b9';
this.debug = opts.debug || 3;
this.maxPeers = opts.maxPeers || 5;
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]);
arrayDiff.forEach(function(peerId) {
console.log('### CONNECTING TO:', peerId);
self.connectTo(peerId);
ret = true;
});
return ret;
};
CopayPeer.prototype._onData = function(data, isInbound) {
var obj;
try {
obj = JSON.parse(data);
} catch (e) {
console.log('### ERROR ON DATA: "%s" ', data, isInbound, e);
return;
};
console.log('### RECEIVED TYPE: %s FROM %s', obj.data.type, obj.sender);
switch(obj.data.type) {
case 'peerList':
this._connectToPeers(obj.data.peers);
this._notify();
break;
case 'disconnect':
this._onClose(obj.sender);
break;
default:
this.emit('data', obj.sender, obj.data, isInbound);
}
};
CopayPeer.prototype._sendPeers = function(peerIds) {
console.log('#### SENDING PEER LIST: ', this.connectedPeers, ' TO ', peerIds?peerIds: 'ALL');
this.send(peerIds, {
type: 'peerList',
peers: this.connectedPeers,
});
};
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, closeCallback) {
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( isInbound ? dataConn.peer : null);
if (typeof openCallback === 'function') openCallback();
}
});
dataConn.on('data', function(data) {
self._onData(data, isInbound);
});
dataConn.on('error', function(e) {
console.log('### DATA ERROR',e ); //TODO
});
dataConn.on('close', function() {
console.log('### CLOSE RECV FROM:', dataConn.peer); //TODO
self._onClose(dataConn.peer);
if (typeof closeCallback === 'function') closeCallback();
});
};
CopayPeer.prototype._notify = function(newPeer) {
this._showConnectedPeers();
this.emit('networkChange', newPeer);
};
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 %d/%d', self.connectedPeers.length, self.maxPeers);
if (self.connectedPeers.length >= self.maxPeers) {
console.log('### PEER REJECTED. PEER MAX LIMIT REACHED');
dataConn.on('open', function() {
console.log('### CLOSING CONN FROM:' + dataConn.peer);
dataConn.close();
});
}
else {
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 (!peerIds) {
peerIds = this.connectedPeers;
data.isBroadcast = 1;
}
console.log('[CopayPeer.js.216:SENDD:]',data); //TODO
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, openCallback, closeCallback ) {
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, openCallback, closeCallback);
};
CopayPeer.prototype.disconnect = function(peerId, cb) {
var self = this;
this.send(null, { 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);

8
js/models/Peer.js Normal file
View File

@ -0,0 +1,8 @@
function Peer(id) {
this.id = id;
};
module.exports = require('soop')(Peer);

View File

@ -59,22 +59,8 @@ PublicKeyRing.encrypt = function (passphrase, payload) {
return payload;
};
PublicKeyRing.read = function (id, passphrase) {
var encPayload = storage.get(id);
if (!encPayload)
throw new Error('Could not find wallet data');
var data;
try {
data = JSON.parse( PublicKeyRing.decrypt( passphrase, encPayload ));
} catch (e) {
throw new Error('error in storage: '+ e.toString());
return;
};
if (data.id !== id)
throw new Error('Wrong id in data');
var config = { networkName: data.networkName };
PublicKeyRing.fromObj = function (data) {
var config = { networkName: data.networkName || 'livenet' };
var w = new PublicKeyRing(config);
@ -93,6 +79,25 @@ PublicKeyRing.read = function (id, passphrase) {
return w;
};
PublicKeyRing.read = function (id, passphrase) {
var encPayload = storage.get(id);
if (!encPayload)
throw new Error('Could not find wallet data');
var data;
try {
data = JSON.parse( PublicKeyRing.decrypt( passphrase, encPayload ));
} catch (e) {
throw new Error('error in storage: '+ e.toString());
return;
};
if (data.id !== id)
throw new Error('Wrong id in data');
return PublicKeyRing.fromObj(data);
};
PublicKeyRing.prototype.toObj = function() {
return {
id: this.id,

View File

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

View File

@ -3,274 +3,176 @@
angular.module('copay.network')
.factory('Network', function($rootScope, Storage) {
var peer;
$rootScope.connectedPeers = [];
$rootScope.peerId = null;
// Array helpers
var _arrayDiff = function(a, b) {
var seen = [];
var diff = [];
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 _refreshUx = function() {
var cp = $rootScope.cp;
console.log('*** UPDATING UX'); //TODO
$rootScope.peerId = cp.peerId;
$rootScope.connectedPeers = cp.connectedPeers;
$rootScope.$digest();
};
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', {
var _store = function() {
Storage.set($rootScope.walletId, '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(),
});
}
//TODO Remove log
console.log('*** PRK:', $rootScope.publicKeyRing.toObj());
break;
}
};
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);
// set new inbound connections
var _setNewPeer = function(newPeer) {
var cp = $rootScope.cp;
console.log('#### SENDING PKR 1111 ');
cp.send(newPeer, {
type: 'publicKeyRing',
publicKeyRing: $rootScope.publicKeyRing.toObj(),
});
};
var _handleNetworkChange = function(newPeer) {
var cp = $rootScope.cp;
if (newPeer)
_setNewPeer(newPeer);
_store();
_refreshUx();
};
// TODO -> probably not in network.js
var createWallet = function(walletId) {
console.log('### CREATING WALLET. ID:' + walletId);
//TODO create a wallet and WalletId, not only pkr
var pkr = new copay.PublicKeyRing({
network: config.networkName,
id: walletId,
});
pkr.addCopayer();
console.log('\t### PublicKeyRing Initialized:');
Storage.addWalletId(pkr.id);
Storage.set(pkr.id, 'publicKeyRing', pkr.toObj());
$rootScope.walletId = pkr.id;
$rootScope.publicKeyRing = pkr;
};
var openWallet = function (walletId) {
var ret = false;
var pkr = Storage.get(walletId, 'publicKeyRing');
if (pkr) {
console.log('### WALLET OPENED:', walletId, pkr);
$rootScope.walletId = walletId;
$rootScope.publicKeyRing = new copay.PublicKeyRing.fromObj(pkr);
ret = true;
}
return ret;
};
var closeWallet = function() {
console.log('### CLOSING WALLET');
$rootScope.walletId = null;
$rootScope.publicKeyRing = null;
//TODO
};
var _checkWallet = function(walletId) {
console.log('[network.js.79:_checkWallet:]',walletId); //TODO
if ($rootScope.walletId && $rootScope.walletId !== walletId)
closeWallet();
if ($rootScope.walletId)
return;
if (!openWallet(walletId)) {
createWallet(walletId);
}
};
var _handleData = function(senderId, data, isInbound) {
var cp = $rootScope.cp;
switch(data.type) {
case 'publicKeyRing':
_checkWallet(data.publicKeyRing.id);
var shouldSend = false;
var recipients, pkr = $rootScope.publicKeyRing;
if (pkr.merge(data.publicKeyRing, true) && !data.isBroadcast) {
console.log('### BROADCASTING PKR');
recipients = null;
shouldSend = true;
}
else if (isInbound && !data.isBroadcast) {
// always replying to connecting peer
console.log('### REPLYING PKR TO:', senderId);
recipients = senderId;
shouldSend = true;
}
if (shouldSend) {
console.log('### SENDING PKR TO:', recipients);
cp.send( recipients, {
type: 'publicKeyRing',
publicKeyRing: $rootScope.publicKeyRing.toObj(),
});
}
_refreshUx();
break;
}
};
var _setupHandlers = function () {
var cp = $rootScope.cp;
cp.on('networkChange', _handleNetworkChange);
cp.on('data', _handleData);
};
// 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 cp = $rootScope.cp = new copay.CopayPeer({
apiKey: config.p2pApiKey,
debug: config.p2pDebug,
maxPeers: config.maxPeers, // TODO: This should be on wallet configuration
});
_setupHandlers();
$rootScope.publicKeyRing = new copay.PublicKeyRing({
network: config.networkName,
});
$rootScope.publicKeyRing.addCopayer();
console.log('### PublicKeyRing Initialized');
peer.on('open', function(pid) {
console.log('### PEER OPEN. I AM:' + pid);
$rootScope.peerId = pid;
_saveDataStorage();
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) {
return cb();
});
};
var connect = function(pid, cb) {
if (pid !== $rootScope.peerId) {
var disconnect = function() {
if ($rootScope.cp) {
$rootScope.cp.disconnect();
}
Storage.remove('peerData');
$rootScope.isLogged = false;
_refreshUx();
};
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 connect = function(peerId, openCallback, failCallBack) {
if ($rootScope.cp) {
$rootScope.cp.connectTo(peerId, openCallback, function () {
disconnect();
failCallBack();
});
}
else
return failCallBack();
};
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 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();
}
});
}
return {
init: init,
connect: connect,
send: _send,
disconnect: disconnect
disconnect: disconnect,
createWallet: createWallet,
openWallet: openWallet,
}
});

View File

@ -2,21 +2,63 @@
angular.module('copay.storage')
.factory('Storage', function($rootScope) {
var _key = function(walletId, key) {
return walletId + '::' + key;
};
var _pushKey = function(walletId, key) {
var keys = localStorage.getItem(walletId);
localStorage.setItem(walletId, (keys?keys+',':'') +key);
};
return {
get: function(key) {
getGlobal: function( key) {
return JSON.parse(localStorage.getItem(key));
},
set: function(key, data) {
setGlobal: function( key, data) {
localStorage.setItem(key, JSON.stringify(data));
},
remove: function(key) {
localStorage.removeItem(key);
get: function(walletId, key) {
if (!walletId) return;
return JSON.parse(localStorage.getItem(_key(walletId,key)));
},
set: function(walletId, key, data) {
if (!walletId) return;
var k = _key(walletId,key);
localStorage.setItem(k, JSON.stringify(data));
_pushKey(walletId, k);
},
remove: function(walletId, key) {
localStorage.removeItem(_key(walletId,key));
},
clearAll: function(walletId){
var keys = localStorage.getItem(walletId);
keys.split(',').forEach(function(k){
localStorage.removeItem(key);
});
},
addWalletId: function(walletId) {
var ids = localStorage.getItem('walletIds');
localStorage.setItem('walletIds', (ids?ids+',':'') + walletId);
},
delWalletId: function(walletId) {
var ids = localStorage.getItem('walletIds');
if (ids) {
var is = ids.split(',');
var newIds = [];
is.forEach(function(i) {
if (i != walletId) {
newIds.push(i);
}
});
localStorage.setItem('walletIds', newIds.join(',') );
}
},
getWalletIds: function() {
var ids = localStorage.getItem('walletIds');
return ids ? ids.split(',') : [];
},
clearAll: function() {
localStorage.clear();
}
};
});

View File

@ -32,6 +32,8 @@
"browserify": "~3.32.1",
"browser-pack": "~2.0.1",
"commander": "~2.1.0",
"uglifyify": "~1.2.3"
"uglifyify": "~1.2.3",
"soop": "~0.1.5",
"bitcore": "~0.1.11"
}
}

View File

@ -63,7 +63,7 @@ if (require.main === module) {
.option('-o, --stdout', 'Specify output as stdout')
.parse(process.argv);
var copayBundle = createBundle(program);
copayBundle.pipe(program.stdout ? process.stdout : fs.createWriteStream('lib/copayBundle.js'));
copayBundle.pipe(program.stdout ? process.stdout : fs.createWriteStream('js/copayBundle.js'));
}
module.exports.createBundle = createBundle;