Merge branch 'master' into feature/socket-io-support

Fixed conflicts on:
	index.html
	js/app.js
This commit is contained in:
Gustavo Cortez 2014-04-17 00:43:50 -03:00
commit 7d52f8487b
39 changed files with 1459 additions and 583 deletions

View file

@ -9,10 +9,11 @@ angular.module('copay',[
'copay.transactions',
'copay.send',
'copay.backup',
'copay.network',
'copay.walletFactory',
'copay.signin',
'copay.peer',
'copay.socket'
'copay.socket',
'copay.setup',
'copay.peer'
]);
angular.module('copay.header', []);
@ -20,8 +21,9 @@ angular.module('copay.home', []);
angular.module('copay.transactions', []);
angular.module('copay.send', []);
angular.module('copay.backup', []);
angular.module('copay.network', []);
angular.module('copay.walletFactory', []);
angular.module('copay.signin', []);
angular.module('copay.setup', []);
angular.module('copay.peer', []);
angular.module('copay.socket', []);

View file

@ -7,9 +7,15 @@ var config = {
maxPeers: 3,
debug: 3,
},
limits: {
totalCopayers: 10,
mPlusN: 15
},
wallet: {
requiredCopayers: 2,
totalCopayers: 3,
spendUnconfirmed: 1,
verbose: 1,
},
blockchain: {
host: 'test.insight.is',

View file

@ -1,7 +1,7 @@
'use strict';
angular.module('copay.header').controller('HeaderController',
function($scope, $rootScope, $location, Network) {
function($scope, $rootScope, $location, walletFactory) {
$scope.menu = [{
'title': 'Home',
'icon': 'fi-home',
@ -35,15 +35,13 @@ angular.module('copay.header').controller('HeaderController',
return false;
};
$scope.init = function() {
$rootScope.isLogged = false;
};
$scope.signout = function() {
Network.disconnect(function() {
var w = $rootScope.wallet;
if (w) {
w.disconnect();
delete $rootScope['wallet'];
$location.path('signin');
$rootScope.$digest();
});
}
};
$scope.clearFlashMessage = function() {

View file

@ -15,7 +15,9 @@ angular.module('copay.home').controller('HomeController',
}
$scope.newAddr = function() {
console.log('[home.js.17:newAddr:]'); //TODO
var a = $rootScope.wallet.generateAddress();
console.log('[home.js.19]',a); //TODO
$scope.addrs.push({ addrStr: a.toString() });
};

View file

@ -1,7 +1,7 @@
'use strict';
angular.module('copay.peer').controller('PeerController',
function($scope, $rootScope, $location, $routeParams, Network) {
function($scope, $rootScope, $location, $routeParams) {
$scope.init = function() {
//Network.connect($rootScope.masterId);

View file

@ -1,7 +1,7 @@
'use strict';
angular.module('copay.send').controller('SendController',
function($scope, $rootScope, $location, Network) {
function($scope, $rootScope, $location) {
$scope.title = 'Send';
if (!$rootScope.wallet.id) {
@ -11,27 +11,8 @@ angular.module('copay.send').controller('SendController',
$scope.sendTest = function() {
var w = $rootScope.wallet;
var pkr = w.publicKeyRing;
var txp = w.txProposals;
var opts = {remainderOut: { address: pkr.generateAddress(true).toString() }};
// From @cmgustavo's wallet
w.listUnspent(function (unspentTest) {
console.log('[send.js.19:unspentTest:]',unspentTest); //TODO
txp.create(
'15q6HKjWHAksHcH91JW23BJEuzZgFwydBt',
'123456789',
unspentTest,
w.privateKey,
opts
);
console.log('[send.js.29:txp:] READY:',txp); //TODO
Network.storeOpenWallet();
Network.sendTxProposals();
$rootScope.$digest;
});
w.createTx( '15q6HKjWHAksHcH91JW23BJEuzZgFwydBt', '12345',function() {
$rootScope.$digest();
});
};
});

48
js/controllers/setup.js Normal file
View file

@ -0,0 +1,48 @@
'use strict';
angular.module('copay.setup').controller('SetupController',
function($scope, $rootScope, $location, walletFactory) {
$scope.loading = false;
$scope.totalCopayers = config.wallet.totalCopayers;
$scope.TCValues = [];
for (var n = 1; n <= config.limits.totalCopayers; n++)
$scope.TCValues.push(n);
var updateRCSelect = function(n) {
$scope.requiredCopayers = parseInt(Math.min(n / 2 + 1, config.limits.mPlusN-n));
$scope.RCValues = [];
for (var m = 1; m <= n; m++) {
if (n + m <= config.limits.mPlusN) {
$scope.RCValues.push(m);
}
}
};
updateRCSelect($scope.totalCopayers);
$scope.$watch('totalCopayers', function(tc) {
updateRCSelect(tc);
})
$scope.create = function(totalCopayers, requiredCopayers) {
$scope.loading = true;
var opts = {
requiredCopayers: requiredCopayers,
totalCopayers: totalCopayers
};
var w = walletFactory.create(opts);
w.on('created', function(){
$location.path('peer');
$rootScope.wallet = w;
$rootScope.$digest();
});
w.on('openError', function(){
$scope.loading = false;
$rootScope.flashMessage = {type:'error', message: 'Wallet not found'};
$location.path('signin');
});
w.netStart();
};
});

View file

@ -1,7 +1,7 @@
'use strict';
angular.module('copay.signin').controller('SigninController',
function($scope, $rootScope, $location, Network) {
function($scope, $rootScope, $location, walletFactory) {
// var peerData = Storage.get($rootScope.walletId, 'peerData');
// $rootScope.peerId = peerData ? peerData.peerId : null;
@ -10,55 +10,68 @@ angular.module('copay.signin').controller('SigninController',
$scope.selectedWalletId = false;
$scope.listWalletIds = function() {
return copay.Wallet.factory.getWalletIds();
return walletFactory.getWalletIds();
};
var _setupUxHandlers = function(w) {
w.on('created', function() {
$location.path('peer');
$rootScope.wallet = w;
$rootScope.$digest();
});
w.on('refresh', function() {
console.log('[signin.js.23] RECEIVED REFRESH'); //TODO
$rootScope.$digest();
});
w.on('openError', function(){
$scope.loading = false;
$rootScope.flashMessage = {type:'error', message: 'Wallet not found'};
$location.path('signin');
});
};
$scope.create = function() {
$scope.loading = true;
Network.createWallet();
Network.init(function() {
$location.path('peer');
$rootScope.$digest();
});
$location.path('setup');
};
$scope.open = function(walletId) {
$scope.loading = true;
Network.openWallet(walletId);
if ($rootScope.wallet && $rootScope.wallet.id) {
Network.init(function() {
$location.path('peer');
$rootScope.$digest();
});
}
else {
$scope.loading = false;
$rootScope.flashMessage = {type:'error', message: 'Wallet not found'};
$location.path('signin');
}
var w = walletFactory.open(walletId);
_setupUxHandlers(w);
w.netStart();
};
$scope.join = function(cid) {
console.log('[signin.js.42:join:]'); //TODO
$scope.loading = true;
if (cid) {
Network.init(function() {
Network.connect(cid,
function() {
$location.path('peer');
$rootScope.$digest();
}, function() {
$rootScope.flashMessage = { message: 'Connection refussed', type: 'error'};
$location.path('home');
$rootScope.$digest();
});
});
}
walletFactory.connectTo(cid, function(w) {
console.log('[signin.js.50]'); //TODO
_setupUxHandlers(w);
w.netStart();
});
};
//
// if (cid) {
// var w = walletFactory.(walletId);
//TODO
// Network.init(null, function() {
// Network.connect(cid,
// function() {
// $location.path('peer');
// $rootScope.$digest();
// }, function() {
// $rootScope.flashMessage = { message: 'Connection refussed', type: 'error'};
// $location.path('home');
// $rootScope.$digest();
// });
// });
// }
// if (peerData && peerData.peerId && peerData.connectedPeers.length > 0) {
// $rootScope.peerId = peerData.peerId;
// $scope.join(peerData.connectedPeers);

View file

@ -24,4 +24,11 @@ angular.module('copay.transactions').controller('TransactionsController',
];
$scope.txsoutput = $rootScope.wallet.getTxProposals();
$scope.sign = function (ntxid) {
var w = $rootScope.wallet;
var ret = w.sign(ntxid);
console.log('[transactions.js.28:ret:]',ret); //TODO
$scope.txsoutput = $rootScope.wallet.getTxProposals();
};
});

View file

@ -59,7 +59,7 @@ Insight.prototype.listUnspent = function(addresses, cb) {
self._request(options, function(err, res) {
if (res && res.length > 0) {
all = all.concat(res);
}
}
callback();
});
}, function() {

View file

@ -7,102 +7,147 @@ var coinUtil = bitcore.util;
var buffertools = bitcore.buffertools;
var Builder = bitcore.TransactionBuilder;
var http = require('http');
var Storage = imports.Storage;
var Network = imports.Network;
var Blockchain = imports.Blockchain;
var EventEmitter= imports.EventEmitter || require('events').EventEmitter;
var copay = copay || require('../../../copay');
function Wallet(config) {
this._startInterface(config);
}
function Wallet(opts) {
var self = this;
Wallet.prototype.log = function(){
if (!this.verbose) return;
//required params
['storage', 'network', 'blockchain',
'requiredCopayers', 'totalCopayers', 'spendUnconfirmed',
'publicKeyRing', 'txProposals', 'privateKey'
].forEach( function(k){
if (typeof opts[k] === 'undefined') throw new Error('missing key:' + k);
self[k] = opts[k];
});
console.this.log(arguments);
}
console.log('creating '+opts.requiredCopayers+' of '+opts.totalCopayers+' wallet');
Wallet.prototype._startInterface = function(config) {
this.storage = new Storage(config.storage);
this.network = new Network(config.network);
this.blockchain = new Blockchain(config.blockchain);
this.networkName = config.networkName;
this.requiredCopayers = config.wallet.requiredCopayers;
this.totalCopayers = config.wallet.totalCopayers;
};
Wallet.prototype.create = function(opts) {
opts = opts || {};
this.id = opts.id || Wallet.getRandomId();
this.log('### CREATING NEW WALLET.' + (opts.id ? ' USING ID: ' + opts.id : ' NEW ID'));
this.verbose = opts.verbose;
this.publicKeyRing.walletId = this.id;
this.txProposals.walletId = this.id;
this.privateKey = new copay.PrivateKey({
networkName: this.networkName
});
this.log('\t### PrivateKey Initialized');
this.publicKeyRing = new copay.PublicKeyRing({
walletId: this.id,
requiredCopayers: opts.requiredCopayers || this.requiredCopayers,
totalCopayers: opts.totalCopayers || this.totalCopayers,
networkName: this.networkName,
});
this.publicKeyRing.addCopayer(this.privateKey.getBIP32().extendedPublicKeyString());
this.log('\t### PublicKeyRing Initialized WalletID: ' + this.publicKeyRing.walletId);
this.txProposals = new copay.TxProposals({
walletId: this.id,
networkName: this.networkName,
});
this.log('\t### TxProposals Initialized');
};
Wallet.prototype._checkLoad = function(walletId) {
var ret = this.storage.get(walletId, 'publicKeyRing') &&
this.storage.get(walletId, 'txProposals') &&
this.storage.get(walletId, 'privateKey')
;
return ret;
}
Wallet.prototype.load = function(walletId) {
if (! this._checkLoad(walletId)) return;
Wallet.parent=EventEmitter;
Wallet.prototype.log = function(){
// if (!this.verbose) return;
console.log(arguments);
};
Wallet.getRandomId = function() {
var r = buffertools.toHex(coinUtil.generateNonce());
return r;
};
this.id = walletId;
this.publicKeyRing = new copay.PublicKeyRing.fromObj(
this.storage.get(this.id, 'publicKeyRing')
);
this.txProposals = new copay.TxProposals.fromObj(
this.storage.get(this.id, 'txProposals')
);
this.privateKey = new copay.PrivateKey.fromObj(
this.storage.get(this.id, 'privateKey')
); //TODO secure
Wallet.prototype._handlePublicKeyRing = function(senderId, data, isInbound) {
this.log('RECV PUBLICKEYRING:',data);
// JIC: Add our key
try {
this.publicKeyRing.addCopayer(
this.privateKey.getBIP32().extendedPublicKeyString()
);
} catch (e) {
this.log('NOT NECCESARY AN ERROR:', e); //TODO
};
var shouldSend = false;
var recipients, pkr = this.publicKeyRing;
var inPKR = copay.PublicKeyRing.fromObj(data.publicKeyRing);
if (pkr.merge(inPKR, true) && !data.isBroadcast) {
this.log('### BROADCASTING PKR');
recipients = null;
shouldSend = true;
}
else if (isInbound && !data.isBroadcast) {
// always replying to connecting peer
this.log('### REPLYING PKR TO:', senderId);
recipients = senderId;
shouldSend = true;
}
if (shouldSend) {
this.sendPublicKeyRing(recipients);
}
this.store();
};
Wallet.prototype._handleTxProposals = function(senderId, data, isInbound) {
this.log('RECV TXPROPOSAL:',data); //TODO
var shouldSend = false;
var recipients;
var inTxp = copay.TxProposals.fromObj(data.txProposals);
var mergeInfo = this.txProposals.merge(inTxp, true);
var addSeen = this.addSeenToTxProposals();
if ((mergeInfo.merged && !data.isBroadcast) || addSeen) {
this.log('### BROADCASTING txProposals. ' );
recipients = null;
shouldSend = true;
}
else if (isInbound && !data.isBroadcast) {
// always replying to connecting peer
this.log('### REPLYING txProposals TO:', senderId);
recipients = senderId;
shouldSend = true;
}
if (shouldSend)
this.sendTxProposals(recipients);
this.store();
};
Wallet.prototype._handleData = function(senderId, data, isInbound) {
if (this.id !== data.walletId)
throw new Error('wrong message received: Bad wallet ID');
console.log('[Wallet.js.98]' , data.type); //TODO
switch(data.type) {
case 'publicKeyRing':
this._handlePublicKeyRing(senderId, data, isInbound);
break;
case 'txProposals':
this._handleTxProposals(senderId, data, isInbound);
break;
case 'abort':
this.emit('abort');
break;
}
};
Wallet.prototype._handleNetworkChange = function(newPeer) {
if (!newPeer) return;
this.log('#### Setting new PEER:', newPeer);
this.sendWalletId(newPeer);
this.sendPublicKeyRing(newPeer);
this.sendTxProposals(newPeer);
};
Wallet.prototype.netStart = function() {
var self = this;
var net = this.network;
net.on('networkChange', self._handleNetworkChange.bind(self) );
net.on('data', self._handleData.bind(self) );
net.on('open', function() {}); // TODO
net.on('close', function() {}); // TODO
net.start(function(peerId) {
self.emit('created');
});
};
Wallet.prototype.store = function() {
Wallet.factory.addWalletId(this.id);
console.log('[Wallet.js.135:store:]'); //TODO
this.storage.set(this.id,'opts', {
id: this.id,
spendUnconfirmed: this.spendUnconfirmed,
requiredCopayers: this.requiredCopayers,
totalCopayers: this.totalCopayers,
});
this.storage.set(this.id,'publicKeyRing', this.publicKeyRing.toObj());
this.storage.set(this.id,'txProposals', this.txProposals.toObj());
this.storage.set(this.id,'privateKey', this.privateKey.toObj());
console.log('[Wallet.js.146] EMIT REFRESH'); //TODO
this.emit('refresh');
};
@ -114,8 +159,20 @@ Wallet.prototype.sendTxProposals = function(recipients) {
txProposals: this.txProposals.toObj(),
walletId: this.id,
});
this.emit('txProposalsUpdated', this.txProposals);
};
Wallet.prototype.sendWalletId = function(recipients) {
this.log('### SENDING walletId TO:', recipients||'All', this.walletId);
this.network.send(recipients, {
type: 'walletId',
walletId: this.id,
});
};
Wallet.prototype.sendPublicKeyRing = function(recipients) {
this.log('### SENDING publicKeyRing TO:', recipients||'All', this.publicKeyRing.toObj());
@ -124,38 +181,82 @@ Wallet.prototype.sendPublicKeyRing = function(recipients) {
publicKeyRing: this.publicKeyRing.toObj(),
walletId: this.id,
});
this.emit('publicKeyRingUpdated', this.publicKeyRing);
};
Wallet.prototype.generateAddress = function() {
var addr = this.publicKeyRing.generateAddress();
this.store();
this.network.send(null, {
type: 'publicKeyRing',
publicKeyRing: this.publicKeyRing.toObj(),
walletId: this.id
});
this.sendPublicKeyRing();
return addr;
};
Wallet.prototype.getTxProposals = function() {
var ret = [];
this.txProposals.txps.forEach(function(txp) {
var self= this;
self.txProposals.txps.forEach(function(txp) {
var i = {txp:txp};
i.signedByUs = txp.signedBy[this.privateKey.id]?true:false;
i.ntxid = txp.builder.build().getNormalizedHash();
i.signedByUs = txp.signedBy[self.privateKey.id]?true:false;
ret.push(i);
});
return ret;
};
// TODO: this can be precalculated.
Wallet.prototype._findTxByNtxid = function(ntxid) {
var ret;
var l = this.txProposals.txps.length;
var id = ntxid.toString('hex');
for(var i=0; i<l; i++) {
var txp = this.txProposals.txps[i];
var id2 = txp.builder.build().getNormalizedHash().toString('hex');
if (id === id2 ) {
ret = txp;
}
}
return ret;
};
Wallet.prototype.sign = function(ntxid) {
var txp = this._findTxByNtxid(ntxid);
if (!txp) return;
var pkr = this.publicKeyRing;
var keys = this.privateKey.getAll(pkr.addressIndex, pkr.changeAddressIndex);
var ret = txp.builder.sign(keys);
if (ret.signaturesAdded) {
txp.signedBy[this.privateKey.id] = Date.now();
console.log('[Wallet.js.230:ret:]',ret); //TODO
if (ret.isFullySigned) {
console.log('[Wallet.js.231] BROADCASTING TX!!!'); //TODO
var tx = txp.builder.build();
var txHex = tx.serialize().toString('hex');
this.blockchain.sendRawTransaction(txHex, function(txid) {
console.log('[Wallet.js.235:txid:]',txid); //TODO
if (txid) {
this.store();
}
});
}
else {
this.sendTxProposals();
this.store();
}
}
return ret;
};
Wallet.prototype.addSeenToTxProposals = function() {
var ret=false;
var self=this;
this.txProposals.txps.forEach(function(txp) {
if (!txp.seenBy[this.privateKey.id]) {
txp.seenBy[this.privateKey.id] = Date.now();
if (!txp.seenBy[self.privateKey.id]) {
txp.seenBy[self.privateKey.id] = Date.now();
ret = true;
}
});
@ -181,7 +282,33 @@ Wallet.prototype.listUnspent = function(cb) {
this.blockchain.listUnspent(this.getAddressesStr(), cb);
};
Wallet.prototype.createTx = function(toAddress, amountSatStr, utxos, opts) {
Wallet.prototype.createTx = function(toAddress, amountSatStr, opts, cb) {
var self = this;
if (typeof opts === 'function') {
cb = opts;
opts = {};
}
opts = opts || {};
if (typeof opts.spendUnconfirmed === 'undefined') {
opts.spendUnconfirmed = this.spendUnconfirmed;
}
if (!opts.remainderOut) {
opts.remainderOut={ address: this.publicKeyRing.generateAddress(true).toString()};
}
self.listUnspent(function(utxos) {
// TODO check enough funds, etc.
self.createTxSync(toAddress, amountSatStr, utxos, opts);
self.store();
self.sendTxProposals();
return cb();
});
};
Wallet.prototype.createTxSync = function(toAddress, amountSatStr, utxos, opts) {
var pkr = this.publicKeyRing;
var priv = this.privateKey;
opts = opts || {};
@ -216,7 +343,12 @@ Wallet.prototype.createTx = function(toAddress, amountSatStr, utxos, opts) {
});
};
Wallet.prototype.sign = function(txp) {
Wallet.prototype.connectTo = function(peerId) {
throw new Error('Wallet.connectTo.. not yet implemented!');
};
Wallet.prototype.disconnect = function() {
this.network.disconnect();
};
// // HERE? not sure
@ -224,57 +356,6 @@ Wallet.prototype.sign = function(txp) {
// this.storage.remove('peerData');
// };
//
Wallet.getRandomId = function() {
var r = buffertools.toHex(coinUtil.generateNonce());
return r;
};
/*
* WalletFactory
*
*/
var WalletFactory = function() {
this.storage = Storage.default();
};
WalletFactory.prototype.create = function(config, opts) {
var w = new Wallet(config);
w.create(opts);
w.store();
return w;
};
WalletFactory.prototype.get = function(config, walletId) {
return Wallet.read(config, walletId);
};
WalletFactory.prototype.remove = function(walletId) {
// TODO remove wallet contents, not only the id (Wallet.remove?)
this._delWalletId(walletId);
};
WalletFactory.prototype.addWalletId = function(walletId) {
var ids = this.getWalletIds();
if (ids.indexOf(walletId) !== -1) return;
ids.push(walletId);
this.storage.setGlobal('walletIds', ids);
};
WalletFactory.prototype._delWalletId = function(walletId) {
var ids = this.getWalletIds();
var index = ids.indexOf(walletId);
if (index === -1) return;
ids.splice(index, 1); // removes walletId
this.storage.setGlobal('walletIds', ids);
};
WalletFactory.prototype.getWalletIds = function() {
var ids = this.storage.getGlobal('walletIds');
return ids || [];
};
Wallet.factory = new WalletFactory();
;
module.exports = require('soop')(Wallet);

View file

@ -0,0 +1,180 @@
'use strict';
var imports = require('soop').imports();
var Storage = imports.Storage;
var Network = imports.Network;
var Blockchain = imports.Blockchain;
var TxProposals = require('./TxProposals');
var PublicKeyRing = require('./PublicKeyRing');
var PrivateKey = require('./PrivateKey');
var Wallet = require('./Wallet');
/*
* WalletFactory
*
*
* var wallet = WF.read(config,walletId); -> always go to storage
* var wallet = WF.create(config,walletId); -> create wallets, with the given ID (or random is not given)
*
* var wallet = WF.open(config,walletId); -> try to read walletId, if fails, create a new wallet with that id
*/
function WalletFactory(config) {
var self = this;
this.storage = new Storage(config.storage);
this.network = new Network(config.network);
this.blockchain = new Blockchain(config.blockchain);
this.networkName = config.networkName;
this.verbose = config.verbose;
this.walletDefaults = config.wallet;
}
WalletFactory.prototype.log = function(){
if (!this.verbose) return;
console.log(arguments);
};
WalletFactory.prototype._checkRead = function(walletId) {
var s = this.storage;
var ret = s.get(walletId, 'publicKeyRing') &&
s.get(walletId, 'txProposals') &&
s.get(walletId, 'opts') &&
s.get(walletId, 'privateKey')
;
return ret;
};
WalletFactory.prototype.read = function(walletId) {
if (! this._checkRead(walletId)) return false;
var s = this.storage;
var opts = s.get(walletId, 'opts');
opts.publicKeyRing = new PublicKeyRing.fromObj(s.get(walletId, 'publicKeyRing'));
opts.txProposals = new TxProposals.fromObj(s.get(walletId, 'txProposals'));
opts.privateKey = new PrivateKey.fromObj(s.get(walletId, 'privateKey'));
opts.storage = this.storage;
opts.network = this.network;
opts.blockchain = this.blockchain;
opts.verbose = this.verbose;
var w = new Wallet(opts);
// JIC: Add our key
try {
w.publicKeyRing.addCopayer(
w.privateKey.getBIP32().extendedPublicKeyString()
);
} catch (e) {
this.log('NOT NECCESARY AN ERROR:', e); //TODO
}
this.log('### WALLET OPENED:', w.id);
return w;
};
WalletFactory.prototype.create = function(opts) {
var s = WalletFactory.storage;
opts = opts || {};
this.log('### CREATING NEW WALLET.' + (opts.id ? ' USING ID: ' + opts.id : ' NEW ID'));
opts.privateKey = opts.privateKey || new PrivateKey({ networkName: this.networkName });
this.log('\t### PrivateKey Initialized');
var requiredCopayers = opts.requiredCopayers || this.walletDefaults.requiredCopayers;
var totalCopayers = opts.totalCopayers || this.walletDefaults.totalCopayers;
opts.publicKeyRing = opts.publicKeyRing || new PublicKeyRing({
networkName: this.networkName,
requiredCopayers: requiredCopayers,
totalCopayers: totalCopayers,
});
opts.publicKeyRing.addCopayer(opts.privateKey.getBIP32().extendedPublicKeyString());
this.log('\t### PublicKeyRing Initialized');
opts.txProposals = opts.txProposals || new TxProposals({
networkName: this.networkName,
});
this.log('\t### TxProposals Initialized');
opts.storage = this.storage;
opts.network = this.network;
opts.blockchain = this.blockchain;
opts.verbose = this.verbose;
opts.spendUnconfirmed = opts.spendUnconfirmed || this.walletDefaults.spendUnconfirmed;
opts.requiredCopayers = requiredCopayers;
opts.totalCopayers = totalCopayers;
var w = new Wallet(opts);
w.store();
return w;
};
WalletFactory.prototype.open = function(walletId) {
var w = this.read(walletId) || this.create({
id: walletId,
verbose: this.verbose,
});
return w;
};
WalletFactory.prototype.openRemote = function(peedId) {
var s = WalletFactory.storage;
opts = opts || {};
this.log('### CREATING NEW WALLET.' + (opts.id ? ' USING ID: ' + opts.id : ' NEW ID'));
opts.privateKey = opts.privateKey || new PrivateKey({ networkName: this.networkName });
this.log('\t### PrivateKey Initialized');
var requiredCopayers = opts.requiredCopayers || this.walletDefaults.requiredCopayers;
var totalCopayers = opts.totalCopayers || this.walletDefaults.totalCopayers;
opts.publicKeyRing = opts.publicKeyRing || new PublicKeyRing({
networkName: this.networkName,
requiredCopayers: requiredCopayers,
totalCopayers: totalCopayers,
});
opts.publicKeyRing.addCopayer(opts.privateKey.getBIP32().extendedPublicKeyString());
this.log('\t### PublicKeyRing Initialized');
opts.txProposals = opts.txProposals || new TxProposals({
networkName: this.networkName,
});
this.log('\t### TxProposals Initialized');
opts.storage = this.storage;
opts.network = this.network;
opts.blockchain = this.blockchain;
opts.spendUnconfirmed = opts.spendUnconfirmed || this.walletDefaults.spendUnconfirmed;
opts.requiredCopayers = requiredCopayers;
opts.totalCopayers = totalCopayers;
var w = new Wallet(opts);
w.store();
this.addWalletId(w.id);
return w;
};
WalletFactory.prototype.getWalletIds = function() {
return this.storage.getWalletIds();
}
WalletFactory.prototype.remove = function(walletId) {
// TODO remove wallet contents
};
WalletFactory.prototype.connectTo = function(peerId, cb) {
var self=this;
self.network.start(function() {
self.network.connectTo(peerId)
self.network.on('walletId', function(walletId) {
console.log('[WalletFactory.js.187]'); //TODO
return cb(self.open(walletId));
});
});
};
module.exports = require('soop')(WalletFactory);

View file

@ -67,7 +67,6 @@ Network.prototype._showConnectedPeers = function() {
};
Network.prototype._onClose = function(peerId) {
console.log('[Network.js.70] _onClose'); //TODO
this.connectedPeers = Network._arrayRemove(peerId, this.connectedPeers);
this._notify();
};
@ -103,6 +102,9 @@ Network.prototype._onData = function(data, isInbound) {
case 'disconnect':
this._onClose(obj.sender);
break;
case 'walletId':
this.emit('walletId', obj.data.walletId);
break;
default:
this.emit('data', obj.sender, obj.data, isInbound);
}
@ -131,8 +133,7 @@ Network.prototype._addPeer = function(peerId, isInbound) {
}
};
Network.prototype._setupConnectionHandlers = function(
dataConn, isInbound, openCallback, closeCallback) {
Network.prototype._setupConnectionHandlers = function(dataConn, isInbound) {
var self=this;
@ -144,7 +145,7 @@ Network.prototype._setupConnectionHandlers = function(
self._addPeer(dataConn.peer, isInbound);
self._notify( isInbound ? dataConn.peer : null);
if (typeof openCallback === 'function') openCallback();
this.emit('open');
}
});
@ -158,10 +159,10 @@ Network.prototype._setupConnectionHandlers = function(
dataConn.on('close', function() {
if (self.closing) return;
self.closing=1;
console.log('### CLOSE RECV FROM:', dataConn.peer);
self._onClose(dataConn.peer);
if (typeof closeCallback === 'function') closeCallback();
this.emit('close');
});
};
@ -174,7 +175,6 @@ Network.prototype._setupPeerHandlers = function(openCallback) {
var self=this;
var p = this.peer;
p.on('open', function(peerId) {
self.peerId = peerId;
self.connectedPeers = [peerId];
@ -208,6 +208,8 @@ Network.prototype._setupPeerHandlers = function(openCallback) {
Network.prototype.start = function(openCallback) {
// Start PeerJS Peer
if (this.peer) return openCallback(); // This is for connectTo-> peer is started before
this.peer = new Peer(this.peerId, {
key: this.apiKey, // TODO: we need our own PeerServer KEY (http://peerjs.com/peerserver)
debug: this.debug,
@ -218,8 +220,6 @@ Network.prototype.start = function(openCallback) {
Network.prototype._sendToOne = function(peerId, data, cb) {
if (peerId !== this.peerId) {
console.log('[WebRTC.js.222:peerId:]',peerId, data); //TODO
var conns = this.peer.connections[peerId];
if (conns) {
@ -257,7 +257,7 @@ Network.prototype.send = function(peerIds, data, cb) {
self._sendToOne(peerIds, data, cb);
};
Network.prototype.connectTo = function(peerId, openCallback, closeCallback ) {
Network.prototype.connectTo = function(peerId) {
var self = this;
console.log('### STARTING TO CONNECT TO:' + peerId );
@ -269,11 +269,11 @@ Network.prototype.connectTo = function(peerId, openCallback, closeCallback ) {
metadata: { message: 'hi copayer!' }
});
self._setupConnectionHandlers(dataConn, false, openCallback, closeCallback);
self._setupConnectionHandlers(dataConn, false);
};
Network.prototype.disconnect = function(peerId, cb) {
Network.prototype.disconnect = function(cb) {
var self = this;
self.closing = 1;

View file

@ -17,6 +17,9 @@ Storage.prototype.set = function(walletId,v) {
Storage.prototype.remove = function(walletId, k) {
};
Storage.prototype.getWalletIds = function() {
};
// remove all values
Storage.prototype.clearAll = function() {
};

102
js/models/storage/File.js Normal file
View file

@ -0,0 +1,102 @@
'use strict';
var imports = require('soop').imports();
var fs = imports.fs || require('fs');
function Storage(opts) {
opts = opts || {};
this.data = {};
}
Storage.prototype.load = function(walletId, callback) {
fs.readFile(walletId, function(err, data) {
if (err) return callback(err);
try {
this.data[walletId] = JSON.parse(data);
} catch (err) {
if (callback)
return callback(err);
}
if (callback)
return callback(null);
});
};
Storage.prototype.save = function(walletId, callback) {
var data = JSON.stringify(this.data[walletId]);
//TODO: update to use a queue to ensure that saves are made sequentially
fs.writeFile(walletId, data, function(err) {
if (callback)
return callback(err);
});
};
Storage.prototype._read = function(k) {
var split = k.split('::');
var walletId = split[0];
var key = split[1];
return this.data[walletId][key];
};
Storage.prototype._write = function(k, v, callback) {
var split = k.split('::');
var walletId = split[0];
var key = split[1];
if (!this.data[walletId])
this.data[walletId] = {};
this.data[walletId][key] = v;
this.save(walletId, callback);
};
// get value by key
Storage.prototype.getGlobal = function(k) {
return this._read(k);
};
// set value for key
Storage.prototype.setGlobal = function(k, v, callback) {
this._write(k, v, callback);
};
// remove value for key
Storage.prototype.removeGlobal = function(k, callback) {
var split = k.split('::');
var walletId = split[0];
var key = split[1];
delete this.data[walletId][key];
this.save(walletId, callback);
};
Storage.prototype._key = function(walletId, k) {
return walletId + '::' + k;
};
// get value by key
Storage.prototype.get = function(walletId, k) {
return this.getGlobal(this._key(walletId, k));
};
// set value for key
Storage.prototype.set = function(walletId, k, v, callback) {
this.setGlobal(this._key(walletId,k), v, callback);
};
// remove value for key
Storage.prototype.remove = function(walletId, k, callback) {
this.removeGlobal(this._key(walletId, k), callback);
};
Storage.prototype.getWalletIds = function() {
return [];
};
// remove all values
Storage.prototype.clearAll = function(callback) {
this.data = {};
this.save(callback);
};
module.exports = require('soop')(Storage);

View file

@ -2,7 +2,7 @@
var imports = require('soop').imports();
//var buffertools = imports.buffertools || require('buffertools');
var parent = imports.parent || require('./Plain');
var parent = imports.parent || require('./LocalPlain');
var id = 0;
function Storage() {

View file

@ -52,6 +52,21 @@ Storage.prototype.remove = function(walletId, k) {
this.removeGlobal(this._key(walletId,k));
};
Storage.prototype.getWalletIds = function() {
var walletIds = [];
for (var i = 0; i < localStorage.length; i++) {
var key = localStorage.key(i);
var split = key.split('::');
if (split.length == 2) {
var walletId = split[0];
walletIds.push(walletId);
}
}
return walletIds;
};
// remove all values
Storage.prototype.clearAll = function() {
localStorage.clear();

View file

@ -12,6 +12,9 @@ angular
.when('/signin', {
templateUrl: 'signin.html'
})
.when('/setup', {
templateUrl: 'setup.html'
})
.when('/home', {
templateUrl: 'home.html'
})

View file

@ -1,204 +0,0 @@
'use strict';
angular.module('copay.network')
.factory('Network', function($rootScope) {
var peer;
var _refreshUx = function() {
var net = $rootScope.wallet.network;
log('*** UPDATING UX'); //TODO
$rootScope.peedId = net.peerId;
$rootScope.connectedPeers = net.connectedPeers;
$rootScope.$digest();
};
// set new inbound connections
var _setNewPeer = function(newPeer) {
var w = $rootScope.wallet;
log('#### Setting new PEER:', newPeer);
w.sendPublicKeyRing(newPeer);
w.sendTxProposals(newPeer);
};
var _handleNetworkChange = function(newPeer) {
var cp = $rootScope.cp;
if (newPeer)
_setNewPeer(newPeer);
_refreshUx();
};
// TODO -> probably not in network.js
var storeOpenWallet = function() {
var w = $rootScope.wallet;
w.store();
log('\t### Wallet %s Stored', w.id);
};
// TODO -> probably not in network.js
var createWallet = function(walletId) {
var w = $rootScope.wallet || new copay.Wallet(config);
w.create({id: walletId});
w.store();
$rootScope.wallet = w;
log('createWallet ENDED'); //TODO
};
var openWallet = function (walletId) {
var w = $rootScope.wallet || new copay.Wallet(config);
w.load(walletId);
if (w && w.publicKeyRing && w.privateKey) {
log('### WALLET OPENED:', w.walletId);
$rootScope.wallet = w;
}
};
var closeWallet = function() {
var w = $rootScope.wallet;
if (w && w.id)
w.store();
log('### CLOSING WALLET');
delete $rootScope['wallet'];
};
var _checkWallet = function(walletId) {
log('[network.js.79:_checkWallet:]',walletId); //TODO
if ($rootScope.wallet && $rootScope.wallet.id === walletId)
return;
if ($rootScope.wallet && $rootScope.wallet.id && $rootScope.wallet.id !== walletId) {
throw new Error('message to wrong walletID');
}
if (!openWallet(walletId)) {
createWallet(walletId);
}
};
var _handlePublicKeyRing = function(senderId, data, isInbound) {
_checkWallet(data.walletId);
var shouldSend = false;
var w = $rootScope.wallet;
var recipients, pkr = w.publicKeyRing;
var inPKR = copay.PublicKeyRing.fromObj(data.publicKeyRing);
if (pkr.merge(inPKR, true) && !data.isBroadcast) {
log('### BROADCASTING PKR');
recipients = null;
shouldSend = true;
}
else if (isInbound && !data.isBroadcast) {
// always replying to connecting peer
log('### REPLYING PKR TO:', senderId);
recipients = senderId;
shouldSend = true;
}
if (shouldSend) {
w.sendPublicKeyRing(recipients);
}
_refreshUx();
};
var _handleTxProposals = function(senderId, data, isInbound) {
_checkWallet(data.walletId);
var shouldSend = false;
var w = $rootScope.wallet;
log('RECV TXPROPOSAL:',data); //TODO
var recipients;
var inTxProposals = copay.TxProposals.fromObj(data.txProposals);
var mergeInfo = w.txProposals.merge(inTxProposals, true);
var addSeen = w.addSeenToTxProposals();
if ((mergeInfo.merged && !data.isBroadcast) || addSeen) {
log('### BROADCASTING txProposals. ' );
recipients = null;
shouldSend = true;
}
else if (isInbound && !data.isBroadcast) {
// always replying to connecting peer
log('### REPLYING txProposals TO:', senderId);
recipients = senderId;
shouldSend = true;
}
if (shouldSend) {
w.sendTxProposals(recipients);
}
};
var _handleData = function(senderId, data, isInbound) {
switch(data.type) {
case 'publicKeyRing':
_handlePublicKeyRing(senderId, data, isInbound);
break;
case 'txProposals':
_handleTxProposals(senderId, data, isInbound);
break;
case 'abort':
disconnect();
_refreshUx();
break;
}
};
// public methods
var init = function(cb) {
if (!$rootScope.wallet) {
// create an empty Wallet
$rootScope.wallet = new copay.Wallet(config);
}
var net = $rootScope.wallet.network;
net.on('networkChange', _handleNetworkChange);
net.on('data', _handleData);
net.start(function(peerId) {
return cb();
});
};
var disconnect = function() {
var w = $rootScope.wallet;
var net = w.network;
if (net) {
net.disconnect();
}
closeWallet();
};
var connect = function(peerId, openCallback, failCallback) {
if ($rootScope.wallet.network) {
$rootScope.wallet.network.connectTo(peerId, openCallback, function () {
disconnect();
failCallback();
});
}
else
return failCallback();
};
var sendTxProposals = function(recipients) {
var w = $rootScope.wallet;
w.sendTxProposals(recipients);
};
return {
init: init,
connect: connect,
disconnect: disconnect,
createWallet: createWallet,
openWallet: openWallet,
storeOpenWallet: storeOpenWallet,
sendTxProposals: sendTxProposals,
}
});

View file

@ -0,0 +1,8 @@
'use strict';
var wf;
angular.module('copay.walletFactory').factory('walletFactory', function($rootScope) {
wf = wf || new copay.WalletFactory(config);
return wf;
});