rm networking on nonShared wallets

This commit is contained in:
Matias Alejo Garcia 2014-11-23 15:52:39 -03:00
commit a7de2ababd
6 changed files with 179 additions and 153 deletions

View file

@ -24,6 +24,7 @@ angular.module('copayApp.controllers').controller('SendController',
$scope.isRateAvailable = false; $scope.isRateAvailable = false;
$scope.rateService = rateService; $scope.rateService = rateService;
$scope.showScanner = false;
rateService.whenAvailable(function() { rateService.whenAvailable(function() {
@ -105,33 +106,19 @@ angular.module('copayApp.controllers').controller('SendController',
var msg = err.toString(); var msg = err.toString();
if (msg.match('BIG')) 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')) 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.error = message;
$scope.loading = false; $scope.loading = false;
$scope.loadTxs(); $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) { $scope.submitForm = function(form) {
if (form.$invalid) { if (form.$invalid) {
$scope.error = 'Unable to send transaction proposal'; $scope.error = 'Unable to send transaction proposal';
@ -160,16 +147,21 @@ angular.module('copayApp.controllers').controller('SendController',
merchant: $rootScope.merchant.request_url merchant: $rootScope.merchant.request_url
}; };
} }
// reset fields w.spend({
$scope.address = $scope.amount = $scope.commentText = null;
form.address.$pristine = form.amount.$pristine = true;
w.createTx({
toAddress: address, toAddress: address,
amountSat: amount, amountSat: amount,
comment: commentText, comment: commentText,
url: (payInfo && payInfo.merchant) ? payInfo.merchant : null, 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 // QR code Scanner
@ -267,6 +259,7 @@ angular.module('copayApp.controllers').controller('SendController',
$scope.openScanner = function() { $scope.openScanner = function() {
if (window.cordova) return $scope.scannerIntent(); if (window.cordova) return $scope.scannerIntent();
console.log('[send.js.260] OPENN'); //TODO
$scope.showScanner = true; $scope.showScanner = true;
// Wait a moment until the canvas shows // Wait a moment until the canvas shows
@ -371,22 +364,24 @@ angular.module('copayApp.controllers').controller('SendController',
$scope.amount = $rootScope.topAmount; $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.send = function(ntxid, cb) {
$scope.error = $scope.success = null; $scope.error = $scope.success = null;
$scope.loading = true; $scope.loading = true;
$rootScope.txAlertCount = 0; $rootScope.txAlertCount = 0;
w.sendTx(ntxid, function(txid, merchantData) { w.broadcastTx(ntxid, function(err, txid, status) {
if (!txid) { $scope.notifyStatus(status);
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);
}
}
if (cb) return cb(); if (cb) return cb();
else $scope.loadTxs(); else $scope.loadTxs();
}); });
@ -396,20 +391,10 @@ angular.module('copayApp.controllers').controller('SendController',
$scope.loading = true; $scope.loading = true;
$scope.error = $scope.success = null; $scope.error = $scope.success = null;
try { w.signAndSend(ntxid, function(err, id, status) {
w.sign(ntxid); $scope.notifyStatus(status);
} catch (e) {
notification.error('Error', 'There was an error signing the transaction');
$scope.loadTxs(); $scope.loadTxs();
return; });
}
var p = w.txProposals.getTxProposal(ntxid);
if (p.builder.isFullySigned()) {
$scope.send(ntxid);
} else {
$scope.loadTxs();
}
}; };
$scope.reject = function(ntxid) { $scope.reject = function(ntxid) {
@ -417,7 +402,6 @@ angular.module('copayApp.controllers').controller('SendController',
$rootScope.txAlertCount = 0; $rootScope.txAlertCount = 0;
w.reject(ntxid); w.reject(ntxid);
notification.warning('Transaction rejected', 'You rejected the transaction successfully'); notification.warning('Transaction rejected', 'You rejected the transaction successfully');
$scope.loading = false;
$scope.loadTxs(); $scope.loadTxs();
}; };
@ -510,7 +494,9 @@ angular.module('copayApp.controllers').controller('SendController',
}, 10 * 1000); }, 10 * 1000);
// Payment Protocol URI (BIP-72) // 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; if (!timeout) return;
clearTimeout(timeout); clearTimeout(timeout);

View file

@ -207,7 +207,9 @@ Network.prototype._onMessage = function(enc) {
log.debug('Ignoring trailing message. Ts:', enc.ts); log.debug('Ignoring trailing message. Ts:', enc.ts);
return; return;
} }
log.debug('Async: receiving ' + JSON.stringify(payload));
log.info('Network message: ', payload.type);
log.debug('Message payload:', payload);
var self = this; var self = this;
switch (payload.type) { switch (payload.type) {
@ -239,6 +241,7 @@ Network.prototype._onMessage = function(enc) {
Network.prototype._setupConnectionHandlers = function(opts, cb) { Network.prototype._setupConnectionHandlers = function(opts, cb) {
preconditions.checkState(this.socket); preconditions.checkState(this.socket);
log.debug('setting up connection', opts);
var self = this; var self = this;
self.socket.on('connect_error', function(m) { self.socket.on('connect_error', function(m) {
@ -260,7 +263,7 @@ Network.prototype._setupConnectionHandlers = function(opts, cb) {
if (fromTs) { if (fromTs) {
self.ignoreMessageFromTs = fromTs; self.ignoreMessageFromTs = fromTs;
} }
log.info('Async: synchronizing from: ',fromTs); log.info('Async: syncing from: ', fromTs);
self.socket.emit('sync', fromTs); self.socket.emit('sync', fromTs);
self.started = true; self.started = true;
}); });
@ -398,8 +401,6 @@ Network.prototype.send = function(dest, payload, cb) {
if (to == this.copayerId) if (to == this.copayerId)
continue; continue;
log.debug('SEND to: ' + to, this.copayerId, JSON.stringify(payload));
var message = this.encode(to, payload); var message = this.encode(to, payload);
this.socket.emit('message', message); this.socket.emit('message', message);
} }

View file

@ -224,8 +224,6 @@ Insight.prototype.subscribe = function(addresses) {
s.emit('subscribe', address); s.emit('subscribe', address);
s.on(address, handler); s.on(address, handler);
} else {
log.debug('Already subcribed to: ', address);
} }
}); });
}; };

View file

@ -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() { TxProposal.prototype._check = function() {
if (this.builder.signhash && this.builder.signhash !== Transaction.SIGHASH_ALL) { if (this.builder.signhash && this.builder.signhash !== Transaction.SIGHASH_ALL) {

View file

@ -205,4 +205,22 @@ TxProposals.prototype.getUsedUnspent = function(maxRejectCount) {
return ret; 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; module.exports = TxProposals;

View file

@ -33,6 +33,8 @@ var copayConfig = require('../../config');
var TX_MAX_SIZE_KB = 50; var TX_MAX_SIZE_KB = 50;
var TX_MAX_INS = 70; var TX_MAX_INS = 70;
/** /**
* @desc * @desc
* Wallet manages a private key for Copay, network, storage of the wallet for * Wallet manages a private key for Copay, network, storage of the wallet for
@ -119,6 +121,11 @@ function Wallet(opts) {
inherits(Wallet, events.EventEmitter); inherits(Wallet, events.EventEmitter);
Wallet.TX_BROADCASTED = 'txBroadcasted';
Wallet.TX_PROPOSAL_SENT = 'txProposalSent';
Wallet.TX_SIGNED = 'txSigned';
Wallet.prototype.emitAndKeepAlive = function(args) { Wallet.prototype.emitAndKeepAlive = function(args) {
var args = Array.prototype.slice.call(arguments); var args = Array.prototype.slice.call(arguments);
log.debug('Wallet Emitting:', args); 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 * Processes the data using {@link HDParams#fromList} and merges it with the
* {@link Wallet#publicKeyRing}. * {@link Wallet#publicKeyRing}.
* *
* Triggers a {@link Wallet#store} if the internal state has changed.
*
* @param {string} senderId - the sender id * @param {string} senderId - the sender id
* @param {Object} data - the data recived, {@see HDParams#fromList} * @param {Object} data - the data recived, {@see HDParams#fromList}
*/ */
Wallet.prototype._onIndexes = function(senderId, data) { Wallet.prototype._onIndexes = function(senderId, data) {
log.debug('Wallet:' + this.id + ' RECV INDEXES:', data);
var inIndexes = HDParams.fromList(data.indexes); var inIndexes = HDParams.fromList(data.indexes);
var hasChanged = this.publicKeyRing.mergeIndexes(inIndexes); var hasChanged = this.publicKeyRing.mergeIndexes(inIndexes);
if (hasChanged) { 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 self = this;
var txp = this.txProposals.get(ntxid); var txp = this.txProposals.get(ntxid);
@ -459,7 +464,7 @@ Wallet.prototype._processTxProposalSent = function(ntxid, cb) {
} }
} }
self.emitAndKeepAlive('txProposalsUpdated'); 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(); var tx = mergeInfo.txp.builder.build();
if (tx.isComplete()) if (tx.isComplete())
self._processTxProposalSent(mergeInfo.ntxid); self._checkIfTxProposalIsSent(mergeInfo.ntxid);
else if (mergeInfo.hasChanged) { else {
self.sendTxProposal(mergeInfo.ntxid);
self.emitAndKeepAlive('txProposalsUpdated'); self.emitAndKeepAlive('txProposalsUpdated');
} }
return cb(); return cb();
@ -521,9 +525,9 @@ Wallet.prototype._processIncomingTxProposal = function(mergeInfo, cb) {
*/ */
Wallet.prototype._onTxProposal = function(senderId, data) { Wallet.prototype._onTxProposal = function(senderId, data) {
var self = this; var self = this;
log.debug('Wallet:' + this.id + ' RECV TXPROPOSAL: ', data);
var m; var m;
console.log('[Wallet.js.533]a', this.txProposals.txps); //TODO
try { try {
m = this.txProposals.merge(data.txProposal, Wallet.builderOpts); m = this.txProposals.merge(data.txProposal, Wallet.builderOpts);
var keyMap = this._getKeyMap(m.txp); var keyMap = this._getKeyMap(m.txp);
@ -534,8 +538,9 @@ Wallet.prototype._onTxProposal = function(senderId, data) {
this.txProposals.deleteOne(m.ntxid); this.txProposals.deleteOne(m.ntxid);
m = null; m = null;
} }
console.log('[Wallet.js.533]a', this.txProposals.txps); //TODO
self._processIncomingTxProposal(m, function(err) { this._processIncomingTxProposal(m, function(err) {
if (err) { if (err) {
log.error('Corrupt TX proposal received from:', senderId, err.toString()); log.error('Corrupt TX proposal received from:', senderId, err.toString());
if (m && m.ntxid) if (m && m.ntxid)
@ -588,8 +593,6 @@ Wallet.prototype._onReject = function(senderId, data) {
*/ */
Wallet.prototype._onSeen = function(senderId, data) { Wallet.prototype._onSeen = function(senderId, data) {
preconditions.checkState(data.ntxid); preconditions.checkState(data.ntxid);
log.debug('Wallet:' + this.id + ' RECV SEEN:', data);
var txp = this.txProposals.get(data.ntxid); var txp = this.txProposals.get(data.ntxid);
txp.setSeen(senderId); txp.setSeen(senderId);
this.emitAndKeepAlive('txProposalEvent', { this.emitAndKeepAlive('txProposalEvent', {
@ -611,14 +614,12 @@ Wallet.prototype._onSeen = function(senderId, data) {
* @emits txProposalEvent * @emits txProposalEvent
*/ */
Wallet.prototype._onAddressBook = function(senderId, data) { Wallet.prototype._onAddressBook = function(senderId, data) {
preconditions.checkState(data.addressBook); if (!data.addressBook || !_.isArray(data.addressBook))
log.debug('Wallet:' + this.id + ' RECV ADDRESSBOOK:', data); return;
var rcv = data.addressBook; var self = this, hasChange;
console.log('[Wallet.js.618:rcv:]', rcv); //TODO
var hasChange, self = this; _.each(data.addressBook, function(value, key) {
_.each(rcv, function(value, key) {
if (!self.addressBook[key] && Address.validate(key)) { if (!self.addressBook[key] && Address.validate(key)) {
self.addressBook[key] = _.pick(value, ['createdTs', 'label']); self.addressBook[key] = _.pick(value, ['createdTs', 'label']);
@ -660,8 +661,6 @@ Wallet.prototype.updateSyncedTimestamp = function(ts) {
* Triggers a call to {@link Wallet#sendWalletReady} * Triggers a call to {@link Wallet#sendWalletReady}
*/ */
Wallet.prototype._onNoMessages = function() { 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)); 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)); this.sendWalletReady(null, parseInt((this.syncedTimestamp + 1) / 1000000));
}; };
@ -676,13 +675,14 @@ Wallet.prototype._onNoMessages = function() {
* @emits corrupt * @emits corrupt
*/ */
Wallet.prototype._onData = function(senderId, data, ts) { Wallet.prototype._onData = function(senderId, data, ts) {
console.log('[Wallet.js.533]0', this.txProposals.txps); //TODO
preconditions.checkArgument(senderId); preconditions.checkArgument(senderId);
preconditions.checkArgument(data); preconditions.checkArgument(data);
preconditions.checkArgument(data.type); preconditions.checkArgument(data.type);
preconditions.checkArgument(ts); preconditions.checkArgument(ts);
preconditions.checkArgument(_.isNumber(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); this.updateSyncedTimestamp(ts);
@ -917,8 +917,13 @@ Wallet.prototype._setBlockchainListeners = function() {
*/ */
Wallet.prototype.netStart = function() { Wallet.prototype.netStart = function() {
var self = this; var self = this;
var net = this.network;
if (!this.isShared()) {
self.emitAndKeepAlive('ready');
return;
}
var net = this.network;
net.removeAllListeners(); net.removeAllListeners();
net.on('connect', self._onConnect.bind(self)); net.on('connect', self._onConnect.bind(self));
net.on('data', self._onData.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() { net.start(startOpts, function() {
self._setBlockchainListeners(); self._setBlockchainListeners();
self.emitAndKeepAlive('ready', net.getPeer()); 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 {string[]} recipients - the pubkey of the recipients of the message
* @param {Object} obj - the data to be sent to them * @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); this.network.send(recipients, obj);
}; };
@ -1181,8 +1191,7 @@ Wallet.prototype.sendAllTxProposals = function(recipients, sinceTs) {
*/ */
Wallet.prototype.sendTxProposal = function(ntxid, recipients) { Wallet.prototype.sendTxProposal = function(ntxid, recipients) {
preconditions.checkArgument(ntxid); preconditions.checkArgument(ntxid);
log.debug('Wallet:' + this.id + ' ### SENDING txProposal ' + ntxid + ' TO:', recipients || 'All', this.txProposals); this._sendToPeers(recipients, {
this.send(recipients, {
type: 'txProposal', type: 'txProposal',
txProposal: this.txProposals.get(ntxid).toObjTrim(), txProposal: this.txProposals.get(ntxid).toObjTrim(),
walletId: this.id, walletId: this.id,
@ -1195,8 +1204,7 @@ Wallet.prototype.sendTxProposal = function(ntxid, recipients) {
*/ */
Wallet.prototype.sendSeen = function(ntxid) { Wallet.prototype.sendSeen = function(ntxid) {
preconditions.checkArgument(ntxid); preconditions.checkArgument(ntxid);
log.debug('Wallet:' + this.id + ' ### SENDING seen: ' + ntxid + ' TO: All'); this._sendToPeers(null, {
this.send(null, {
type: 'seen', type: 'seen',
ntxid: ntxid, ntxid: ntxid,
walletId: this.id, walletId: this.id,
@ -1209,8 +1217,8 @@ Wallet.prototype.sendSeen = function(ntxid) {
*/ */
Wallet.prototype.sendReject = function(ntxid) { Wallet.prototype.sendReject = function(ntxid) {
preconditions.checkArgument(ntxid); preconditions.checkArgument(ntxid);
log.debug('Wallet:' + this.id + ' ### SENDING reject: ' + ntxid + ' TO: All');
this.send(null, { this._sendToPeers(null, {
type: 'reject', type: 'reject',
ntxid: ntxid, ntxid: ntxid,
walletId: this.id, walletId: this.id,
@ -1222,9 +1230,7 @@ Wallet.prototype.sendReject = function(ntxid) {
* @param {string[]} [recipients] - the pubkeys of the recipients * @param {string[]} [recipients] - the pubkeys of the recipients
*/ */
Wallet.prototype.sendWalletReady = function(recipients, sinceTs) { Wallet.prototype.sendWalletReady = function(recipients, sinceTs) {
log.debug('Wallet:' + this.id + ' ### SENDING WalletReady TO:', recipients || 'All'); this._sendToPeers(recipients, {
this.send(recipients, {
type: 'walletReady', type: 'walletReady',
walletId: this.id, walletId: this.id,
sinceTs: sinceTs, sinceTs: sinceTs,
@ -1239,7 +1245,7 @@ Wallet.prototype.sendWalletReady = function(recipients, sinceTs) {
Wallet.prototype.sendWalletId = function(recipients) { Wallet.prototype.sendWalletId = function(recipients) {
log.debug('Wallet:' + this.id + ' ### SENDING walletId TO:', recipients || 'All', this.id); log.debug('Wallet:' + this.id + ' ### SENDING walletId TO:', recipients || 'All', this.id);
this.send(recipients, { this._sendToPeers(recipients, {
type: 'walletId', type: 'walletId',
walletId: this.id, walletId: this.id,
opts: this._optsToObj(), opts: this._optsToObj(),
@ -1252,12 +1258,11 @@ Wallet.prototype.sendWalletId = function(recipients) {
* @param {string[]} [recipients] - the pubkeys of the recipients * @param {string[]} [recipients] - the pubkeys of the recipients
*/ */
Wallet.prototype.sendPublicKeyRing = function(recipients) { Wallet.prototype.sendPublicKeyRing = function(recipients) {
log.debug('Wallet:' + this.id + ' ### SENDING publicKeyRing TO:', recipients || 'All', this.publicKeyRing.toObj()); var publicKeyRingObj = this.publicKeyRing.toObj();
var publicKeyRing = this.publicKeyRing.toObj();
this.send(recipients, { this._sendToPeers(recipients, {
type: 'publicKeyRing', type: 'publicKeyRing',
publicKeyRing: publicKeyRing, publicKeyRing: publicKeyRingObj,
walletId: this.id, walletId: this.id,
}); });
}; };
@ -1268,9 +1273,7 @@ Wallet.prototype.sendPublicKeyRing = function(recipients) {
*/ */
Wallet.prototype.sendIndexes = function(recipients) { Wallet.prototype.sendIndexes = function(recipients) {
var indexes = HDParams.serialize(this.publicKeyRing.indexes); var indexes = HDParams.serialize(this.publicKeyRing.indexes);
log.debug('Wallet:' + this.id + ' ### INDEXES TO:', recipients || 'All', indexes); this._sendToPeers(recipients, {
this.send(recipients, {
type: 'indexes', type: 'indexes',
indexes: indexes, indexes: indexes,
walletId: this.id, walletId: this.id,
@ -1289,18 +1292,17 @@ Wallet.prototype.sendAddressBook = function(recipients, onlyKey) {
var toSend = [], var toSend = [],
myId = this.getMyCopayerId(); myId = this.getMyCopayerId();
if (onlyKey) { if (onlyKey && this.addressBook[onlyKey]) {
toSend = [this.addressBook[onlyKey]]; toSend = {};
toSend[onlyKey] = this.addressBook[onlyKey];
} else { } else {
toSend = _.find(this.addressBook, function(key, value) { toSend = _.filter(this.addressBook, function(entry) {
return value.copayerId = myId; return entry.copayerId === myId;
}); });
} }
if (_.isEmpty(toSend)) return; if (_.isEmpty(toSend)) return;
log.debug('Wallet:' + this.id + ' ### SENDING addressBook TO:', recipients || 'All', toSend); this._sendToPeers(recipients, {
this.send(recipients, {
type: 'addressbook', type: 'addressbook',
addressBook: toSend, addressBook: toSend,
walletId: this.id, walletId: this.id,
@ -1432,17 +1434,11 @@ Wallet.prototype.getPendingTxProposals = function() {
* @return {number} the number of deleted proposals * @return {number} the number of deleted proposals
*/ */
Wallet.prototype.purgeTxProposals = function(deleteAll) { Wallet.prototype.purgeTxProposals = function(deleteAll) {
var m = this.txProposals.length(); var deleted = this.txProposals.purge(deleteAll, this.maxRejectCount());
if (deleted) {
if (deleteAll) { this.emitAndKeepAlive('txProposalsUpdated');
this.txProposals.deleteAll();
} else {
this.txProposals.deletePending(this.maxRejectCount());
} }
this.emitAndKeepAlive('txProposalsUpdated'); return deleted;
var n = this.txProposals.length();
return m - n;
}; };
/** /**
@ -1467,34 +1463,40 @@ Wallet.prototype.reject = function(ntxid) {
Wallet.prototype.sign = function(ntxid) { Wallet.prototype.sign = function(ntxid) {
preconditions.checkState(!_.isUndefined(this.getMyCopayerId())); preconditions.checkState(!_.isUndefined(this.getMyCopayerId()));
var myId = this.getMyCopayerId();
var txp = this.txProposals.get(ntxid); var txp = this.txProposals.get(ntxid);
var before = txp.countSignatures();
var keys = this.privateKey.getForPaths(txp.inputChainPaths); 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; return false;
}
txp.signedBy[myId] = Date.now();
this.sendTxProposal(ntxid);
this.emitAndKeepAlive('txProposalsUpdated'); this.emitAndKeepAlive('txProposalsUpdated');
return true; return true;
}; };
/**
* @callback broadcastCallback Wallet.prototype.signAndSend = function(ntxid, cb) {
* @param {string} txid - the transaction id on the blockchain 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 * @desc Broadcasts a transaction to the blockchain
* @param {string} ntxid - the transaction proposal id * @param {string} ntxid - the transaction proposal id
* @param {broadcastCallback} cb * @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 self = this;
var txp = this.txProposals.get(ntxid); var txp = this.txProposals.get(ntxid);
@ -1506,7 +1508,6 @@ Wallet.prototype.sendTx = function(ntxid, cb) {
var serializedTx = tx.serialize(); var serializedTx = tx.serialize();
if (txp.merchant) { if (txp.merchant) {
this.sendPaymentTx(ntxid, serializedTx); this.sendPaymentTx(ntxid, serializedTx);
} }
@ -1519,17 +1520,15 @@ Wallet.prototype.sendTx = function(ntxid, cb) {
log.error('Error sending TX:', err); log.error('Error sending TX:', err);
if (txid) { 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); var txp = self.txProposals.get(ntxid);
txp.setSent(txid); txp.setSent(txid);
self.sendTxProposal(ntxid); self.sendTxProposal(ntxid);
self.emitAndKeepAlive('txProposalsUpdated'); self.emitAndKeepAlive('txProposalsUpdated');
return cb(txid); return cb(null, txid, Wallet.TX_BROADCASTED);
} else { } else {
log.info('Wallet:' + self.getName() + '. Sent failed. Checking if the TX was sent already'); log.info('Wallet:' + self.getName() + '. Sent failed. Checking if the TX was sent already');
self._processTxProposalSent(ntxid, function(err, txid) { self._checkIfTxProposalIsSent(ntxid, cb);
return cb(txid);
});
} }
}); });
}; };
@ -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)); preconditions.checkArgument(_.isObject(opts));
log.debug('create Options', opts); log.debug('create Options', opts);
@ -2098,7 +2099,8 @@ Wallet.prototype.createTx = function(opts, cb) {
var ntxid, txp; var ntxid, txp;
try { try {
txp = self.createTxProposal(toAddress, amountSat, comment, safeUnspent, opts.builderOpts); txp = self._createTxProposal(toAddress,
amountSat, comment, safeUnspent, opts.builderOpts);
} catch (e) { } catch (e) {
log.error(e); log.error(e);
return cb(e); return cb(e);
@ -2109,17 +2111,21 @@ Wallet.prototype.createTx = function(opts, cb) {
} }
var ntxid = self.txProposals.add(txp); var ntxid = self.txProposals.add(txp);
log.debug('TXP Added: ', ntxid);
if (!ntxid) { if (!ntxid) {
return cb(new Error('Error creating the transaction')); return cb(new Error('Error creating the transaction'));
} }
log.debug('TXP Added: ', ntxid);
self.sendIndexes(); self.sendIndexes();
self.sendTxProposal(ntxid); // Needs only one signature? Broadcast it!
self.emitAndKeepAlive('txProposalsUpdated'); if (!self.requiresMultipleSignatures()) {
return cb(null, ntxid); 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 * Creates a transaction proposal and run many sanity checks
* *
* @param toAddress * @param toAddress
@ -2171,7 +2177,7 @@ Wallet.prototype._getBuilder = function(opts) {
* Throws errors on unexpected inputs. * 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(toAddress);
preconditions.checkArgument(amountSat); preconditions.checkArgument(amountSat);
preconditions.checkArgument(_.isArray(utxos)); 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 * @desc Updates all the indexes for the current publicKeyRing. This scans
* the blockchain looking for transactions on derived addresses. * 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 * @param {Function} callback - called when all indexes have been updated. Receives an error, if any, as first argument
*/ */
Wallet.prototype.updateIndexes = function(callback) { Wallet.prototype.updateIndexes = function(callback) {
@ -2380,7 +2385,7 @@ Wallet.prototype.getNetwork = function() {
*/ */
Wallet.prototype._checkAddressBook = function(key) { Wallet.prototype._checkAddressBook = function(key) {
if (this.addressBook[key] && this.addressBook[key].copayerId != -1) { 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');
} }
}; };