diff --git a/lib/plugins.js b/lib/plugins.js index 43883954..9cc982b3 100644 --- a/lib/plugins.js +++ b/lib/plugins.js @@ -1,61 +1,61 @@ 'use strict' -var fs = require('fs') -var R = require('ramda') -var async = require('async') -var HKDF = require('node-hkdf-sync') -var BigNumber = require('bignumber.js') +const fs = require('fs') +const R = require('ramda') +const async = require('async') +const HKDF = require('node-hkdf-sync') +const BigNumber = require('bignumber.js') BigNumber.config({CRYPTO: true}) -var db = require('./postgresql_interface') -var logger = require('./logger') -var notifier = require('./notifier') +const db = require('./postgresql_interface') +const logger = require('./logger') +const notifier = require('./notifier') -var uuid = require('node-uuid') -var tradeIntervals = {} +const uuid = require('node-uuid') +const tradeIntervals = {} -var CHECK_NOTIFICATION_INTERVAL = 60 * 1000 -var ALERT_SEND_INTERVAL = 60 * 60 * 1000 -var POLLING_RATE = 60 * 1000 // poll each minute -var INCOMING_TX_INTERVAL = 5 * 1000 -var LIVE_INCOMING_TX_INTERVAL = 30 * 1000 -var STALE_INCOMING_TX_AGE = 7 * 24 * 60 * 60 * 1000 -var UNNOTIFIED_INTERVAL = 60 * 1000 -var MAX_NOTIFY_AGE = 48 * 60 * 60 * 1000 -var MIN_NOTIFY_AGE = 5 * 60 * 1000 -var TRANSACTION_EXPIRATION = 48 * 60 * 60 * 1000 -var SWEEP_LIVE_HD_INTERVAL = 60 * 1000 -var SWEEP_OLD_HD_INTERVAL = 2 * 60 * 1000 -var TRADE_INTERVAL = 60 * 1000 -var TRADE_TTL = 5 * 60 * 1000 +const CHECK_NOTIFICATION_INTERVAL = 60 * 1000 +const ALERT_SEND_INTERVAL = 60 * 60 * 1000 +const POLLING_RATE = 60 * 1000 // poll each minute +const INCOMING_TX_INTERVAL = 5 * 1000 +const LIVE_INCOMING_TX_INTERVAL = 30 * 1000 +const STALE_INCOMING_TX_AGE = 7 * 24 * 60 * 60 * 1000 +const UNNOTIFIED_INTERVAL = 60 * 1000 +const MAX_NOTIFY_AGE = 48 * 60 * 60 * 1000 +const MIN_NOTIFY_AGE = 5 * 60 * 1000 +const TRANSACTION_EXPIRATION = 48 * 60 * 60 * 1000 +const SWEEP_LIVE_HD_INTERVAL = 60 * 1000 +const SWEEP_OLD_HD_INTERVAL = 2 * 60 * 1000 +const TRADE_INTERVAL = 60 * 1000 +const TRADE_TTL = 5 * 60 * 1000 -var cryptoCodes = null +let cryptoCodes = null -var tickerPlugins = {} -var traderPlugins = {} -var walletPlugins = {} -var idVerifierPlugin = null -var infoPlugin = null -var emailPlugin = null -var smsPlugin = null -var hkdf = null +const tickerPlugins = {} +const traderPlugins = {} +const walletPlugins = {} +let idVerifierPlugin = null +let infoPlugin = null +let emailPlugin = null +let smsPlugin = null +let hkdf = null -var currentlyUsedPlugins = {} +const currentlyUsedPlugins = {} -var cachedConfig = null -var deviceCurrency = 'USD' +let cachedConfig = null +let deviceCurrency = 'USD' -var lastBalances = {} -var lastRates = {} +const lastBalances = {} +const lastRates = {} -var tradesQueues = {} +const tradesQueues = {} -var coins = { +const coins = { BTC: {unitScale: 8}, ETH: {unitScale: 18} } -var alertFingerprint = null -var lastAlertTime = null +let alertFingerprint = null +let lastAlertTime = null exports.init = function init (connectionString) { const masterSeed = fs.readFileSync('seeds/seed.txt', 'utf8').trim() @@ -66,7 +66,7 @@ exports.init = function init (connectionString) { function loadPlugin (name, config) { // plugins definitions - var moduleMethods = { + const moduleMethods = { ticker: ['ticker'], trader: ['purchase', 'sell'], wallet: ['balance', 'sendBitcoins', 'newAddress'], @@ -75,7 +75,7 @@ function loadPlugin (name, config) { email: ['sendMessage'] } - var plugin = null + let plugin = null // each used plugin MUST be installed try { @@ -97,8 +97,8 @@ function loadPlugin (name, config) { '\'SUPPORTED_MODULES\' constant') } - plugin.SUPPORTED_MODULES.forEach(function (moduleName) { - moduleMethods[moduleName].forEach(function (methodName) { + plugin.SUPPORTED_MODULES.forEach(moduleName => { + moduleMethods[moduleName].forEach(methodName => { if (typeof plugin[methodName] !== 'function') { throw new Error('\'' + name + '\' declares \'' + moduleName + '\', but fails to implement \'' + methodName + '\' method') @@ -115,7 +115,7 @@ function loadPlugin (name, config) { if (typeof plugin.config !== 'function') { logger.warn(new Error('\'' + name + '\' fails to implement *recommended* \'config\' method')) - plugin.config = function () {} + plugin.config = () => {} } else if (config !== null) { plugin.config(config, logger) // only when plugin supports it, and config is passed } @@ -127,17 +127,17 @@ function loadOrConfigPlugin (pluginHandle, pluginType, cryptoCode, options, onChangeCallback) { cryptoCode = cryptoCode || 'BTC' - var currentName = cryptoCode === 'any' || cryptoCode === 'BTC' + const currentName = cryptoCode === 'any' || cryptoCode === 'BTC' ? cachedConfig.exchanges.plugins.current[pluginType] : cachedConfig.exchanges.plugins.current[cryptoCode][pluginType] currentlyUsedPlugins[cryptoCode] = currentlyUsedPlugins[cryptoCode] || {} - var pluginChanged = currentlyUsedPlugins[cryptoCode][pluginType] !== currentName + const pluginChanged = currentlyUsedPlugins[cryptoCode][pluginType] !== currentName if (!currentName) pluginHandle = null else { // some plugins may be disabled - var pluginConfig = cachedConfig.exchanges.plugins.settings[currentName] || {} + const pluginConfig = cachedConfig.exchanges.plugins.settings[currentName] || {} const mergedConfig = R.merge(pluginConfig, options) @@ -167,7 +167,7 @@ exports.configure = function configure (config) { deviceCurrency = config.exchanges.settings.currency cryptoCodes = config.exchanges.settings.coins || ['BTC', 'ETH'] - cryptoCodes.forEach(function (cryptoCode) { + cryptoCodes.forEach(cryptoCode => { // TICKER [required] configure (or load) loadOrConfigPlugin( tickerPlugins[cryptoCode], @@ -252,15 +252,15 @@ function buildCartridges (cartridges, virtualCartridges, rec) { count: parseInt(rec.counts[1], 10) } ], - virtualCartridges: virtualCartridges, + virtualCartridges, id: rec.id } } exports.pollQueries = function pollQueries (session, cb) { - var cartridges = cachedConfig.exchanges.settings.cartridges + const cartridges = cachedConfig.exchanges.settings.cartridges if (!cartridges) return cb(null, {}) - var virtualCartridges = cachedConfig.exchanges.settings.virtualCartridges + const virtualCartridges = cachedConfig.exchanges.settings.virtualCartridges return db.cartridgeCounts(session) .then(result => ({ @@ -278,8 +278,8 @@ function _sendCoins (toAddress, cryptoAtoms, cryptoCode) { } function _sendCoinsCb (toAddress, cryptoAtoms, cryptoCode, cb) { - var walletPlugin = walletPlugins[cryptoCode] - var transactionFee = cachedConfig.exchanges.settings.transactionFee + const walletPlugin = walletPlugins[cryptoCode] + const transactionFee = cachedConfig.exchanges.settings.transactionFee logger.debug('Sending coins [%s] to: %s', cryptoCode, toAddress) if (cryptoCode === 'BTC') { @@ -300,7 +300,7 @@ function executeTx (session, tx) { .then(() => pollBalance(tx.cryptoCode)) .then(() => ({ statusCode: 201, // Created - txHash: txHash, + txHash, txId: tx.txId })) }) @@ -310,15 +310,15 @@ function executeTx (session, tx) { exports.trade = function trade (session, rawTrade) { // TODO: move this to DB, too // add bill to trader queue (if trader is enabled) - var cryptoCode = rawTrade.cryptoCode || 'BTC' - var traderPlugin = traderPlugins[cryptoCode] + const cryptoCode = rawTrade.cryptoCode || 'BTC' + const traderPlugin = traderPlugins[cryptoCode] if (traderPlugin) { logger.debug('[%s] Pushing trade: %d', cryptoCode, rawTrade.cryptoAtoms) tradesQueues[cryptoCode].push({ currency: rawTrade.currency, cryptoAtoms: rawTrade.cryptoAtoms, - cryptoCode: cryptoCode, + cryptoCode, timestamp: Date.now() }) } @@ -327,7 +327,7 @@ exports.trade = function trade (session, rawTrade) { } exports.stateChange = function stateChange (session, rec, cb) { - var event = { + const event = { id: rec.uuid, fingerprint: session.fingerprint, eventType: 'stateChange', @@ -338,7 +338,7 @@ exports.stateChange = function stateChange (session, rec, cb) { } exports.recordPing = function recordPing (session, rec, cb) { - var event = { + const event = { id: uuid.v4(), fingerprint: session.fingerprint, eventType: 'ping', @@ -349,23 +349,23 @@ exports.recordPing = function recordPing (session, rec, cb) { } exports.sendCoins = function sendCoins (session, rawTx) { - var _session = {id: rawTx.sessionId || session.id, fingerprint: session.fingerprint} + const _session = {id: rawTx.sessionId || session.id, fingerprint: session.fingerprint} return executeTx(_session, rawTx) } exports.cashOut = function cashOut (session, tx) { - var cryptoCode = tx.cryptoCode || 'BTC' - var walletPlugin = walletPlugins[cryptoCode] + const cryptoCode = tx.cryptoCode || 'BTC' + const walletPlugin = walletPlugins[cryptoCode] return db.nextCashOutSerialHD(tx.sessionId, cryptoCode) .then(serialNumber => new Promise((resolve, reject) => { const tmpInfo = { label: 'TX ' + Date.now(), account: 'deposit', - serialNumber: serialNumber + serialNumber } - walletPlugin.newAddress(tmpInfo, function (err, address) { + walletPlugin.newAddress(tmpInfo, (err, address) => { if (err) return reject(err) const newTx = R.assoc('toAddress', address, tx) @@ -380,25 +380,25 @@ exports.dispenseAck = function dispenseAck (session, rec) { } exports.fiatBalance = function fiatBalance (cryptoCode) { - var deviceRate = exports.getDeviceRate(cryptoCode) + const deviceRate = exports.getDeviceRate(cryptoCode) if (!deviceRate) return null - var rawRate = deviceRate.rates.ask - var commission = cachedConfig.exchanges.settings.commission - var lastBalanceRec = lastBalances[cryptoCode] + const rawRate = deviceRate.rates.ask + const commission = cachedConfig.exchanges.settings.commission + const lastBalanceRec = lastBalances[cryptoCode] if (!lastBalanceRec) return null - var lastBalance = lastBalanceRec.balance + const lastBalance = lastBalanceRec.balance if (!rawRate || !lastBalance) return null // The rate is actually our commission times real rate. - var rate = rawRate.times(commission) + const rate = rawRate.times(commission) // `lowBalanceMargin` is our safety net. It's a number > 1, and we divide // all our balances by it to provide a safety margin. - var lowBalanceMargin = cachedConfig.exchanges.settings.lowBalanceMargin + const lowBalanceMargin = cachedConfig.exchanges.settings.lowBalanceMargin - var unitScale = new BigNumber(10).pow(coins[cryptoCode].unitScale) - var fiatTransferBalance = lastBalance.div(unitScale).times(rate).div(lowBalanceMargin) + const unitScale = new BigNumber(10).pow(coins[cryptoCode].unitScale) + const fiatTransferBalance = lastBalance.div(unitScale).times(rate).div(lowBalanceMargin) return {timestamp: lastBalanceRec.timestamp, balance: fiatTransferBalance.round(3).toNumber()} } @@ -411,6 +411,7 @@ function processTxStatus (tx) { return walletPlugin.getStatus(tx.toAddress, tx.cryptoAtoms) .then(res => db.updateTxStatus(tx, res.status)) + .catch(err => logger.error('[%s] Tx status processing error: %s', cryptoCode, err.message)) } function notifyConfirmation (tx) { @@ -454,7 +455,7 @@ function monitorUnnotified () { exports.startPolling = function startPolling () { executeTrades() - cryptoCodes.forEach(function (cryptoCode) { + cryptoCodes.forEach(cryptoCode => { setInterval(async.apply(pollBalance, cryptoCode), POLLING_RATE) setInterval(async.apply(pollRate, cryptoCode), POLLING_RATE) startTrader(cryptoCode) @@ -478,13 +479,13 @@ function startTrader (cryptoCode) { // since configuration can always change in `Trader#configure`. // `Trader#executeTrades` returns early if we don't have a trade exchange // configured at the moment. - var traderPlugin = traderPlugins[cryptoCode] + const traderPlugin = traderPlugins[cryptoCode] if (!traderPlugin || tradeIntervals[cryptoCode]) return logger.debug('[%s] startTrader', cryptoCode) tradeIntervals[cryptoCode] = setInterval( - function () { executeTrades(cryptoCode) }, + () => { executeTrades(cryptoCode) }, TRADE_INTERVAL ) } @@ -501,9 +502,9 @@ function stopTrader (cryptoCode) { function pollBalance (cryptoCode, cb) { logger.debug('[%s] collecting balance', cryptoCode) - var walletPlugin = walletPlugins[cryptoCode] + const walletPlugin = walletPlugins[cryptoCode] - walletPlugin.balance(function (err, balance) { + walletPlugin.balance((err, balance) => { if (err) { logger.error('[%s] Error loading balance: %s', cryptoCode, err.message) return cb && cb(err) @@ -517,24 +518,24 @@ function pollBalance (cryptoCode, cb) { } function pollRate (cryptoCode, cb) { - var tickerPlugin = tickerPlugins[cryptoCode] + const tickerPlugin = tickerPlugins[cryptoCode] logger.debug('[%s] polling for rates (%s)', cryptoCode, tickerPlugin.NAME) - var currencies = deviceCurrency + let currencies = deviceCurrency if (typeof currencies === 'string') currencies = [currencies] - var tickerF = cryptoCode === 'BTC' + const tickerF = cryptoCode === 'BTC' ? async.apply(tickerPlugin.ticker, currencies) : async.apply(tickerPlugin.ticker, currencies, cryptoCode) - tickerF(function (err, resRates) { + tickerF((err, resRates) => { if (err) { logger.error(err) return cb && cb(err) } resRates.timestamp = Date.now() - var rates = resRates[deviceCurrency].rates + const rates = resRates[deviceCurrency].rates if (rates) { rates.ask = rates.ask && new BigNumber(rates.ask) rates.bid = rates.bid && new BigNumber(rates.bid) @@ -552,7 +553,7 @@ function pollRate (cryptoCode, cb) { */ exports.getDeviceRate = function getDeviceRate (cryptoCode) { - var lastRate = lastRates[cryptoCode] + const lastRate = lastRates[cryptoCode] if (!lastRate) return null return lastRate[deviceCurrency] @@ -562,14 +563,14 @@ exports.getDeviceRate = function getDeviceRate (cryptoCode) { * Trader functions */ function purchase (trade, cb) { - var cryptoCode = trade.cryptoCode - var traderPlugin = traderPlugins[cryptoCode] - var opts = { - cryptoCode: cryptoCode, + const cryptoCode = trade.cryptoCode + const traderPlugin = traderPlugins[cryptoCode] + const opts = { + cryptoCode, fiat: deviceCurrency } - traderPlugin.purchase(trade.cryptoAtoms, opts, function (err) { + traderPlugin.purchase(trade.cryptoAtoms, opts, err => { if (err) return cb(err) pollBalance(cryptoCode) if (typeof cb === 'function') cb() @@ -599,10 +600,10 @@ function consolidateTrades (cryptoCode) { const cryptoAtoms = filtered .reduce((prev, current) => prev.plus(current.cryptoAtoms), new BigNumber(0)) - var consolidatedTrade = { + const consolidatedTrade = { currency: deviceCurrency, - cryptoAtoms: cryptoAtoms, - cryptoCode: cryptoCode + cryptoAtoms, + cryptoCode } tradesQueues[cryptoCode] = [] @@ -612,12 +613,12 @@ function consolidateTrades (cryptoCode) { } function executeTrades (cryptoCode) { - var traderPlugin = traderPlugins[cryptoCode] + const traderPlugin = traderPlugins[cryptoCode] if (!traderPlugin) return logger.debug('[%s] checking for trades', cryptoCode) - var trade = consolidateTrades(cryptoCode) + const trade = consolidateTrades(cryptoCode) if (trade === null) return logger.debug('[%s] no trades', cryptoCode) if (trade.cryptoAtoms.eq(0)) { @@ -626,7 +627,7 @@ function executeTrades (cryptoCode) { } logger.debug('making a trade: %d', trade.cryptoAtoms.toString()) - purchase(trade, function (err) { + purchase(trade, err => { if (err) { tradesQueues[cryptoCode].push(trade) if (err.name !== 'orderTooSmall') return logger.error(err) @@ -652,8 +653,8 @@ exports.getcryptoCodes = function getcryptoCodes () { } function sendMessage (rec) { - var pluginTypes = JSON.parse(cachedConfig.exchanges.plugins.current.notify) - var pluginPromises = pluginTypes.map(function (pluginType) { + const pluginTypes = JSON.parse(cachedConfig.exchanges.plugins.current.notify) + const pluginPromises = pluginTypes.map(pluginType => { if (pluginType === 'email') return emailPlugin.sendMessage(rec) if (pluginType === 'sms') return smsPlugin.sendMessage(rec) throw new Error('No such plugin type: ' + pluginType) @@ -663,13 +664,13 @@ function sendMessage (rec) { exports.sendMessage = sendMessage function sendNoAlerts () { - var subject = '[Lamassu] All clear' - var rec = { + const subject = '[Lamassu] All clear' + const rec = { sms: { body: subject }, email: { - subject: subject, + subject, body: 'No errors are reported for your machines.' } } @@ -678,26 +679,26 @@ function sendNoAlerts () { function checkNotification () { return notifier.checkStatus() - .then(function (alertRec) { - var fingerprint = notifier.alertFingerprint(alertRec) + .then(alertRec => { + const fingerprint = notifier.alertFingerprint(alertRec) if (!fingerprint) { - var inAlert = !!alertFingerprint + const inAlert = !!alertFingerprint alertFingerprint = null lastAlertTime = null if (inAlert) return sendNoAlerts() } - var alertChanged = fingerprint === alertFingerprint && + const alertChanged = fingerprint === alertFingerprint && lastAlertTime - Date.now() < ALERT_SEND_INTERVAL if (alertChanged) return - var subject = notifier.alertSubject(alertRec) - var rec = { + const subject = notifier.alertSubject(alertRec) + const rec = { sms: { body: subject }, email: { - subject: subject, + subject, body: notifier.printEmailAlerts(alertRec) } } @@ -706,22 +707,22 @@ function checkNotification () { return sendMessage(rec) }) - .then(function () { + .then(() => { logger.debug('Successfully sent alerts') }) - .catch(function (err) { + .catch(err => { logger.error(err) }) } function checkBalances () { - var cryptoCodes = exports.getcryptoCodes() + const cryptoCodes = exports.getcryptoCodes() - var balances = [] - cryptoCodes.forEach(function (cryptoCode) { - var balanceRec = exports.fiatBalance(cryptoCode) + const balances = [] + cryptoCodes.forEach(cryptoCode => { + const balanceRec = exports.fiatBalance(cryptoCode) if (!balanceRec) return - var rec = {fiatBalance: balanceRec.balance, cryptoCode: cryptoCode, + const rec = {fiatBalance: balanceRec.balance, cryptoCode, fiatCode: deviceCurrency} balances.push(rec) }) @@ -730,7 +731,7 @@ function checkBalances () { } exports.startCheckingNotification = function startCheckingNotification () { - var config = cachedConfig.exchanges.plugins.settings.notifier + const config = cachedConfig.exchanges.plugins.settings.notifier notifier.init(db, checkBalances, config) checkNotification() setInterval(checkNotification, CHECK_NOTIFICATION_INTERVAL) @@ -782,13 +783,9 @@ exports.requestDispense = function requestDispense (tx) { return db.addDispenseRequest(tx) } -exports.cachedResponse = function (session, path, method) { - return db.cachedResponse(session, path, method) -} +exports.cachedResponse = (session, path, method) => db.cachedResponse(session, path, method) -exports.cacheResponse = function (session, path, method, body) { - return db.cacheResponse(session, path, method, body) -} +exports.cacheResponse = (session, path, method, body) => db.cacheResponse(session, path, method, body) function sweepHD (row) { const cryptoCode = row.crypto_code @@ -800,7 +797,7 @@ function sweepHD (row) { return db.markSwept(row.session_id) } }) - .catch(err => logger.error(err)) + .catch(err => logger.error('[%s] Sweep error: %s', cryptoCode, err.message)) } function sweepLiveHD () {