From 86adfd0a63009f5b6160f1e0605844937f9ae5f1 Mon Sep 17 00:00:00 2001 From: Josh Harvey Date: Sun, 23 Apr 2017 13:44:36 +0300 Subject: [PATCH] handle cash-in errors --- lib/cash-in-tx.js | 5 +++++ lib/error.js | 8 ++++++-- lib/plugins/wallet/bitcoind/bitcoind.js | 20 ++++--------------- lib/plugins/wallet/bitgo/bitgo.js | 6 +++--- lib/plugins/wallet/mock-wallet/mock-wallet.js | 11 ++++++++-- lib/routes.js | 13 +++++++++++- migrations/026-add_send_confirmed.js | 1 + 7 files changed, 40 insertions(+), 24 deletions(-) diff --git a/lib/cash-in-tx.js b/lib/cash-in-tx.js index 4380872f..61c00bd2 100644 --- a/lib/cash-in-tx.js +++ b/lib/cash-in-tx.js @@ -174,6 +174,11 @@ function postProcess (txVector, pi) { sendConfirmed: true, sendTime: 'now()^' })) + .catch(err => ({ + sendTime: 'now()^', + error: err.message, + errorCode: err.name + })) } return Promise.resolve({}) diff --git a/lib/error.js b/lib/error.js index fdb34774..f7d76775 100644 --- a/lib/error.js +++ b/lib/error.js @@ -1,11 +1,14 @@ -const E = function generateError (name) { +const _ = require('lodash/fp') + +const E = function (name) { var CustomErr = function (msg) { - this.message = msg + this.message = msg || _.startCase(name) this.name = name Error.captureStackTrace(this, CustomErr) } CustomErr.prototype = Object.create(Error.prototype) CustomErr.prototype.constructor = CustomErr + CustomErr.code = name return CustomErr } @@ -18,3 +21,4 @@ function register (errorName) { register('BadNumberError') register('NoDataError') +register('InsufficientFundsError') diff --git a/lib/plugins/wallet/bitcoind/bitcoind.js b/lib/plugins/wallet/bitcoind/bitcoind.js index 883e72a9..b307dbd0 100644 --- a/lib/plugins/wallet/bitcoind/bitcoind.js +++ b/lib/plugins/wallet/bitcoind/bitcoind.js @@ -5,6 +5,7 @@ const fs = require('fs') const pify = require('pify') const BN = require('../../../bn') +const E = require('../../../error') const NAME = 'Bitcoind' @@ -28,12 +29,6 @@ function initRpc () { return new RpcClient(rpcConfig) } -function richError (msg, name) { - const err = new Error(msg) - err.name = name - return err -} - /* * initialize RpcClient */ @@ -68,7 +63,7 @@ function balance (account, cryptoCode) { if (err) return reject(err) if (result.error) { - return reject(richError(result.error, 'bitcoindError')) + return reject(err) } resolve(BN(result.result).shift(SATOSHI_SHIFT).round()) @@ -85,15 +80,8 @@ function sendCoins (account, address, cryptoAtoms, cryptoCode) { checkCryptoCode(cryptoCode) rpc.sendFrom(pluginConfig.account, address, bitcoins, confirmations, (err, result) => { if (err) { - if (err.code === -6) { - return reject(richError('Insufficient funds', 'InsufficientFunds')) - } - - if (err instanceof Error) { - return reject(err) - } - - return reject(richError(err.message, 'bitcoindError')) + if (err.code === -6) return reject(new E.InsufficientFundsError()) + return reject(err) } resolve(result.result) diff --git a/lib/plugins/wallet/bitgo/bitgo.js b/lib/plugins/wallet/bitgo/bitgo.js index 38dbfeb6..cff3b043 100644 --- a/lib/plugins/wallet/bitgo/bitgo.js +++ b/lib/plugins/wallet/bitgo/bitgo.js @@ -1,6 +1,8 @@ const BitGo = require('bitgo') const BN = require('../../../bn') +const E = require('../../../error') + const pjson = require('../../../../package.json') const userAgent = 'Lamassu-Server/' + pjson.version @@ -38,9 +40,7 @@ function sendCoins (account, address, cryptoAtoms, cryptoCode) { return result.hash }) .catch(err => { - if (err.message === 'Insufficient funds') { - err.name = 'InsufficientFunds' - } + if (err.message === 'Insufficient funds') throw new E.InsufficientFundsError() throw err }) } diff --git a/lib/plugins/wallet/mock-wallet/mock-wallet.js b/lib/plugins/wallet/mock-wallet/mock-wallet.js index e5bb4d74..e3c92bef 100644 --- a/lib/plugins/wallet/mock-wallet/mock-wallet.js +++ b/lib/plugins/wallet/mock-wallet/mock-wallet.js @@ -1,4 +1,5 @@ const BN = require('../../../bn') +const E = require('../../../error') const NAME = 'FakeWallet' @@ -18,12 +19,18 @@ function balance (account, cryptoCode) { }) } +function isInsufficient (cryptoCode) { + if (cryptoCode === 'BTC') return BN(1e5 * 10) + if (cryptoCode === 'ETH') return BN(1e18 * 0.25) + throw new Error('Unsupported crypto: ' + cryptoCode) +} function sendCoins (account, toAddress, cryptoAtoms, cryptoCode) { - return new Promise(resolve => { + return new Promise((resolve, reject) => { setTimeout(() => { console.log('[%s] DEBUG: Mock wallet sending %s cryptoAtoms to %s', cryptoCode, cryptoAtoms.toString(), toAddress) - resolve('') + if (isInsufficient(cryptoCode)) return reject(new E.InsufficientFundsError()) + return resolve('') }, 2000) }) } diff --git a/lib/routes.js b/lib/routes.js index df6d78f6..b34e2794 100644 --- a/lib/routes.js +++ b/lib/routes.js @@ -15,6 +15,7 @@ const plugins = require('./plugins') const helpers = require('./route-helpers') const poller = require('./poller') const Tx = require('./tx') +const E = require('./error') const argv = require('minimist')(process.argv.slice(2)) @@ -102,7 +103,17 @@ function postTx (req, res, next) { const pi = plugins(req.settings, req.deviceId) return Tx.post(_.set('deviceId', req.deviceId, req.body), pi) - .then(tx => res.json(tx)) + .then(tx => { + if (tx.errorCode) { + console.log('DEBUG100: %s, %s', tx.errorCode, E.InsufficientFundsError.name) + if (tx.errorCode === E.InsufficientFundsError.code) { + throw httpError(tx.error, 570) + } + throw httpError(tx.error, 500) + } + + return res.json(tx) + }) .catch(next) } diff --git a/migrations/026-add_send_confirmed.js b/migrations/026-add_send_confirmed.js index a696b9af..692a5af7 100644 --- a/migrations/026-add_send_confirmed.js +++ b/migrations/026-add_send_confirmed.js @@ -6,6 +6,7 @@ exports.up = function (next) { 'alter table cash_in_txs add column device_time bigint not null', 'alter table cash_in_txs add column timedout boolean not null default false', 'alter table cash_in_txs add column send_time timestamptz', + 'alter table cash_in_txs add column error_code text', 'alter table cash_out_txs add column device_time bigint not null', 'alter table cash_out_txs add column timedout boolean not null default false' ]