diff --git a/lib/app.js b/lib/app.js index fc1c77ba..700be241 100644 --- a/lib/app.js +++ b/lib/app.js @@ -32,7 +32,7 @@ function runOnce () { const app = express() const localApp = express() - return settingsLoader.load() + return settingsLoader.loadLatest() .then(settings => { poller.start(settings) diff --git a/lib/exchange.js b/lib/exchange.js index d396d863..2908e6ec 100644 --- a/lib/exchange.js +++ b/lib/exchange.js @@ -1,5 +1,4 @@ const configManager = require('./config-manager') -const settingsLoader = require('./settings-loader') function noExchangeError (cryptoCode) { const err = new Error('No exchange plugin defined for: ' + cryptoCode) @@ -8,15 +7,13 @@ function noExchangeError (cryptoCode) { return err } -function lookupExchange (cryptoCode) { - const settings = settingsLoader.settings() +function lookupExchange (settings, cryptoCode) { return configManager.cryptoScoped(cryptoCode, settings.config).exchange } -function fetchExchange (cryptoCode) { +function fetchExchange (settings, cryptoCode) { return Promise.resolve() .then(() => { - const settings = settingsLoader.settings() const plugin = lookupExchange(cryptoCode) if (!plugin) throw noExchangeError(cryptoCode) const account = settings.accounts[plugin] @@ -26,18 +23,18 @@ function fetchExchange (cryptoCode) { }) } -function buy (cryptoAtoms, fiatCode, cryptoCode) { - return fetchExchange(cryptoCode) +function buy (settings, cryptoAtoms, fiatCode, cryptoCode) { + return fetchExchange(settings, cryptoCode) .then(r => r.exchange.buy(r.account, cryptoAtoms, fiatCode, cryptoCode)) } -function sell (cryptoAtoms, fiatCode, cryptoCode) { - return fetchExchange(cryptoCode) +function sell (settings, cryptoAtoms, fiatCode, cryptoCode) { + return fetchExchange(settings, cryptoCode) .then(r => r.exchange.sell(r.account, cryptoAtoms, fiatCode, cryptoCode)) } -function active (fiatCode, cryptoCode) { - return !!lookupExchange(cryptoCode) +function active (settings, fiatCode, cryptoCode) { + return !!lookupExchange(settings, cryptoCode) } module.exports = { diff --git a/lib/plugins.js b/lib/plugins.js index a557413b..4ec6fb39 100644 --- a/lib/plugins.js +++ b/lib/plugins.js @@ -87,6 +87,15 @@ function plugins (settings) { } } + function fetchCurrentConfigVersion () { + const sql = `select id from config_users + where type=$1 + order by id desc + limit 1` + + return db.one(sql, ['config']) + } + function pollQueries (deviceTime, deviceId, deviceRec) { const config = configManager.machineScoped(deviceId, settings.config) const fiatCode = config.fiatCurrency @@ -95,22 +104,29 @@ function plugins (settings) { config.bottomCashOutDenomination ] const virtualCartridges = [config.virtualCashOutDenomination] - const tickerPromises = cryptoCodes.map(c => ticker.getRates(fiatCode, c)) - const balancePromises = cryptoCodes.map(wallet.balance) + const tickerPromises = cryptoCodes.map(c => ticker.getRates(settings, fiatCode, c)) + const balancePromises = cryptoCodes.map(c => wallet.balance(settings, c)) const pingPromise = recordPing(deviceId, deviceTime, deviceRec) + const currentConfigVersionPromise = fetchCurrentConfigVersion() - const promises = [dbm.cartridgeCounts(deviceId), pingPromise].concat(tickerPromises, balancePromises) + const promises = [ + dbm.cartridgeCounts(deviceId), + pingPromise, + currentConfigVersionPromise + ].concat(tickerPromises, balancePromises) return Promise.all(promises) .then(arr => { const cartridgeCounts = arr[0] - const tickers = arr.slice(2, cryptoCodes.length + 2) - const balances = arr.slice(cryptoCodes.length + 2) + const currentConfigVersion = arr[2] + const tickers = arr.slice(3, cryptoCodes.length + 3) + const balances = arr.slice(cryptoCodes.length + 3) return { cartridges: buildCartridges(cartridges, virtualCartridges, cartridgeCounts), rates: buildRates(deviceId, tickers), - balances: buildBalances(deviceId, balances) + balances: buildBalances(deviceId, balances), + currentConfigVersion } }) } @@ -119,7 +135,7 @@ function plugins (settings) { // a dbm unique dbm record in the table already. function executeTx (deviceId, tx) { return dbm.addOutgoingTx(deviceId, tx) - .then(() => wallet.sendCoins(tx.toAddress, tx.cryptoAtoms, tx.cryptoCode)) + .then(() => wallet.sendCoins(settings, tx.toAddress, tx.cryptoAtoms, tx.cryptoCode)) .then(txHash => { const fee = null // Need to fill this out in plugins const toSend = {cryptoAtoms: tx.cryptoAtoms, fiat: tx.fiat} @@ -144,7 +160,7 @@ function plugins (settings) { .then(() => { const market = [fiatCode, cryptoCode].join('') - if (!exchange.active(cryptoCode)) return + if (!exchange.active(settings, cryptoCode)) return logger.debug('[%s] Pushing trade: %d', market, cryptoAtoms) if (!tradesQueues[market]) tradesQueues[market] = [] @@ -187,7 +203,7 @@ function plugins (settings) { serialNumber } - return wallet.newAddress(cryptoCode, info) + return wallet.newAddress(settings, cryptoCode, info) .then(address => { const newTx = R.assoc('toAddress', address, tx) @@ -208,7 +224,10 @@ function plugins (settings) { function fiatBalance (fiatCode, cryptoCode, deviceId) { const config = configManager.scoped(cryptoCode, deviceId, settings.config) - return Promise.all([ticker.getRates(fiatCode, cryptoCode), wallet.balance(cryptoCode)]) + return Promise.all([ + ticker.getRates(settings, fiatCode, cryptoCode), + wallet.balance(settings, cryptoCode) + ]) .then(([rates, balanceRec]) => { const rawRate = rates.rates.ask const commission = (new BigNumber(config.cashInCommission).div(100)).plus(1) @@ -231,7 +250,7 @@ function plugins (settings) { } function processTxStatus (tx) { - return wallet.getStatus(tx.toAddress, tx.cryptoAtoms, tx.cryptoCode) + return wallet.getStatus(settings, tx.toAddress, tx.cryptoAtoms, tx.cryptoCode) .then(res => dbm.updateTxStatus(tx, res.status)) } @@ -355,7 +374,7 @@ function plugins (settings) { } function executeTradesForMarket (settings, fiatCode, cryptoCode) { - if (!exchange.active(cryptoCode)) return + if (!exchange.active(settings, cryptoCode)) return const market = [fiatCode, cryptoCode].join('') logger.debug('[%s] checking for trades', market) @@ -370,7 +389,7 @@ function plugins (settings) { logger.debug('[%s] making a trade: %d', market, tradeEntry.cryptoAtoms.toString()) - return exchange.buy(tradeEntry.cryptoAtoms, tradeEntry.fiatCode, tradeEntry.cryptoCode) + return exchange.buy(settings, tradeEntry.cryptoAtoms, tradeEntry.fiatCode, tradeEntry.cryptoCode) .then(() => logger.debug('[%s] Successful trade.', market)) .catch(err => { tradesQueues[market].push(tradeEntry) @@ -444,7 +463,7 @@ function plugins (settings) { function sweepHD (row) { const cryptoCode = row.crypto_code - return wallet.sweep(row.hd_serial) + return wallet.sweep(settings, row.hd_serial) .then(txHash => { if (txHash) { logger.debug('[%s] Swept address with tx: %s', cryptoCode, txHash) diff --git a/lib/route-helpers.js b/lib/route-helpers.js index b6855917..f15c7951 100644 --- a/lib/route-helpers.js +++ b/lib/route-helpers.js @@ -1,5 +1,6 @@ const R = require('ramda') +const db = require('./db') const dbm = require('./postgresql_interface') const T = require('./time') const TRANSACTION_EXPIRATION = 2 * T.days @@ -32,4 +33,8 @@ function fetchPhoneTx (phone) { }) } -module.exports = {stateChange, fetchPhoneTx} +function updateDeviceConfigVersion (versionId) { + return db.none('update devices set user_config_id=$1', [versionId]) +} + +module.exports = {stateChange, fetchPhoneTx, updateDeviceConfigVersion} diff --git a/lib/routes.js b/lib/routes.js index 3852323b..f1838947 100644 --- a/lib/routes.js +++ b/lib/routes.js @@ -62,7 +62,8 @@ function poll (req, res, next) { reboot, rates: results.rates, balances: results.balances, - coins: config.cryptoCurrencies + coins: config.cryptoCurrencies, + configVersion: results.currentConfigVersion } if (response.idVerificationEnabled) { @@ -421,13 +422,14 @@ function populateDeviceId (req, res, next) { } function populateSettings (req, res, next) { - const versionId = req.headers['config-version-id'] + const versionId = req.headers['config-version'] if (!versionId) { - logger.debug('No config-version-id header') + logger.debug('No config-version header') return res.sendStatus(400) } settingsLoader.log(versionId) .then(settings => { req.settings = settings }) + .then(() => helpers.updateDeviceConfigVersion(versionId)) .catch(next) } diff --git a/lib/settings-loader.js b/lib/settings-loader.js index ca17efd4..5355226b 100644 --- a/lib/settings-loader.js +++ b/lib/settings-loader.js @@ -5,6 +5,8 @@ const db = require('./db') let settingsCache function load (versionId) { + if (!versionId) throw new Error('versionId is required') + return Promise.all([loadConfig(versionId), loadAccounts()]) .then(([config, accounts]) => ({ config, @@ -23,7 +25,7 @@ function loadLatest (versionId) { function loadConfig (versionId) { const sql = `select data from user_config - where versionId=$1 and type=$2` + where id=$1 and type=$2` return db.oneOrNone(sql, [versionId, 'config']) .then(row => row ? row.data.config : []) @@ -33,7 +35,7 @@ function loadLatestConfig () { const sql = `select data from user_config where type=$1 - order by versionId desc + order by id desc limit 1` return db.oneOrNone(sql, ['config']) diff --git a/lib/ticker.js b/lib/ticker.js index 446afc33..16754f2f 100644 --- a/lib/ticker.js +++ b/lib/ticker.js @@ -1,12 +1,10 @@ const mem = require('mem') const configManager = require('./config-manager') -const settingsLoader = require('./settings-loader') const FETCH_INTERVAL = 10000 -function getRates (fiatCode, cryptoCode) { +function getRates (settings, fiatCode, cryptoCode) { return Promise.resolve() .then(() => { - const settings = settingsLoader.settings() const config = settings.config const plugin = configManager.cryptoScoped(cryptoCode, config).ticker diff --git a/lib/wallet.js b/lib/wallet.js index 535c8db1..05cf92dd 100644 --- a/lib/wallet.js +++ b/lib/wallet.js @@ -1,13 +1,11 @@ const mem = require('mem') const configManager = require('./config-manager') -const settingsLoader = require('./settings-loader') const FETCH_INTERVAL = 5000 -function fetchWallet (cryptoCode) { +function fetchWallet (settings, cryptoCode) { return Promise.resolve() .then(() => { - const settings = settingsLoader.settings() const plugin = configManager.cryptoScoped(cryptoCode, settings.config).wallet const account = settings.accounts[plugin] const wallet = require('lamassu-' + plugin) @@ -16,14 +14,14 @@ function fetchWallet (cryptoCode) { }) } -function balance (cryptoCode) { - return fetchWallet(cryptoCode) +function balance (settings, cryptoCode) { + return fetchWallet(settings, cryptoCode) .then(r => r.wallet.balance(r.account, cryptoCode)) .then(balance => ({balance, timestamp: Date.now()})) } -function sendCoins (toAddress, cryptoAtoms, cryptoCode) { - return fetchWallet(cryptoCode) +function sendCoins (settings, toAddress, cryptoAtoms, cryptoCode) { + return fetchWallet(settings, cryptoCode) .then(r => { return r.wallet.sendCoins(r.account, toAddress, cryptoAtoms, cryptoCode) .then(res => { @@ -33,13 +31,13 @@ function sendCoins (toAddress, cryptoAtoms, cryptoCode) { }) } -function newAddress (cryptoCode, info) { - return fetchWallet(cryptoCode) +function newAddress (settings, cryptoCode, info) { + return fetchWallet(settings, cryptoCode) .then(r => r.wallet.newAddress(r.account, cryptoCode, info)) } -function getStatus (toAddress, cryptoAtoms, cryptoCode) { - return fetchWallet(cryptoCode) +function getStatus (settings, toAddress, cryptoAtoms, cryptoCode) { + return fetchWallet(settings, cryptoCode) .then(r => r.wallet.getStatus(r.account, toAddress, cryptoAtoms, cryptoCode)) } diff --git a/migrations/021-config-version-id.js b/migrations/021-config-version-id.js new file mode 100644 index 00000000..7a1ba1ce --- /dev/null +++ b/migrations/021-config-version-id.js @@ -0,0 +1,15 @@ +var db = require('./db') + +exports.up = function (next) { + var sql = [ + 'alter table devices add column user_config_id int', + `ALTER TABLE devices ADD CONSTRAINT user_config_id + FOREIGN KEY (user_config_id) + REFERENCES user_config (id)` + ] + db.multi(sql, next) +} + +exports.down = function (next) { + next() +}