diff --git a/index.html b/index.html index fb9330988..1de223647 100644 --- a/index.html +++ b/index.html @@ -66,12 +66,11 @@

Open a Existing Wallet

-
- +

@@ -134,7 +133,7 @@
- {{addr.addrStr}} > + {{addr}} >
diff --git a/js/config.js b/js/config.js index 61e45f54b..8e0d35ee8 100644 --- a/js/config.js +++ b/js/config.js @@ -5,17 +5,19 @@ var config = { network: { apiKey: 'lwjd5qra8257b9', maxPeers: 3, - debug: 0, + debug: 3, }, wallet: { requiredCopayers: 2, totalCopayers: 3, }, - insight: { - host: 'localhost', - port: 3001 + blockchain: { + host: 'test.insight.is', + port: 80 + // host: 'localhost', + // port: 3001 }, - verbose: 0, + verbose: 1, }; var log = function () { diff --git a/js/controllers/home.js b/js/controllers/home.js index 9f6d0bc41..204f10504 100644 --- a/js/controllers/home.js +++ b/js/controllers/home.js @@ -10,13 +10,13 @@ angular.module('copay.home').controller('HomeController', $location.path('signin'); } else { - $scope.addrs = $rootScope.wallet.publicKeyRing.getAddresses(); + $scope.addrs = $rootScope.wallet.getAddressesStr(); $scope.selectedAddr = $scope.addrs[0]; } $scope.newAddr = function() { var a = $rootScope.wallet.generateAddress(); - $scope.addrs.push({ addrStr: a.toString('hex') }); + $scope.addrs.push({ addrStr: a.toString() }); }; $scope.selectAddr = function(addr) { diff --git a/js/controllers/send.js b/js/controllers/send.js index 0c05b0af2..8b2bf9e0a 100644 --- a/js/controllers/send.js +++ b/js/controllers/send.js @@ -16,29 +16,22 @@ angular.module('copay.send').controller('SendController', var opts = {remainderOut: { address: pkr.generateAddress(true).toString() }}; // From @cmgustavo's wallet - var unspentTest = [{ - "txid": "2ac165fa7a3a2b535d106a0041c7568d03b531e58aeccdd3199d7289ab12cfc1", - "vout": 1, - "amount": 10, - "confirmations":7 - }]; + w.listUnspent(function (unspentTest) { +console.log('[send.js.19:unspentTest:]',unspentTest); //TODO - unspentTest[0].address = pkr.generateAddress(false).toString(); - unspentTest[0].scriptPubKey = pkr.getScriptPubKeyHex(false); - -console.log('[send.js.29:txp:] BEFORE',txp); //TODO - - txp.create( - '15q6HKjWHAksHcH91JW23BJEuzZgFwydBt', - '123456789', - unspentTest, - w.privateKey, - opts - ); + txp.create( + '15q6HKjWHAksHcH91JW23BJEuzZgFwydBt', + '123456789', + unspentTest, + w.privateKey, + opts + ); console.log('[send.js.29:txp:] READY:',txp); //TODO - Network.storeOpenWallet(); - Network.sendTxProposals(); - $rootScope.$digest; + Network.storeOpenWallet(); + Network.sendTxProposals(); + $rootScope.$digest; + + }); }; }); diff --git a/js/controllers/signin.js b/js/controllers/signin.js index 810b9229b..62fca1008 100644 --- a/js/controllers/signin.js +++ b/js/controllers/signin.js @@ -7,6 +7,8 @@ angular.module('copay.signin').controller('SigninController', // $rootScope.peerId = peerData ? peerData.peerId : null; $scope.loading = false; + $scope.selectedWalletId = false; + $scope.listWalletIds = function() { return copay.Wallet.factory.getWalletIds(); }; @@ -23,12 +25,20 @@ angular.module('copay.signin').controller('SigninController', $scope.open = function(walletId) { $scope.loading = true; - if (Network.openWallet(walletId)) { + + 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'); + } }; $scope.join = function(cid) { diff --git a/js/controllers/transactions.js b/js/controllers/transactions.js index 50fe25d5e..beaf553cb 100644 --- a/js/controllers/transactions.js +++ b/js/controllers/transactions.js @@ -23,5 +23,5 @@ angular.module('copay.transactions').controller('TransactionsController', } ]; - $scope.txsoutput = $rootScope.wallet.txProposals.txps; + $scope.txsoutput = $rootScope.wallet.getTxProposals(); }); diff --git a/js/models/blockchain/Insight.js b/js/models/blockchain/Insight.js index 6ced3ebdb..d45be6266 100644 --- a/js/models/blockchain/Insight.js +++ b/js/models/blockchain/Insight.js @@ -5,8 +5,9 @@ var bitcore = require('bitcore'); function Insight(opts) { opts = opts || {}; - this.host = 'test.insight.is'; - this.port = '80'; + this.host = opts.host || 'localhost'; + this.port = opts.port || '3001'; + this.scheme = opts.scheme || 'http'; } function _asyncForEach(array, fn, callback) { @@ -40,7 +41,7 @@ Insight.prototype.getBalance = function(unspent) { Insight.prototype.listUnspent = function(addresses, cb) { var self = this; - if (!addresses || !addresses.length) return cb(); + if (!addresses || !addresses.length) return cb([]); var all = []; @@ -48,8 +49,11 @@ Insight.prototype.listUnspent = function(addresses, cb) { var options = { host: self.host, port: self.port, + scheme: self.scheme, method: 'GET', - path: '/api/addr/' + addr + '/utxo' + path: '/api/addr/' + addr + '/utxo', + + headers: { 'Access-Control-Request-Headers' : '' } }; self._request(options, function(err, res) { @@ -74,7 +78,6 @@ Insight.prototype.sendRawTransaction = function(rawtx, cb) { data: 'rawtx='+rawtx, headers: { 'content-type' : 'application/x-www-form-urlencoded' } }; - this._request(options, function(err,res) { if (err) return cb(); return cb(res.txid); diff --git a/js/models/core/PublicKeyRing.js b/js/models/core/PublicKeyRing.js index a5e4a8bcb..b7851ad24 100644 --- a/js/models/core/PublicKeyRing.js +++ b/js/models/core/PublicKeyRing.js @@ -275,7 +275,7 @@ PublicKeyRing.prototype._mergePubkeys = function(inPKR) { } if (!haveIt) { if (self.isComplete()) { - console.log('[PublicKeyRing.js.318] REPEATED KEY', epk); //TODO + //console.log('[PublicKeyRing.js.318] REPEATED KEY', epk); //TODO throw new Error('trying to add more pubkeys, when PKR isComplete at merge'); } self.copayersBIP32.push(new BIP32(epk)); diff --git a/js/models/core/TxProposals.js b/js/models/core/TxProposals.js index 28de54ae7..b3e0cced0 100644 --- a/js/models/core/TxProposals.js +++ b/js/models/core/TxProposals.js @@ -26,7 +26,6 @@ function TxProposals(opts) { this.walletId = opts.walletId; this.network = opts.networkName === 'livenet' ? bitcore.networks.livenet : bitcore.networks.testnet; - this.publicKeyRing = opts.publicKeyRing; this.txps = []; } @@ -136,6 +135,10 @@ TxProposals.prototype._mergeBuilder = function(myTxps, theirTxps, mergeInfo) { }); }; +TxProposals.prototype.add = function(data) { + this.txps.push( new TxProposal(data) ); +}; + TxProposals.prototype.merge = function(t) { if (this.network.name !== t.network.name) throw new Error('network mismatch in:', t); @@ -157,44 +160,6 @@ TxProposals.prototype.merge = function(t) { return mergeInfo.stats; }; -TxProposals.prototype.create = function(toAddress, amountSatStr, utxos, priv, opts) { - var pkr = this.publicKeyRing; - opts = opts || {}; - var amountSat = bitcore.bignum(amountSatStr); - - if (! pkr.isComplete() ) { - throw new Error('publicKeyRing is not complete'); - } - - var opts = { - remainderOut: opts.remainderOut || { address: pkr.generateAddress(true).toString() } - }; - - var b = new Builder(opts) - .setUnspent(utxos) - .setHashToScriptMap(pkr.getRedeemScriptMap()) - .setOutputs([{address: toAddress, amountSat: amountSat}]) - ; - - var signRet; - if (priv) { - b.sign( priv.getAll(pkr.addressIndex, pkr.changeAddressIndex) ); - } - var me = {}; - if (priv) me[priv.id] = Date.now(); - - this.txps.push( - new TxProposal({ - signedBy: priv && b.signaturesAdded ? me : {}, - seenBy: priv ? me : {}, - builder: b, - }) - ); - return 1; -}; - -TxProposals.prototype.sign = function(index) { -}; module.exports = require('soop')(TxProposals); diff --git a/js/models/core/Wallet.js b/js/models/core/Wallet.js index dd4a03e2a..39ba995ff 100644 --- a/js/models/core/Wallet.js +++ b/js/models/core/Wallet.js @@ -1,17 +1,18 @@ 'use strict'; -var imports = require('soop').imports(); +var imports = require('soop').imports(); -var bitcore = require('bitcore'); -var coinUtil = bitcore.util; +var bitcore = require('bitcore'); +var coinUtil = bitcore.util; var buffertools = bitcore.buffertools; -var http = require('http'); +var Builder = bitcore.TransactionBuilder; +var http = require('http'); -var Storage = imports.Storage; -var Network = imports.Network; -var Blockchain = imports.Blockchain; +var Storage = imports.Storage; +var Network = imports.Network; +var Blockchain = imports.Blockchain; -var copay = copay || require('../../../copay'); +var copay = copay || require('../../../copay'); function Wallet(config) { this._startInterface(config); @@ -35,6 +36,7 @@ Wallet.prototype._startInterface = function(config) { 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')); @@ -55,7 +57,6 @@ Wallet.prototype.create = function(opts) { this.txProposals = new copay.TxProposals({ walletId: this.id, - publicKeyRing: this.publicKeyRing, networkName: this.networkName, }); this.log('\t### TxProposals Initialized'); @@ -63,11 +64,11 @@ Wallet.prototype.create = function(opts) { Wallet.prototype._checkLoad = function(walletId) { - return ( - this.storage.get(walletId, 'publicKeyRing') && + var ret = this.storage.get(walletId, 'publicKeyRing') && this.storage.get(walletId, 'txProposals') && this.storage.get(walletId, 'privateKey') - ); + ; + return ret; } Wallet.prototype.load = function(walletId) { @@ -138,6 +139,86 @@ Wallet.prototype.generateAddress = function() { return addr; }; +Wallet.prototype.getTxProposals = function() { + var ret = []; + this.txProposals.txps.forEach(function(txp) { + var i = {txp:txp}; + i.signedByUs = txp.signedBy[this.privateKey.id]?true:false; + ret.push(i); + }); + + return ret; +}; + + +Wallet.prototype.addSeenToTxProposals = function() { + var ret=false; + this.txProposals.txps.forEach(function(txp) { + if (!txp.seenBy[this.privateKey.id]) { + txp.seenBy[this.privateKey.id] = Date.now(); + ret = true; + } + }); + return ret; +}; + + +Wallet.prototype.getAddresses = function() { + return this.publicKeyRing.getAddresses(); +}; + +Wallet.prototype.getAddressesStr = function() { + var ret = []; + this.publicKeyRing.getAddresses().forEach(function(a) { + ret.push(a.toString()); + }); + return ret; +}; + + + +Wallet.prototype.listUnspent = function(cb) { + this.blockchain.listUnspent(this.getAddressesStr(), cb); +}; + +Wallet.prototype.createTx = function(toAddress, amountSatStr, utxos, opts) { + var pkr = this.publicKeyRing; + var priv = this.privateKey; + opts = opts || {}; + + var amountSat = bitcore.bignum(amountSatStr); + + if (! pkr.isComplete() ) { + throw new Error('publicKeyRing is not complete'); + } + + if (!opts.remainderOut) { + opts.remainderOut ={ address: pkr.generateAddress(true).toString() }; + }; + + var b = new Builder(opts) + .setUnspent(utxos) + .setHashToScriptMap(pkr.getRedeemScriptMap()) + .setOutputs([{address: toAddress, amountSat: amountSat}]) + ; + + var signRet; + if (priv) { + b.sign( priv.getAll(pkr.addressIndex, pkr.changeAddressIndex) ); + } + var me = {}; + if (priv) me[priv.id] = Date.now(); + + this.txProposals.add({ + signedBy: priv && b.signaturesAdded ? me : {}, + seenBy: priv ? me : {}, + builder: b, + }); +}; + +Wallet.prototype.sign = function(txp) { +}; + // // HERE? not sure // Wallet.prototype.cleanPeers = function() { // this.storage.remove('peerData'); diff --git a/js/services/network.js b/js/services/network.js index edeedb728..a4e69dac7 100644 --- a/js/services/network.js +++ b/js/services/network.js @@ -114,8 +114,10 @@ angular.module('copay.network') var recipients; var inTxProposals = copay.TxProposals.fromObj(data.txProposals); var mergeInfo = w.txProposals.merge(inTxProposals, true); - if ( mergeInfo.merged && !data.isBroadcast) { - log('### BROADCASTING txProposals'); + + var addSeen = w.addSeenToTxProposals(); + if ((mergeInfo.merged && !data.isBroadcast) || addSeen) { + log('### BROADCASTING txProposals. ' ); recipients = null; shouldSend = true; } diff --git a/test/test.txproposal.js b/test/test.txproposal.js index 66d72d5ba..26ed2974d 100644 --- a/test/test.txproposal.js +++ b/test/test.txproposal.js @@ -10,6 +10,7 @@ var Key = bitcore.Key; var BIP32 = bitcore.BIP32; var bignum = bitcore.bignum; var Script = bitcore.Script; +var Builder = bitcore.TransactionBuilder; var util = bitcore.util; var networks = bitcore.networks; var copay = copay || require('../copay'); @@ -69,106 +70,60 @@ describe('TxProposals model', function() { w.network.name.should.equal('livenet'); }); - it('#create, no signing', function () { - var w = new TxProposals({ - networkName: config.networkName, - publicKeyRing: createPKR(), - }); - should.exist(w); - w.network.name.should.equal('livenet'); + function createTx(toAddress, amountSatStr, utxos, opts, priv, pkr) { + opts = opts || {}; - unspentTest[0].address = w.publicKeyRing.getAddress(1, true).toString(); - unspentTest[0].scriptPubKey = w.publicKeyRing.getScriptPubKeyHex(1, true); + var amountSat = bitcore.bignum(amountSatStr); - w.create( - '15q6HKjWHAksHcH91JW23BJEuzZgFwydBt', - '123456789', - unspentTest - ); - var tx = w.txps[0].builder.build(); - should.exist(tx); - tx.isComplete().should.equal(false); - Object.keys(w.txps[0].signedBy).length.should.equal(0); - Object.keys(w.txps[0].seenBy).length.should.equal(0); - }); - - - it('#create, signing with wrong key', function () { - var w = new TxProposals({ - networkName: config.networkName, - publicKeyRing: createPKR(), - }); - should.exist(w); - w.network.name.should.equal('livenet'); - - unspentTest[0].address = w.publicKeyRing.getAddress(1, true).toString(); - unspentTest[0].scriptPubKey = w.publicKeyRing.getScriptPubKeyHex(1, true); - - var priv = new PrivateKey(config); - w.create( - '15q6HKjWHAksHcH91JW23BJEuzZgFwydBt', - '123456789', - unspentTest, - priv - ); - var tx = w.txps[0].builder.build(); - should.exist(tx); - tx.isComplete().should.equal(false); - Object.keys(w.txps[0].signedBy).length.should.equal(0); - Object.keys(w.txps[0].seenBy).length.should.equal(1); - }); - - - it('#create. Signing with derivate keys', function () { - - var priv = new PrivateKey(config); - var w = new TxProposals({ - networkName: config.networkName, - publicKeyRing: createPKR([priv.getBIP32()]), - }); - - var ts = Date.now(); - for (var isChange=0; isChange<2; isChange++) { - for (var index=0; index<3; index++) { - unspentTest[0].address = w.publicKeyRing.getAddress(index, isChange).toString(); - unspentTest[0].scriptPubKey = w.publicKeyRing.getScriptPubKeyHex(index, isChange); - w.create( - '15q6HKjWHAksHcH91JW23BJEuzZgFwydBt', - '123456789', - unspentTest, - priv - ); - var tx = w.txps[0].builder.build(); - should.exist(tx); - tx.isComplete().should.equal(false); - - tx.countInputMissingSignatures(0).should.equal(2); - - (w.txps[0].signedBy[priv.id] - ts > 0).should.equal(true); - (w.txps[0].seenBy[priv.id] - ts > 0).should.equal(true); - } + if(! pkr.isComplete() ) { + throw new Error('publicKeyRing is not complete'); } - }); + + if (!opts.remainderOut) { + opts.remainderOut ={ address: pkr.generateAddress(true).toString() }; + }; + + var b = new Builder(opts) + .setUnspent(utxos) + .setHashToScriptMap(pkr.getRedeemScriptMap()) + .setOutputs([{address: toAddress, amountSat: amountSat}]) + ; + + var signRet; + if (priv) { + b.sign( priv.getAll(pkr.addressIndex, pkr.changeAddressIndex) ); + } + var me = {}; + if (priv) me[priv.id] = Date.now(); + + return { + signedBy: priv && b.signaturesAdded ? me : {}, + seenBy: priv ? me : {}, + builder: b, + }; + }; + it('#merge with self', function () { - var priv = new PrivateKey(config); var w = new TxProposals({ networkName: config.networkName, - publicKeyRing: createPKR([priv.getBIP32()]), }); + var pkr=createPKR([priv.getBIP32()]); var ts = Date.now(); var isChange=0; var index=0; - unspentTest[0].address = w.publicKeyRing.getAddress(index, isChange).toString(); - unspentTest[0].scriptPubKey = w.publicKeyRing.getScriptPubKeyHex(index, isChange); - w.create( + unspentTest[0].address = pkr.getAddress(index, isChange).toString(); + unspentTest[0].scriptPubKey = pkr.getScriptPubKeyHex(index, isChange); + w.add(createTx( '15q6HKjWHAksHcH91JW23BJEuzZgFwydBt', '123456789', unspentTest, - priv - ); + {}, + priv, + pkr + )); var tx = w.txps[0].builder.build(); tx.isComplete().should.equal(false); tx.countInputMissingSignatures(0).should.equal(2); @@ -184,13 +139,11 @@ describe('TxProposals model', function() { (w.txps[0].signedBy[priv.id] - ts > 0).should.equal(true); (w.txps[0].seenBy[priv.id] - ts > 0).should.equal(true); - }); it('#merge, merge signatures case 1', function () { - var priv2 = new PrivateKey(config); var priv = new PrivateKey(config); var ts = Date.now(); @@ -202,17 +155,17 @@ describe('TxProposals model', function() { var w = new TxProposals({ networkName: config.networkName, - publicKeyRing: pkr, }); - unspentTest[0].address = w.publicKeyRing.getAddress(index, isChange).toString(); - unspentTest[0].scriptPubKey = w.publicKeyRing.getScriptPubKeyHex(index, isChange); - w.create( + unspentTest[0].address = pkr.getAddress(index, isChange).toString(); + unspentTest[0].scriptPubKey = pkr.getScriptPubKeyHex(index, isChange); + w.add(createTx( '15q6HKjWHAksHcH91JW23BJEuzZgFwydBt', '123456789', unspentTest, + opts, priv2, - opts - ); + pkr + )); var tx = w.txps[0].builder.build(); tx.isComplete().should.equal(false); @@ -226,15 +179,16 @@ describe('TxProposals model', function() { networkName: config.networkName, publicKeyRing: w.publicKeyRing, }); - unspentTest[0].address = w.publicKeyRing.getAddress(index, isChange).toString(); - unspentTest[0].scriptPubKey = w.publicKeyRing.getScriptPubKeyHex(index, isChange); - w2.create( + unspentTest[0].address = pkr.getAddress(index, isChange).toString(); + unspentTest[0].scriptPubKey = pkr.getScriptPubKeyHex(index, isChange); + w2.add(createTx( '15q6HKjWHAksHcH91JW23BJEuzZgFwydBt', '123456789', unspentTest, + opts, priv, - opts - ); + pkr + )); var tx = w2.txps[0].builder.build(); tx.isComplete().should.equal(false); @@ -275,17 +229,18 @@ var _dumpChunks = function (scriptSig, label) { var w = new TxProposals({ networkName: config.networkName, - publicKeyRing: pkr, }); - unspentTest[0].address = w.publicKeyRing.getAddress(index, isChange).toString(); - unspentTest[0].scriptPubKey = w.publicKeyRing.getScriptPubKeyHex(index, isChange); - w.create( + unspentTest[0].address = pkr.getAddress(index, isChange).toString(); + unspentTest[0].scriptPubKey = pkr.getScriptPubKeyHex(index, isChange); + + w.add(createTx( '15q6HKjWHAksHcH91JW23BJEuzZgFwydBt', '123456789', unspentTest, + opts, priv3, - opts - ); + pkr + )); var tx = w.txps[0].builder.build(); tx.isComplete().should.equal(false); @@ -297,17 +252,18 @@ var _dumpChunks = function (scriptSig, label) { var w2 = new TxProposals({ networkName: config.networkName, - publicKeyRing: pkr, }); - unspentTest[0].address = w.publicKeyRing.getAddress(index, isChange).toString(); - unspentTest[0].scriptPubKey = w.publicKeyRing.getScriptPubKeyHex(index, isChange); - w2.create( + + unspentTest[0].address = pkr.getAddress(index, isChange).toString(); + unspentTest[0].scriptPubKey = pkr.getScriptPubKeyHex(index, isChange); + w2.add(createTx( '15q6HKjWHAksHcH91JW23BJEuzZgFwydBt', '123456789', unspentTest, + opts, priv, - opts - ); + pkr + )); tx = w2.txps[0].builder.build(); tx.isComplete().should.equal(false); tx.countInputMissingSignatures(0).should.equal(2); @@ -329,15 +285,16 @@ var _dumpChunks = function (scriptSig, label) { networkName: config.networkName, publicKeyRing: pkr, }); - unspentTest[0].address = w.publicKeyRing.getAddress(index, isChange).toString(); - unspentTest[0].scriptPubKey = w.publicKeyRing.getScriptPubKeyHex(index, isChange); - w3.create( + unspentTest[0].address = pkr.getAddress(index, isChange).toString(); + unspentTest[0].scriptPubKey = pkr.getScriptPubKeyHex(index, isChange); + w3.add(createTx( '15q6HKjWHAksHcH91JW23BJEuzZgFwydBt', '123456789', unspentTest, + opts, priv2, - opts - ); + pkr + )); tx = w3.txps[0].builder.build(); tx.isComplete().should.equal(false); tx.countInputMissingSignatures(0).should.equal(2); @@ -372,17 +329,17 @@ var _dumpChunks = function (scriptSig, label) { var w = new TxProposals({ networkName: config.networkName, - publicKeyRing: pkr, }); - unspentTest[0].address = w.publicKeyRing.getAddress(index, isChange).toString(); - unspentTest[0].scriptPubKey = w.publicKeyRing.getScriptPubKeyHex(index, isChange); - w.create( + unspentTest[0].address = pkr.getAddress(index, isChange).toString(); + unspentTest[0].scriptPubKey = pkr.getScriptPubKeyHex(index, isChange); + w.add(createTx( '15q6HKjWHAksHcH91JW23BJEuzZgFwydBt', '123456789', unspentTest, + opts, priv, - opts - ); + pkr + )); var tx = w.txps[0].builder.build(); tx.isComplete().should.equal(false); tx.countInputMissingSignatures(0).should.equal(2); @@ -392,17 +349,17 @@ var _dumpChunks = function (scriptSig, label) { var w2 = new TxProposals({ networkName: config.networkName, - publicKeyRing: pkr, }); - unspentTest[0].address = w.publicKeyRing.getAddress(index, isChange).toString(); - unspentTest[0].scriptPubKey = w.publicKeyRing.getScriptPubKeyHex(index, isChange); - w2.create( + unspentTest[0].address = pkr.getAddress(index, isChange).toString(); + unspentTest[0].scriptPubKey = pkr.getScriptPubKeyHex(index, isChange); + w2.add(createTx( '15q6HKjWHAksHcH91JW23BJEuzZgFwydBt', '123456789', unspentTest, + opts, priv2, - opts - ); + pkr + )); var tx = w2.txps[0].builder.build(); tx.isComplete().should.equal(false); tx.countInputMissingSignatures(0).should.equal(2); @@ -412,17 +369,17 @@ var _dumpChunks = function (scriptSig, label) { var w3 = new TxProposals({ networkName: config.networkName, - publicKeyRing: pkr, }); - unspentTest[0].address = w.publicKeyRing.getAddress(index, isChange).toString(); - unspentTest[0].scriptPubKey = w.publicKeyRing.getScriptPubKeyHex(index, isChange); - w3.create( + unspentTest[0].address = pkr.getAddress(index, isChange).toString(); + unspentTest[0].scriptPubKey = pkr.getScriptPubKeyHex(index, isChange); + w3.add(createTx( '15q6HKjWHAksHcH91JW23BJEuzZgFwydBt', '123456789', unspentTest, + opts, priv3, - opts - ); + pkr + )); var tx = w3.txps[0].builder.build(); tx.isComplete().should.equal(false); tx.countInputMissingSignatures(0).should.equal(2); @@ -458,23 +415,25 @@ var _dumpChunks = function (scriptSig, label) { it('#toObj #fromObj roundtrip', function () { var priv = new PrivateKey(config); + var pkr = createPKR([priv.getBIP32()]); var w = new TxProposals({ walletId: 'qwerty', networkName: config.networkName, - publicKeyRing: createPKR([priv.getBIP32()]), }); var ts = Date.now(); var isChange=0; var index=0; - unspentTest[0].address = w.publicKeyRing.getAddress(index, isChange).toString(); - unspentTest[0].scriptPubKey = w.publicKeyRing.getScriptPubKeyHex(index, isChange); - w.create( + unspentTest[0].address = pkr.getAddress(index, isChange).toString(); + unspentTest[0].scriptPubKey = pkr.getScriptPubKeyHex(index, isChange); + w.add(createTx( '15q6HKjWHAksHcH91JW23BJEuzZgFwydBt', '123456789', unspentTest, - priv - ); + {}, + priv, + pkr + )); var tx = w.txps[0].builder.build(); tx.isComplete().should.equal(false); tx.countInputMissingSignatures(0).should.equal(2); diff --git a/test/test.wallet.js b/test/test.wallet.js index 54a487da2..98f069e32 100644 --- a/test/test.wallet.js +++ b/test/test.wallet.js @@ -9,23 +9,68 @@ var Wallet = require('soop').load('../js/models/core/Wallet', { Blockchain: copay.Insight }); + +var addCopayers = function (w) { + for(var i=0; i<4; i++) { + w.publicKeyRing.addCopayer(); + } +}; + describe('Wallet model', function() { var config = { wallet: { requiredCopayers: 3, totalCopayers: 5, - } + }, + blockchain: { + host: 'test.insight.is', + port: 80 + }, }; var opts = {}; - it.skip('should create an instance', function () { + it('should create an instance', function () { var opts = {}; - var w = Wallet.create(config, opts); + var w = new Wallet(config); should.exist(w); }); + it('should fail to load', function () { + var opts = {}; + var w = new Wallet(config); + w.load(123); + should.not.exist(w.id); + }); + + + it('should create', function () { + var opts = {}; + var w = new Wallet(config); + w.create(); + should.exist(w.id); + should.exist(w.publicKeyRing); + should.exist(w.privateKey); + should.exist(w.txProposals); + }); + + it('should list unspent', function (done) { + var opts = {}; + var w = new Wallet(config); + w.create(); + addCopayers(w); + w.publicKeyRing.generateAddress(false); + + should.exist(w.id); + w.publicKeyRing.isComplete().should.equal(true); + + w.listUnspent(function(utxos) { + utxos.length.should.equal(0); + done(); + }); + }); + describe('factory', function() { it('should create the factory', function() { should.exist(Wallet.factory); @@ -41,4 +86,111 @@ describe('Wallet model', function() { }); }); + var unspentTest = [ + { + "address": "dummy", + "scriptPubKey": "dummy", + "txid": "2ac165fa7a3a2b535d106a0041c7568d03b531e58aeccdd3199d7289ab12cfc1", + "vout": 1, + "amount": 10, + "confirmations":7 + } + ]; + + var createWallet = function (bip32s) { + var w = new Wallet(config); + w.create(); + should.exist(w); + + var pkr = w.publicKeyRing; + + for(var i=0; i<4; i++) { + if (bip32s) { + var b=bip32s[i]; + pkr.addCopayer(b?b.extendedPublicKeyString():null); + } + else + pkr.addCopayer(); + } + pkr.generateAddress(true); + pkr.generateAddress(true); + pkr.generateAddress(true); + pkr.generateAddress(false); + pkr.generateAddress(false); + pkr.generateAddress(false); + //3x3 indexes + + return w; + }; + + it('#create, 1 sign', function () { + + var w = createWallet(); + + unspentTest[0].address = w.publicKeyRing.getAddress(1, true).toString(); + unspentTest[0].scriptPubKey = w.publicKeyRing.getScriptPubKeyHex(1, true); + + w.createTx( + '15q6HKjWHAksHcH91JW23BJEuzZgFwydBt', + '123456789', + unspentTest + ); + + var t = w.txProposals; + var tx = t.txps[0].builder.build(); + should.exist(tx); + tx.isComplete().should.equal(false); + Object.keys(t.txps[0].signedBy).length.should.equal(1); + Object.keys(t.txps[0].seenBy).length.should.equal(1); + }); + + + + it('#create. Signing with derivate keys', function () { + + var w = createWallet(); + + var ts = Date.now(); + for (var isChange=0; isChange<2; isChange++) { + for (var index=0; index<3; index++) { + unspentTest[0].address = w.publicKeyRing.getAddress(index, isChange).toString(); + unspentTest[0].scriptPubKey = w.publicKeyRing.getScriptPubKeyHex(index, isChange); + w.createTx( + '15q6HKjWHAksHcH91JW23BJEuzZgFwydBt', + '123456789', + unspentTest + ); + var t = w.txProposals; + var tx = t.txps[0].builder.build(); + should.exist(tx); + tx.isComplete().should.equal(false); + tx.countInputMissingSignatures(0).should.equal(2); + + ( t.txps[0].signedBy[w.privateKey.id] - ts > 0).should.equal(true); + ( t.txps[0].seenBy[w.privateKey.id] - ts > 0).should.equal(true); + } + } + }); + + // TODO: when sign is implemented + it.skip('#create, signing with wrong key', function () { + var w1 = createWallet(); + + unspentTest[0].address = w.publicKeyRing.getAddress(1, true).toString(); + unspentTest[0].scriptPubKey = w.publicKeyRing.getScriptPubKeyHex(1, true); + + var priv = new PrivateKey(config); + w.create( + '15q6HKjWHAksHcH91JW23BJEuzZgFwydBt', + '123456789', + unspentTest + ); + var tx = w.txps[0].builder.build(); + should.exist(tx); + tx.isComplete().should.equal(false); + Object.keys(w.txps[0].signedBy).length.should.equal(0); + Object.keys(w.txps[0].seenBy).length.should.equal(1); + }); + + });