diff --git a/lib/server.js b/lib/server.js index b3d1878..8db62ab 100644 --- a/lib/server.js +++ b/lib/server.js @@ -603,7 +603,10 @@ WalletService.prototype._getUtxos = function(cb) { var bc = self._getBlockchainExplorer('insight', networkName); bc.getUnspentUtxos(addressStrs, function(err, inutxos) { - if (err) return cb(new Error('Could not fetch unspend outputs:' + err)); + if (err) { + log.error('Could not fetch unspent outputs', err); + return cb(new ClientError('BLOCKCHAINERROR', 'Could not fetch unspent outputs')); + } var utxos = _.map(inutxos, function(i) { return _.pick(i.toObject(), ['txid', 'vout', 'address', 'scriptPubKey', 'amount', 'satoshis']); }); @@ -911,7 +914,11 @@ WalletService.prototype._broadcastTx = function(txp, cb) { } var bc = this._getBlockchainExplorer('insight', txp.getNetworkName()); bc.broadcast(raw, function(err, txid) { - return cb(err, txid); + if (err) { + log.error('Could not broadcast transaction', err); + return cb(new ClientError('BLOCKCHAINERROR', 'Could not broadcast transaction')); + } + return cb(null, txid); }) }; @@ -1283,8 +1290,10 @@ WalletService.prototype.getTxHistory = function(opts, cb) { }, function(next) { bc.getTransactions(addressStrs, null, null, function(err, txs) { - if (err) return next(new Error('Could not fetch transactions: ' + err)); - + if (err) { + log.error('Could not fetch transactions', err); + return next(new ClientError('BLOCKCHAINERROR', 'Could not fetch transactions')); + } next(null, self._normalizeTxHistory(txs)); }); }, @@ -1342,7 +1351,11 @@ WalletService.prototype.scan = function(opts, cb) { if (err) return next(err); networkName = networkName || Bitcore.Address(addresses[0].address).toObject().network; checkActivity(_.pluck(addresses, 'address'), networkName, function(err, thereIsActivity) { - if (err) return next(new Error('Could not fetch TX activity:' + err)); + if (err) { + log.error('Could not check address activity', err); + return next(new ClientError('BLOCKCHAINERROR', 'Could not check address activity')); + } + activity = thereIsActivity; if (thereIsActivity) { allAddresses.push(addresses); diff --git a/test/integration/server.js b/test/integration/server.js index 20d38df..22353e2 100644 --- a/test/integration/server.js +++ b/test/integration/server.js @@ -902,6 +902,17 @@ describe('Wallet service', function() { }); }); }); + it('should fail gracefully when blockchain is unreachable', function(done) { + blockchainExplorer.getUnspentUtxos = sinon.stub().callsArgWith(1, 'dummy error'); + server.createAddress({}, function(err, address) { + should.not.exist(err); + server.getBalance({}, function(err, balance) { + should.exist(err); + err.code.should.equal('BLOCKCHAINERROR'); + done(); + }); + }); + }); }); describe('Wallet not complete tests', function() { @@ -1032,6 +1043,19 @@ describe('Wallet service', function() { }); }); + it('should fail gracefully if unable to reach the blockchain', function(done) { + blockchainExplorer.getUnspentUtxos = sinon.stub().callsArgWith(1, 'dummy error'); + server.createAddress({}, function(err, address) { + should.not.exist(err); + var txOpts = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 80, 'some message', TestData.copayers[0].privKey_1H_0); + server.createTx(txOpts, function(err, tx) { + should.exist(err); + err.code.should.equal('BLOCKCHAINERROR'); + done(); + }); + }); + }); + it('should fail to create tx with invalid proposal signature', function(done) { helpers.stubUtxos(server, wallet, [100, 200], function() { @@ -1676,6 +1700,7 @@ describe('Wallet service', function() { txProposalId: txpid }, function(err) { should.exist(err); + err.code.should.equal('BLOCKCHAINERROR'); server.getTx({ txProposalId: txpid }, function(err, txp) { @@ -2691,6 +2716,14 @@ describe('Wallet service', function() { }); }, done); }); + it('should fail gracefully if unable to reach the blockchain', function(done) { + blockchainExplorer.getTransactions = sinon.stub().callsArgWith(3, 'dummy error'); + server.getTxHistory({}, function(err, txs) { + should.exist(err); + err.code.should.equal('BLOCKCHAINERROR'); + done(); + }); + }); }); describe('#scan', function() { @@ -2727,17 +2760,21 @@ describe('Wallet service', function() { ]; server.scan({}, function(err) { should.not.exist(err); - server.storage.fetchAddresses(wallet.id, function(err, addresses) { - should.exist(addresses); - addresses.length.should.equal(expectedPaths.length); - var paths = _.pluck(addresses, 'path'); - _.difference(paths, expectedPaths).length.should.equal(0); - server.createAddress({}, function(err, address) { - should.not.exist(err); - address.path.should.equal('m/2147483647/0/4'); - done(); + server.getWallet({}, function(err, wallet) { + should.not.exist(err); + wallet.scanStatus.should.equal('success'); + server.storage.fetchAddresses(wallet.id, function(err, addresses) { + should.exist(addresses); + addresses.length.should.equal(expectedPaths.length); + var paths = _.pluck(addresses, 'path'); + _.difference(paths, expectedPaths).length.should.equal(0); + server.createAddress({}, function(err, address) { + should.not.exist(err); + address.path.should.equal('m/2147483647/0/4'); + done(); + }); }); - }) + }); }); }); it('should scan main addresses & copayer addresses', function(done) { @@ -2814,8 +2851,22 @@ describe('Wallet service', function() { done(); }); }); - it.skip('should abort scan if there is an error checking address activity', function(done) {}); - + it('should abort scan if there is an error checking address activity', function(done) { + blockchainExplorer.getAddressActivity = sinon.stub().callsArgWith(1, 'dummy error'); + server.scan({}, function(err) { + should.exist(err); + err.code.should.equal('BLOCKCHAINERROR'); + server.getWallet({}, function(err, wallet) { + should.not.exist(err); + wallet.scanStatus.should.equal('error'); + server.storage.fetchAddresses(wallet.id, function(err, addresses) { + should.not.exist(err); + addresses.should.be.empty; + done(); + }); + }); + }); + }); }); describe('#startScan', function() { @@ -3134,6 +3185,7 @@ describe('Wallet service', function() { }); }); }); + describe('Email notifications', function() { var server, wallet, sendMailStub; beforeEach(function(done) {