From b659333c942b4fd2931eb7524bc3b651dd1747d4 Mon Sep 17 00:00:00 2001
From: Ivan Socolsky <jungans@gmail.com>
Date: Thu, 30 Jul 2015 14:55:35 -0300
Subject: [PATCH 1/6] differentiate INSUFFICIENTFUNDS from
 INSUFFICIENTFUNDSFORFEE

---
 lib/server.js              | 2 +-
 test/integration/server.js | 8 ++++----
 2 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/lib/server.js b/lib/server.js
index 1d03ecf..b508368 100644
--- a/lib/server.js
+++ b/lib/server.js
@@ -896,7 +896,7 @@ WalletService.prototype._selectTxInputs = function(txp, cb) {
     }
 
     if (bitcoreError instanceof Bitcore.errors.Transaction.FeeError) {
-      return cb(new ClientError('INSUFFICIENTFUNDS', 'Insufficient funds for fee'));
+      return cb(new ClientError('INSUFFICIENTFUNDSFORFEE', 'Insufficient funds for fee'));
     }
     if (bitcoreError instanceof Bitcore.errors.Transaction.DustOutputs) {
       return cb(new ClientError('DUSTAMOUNT', 'Amount below dust threshold'));
diff --git a/test/integration/server.js b/test/integration/server.js
index e255205..1df33a5 100644
--- a/test/integration/server.js
+++ b/test/integration/server.js
@@ -1692,7 +1692,7 @@ describe('Wallet service', function() {
           txOpts.excludeUnconfirmedUtxos = true;
           server.createTx(txOpts, function(err, tx) {
             should.exist(err);
-            err.code.should.equal('INSUFFICIENTFUNDS');
+            err.code.should.equal('INSUFFICIENTFUNDSFORFEE');
             err.message.should.equal('Insufficient funds for fee');
             done();
           });
@@ -1826,7 +1826,7 @@ describe('Wallet service', function() {
         var txOpts = helpers.createSimpleProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 0.048200, null, TestData.copayers[0].privKey_1H_0);
         server.createTx(txOpts, function(err, tx) {
           should.exist(err);
-          err.code.should.equal('INSUFFICIENTFUNDS');
+          err.code.should.equal('INSUFFICIENTFUNDSFORFEE');
           err.message.should.equal('Insufficient funds for fee');
           done();
         });
@@ -1850,7 +1850,7 @@ describe('Wallet service', function() {
         var txOpts = helpers.createSimpleProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 0.99995, null, TestData.copayers[0].privKey_1H_0, 80000);
         server.createTx(txOpts, function(err, tx) {
           should.exist(err);
-          err.code.should.equal('INSUFFICIENTFUNDS');
+          err.code.should.equal('INSUFFICIENTFUNDSFORFEE');
           var txOpts = helpers.createSimpleProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 0.99995, null, TestData.copayers[0].privKey_1H_0, 5000);
           server.createTx(txOpts, function(err, tx) {
             should.not.exist(err);
@@ -4367,7 +4367,7 @@ describe('Wallet service', function() {
 
             server.createTx(txOpts, function(err, tx) {
               should.exist(err);
-              err.code.should.equal('INSUFFICIENTFUNDS');
+              err.code.should.equal('INSUFFICIENTFUNDSFORFEE');
               var txOpts = helpers.createSimpleProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 0.99995, null, TestData.copayers[0].privKey_1H_0, 5000);
               server.createTx(txOpts, function(err, tx) {
                 should.not.exist(err);

From 86779303a9535c238ca838c1180e1f0baa45dc11 Mon Sep 17 00:00:00 2001
From: Ivan Socolsky <jungans@gmail.com>
Date: Thu, 30 Jul 2015 16:08:17 -0300
Subject: [PATCH 2/6] add UPGRADENEEDED error code

---
 lib/server.js              | 2 +-
 test/integration/server.js | 3 ++-
 2 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/lib/server.js b/lib/server.js
index b508368..0488241 100644
--- a/lib/server.js
+++ b/lib/server.js
@@ -1396,7 +1396,7 @@ WalletService.prototype.getPendingTxs = function(opts, cb) {
         return _.startsWith(txp.version, '1.');
       });
 
-      if (!allLegacy) return cb(new Error('Some spend proposals were created using a newer version. Please upgrade your client app.'))
+      if (!allLegacy) return cb(new ClientError('UPGRADENEEDED', 'Some spend proposals were created using a newer version of the client app'));
     }
 
     _.each(txps, function(txp) {
diff --git a/test/integration/server.js b/test/integration/server.js
index 1df33a5..70e89eb 100644
--- a/test/integration/server.js
+++ b/test/integration/server.js
@@ -4309,7 +4309,8 @@ describe('Wallet service', function() {
               server.getPendingTxs({}, function(err, txps) {
                 should.exist(err);
                 should.not.exist(txps);
-                err.toString().should.contain('created by a newer version');
+                err.code.should.equal('UPGRADENEEDED');
+                err.message.should.contain('newer version');
                 done();
               });
             });

From e474c71a1ea8ce5d537233d743e36702a6e735b0 Mon Sep 17 00:00:00 2001
From: Ivan Socolsky <jungans@gmail.com>
Date: Fri, 31 Jul 2015 12:16:18 -0300
Subject: [PATCH 3/6] use predefined errors

---
 lib/{ => errors}/clienterror.js |  2 +
 lib/errors/errordefinitions.js  | 39 ++++++++++++++
 lib/server.js                   | 91 ++++++++++++++-------------------
 3 files changed, 79 insertions(+), 53 deletions(-)
 rename lib/{ => errors}/clienterror.js (97%)
 create mode 100644 lib/errors/errordefinitions.js

diff --git a/lib/clienterror.js b/lib/errors/clienterror.js
similarity index 97%
rename from lib/clienterror.js
rename to lib/errors/clienterror.js
index f0f4015..98f264b 100644
--- a/lib/clienterror.js
+++ b/lib/errors/clienterror.js
@@ -1,3 +1,5 @@
+'use strict';
+
 function ClientError() {
   var args = Array.prototype.slice.call(arguments);
 
diff --git a/lib/errors/errordefinitions.js b/lib/errors/errordefinitions.js
new file mode 100644
index 0000000..d88d701
--- /dev/null
+++ b/lib/errors/errordefinitions.js
@@ -0,0 +1,39 @@
+'use strict';
+
+var _ = require('lodash');
+
+var ClientError = require('./clienterror');
+
+var errors = {
+  BADSIGNATURES: 'Bad signatures',
+  BLOCKCHAINERROR: 'An error ocurred while trying to interact with the blockchain explorer',
+  CDATAMISMATCH: 'Copayer data mismatch',
+  CINWALLET: 'Copayer already in wallet',
+  CREGISTERED: 'Copayer ID already registered on server',
+  CVOTED: 'Copayer already voted on this transaction proposal',
+  DUSTAMOUNT: 'Amount below dust threshold',
+  INSUFFICIENTFUNDS: 'Insufficient funds',
+  INSUFFICIENTFUNDSFORFEE: 'Insufficient funds for fee',
+  INVALIDADDRESS: 'Invalid address',
+  LOCKEDFUNDS: 'Funds are locked by pending transaction proposals',
+  NOTALLOWEDTOCREATETX: 'Cannot create TX proposal during backoff time',
+  NOTAUTHORIZED: 'Not authorized',
+  TXALREADYBROADCASTED: 'The transaction proposal is already broadcasted',
+  TXCANNOTREMOVE: 'Cannot remove this tx proposal during locktime',
+  TXNOTACCEPTED: 'The transaction proposal is not accepted',
+  TXNOTPENDING: 'The transaction proposal is not pending',
+  UPGRADENEEDED: 'Client app needs to be upgraded',
+  WEXISTS: 'Wallet already exists',
+  WFULL: 'Wallet full',
+  WNOTFULL: 'Replace only works on full wallets',
+};
+
+var errorObjects = _.zipObject(_.map(errors, function(msg, code) {
+  return [code, new ClientError(code, msg)];
+}));
+
+errorObjects.codes = _.mapValues(errors, function(v, k) {
+  return k;
+});
+
+module.exports = errorObjects;
diff --git a/lib/server.js b/lib/server.js
index 0488241..d60b146 100644
--- a/lib/server.js
+++ b/lib/server.js
@@ -13,7 +13,9 @@ var PublicKey = Bitcore.PublicKey;
 var HDPublicKey = Bitcore.HDPublicKey;
 var Address = Bitcore.Address;
 
-var ClientError = require('./clienterror');
+var ClientError = require('./errors/clienterror');
+var Errors = require('./errors/errordefinitions');
+
 var Utils = require('./utils');
 var Lock = require('./lock');
 var Storage = require('./storage');
@@ -159,11 +161,11 @@ WalletService.getInstanceWithAuth = function(opts, cb) {
   var server = new WalletService();
   server.storage.fetchCopayerLookup(opts.copayerId, function(err, copayer) {
     if (err) return cb(err);
-    if (!copayer) return cb(new ClientError('NOTAUTHORIZED', 'Copayer not found'));
+    if (!copayer) return cb(new ClientError(Errors.codes.NOTAUTHORIZED, 'Copayer not found'));
 
     var isValid = server._verifySignature(opts.message, opts.signature, copayer.requestPubKey);
     if (!isValid)
-      return cb(new ClientError('NOTAUTHORIZED', 'Invalid signature'));
+      return cb(new ClientError(Errors.codes.NOTAUTHORIZED, 'Invalid signature'));
 
     server.copayerId = opts.copayerId;
     server.walletId = copayer.walletId;
@@ -217,7 +219,7 @@ WalletService.prototype.createWallet = function(opts, cb) {
         return acb();
 
       self.storage.fetchWallet(opts.id, function(err, wallet) {
-        if (wallet) return acb(new ClientError('WEXISTS', 'Wallet already exists'));
+        if (wallet) return acb(Errors.WEXISTS);
         return acb(err);
       });
     },
@@ -295,10 +297,10 @@ WalletService.prototype.replaceTemporaryRequestKey = function(opts, cb) {
       $.checkState(oldCopayerData);
 
       if (oldCopayerData.xPubKey !== opts.xPubKey || !oldCopayerData.isTemporaryRequestKey)
-        return cb(new ClientError('CDATAMISMATCH', 'Copayer data mismatch'));
+        return cb(Errors.CDATAMISMATCH);
 
       if (wallet.copayers.length != wallet.n)
-        return cb(new ClientError('WNOTFULL', 'Replace only works on full wallets'));
+        return cb(Errors.WNOTFULL);
 
       wallet.updateCopayerRequestKey(self.copayerId, opts.requestPubKey, opts.copayerSignature);
 
@@ -404,10 +406,9 @@ WalletService.prototype.joinWallet = function(opts, cb) {
 
       if (_.find(wallet.copayers, {
         xPubKey: opts.xPubKey
-      })) return cb(new ClientError('CINWALLET', 'Copayer already in wallet'));
+      })) return cb(Errors.CINWALLET);
 
-      if (wallet.copayers.length == wallet.n)
-        return cb(new ClientError('WFULL', 'Wallet full'));
+      if (wallet.copayers.length == wallet.n) return cb(Errors.WFULL);
 
       var copayer = Model.Copayer.create({
         name: opts.name,
@@ -420,14 +421,12 @@ WalletService.prototype.joinWallet = function(opts, cb) {
 
       self.storage.fetchCopayerLookup(copayer.id, function(err, res) {
         if (err) return cb(err);
-        if (res)
-          return cb(new ClientError('CREGISTERED', 'Copayer ID already registered on server'));
+        if (res) return cb(Errors.CREGISTERED);
 
         wallet.addCopayer(copayer);
         self.storage.storeWalletAndUpdateCopayersLookup(wallet, function(err) {
           if (err) return cb(err);
 
-
           async.series([
 
             function(next) {
@@ -643,7 +642,7 @@ WalletService.prototype.getUtxos = function(cb) {
     bc.getUnspentUtxos(addressStrs, function(err, inutxos) {
       if (err) {
         log.error('Could not fetch unspent outputs', err);
-        return cb(new ClientError('BLOCKCHAINERROR', 'Could not fetch unspent outputs'));
+        return cb(new ClientError(Errors.codes.BLOCKCHAINERROR, 'Could not fetch unspent outputs'));
       }
       var utxos = _.map(inutxos, function(utxo) {
         var u = _.pick(utxo, ['txid', 'vout', 'address', 'scriptPubKey', 'amount', 'satoshis', 'confirmations']);
@@ -848,10 +847,8 @@ WalletService.prototype._selectTxInputs = function(txp, cb) {
       availableAmount = balance.availableAmount;
     }
 
-    if (totalAmount < txp.getTotalAmount())
-      return cb(new ClientError('INSUFFICIENTFUNDS', 'Insufficient funds'));
-    if (availableAmount < txp.amount)
-      return cb(new ClientError('LOCKEDFUNDS', 'Funds are locked by pending transaction proposals'));
+    if (totalAmount < txp.getTotalAmount()) return cb(Errors.INSUFFICIENTFUNDS);
+    if (availableAmount < txp.amount) return cb(Errors.LOCKEDFUNDS);
 
     // Prepare UTXOs list
     utxos = _.reject(utxos, 'locked');
@@ -895,12 +892,11 @@ WalletService.prototype._selectTxInputs = function(txp, cb) {
       }
     }
 
-    if (bitcoreError instanceof Bitcore.errors.Transaction.FeeError) {
-      return cb(new ClientError('INSUFFICIENTFUNDSFORFEE', 'Insufficient funds for fee'));
-    }
-    if (bitcoreError instanceof Bitcore.errors.Transaction.DustOutputs) {
-      return cb(new ClientError('DUSTAMOUNT', 'Amount below dust threshold'));
-    }
+    if (bitcoreError instanceof Bitcore.errors.Transaction.FeeError)
+      return cb(Errors.INSUFFICIENTFUNDSFORFEE);
+
+    if (bitcoreError instanceof Bitcore.errors.Transaction.DustOutputs)
+      return cb(Errors.DUSTAMOUNT);
 
     return cb(bitcoreError || new Error('Could not select tx inputs'));
   });
@@ -1007,8 +1003,7 @@ WalletService.prototype.createTx = function(opts, cb) {
 
       self._canCreateTx(self.copayerId, function(err, canCreate) {
         if (err) return cb(err);
-        if (!canCreate)
-          return cb(new ClientError('NOTALLOWEDTOCREATETX', 'Cannot create TX proposal during backoff time'));
+        if (!canCreate) return cb(Errors.NOTALLOWEDTOCREATETX);
 
         _.each(opts.outputs, function(output) {
           output.valid = false;
@@ -1016,11 +1011,11 @@ WalletService.prototype.createTx = function(opts, cb) {
           try {
             toAddress = new Bitcore.Address(output.toAddress);
           } catch (ex) {
-            cb(new ClientError('INVALIDADDRESS', 'Invalid address'));
+            cb(new ClientError(Errors.codes.INVALIDADDRESS, 'Invalid address'));
             return false;
           }
           if (toAddress.network != wallet.getNetworkName()) {
-            cb(new ClientError('INVALIDADDRESS', 'Incorrect address network'));
+            cb(new ClientError(Errors.codes.INVALIDADDRESS, 'Incorrect address network'));
             return false;
           }
           if (!_.isNumber(output.amount) || _.isNaN(output.amount) || output.amount <= 0) {
@@ -1028,7 +1023,7 @@ WalletService.prototype.createTx = function(opts, cb) {
             return false;
           }
           if (output.amount < Bitcore.Transaction.DUST_AMOUNT) {
-            cb(new ClientError('DUSTAMOUNT', 'Amount below dust threshold'));
+            cb(Errors.DUSTAMOUNT);
             return false;
           }
           output.valid = true;
@@ -1155,14 +1150,11 @@ WalletService.prototype.removePendingTx = function(opts, cb) {
     }, function(err, txp) {
       if (err) return cb(err);
 
-      if (!txp.isPending())
-        return cb(new ClientError('TXNOTPENDING', 'Transaction proposal not pending'));
-
+      if (!txp.isPending()) return cb(Errors.TXNOTPENDING);
 
       var deleteLockTime = self.getRemainingDeleteLockTime(txp);
-      if (deleteLockTime > 0) {
-        return cb(new ClientError('TXCANNOTREMOVE', 'Cannot remove this tx proposal during locktime'));
-      }
+      if (deleteLockTime > 0) return cb(Errors.TXCANNOTREMOVE);
+
       self.storage.removeTx(self.walletId, txp.id, function() {
         self._notify('TxProposalRemoved', {}, cb);
       });
@@ -1182,7 +1174,7 @@ WalletService.prototype._broadcastTx = function(txp, cb) {
   bc.broadcast(raw, function(err, txid) {
     if (err) {
       log.error('Could not broadcast transaction', err);
-      return cb(new ClientError('BLOCKCHAINERROR', 'Could not broadcast transaction'));
+      return cb(new ClientError(Errors.codes.BLOCKCHAINERROR, 'Could not broadcast transaction'));
     }
     return cb(null, txid);
   })
@@ -1194,7 +1186,7 @@ WalletService.prototype._checkTxInBlockchain = function(txp, cb) {
   bc.getTransaction(tx.id, function(err, tx) {
     if (err) {
       log.error('Could not get transaction info', err);
-      return cb(new ClientError('BLOCKCHAINERROR', 'Could not get transaction info'));
+      return cb(new ClientError(Errors.codes.BLOCKCHAINERROR, 'Could not get transaction info'));
     }
     return cb(null, tx);
   })
@@ -1223,15 +1215,13 @@ WalletService.prototype.signTx = function(opts, cb) {
       var action = _.find(txp.actions, {
         copayerId: self.copayerId
       });
-      if (action)
-        return cb(new ClientError('CVOTED', 'Copayer already voted on this transaction proposal'));
-      if (!txp.isPending())
-        return cb(new ClientError('TXNOTPENDING', 'The transaction proposal is not pending'));
+      if (action) return cb(Errors.CVOTED);
+      if (!txp.isPending()) return cb(Errors.TXNOTPENDING);
 
       var copayer = wallet.getCopayer(self.copayerId);
 
       if (!txp.sign(self.copayerId, opts.signatures, copayer.xPubKey))
-        return cb(new ClientError('BADSIGNATURES', 'Bad signatures'));
+        return cb(Errors.BADSIGNATURES);
 
       self.storage.storeTx(self.walletId, txp, function(err) {
         if (err) return cb(err);
@@ -1296,11 +1286,8 @@ WalletService.prototype.broadcastTx = function(opts, cb) {
     }, function(err, txp) {
       if (err) return cb(err);
 
-      if (txp.status == 'broadcasted')
-        return cb(new ClientError('TXALREADYBROADCASTED', 'The transaction proposal is already broadcasted'));
-
-      if (txp.status != 'accepted')
-        return cb(new ClientError('TXNOTACCEPTED', 'The transaction proposal is not accepted'));
+      if (txp.status == 'broadcasted') return cb(Errors.TXALREADYBROADCASTED);
+      if (txp.status != 'accepted') return cb(Errors.TXNOTACCEPTED);
 
       self._broadcastTx(txp, function(err, txid) {
         if (err) {
@@ -1340,11 +1327,9 @@ WalletService.prototype.rejectTx = function(opts, cb) {
     var action = _.find(txp.actions, {
       copayerId: self.copayerId
     });
-    if (action)
-      return cb(new ClientError('CVOTED', 'Copayer already voted on this transaction proposal'));
 
-    if (txp.status != 'pending')
-      return cb(new ClientError('TXNOTPENDING', 'The transaction proposal is not pending'));
+    if (action) return cb(Errors.CVOTED);
+    if (txp.status != 'pending') return cb(Errors.TXNOTPENDING);
 
     txp.reject(self.copayerId, opts.reason);
 
@@ -1396,7 +1381,7 @@ WalletService.prototype.getPendingTxs = function(opts, cb) {
         return _.startsWith(txp.version, '1.');
       });
 
-      if (!allLegacy) return cb(new ClientError('UPGRADENEEDED', 'Some spend proposals were created using a newer version of the client app'));
+      if (!allLegacy) return cb(new ClientError(Errors.codes.UPGRADENEEDED, 'Some spend proposals were created using a newer version of the client app'));
     }
 
     _.each(txps, function(txp) {
@@ -1617,7 +1602,7 @@ WalletService.prototype.getTxHistory = function(opts, cb) {
         bc.getTransactions(addressStrs, from, to, function(err, txs) {
           if (err) {
             log.error('Could not fetch transactions', err);
-            return next(new ClientError('BLOCKCHAINERROR', 'Could not fetch transactions'));
+            return next(new ClientError(Errors.codes.BLOCKCHAINERROR, 'Could not fetch transactions'));
           }
           next(null, self._normalizeTxHistory(txs));
         });
@@ -1673,7 +1658,7 @@ WalletService.prototype.scan = function(opts, cb) {
         checkActivity(_.pluck(addresses, 'address'), networkName, function(err, thereIsActivity) {
           if (err) {
             log.error('Could not check address activity', err);
-            return next(new ClientError('BLOCKCHAINERROR', 'Could not check address activity'));
+            return next(new ClientError(Errors.codes.BLOCKCHAINERROR, 'Could not check address activity'));
           }
 
           activity = thereIsActivity;

From 602bc9a9a390e5730041739cfb1a89a7635d9852 Mon Sep 17 00:00:00 2001
From: Ivan Socolsky <jungans@gmail.com>
Date: Sun, 2 Aug 2015 19:48:18 -0300
Subject: [PATCH 4/6] rm BLOCKCHAINERROR error code

---
 lib/errors/errordefinitions.js |  1 -
 lib/server.js                  | 26 ++++++--------------------
 test/integration/server.js     | 24 ++++++++++--------------
 3 files changed, 16 insertions(+), 35 deletions(-)

diff --git a/lib/errors/errordefinitions.js b/lib/errors/errordefinitions.js
index d88d701..aa33e98 100644
--- a/lib/errors/errordefinitions.js
+++ b/lib/errors/errordefinitions.js
@@ -6,7 +6,6 @@ var ClientError = require('./clienterror');
 
 var errors = {
   BADSIGNATURES: 'Bad signatures',
-  BLOCKCHAINERROR: 'An error ocurred while trying to interact with the blockchain explorer',
   CDATAMISMATCH: 'Copayer data mismatch',
   CINWALLET: 'Copayer already in wallet',
   CREGISTERED: 'Copayer ID already registered on server',
diff --git a/lib/server.js b/lib/server.js
index d60b146..11144b9 100644
--- a/lib/server.js
+++ b/lib/server.js
@@ -640,10 +640,8 @@ WalletService.prototype.getUtxos = function(cb) {
 
     var bc = self._getBlockchainExplorer(networkName);
     bc.getUnspentUtxos(addressStrs, function(err, inutxos) {
-      if (err) {
-        log.error('Could not fetch unspent outputs', err);
-        return cb(new ClientError(Errors.codes.BLOCKCHAINERROR, 'Could not fetch unspent outputs'));
-      }
+      if (err) return cb(err);
+
       var utxos = _.map(inutxos, function(utxo) {
         var u = _.pick(utxo, ['txid', 'vout', 'address', 'scriptPubKey', 'amount', 'satoshis', 'confirmations']);
         u.confirmations = u.confirmations || 0;
@@ -1172,10 +1170,7 @@ WalletService.prototype._broadcastTx = function(txp, cb) {
   }
   var bc = this._getBlockchainExplorer(txp.getNetworkName());
   bc.broadcast(raw, function(err, txid) {
-    if (err) {
-      log.error('Could not broadcast transaction', err);
-      return cb(new ClientError(Errors.codes.BLOCKCHAINERROR, 'Could not broadcast transaction'));
-    }
+    if (err) return cb(err);
     return cb(null, txid);
   })
 };
@@ -1184,10 +1179,7 @@ WalletService.prototype._checkTxInBlockchain = function(txp, cb) {
   var tx = txp.getBitcoreTx();
   var bc = this._getBlockchainExplorer(txp.getNetworkName());
   bc.getTransaction(tx.id, function(err, tx) {
-    if (err) {
-      log.error('Could not get transaction info', err);
-      return cb(new ClientError(Errors.codes.BLOCKCHAINERROR, 'Could not get transaction info'));
-    }
+    if (err) return cb(err);
     return cb(null, tx);
   })
 };
@@ -1600,10 +1592,7 @@ WalletService.prototype.getTxHistory = function(opts, cb) {
         var from = opts.skip || 0;
         var to = from + (_.isUndefined(opts.limit) ? 100 : opts.limit);
         bc.getTransactions(addressStrs, from, to, function(err, txs) {
-          if (err) {
-            log.error('Could not fetch transactions', err);
-            return next(new ClientError(Errors.codes.BLOCKCHAINERROR, 'Could not fetch transactions'));
-          }
+          if (err) return cb(err);
           next(null, self._normalizeTxHistory(txs));
         });
       },
@@ -1656,10 +1645,7 @@ 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) {
-            log.error('Could not check address activity', err);
-            return next(new ClientError(Errors.codes.BLOCKCHAINERROR, 'Could not check address activity'));
-          }
+          if (err) return cb(err);
 
           activity = thereIsActivity;
           if (thereIsActivity) {
diff --git a/test/integration/server.js b/test/integration/server.js
index 70e89eb..ed796d9 100644
--- a/test/integration/server.js
+++ b/test/integration/server.js
@@ -169,10 +169,6 @@ helpers.stubBroadcast = function(txid) {
   blockchainExplorer.broadcast = sinon.stub().callsArgWith(1, null, txid);
 };
 
-helpers.stubBroadcastFail = function() {
-  blockchainExplorer.broadcast = sinon.stub().callsArgWith(1, 'broadcast error');
-};
-
 helpers.stubHistory = function(txs) {
   blockchainExplorer.getTransactions = function(addresses, from, to, cb) {
     var MAX_BATCH_SIZE = 100;
@@ -1452,7 +1448,7 @@ describe('Wallet service', function() {
         should.not.exist(err);
         server.getBalance({}, function(err, balance) {
           should.exist(err);
-          err.code.should.equal('BLOCKCHAINERROR');
+          err.toString().should.equal('dummy error');
           done();
         });
       });
@@ -1731,7 +1727,7 @@ describe('Wallet service', function() {
         var txOpts = helpers.createSimpleProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 80, 'some message', TestData.copayers[0].privKey_1H_0);
         server.createTx(txOpts, function(err, tx) {
           should.exist(err);
-          err.code.should.equal('BLOCKCHAINERROR');
+          err.toString().should.equal('dummy error');
           done();
         });
       });
@@ -2602,13 +2598,13 @@ describe('Wallet service', function() {
     });
 
     it('should keep tx as accepted if unable to broadcast it', function(done) {
-      helpers.stubBroadcastFail();
+      blockchainExplorer.broadcast = sinon.stub().callsArgWith(1, 'broadcast error');
       blockchainExplorer.getTransaction = sinon.stub().callsArgWith(1, null, null);
       server.broadcastTx({
         txProposalId: txpid
       }, function(err) {
         should.exist(err);
-        err.code.should.equal('BLOCKCHAINERROR');
+        err.toString().should.equal('broadcast error');
         server.getTx({
           txProposalId: txpid
         }, function(err, txp) {
@@ -2623,7 +2619,7 @@ describe('Wallet service', function() {
     });
 
     it('should mark tx as broadcasted if accepted but already in blockchain', function(done) {
-      helpers.stubBroadcastFail();
+      blockchainExplorer.broadcast = sinon.stub().callsArgWith(1, 'broadcast error');
       blockchainExplorer.getTransaction = sinon.stub().callsArgWith(1, null, {
         txid: '999'
       });
@@ -2645,13 +2641,13 @@ describe('Wallet service', function() {
     });
 
     it('should keep tx as accepted if broadcast fails and cannot check tx in blockchain', function(done) {
-      helpers.stubBroadcastFail();
+      blockchainExplorer.broadcast = sinon.stub().callsArgWith(1, 'broadcast error');
       blockchainExplorer.getTransaction = sinon.stub().callsArgWith(1, 'bc check error');
       server.broadcastTx({
         txProposalId: txpid
       }, function(err) {
         should.exist(err);
-        err.code.should.equal('BLOCKCHAINERROR');
+        err.toString().should.equal('bc check error');
         server.getTx({
           txProposalId: txpid
         }, function(err, txp) {
@@ -3085,7 +3081,7 @@ describe('Wallet service', function() {
 
     it('should notify sign and acceptance', function(done) {
       server.getPendingTxs({}, function(err, txs) {
-        helpers.stubBroadcastFail();
+        blockchainExplorer.broadcast = sinon.stub().callsArgWith(1, 'broadcast error');
         var tx = txs[0];
         var signatures = helpers.clientSign(tx, TestData.copayers[0].xPrivKey);
         server.signTx({
@@ -3770,7 +3766,7 @@ describe('Wallet service', function() {
       blockchainExplorer.getTransactions = sinon.stub().callsArgWith(3, 'dummy error');
       server.getTxHistory({}, function(err, txs) {
         should.exist(err);
-        err.code.should.equal('BLOCKCHAINERROR');
+        err.toString().should.equal('dummy error');
         done();
       });
     });
@@ -3920,7 +3916,7 @@ describe('Wallet service', function() {
       blockchainExplorer.getAddressActivity = sinon.stub().callsArgWith(1, 'dummy error');
       server.scan({}, function(err) {
         should.exist(err);
-        err.code.should.equal('BLOCKCHAINERROR');
+        err.toString().should.equal('dummy error');
         server.getWallet({}, function(err, wallet) {
           should.not.exist(err);
           wallet.scanStatus.should.equal('error');

From e1fac88a8e7850001f5e4878396deb0da4d40a04 Mon Sep 17 00:00:00 2001
From: Ivan Socolsky <jungans@gmail.com>
Date: Mon, 3 Aug 2015 16:11:09 -0300
Subject: [PATCH 5/6] improve error codes

---
 lib/errors/errordefinitions.js | 40 ++++++++++-----------
 lib/expressapp.js              |  4 +--
 lib/server.js                  | 52 +++++++++++++--------------
 test/integration/server.js     | 66 +++++++++++++++++-----------------
 4 files changed, 81 insertions(+), 81 deletions(-)

diff --git a/lib/errors/errordefinitions.js b/lib/errors/errordefinitions.js
index aa33e98..72f7f10 100644
--- a/lib/errors/errordefinitions.js
+++ b/lib/errors/errordefinitions.js
@@ -5,26 +5,26 @@ var _ = require('lodash');
 var ClientError = require('./clienterror');
 
 var errors = {
-  BADSIGNATURES: 'Bad signatures',
-  CDATAMISMATCH: 'Copayer data mismatch',
-  CINWALLET: 'Copayer already in wallet',
-  CREGISTERED: 'Copayer ID already registered on server',
-  CVOTED: 'Copayer already voted on this transaction proposal',
-  DUSTAMOUNT: 'Amount below dust threshold',
-  INSUFFICIENTFUNDS: 'Insufficient funds',
-  INSUFFICIENTFUNDSFORFEE: 'Insufficient funds for fee',
-  INVALIDADDRESS: 'Invalid address',
-  LOCKEDFUNDS: 'Funds are locked by pending transaction proposals',
-  NOTALLOWEDTOCREATETX: 'Cannot create TX proposal during backoff time',
-  NOTAUTHORIZED: 'Not authorized',
-  TXALREADYBROADCASTED: 'The transaction proposal is already broadcasted',
-  TXCANNOTREMOVE: 'Cannot remove this tx proposal during locktime',
-  TXNOTACCEPTED: 'The transaction proposal is not accepted',
-  TXNOTPENDING: 'The transaction proposal is not pending',
-  UPGRADENEEDED: 'Client app needs to be upgraded',
-  WEXISTS: 'Wallet already exists',
-  WFULL: 'Wallet full',
-  WNOTFULL: 'Replace only works on full wallets',
+  BAD_SIGNATURES: 'Bad signatures',
+  COPAYER_DATA_MISMATCH: 'Copayer data mismatch',
+  COPAYER_IN_WALLET: 'Copayer already in wallet',
+  COPAYER_REGISTERED: 'Copayer ID already registered on server',
+  COPAYER_VOTED: 'Copayer already voted on this transaction proposal',
+  DUST_AMOUNT: 'Amount below dust threshold',
+  INSUFFICIENT_FUNDS: 'Insufficient funds',
+  INSUFFICIENT_FUNDS_FOR_FEE: 'Insufficient funds for fee',
+  INVALID_ADDRESS: 'Invalid address',
+  LOCKED_FUNDS: 'Funds are locked by pending transaction proposals',
+  NOT_AUTHORIZED: 'Not authorized',
+  TX_ALREADY_BROADCASTED: 'The transaction proposal is already broadcasted',
+  TX_CANNOT_CREATE: 'Cannot create TX proposal during backoff time',
+  TX_CANNOT_REMOVE: 'Cannot remove this tx proposal during locktime',
+  TX_NOT_ACCEPTED: 'The transaction proposal is not accepted',
+  TX_NOT_PENDING: 'The transaction proposal is not pending',
+  UPGRADE_NEEDED: 'Client app needs to be upgraded',
+  WALLET_ALREADY_EXISTS: 'Wallet already exists',
+  WALLET_FULL: 'Wallet full',
+  WALLET_NOT_COMPLETE: 'Replace only works on complete wallets',
 };
 
 var errorObjects = _.zipObject(_.map(errors, function(msg, code) {
diff --git a/lib/expressapp.js b/lib/expressapp.js
index 56a1c2a..7642079 100644
--- a/lib/expressapp.js
+++ b/lib/expressapp.js
@@ -69,7 +69,7 @@ ExpressApp.prototype.start = function(opts, cb) {
   function returnError(err, res, req) {
     if (err instanceof WalletService.ClientError) {
 
-      var status = (err.code == 'NOTAUTHORIZED') ? 401 : 400;
+      var status = (err.code == 'NOT_AUTHORIZED') ? 401 : 400;
       if (!opts.disableLogs)
         log.info('Client Err: ' + status + ' ' + req.url + ' ' + err);
 
@@ -115,7 +115,7 @@ ExpressApp.prototype.start = function(opts, cb) {
     var credentials = getCredentials(req);
     if (!credentials)
       return returnError(new WalletService.ClientError({
-        code: 'NOTAUTHORIZED'
+        code: 'NOT_AUTHORIZED'
       }), res, req);
 
     var auth = {
diff --git a/lib/server.js b/lib/server.js
index 11144b9..c558b34 100644
--- a/lib/server.js
+++ b/lib/server.js
@@ -161,11 +161,11 @@ WalletService.getInstanceWithAuth = function(opts, cb) {
   var server = new WalletService();
   server.storage.fetchCopayerLookup(opts.copayerId, function(err, copayer) {
     if (err) return cb(err);
-    if (!copayer) return cb(new ClientError(Errors.codes.NOTAUTHORIZED, 'Copayer not found'));
+    if (!copayer) return cb(new ClientError(Errors.codes.NOT_AUTHORIZED, 'Copayer not found'));
 
     var isValid = server._verifySignature(opts.message, opts.signature, copayer.requestPubKey);
     if (!isValid)
-      return cb(new ClientError(Errors.codes.NOTAUTHORIZED, 'Invalid signature'));
+      return cb(new ClientError(Errors.codes.NOT_AUTHORIZED, 'Invalid signature'));
 
     server.copayerId = opts.copayerId;
     server.walletId = copayer.walletId;
@@ -219,7 +219,7 @@ WalletService.prototype.createWallet = function(opts, cb) {
         return acb();
 
       self.storage.fetchWallet(opts.id, function(err, wallet) {
-        if (wallet) return acb(Errors.WEXISTS);
+        if (wallet) return acb(Errors.WALLET_ALREADY_EXISTS);
         return acb(err);
       });
     },
@@ -297,10 +297,10 @@ WalletService.prototype.replaceTemporaryRequestKey = function(opts, cb) {
       $.checkState(oldCopayerData);
 
       if (oldCopayerData.xPubKey !== opts.xPubKey || !oldCopayerData.isTemporaryRequestKey)
-        return cb(Errors.CDATAMISMATCH);
+        return cb(Errors.COPAYER_DATA_MISMATCH);
 
       if (wallet.copayers.length != wallet.n)
-        return cb(Errors.WNOTFULL);
+        return cb(Errors.WALLET_NOT_COMPLETE);
 
       wallet.updateCopayerRequestKey(self.copayerId, opts.requestPubKey, opts.copayerSignature);
 
@@ -406,9 +406,9 @@ WalletService.prototype.joinWallet = function(opts, cb) {
 
       if (_.find(wallet.copayers, {
         xPubKey: opts.xPubKey
-      })) return cb(Errors.CINWALLET);
+      })) return cb(Errors.COPAYER_IN_WALLET);
 
-      if (wallet.copayers.length == wallet.n) return cb(Errors.WFULL);
+      if (wallet.copayers.length == wallet.n) return cb(Errors.WALLET_FULL);
 
       var copayer = Model.Copayer.create({
         name: opts.name,
@@ -421,7 +421,7 @@ WalletService.prototype.joinWallet = function(opts, cb) {
 
       self.storage.fetchCopayerLookup(copayer.id, function(err, res) {
         if (err) return cb(err);
-        if (res) return cb(Errors.CREGISTERED);
+        if (res) return cb(Errors.COPAYER_REGISTERED);
 
         wallet.addCopayer(copayer);
         self.storage.storeWalletAndUpdateCopayersLookup(wallet, function(err) {
@@ -845,8 +845,8 @@ WalletService.prototype._selectTxInputs = function(txp, cb) {
       availableAmount = balance.availableAmount;
     }
 
-    if (totalAmount < txp.getTotalAmount()) return cb(Errors.INSUFFICIENTFUNDS);
-    if (availableAmount < txp.amount) return cb(Errors.LOCKEDFUNDS);
+    if (totalAmount < txp.getTotalAmount()) return cb(Errors.INSUFFICIENT_FUNDS);
+    if (availableAmount < txp.amount) return cb(Errors.LOCKED_FUNDS);
 
     // Prepare UTXOs list
     utxos = _.reject(utxos, 'locked');
@@ -891,10 +891,10 @@ WalletService.prototype._selectTxInputs = function(txp, cb) {
     }
 
     if (bitcoreError instanceof Bitcore.errors.Transaction.FeeError)
-      return cb(Errors.INSUFFICIENTFUNDSFORFEE);
+      return cb(Errors.INSUFFICIENT_FUNDS_FOR_FEE);
 
     if (bitcoreError instanceof Bitcore.errors.Transaction.DustOutputs)
-      return cb(Errors.DUSTAMOUNT);
+      return cb(Errors.DUST_AMOUNT);
 
     return cb(bitcoreError || new Error('Could not select tx inputs'));
   });
@@ -1001,7 +1001,7 @@ WalletService.prototype.createTx = function(opts, cb) {
 
       self._canCreateTx(self.copayerId, function(err, canCreate) {
         if (err) return cb(err);
-        if (!canCreate) return cb(Errors.NOTALLOWEDTOCREATETX);
+        if (!canCreate) return cb(Errors.TX_CANNOT_CREATE);
 
         _.each(opts.outputs, function(output) {
           output.valid = false;
@@ -1009,11 +1009,11 @@ WalletService.prototype.createTx = function(opts, cb) {
           try {
             toAddress = new Bitcore.Address(output.toAddress);
           } catch (ex) {
-            cb(new ClientError(Errors.codes.INVALIDADDRESS, 'Invalid address'));
+            cb(new ClientError(Errors.codes.INVALID_ADDRESS, 'Invalid address'));
             return false;
           }
           if (toAddress.network != wallet.getNetworkName()) {
-            cb(new ClientError(Errors.codes.INVALIDADDRESS, 'Incorrect address network'));
+            cb(new ClientError(Errors.codes.INVALID_ADDRESS, 'Incorrect address network'));
             return false;
           }
           if (!_.isNumber(output.amount) || _.isNaN(output.amount) || output.amount <= 0) {
@@ -1021,7 +1021,7 @@ WalletService.prototype.createTx = function(opts, cb) {
             return false;
           }
           if (output.amount < Bitcore.Transaction.DUST_AMOUNT) {
-            cb(Errors.DUSTAMOUNT);
+            cb(Errors.DUST_AMOUNT);
             return false;
           }
           output.valid = true;
@@ -1148,10 +1148,10 @@ WalletService.prototype.removePendingTx = function(opts, cb) {
     }, function(err, txp) {
       if (err) return cb(err);
 
-      if (!txp.isPending()) return cb(Errors.TXNOTPENDING);
+      if (!txp.isPending()) return cb(Errors.TX_NOT_PENDING);
 
       var deleteLockTime = self.getRemainingDeleteLockTime(txp);
-      if (deleteLockTime > 0) return cb(Errors.TXCANNOTREMOVE);
+      if (deleteLockTime > 0) return cb(Errors.TX_CANNOT_REMOVE);
 
       self.storage.removeTx(self.walletId, txp.id, function() {
         self._notify('TxProposalRemoved', {}, cb);
@@ -1207,13 +1207,13 @@ WalletService.prototype.signTx = function(opts, cb) {
       var action = _.find(txp.actions, {
         copayerId: self.copayerId
       });
-      if (action) return cb(Errors.CVOTED);
-      if (!txp.isPending()) return cb(Errors.TXNOTPENDING);
+      if (action) return cb(Errors.COPAYER_VOTED);
+      if (!txp.isPending()) return cb(Errors.TX_NOT_PENDING);
 
       var copayer = wallet.getCopayer(self.copayerId);
 
       if (!txp.sign(self.copayerId, opts.signatures, copayer.xPubKey))
-        return cb(Errors.BADSIGNATURES);
+        return cb(Errors.BAD_SIGNATURES);
 
       self.storage.storeTx(self.walletId, txp, function(err) {
         if (err) return cb(err);
@@ -1278,8 +1278,8 @@ WalletService.prototype.broadcastTx = function(opts, cb) {
     }, function(err, txp) {
       if (err) return cb(err);
 
-      if (txp.status == 'broadcasted') return cb(Errors.TXALREADYBROADCASTED);
-      if (txp.status != 'accepted') return cb(Errors.TXNOTACCEPTED);
+      if (txp.status == 'broadcasted') return cb(Errors.TX_ALREADY_BROADCASTED);
+      if (txp.status != 'accepted') return cb(Errors.TX_NOT_ACCEPTED);
 
       self._broadcastTx(txp, function(err, txid) {
         if (err) {
@@ -1320,8 +1320,8 @@ WalletService.prototype.rejectTx = function(opts, cb) {
       copayerId: self.copayerId
     });
 
-    if (action) return cb(Errors.CVOTED);
-    if (txp.status != 'pending') return cb(Errors.TXNOTPENDING);
+    if (action) return cb(Errors.COPAYER_VOTED);
+    if (txp.status != 'pending') return cb(Errors.TX_NOT_PENDING);
 
     txp.reject(self.copayerId, opts.reason);
 
@@ -1373,7 +1373,7 @@ WalletService.prototype.getPendingTxs = function(opts, cb) {
         return _.startsWith(txp.version, '1.');
       });
 
-      if (!allLegacy) return cb(new ClientError(Errors.codes.UPGRADENEEDED, 'Some spend proposals were created using a newer version of the client app'));
+      if (!allLegacy) return cb(new ClientError(Errors.codes.UPGRADE_NEEDED, 'Some spend proposals were created using a newer version of the client app'));
     }
 
     _.each(txps, function(txp) {
diff --git a/test/integration/server.js b/test/integration/server.js
index ed796d9..3f68f96 100644
--- a/test/integration/server.js
+++ b/test/integration/server.js
@@ -731,7 +731,7 @@ describe('Wallet service', function() {
         message: TestData.message.text,
         signature: TestData.message.signature,
       }, function(err, server) {
-        err.code.should.equal('NOTAUTHORIZED');
+        err.code.should.equal('NOT_AUTHORIZED');
         err.message.should.contain('Copayer not found');
         done();
       });
@@ -744,7 +744,7 @@ describe('Wallet service', function() {
           message: 'dummy',
           signature: 'dummy',
         }, function(err, server) {
-          err.code.should.equal('NOTAUTHORIZED');
+          err.code.should.equal('NOT_AUTHORIZED');
           err.message.should.contain('Invalid signature');
           done();
         });
@@ -977,7 +977,7 @@ describe('Wallet service', function() {
         });
         server.joinWallet(copayerOpts, function(err) {
           should.exist(err);
-          err.code.should.equal('WFULL');
+          err.code.should.equal('WALLET_FULL');
           err.message.should.equal('Wallet full');
           done();
         });
@@ -995,7 +995,7 @@ describe('Wallet service', function() {
         should.not.exist(err);
         server.joinWallet(copayerOpts, function(err) {
           should.exist(err);
-          err.code.should.equal('CINWALLET');
+          err.code.should.equal('COPAYER_IN_WALLET');
           err.message.should.equal('Copayer already in wallet');
           done();
         });
@@ -1028,7 +1028,7 @@ describe('Wallet service', function() {
           });
           server.joinWallet(copayerOpts, function(err) {
             should.exist(err);
-            err.code.should.equal('CREGISTERED');
+            err.code.should.equal('COPAYER_REGISTERED');
             err.message.should.equal('Copayer ID already registered on server');
             done();
           });
@@ -1682,13 +1682,13 @@ describe('Wallet service', function() {
         txOpts.excludeUnconfirmedUtxos = true;
         server.createTx(txOpts, function(err, tx) {
           should.exist(err);
-          err.code.should.equal('INSUFFICIENTFUNDS');
+          err.code.should.equal('INSUFFICIENT_FUNDS');
           err.message.should.equal('Insufficient funds');
           var txOpts = helpers.createSimpleProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 2.5, 'some message', TestData.copayers[0].privKey_1H_0);
           txOpts.excludeUnconfirmedUtxos = true;
           server.createTx(txOpts, function(err, tx) {
             should.exist(err);
-            err.code.should.equal('INSUFFICIENTFUNDSFORFEE');
+            err.code.should.equal('INSUFFICIENT_FUNDS_FOR_FEE');
             err.message.should.equal('Insufficient funds for fee');
             done();
           });
@@ -1712,7 +1712,7 @@ describe('Wallet service', function() {
             txOpts.excludeUnconfirmedUtxos = true;
             server.createTx(txOpts, function(err, tx) {
               should.exist(err);
-              err.code.should.equal('LOCKEDFUNDS');
+              err.code.should.equal('LOCKED_FUNDS');
               done();
             });
           });
@@ -1779,7 +1779,7 @@ describe('Wallet service', function() {
         server.createTx(txOpts, function(err, tx) {
           should.not.exist(tx);
           should.exist(err);
-          err.code.should.equal('INVALIDADDRESS');
+          err.code.should.equal('INVALID_ADDRESS');
           err.message.should.equal('Incorrect address network');
           done();
         });
@@ -1801,7 +1801,7 @@ describe('Wallet service', function() {
         var txOpts = helpers.createSimpleProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 120, null, TestData.copayers[0].privKey_1H_0);
         server.createTx(txOpts, function(err, tx) {
           should.exist(err);
-          err.code.should.equal('INSUFFICIENTFUNDS');
+          err.code.should.equal('INSUFFICIENT_FUNDS');
           err.message.should.equal('Insufficient funds');
           server.getPendingTxs({}, function(err, txs) {
             should.not.exist(err);
@@ -1822,7 +1822,7 @@ describe('Wallet service', function() {
         var txOpts = helpers.createSimpleProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 0.048200, null, TestData.copayers[0].privKey_1H_0);
         server.createTx(txOpts, function(err, tx) {
           should.exist(err);
-          err.code.should.equal('INSUFFICIENTFUNDSFORFEE');
+          err.code.should.equal('INSUFFICIENT_FUNDS_FOR_FEE');
           err.message.should.equal('Insufficient funds for fee');
           done();
         });
@@ -1846,7 +1846,7 @@ describe('Wallet service', function() {
         var txOpts = helpers.createSimpleProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 0.99995, null, TestData.copayers[0].privKey_1H_0, 80000);
         server.createTx(txOpts, function(err, tx) {
           should.exist(err);
-          err.code.should.equal('INSUFFICIENTFUNDSFORFEE');
+          err.code.should.equal('INSUFFICIENT_FUNDS_FOR_FEE');
           var txOpts = helpers.createSimpleProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 0.99995, null, TestData.copayers[0].privKey_1H_0, 5000);
           server.createTx(txOpts, function(err, tx) {
             should.not.exist(err);
@@ -1872,7 +1872,7 @@ describe('Wallet service', function() {
         var txOpts = helpers.createSimpleProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 0.00000001, null, TestData.copayers[0].privKey_1H_0);
         server.createTx(txOpts, function(err, tx) {
           should.exist(err);
-          err.code.should.equal('DUSTAMOUNT');
+          err.code.should.equal('DUST_AMOUNT');
           err.message.should.equal('Amount below dust threshold');
           done();
         });
@@ -1888,7 +1888,7 @@ describe('Wallet service', function() {
         var txOpts = helpers.createSimpleProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', amount, null, TestData.copayers[0].privKey_1H_0, 10000);
         server.createTx(txOpts, function(err, tx) {
           should.exist(err);
-          err.code.should.equal('DUSTAMOUNT');
+          err.code.should.equal('DUST_AMOUNT');
           err.message.should.equal('Amount below dust threshold');
           done();
         });
@@ -1907,7 +1907,7 @@ describe('Wallet service', function() {
             txOpts = helpers.createSimpleProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 8, null, TestData.copayers[0].privKey_1H_0);
             server.createTx(txOpts, function(err, tx) {
               should.exist(err);
-              err.code.should.equal('LOCKEDFUNDS');
+              err.code.should.equal('LOCKED_FUNDS');
               err.message.should.equal('Funds are locked by pending transaction proposals');
               done();
             });
@@ -1983,7 +1983,7 @@ describe('Wallet service', function() {
           should.exist(tx);
           var txOpts2 = helpers.createSimpleProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 24, null, TestData.copayers[0].privKey_1H_0);
           server.createTx(txOpts2, function(err, tx) {
-            err.code.should.equal('LOCKEDFUNDS');
+            err.code.should.equal('LOCKED_FUNDS');
             should.not.exist(tx);
             server.getPendingTxs({}, function(err, txs) {
               should.not.exist(err);
@@ -2188,7 +2188,7 @@ describe('Wallet service', function() {
           var txOpts = helpers.createSimpleProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 1, null, TestData.copayers[0].privKey_1H_0);
           server.createTx(txOpts, function(err, tx) {
             should.exist(err);
-            err.code.should.equal('NOTALLOWEDTOCREATETX');
+            err.code.should.equal('TX_CANNOT_CREATE');
             next();
           });
         },
@@ -2210,7 +2210,7 @@ describe('Wallet service', function() {
           server.createTx(txOpts, function(err, tx) {
             clock.restore();
             should.exist(err);
-            err.code.should.equal('NOTALLOWEDTOCREATETX');
+            err.code.should.equal('TX_CANNOT_CREATE');
             next();
           });
         },
@@ -2302,7 +2302,7 @@ describe('Wallet service', function() {
               reason: 'some other reason',
             }, function(err) {
               should.exist(err);
-              err.code.should.equal('TXNOTPENDING');
+              err.code.should.equal('TX_NOT_PENDING');
               done();
             });
           });
@@ -2367,7 +2367,7 @@ describe('Wallet service', function() {
           txProposalId: txid,
           signatures: signatures,
         }, function(err) {
-          err.code.should.contain('BADSIG');
+          err.code.should.equal('BAD_SIGNATURES');
           done();
         });
       });
@@ -2438,7 +2438,7 @@ describe('Wallet service', function() {
           server.rejectTx({
             txProposalId: txid,
           }, function(err) {
-            err.code.should.contain('CVOTED');
+            err.code.should.contain('COPAYER_VOTED');
             done();
           });
         });
@@ -2458,7 +2458,7 @@ describe('Wallet service', function() {
             txProposalId: txid,
             signatures: signatures,
           }, function(err) {
-            err.code.should.contain('CVOTED');
+            err.code.should.contain('COPAYER_VOTED');
             done();
           });
         });
@@ -2507,7 +2507,7 @@ describe('Wallet service', function() {
                 signatures: signatures,
               }, function(err) {
                 should.exist(err);
-                err.code.should.equal('TXNOTPENDING');
+                err.code.should.equal('TX_NOT_PENDING');
                 done();
               });
             });
@@ -2575,7 +2575,7 @@ describe('Wallet service', function() {
           txProposalId: txpid
         }, function(err) {
           should.exist(err);
-          err.code.should.equal('TXALREADYBROADCASTED');
+          err.code.should.equal('TX_ALREADY_BROADCASTED');
           done();
         });
       });
@@ -2591,7 +2591,7 @@ describe('Wallet service', function() {
           txProposalId: txp.id
         }, function(err) {
           should.exist(err);
-          err.code.should.equal('TXNOTACCEPTED');
+          err.code.should.equal('TX_NOT_ACCEPTED');
           done();
         });
       });
@@ -3375,7 +3375,7 @@ describe('Wallet service', function() {
             txProposalId: txp.id
           }, function(err) {
             should.exist(err);
-            err.code.should.equal('TXNOTPENDING');
+            err.code.should.equal('TX_NOT_PENDING');
             done();
           });
         },
@@ -3388,7 +3388,7 @@ describe('Wallet service', function() {
           txProposalId: txp.id
         }, function(err) {
           should.exist(err);
-          err.code.should.contain('TXCANNOTREMOVE');
+          err.code.should.contain('TX_CANNOT_REMOVE');
           server2.getPendingTxs({}, function(err, txs) {
             txs.length.should.equal(1);
             done();
@@ -3408,7 +3408,7 @@ describe('Wallet service', function() {
           server.removePendingTx({
             txProposalId: txp.id
           }, function(err) {
-            err.code.should.equal('TXCANNOTREMOVE');
+            err.code.should.equal('TX_CANNOT_REMOVE');
             err.message.should.contain('Cannot remove');
             done();
           });
@@ -4113,7 +4113,7 @@ describe('Wallet service', function() {
             });
             copayerOpts.isTemporaryRequestKey = false;
             server.replaceTemporaryRequestKey(copayerOpts, function(err, wallet) {
-              err.code.should.equal('WNOTFULL');
+              err.code.should.equal('WALLET_NOT_COMPLETE');
               done();
             });
           });
@@ -4145,7 +4145,7 @@ describe('Wallet service', function() {
             });
             copayerOpts.isTemporaryRequestKey = false;
             server.replaceTemporaryRequestKey(copayerOpts, function(err, wallet) {
-              err.code.should.equal('CDATAMISMATCH');
+              err.code.should.equal('COPAYER_DATA_MISMATCH');
               done();
             });
           });
@@ -4188,7 +4188,7 @@ describe('Wallet service', function() {
               });
               copayerOpts.isTemporaryRequestKey = false;
               server.replaceTemporaryRequestKey(copayerOpts, function(err, wallet) {
-                err.code.should.equal('CDATAMISMATCH');
+                err.code.should.equal('COPAYER_DATA_MISMATCH');
                 done();
               });
             });
@@ -4305,7 +4305,7 @@ describe('Wallet service', function() {
               server.getPendingTxs({}, function(err, txps) {
                 should.exist(err);
                 should.not.exist(txps);
-                err.code.should.equal('UPGRADENEEDED');
+                err.code.should.equal('UPGRADE_NEEDED');
                 err.message.should.contain('newer version');
                 done();
               });
@@ -4364,7 +4364,7 @@ describe('Wallet service', function() {
 
             server.createTx(txOpts, function(err, tx) {
               should.exist(err);
-              err.code.should.equal('INSUFFICIENTFUNDSFORFEE');
+              err.code.should.equal('INSUFFICIENT_FUNDS_FOR_FEE');
               var txOpts = helpers.createSimpleProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 0.99995, null, TestData.copayers[0].privKey_1H_0, 5000);
               server.createTx(txOpts, function(err, tx) {
                 should.not.exist(err);

From 90dfc17ab5c5c5b1b313cd1f25afba06f55cefbe Mon Sep 17 00:00:00 2001
From: Ivan Socolsky <jungans@gmail.com>
Date: Tue, 4 Aug 2015 12:07:25 -0300
Subject: [PATCH 6/6] new error for address network mismatch

---
 lib/errors/errordefinitions.js | 1 +
 lib/server.js                  | 4 ++--
 test/integration/server.js     | 2 +-
 3 files changed, 4 insertions(+), 3 deletions(-)

diff --git a/lib/errors/errordefinitions.js b/lib/errors/errordefinitions.js
index 72f7f10..877be20 100644
--- a/lib/errors/errordefinitions.js
+++ b/lib/errors/errordefinitions.js
@@ -11,6 +11,7 @@ var errors = {
   COPAYER_REGISTERED: 'Copayer ID already registered on server',
   COPAYER_VOTED: 'Copayer already voted on this transaction proposal',
   DUST_AMOUNT: 'Amount below dust threshold',
+  INCORRECT_ADDRESS_NETWORK: 'Incorrect address network',
   INSUFFICIENT_FUNDS: 'Insufficient funds',
   INSUFFICIENT_FUNDS_FOR_FEE: 'Insufficient funds for fee',
   INVALID_ADDRESS: 'Invalid address',
diff --git a/lib/server.js b/lib/server.js
index c558b34..9f6da6f 100644
--- a/lib/server.js
+++ b/lib/server.js
@@ -1009,11 +1009,11 @@ WalletService.prototype.createTx = function(opts, cb) {
           try {
             toAddress = new Bitcore.Address(output.toAddress);
           } catch (ex) {
-            cb(new ClientError(Errors.codes.INVALID_ADDRESS, 'Invalid address'));
+            cb(Errors.INVALID_ADDRESS);
             return false;
           }
           if (toAddress.network != wallet.getNetworkName()) {
-            cb(new ClientError(Errors.codes.INVALID_ADDRESS, 'Incorrect address network'));
+            cb(Errors.INCORRECT_ADDRESS_NETWORK);
             return false;
           }
           if (!_.isNumber(output.amount) || _.isNaN(output.amount) || output.amount <= 0) {
diff --git a/test/integration/server.js b/test/integration/server.js
index 3f68f96..4b5e217 100644
--- a/test/integration/server.js
+++ b/test/integration/server.js
@@ -1779,7 +1779,7 @@ describe('Wallet service', function() {
         server.createTx(txOpts, function(err, tx) {
           should.not.exist(tx);
           should.exist(err);
-          err.code.should.equal('INVALID_ADDRESS');
+          err.code.should.equal('INCORRECT_ADDRESS_NETWORK');
           err.message.should.equal('Incorrect address network');
           done();
         });