From fea90215d46929d5d414d3cb3d6129c4b04e79d7 Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Wed, 20 Aug 2014 14:06:37 -0400 Subject: [PATCH 01/22] add options to show the private key --- js/controllers/backup.js | 10 ++++++++-- views/backup.html | 5 +++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/js/controllers/backup.js b/js/controllers/backup.js index df14062c7..8582b3bfe 100644 --- a/js/controllers/backup.js +++ b/js/controllers/backup.js @@ -1,8 +1,14 @@ 'use strict'; angular.module('copayApp.controllers').controller('BackupController', - function($scope, $rootScope, backupService, walletFactory, controllerUtils) { - + function($scope, $rootScope, $location, backupService, walletFactory, controllerUtils) { + var s = ($location.search()).showPrivate; + if (s) { + var w = $rootScope.wallet; + $scope.priv = w.privateKey.toObj().extendedPrivateKeyString; + } + + $scope.downloadBackup = function() { var w = $rootScope.wallet; backupService.download(w); diff --git a/views/backup.html b/views/backup.html index 174100023..d747bbb9e 100644 --- a/views/backup.html +++ b/views/backup.html @@ -17,5 +17,10 @@ +
+

Master Private Key

+

+ {{priv}} +

From 7f0e9cd03dadf4a1011813219d9a1785bbf72de2 Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Wed, 20 Aug 2014 14:45:59 -0400 Subject: [PATCH 02/22] implements skipping fields in backup imports --- js/controllers/backup.js | 1 - js/controllers/import.js | 6 +- js/models/core/PublicKeyRing.js | 2 +- js/models/core/Wallet.js | 230 ++++++++++++++++++-------------- js/models/core/WalletFactory.js | 22 ++- test/test.WalletFactory.js | 13 ++ views/import.html | 8 ++ 7 files changed, 169 insertions(+), 113 deletions(-) diff --git a/js/controllers/backup.js b/js/controllers/backup.js index 8582b3bfe..9b0e23656 100644 --- a/js/controllers/backup.js +++ b/js/controllers/backup.js @@ -21,5 +21,4 @@ angular.module('copayApp.controllers').controller('BackupController', controllerUtils.logout(); }); }; - }); diff --git a/js/controllers/import.js b/js/controllers/import.js index f978884cd..c64bea1c6 100644 --- a/js/controllers/import.js +++ b/js/controllers/import.js @@ -7,6 +7,10 @@ angular.module('copayApp.controllers').controller('ImportController', $scope.title = 'Import a backup'; $scope.importStatus = 'Importing wallet - Reading backup...'; + var s = ($location.search()).skip; + if (s) { + $scope.skipFields = s.split(','); + } var reader = new FileReader(); @@ -22,7 +26,7 @@ angular.module('copayApp.controllers').controller('ImportController', // try to import encrypted wallet with passphrase try { - w = walletFactory.import(encryptedObj, passphrase); + w = walletFactory.import(encryptedObj, passphrase, $scope.skipFields); } catch (e) { errMsg = e.message; } diff --git a/js/models/core/PublicKeyRing.js b/js/models/core/PublicKeyRing.js index 898739283..3a71dd0e6 100644 --- a/js/models/core/PublicKeyRing.js +++ b/js/models/core/PublicKeyRing.js @@ -239,7 +239,7 @@ PublicKeyRing.prototype.getCosigner = function(pubKey) { }); var index = sorted.indexOf(pubKey); - if (index == -1) throw new Error('no public key in ring'); + if (index == -1) throw new Error('public key is not on the ring'); return index; } diff --git a/js/models/core/Wallet.js b/js/models/core/Wallet.js index c08687879..85e3ac820 100644 --- a/js/models/core/Wallet.js +++ b/js/models/core/Wallet.js @@ -39,7 +39,7 @@ function Wallet(opts) { }); if (copayConfig.forceNetwork && this.getNetworkName() !== copayConfig.networkName) throw new Error('Network forced to ' + copayConfig.networkName + - ' and tried to create a Wallet with network ' + this.getNetworkName()); + ' and tried to create a Wallet with network ' + this.getNetworkName()); this.log('creating ' + opts.requiredCopayers + ' of ' + opts.totalCopayers + ' wallet'); @@ -321,30 +321,30 @@ Wallet.prototype._handleData = function(senderId, data, isInbound) { // This handler is repeaded on WalletFactory (#join). TODO case 'walletId': this.sendWalletReady(senderId); - break; + break; case 'walletReady': this.sendPublicKeyRing(senderId); - this.sendAddressBook(senderId); - this.sendAllTxProposals(senderId); // send old txps - break; + this.sendAddressBook(senderId); + this.sendAllTxProposals(senderId); // send old txps + break; case 'publicKeyRing': this._handlePublicKeyRing(senderId, data, isInbound); - break; + break; case 'reject': this._handleReject(senderId, data, isInbound); - break; + break; case 'seen': this._handleSeen(senderId, data, isInbound); - break; + break; case 'txProposal': this._handleTxProposal(senderId, data, isInbound); - break; + break; case 'indexes': this._handleIndexes(senderId, data, isInbound); - break; + break; case 'addressbook': this._handleAddressBook(senderId, data, isInbound); - break; + break; } }; @@ -495,11 +495,11 @@ Wallet.prototype.getRegisteredPeerIds = function() { }; Wallet.prototype.keepAlive = function() { - try{ + try { this.lock.keepAlive(); - } catch(e){ + } catch (e) { this.log(e); - this.emit('locked',null,'Wallet appears to be openned on other browser instance. Closing this one.' ); + this.emit('locked', null, 'Wallet appears to be openned on other browser instance. Closing this one.'); } }; @@ -535,9 +535,33 @@ Wallet.fromObj = function(o, storage, network, blockchain) { var opts = JSON.parse(JSON.stringify(o.opts)); opts.addressBook = o.addressBook; - opts.publicKeyRing = PublicKeyRing.fromObj(o.publicKeyRing); - opts.txProposals = TxProposals.fromObj(o.txProposals, Wallet.builderOpts); - opts.privateKey = PrivateKey.fromObj(o.privateKey); + if (o.privateKey) + opts.privateKey = PrivateKey.fromObj(o.privateKey); + else + opts.privateKey = new PrivateKey({ + networkName: this.networkName + }); + + if (o.publicKeyRing) + opts.publicKeyRing = PublicKeyRing.fromObj(o.publicKeyRing); + else { + opts.publicKeyRing = new PublicKeyRing({ + networkName: opts.networkName, + requiredCopayers: opts.requiredCopayers, + totalCopayers: opts.totalCopayers, + }); + opts.publicKeyRing.addCopayer( + opts.privateKey.deriveBIP45Branch().extendedPublicKeyString(), + opts.nickname + ); + } + + if (o.txProposals) + opts.txProposals = TxProposals.fromObj(o.txProposals, Wallet.builderOpts); + else + opts.txProposals = new TxProposals({ + networkName: this.networkName, + }); opts.storage = storage; opts.network = network; @@ -788,22 +812,22 @@ Wallet.prototype.createPaymentTx = function(options, cb) { } return Wallet.request({ - method: 'GET', - url: options.uri, - headers: { - 'Accept': PayPro.PAYMENT_REQUEST_CONTENT_TYPE - }, - responseType: 'arraybuffer' - }) - .success(function(data, status, headers, config) { - data = PayPro.PaymentRequest.decode(data); - var pr = new PayPro(); - pr = pr.makePaymentRequest(data); - return self.receivePaymentRequest(options, pr, cb); - }) - .error(function(data, status, headers, config) { - return cb(new Error('Status: ' + JSON.stringify(status))); - }); + method: 'GET', + url: options.uri, + headers: { + 'Accept': PayPro.PAYMENT_REQUEST_CONTENT_TYPE + }, + responseType: 'arraybuffer' + }) + .success(function(data, status, headers, config) { + data = PayPro.PaymentRequest.decode(data); + var pr = new PayPro(); + pr = pr.makePaymentRequest(data); + return self.receivePaymentRequest(options, pr, cb); + }) + .error(function(data, status, headers, config) { + return cb(new Error('Status: ' + JSON.stringify(status))); + }); }; Wallet.prototype.fetchPaymentTx = function(options, cb) { @@ -991,23 +1015,23 @@ Wallet.prototype.sendPaymentTx = function(ntxid, options, cb) { rpo.set('amount', +total.toString(10)); rpo.set('script', - Buffer.concat([ - new Buffer([ - 118, // OP_DUP - 169, // OP_HASH160 - 76, // OP_PUSHDATA1 - 20, // number of bytes - ]), - // needs to be ripesha'd - bitcore.util.sha256ripe160(options.refund_to), - new Buffer([ - 136, // OP_EQUALVERIFY - 172 // OP_CHECKSIG - ]) - ]) - ); + Buffer.concat([ + new Buffer([ + 118, // OP_DUP + 169, // OP_HASH160 + 76, // OP_PUSHDATA1 + 20, // number of bytes + ]), + // needs to be ripesha'd + bitcore.util.sha256ripe160(options.refund_to), + new Buffer([ + 136, // OP_EQUALVERIFY + 172 // OP_CHECKSIG + ]) + ]) + ); - refund_outputs.push(rpo.message); + refund_outputs.push(rpo.message); } // We send this to the serve after receiving a PaymentRequest @@ -1035,30 +1059,30 @@ Wallet.prototype.sendPaymentTx = function(ntxid, options, cb) { } return Wallet.request({ - method: 'POST', - url: txp.merchant.pr.pd.payment_url, - headers: { - // BIP-71 - 'Accept': PayPro.PAYMENT_ACK_CONTENT_TYPE, - 'Content-Type': PayPro.PAYMENT_CONTENT_TYPE - // XHR does not allow these: - // 'Content-Length': (pay.byteLength || pay.length) + '', - // 'Content-Transfer-Encoding': 'binary' - }, - // Technically how this should be done via XHR (used to - // be the ArrayBuffer, now you send the View instead). - data: view, - responseType: 'arraybuffer' - }) - .success(function(data, status, headers, config) { - data = PayPro.PaymentACK.decode(data); - var ack = new PayPro(); - ack = ack.makePaymentACK(data); - return self.receivePaymentRequestACK(ntxid, tx, txp, ack, cb); - }) - .error(function(data, status, headers, config) { - return cb(new Error('Status: ' + JSON.stringify(status))); - }); + method: 'POST', + url: txp.merchant.pr.pd.payment_url, + headers: { + // BIP-71 + 'Accept': PayPro.PAYMENT_ACK_CONTENT_TYPE, + 'Content-Type': PayPro.PAYMENT_CONTENT_TYPE + // XHR does not allow these: + // 'Content-Length': (pay.byteLength || pay.length) + '', + // 'Content-Transfer-Encoding': 'binary' + }, + // Technically how this should be done via XHR (used to + // be the ArrayBuffer, now you send the View instead). + data: view, + responseType: 'arraybuffer' + }) + .success(function(data, status, headers, config) { + data = PayPro.PaymentACK.decode(data); + var ack = new PayPro(); + ack = ack.makePaymentACK(data); + return self.receivePaymentRequestACK(ntxid, tx, txp, ack, cb); + }) + .error(function(data, status, headers, config) { + return cb(new Error('Status: ' + JSON.stringify(status))); + }); }; Wallet.prototype.receivePaymentRequestACK = function(ntxid, tx, txp, ack, cb) { @@ -1175,8 +1199,8 @@ Wallet.prototype.createPaymentTxSync = function(options, merchantData, unspent) merchantData.total = merchantData.total.toString(10); var b = new Builder(opts) - .setUnspent(unspent) - .setOutputs(outs); + .setUnspent(unspent) + .setOutputs(outs); merchantData.pr.pd.outputs.forEach(function(output, i) { var script = { @@ -1368,7 +1392,7 @@ Wallet.prototype.verifyPaymentRequest = function(ntxid) { // Actual script var as = new Buffer(ro.script.buffer, 'hex') - .slice(ro.script.offset, ro.script.limit); + .slice(ro.script.offset, ro.script.limit); if (av.toString('hex') !== ev.toString('hex') || as.toString('hex') !== es.toString('hex')) { return false; @@ -1548,11 +1572,11 @@ Wallet.prototype.createTxSync = function(toAddress, amountSatStr, comment, utxos } var b = new Builder(opts) - .setUnspent(utxos) - .setOutputs([{ - address: toAddress, - amountSatStr: amountSatStr, - }]); + .setUnspent(utxos) + .setOutputs([{ + address: toAddress, + amountSatStr: amountSatStr, + }]); var selectedUtxos = b.getSelectedUnspent(); var inputChainPaths = selectedUtxos.map(function(utxo) { @@ -1649,29 +1673,29 @@ Wallet.prototype.indexDiscovery = function(start, change, copayerIndex, gap, cb) var self = this; async.doWhilst( function _do(next) { - // Optimize window to minimize the derivations. - var scanWindow = (lastActive == -1) ? gap : gap - (scanIndex - lastActive) + 1; - var addresses = self.deriveAddresses(scanIndex, scanWindow, change, copayerIndex); - self.blockchain.checkActivity(addresses, function(err, actives) { - if (err) throw err; + // Optimize window to minimize the derivations. + var scanWindow = (lastActive == -1) ? gap : gap - (scanIndex - lastActive) + 1; + var addresses = self.deriveAddresses(scanIndex, scanWindow, change, copayerIndex); + self.blockchain.checkActivity(addresses, function(err, actives) { + if (err) throw err; - // Check for new activities in the newlly scanned addresses - var recentActive = actives.reduce(function(r, e, i) { - return e ? scanIndex + i : r; - }, lastActive); - hasActivity = lastActive != recentActive; - lastActive = recentActive; - scanIndex += scanWindow; - next(); - }); - }, - function _while() { - return hasActivity; - }, - function _finnaly(err) { - if (err) return cb(err); - cb(null, lastActive); - } + // Check for new activities in the newlly scanned addresses + var recentActive = actives.reduce(function(r, e, i) { + return e ? scanIndex + i : r; + }, lastActive); + hasActivity = lastActive != recentActive; + lastActive = recentActive; + scanIndex += scanWindow; + next(); + }); + }, + function _while() { + return hasActivity; + }, + function _finnaly(err) { + if (err) return cb(err); + cb(null, lastActive); + } ); } diff --git a/js/models/core/WalletFactory.js b/js/models/core/WalletFactory.js index 3173fcc31..cee830506 100644 --- a/js/models/core/WalletFactory.js +++ b/js/models/core/WalletFactory.js @@ -50,11 +50,19 @@ WalletFactory.prototype._checkRead = function(walletId) { return !!ret; }; -WalletFactory.prototype.fromObj = function(obj) { +WalletFactory.prototype.fromObj = function(obj, skipFields) { // not stored options obj.opts.reconnectDelay = this.walletDefaults.reconnectDelay; + skipFields = skipFields || []; + skipFields.forEach(function(k){ + if (obj[k]) + delete obj[k]; + else + throw new Error('unknown field:' + k); + }); + var w = Wallet.fromObj(obj, this.storage, this.network, this.blockchain); if (!w) return false; w.verbose = this.verbose; @@ -63,23 +71,23 @@ WalletFactory.prototype.fromObj = function(obj) { return w; }; -WalletFactory.prototype.fromEncryptedObj = function(base64, password) { +WalletFactory.prototype.fromEncryptedObj = function(base64, password, skipFields) { this.storage._setPassphrase(password); var walletObj = this.storage.import(base64); if (!walletObj) return false; - var w = this.fromObj(walletObj); + var w = this.fromObj(walletObj, skipFields); return w; }; -WalletFactory.prototype.import = function(base64, password) { +WalletFactory.prototype.import = function(base64, password, skipFields) { var self = this; - var w = self.fromEncryptedObj(base64, password); + var w = self.fromEncryptedObj(base64, password, skipFields); if (!w) throw new Error('Wrong password'); return w; } -WalletFactory.prototype.read = function(walletId) { +WalletFactory.prototype.read = function(walletId, skipFields) { if (!this._checkRead(walletId)) return false; @@ -94,7 +102,7 @@ WalletFactory.prototype.read = function(walletId) { obj.addressBook = s.get(walletId, 'addressBook'); obj.backupOffered = s.get(walletId, 'backupOffered'); - var w = this.fromObj(obj); + var w = this.fromObj(obj, skipFields); return w; }; diff --git a/test/test.WalletFactory.js b/test/test.WalletFactory.js index 7e24aa24d..47a1ab9b8 100644 --- a/test/test.WalletFactory.js +++ b/test/test.WalletFactory.js @@ -175,6 +175,19 @@ describe('WalletFactory model', function() { assertObjectEqual(w.toObj(), JSON.parse(o)); }); + + it('#fromObj, skipping fields', function() { + var wf = new WalletFactory(config, '0.0.5'); + var w = wf.fromObj(JSON.parse(o), ['publicKeyRing']); + + should.exist(w); + w.id.should.equal("dbfe10c3fae71cea"); + should.exist(w.publicKeyRing.getCopayerId); + should.exist(w.txProposals.toObj()); + should.exist(w.privateKey.toObj()); + (function() { assertObjectEqual(w.toObj(), JSON.parse(o))}).should.throw(); + }); + it('support old index schema: #fromObj #toObj round trip', function() { var o = '{"opts":{"id":"dbfe10c3fae71cea","spendUnconfirmed":1,"requiredCopayers":3,"totalCopayers":5,"version":"0.0.5"},"networkNonce":"0000000000000001","networkNonces":[],"publicKeyRing":{"walletId":"dbfe10c3fae71cea","networkName":"testnet","requiredCopayers":3,"totalCopayers":5,"indexes":{"changeIndex":0,"receiveIndex":0},"copayersBackup":[],"copayersExtPubKeys":["tpubD6NzVbkrYhZ4YGK8ZhZ8WVeBXNAAoTYjjpw9twCPiNGrGQYFktP3iVQkKmZNiFnUcAFMJRxJVJF6Nq9MDv2kiRceExJaHFbxUCGUiRhmy97","tpubD6NzVbkrYhZ4YKGDJkzWdQsQV3AcFemaQKiwNhV4RL8FHnBFvinidGdQtP8RKj3h34E65RkdtxjrggZYqsEwJ8RhhN2zz9VrjLnrnwbXYNc","tpubD6NzVbkrYhZ4YkDiewjb32Pp3Sz9WK2jpp37KnL7RCrHAyPpnLfgdfRnTdpn6DTWmPS7niywfgWiT42aJb1J6CjWVNmkgsMCxuw7j9DaGKB","tpubD6NzVbkrYhZ4XEtUAz4UUTWbprewbLTaMhR8NUvSJUEAh4Sidxr6rRPFdqqVRR73btKf13wUjds2i8vVCNo8sbKrAnyoTr3o5Y6QSbboQjk","tpubD6NzVbkrYhZ4Yj9AAt6xUVuGPVd8jXCrEE6V2wp7U3PFh8jYYvVad31b4VUXEYXzSnkco4fktu8r4icBsB2t3pCR3WnhVLedY2hxGcPFLKD"],"nicknameFor":{}},"txProposals":{"txps":[],"walletId":"dbfe10c3fae71cea","networkName":"testnet"},"privateKey":{"extendedPrivateKeyString":"tprv8ZgxMBicQKsPeoHLg3tY75z4xLeEe8MqAXLNcRA6J6UTRvHV8VZTXznt9eoTmSk1fwSrwZtMhY3XkNsceJ14h6sCXHSWinRqMSSbY8tfhHi","networkName":"testnet"},"addressBook":{}}'; var o2 = '{"opts":{"id":"dbfe10c3fae71cea","spendUnconfirmed":1,"requiredCopayers":3,"totalCopayers":5,"version":"0.0.5"},"networkNonce":"0000000000000001","networkNonces":[],"publicKeyRing":{"walletId":"dbfe10c3fae71cea","networkName":"testnet","requiredCopayers":3,"totalCopayers":5,"indexes":[{"copayerIndex":2147483647,"changeIndex":0,"receiveIndex":0},{"copayerIndex":0,"changeIndex":0,"receiveIndex":0},{"copayerIndex":1,"changeIndex":0,"receiveIndex":0},{"copayerIndex":2,"changeIndex":0,"receiveIndex":0},{"copayerIndex":3,"changeIndex":0,"receiveIndex":0},{"copayerIndex":4,"changeIndex":0,"receiveIndex":0}],"copayersBackup":[],"copayersExtPubKeys":["tpubD6NzVbkrYhZ4YGK8ZhZ8WVeBXNAAoTYjjpw9twCPiNGrGQYFktP3iVQkKmZNiFnUcAFMJRxJVJF6Nq9MDv2kiRceExJaHFbxUCGUiRhmy97","tpubD6NzVbkrYhZ4YKGDJkzWdQsQV3AcFemaQKiwNhV4RL8FHnBFvinidGdQtP8RKj3h34E65RkdtxjrggZYqsEwJ8RhhN2zz9VrjLnrnwbXYNc","tpubD6NzVbkrYhZ4YkDiewjb32Pp3Sz9WK2jpp37KnL7RCrHAyPpnLfgdfRnTdpn6DTWmPS7niywfgWiT42aJb1J6CjWVNmkgsMCxuw7j9DaGKB","tpubD6NzVbkrYhZ4XEtUAz4UUTWbprewbLTaMhR8NUvSJUEAh4Sidxr6rRPFdqqVRR73btKf13wUjds2i8vVCNo8sbKrAnyoTr3o5Y6QSbboQjk","tpubD6NzVbkrYhZ4Yj9AAt6xUVuGPVd8jXCrEE6V2wp7U3PFh8jYYvVad31b4VUXEYXzSnkco4fktu8r4icBsB2t3pCR3WnhVLedY2hxGcPFLKD"],"nicknameFor":{}},"txProposals":{"txps":[],"walletId":"dbfe10c3fae71cea","networkName":"testnet"},"privateKey":{"extendedPrivateKeyString":"tprv8ZgxMBicQKsPeoHLg3tY75z4xLeEe8MqAXLNcRA6J6UTRvHV8VZTXznt9eoTmSk1fwSrwZtMhY3XkNsceJ14h6sCXHSWinRqMSSbY8tfhHi","networkName":"testnet"},"addressBook":{}}'; diff --git a/views/import.html b/views/import.html index 627e1b92c..da2dabc00 100644 --- a/views/import.html +++ b/views/import.html @@ -3,12 +3,14 @@ {{ importStatus }} +
Copay
+

{{title}}

@@ -20,6 +22,12 @@ +
+ + Skipping fields: {{skipFields}} +
+ +
« Back From ca23aed70f2756a4fbf341fbb5adf548f5eab977 Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Wed, 20 Aug 2014 20:16:20 -0400 Subject: [PATCH 05/22] add test for priv key import --- test/test.WalletFactory.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/test/test.WalletFactory.js b/test/test.WalletFactory.js index 47a1ab9b8..419d2ffcc 100644 --- a/test/test.WalletFactory.js +++ b/test/test.WalletFactory.js @@ -404,9 +404,17 @@ describe('WalletFactory model', function() { var wf = new WalletFactory(config, '0.0.1'); wf.network.cleanUp = sinon.spy(); wf.network.start = sinon.spy(); - wf.joinCreateSession('8WtTuiFTkhP5ao7AF2QErSwV39Cbur6pdMebKzQXFqL59RscXM', 'test', null, undefined); + wf.joinCreateSession('8WtTuiFTkhP5ao7AF2QErSwV39Cbur6pdMebKzQXFqL59RscXM', 'test'); wf.network.start.calledOnce.should.equal(true); }); + it('should accept a priv key a input', function() { + var wf = new WalletFactory(config, '0.0.1'); + var privHex = 'tprv8ZgxMBicQKsPf7MCvCjnhnr4uiR2Z2gyNC27vgd9KUu98F9mM1tbaRrWMyddVju36GxLbeyntuSadBAttriwGGMWUkRgVmUUCg5nFioGZsd'; + wf.network.cleanUp = sinon.spy(); + wf.network.start = sinon.spy(); + wf.joinCreateSession('8WtTuiFTkhP5ao7AF2QErSwV39Cbur6pdMebKzQXFqL59RscXM', 'test', null, privHex); + wf.network.start.getCall(0).args[0].privkey.should.equal('ddc2fa8c583a73c4b2a24630ec7c283df4e7c230a02c4e48bc36ec61687afd7d'); + }); it('should call network.start with private key', function() { var wf = new WalletFactory(config, '0.0.1'); wf.network.cleanUp = sinon.spy(); From 4b3cbfcfb0eff98bbc7952e039740bc2cbe003c1 Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Wed, 20 Aug 2014 20:29:07 -0400 Subject: [PATCH 06/22] add test for priv key import --- js/controllers/backup.js | 2 +- js/controllers/join.js | 2 +- js/models/core/WalletFactory.js | 2 +- views/join.html | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/js/controllers/backup.js b/js/controllers/backup.js index 9b0e23656..4c9311e02 100644 --- a/js/controllers/backup.js +++ b/js/controllers/backup.js @@ -2,7 +2,7 @@ angular.module('copayApp.controllers').controller('BackupController', function($scope, $rootScope, $location, backupService, walletFactory, controllerUtils) { - var s = ($location.search()).showPrivate; + var s = ($location.search()).advanced; if (s) { var w = $rootScope.wallet; $scope.priv = w.privateKey.toObj().extendedPrivateKeyString; diff --git a/js/controllers/join.js b/js/controllers/join.js index 5c4f68986..4bea5625f 100644 --- a/js/controllers/join.js +++ b/js/controllers/join.js @@ -15,7 +15,7 @@ angular.module('copayApp.controllers').controller('JoinController', var context; var localMediaStream; - var s = ($location.search()).enterPrivate; + var s = ($location.search()).advanced; if (s) { $scope.enterPrivate = true; } diff --git a/js/models/core/WalletFactory.js b/js/models/core/WalletFactory.js index 146f21e9a..846666744 100644 --- a/js/models/core/WalletFactory.js +++ b/js/models/core/WalletFactory.js @@ -224,7 +224,7 @@ WalletFactory.prototype.joinCreateSession = function(secret, nickname, passphras networkName: this.networkName, }; - if (privateHex && privateHex.length>10) { + if (privateHex && privateHex.length>1) { privOpts.extendedPrivateKeyString = privateHex; } diff --git a/views/join.html b/views/join.html index 36a2e7dbc..20cbad630 100644 --- a/views/join.html +++ b/views/join.html @@ -69,9 +69,9 @@ match="joinPassword" required>
From e760dae2bd0c1999d20304ca1a26fca1c4c45c1d Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Wed, 20 Aug 2014 20:52:31 -0400 Subject: [PATCH 07/22] add scaning options too more --- js/controllers/backup.js | 23 ++++++++++++++++++++--- views/backup.html | 25 ++++++++++++++++++++++--- 2 files changed, 42 insertions(+), 6 deletions(-) diff --git a/js/controllers/backup.js b/js/controllers/backup.js index 4c9311e02..359354691 100644 --- a/js/controllers/backup.js +++ b/js/controllers/backup.js @@ -1,14 +1,15 @@ 'use strict'; angular.module('copayApp.controllers').controller('BackupController', - function($scope, $rootScope, $location, backupService, walletFactory, controllerUtils) { + function($scope, $rootScope, $location, backupService, walletFactory, controllerUtils, notification) { var s = ($location.search()).advanced; if (s) { var w = $rootScope.wallet; - $scope.priv = w.privateKey.toObj().extendedPrivateKeyString; + $scope.advanced = 1; + $scope.priv = w.privateKey.toObj().extendedPrivateKeyString; } - + $scope.downloadBackup = function() { var w = $rootScope.wallet; backupService.download(w); @@ -21,4 +22,20 @@ angular.module('copayApp.controllers').controller('BackupController', controllerUtils.logout(); }); }; + + $scope.updateIndexes = function() { + var w = $rootScope.wallet; + + notification.info('Scaning for transactions','Using derived addresses from your wallet'); + w.updateIndexes(function(err) { + notification.info('Scan Ended', 'Updating balance'); + if (err) { + notification.error('Error', 'Error updating indexes: ' + err); + } + controllerUtils.updateAddressList(); + controllerUtils.updateBalance(function(){ + notification.info('Finished', 'The balance is updated using the derived addresses'); + }); + }); + }; }); diff --git a/views/backup.html b/views/backup.html index d747bbb9e..25d6ae28a 100644 --- a/views/backup.html +++ b/views/backup.html @@ -18,9 +18,28 @@
-

Master Private Key

-

- {{priv}} +

Master Private Key

+

Your master private key contains the information to sign any transaction on this wallet. Handle with care. +

+ +

+
+

Scan Wallet Addresses

+

This will scan the blockchain looking for addresses derived from your wallet, in case you have funds in addresses not yet generated (e.g.: you restored an old backup). +

+
+
From c1564ae015ff9eba1b12df3be975f8a04515a7d9 Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Wed, 20 Aug 2014 21:05:44 -0400 Subject: [PATCH 08/22] scan is working! --- js/controllers/join.js | 2 +- js/models/core/WalletFactory.js | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/js/controllers/join.js b/js/controllers/join.js index 4bea5625f..d926fb47b 100644 --- a/js/controllers/join.js +++ b/js/controllers/join.js @@ -119,7 +119,7 @@ angular.module('copayApp.controllers').controller('JoinController', walletFactory.network.on('badSecret', function() {}); Passphrase.getBase64Async($scope.joinPassword, function(passphrase) { - walletFactory.joinCreateSession($scope.connectionId, $scope.nickname, passphrase, $scope.enterPrivate ? $scope.private : 'null', function(err, w) { + walletFactory.joinCreateSession($scope.connectionId, $scope.nickname, passphrase, $scope.enterPrivate ? $scope.private : null, function(err, w) { $scope.loading = false; if (err || !w) { if (err === 'joinError') diff --git a/js/models/core/WalletFactory.js b/js/models/core/WalletFactory.js index 846666744..70d28a1ba 100644 --- a/js/models/core/WalletFactory.js +++ b/js/models/core/WalletFactory.js @@ -216,6 +216,7 @@ WalletFactory.prototype.decodeSecret = function(secret) { WalletFactory.prototype.joinCreateSession = function(secret, nickname, passphrase, privateHex, cb) { +console.log('[WalletFactory.js.218:privateHex:]',privateHex); //TODO var self = this; var s = self.decodeSecret(secret); if (!s) return cb('badSecret'); @@ -228,6 +229,7 @@ WalletFactory.prototype.joinCreateSession = function(secret, nickname, passphras privOpts.extendedPrivateKeyString = privateHex; } +console.log('[WalletFactory.js.229:privOpts:]',privOpts); //TODO //Create our PrivateK var privateKey = new PrivateKey(privOpts); this.log('\t### PrivateKey Initialized'); From 93239f7983b6c757805e28f873ca2833ee0125b7 Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Wed, 20 Aug 2014 21:29:01 -0400 Subject: [PATCH 09/22] add send Indexes --- js/controllers/backup.js | 1 + 1 file changed, 1 insertion(+) diff --git a/js/controllers/backup.js b/js/controllers/backup.js index 359354691..d884a3477 100644 --- a/js/controllers/backup.js +++ b/js/controllers/backup.js @@ -35,6 +35,7 @@ angular.module('copayApp.controllers').controller('BackupController', controllerUtils.updateAddressList(); controllerUtils.updateBalance(function(){ notification.info('Finished', 'The balance is updated using the derived addresses'); + w.sendIndexes(); }); }); }; From b388c3eadf9927a681cea6f8e3c7e17f40e118a0 Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Thu, 21 Aug 2014 11:02:07 -0400 Subject: [PATCH 10/22] add UX to backup tab --- js/controllers/backup.js | 13 +++++----- views/backup.html | 51 +++++++++++++++++++++++----------------- 2 files changed, 36 insertions(+), 28 deletions(-) diff --git a/js/controllers/backup.js b/js/controllers/backup.js index d884a3477..356306918 100644 --- a/js/controllers/backup.js +++ b/js/controllers/backup.js @@ -2,13 +2,14 @@ angular.module('copayApp.controllers').controller('BackupController', function($scope, $rootScope, $location, backupService, walletFactory, controllerUtils, notification) { - var s = ($location.search()).advanced; - if (s) { - var w = $rootScope.wallet; - $scope.advanced = 1; - $scope.priv = w.privateKey.toObj().extendedPrivateKeyString; - } + $scope.hideAdv=true; + $scope.hidePriv=true; + + $scope.getPrivate = function() { + var w = $rootScope.wallet; + return w.privateKey.toObj().extendedPrivateKeyString; + } $scope.downloadBackup = function() { var w = $rootScope.wallet; diff --git a/views/backup.html b/views/backup.html index 25d6ae28a..417f04120 100644 --- a/views/backup.html +++ b/views/backup.html @@ -17,28 +17,35 @@
-
-

Master Private Key

-

Your master private key contains the information to sign any transaction on this wallet. Handle with care. -

- -

-
-
-

Scan Wallet Addresses

-

This will scan the blockchain looking for addresses derived from your wallet, in case you have funds in addresses not yet generated (e.g.: you restored an old backup). -

+

+ + Show + Hide + advanced options + + + +

+
+

Master Private Key

+

Your master private key contains the information to sign any transaction on this wallet. Handle with care. +

+ +
+
+

Scan Wallet Addresses

+

This will scan the blockchain looking for addresses derived from your wallet, in case you have funds in addresses not yet generated (e.g.: you restored an old backup). +

+
From 2617b76428a784b52930449bc4c530f9f0b305e6 Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Thu, 21 Aug 2014 11:04:19 -0400 Subject: [PATCH 11/22] mv backup to more filenames --- js/controllers/{backup.js => more.js} | 2 +- test/unit/controllers/controllersSpec.js | 2 +- views/{backup.html => more.html} | 0 3 files changed, 2 insertions(+), 2 deletions(-) rename js/controllers/{backup.js => more.js} (94%) rename views/{backup.html => more.html} (100%) diff --git a/js/controllers/backup.js b/js/controllers/more.js similarity index 94% rename from js/controllers/backup.js rename to js/controllers/more.js index 356306918..96e3e8632 100644 --- a/js/controllers/backup.js +++ b/js/controllers/more.js @@ -1,6 +1,6 @@ 'use strict'; -angular.module('copayApp.controllers').controller('BackupController', +angular.module('copayApp.controllers').controller('MoreController', function($scope, $rootScope, $location, backupService, walletFactory, controllerUtils, notification) { $scope.hideAdv=true; diff --git a/test/unit/controllers/controllersSpec.js b/test/unit/controllers/controllersSpec.js index 5cee6da41..2efd62a46 100644 --- a/test/unit/controllers/controllersSpec.js +++ b/test/unit/controllers/controllersSpec.js @@ -38,7 +38,7 @@ describe("Unit: Controllers", function() { scope = $rootScope.$new(); $rootScope.wallet = new FakeWallet(config); - ctrl = $controller('BackupController', { + ctrl = $controller('MoreController', { $scope: scope, $modal: {}, }); diff --git a/views/backup.html b/views/more.html similarity index 100% rename from views/backup.html rename to views/more.html From 96b841bd510e004fa5df19abb2add832fc91573b Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Thu, 21 Aug 2014 11:08:20 -0400 Subject: [PATCH 12/22] update sidebar --- js/controllers/sidebar.js | 2 +- js/routes.js | 4 ++-- views/more.html | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/js/controllers/sidebar.js b/js/controllers/sidebar.js index 26c3f60f8..e93bf27e2 100644 --- a/js/controllers/sidebar.js +++ b/js/controllers/sidebar.js @@ -17,7 +17,7 @@ angular.module('copayApp.controllers').controller('SidebarController', function( }, { 'title': 'More', 'icon': 'fi-download', - 'link': 'backup' + 'link': 'more' }]; $scope.signout = function() { diff --git a/js/routes.js b/js/routes.js index c40a6179c..426862a4e 100644 --- a/js/routes.js +++ b/js/routes.js @@ -42,8 +42,8 @@ angular templateUrl: 'views/send.html', validate: true }) - .when('/backup', { - templateUrl: 'views/backup.html', + .when('/more', { + templateUrl: 'views/more.html', validate: true }) .when('/settings', { diff --git a/views/more.html b/views/more.html index 417f04120..4b376c38c 100644 --- a/views/more.html +++ b/views/more.html @@ -1,4 +1,4 @@ -
+

Backup & Delete

Backup

From db1b7d7c453159668d678ac0a3c059dcaf0d26e3 Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Thu, 21 Aug 2014 11:50:40 -0400 Subject: [PATCH 13/22] add advanced options to import --- js/controllers/import.js | 18 +++++++++++++----- views/import.html | 31 +++++++++++++++++++++++++++---- views/more.html | 2 +- 3 files changed, 41 insertions(+), 10 deletions(-) diff --git a/js/controllers/import.js b/js/controllers/import.js index c64bea1c6..82ee02fe9 100644 --- a/js/controllers/import.js +++ b/js/controllers/import.js @@ -7,10 +7,7 @@ angular.module('copayApp.controllers').controller('ImportController', $scope.title = 'Import a backup'; $scope.importStatus = 'Importing wallet - Reading backup...'; - var s = ($location.search()).skip; - if (s) { - $scope.skipFields = s.split(','); - } + $scope.hideAdv=true; var reader = new FileReader(); @@ -24,9 +21,20 @@ angular.module('copayApp.controllers').controller('ImportController', updateStatus('Importing wallet - Setting things up...'); var w, errMsg; + var skipFields = []; + if ($scope.skipPublicKeyRing) + skipFields.push('publicKeyRing'); + + if ($scope.skipTxProposals) + skipFields.push('txProposals'); + + if ($scope.skipPrivateKey) + skipFields.push('privateKey'); + +console.log('[import.js.36:skipFields:]',skipFields); //TODO // try to import encrypted wallet with passphrase try { - w = walletFactory.import(encryptedObj, passphrase, $scope.skipFields); + w = walletFactory.import(encryptedObj, passphrase, skipFields); } catch (e) { errMsg = e.message; } diff --git a/views/import.html b/views/import.html index da2dabc00..58984695e 100644 --- a/views/import.html +++ b/views/import.html @@ -17,11 +17,34 @@
Choose backup file from your computer + + + + + + + Show + Hide + advanced options + + + +
+ + + + + +
+ +
- - - -
Skipping fields: {{skipFields}} diff --git a/views/more.html b/views/more.html index 4b376c38c..930232624 100644 --- a/views/more.html +++ b/views/more.html @@ -18,7 +18,7 @@

- + Show Hide advanced options From 60d81e6b9fdfe882b70d05ec06077a908c64e9a8 Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Thu, 21 Aug 2014 11:56:02 -0400 Subject: [PATCH 14/22] add advanced options to join --- js/controllers/join.js | 7 ++----- views/join.html | 23 +++++++++++++++++------ 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/js/controllers/join.js b/js/controllers/join.js index d926fb47b..beae1eb14 100644 --- a/js/controllers/join.js +++ b/js/controllers/join.js @@ -1,7 +1,7 @@ 'use strict'; angular.module('copayApp.controllers').controller('JoinController', - function($scope, $rootScope, $timeout, $location, walletFactory, controllerUtils, Passphrase, notification) { + function($scope, $rootScope, $timeout, walletFactory, controllerUtils, Passphrase, notification) { controllerUtils.redirIfLogged(); $rootScope.fromSetup = false; $scope.loading = false; @@ -15,10 +15,7 @@ angular.module('copayApp.controllers').controller('JoinController', var context; var localMediaStream; - var s = ($location.search()).advanced; - if (s) { - $scope.enterPrivate = true; - } + $scope.hideAdv=true; diff --git a/views/join.html b/views/join.html index 20cbad630..b9a987e2a 100644 --- a/views/join.html +++ b/views/join.html @@ -67,17 +67,28 @@ name="joinPasswordConfirm" ng-model="joinPasswordConfirm" match="joinPassword" required> - - + + Show + Hide + advanced options + +

+

+ +

« Back
+ + + +
From 4931c9d618cab940723184c5fb8cf998a4d14a90 Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Thu, 21 Aug 2014 13:12:55 -0400 Subject: [PATCH 15/22] add purge to tx prosposals --- js/controllers/more.js | 6 ++++++ js/models/core/TxProposal.js | 11 +++++++++++ js/models/core/TxProposals.js | 18 +++++++++++++++++- js/models/core/Wallet.js | 26 ++++++++++++++++++++++++-- test/test.Wallet.js | 35 +++++++++++++++++++++++++++++++++++ views/more.html | 24 +++++++++++++++++++++--- 6 files changed, 114 insertions(+), 6 deletions(-) diff --git a/js/controllers/more.js b/js/controllers/more.js index 96e3e8632..a6ece95e2 100644 --- a/js/controllers/more.js +++ b/js/controllers/more.js @@ -24,6 +24,12 @@ angular.module('copayApp.controllers').controller('MoreController', }); }; + $scope.purge = function(deleteAll) { + var w = $rootScope.wallet; + var removed = w.purgeTxProposals(deleteAll); + notification.info('Tx Proposals Purged', removed + ' transactions proposals were purged'); + }; + $scope.updateIndexes = function() { var w = $rootScope.wallet; diff --git a/js/models/core/TxProposal.js b/js/models/core/TxProposal.js index fb258af51..0349a654a 100644 --- a/js/models/core/TxProposal.js +++ b/js/models/core/TxProposal.js @@ -65,6 +65,17 @@ TxProposal.prototype._check = function() { } }; +TxProposal.prototype.rejectCount = function() { + return Object.keys(this.rejectedBy); +}; + +TxProposal.prototype.isPending = function(maxRejectCount) { + if (this.rejectCount() < maxRejectCount || p.sentTxid) + return false; + + return true; +}; + TxProposal.prototype._updateSignedBy = function() { this._inputSignatures = []; diff --git a/js/models/core/TxProposals.js b/js/models/core/TxProposals.js index 558d9c501..e5b48cf5b 100644 --- a/js/models/core/TxProposals.js +++ b/js/models/core/TxProposals.js @@ -36,10 +36,25 @@ TxProposals.fromObj = function(o, forceOpts) { return ret; }; +TxProposals.prototype.length = function() { + return Object.keys(this.txps).length; +}; + TxProposals.prototype.getNtxids = function() { return Object.keys(this.txps); }; +TxProposals.prototype.deleteAll = function() { + this.txps = {}; +}; + +TxProposals.prototype.deletePending = function(maxRejectCount) { + for (var ntxid in this.txps) { + if (this.txps[ntxid].isPending(maxRejectCount)) + delete this.txps[ntxid]; + }; +}; + TxProposals.prototype.toObj = function() { var ret = []; for (var id in this.txps) { @@ -153,7 +168,8 @@ TxProposals.prototype.getUsedUnspent = function(maxRejectCount) { for (var i in this.txps) { var u = this.txps[i].builder.getSelectedUnspent(); var p = this.getTxProposal(i); - if (p.rejectCount > maxRejectCount || p.sentTxid) + + if (!p.isPending(maxRejectCount)) continue; for (var j in u) { diff --git a/js/models/core/Wallet.js b/js/models/core/Wallet.js index 2d1786d92..d53334927 100644 --- a/js/models/core/Wallet.js +++ b/js/models/core/Wallet.js @@ -706,6 +706,18 @@ Wallet.prototype.getTxProposals = function() { return ret; }; +Wallet.prototype.purgeTxProposals = function(deleteAll) { + var m = this.txProposals.length(); + + if (deleteAll) { + this.txProposals.deleteAll(); + } else { + this.txProposals.deletePending(this.maxRejectCount()); + } + + var n = this.txProposals.length(); + return m-n; +}; Wallet.prototype.reject = function(ntxid) { var txp = this.txProposals.reject(ntxid, this.getMyCopayerId()); @@ -1484,6 +1496,17 @@ Wallet.prototype.getBalance = function(cb) { }); }; + +// See +// https://github.com/bitpay/copay/issues/1056 +// +// maxRejectCount should equal requiredCopayers +// strictly. +// +Wallet.prototype.maxRejectCount = function(cb) { + return this.totalCopayers - this.requiredCopayers; +}; + Wallet.prototype.getUnspent = function(cb) { var self = this; this.blockchain.getUnspent(this.getAddressesStr(), function(err, unspentList) { @@ -1493,8 +1516,7 @@ Wallet.prototype.getUnspent = function(cb) { } var safeUnspendList = []; - var maxRejectCount = self.totalCopayers - self.requiredCopayers; - var uu = self.txProposals.getUsedUnspent(maxRejectCount); + var uu = self.txProposals.getUsedUnspent(self.maxRejectCount()); for (var i in unspentList) { var u = unspentList[i]; diff --git a/test/test.Wallet.js b/test/test.Wallet.js index 59ac675ca..320c90284 100644 --- a/test/test.Wallet.js +++ b/test/test.Wallet.js @@ -357,6 +357,39 @@ describe('Wallet model', function() { throw(); }); + + it.only('#maxRejectCount', function() { + var w = cachedCreateW(); + w.maxRejectCount().should.equal(2); + }); + + + describe('#purgeTxProposals', function() { + it('should delete all', function() { + var w = cachedCreateW(); + var spy1 = sinon.spy(w.txProposals, 'deleteAll'); + var spy2 = sinon.spy(w.txProposals, 'deletePending'); + w.purgeTxProposals(1); + spy1.callCount.should.equal(1); + spy2.callCount.should.equal(0); + }); + it('should delete pending', function() { + var w = cachedCreateW(); + var spy1 = sinon.spy(w.txProposals, 'deleteAll'); + var spy2 = sinon.spy(w.txProposals, 'deletePending'); + w.purgeTxProposals(); + spy1.callCount.should.equal(0); + spy2.callCount.should.equal(1); + }); + it('should count deletions', function() { + var w = cachedCreateW(); + var s = sinon.stub(w.txProposals, 'length').returns(10); + var n = w.purgeTxProposals(); + n.should.equal(0); + }); + }); + + //this test fails randomly it.skip('call reconnect after interval', function(done) { this.timeout(10000); @@ -378,6 +411,8 @@ describe('Wallet model', function() { w.isShared().should.equal(false); }); + + it('#isReady', function() { var w = createW(); w.publicKeyRing.isComplete().should.equal(false); diff --git a/views/more.html b/views/more.html index 930232624..f7df99524 100644 --- a/views/more.html +++ b/views/more.html @@ -30,7 +30,7 @@

Master Private Key

Your master private key contains the information to sign any transaction on this wallet. Handle with care.

- + Show Hide @@ -39,13 +39,31 @@

Scan Wallet Addresses

-

This will scan the blockchain looking for addresses derived from your wallet, in case you have funds in addresses not yet generated (e.g.: you restored an old backup). +

This will scan the blockchain looking for addresses derived from your wallet, in case you have funds in addresses not yet generated (e.g.: you restored an old backup). This will also trigger a syncronization of addresses to other connected peers.

+
+

Purge Pending Transaction Proposals

+

Pending Transactions Proposals will be discarted. This need to be done on ALL peers of a wallet, to prevent the old proposals to be resynced again. +

+
+
+

Purge ALL Transaction Proposals

+

ALL Transactions Proposals will be discarted. This need to be done on ALL peers of a wallet, to prevent the old proposals to be resynced again. +

+
From e11ac3e9569ed268cf5d83108e66ffb523c6cf32 Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Thu, 21 Aug 2014 13:54:38 -0400 Subject: [PATCH 16/22] add tests --- js/models/core/TxProposal.js | 6 ++++-- test/test.TxProposal.js | 30 ++++++++++++++++++++++++++++++ test/test.Wallet.js | 7 ++++++- 3 files changed, 40 insertions(+), 3 deletions(-) diff --git a/js/models/core/TxProposal.js b/js/models/core/TxProposal.js index 0349a654a..32aa0f38f 100644 --- a/js/models/core/TxProposal.js +++ b/js/models/core/TxProposal.js @@ -66,11 +66,13 @@ TxProposal.prototype._check = function() { }; TxProposal.prototype.rejectCount = function() { - return Object.keys(this.rejectedBy); + return Object.keys(this.rejectedBy).length; }; TxProposal.prototype.isPending = function(maxRejectCount) { - if (this.rejectCount() < maxRejectCount || p.sentTxid) + preconditions.checkArgument(typeof maxRejectCount != 'undefined'); + + if (this.rejectCount() > maxRejectCount || this.sentTxid) return false; return true; diff --git a/test/test.TxProposal.js b/test/test.TxProposal.js index 1ac3a1981..a8535dd25 100644 --- a/test/test.TxProposal.js +++ b/test/test.TxProposal.js @@ -139,6 +139,7 @@ describe('TxProposal', function() { }); + describe('#setSent', function() { it('should set txid and timestamp', function() { var now = Date.now(); @@ -430,4 +431,33 @@ describe('TxProposal', function() { }); }); + + describe('micelaneous functions', function() { + it('should report rejectCount', function() { + var txp = dummyProposal; + txp.rejectCount().should.equal(0); + txp.setRejected(['juan']) + txp.rejectCount().should.equal(1); + }); + it('should report isPending 1', function() { + var txp = dummyProposal; + txp.rejectedBy=[]; + txp.sentTxid=1; + txp.isPending(3).should.equal(false); + }); + it('should report isPending 2', function() { + var txp = dummyProposal; + txp.rejectedBy=[]; + txp.sentTxid=null; + txp.isPending(3).should.equal(true); + }); + it('should report isPending 3', function() { + var txp = dummyProposal; + txp.rejectedBy=[1,2,3,4]; + txp.sentTxid=null; + txp.isPending(3).should.equal(false); + }); + }); + + }); diff --git a/test/test.Wallet.js b/test/test.Wallet.js index 320c90284..b1320d6a0 100644 --- a/test/test.Wallet.js +++ b/test/test.Wallet.js @@ -358,7 +358,7 @@ describe('Wallet model', function() { }); - it.only('#maxRejectCount', function() { + it('#maxRejectCount', function() { var w = cachedCreateW(); w.maxRejectCount().should.equal(2); }); @@ -372,6 +372,8 @@ describe('Wallet model', function() { w.purgeTxProposals(1); spy1.callCount.should.equal(1); spy2.callCount.should.equal(0); + spy1.restore(); + spy2.restore(); }); it('should delete pending', function() { var w = cachedCreateW(); @@ -380,12 +382,15 @@ describe('Wallet model', function() { w.purgeTxProposals(); spy1.callCount.should.equal(0); spy2.callCount.should.equal(1); + spy1.restore(); + spy2.restore(); }); it('should count deletions', function() { var w = cachedCreateW(); var s = sinon.stub(w.txProposals, 'length').returns(10); var n = w.purgeTxProposals(); n.should.equal(0); + s.restore(); }); }); From c616f0216917fef6db673f41172bf19675b653da Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Thu, 21 Aug 2014 14:19:08 -0400 Subject: [PATCH 17/22] fix isPending usage --- js/models/core/TxProposals.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/js/models/core/TxProposals.js b/js/models/core/TxProposals.js index e5b48cf5b..11d9ad3b8 100644 --- a/js/models/core/TxProposals.js +++ b/js/models/core/TxProposals.js @@ -166,12 +166,12 @@ TxProposals.prototype.seen = function(ntxid, copayerId) { TxProposals.prototype.getUsedUnspent = function(maxRejectCount) { var ret = {}; for (var i in this.txps) { + if (!this.txps[i].isPending(maxRejectCount)) + continue; + var u = this.txps[i].builder.getSelectedUnspent(); var p = this.getTxProposal(i); - if (!p.isPending(maxRejectCount)) - continue; - for (var j in u) { ret[u[j].txid + ',' + u[j].vout] = 1; } From dd724a613550dfd663f45bb5dc4c8344adf67030 Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Thu, 21 Aug 2014 14:28:02 -0400 Subject: [PATCH 18/22] add store / refrest --- js/controllers/more.js | 6 ++++-- js/models/core/Wallet.js | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/js/controllers/more.js b/js/controllers/more.js index a6ece95e2..130533657 100644 --- a/js/controllers/more.js +++ b/js/controllers/more.js @@ -27,12 +27,14 @@ angular.module('copayApp.controllers').controller('MoreController', $scope.purge = function(deleteAll) { var w = $rootScope.wallet; var removed = w.purgeTxProposals(deleteAll); - notification.info('Tx Proposals Purged', removed + ' transactions proposals were purged'); + if (removed){ + controllerUtils.updateBalance(); + } + notification.info('Tx Proposals Purged', removed + ' transaction proposal purged'); }; $scope.updateIndexes = function() { var w = $rootScope.wallet; - notification.info('Scaning for transactions','Using derived addresses from your wallet'); w.updateIndexes(function(err) { notification.info('Scan Ended', 'Updating balance'); diff --git a/js/models/core/Wallet.js b/js/models/core/Wallet.js index d53334927..e3ca1d872 100644 --- a/js/models/core/Wallet.js +++ b/js/models/core/Wallet.js @@ -714,6 +714,7 @@ Wallet.prototype.purgeTxProposals = function(deleteAll) { } else { this.txProposals.deletePending(this.maxRejectCount()); } + this.store(); var n = this.txProposals.length(); return m-n; From 291ed73916ceb603de0bd7a42f6aeccf6c477ec4 Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Thu, 21 Aug 2014 14:54:36 -0400 Subject: [PATCH 19/22] add test and adv opts to create --- js/controllers/join.js | 2 +- js/controllers/setup.js | 2 ++ js/models/core/WalletFactory.js | 14 +++++++++----- test/test.WalletFactory.js | 21 +++++++++++++++++++++ views/setup.html | 17 ++++++++++++++++- 5 files changed, 49 insertions(+), 7 deletions(-) diff --git a/js/controllers/join.js b/js/controllers/join.js index beae1eb14..3cf0033ce 100644 --- a/js/controllers/join.js +++ b/js/controllers/join.js @@ -116,7 +116,7 @@ angular.module('copayApp.controllers').controller('JoinController', walletFactory.network.on('badSecret', function() {}); Passphrase.getBase64Async($scope.joinPassword, function(passphrase) { - walletFactory.joinCreateSession($scope.connectionId, $scope.nickname, passphrase, $scope.enterPrivate ? $scope.private : null, function(err, w) { + walletFactory.joinCreateSession($scope.connectionId, $scope.nickname, passphrase, $scope.private, function(err, w) { $scope.loading = false; if (err || !w) { if (err === 'joinError') diff --git a/js/controllers/setup.js b/js/controllers/setup.js index 2c353c209..a87bc871c 100644 --- a/js/controllers/setup.js +++ b/js/controllers/setup.js @@ -41,6 +41,7 @@ angular.module('copayApp.controllers').controller('SetupController', $scope.loading = false; $scope.walletPassword = $rootScope.walletPassword; $scope.isMobile = !!window.cordova; + $scope.hideAdv = true; // ng-repeat defined number of times instead of repeating over array? $scope.getNumber = function(num) { @@ -82,6 +83,7 @@ angular.module('copayApp.controllers').controller('SetupController', name: $scope.walletName, nickname: $scope.myNickname, passphrase: passphrase, + privateKeyHex: $scope.private, }; var w = walletFactory.create(opts); controllerUtils.startNetwork(w, $scope); diff --git a/js/models/core/WalletFactory.js b/js/models/core/WalletFactory.js index 70d28a1ba..909fba68f 100644 --- a/js/models/core/WalletFactory.js +++ b/js/models/core/WalletFactory.js @@ -113,9 +113,15 @@ WalletFactory.prototype.create = function(opts) { opts = opts || {}; this.log('### CREATING NEW WALLET.' + (opts.id ? ' USING ID: ' + opts.id : ' NEW ID') + (opts.privateKey ? ' USING PrivateKey: ' + opts.privateKey.getId() : ' NEW PrivateKey')); - opts.privateKey = opts.privateKey || new PrivateKey({ - networkName: this.networkName - }); + var privOpts = { + networkName: this.networkName, + }; + + if (opts.privateKeyHex && opts.privateKeyHex.length>1) { + privOpts.extendedPrivateKeyString = opts.privateKeyHex; + } + + opts.privateKey = opts.privateKey || new PrivateKey(privOpts); var requiredCopayers = opts.requiredCopayers || this.walletDefaults.requiredCopayers; var totalCopayers = opts.totalCopayers || this.walletDefaults.totalCopayers; @@ -216,7 +222,6 @@ WalletFactory.prototype.decodeSecret = function(secret) { WalletFactory.prototype.joinCreateSession = function(secret, nickname, passphrase, privateHex, cb) { -console.log('[WalletFactory.js.218:privateHex:]',privateHex); //TODO var self = this; var s = self.decodeSecret(secret); if (!s) return cb('badSecret'); @@ -229,7 +234,6 @@ console.log('[WalletFactory.js.218:privateHex:]',privateHex); //TODO privOpts.extendedPrivateKeyString = privateHex; } -console.log('[WalletFactory.js.229:privOpts:]',privOpts); //TODO //Create our PrivateK var privateKey = new PrivateKey(privOpts); this.log('\t### PrivateKey Initialized'); diff --git a/test/test.WalletFactory.js b/test/test.WalletFactory.js index 419d2ffcc..8581720f7 100644 --- a/test/test.WalletFactory.js +++ b/test/test.WalletFactory.js @@ -138,6 +138,27 @@ describe('WalletFactory model', function() { var w = wf.create(); should.exist(w); }); + + it('should be able to create wallets with given pk', function() { + var wf = new WalletFactory(config, '0.0.1'); + var priv = 'tprv8ZgxMBicQKsPdEqHcA7RjJTayxA3gSSqeRTttS1JjVbgmNDZdSk9EHZK5pc52GY5xFmwcakmUeKWUDzGoMLGAhrfr5b3MovMUZUTPqisL2m'; + var w = wf.create({ + privateKeyHex:priv, + }); + w.privateKey.toObj().extendedPrivateKeyString.should.equal(priv); + }); + + it('should be able to create wallets with random pk', function() { + var wf = new WalletFactory(config, '0.0.1'); + var priv = 'tprv8ZgxMBicQKsPdEqHcA7RjJTayxA3gSSqeRTttS1JjVbgmNDZdSk9EHZK5pc52GY5xFmwcakmUeKWUDzGoMLGAhrfr5b3MovMUZUTPqisL2m'; + var w1 = wf.create(); + var w2 = wf.create(); + w1.privateKey.toObj().extendedPrivateKeyString.should.not.equal( + w2.privateKey.toObj().extendedPrivateKeyString + ); + }); + + it('should be able to get wallets', function() { var wf = new WalletFactory(config, '0.0.1'); var w = wf.create(); diff --git a/views/setup.html b/views/setup.html index 6ad6d1e36..5a3c873ed 100644 --- a/views/setup.html +++ b/views/setup.html @@ -44,6 +44,21 @@ match="walletPassword" required> + + + Show + Hide + advanced options + +
+

+ +

+
@@ -68,7 +83,7 @@ width="50px">
-

(*) The limits are imposed by the bitcoin network.

+

(*) The limits are imposed by the bitcoin network.

- +

Scan Wallet Addresses

From bb9fbfa1e737cffe619d650c74c5270dc1fd3cd9 Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Thu, 21 Aug 2014 16:50:32 -0400 Subject: [PATCH 21/22] fix tests --- js/controllers/more.js | 3 ++- test/mocks/FakeWallet.js | 8 ++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/js/controllers/more.js b/js/controllers/more.js index eaa1123af..d0cee706b 100644 --- a/js/controllers/more.js +++ b/js/controllers/more.js @@ -6,7 +6,8 @@ angular.module('copayApp.controllers').controller('MoreController', $scope.hideAdv=true; $scope.hidePriv=true; - $scope.priv = w.privateKey.toObj().extendedPrivateKeyString; + if (w) + $scope.priv = w.privateKey.toObj().extendedPrivateKeyString; $scope.downloadBackup = function() { var w = $rootScope.wallet; diff --git a/test/mocks/FakeWallet.js b/test/mocks/FakeWallet.js index 5ca39a8cc..b09cf8f4e 100644 --- a/test/mocks/FakeWallet.js +++ b/test/mocks/FakeWallet.js @@ -6,6 +6,12 @@ if (is_browser) { } var Wallet = copay.Wallet; +var FakePrivateKey = function () { +}; + +FakePrivateKey.prototype.toObj = function() { + return extendedPublicKeyString = 'privHex'; +}; var FakeWallet = function() { this.id = 'testID'; @@ -30,6 +36,8 @@ var FakeWallet = function() { return true; } }; + + this.privateKey = new FakePrivateKey(); }; FakeWallet.prototype.createTx = function(toAddress, amountSatStr, comment, opts, cb) { From 80b01cb9bf8507f1607844ab297d5cc37491a819 Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Thu, 21 Aug 2014 21:12:28 -0400 Subject: [PATCH 22/22] rm skip Private Key --- js/controllers/import.js | 4 ---- views/import.html | 4 ---- 2 files changed, 8 deletions(-) diff --git a/js/controllers/import.js b/js/controllers/import.js index 82ee02fe9..e52f77e86 100644 --- a/js/controllers/import.js +++ b/js/controllers/import.js @@ -28,10 +28,6 @@ angular.module('copayApp.controllers').controller('ImportController', if ($scope.skipTxProposals) skipFields.push('txProposals'); - if ($scope.skipPrivateKey) - skipFields.push('privateKey'); - -console.log('[import.js.36:skipFields:]',skipFields); //TODO // try to import encrypted wallet with passphrase try { w = walletFactory.import(encryptedObj, passphrase, skipFields); diff --git a/views/import.html b/views/import.html index 58984695e..937c6d720 100644 --- a/views/import.html +++ b/views/import.html @@ -37,10 +37,6 @@ - -