mirror of https://github.com/BTCPrivate/copay.git
Merge pull request #24 from matiu/feature/pubkey-sync
Feature/pubkey sync
This commit is contained in:
commit
818be3295b
|
@ -0,0 +1,51 @@
|
||||||
|
module.exports = function(grunt) {
|
||||||
|
|
||||||
|
//Load NPM tasks
|
||||||
|
grunt.loadNpmTasks('grunt-contrib-watch');
|
||||||
|
grunt.loadNpmTasks('grunt-mocha-test');
|
||||||
|
grunt.loadNpmTasks('grunt-markdown');
|
||||||
|
grunt.loadNpmTasks('grunt-shell');
|
||||||
|
|
||||||
|
// Project Configuration
|
||||||
|
grunt.initConfig({
|
||||||
|
shell: {
|
||||||
|
browserify: {
|
||||||
|
options: {
|
||||||
|
stdout: true,
|
||||||
|
stderr: true
|
||||||
|
},
|
||||||
|
command: grunt.option('target') === 'dev' ?
|
||||||
|
'node ./util/build.js -d ' : 'node ./util/build.js '
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
readme: {
|
||||||
|
files: ['README.md'],
|
||||||
|
tasks: ['markdown']
|
||||||
|
},
|
||||||
|
scripts: {
|
||||||
|
files: ['**/*.js', '**/*.html', '!**/node_modules/**', '!browser/bundle.js', '!browser/testdata.js', '!lib/**js', '!browser/vendor-bundle.js'],
|
||||||
|
tasks: ['shell'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mochaTest: {
|
||||||
|
options: {
|
||||||
|
reporter: 'spec',
|
||||||
|
},
|
||||||
|
src: ['test/*.js'],
|
||||||
|
},
|
||||||
|
markdown: {
|
||||||
|
all: {
|
||||||
|
files: [{
|
||||||
|
expand: true,
|
||||||
|
src: 'README.md',
|
||||||
|
dest: '.',
|
||||||
|
ext: '.html'
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
grunt.registerTask('default', ['shell','watch']);
|
||||||
|
|
||||||
|
};
|
|
@ -0,0 +1,7 @@
|
||||||
|
#! /bin/bash
|
||||||
|
|
||||||
|
cd vendor/
|
||||||
|
cat browser-adapter.js crypto-2.0.js crypto-3.1.js jsbn.js jsbn2.js prng4.js util.js rng.js ec.js sec.js ecdsa.js eckey.js > vendor-bundle.js
|
||||||
|
mv vendor-bundle.js ../
|
||||||
|
cd ../
|
||||||
|
|
|
@ -0,0 +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');
|
41
index.html
41
index.html
|
@ -10,7 +10,8 @@
|
||||||
<link rel="stylesheet" href="css/main.css">
|
<link rel="stylesheet" href="css/main.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<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">
|
<div class="header-content">
|
||||||
<figure class="left">
|
<figure class="left">
|
||||||
<img src="./img/logo-negative.svg" alt="" width="130">
|
<img src="./img/logo-negative.svg" alt="" width="130">
|
||||||
|
@ -38,8 +39,17 @@
|
||||||
</ul>
|
</ul>
|
||||||
</section>
|
</section>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
</div>
|
</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="row">
|
||||||
<div class="large-12 columns" ng-view></div>
|
<div class="large-12 columns" ng-view></div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -61,7 +71,19 @@
|
||||||
<div ng-show="!loading">
|
<div ng-show="!loading">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="large-6 columns">
|
<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>
|
<input type="text" class="form-control" placeholder="Peer ID" ng-model="connectionId" autofocus>
|
||||||
</div>
|
</div>
|
||||||
<div class="large-3 columns">
|
<div class="large-3 columns">
|
||||||
|
@ -71,7 +93,7 @@
|
||||||
<hr>
|
<hr>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="large-6 columns">
|
<div class="large-6 columns">
|
||||||
<h3>Create a new wallet</h3>
|
<h3>Create a New Wallet</h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="large-3 columns">
|
<div class="large-3 columns">
|
||||||
<button class="button secondary expand round" ng-click="create()">Create</button>
|
<button class="button secondary expand round" ng-click="create()">Create</button>
|
||||||
|
@ -85,6 +107,14 @@
|
||||||
<div class="row" ng-controller="PeerController" ng-init="init()">
|
<div class="row" ng-controller="PeerController" ng-init="init()">
|
||||||
<div class="large-6 columns">
|
<div class="large-6 columns">
|
||||||
<h1>I am <p class="text-muted">{{$root.peerId}}</p></h1>
|
<h1>I am <p class="text-muted">{{$root.peerId}}</p></h1>
|
||||||
|
|
||||||
|
<ul class="no-bullet">
|
||||||
|
<li> [DEBUG] Pubkeys that you have: {{$root.publicKeyRing.registeredCopayers()}}
|
||||||
|
<li class="panel" style="word-wrap: break-word;" ng-repeat="pub in $root.publicKeyRing.copayersBIP32">
|
||||||
|
${{pub.extendedPublicKeyString()}}
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<h3 class="panel-title">Copayers ({{$root.connectedPeers.length}}/5)</h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="large-6 columns">
|
<div class="large-6 columns">
|
||||||
<h3 class="panel-title">Copayers ({{$root.connectedPeers.length}}/5)</h3>
|
<h3 class="panel-title">Copayers ({{$root.connectedPeers.length}}/5)</h3>
|
||||||
|
@ -250,12 +280,15 @@
|
||||||
<script src="lib/angular-foundation/mm-foundation.min.js"></script>
|
<script src="lib/angular-foundation/mm-foundation.min.js"></script>
|
||||||
<script src="lib/angular-foundation/mm-foundation-tpls.min.js"></script>
|
<script src="lib/angular-foundation/mm-foundation-tpls.min.js"></script>
|
||||||
<script src="lib/peerjs/peer.js"></script>
|
<script src="lib/peerjs/peer.js"></script>
|
||||||
|
<script src="lib/bitcore.js"></script>
|
||||||
|
<script src="lib/copayBundle.js"></script>
|
||||||
|
|
||||||
|
|
||||||
<script src="js/app.js"></script>
|
<script src="js/app.js"></script>
|
||||||
<script src="js/config.js"></script>
|
<script src="js/config.js"></script>
|
||||||
|
<script src="js/routes.js"></script>
|
||||||
<script src="js/directives.js"></script>
|
<script src="js/directives.js"></script>
|
||||||
<script src="js/filters.js"></script>
|
<script src="js/filters.js"></script>
|
||||||
|
|
||||||
<script src="js/services/network.js"></script>
|
<script src="js/services/network.js"></script>
|
||||||
<script src="js/services/storage.js"></script>
|
<script src="js/services/storage.js"></script>
|
||||||
|
|
||||||
|
|
49
js/config.js
49
js/config.js
|
@ -1,45 +1,8 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
//Setting up route
|
var config = {
|
||||||
angular
|
networkName: 'testnet',
|
||||||
.module('copay')
|
p2pApiKey: 'lwjd5qra8257b9',
|
||||||
.config(function($routeProvider) {
|
p2pDebug: 3,
|
||||||
|
maxPeers: 5,
|
||||||
$routeProvider
|
};
|
||||||
.when('/', {
|
|
||||||
templateUrl: 'signin.html'
|
|
||||||
})
|
|
||||||
.when('/signin', {
|
|
||||||
templateUrl: 'signin.html'
|
|
||||||
})
|
|
||||||
.when('/home', {
|
|
||||||
templateUrl: 'home.html'
|
|
||||||
})
|
|
||||||
.when('/join/:id', {
|
|
||||||
templateUrl: 'join.html'
|
|
||||||
})
|
|
||||||
.when('/peer', {
|
|
||||||
templateUrl: 'peer.html'
|
|
||||||
})
|
|
||||||
.when('/transactions', {
|
|
||||||
templateUrl: 'transactions.html'
|
|
||||||
})
|
|
||||||
.when('/send', {
|
|
||||||
templateUrl: 'send.html'
|
|
||||||
})
|
|
||||||
.when('/backup', {
|
|
||||||
templateUrl: 'backup.html'
|
|
||||||
})
|
|
||||||
.otherwise({
|
|
||||||
templateUrl: '404.html'
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
//Setting HTML5 Location Mode
|
|
||||||
angular
|
|
||||||
.module('copay')
|
|
||||||
.config(function($locationProvider) {
|
|
||||||
$locationProvider
|
|
||||||
.html5Mode(false);
|
|
||||||
//.hashPrefix('!');
|
|
||||||
});
|
|
||||||
|
|
|
@ -32,9 +32,12 @@ angular.module('copay.header').controller('HeaderController',
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.signout = function() {
|
$scope.signout = function() {
|
||||||
$rootScope.isLogged = false;
|
|
||||||
Network.disconnect(function() {
|
Network.disconnect(function() {
|
||||||
$location.path('signin');
|
$location.path('signin');
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
$scope.clearFlashMessage = function() {
|
||||||
|
$rootScope.flashMessage = {};
|
||||||
|
};
|
||||||
});
|
});
|
||||||
|
|
|
@ -2,38 +2,60 @@
|
||||||
|
|
||||||
angular.module('copay.signin').controller('SigninController',
|
angular.module('copay.signin').controller('SigninController',
|
||||||
function($scope, $rootScope, $location, Network, Storage) {
|
function($scope, $rootScope, $location, Network, Storage) {
|
||||||
var peerData = Storage.get('peerData');
|
|
||||||
|
var peerData = Storage.get($rootScope.walletId, 'peerData');
|
||||||
|
|
||||||
$scope.loading = false;
|
$scope.loading = false;
|
||||||
$rootScope.peerId = peerData ? peerData.peerId : null;
|
$rootScope.peerId = peerData ? peerData.peerId : null;
|
||||||
|
|
||||||
|
$scope.listWalletIds = function() {
|
||||||
|
return Storage.getWalletIds();
|
||||||
|
};
|
||||||
|
|
||||||
$scope.create = function() {
|
$scope.create = function() {
|
||||||
$scope.loading = true;
|
$scope.loading = true;
|
||||||
|
|
||||||
|
Network.createWallet();
|
||||||
Network.init(function() {
|
Network.init(function() {
|
||||||
$location.path('peer');
|
$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.join = function(cid) {
|
||||||
$scope.loading = true;
|
$scope.loading = true;
|
||||||
|
|
||||||
if (cid) {
|
if (cid) {
|
||||||
$rootScope.connectedTo.push(cid);
|
|
||||||
|
|
||||||
Network.init(function() {
|
Network.init(function() {
|
||||||
Network.connect(cid, function() {
|
Network.connect(cid,
|
||||||
$location.path('peer');
|
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();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (peerData && peerData.peerId && peerData.connectedTo.length > 0) {
|
if (peerData && peerData.peerId && peerData.connectedPeers.length > 0) {
|
||||||
$rootScope.peerId = peerData.peerId;
|
$rootScope.peerId = peerData.peerId;
|
||||||
$rootScope.connectedPeers = peerData.connectedPeers;
|
$scope.join(peerData.connectedPeers);
|
||||||
|
|
||||||
$scope.join(peerData.connectedTo[0]);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
var copay = require('copay');
|
||||||
angular.element(document).ready(function() {
|
angular.element(document).ready(function() {
|
||||||
// Init the app
|
// Init the app
|
||||||
angular.bootstrap(document, ['copay']);
|
angular.bootstrap(document, ['copay']);
|
||||||
|
|
|
@ -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);
|
|
@ -0,0 +1,8 @@
|
||||||
|
|
||||||
|
|
||||||
|
function Peer(id) {
|
||||||
|
this.id = id;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = require('soop')(Peer);
|
|
@ -12,8 +12,6 @@ var Transaction = bitcore.Transaction;
|
||||||
var buffertools = bitcore.buffertools;
|
var buffertools = bitcore.buffertools;
|
||||||
|
|
||||||
var Storage = imports.Storage || require('./Storage');
|
var Storage = imports.Storage || require('./Storage');
|
||||||
var log = imports.log || console.log;
|
|
||||||
|
|
||||||
var storage = Storage.default();
|
var storage = Storage.default();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -31,7 +29,7 @@ var CHANGE_BRANCH = 'm/1/';
|
||||||
function PublicKeyRing(opts) {
|
function PublicKeyRing(opts) {
|
||||||
opts = opts || {};
|
opts = opts || {};
|
||||||
|
|
||||||
this.network = opts.network === 'livenet' ?
|
this.network = opts.networkName === 'livenet' ?
|
||||||
bitcore.networks.livenet : bitcore.networks.testnet;
|
bitcore.networks.livenet : bitcore.networks.testnet;
|
||||||
|
|
||||||
this.requiredCopayers = opts.requiredCopayers || 3;
|
this.requiredCopayers = opts.requiredCopayers || 3;
|
||||||
|
@ -52,33 +50,17 @@ PublicKeyRing.getRandomId = function () {
|
||||||
};
|
};
|
||||||
|
|
||||||
PublicKeyRing.decrypt = function (passphrase, encPayload) {
|
PublicKeyRing.decrypt = function (passphrase, encPayload) {
|
||||||
log('[wallet.js.35] TODO READ: passphrase IGNORED');
|
console.log('[wallet.js.35] TODO READ: passphrase IGNORED');
|
||||||
return encPayload;
|
return encPayload;
|
||||||
};
|
};
|
||||||
|
|
||||||
PublicKeyRing.encrypt = function (passphrase, payload) {
|
PublicKeyRing.encrypt = function (passphrase, payload) {
|
||||||
log('[wallet.js.92] TODO: passphrase IGNORED');
|
console.log('[wallet.js.92] TODO: passphrase IGNORED');
|
||||||
return payload;
|
return payload;
|
||||||
};
|
};
|
||||||
|
|
||||||
PublicKeyRing.read = function (id, passphrase) {
|
PublicKeyRing.fromObj = function (data) {
|
||||||
var encPayload = storage.read(id);
|
var config = { networkName: data.networkName || 'livenet' };
|
||||||
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 = { network: data.networkName === 'livenet' ?
|
|
||||||
bitcore.networks.livenet : bitcore.networks.testnet
|
|
||||||
};
|
|
||||||
|
|
||||||
var w = new PublicKeyRing(config);
|
var w = new PublicKeyRing(config);
|
||||||
|
|
||||||
|
@ -97,6 +79,25 @@ PublicKeyRing.read = function (id, passphrase) {
|
||||||
return w;
|
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() {
|
PublicKeyRing.prototype.toObj = function() {
|
||||||
return {
|
return {
|
||||||
id: this.id,
|
id: this.id,
|
||||||
|
@ -123,7 +124,7 @@ PublicKeyRing.prototype.store = function (passphrase) {
|
||||||
if (!this.id)
|
if (!this.id)
|
||||||
throw new Error('wallet has no id');
|
throw new Error('wallet has no id');
|
||||||
|
|
||||||
storage.save(this.id, PublicKeyRing.encrypt(passphrase,this.serialize()));
|
storage.set(this.id, PublicKeyRing.encrypt(passphrase,this.serialize()));
|
||||||
this.dirty = 0;
|
this.dirty = 0;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -136,7 +137,7 @@ PublicKeyRing.prototype.registeredCopayers = function () {
|
||||||
|
|
||||||
|
|
||||||
PublicKeyRing.prototype.haveAllRequiredPubKeys = function () {
|
PublicKeyRing.prototype.haveAllRequiredPubKeys = function () {
|
||||||
return this.registeredCopayers() === this.totalCopayers;
|
return this.registeredCopayers() >= this.totalCopayers;
|
||||||
};
|
};
|
||||||
|
|
||||||
PublicKeyRing.prototype._checkKeys = function() {
|
PublicKeyRing.prototype._checkKeys = function() {
|
||||||
|
@ -188,7 +189,7 @@ PublicKeyRing.prototype.getCopayersPubKeys = function (index, isChange) {
|
||||||
PublicKeyRing.prototype._checkIndexRange = function (index, isChange) {
|
PublicKeyRing.prototype._checkIndexRange = function (index, isChange) {
|
||||||
if ( (isChange && index > this.changeAddressIndex) ||
|
if ( (isChange && index > this.changeAddressIndex) ||
|
||||||
(!isChange && index > this.addressIndex)) {
|
(!isChange && index > this.addressIndex)) {
|
||||||
log('Out of bounds at getAddress: Index %d isChange: %d', index, isChange);
|
console.log('Out of bounds at getAddress: Index %d isChange: %d', index, isChange);
|
||||||
throw new Error('index out of bound');
|
throw new Error('index out of bound');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -238,9 +239,16 @@ PublicKeyRing.prototype.getAddresses = function() {
|
||||||
return ret;
|
return ret;
|
||||||
};
|
};
|
||||||
|
|
||||||
PublicKeyRing.prototype._checkInPRK = function(inPKR) {
|
PublicKeyRing.prototype._checkInPRK = function(inPKR, ignoreId) {
|
||||||
if (this.id !== inPKR.id)
|
|
||||||
|
|
||||||
|
if (!inPKR.ts) {
|
||||||
|
throw new Error('inPRK bad format: Did you use .toObj()?');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ignoreId && this.id !== inPKR.id) {
|
||||||
throw new Error('inPRK id mismatch');
|
throw new Error('inPRK id mismatch');
|
||||||
|
}
|
||||||
|
|
||||||
if (this.network.name !== inPKR.networkName)
|
if (this.network.name !== inPKR.networkName)
|
||||||
throw new Error('inPRK network mismatch');
|
throw new Error('inPRK network mismatch');
|
||||||
|
@ -298,10 +306,10 @@ PublicKeyRing.prototype._mergePubkeys = function(inPKR) {
|
||||||
return hasChanged;
|
return hasChanged;
|
||||||
};
|
};
|
||||||
|
|
||||||
PublicKeyRing.prototype.merge = function(inPKR) {
|
PublicKeyRing.prototype.merge = function(inPKR, ignoreId) {
|
||||||
var hasChanged = false;
|
var hasChanged = false;
|
||||||
|
|
||||||
this._checkInPRK(inPKR);
|
this._checkInPRK(inPKR, ignoreId);
|
||||||
|
|
||||||
if (this._mergeIndexes(inPKR))
|
if (this._mergeIndexes(inPKR))
|
||||||
hasChanged = true;
|
hasChanged = true;
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var imports = require('soop').imports();
|
||||||
|
|
||||||
|
function Storage() {
|
||||||
|
this.data = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
Storage.prototype.get = function(k) {
|
||||||
|
return JSON.parse(localStorage.getItem(k));
|
||||||
|
};
|
||||||
|
|
||||||
|
Storage.prototype.set = function(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);
|
|
@ -0,0 +1,45 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
//Setting up route
|
||||||
|
angular
|
||||||
|
.module('copay')
|
||||||
|
.config(function($routeProvider) {
|
||||||
|
|
||||||
|
$routeProvider
|
||||||
|
.when('/', {
|
||||||
|
templateUrl: 'signin.html'
|
||||||
|
})
|
||||||
|
.when('/signin', {
|
||||||
|
templateUrl: 'signin.html'
|
||||||
|
})
|
||||||
|
.when('/home', {
|
||||||
|
templateUrl: 'home.html'
|
||||||
|
})
|
||||||
|
.when('/join/:id', {
|
||||||
|
templateUrl: 'join.html'
|
||||||
|
})
|
||||||
|
.when('/peer', {
|
||||||
|
templateUrl: 'peer.html'
|
||||||
|
})
|
||||||
|
.when('/transactions', {
|
||||||
|
templateUrl: 'transactions.html'
|
||||||
|
})
|
||||||
|
.when('/send', {
|
||||||
|
templateUrl: 'send.html'
|
||||||
|
})
|
||||||
|
.when('/backup', {
|
||||||
|
templateUrl: 'backup.html'
|
||||||
|
})
|
||||||
|
.otherwise({
|
||||||
|
templateUrl: '404.html'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
//Setting HTML5 Location Mode
|
||||||
|
angular
|
||||||
|
.module('copay')
|
||||||
|
.config(function($locationProvider) {
|
||||||
|
$locationProvider
|
||||||
|
.html5Mode(false);
|
||||||
|
//.hashPrefix('!');
|
||||||
|
});
|
|
@ -3,217 +3,176 @@
|
||||||
angular.module('copay.network')
|
angular.module('copay.network')
|
||||||
.factory('Network', function($rootScope, Storage) {
|
.factory('Network', function($rootScope, Storage) {
|
||||||
var peer;
|
var peer;
|
||||||
$rootScope.connectedPeers = [];
|
|
||||||
$rootScope.connectedTo = [];
|
|
||||||
$rootScope.peerId = null;
|
|
||||||
|
|
||||||
// Array helpers
|
|
||||||
var _arrayDiff = function(a, b) {
|
|
||||||
var seen = [];
|
|
||||||
var diff = [];
|
|
||||||
|
|
||||||
for (var i = 0; i < b.length; i++)
|
var _refreshUx = function() {
|
||||||
seen[b[i]] = true;
|
var cp = $rootScope.cp;
|
||||||
|
console.log('*** UPDATING UX'); //TODO
|
||||||
for (var i = 0; i < a.length; i++)
|
$rootScope.peerId = cp.peerId;
|
||||||
if (!seen[a[i]])
|
$rootScope.connectedPeers = cp.connectedPeers;
|
||||||
diff.push(a[i]);
|
$rootScope.$digest();
|
||||||
|
|
||||||
return diff;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
var _inArray = function(el, array) {
|
var _store = function() {
|
||||||
return array.indexOf(el) > -1;
|
Storage.set($rootScope.walletId, 'peerData', {
|
||||||
};
|
|
||||||
|
|
||||||
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,
|
peerId: $rootScope.peerId,
|
||||||
connectedTo: $rootScope.connectedTo,
|
|
||||||
connectedPeers: $rootScope.connectedPeers
|
connectedPeers: $rootScope.connectedPeers
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
var _sender = function(pid, data, cb) {
|
// set new inbound connections
|
||||||
if (pid !== $rootScope.peerId) {
|
var _setNewPeer = function(newPeer) {
|
||||||
var conns = peer.connections[pid];
|
var cp = $rootScope.cp;
|
||||||
|
console.log('#### SENDING PKR 1111 ');
|
||||||
|
cp.send(newPeer, {
|
||||||
|
type: 'publicKeyRing',
|
||||||
|
publicKeyRing: $rootScope.publicKeyRing.toObj(),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
if (conns) {
|
var _handleNetworkChange = function(newPeer) {
|
||||||
var str = JSON.stringify({
|
var cp = $rootScope.cp;
|
||||||
sender: $rootScope.peerId,
|
|
||||||
data: data
|
|
||||||
});
|
|
||||||
|
|
||||||
for (var i = 0; i < conns.length; i++) {
|
if (newPeer)
|
||||||
var conn = conns[i];
|
_setNewPeer(newPeer);
|
||||||
conn.send(str);
|
|
||||||
|
_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 (typeof cb === 'function') cb();
|
if (shouldSend) {
|
||||||
}
|
console.log('### SENDING PKR TO:', recipients);
|
||||||
}
|
cp.send( recipients, {
|
||||||
};
|
type: 'publicKeyRing',
|
||||||
|
publicKeyRing: $rootScope.publicKeyRing.toObj(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
var _onData = function(data) {
|
_refreshUx();
|
||||||
var obj = JSON.parse(data);
|
|
||||||
|
|
||||||
switch(obj.data.type) {
|
|
||||||
case 'connectedPeers':
|
|
||||||
_connectToPeers(obj.data.peers);
|
|
||||||
break;
|
|
||||||
case 'getPeers':
|
|
||||||
_send(obj.sender, {
|
|
||||||
type: 'connectToPeers',
|
|
||||||
peers: $rootScope.connectedPeers
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
case 'disconnect':
|
|
||||||
_onClose(obj.sender);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
var _setupHandlers = function () {
|
||||||
var _onClose = function(pid) {
|
var cp = $rootScope.cp;
|
||||||
$rootScope.connectedPeers = _arrayRemove(pid, $rootScope.connectedPeers);
|
cp.on('networkChange', _handleNetworkChange);
|
||||||
$rootScope.connectedTo = _arrayRemove(pid, $rootScope.connectedTo);
|
cp.on('data', _handleData);
|
||||||
|
|
||||||
_saveDataStorage();
|
|
||||||
|
|
||||||
$rootScope.$digest();
|
|
||||||
};
|
|
||||||
|
|
||||||
var _connectToPeers = function(peers) {
|
|
||||||
var arrayDiff = _arrayDiff(peers, $rootScope.connectedTo);
|
|
||||||
|
|
||||||
arrayDiff.forEach(function(pid) {
|
|
||||||
_connect(pid);
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// public methods
|
// public methods
|
||||||
var init = function(cb) {
|
var init = function(cb) {
|
||||||
peer = new Peer($rootScope.peerId, {
|
|
||||||
key: 'lwjd5qra8257b9', // TODO: we need our own PeerServer KEY (http://peerjs.com/peerserver)
|
|
||||||
debug: 3
|
|
||||||
});
|
|
||||||
|
|
||||||
peer.on('open', function(pid) {
|
var cp = $rootScope.cp = new copay.CopayPeer({
|
||||||
$rootScope.peerId = pid;
|
apiKey: config.p2pApiKey,
|
||||||
_arrayPushOnce(pid, $rootScope.connectedPeers);
|
debug: config.p2pDebug,
|
||||||
_saveDataStorage();
|
maxPeers: config.maxPeers, // TODO: This should be on wallet configuration
|
||||||
|
});
|
||||||
|
_setupHandlers();
|
||||||
|
|
||||||
cb();
|
// inicia session
|
||||||
|
cp.start(function(peerId) {
|
||||||
$rootScope.$digest();
|
return cb();
|
||||||
});
|
|
||||||
|
|
||||||
peer.on('connection', function(conn) {
|
|
||||||
if (conn.label === 'wallet') {
|
|
||||||
conn.on('open', function() {
|
|
||||||
if (!_inArray(conn.peer, $rootScope.connectedTo)) {
|
|
||||||
var c = peer.connect(conn.peer, {
|
|
||||||
label: 'wallet',
|
|
||||||
serialization: 'none',
|
|
||||||
reliable: false,
|
|
||||||
metadata: { message: 'hi copayer!' }
|
|
||||||
});
|
|
||||||
|
|
||||||
c.on('open', function() {
|
|
||||||
$rootScope.connectedTo.push(conn.peer);
|
|
||||||
_arrayPushOnce(conn.peer, $rootScope.connectedPeers);
|
|
||||||
_saveDataStorage();
|
|
||||||
|
|
||||||
$rootScope.$digest();
|
|
||||||
});
|
|
||||||
|
|
||||||
c.on('data', _onData);
|
|
||||||
|
|
||||||
c.on('close', function() {
|
|
||||||
_onClose(c.peer);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
var connect = function(pid, cb) {
|
var disconnect = function() {
|
||||||
if (pid !== $rootScope.peerId) {
|
if ($rootScope.cp) {
|
||||||
var c = peer.connect(pid, {
|
$rootScope.cp.disconnect();
|
||||||
label: 'wallet',
|
}
|
||||||
serialization: 'none',
|
Storage.remove('peerData');
|
||||||
reliable: false,
|
$rootScope.isLogged = false;
|
||||||
metadata: { message: 'hi copayer!' }
|
_refreshUx();
|
||||||
});
|
};
|
||||||
|
|
||||||
c.on('open', function() {
|
var connect = function(peerId, openCallback, failCallBack) {
|
||||||
_arrayPushOnce(pid, $rootScope.connectedTo);
|
if ($rootScope.cp) {
|
||||||
_arrayPushOnce(pid, $rootScope.connectedPeers);
|
$rootScope.cp.connectTo(peerId, openCallback, function () {
|
||||||
|
disconnect();
|
||||||
_send(pid, { type: 'getPeers' });
|
failCallBack();
|
||||||
_saveDataStorage();
|
|
||||||
|
|
||||||
if (typeof cb === 'function') cb();
|
|
||||||
|
|
||||||
$rootScope.$digest();
|
|
||||||
});
|
|
||||||
|
|
||||||
c.on('data', _onData);
|
|
||||||
|
|
||||||
c.on('close', function() {
|
|
||||||
_onClose(c.peer);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
return failCallBack();
|
||||||
};
|
};
|
||||||
|
|
||||||
var _send = function(pids, data, cb) {
|
|
||||||
if (Array.isArray(pids))
|
|
||||||
pids.forEach(function(pid) {
|
|
||||||
_sender(pid, data, cb);
|
|
||||||
});
|
|
||||||
else if (typeof pids === 'string')
|
|
||||||
_sender(pids, data, cb);
|
|
||||||
};
|
|
||||||
|
|
||||||
var disconnect = function(cb) {
|
|
||||||
var conns = $rootScope.connectedPeers.length;
|
|
||||||
var i = 1;
|
|
||||||
|
|
||||||
_send($rootScope.connectedPeers, { type: 'disconnect' }, function() {
|
|
||||||
i += 1;
|
|
||||||
|
|
||||||
if (i === conns) {
|
|
||||||
peer.disconnect();
|
|
||||||
peer.destroy();
|
|
||||||
|
|
||||||
if (typeof cb === 'function') cb();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Storage.remove('peerData');
|
|
||||||
|
|
||||||
$rootScope.connectedPeers = [];
|
|
||||||
$rootScope.connectedTo = [];
|
|
||||||
$rootScope.peerId = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
init: init,
|
init: init,
|
||||||
connect: connect,
|
connect: connect,
|
||||||
send: _send,
|
disconnect: disconnect,
|
||||||
disconnect: disconnect
|
createWallet: createWallet,
|
||||||
|
openWallet: openWallet,
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -2,21 +2,63 @@
|
||||||
|
|
||||||
angular.module('copay.storage')
|
angular.module('copay.storage')
|
||||||
.factory('Storage', function($rootScope) {
|
.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 {
|
return {
|
||||||
get: function(key) {
|
getGlobal: function( key) {
|
||||||
return JSON.parse(localStorage.getItem(key));
|
return JSON.parse(localStorage.getItem(key));
|
||||||
},
|
},
|
||||||
|
setGlobal: function( key, data) {
|
||||||
save: function(key, data) {
|
|
||||||
localStorage.setItem(key, JSON.stringify(data));
|
localStorage.setItem(key, JSON.stringify(data));
|
||||||
},
|
},
|
||||||
|
get: function(walletId, key) {
|
||||||
remove: function(key) {
|
if (!walletId) return;
|
||||||
localStorage.removeItem(key);
|
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();
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
13
package.json
13
package.json
|
@ -19,10 +19,19 @@
|
||||||
},
|
},
|
||||||
"homepage": "https://github.com/bitpay/copay",
|
"homepage": "https://github.com/bitpay/copay",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"grunt-cli": "~0.1.13",
|
"grunt-contrib-watch": "~0.5.3",
|
||||||
|
"grunt-mocha-test": "~0.8.2",
|
||||||
|
"grunt-shell": "~0.6.4",
|
||||||
|
"grunt-browserify": "~2.0.0",
|
||||||
|
"grunt-markdown": "~0.5.0",
|
||||||
"karma": "~0.12.1",
|
"karma": "~0.12.1",
|
||||||
"karma-chrome-launcher": "~0.1.2",
|
"karma-chrome-launcher": "~0.1.2",
|
||||||
"mocha": "~1.18.2",
|
"mocha": "~1.18.2",
|
||||||
"karma-mocha": "~0.1.3"
|
"karma-mocha": "~0.1.3",
|
||||||
|
"buffertools": "~2.0.1",
|
||||||
|
"browserify": "~3.32.1",
|
||||||
|
"browser-pack": "~2.0.1",
|
||||||
|
"commander": "~2.1.0",
|
||||||
|
"uglifyify": "~1.2.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,11 +3,11 @@ var FakeStorage = function(){
|
||||||
this.storage = {};
|
this.storage = {};
|
||||||
};
|
};
|
||||||
|
|
||||||
FakeStorage.prototype.read = function (id) {
|
FakeStorage.prototype.set = function (id) {
|
||||||
return this.storage[id];
|
return this.storage[id];
|
||||||
};
|
};
|
||||||
|
|
||||||
FakeStorage.prototype.save = function(id, payload) {
|
FakeStorage.prototype.get = function(id, payload) {
|
||||||
this.storage[id] = payload;
|
this.storage[id] = payload;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Mocha</title>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<link rel="stylesheet" href="../node_modules/mocha/mocha.css" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="mocha"></div>
|
||||||
|
<script src="../node_modules/mocha/mocha.js"></script>
|
||||||
|
<script src="../node_modules/chai/chai.js"></script>
|
||||||
|
<script>mocha.setup('bdd')</script>
|
||||||
|
<script src="../lib/bitcore.js"></script>
|
||||||
|
<script src="../lib/copayBundle.js"></script>
|
||||||
|
<script>
|
||||||
|
|
||||||
|
var copay = require('copay');
|
||||||
|
</script>
|
||||||
|
<script src="test.PublicKeyRing.js"></script>
|
||||||
|
<script src="test.Storage.js"></script>
|
||||||
|
<!--
|
||||||
|
-->
|
||||||
|
<script>
|
||||||
|
mocha.run();
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -2,24 +2,24 @@
|
||||||
|
|
||||||
var chai = chai || require('chai');
|
var chai = chai || require('chai');
|
||||||
var should = chai.should();
|
var should = chai.should();
|
||||||
var bitcore = bitcore || require('../node_modules/bitcore');
|
var bitcore = bitcore || require('bitcore');
|
||||||
var Address = bitcore.Address;
|
var Address = bitcore.Address;
|
||||||
var buffertools = bitcore.buffertools;
|
var buffertools = bitcore.buffertools;
|
||||||
var copay = copay || {};
|
var copay = copay || require('../copay');
|
||||||
var fakeStorage = require('./FakeStorage');
|
var fakeStorage = copay.FakeStorage;
|
||||||
var PublicKeyRing = copay.PublicKeyRing || require('soop').load('../js/models/PublicKeyRing', {Storage: fakeStorage});
|
var PublicKeyRing = copay.PublicKeyRing || require('soop').load('../js/models/PublicKeyRing', {Storage: fakeStorage});
|
||||||
|
|
||||||
var aMasterPubKey = 'tprv8ZgxMBicQKsPdSVTiWXEqCCzqRaRr9EAQdn5UVMpT9UHX67Dh1FmzEMbavPumpAicsUm2XvC6NTdcWB89yN5DUWx5HQ7z3KByUg7Ht74VRZ';
|
var aMasterPubKey = 'tprv8ZgxMBicQKsPdSVTiWXEqCCzqRaRr9EAQdn5UVMpT9UHX67Dh1FmzEMbavPumpAicsUm2XvC6NTdcWB89yN5DUWx5HQ7z3KByUg7Ht74VRZ';
|
||||||
|
|
||||||
|
|
||||||
var config = {
|
var config = {
|
||||||
network:'livenet',
|
networkName:'livenet',
|
||||||
};
|
};
|
||||||
|
|
||||||
var createW = function (network) {
|
var createW = function (networkName) {
|
||||||
|
|
||||||
var config = {
|
var config = {
|
||||||
network: network || 'livenet',
|
networkName: networkName || 'livenet',
|
||||||
};
|
};
|
||||||
|
|
||||||
var w = new PublicKeyRing(config);
|
var w = new PublicKeyRing(config);
|
||||||
|
@ -39,7 +39,7 @@ describe('PublicKeyRing model', function() {
|
||||||
|
|
||||||
it('should create an instance (livenet)', function () {
|
it('should create an instance (livenet)', function () {
|
||||||
var w = new PublicKeyRing({
|
var w = new PublicKeyRing({
|
||||||
network: config.network
|
networkName: config.networkName
|
||||||
});
|
});
|
||||||
should.exist(w);
|
should.exist(w);
|
||||||
w.network.name.should.equal('livenet');
|
w.network.name.should.equal('livenet');
|
||||||
|
@ -158,7 +158,7 @@ describe('PublicKeyRing model', function() {
|
||||||
w.generateAddress(false);
|
w.generateAddress(false);
|
||||||
|
|
||||||
var w2 = new PublicKeyRing({
|
var w2 = new PublicKeyRing({
|
||||||
network: 'livenet',
|
networkName: 'livenet',
|
||||||
id: w.id,
|
id: w.id,
|
||||||
});
|
});
|
||||||
w2.merge(w.toObj()).should.equal(true);
|
w2.merge(w.toObj()).should.equal(true);
|
||||||
|
@ -182,33 +182,48 @@ describe('PublicKeyRing model', function() {
|
||||||
w.generateAddress(false);
|
w.generateAddress(false);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var w2 = new PublicKeyRing({
|
||||||
|
networkName: 'livenet',
|
||||||
|
});
|
||||||
|
(function() { w2.merge(w.toObj());}).should.throw();
|
||||||
|
(function() { w2.merge(w,true);}).should.throw();
|
||||||
|
|
||||||
|
console.log('[test.publickeyring.js.190]'); //TODO
|
||||||
|
w2.merge(w.toObj(),true).should.equal(true);
|
||||||
|
|
||||||
|
console.log('[test.publickeyring.js.193]'); //TODO
|
||||||
|
|
||||||
|
|
||||||
var w3 = new PublicKeyRing({
|
var w3 = new PublicKeyRing({
|
||||||
network: 'livenet',
|
networkName: 'livenet',
|
||||||
id: w.id,
|
id: w.id,
|
||||||
requiredCopayers: 2,
|
requiredCopayers: 2,
|
||||||
});
|
});
|
||||||
(function() { w3.merge(w.toObj());}).should.throw();
|
(function() { w3.merge(w.toObj());}).should.throw();
|
||||||
|
|
||||||
var w4 = new PublicKeyRing({
|
var w4 = new PublicKeyRing({
|
||||||
network: 'testnet',
|
networkName: 'testnet',
|
||||||
id: w.id,
|
id: w.id,
|
||||||
});
|
});
|
||||||
(function() { w4.merge(w.toObj());}).should.throw();
|
(function() { w4.merge(w.toObj());}).should.throw();
|
||||||
|
|
||||||
var w5 = new PublicKeyRing({
|
var w5 = new PublicKeyRing({
|
||||||
network: 'livenet',
|
networkName: 'livenet',
|
||||||
id: w.id,
|
id: w.id,
|
||||||
totalCopayers: 4,
|
totalCopayers: 4,
|
||||||
});
|
});
|
||||||
(function() { w5.merge(w.toObj());}).should.throw();
|
(function() { w5.merge(w.toObj());}).should.throw();
|
||||||
|
|
||||||
var w6 = new PublicKeyRing({
|
var w6 = new PublicKeyRing({
|
||||||
network: 'livenet',
|
networkName: 'livenet',
|
||||||
id: w.id,
|
id: w.id,
|
||||||
});
|
});
|
||||||
(function() { w6.merge(w);}).should.throw();
|
(function() { w6.merge(w);}).should.throw();
|
||||||
w.networkName= 'livenet';
|
w.networkName= 'livenet';
|
||||||
(function() { w6.merge(w);}).should.throw();
|
(function() { w6.merge(w);}).should.throw();
|
||||||
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
@ -222,7 +237,7 @@ describe('PublicKeyRing model', function() {
|
||||||
}
|
}
|
||||||
|
|
||||||
var w2 = new PublicKeyRing({
|
var w2 = new PublicKeyRing({
|
||||||
network: 'livenet',
|
networkName: 'livenet',
|
||||||
id: w.id,
|
id: w.id,
|
||||||
});
|
});
|
||||||
should.exist(w);
|
should.exist(w);
|
||||||
|
@ -248,17 +263,14 @@ describe('PublicKeyRing model', function() {
|
||||||
for(var i=0; i<5; i++) {
|
for(var i=0; i<5; i++) {
|
||||||
w.haveAllRequiredPubKeys().should.equal(false);
|
w.haveAllRequiredPubKeys().should.equal(false);
|
||||||
var w2 = new PublicKeyRing({
|
var w2 = new PublicKeyRing({
|
||||||
network: 'livenet',
|
networkName: 'livenet',
|
||||||
id: w.id,
|
id: w.id,
|
||||||
});
|
});
|
||||||
w2.addCopayer();
|
w2.addCopayer();
|
||||||
w.merge(w2.toObj());
|
w.merge(w2.toObj()).should.equal(true);
|
||||||
}
|
}
|
||||||
w.haveAllRequiredPubKeys().should.equal(true);
|
w.haveAllRequiredPubKeys().should.equal(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var chai = chai || require('chai');
|
||||||
|
var should = chai.should();
|
||||||
|
var copay = copay || require('../copay');
|
||||||
|
var Storage = copay.Storage || require('../js/models/Storage');
|
||||||
|
|
||||||
|
describe('Storage model', function() {
|
||||||
|
|
||||||
|
it('should create an instance', function () {
|
||||||
|
var s = new Storage();
|
||||||
|
should.exist(s);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
|
@ -0,0 +1,69 @@
|
||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var fs = require('fs');
|
||||||
|
var browserify = require('browserify');
|
||||||
|
var browserPack = require('browser-pack');
|
||||||
|
var exec = require('child_process').exec;
|
||||||
|
var sys = require('sys');
|
||||||
|
var puts = function(error, stdout, stderr) {
|
||||||
|
if (error) console.log(error);
|
||||||
|
//sys.puts(stdout);
|
||||||
|
//sys.puts(stderr);
|
||||||
|
};
|
||||||
|
|
||||||
|
var pack = function (params) {
|
||||||
|
var file = require.resolve('soop');
|
||||||
|
var dir = file.substr(0, file.length - String('soop.js').length);
|
||||||
|
var preludePath = dir + 'example/custom_prelude.js';
|
||||||
|
params.raw = true;
|
||||||
|
params.sourceMapPrefix = '//#';
|
||||||
|
params.prelude = fs.readFileSync(preludePath, 'utf8');
|
||||||
|
params.preludePath = preludePath;
|
||||||
|
return browserPack(params);
|
||||||
|
};
|
||||||
|
|
||||||
|
var createBundle = function(opts) {
|
||||||
|
|
||||||
|
|
||||||
|
opts.dir = opts.dir || 'js/';
|
||||||
|
|
||||||
|
// concat browser vendor files
|
||||||
|
exec('cd ' + opts.dir + 'browser; sh concat.sh', puts);
|
||||||
|
|
||||||
|
var bopts = {
|
||||||
|
pack: pack,
|
||||||
|
debug: true,
|
||||||
|
standalone: 'copay',
|
||||||
|
insertGlobals: true
|
||||||
|
};
|
||||||
|
var b = browserify(bopts);
|
||||||
|
b.require('./copay', {
|
||||||
|
expose: 'copay'
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!opts.dontminify) {
|
||||||
|
b.transform({
|
||||||
|
global: true
|
||||||
|
}, 'uglifyify');
|
||||||
|
}
|
||||||
|
var bundle = b.bundle();
|
||||||
|
return bundle;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (require.main === module) {
|
||||||
|
var list = function(val) {
|
||||||
|
return val.split(',');
|
||||||
|
};
|
||||||
|
var program = require('commander');
|
||||||
|
program
|
||||||
|
.version('0.0.1')
|
||||||
|
.option('-d, --dontminify', 'Don\'t minify the code.')
|
||||||
|
.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'));
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports.createBundle = createBundle;
|
Loading…
Reference in New Issue