Merge pull request #1740 from cmgustavo/bug/tx-proposals

Refresh list of pending transactions proposals after any events
This commit is contained in:
Esteban Ordano 2014-11-07 15:16:36 -03:00
commit d475ea4f16
7 changed files with 113 additions and 130 deletions

View file

@ -16,24 +16,10 @@ angular.module('copayApp.controllers').controller('HistoryController',
$scope.blockchain_txs = []; $scope.blockchain_txs = [];
$scope.alternativeCurrency = []; $scope.alternativeCurrency = [];
var satToUnit = 1 / w.settings.unitToSatoshi;
$scope.update = function() { $scope.update = function() {
$scope.loading = true; $scope.getTransactions();
var from = ($scope.txpCurrentPage - 1) * $scope.txpItemsPerPage;
var opts = {
pending: false,
skip: [from, from + $scope.txpItemsPerPage]
};
controllerUtils.updateTxs(opts);
setTimeout(function() {
$rootScope.$digest();
}, 0);
}; };
$scope.show = function() { $scope.show = function() {
$scope.loading = true; $scope.loading = true;
setTimeout(function() { setTimeout(function() {
@ -61,9 +47,6 @@ angular.module('copayApp.controllers').controller('HistoryController',
_.each(res, function(r) { _.each(res, function(r) {
r.ts = r.minedTs || r.sentTs; r.ts = r.minedTs || r.sentTs;
if (r.action === 'sent' && r.peerActions) {
r.actionList = controllerUtils.getActionList(r.peerActions);
}
}); });
$scope.blockchain_txs = w.cached_txs = res; $scope.blockchain_txs = w.cached_txs = res;
$scope.loading = false; $scope.loading = false;
@ -76,13 +59,11 @@ angular.module('copayApp.controllers').controller('HistoryController',
$scope.hasAction = function(actions, action) { $scope.hasAction = function(actions, action) {
return actions.hasOwnProperty('create'); return actions.hasOwnProperty('create');
} };
$scope.getShortNetworkName = function() { $scope.getShortNetworkName = function() {
var w = $rootScope.wallet; var w = $rootScope.wallet;
return w.getNetworkName().substring(0, 4); return w.getNetworkName().substring(0, 4);
}; };
// Autoload transactions
$scope.getTransactions();
}); });

View file

@ -72,11 +72,8 @@ angular.module('copayApp.controllers').controller('SendController',
}); });
$scope.loadTxs = function() { $scope.loadTxs = function() {
var opts = { controllerUtils.updateTxs();
pending: true,
skip: null
};
controllerUtils.updateTxs(opts);
setTimeout(function() { setTimeout(function() {
$scope.loading = false; $scope.loading = false;
$rootScope.$digest(); $rootScope.$digest();

View file

@ -72,10 +72,6 @@ angular.module('copayApp.controllers').controller('SidebarController', function(
if (controllerUtils.isFocusedWallet(wid)) return; if (controllerUtils.isFocusedWallet(wid)) return;
var w = $rootScope.iden.getWalletById(wid); var w = $rootScope.iden.getWalletById(wid);
$scope.wallets.push(w); $scope.wallets.push(w);
controllerUtils.updateTxs({
wallet: w,
pending: true
});
controllerUtils.updateBalance(w, function() { controllerUtils.updateBalance(w, function() {
$rootScope.$digest(); $rootScope.$digest();
}) })

View file

@ -361,7 +361,7 @@ Wallet.prototype._processProposalEvents = function(senderId, m) {
} else { } else {
ev = { ev = {
type: 'corrupt', type: 'corrupt',
cId: senderId, cId: senderId
}; };
} }
if (ev) if (ev)
@ -513,7 +513,7 @@ Wallet.prototype._onReject = function(senderId, data) {
this.emitAndKeepAlive('txProposalEvent', { this.emitAndKeepAlive('txProposalEvent', {
type: 'rejected', type: 'rejected',
cId: senderId, cId: senderId,
txId: data.ntxid, txId: data.ntxid
}); });
}; };
@ -536,7 +536,7 @@ Wallet.prototype._onSeen = function(senderId, data) {
this.emitAndKeepAlive('txProposalEvent', { this.emitAndKeepAlive('txProposalEvent', {
type: 'seen', type: 'seen',
cId: senderId, cId: senderId,
txId: data.ntxid, txId: data.ntxid
}); });
}; };
@ -1281,6 +1281,64 @@ Wallet.prototype.getTxProposals = function() {
return ret; return ret;
}; };
/**
* @desc get list of actions (see {@link getPendingTxProposals})
*/
Wallet.prototype._getActionList = function(actions) {
if (!actions) return;
var peers = Object.keys(actions).map(function(i) {
return {
cId: i,
actions: actions[i]
}
});
return peers.sort(function(a, b) {
return !!b.actions.create - !!a.actions.create;
});
};
/**
* @desc Retrieve Pendings Transaction proposals (see {@link TxProposals})
* @return {Object[]} each object returned represents a transaction proposal
*/
Wallet.prototype.getPendingTxProposals = function() {
var that = this;
var ret = [];
ret.txs = [];
var pendingForUs = 0;
var txps = this.getTxProposals();
var satToUnit = 1 / this.settings.unitToSatoshi;
_.find(txps, function(txp) {
if (txp.isPending) {
pendingForUs++;
var tx = txp.builder.build();
var outs = [];
tx.outs.forEach(function(o) {
var addr = bitcore.Address.fromScriptPubKey(o.getScript(), that.getNetworkName())[0].toString();
if (!that.addressIsOwn(addr, {
excludeMain: true
})) {
outs.push({
address: addr,
value: bitcore.util.valueToBigInt(o.getValue()) * satToUnit,
});
}
});
// extra fields
txp.outs = outs;
txp.fee = txp.builder.feeSat * satToUnit;
txp.missingSignatures = tx.countInputMissingSignatures(0);
txp.actionList = that._getActionList(txp.peerActions);
ret.txs.push(txp);
}
});
ret.pendingForUs = pendingForUs;
return ret;
};
/** /**
* @desc Removes old transactions * @desc Removes old transactions
* @param {boolean} deleteAll - if true, remove all the transactions * @param {boolean} deleteAll - if true, remove all the transactions
@ -2882,6 +2940,13 @@ Wallet.prototype.getTransactionHistory = function(cb) {
tx.merchant = proposal.merchant; tx.merchant = proposal.merchant;
tx.peerActions = proposal.peerActions; tx.peerActions = proposal.peerActions;
tx.finallyRejected = proposal.finallyRejected; tx.finallyRejected = proposal.finallyRejected;
tx.merchant = proposal.merchant;
tx.peerActions = proposal.peerActions;
tx.finallyRejected = proposal.finallyRejected;
if (tx.peerActions) {
tx.actionList = self._getActionList(tx.peerActions);
}
} }
}; };

View file

@ -63,13 +63,10 @@ angular.module('copayApp.services')
root.updateTxsAndBalance = _.debounce(function(w) { root.updateTxsAndBalance = _.debounce(function(w) {
root.updateTxs({ root.updateTxs();
wallet: w,
pending: true,
});
root.updateBalance(w, function() { root.updateBalance(w, function() {
$rootScope.$digest(); $rootScope.$digest();
}) });
}, 3000); }, 3000);
root.installWalletHandlers = function($scope, w) { root.installWalletHandlers = function($scope, w) {
@ -139,19 +136,29 @@ angular.module('copayApp.services')
w.on('txProposalEvent', function(e) { w.on('txProposalEvent', function(e) {
root.updateTxsAndBalance(w);
// TODO: add wallet name notification // TODO: add wallet name notification
var user = w.publicKeyRing.nicknameForCopayer(e.cId); var user = w.publicKeyRing.nicknameForCopayer(e.cId);
var name = w.getName();
switch (e.type) { switch (e.type) {
case 'new':
notification.info('['+ name +'] New Transaction',
$filter('translate')('You received a transaction proposal from') + ' ' + user);
break;
case 'signed': case 'signed':
notification.info('Transaction Update', $filter('translate')('A transaction was signed by') + ' ' + user); notification.info('['+ name +'] Transaction Signed',
$filter('translate')('A transaction was signed by') + ' ' + user);
break; break;
case 'rejected': case 'rejected':
notification.info('Transaction Update', $filter('translate')('A transaction was rejected by') + ' ' + user); notification.info('['+ name +'] Transaction Rejected',
$filter('translate')('A transaction was rejected by') + ' ' + user);
break; break;
case 'corrupt': case 'corrupt':
notification.error('Transaction Error', $filter('translate')('Received corrupt transaction from') + ' ' + user); notification.error('['+ name +'] Transaction Error',
$filter('translate')('Received corrupt transaction from') + ' ' + user);
break; break;
} }
$rootScope.$digest();
}); });
w.on('addressBookUpdated', function(dontDigest) { w.on('addressBookUpdated', function(dontDigest) {
if (root.isFocusedWallet(wid)) { if (root.isFocusedWallet(wid)) {
@ -210,6 +217,7 @@ angular.module('copayApp.services')
$rootScope.wallet = w; $rootScope.wallet = w;
w.updateFocusedTimestamp(Date.now()); w.updateFocusedTimestamp(Date.now());
root.redirIfLogged(); root.redirIfLogged();
root.updateTxs();
root.updateBalance(w, function() { root.updateBalance(w, function() {
$rootScope.$digest(); $rootScope.$digest();
}) })
@ -323,75 +331,26 @@ angular.module('copayApp.services')
}); });
}; };
root.updateTxs = function(opts) { root.computeAlternativeAmount = function(w, tx, cb) {
function computeAlternativeAmount(w, tx, cb) { rateService.whenAvailable(function() {
rateService.whenAvailable(function() { _.each(tx.outs, function(out) {
_.each(tx.outs, function(out) { var valueSat = out.value * w.settings.unitToSatoshi;
var valueSat = out.value * w.settings.unitToSatoshi; out.alternativeAmount = rateService.toFiat(valueSat, w.settings.alternativeIsoCode);
out.alternativeAmount = rateService.toFiat(valueSat, w.settings.alternativeIsoCode); out.alternativeIsoCode = w.settings.alternativeIsoCode;
out.alternativeIsoCode = w.settings.alternativeIsoCode;
});
if (cb) return cb();
}); });
}; if (cb) return cb(tx);
var w = opts.wallet || $rootScope.wallet;
if (!w) return;
opts = opts || $rootScope.txsOpts || {};
var satToUnit = 1 / w.settings.unitToSatoshi;
var myCopayerId = w.getMyCopayerId();
var pendingForUs = 0;
var inT = w.getTxProposals().sort(function(t1, t2) {
return t2.createdTs - t1.createdTs
}); });
var txs = []; };
inT.forEach(function(i, index) { root.updateTxs = function() {
if (opts.skip && (index < opts.skip[0] || index >= opts.skip[1])) { var w = $rootScope.wallet;
return txs.push(null); if (!w) return root.onErrorDigest();
} var res = w.getPendingTxProposals();
$rootScope.txps = res.txs;
if (i.isPending && myCopayerId != i.creator && !i.rejectedByUs && !i.signedByUs) { if ($rootScope.pendingTxCount < res.pendingForUs) {
pendingForUs++; $rootScope.txAlertCount = res.pendingForUs;
}
if (!!opts.pending == !!i.isPending) {
var tx = i.builder.build();
var outs = [];
tx.outs.forEach(function(o) {
var addr = bitcore.Address.fromScriptPubKey(o.getScript(), w.getNetworkName())[0].toString();
if (!w.addressIsOwn(addr, {
excludeMain: true
})) {
outs.push({
address: addr,
value: bitcore.util.valueToBigInt(o.getValue()) * satToUnit,
});
}
});
// extra fields
i.outs = outs;
i.fee = i.builder.feeSat * satToUnit;
i.missingSignatures = tx.countInputMissingSignatures(0);
i.actionList = getActionList(i.peerActions);
if (i.isPending) {
computeAlternativeAmount(w, i);
}
txs.push(i);
}
});
// Disabling this as discrepancies in local time on copayer machines is causing
// valid TXPs to get removed
//w.removeTxWithSpentInputs();
$rootScope.txs = txs;
$rootScope.txsOpts = opts;
if ($rootScope.pendingTxCount < pendingForUs) {
$rootScope.txAlertCount = pendingForUs;
} }
$rootScope.pendingTxCount = pendingForUs; $rootScope.pendingTxCount = res.pendingForUs;
}; };
root.deleteWallet = function($scope, w) { root.deleteWallet = function($scope, w) {
@ -404,24 +363,5 @@ angular.module('copayApp.services')
}); });
}; };
root.getActionList = function(actions) {
return getActionList(actions);
};
function getActionList(actions) {
var peers = Object.keys(actions).map(function(i) {
return {
cId: i,
actions: actions[i]
}
});
return peers.sort(function(a, b) {
return !!b.actions.create - !!a.actions.create;
});
}
return root; return root;
}); });

View file

@ -43,6 +43,7 @@ describe("Unit: Controllers", function() {
scope = $rootScope.$new(); scope = $rootScope.$new();
$rootScope.iden = sinon.stub(); $rootScope.iden = sinon.stub();
$rootScope.safeUnspentCount = 1; $rootScope.safeUnspentCount = 1;
$rootScope.pendingTxCount = 0;
var w = {}; var w = {};
w.isReady = sinon.stub().returns(true); w.isReady = sinon.stub().returns(true);
@ -72,7 +73,10 @@ describe("Unit: Controllers", function() {
w.sendTx = sinon.stub().yields(null); w.sendTx = sinon.stub().yields(null);
w.requiresMultipleSignatures = sinon.stub().returns(true); w.requiresMultipleSignatures = sinon.stub().returns(true);
w.getTxProposals = sinon.stub().returns([1, 2, 3]); w.getTxProposals = sinon.stub().returns([1, 2, 3]);
w.getPendingTxProposals = sinon.stub().returns({
txs : [{ isPending : true }],
pendingForUs: 1
});
$rootScope.wallet = w; $rootScope.wallet = w;
})); }));

View file

@ -1,15 +1,15 @@
<div class="send" data-ng-controller="SendController" data-ng-init="loadTxs()"> <div class="send" ng-controller="SendController" ng-init="loadTxs()">
<div ng-show='$root.wallet.isReady()'> <div ng-show='$root.wallet.isReady()'>
<div class="row" ng-show="txs.length != 0"> <div class="row" ng-show="$root.txps.length != 0">
<div class="large-12 columns"> <div class="large-12 columns">
<h2 translate>Pending Transactions Proposals</h2> <h2 translate>Pending Transactions Proposals</h2>
<div class="last-transactions" <div class="last-transactions"
ng-repeat="tx in txs | paged" ng-repeat="tx in $root.txps | paged"
ng-include="'views/includes/transaction.html'"></div> ng-include="'views/includes/transaction.html'"></div>
</div> </div>
</div> </div>
<div ng-show="txs.length != 0" class="line-dashed-h m20b"></div> <div ng-show="$root.txps.length != 0" class="line-dashed-h m20b"></div>
<h1 class="hide-for-large-up">{{$root.title}}</h1> <h1 class="hide-for-large-up">{{$root.title}}</h1>
<div class="row"> <div class="row">