From a7de2ababd5097463490460a2fb2e4946c912486 Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Sun, 23 Nov 2014 15:52:39 -0300 Subject: [PATCH] rm networking on nonShared wallets --- js/controllers/send.js | 94 +++++++++---------- js/models/Async.js | 9 +- js/models/Insight.js | 4 +- js/models/TxProposal.js | 18 ++++ js/models/TxProposals.js | 18 ++++ js/models/Wallet.js | 189 ++++++++++++++++++++------------------- 6 files changed, 179 insertions(+), 153 deletions(-) diff --git a/js/controllers/send.js b/js/controllers/send.js index efceab7fb..adc22d0f1 100644 --- a/js/controllers/send.js +++ b/js/controllers/send.js @@ -24,6 +24,7 @@ angular.module('copayApp.controllers').controller('SendController', $scope.isRateAvailable = false; $scope.rateService = rateService; + $scope.showScanner = false; rateService.whenAvailable(function() { @@ -105,33 +106,19 @@ angular.module('copayApp.controllers').controller('SendController', var msg = err.toString(); if (msg.match('BIG')) - msg = 'The transaction have too many inputs. Try creating many transactions for smaller amounts.' + msg = 'The transaction have too many inputs. Try creating many transactions for smaller amounts' if (msg.match('totalNeededAmount')) - msg = 'Not enough funds.' + msg = 'Not enough funds' + + var message = 'The transaction' + (w.isShared() ? ' proposal' : '') + + ' could not be created: ' + msg; - var message = 'The transaction' + (w.isShared() ? ' proposal' : '') + ' could not be created: ' + msg; $scope.error = message; $scope.loading = false; $scope.loadTxs(); }; - $scope._afterSend = function(err, ntxid, merchantData) { - $scope.loading = false; - - if (err) - return $scope._showError(err); - - - if (w.requiresMultipleSignatures()) { - notification.success('Success', 'The transaction proposal created'); - $scope.loadTxs(); - } else { - $scope.send(ntxid); - } - $rootScope.pendingPayment = null; - }; - $scope.submitForm = function(form) { if (form.$invalid) { $scope.error = 'Unable to send transaction proposal'; @@ -160,16 +147,21 @@ angular.module('copayApp.controllers').controller('SendController', merchant: $rootScope.merchant.request_url }; } - // reset fields - $scope.address = $scope.amount = $scope.commentText = null; - form.address.$pristine = form.amount.$pristine = true; - - w.createTx({ - toAddress: address, - amountSat: amount, - comment: commentText, + w.spend({ + toAddress: address, + amountSat: amount, + comment: commentText, url: (payInfo && payInfo.merchant) ? payInfo.merchant : null, - }, $scope._afterSend); + }, function(err, txid, status) { + // reset fields + $scope.address = $scope.amount = $scope.commentText = null; + form.address.$pristine = form.amount.$pristine = true; + $rootScope.pendingPayment = null; + if (err) return $scope._showError(err); + + $scope.notifyStatus(status); + $scope.loadTxs(); + }); }; // QR code Scanner @@ -267,6 +259,7 @@ angular.module('copayApp.controllers').controller('SendController', $scope.openScanner = function() { if (window.cordova) return $scope.scannerIntent(); +console.log('[send.js.260] OPENN'); //TODO $scope.showScanner = true; // Wait a moment until the canvas shows @@ -371,22 +364,24 @@ angular.module('copayApp.controllers').controller('SendController', $scope.amount = $rootScope.topAmount; }; + $scope.notifyStatus = function(status) { + if (status == copay.Wallet.TX_BROADCASTED) + notification.success('Success', 'Transaction broadcasted!'); + else if (status == copay.Wallet.TX_PROPOSAL_SENT) + notification.success('Success', 'Transaction proposal created'); + else if (status == copay.Wallet.TX_SIGNED) + notification.success('Success', 'Transaction proposal was signed'); + else + notification.error('Error', 'Unknown error occured'); + }; + $scope.send = function(ntxid, cb) { $scope.error = $scope.success = null; $scope.loading = true; $rootScope.txAlertCount = 0; - w.sendTx(ntxid, function(txid, merchantData) { - if (!txid) { - notification.error('Error', 'There was an error sending the transaction'); - } else { - if (!merchantData) { - notification.success('Success', 'Transaction broadcasted!'); - } else { - notification.success('Success', 'Payment sent. ' + merchantData.ack.memo); - } - } - + w.broadcastTx(ntxid, function(err, txid, status) { + $scope.notifyStatus(status); if (cb) return cb(); else $scope.loadTxs(); }); @@ -396,20 +391,10 @@ angular.module('copayApp.controllers').controller('SendController', $scope.loading = true; $scope.error = $scope.success = null; - try { - w.sign(ntxid); - } catch (e) { - notification.error('Error', 'There was an error signing the transaction'); + w.signAndSend(ntxid, function(err, id, status) { + $scope.notifyStatus(status); $scope.loadTxs(); - return; - } - - var p = w.txProposals.getTxProposal(ntxid); - if (p.builder.isFullySigned()) { - $scope.send(ntxid); - } else { - $scope.loadTxs(); - } + }); }; $scope.reject = function(ntxid) { @@ -417,7 +402,6 @@ angular.module('copayApp.controllers').controller('SendController', $rootScope.txAlertCount = 0; w.reject(ntxid); notification.warning('Transaction rejected', 'You rejected the transaction successfully'); - $scope.loading = false; $scope.loadTxs(); }; @@ -510,7 +494,9 @@ angular.module('copayApp.controllers').controller('SendController', }, 10 * 1000); // Payment Protocol URI (BIP-72) - $scope.wallet.fetchPaymentRequest({url: uri.merchant}, function(err, merchantData) { + $scope.wallet.fetchPaymentRequest({ + url: uri.merchant + }, function(err, merchantData) { if (!timeout) return; clearTimeout(timeout); diff --git a/js/models/Async.js b/js/models/Async.js index 0e9d0c522..361162283 100644 --- a/js/models/Async.js +++ b/js/models/Async.js @@ -207,7 +207,9 @@ Network.prototype._onMessage = function(enc) { log.debug('Ignoring trailing message. Ts:', enc.ts); return; } - log.debug('Async: receiving ' + JSON.stringify(payload)); + + log.info('Network message: ', payload.type); + log.debug('Message payload:', payload); var self = this; switch (payload.type) { @@ -239,6 +241,7 @@ Network.prototype._onMessage = function(enc) { Network.prototype._setupConnectionHandlers = function(opts, cb) { preconditions.checkState(this.socket); + log.debug('setting up connection', opts); var self = this; self.socket.on('connect_error', function(m) { @@ -260,7 +263,7 @@ Network.prototype._setupConnectionHandlers = function(opts, cb) { if (fromTs) { self.ignoreMessageFromTs = fromTs; } - log.info('Async: synchronizing from: ',fromTs); + log.info('Async: syncing from: ', fromTs); self.socket.emit('sync', fromTs); self.started = true; }); @@ -398,8 +401,6 @@ Network.prototype.send = function(dest, payload, cb) { if (to == this.copayerId) continue; - log.debug('SEND to: ' + to, this.copayerId, JSON.stringify(payload)); - var message = this.encode(to, payload); this.socket.emit('message', message); } diff --git a/js/models/Insight.js b/js/models/Insight.js index 2f3e8f90c..a040c7fec 100644 --- a/js/models/Insight.js +++ b/js/models/Insight.js @@ -224,9 +224,7 @@ Insight.prototype.subscribe = function(addresses) { s.emit('subscribe', address); s.on(address, handler); - } else { - log.debug('Already subcribed to: ', address); - } + } }); }; diff --git a/js/models/TxProposal.js b/js/models/TxProposal.js index 934319171..09a32812c 100644 --- a/js/models/TxProposal.js +++ b/js/models/TxProposal.js @@ -82,6 +82,24 @@ TxProposal.prototype._checkPayPro = function() { }; + +TxProposal.prototype.isFullySigned = function() { + return txp.builder && txp.builder.isFullySigned(); +}; + +TxProposal.prototype.sign = function(keys, signerId) { + var before = txp.countSignatures(); + txp.builder.sign(keys); + + var signaturesAdded = txp.countSignatures() > before; + if (signaturesAdded){ + txp.signedBy[signerId] = Date.now(); + } + return signturesAdded; +}; + + + TxProposal.prototype._check = function() { if (this.builder.signhash && this.builder.signhash !== Transaction.SIGHASH_ALL) { diff --git a/js/models/TxProposals.js b/js/models/TxProposals.js index d800ddacf..df359f57a 100644 --- a/js/models/TxProposals.js +++ b/js/models/TxProposals.js @@ -205,4 +205,22 @@ TxProposals.prototype.getUsedUnspent = function(maxRejectCount) { return ret; }; +/** + * purge + * + * @param deleteAll + * @return {undefined} + */ +TxProposals.prototype.purge = function(deleteAll, maxRejectCount) { + var m = _.size(this.txps); + + if (deleteAll) { + this.deleteAll(); + } else { + this.deletePending(maxRejectCount); + } + var n = _.size(this.txps); + return m - n; +}; + module.exports = TxProposals; diff --git a/js/models/Wallet.js b/js/models/Wallet.js index 5a3835a5e..eaf3f84f2 100644 --- a/js/models/Wallet.js +++ b/js/models/Wallet.js @@ -33,6 +33,8 @@ var copayConfig = require('../../config'); var TX_MAX_SIZE_KB = 50; var TX_MAX_INS = 70; + + /** * @desc * Wallet manages a private key for Copay, network, storage of the wallet for @@ -119,6 +121,11 @@ function Wallet(opts) { inherits(Wallet, events.EventEmitter); + +Wallet.TX_BROADCASTED = 'txBroadcasted'; +Wallet.TX_PROPOSAL_SENT = 'txProposalSent'; +Wallet.TX_SIGNED = 'txSigned'; + Wallet.prototype.emitAndKeepAlive = function(args) { var args = Array.prototype.slice.call(arguments); log.debug('Wallet Emitting:', args); @@ -260,13 +267,10 @@ Wallet.prototype._newAddresses = function(dontUpdateUx) { * Processes the data using {@link HDParams#fromList} and merges it with the * {@link Wallet#publicKeyRing}. * - * Triggers a {@link Wallet#store} if the internal state has changed. - * * @param {string} senderId - the sender id * @param {Object} data - the data recived, {@see HDParams#fromList} */ Wallet.prototype._onIndexes = function(senderId, data) { - log.debug('Wallet:' + this.id + ' RECV INDEXES:', data); var inIndexes = HDParams.fromList(data.indexes); var hasChanged = this.publicKeyRing.mergeIndexes(inIndexes); if (hasChanged) { @@ -448,7 +452,8 @@ Wallet.prototype._processTxProposalSeen = function(ntxid) { }; -Wallet.prototype._processTxProposalSent = function(ntxid, cb) { + +Wallet.prototype._checkIfTxProposalIsSent = function(ntxid, cb) { var self = this; var txp = this.txProposals.get(ntxid); @@ -459,7 +464,7 @@ Wallet.prototype._processTxProposalSent = function(ntxid, cb) { } } self.emitAndKeepAlive('txProposalsUpdated'); - if (cb) return cb(null, txid); + if (cb) return cb(null, txid, txid ? Wallet.TX_BROADCASTED : null); }); }; @@ -501,9 +506,8 @@ Wallet.prototype._processIncomingTxProposal = function(mergeInfo, cb) { var tx = mergeInfo.txp.builder.build(); if (tx.isComplete()) - self._processTxProposalSent(mergeInfo.ntxid); - else if (mergeInfo.hasChanged) { - self.sendTxProposal(mergeInfo.ntxid); + self._checkIfTxProposalIsSent(mergeInfo.ntxid); + else { self.emitAndKeepAlive('txProposalsUpdated'); } return cb(); @@ -521,9 +525,9 @@ Wallet.prototype._processIncomingTxProposal = function(mergeInfo, cb) { */ Wallet.prototype._onTxProposal = function(senderId, data) { var self = this; - log.debug('Wallet:' + this.id + ' RECV TXPROPOSAL: ', data); var m; +console.log('[Wallet.js.533]a', this.txProposals.txps); //TODO try { m = this.txProposals.merge(data.txProposal, Wallet.builderOpts); var keyMap = this._getKeyMap(m.txp); @@ -534,8 +538,9 @@ Wallet.prototype._onTxProposal = function(senderId, data) { this.txProposals.deleteOne(m.ntxid); m = null; } +console.log('[Wallet.js.533]a', this.txProposals.txps); //TODO - self._processIncomingTxProposal(m, function(err) { + this._processIncomingTxProposal(m, function(err) { if (err) { log.error('Corrupt TX proposal received from:', senderId, err.toString()); if (m && m.ntxid) @@ -588,8 +593,6 @@ Wallet.prototype._onReject = function(senderId, data) { */ Wallet.prototype._onSeen = function(senderId, data) { preconditions.checkState(data.ntxid); - log.debug('Wallet:' + this.id + ' RECV SEEN:', data); - var txp = this.txProposals.get(data.ntxid); txp.setSeen(senderId); this.emitAndKeepAlive('txProposalEvent', { @@ -611,14 +614,12 @@ Wallet.prototype._onSeen = function(senderId, data) { * @emits txProposalEvent */ Wallet.prototype._onAddressBook = function(senderId, data) { - preconditions.checkState(data.addressBook); - log.debug('Wallet:' + this.id + ' RECV ADDRESSBOOK:', data); + if (!data.addressBook || !_.isArray(data.addressBook)) + return; - var rcv = data.addressBook; - console.log('[Wallet.js.618:rcv:]', rcv); //TODO + var self = this, hasChange; - var hasChange, self = this; - _.each(rcv, function(value, key) { + _.each(data.addressBook, function(value, key) { if (!self.addressBook[key] && Address.validate(key)) { self.addressBook[key] = _.pick(value, ['createdTs', 'label']); @@ -660,8 +661,6 @@ Wallet.prototype.updateSyncedTimestamp = function(ts) { * Triggers a call to {@link Wallet#sendWalletReady} */ Wallet.prototype._onNoMessages = function() { - if (!this.isShared()) return; - log.debug('Wallet:' + this.id + ' No messages at the server. Requesting peer sync from: ' + (this.syncedTimestamp + 1)); this.sendWalletReady(null, parseInt((this.syncedTimestamp + 1) / 1000000)); }; @@ -676,13 +675,14 @@ Wallet.prototype._onNoMessages = function() { * @emits corrupt */ Wallet.prototype._onData = function(senderId, data, ts) { +console.log('[Wallet.js.533]0', this.txProposals.txps); //TODO preconditions.checkArgument(senderId); preconditions.checkArgument(data); preconditions.checkArgument(data.type); preconditions.checkArgument(ts); preconditions.checkArgument(_.isNumber(ts)); - log.debug('Wallet:' + this.id + ' RECV', senderId, data); + log.debug('Wallet:' + this.getName() + ' RECV:', data.type, data, senderId); this.updateSyncedTimestamp(ts); @@ -917,8 +917,13 @@ Wallet.prototype._setBlockchainListeners = function() { */ Wallet.prototype.netStart = function() { var self = this; - var net = this.network; + if (!this.isShared()) { + self.emitAndKeepAlive('ready'); + return; + } + + var net = this.network; net.removeAllListeners(); net.on('connect', self._onConnect.bind(self)); net.on('data', self._onData.bind(self)); @@ -953,7 +958,7 @@ Wallet.prototype.netStart = function() { }; - log.debug('Wallet:' + self.id + ' Starting networking: ' + startOpts.copayerId); + log.debug('Wallet:' + self.id + ' Starting network.'); net.start(startOpts, function() { self._setBlockchainListeners(); self.emitAndKeepAlive('ready', net.getPeer()); @@ -1154,11 +1159,16 @@ Wallet.fromObj = function(o, readOpts) { /** - * @desc Send a message to other peers + * @desc sendToPeers a message to other peers * @param {string[]} recipients - the pubkey of the recipients of the message * @param {Object} obj - the data to be sent to them */ -Wallet.prototype.send = function(recipients, obj) { +Wallet.prototype._sendToPeers = function(recipients, obj) { + if (!this.isShared()) return; + + log.info('Wallet:' + this.getName() + ' ### SENDING ' + obj.type); + log.debug('Sending obj', obj); + this.network.send(recipients, obj); }; @@ -1181,8 +1191,7 @@ Wallet.prototype.sendAllTxProposals = function(recipients, sinceTs) { */ Wallet.prototype.sendTxProposal = function(ntxid, recipients) { preconditions.checkArgument(ntxid); - log.debug('Wallet:' + this.id + ' ### SENDING txProposal ' + ntxid + ' TO:', recipients || 'All', this.txProposals); - this.send(recipients, { + this._sendToPeers(recipients, { type: 'txProposal', txProposal: this.txProposals.get(ntxid).toObjTrim(), walletId: this.id, @@ -1195,8 +1204,7 @@ Wallet.prototype.sendTxProposal = function(ntxid, recipients) { */ Wallet.prototype.sendSeen = function(ntxid) { preconditions.checkArgument(ntxid); - log.debug('Wallet:' + this.id + ' ### SENDING seen: ' + ntxid + ' TO: All'); - this.send(null, { + this._sendToPeers(null, { type: 'seen', ntxid: ntxid, walletId: this.id, @@ -1209,8 +1217,8 @@ Wallet.prototype.sendSeen = function(ntxid) { */ Wallet.prototype.sendReject = function(ntxid) { preconditions.checkArgument(ntxid); - log.debug('Wallet:' + this.id + ' ### SENDING reject: ' + ntxid + ' TO: All'); - this.send(null, { + + this._sendToPeers(null, { type: 'reject', ntxid: ntxid, walletId: this.id, @@ -1222,9 +1230,7 @@ Wallet.prototype.sendReject = function(ntxid) { * @param {string[]} [recipients] - the pubkeys of the recipients */ Wallet.prototype.sendWalletReady = function(recipients, sinceTs) { - log.debug('Wallet:' + this.id + ' ### SENDING WalletReady TO:', recipients || 'All'); - - this.send(recipients, { + this._sendToPeers(recipients, { type: 'walletReady', walletId: this.id, sinceTs: sinceTs, @@ -1239,7 +1245,7 @@ Wallet.prototype.sendWalletReady = function(recipients, sinceTs) { Wallet.prototype.sendWalletId = function(recipients) { log.debug('Wallet:' + this.id + ' ### SENDING walletId TO:', recipients || 'All', this.id); - this.send(recipients, { + this._sendToPeers(recipients, { type: 'walletId', walletId: this.id, opts: this._optsToObj(), @@ -1252,12 +1258,11 @@ Wallet.prototype.sendWalletId = function(recipients) { * @param {string[]} [recipients] - the pubkeys of the recipients */ Wallet.prototype.sendPublicKeyRing = function(recipients) { - log.debug('Wallet:' + this.id + ' ### SENDING publicKeyRing TO:', recipients || 'All', this.publicKeyRing.toObj()); - var publicKeyRing = this.publicKeyRing.toObj(); + var publicKeyRingObj = this.publicKeyRing.toObj(); - this.send(recipients, { + this._sendToPeers(recipients, { type: 'publicKeyRing', - publicKeyRing: publicKeyRing, + publicKeyRing: publicKeyRingObj, walletId: this.id, }); }; @@ -1268,9 +1273,7 @@ Wallet.prototype.sendPublicKeyRing = function(recipients) { */ Wallet.prototype.sendIndexes = function(recipients) { var indexes = HDParams.serialize(this.publicKeyRing.indexes); - log.debug('Wallet:' + this.id + ' ### INDEXES TO:', recipients || 'All', indexes); - - this.send(recipients, { + this._sendToPeers(recipients, { type: 'indexes', indexes: indexes, walletId: this.id, @@ -1289,18 +1292,17 @@ Wallet.prototype.sendAddressBook = function(recipients, onlyKey) { var toSend = [], myId = this.getMyCopayerId(); - if (onlyKey) { - toSend = [this.addressBook[onlyKey]]; + if (onlyKey && this.addressBook[onlyKey]) { + toSend = {}; + toSend[onlyKey] = this.addressBook[onlyKey]; } else { - toSend = _.find(this.addressBook, function(key, value) { - return value.copayerId = myId; + toSend = _.filter(this.addressBook, function(entry) { + return entry.copayerId === myId; }); } if (_.isEmpty(toSend)) return; - log.debug('Wallet:' + this.id + ' ### SENDING addressBook TO:', recipients || 'All', toSend); - - this.send(recipients, { + this._sendToPeers(recipients, { type: 'addressbook', addressBook: toSend, walletId: this.id, @@ -1432,17 +1434,11 @@ Wallet.prototype.getPendingTxProposals = function() { * @return {number} the number of deleted proposals */ Wallet.prototype.purgeTxProposals = function(deleteAll) { - var m = this.txProposals.length(); - - if (deleteAll) { - this.txProposals.deleteAll(); - } else { - this.txProposals.deletePending(this.maxRejectCount()); + var deleted = this.txProposals.purge(deleteAll, this.maxRejectCount()); + if (deleted) { + this.emitAndKeepAlive('txProposalsUpdated'); } - this.emitAndKeepAlive('txProposalsUpdated'); - - var n = this.txProposals.length(); - return m - n; + return deleted; }; /** @@ -1467,34 +1463,40 @@ Wallet.prototype.reject = function(ntxid) { Wallet.prototype.sign = function(ntxid) { preconditions.checkState(!_.isUndefined(this.getMyCopayerId())); - var myId = this.getMyCopayerId(); var txp = this.txProposals.get(ntxid); - - var before = txp.countSignatures(); var keys = this.privateKey.getForPaths(txp.inputChainPaths); - txp.builder.sign(keys); - if (txp.countSignatures() <= before) { + var signaturesAdded = txp.sign(keys, this.getMyCopayerId()); + if (!signturesAdded) return false; - } - txp.signedBy[myId] = Date.now(); - this.sendTxProposal(ntxid); this.emitAndKeepAlive('txProposalsUpdated'); - return true; }; -/** - * @callback broadcastCallback - * @param {string} txid - the transaction id on the blockchain - */ + +Wallet.prototype.signAndSend = function(ntxid, cb) { + if (this.sign(ntxid)) { + var txp = w.txProposals.get(ntxid); + if (txp.isFullySigned()) { + return this.broadcastTx(ntxid, cb); + } else { + this.sendTxProposal(ntxid); + return cb(null, ntxid, Wallet.TX_SIGNED ); + } + } else { + return cb(new Error('Could not sign the proposal')); + } +}; + /** * @desc Broadcasts a transaction to the blockchain * @param {string} ntxid - the transaction proposal id * @param {broadcastCallback} cb + * @callback broadcastCallback + * @param {string} txid - the transaction id on the blockchain */ -Wallet.prototype.sendTx = function(ntxid, cb) { +Wallet.prototype.broadcastTx = function(ntxid, cb) { var self = this; var txp = this.txProposals.get(ntxid); @@ -1506,7 +1508,6 @@ Wallet.prototype.sendTx = function(ntxid, cb) { var serializedTx = tx.serialize(); - if (txp.merchant) { this.sendPaymentTx(ntxid, serializedTx); } @@ -1519,17 +1520,15 @@ Wallet.prototype.sendTx = function(ntxid, cb) { log.error('Error sending TX:', err); if (txid) { - log.debug('Wallet:' + self.getName() + ' Broadcasted TX. BITCOIND txid:', txid); + log.debug('Wallet:' + self.getName() + ' broadcasted a TX. BITCOIND txid:', txid); var txp = self.txProposals.get(ntxid); txp.setSent(txid); self.sendTxProposal(ntxid); self.emitAndKeepAlive('txProposalsUpdated'); - return cb(txid); + return cb(null, txid, Wallet.TX_BROADCASTED); } else { log.info('Wallet:' + self.getName() + '. Sent failed. Checking if the TX was sent already'); - self._processTxProposalSent(ntxid, function(err, txid) { - return cb(txid); - }); + self._checkIfTxProposalIsSent(ntxid, cb); } }); }; @@ -2062,9 +2061,11 @@ Wallet.prototype.removeTxWithSpentInputs = function(cb) { }; /** - * @desc Create a transaction proposal, and run many sanity checks + * @desc Spends coins from the wallet + * Create a Transaction Proposal and broadcast it or send it + * to copayers */ -Wallet.prototype.createTx = function(opts, cb) { +Wallet.prototype.spend = function(opts, cb) { preconditions.checkArgument(_.isObject(opts)); log.debug('create Options', opts); @@ -2098,7 +2099,8 @@ Wallet.prototype.createTx = function(opts, cb) { var ntxid, txp; try { - txp = self.createTxProposal(toAddress, amountSat, comment, safeUnspent, opts.builderOpts); + txp = self._createTxProposal(toAddress, + amountSat, comment, safeUnspent, opts.builderOpts); } catch (e) { log.error(e); return cb(e); @@ -2109,17 +2111,21 @@ Wallet.prototype.createTx = function(opts, cb) { } var ntxid = self.txProposals.add(txp); - log.debug('TXP Added: ', ntxid); - - if (!ntxid) { return cb(new Error('Error creating the transaction')); } + log.debug('TXP Added: ', ntxid); + self.sendIndexes(); - self.sendTxProposal(ntxid); - self.emitAndKeepAlive('txProposalsUpdated'); - return cb(null, ntxid); + // Needs only one signature? Broadcast it! + if (!self.requiresMultipleSignatures()) { + self.broadcastTx(ntxid, cb); + } else { + self.sendTxProposal(ntxid); + self.emitAndKeepAlive('txProposalsUpdated'); + return cb(null, ntxid, Wallet.TX_PROPOSAL_SENT); + } }); }; @@ -2159,7 +2165,7 @@ Wallet.prototype._getBuilder = function(opts) { /* - * createTxProposal + * _createTxProposal * Creates a transaction proposal and run many sanity checks * * @param toAddress @@ -2171,7 +2177,7 @@ Wallet.prototype._getBuilder = function(opts) { * Throws errors on unexpected inputs. */ -Wallet.prototype.createTxProposal = function(toAddress, amountSat, comment, utxos, builderOpts) { +Wallet.prototype._createTxProposal = function(toAddress, amountSat, comment, utxos, builderOpts) { preconditions.checkArgument(toAddress); preconditions.checkArgument(amountSat); preconditions.checkArgument(_.isArray(utxos)); @@ -2234,7 +2240,6 @@ Wallet.prototype.createTxProposal = function(toAddress, amountSat, comment, utxo * @desc Updates all the indexes for the current publicKeyRing. This scans * the blockchain looking for transactions on derived addresses. * - * Triggers a wallet {@link Wallet#store} call * @param {Function} callback - called when all indexes have been updated. Receives an error, if any, as first argument */ Wallet.prototype.updateIndexes = function(callback) { @@ -2380,7 +2385,7 @@ Wallet.prototype.getNetwork = function() { */ Wallet.prototype._checkAddressBook = function(key) { if (this.addressBook[key] && this.addressBook[key].copayerId != -1) { - throw new Error('This address already exists in your Address Book: ' + address); + throw new Error('This address already exists in your Address Book'); } };