fix up crypto services

This commit is contained in:
Josh Harvey 2016-12-09 00:10:18 +02:00
parent e24da06a1b
commit 1b89ed5c76
9 changed files with 82 additions and 46 deletions

View file

@ -32,7 +32,7 @@ function runOnce () {
const app = express() const app = express()
const localApp = express() const localApp = express()
return settingsLoader.load() return settingsLoader.loadLatest()
.then(settings => { .then(settings => {
poller.start(settings) poller.start(settings)

View file

@ -1,5 +1,4 @@
const configManager = require('./config-manager') const configManager = require('./config-manager')
const settingsLoader = require('./settings-loader')
function noExchangeError (cryptoCode) { function noExchangeError (cryptoCode) {
const err = new Error('No exchange plugin defined for: ' + cryptoCode) const err = new Error('No exchange plugin defined for: ' + cryptoCode)
@ -8,15 +7,13 @@ function noExchangeError (cryptoCode) {
return err return err
} }
function lookupExchange (cryptoCode) { function lookupExchange (settings, cryptoCode) {
const settings = settingsLoader.settings()
return configManager.cryptoScoped(cryptoCode, settings.config).exchange return configManager.cryptoScoped(cryptoCode, settings.config).exchange
} }
function fetchExchange (cryptoCode) { function fetchExchange (settings, cryptoCode) {
return Promise.resolve() return Promise.resolve()
.then(() => { .then(() => {
const settings = settingsLoader.settings()
const plugin = lookupExchange(cryptoCode) const plugin = lookupExchange(cryptoCode)
if (!plugin) throw noExchangeError(cryptoCode) if (!plugin) throw noExchangeError(cryptoCode)
const account = settings.accounts[plugin] const account = settings.accounts[plugin]
@ -26,18 +23,18 @@ function fetchExchange (cryptoCode) {
}) })
} }
function buy (cryptoAtoms, fiatCode, cryptoCode) { function buy (settings, cryptoAtoms, fiatCode, cryptoCode) {
return fetchExchange(cryptoCode) return fetchExchange(settings, cryptoCode)
.then(r => r.exchange.buy(r.account, cryptoAtoms, fiatCode, cryptoCode)) .then(r => r.exchange.buy(r.account, cryptoAtoms, fiatCode, cryptoCode))
} }
function sell (cryptoAtoms, fiatCode, cryptoCode) { function sell (settings, cryptoAtoms, fiatCode, cryptoCode) {
return fetchExchange(cryptoCode) return fetchExchange(settings, cryptoCode)
.then(r => r.exchange.sell(r.account, cryptoAtoms, fiatCode, cryptoCode)) .then(r => r.exchange.sell(r.account, cryptoAtoms, fiatCode, cryptoCode))
} }
function active (fiatCode, cryptoCode) { function active (settings, fiatCode, cryptoCode) {
return !!lookupExchange(cryptoCode) return !!lookupExchange(settings, cryptoCode)
} }
module.exports = { module.exports = {

View file

@ -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) { function pollQueries (deviceTime, deviceId, deviceRec) {
const config = configManager.machineScoped(deviceId, settings.config) const config = configManager.machineScoped(deviceId, settings.config)
const fiatCode = config.fiatCurrency const fiatCode = config.fiatCurrency
@ -95,22 +104,29 @@ function plugins (settings) {
config.bottomCashOutDenomination ] config.bottomCashOutDenomination ]
const virtualCartridges = [config.virtualCashOutDenomination] const virtualCartridges = [config.virtualCashOutDenomination]
const tickerPromises = cryptoCodes.map(c => ticker.getRates(fiatCode, c)) const tickerPromises = cryptoCodes.map(c => ticker.getRates(settings, fiatCode, c))
const balancePromises = cryptoCodes.map(wallet.balance) const balancePromises = cryptoCodes.map(c => wallet.balance(settings, c))
const pingPromise = recordPing(deviceId, deviceTime, deviceRec) 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) return Promise.all(promises)
.then(arr => { .then(arr => {
const cartridgeCounts = arr[0] const cartridgeCounts = arr[0]
const tickers = arr.slice(2, cryptoCodes.length + 2) const currentConfigVersion = arr[2]
const balances = arr.slice(cryptoCodes.length + 2) const tickers = arr.slice(3, cryptoCodes.length + 3)
const balances = arr.slice(cryptoCodes.length + 3)
return { return {
cartridges: buildCartridges(cartridges, virtualCartridges, cartridgeCounts), cartridges: buildCartridges(cartridges, virtualCartridges, cartridgeCounts),
rates: buildRates(deviceId, tickers), 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. // a dbm unique dbm record in the table already.
function executeTx (deviceId, tx) { function executeTx (deviceId, tx) {
return dbm.addOutgoingTx(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 => { .then(txHash => {
const fee = null // Need to fill this out in plugins const fee = null // Need to fill this out in plugins
const toSend = {cryptoAtoms: tx.cryptoAtoms, fiat: tx.fiat} const toSend = {cryptoAtoms: tx.cryptoAtoms, fiat: tx.fiat}
@ -144,7 +160,7 @@ function plugins (settings) {
.then(() => { .then(() => {
const market = [fiatCode, cryptoCode].join('') const market = [fiatCode, cryptoCode].join('')
if (!exchange.active(cryptoCode)) return if (!exchange.active(settings, cryptoCode)) return
logger.debug('[%s] Pushing trade: %d', market, cryptoAtoms) logger.debug('[%s] Pushing trade: %d', market, cryptoAtoms)
if (!tradesQueues[market]) tradesQueues[market] = [] if (!tradesQueues[market]) tradesQueues[market] = []
@ -187,7 +203,7 @@ function plugins (settings) {
serialNumber serialNumber
} }
return wallet.newAddress(cryptoCode, info) return wallet.newAddress(settings, cryptoCode, info)
.then(address => { .then(address => {
const newTx = R.assoc('toAddress', address, tx) const newTx = R.assoc('toAddress', address, tx)
@ -208,7 +224,10 @@ function plugins (settings) {
function fiatBalance (fiatCode, cryptoCode, deviceId) { function fiatBalance (fiatCode, cryptoCode, deviceId) {
const config = configManager.scoped(cryptoCode, deviceId, settings.config) 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]) => { .then(([rates, balanceRec]) => {
const rawRate = rates.rates.ask const rawRate = rates.rates.ask
const commission = (new BigNumber(config.cashInCommission).div(100)).plus(1) const commission = (new BigNumber(config.cashInCommission).div(100)).plus(1)
@ -231,7 +250,7 @@ function plugins (settings) {
} }
function processTxStatus (tx) { 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)) .then(res => dbm.updateTxStatus(tx, res.status))
} }
@ -355,7 +374,7 @@ function plugins (settings) {
} }
function executeTradesForMarket (settings, fiatCode, cryptoCode) { function executeTradesForMarket (settings, fiatCode, cryptoCode) {
if (!exchange.active(cryptoCode)) return if (!exchange.active(settings, cryptoCode)) return
const market = [fiatCode, cryptoCode].join('') const market = [fiatCode, cryptoCode].join('')
logger.debug('[%s] checking for trades', market) 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()) 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)) .then(() => logger.debug('[%s] Successful trade.', market))
.catch(err => { .catch(err => {
tradesQueues[market].push(tradeEntry) tradesQueues[market].push(tradeEntry)
@ -444,7 +463,7 @@ function plugins (settings) {
function sweepHD (row) { function sweepHD (row) {
const cryptoCode = row.crypto_code const cryptoCode = row.crypto_code
return wallet.sweep(row.hd_serial) return wallet.sweep(settings, row.hd_serial)
.then(txHash => { .then(txHash => {
if (txHash) { if (txHash) {
logger.debug('[%s] Swept address with tx: %s', cryptoCode, txHash) logger.debug('[%s] Swept address with tx: %s', cryptoCode, txHash)

View file

@ -1,5 +1,6 @@
const R = require('ramda') const R = require('ramda')
const db = require('./db')
const dbm = require('./postgresql_interface') const dbm = require('./postgresql_interface')
const T = require('./time') const T = require('./time')
const TRANSACTION_EXPIRATION = 2 * T.days 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}

View file

@ -62,7 +62,8 @@ function poll (req, res, next) {
reboot, reboot,
rates: results.rates, rates: results.rates,
balances: results.balances, balances: results.balances,
coins: config.cryptoCurrencies coins: config.cryptoCurrencies,
configVersion: results.currentConfigVersion
} }
if (response.idVerificationEnabled) { if (response.idVerificationEnabled) {
@ -421,13 +422,14 @@ function populateDeviceId (req, res, next) {
} }
function populateSettings (req, res, next) { function populateSettings (req, res, next) {
const versionId = req.headers['config-version-id'] const versionId = req.headers['config-version']
if (!versionId) { if (!versionId) {
logger.debug('No config-version-id header') logger.debug('No config-version header')
return res.sendStatus(400) return res.sendStatus(400)
} }
settingsLoader.log(versionId) settingsLoader.log(versionId)
.then(settings => { req.settings = settings }) .then(settings => { req.settings = settings })
.then(() => helpers.updateDeviceConfigVersion(versionId))
.catch(next) .catch(next)
} }

View file

@ -5,6 +5,8 @@ const db = require('./db')
let settingsCache let settingsCache
function load (versionId) { function load (versionId) {
if (!versionId) throw new Error('versionId is required')
return Promise.all([loadConfig(versionId), loadAccounts()]) return Promise.all([loadConfig(versionId), loadAccounts()])
.then(([config, accounts]) => ({ .then(([config, accounts]) => ({
config, config,
@ -23,7 +25,7 @@ function loadLatest (versionId) {
function loadConfig (versionId) { function loadConfig (versionId) {
const sql = `select data const sql = `select data
from user_config from user_config
where versionId=$1 and type=$2` where id=$1 and type=$2`
return db.oneOrNone(sql, [versionId, 'config']) return db.oneOrNone(sql, [versionId, 'config'])
.then(row => row ? row.data.config : []) .then(row => row ? row.data.config : [])
@ -33,7 +35,7 @@ function loadLatestConfig () {
const sql = `select data const sql = `select data
from user_config from user_config
where type=$1 where type=$1
order by versionId desc order by id desc
limit 1` limit 1`
return db.oneOrNone(sql, ['config']) return db.oneOrNone(sql, ['config'])

View file

@ -1,12 +1,10 @@
const mem = require('mem') const mem = require('mem')
const configManager = require('./config-manager') const configManager = require('./config-manager')
const settingsLoader = require('./settings-loader')
const FETCH_INTERVAL = 10000 const FETCH_INTERVAL = 10000
function getRates (fiatCode, cryptoCode) { function getRates (settings, fiatCode, cryptoCode) {
return Promise.resolve() return Promise.resolve()
.then(() => { .then(() => {
const settings = settingsLoader.settings()
const config = settings.config const config = settings.config
const plugin = configManager.cryptoScoped(cryptoCode, config).ticker const plugin = configManager.cryptoScoped(cryptoCode, config).ticker

View file

@ -1,13 +1,11 @@
const mem = require('mem') const mem = require('mem')
const configManager = require('./config-manager') const configManager = require('./config-manager')
const settingsLoader = require('./settings-loader')
const FETCH_INTERVAL = 5000 const FETCH_INTERVAL = 5000
function fetchWallet (cryptoCode) { function fetchWallet (settings, cryptoCode) {
return Promise.resolve() return Promise.resolve()
.then(() => { .then(() => {
const settings = settingsLoader.settings()
const plugin = configManager.cryptoScoped(cryptoCode, settings.config).wallet const plugin = configManager.cryptoScoped(cryptoCode, settings.config).wallet
const account = settings.accounts[plugin] const account = settings.accounts[plugin]
const wallet = require('lamassu-' + plugin) const wallet = require('lamassu-' + plugin)
@ -16,14 +14,14 @@ function fetchWallet (cryptoCode) {
}) })
} }
function balance (cryptoCode) { function balance (settings, cryptoCode) {
return fetchWallet(cryptoCode) return fetchWallet(settings, cryptoCode)
.then(r => r.wallet.balance(r.account, cryptoCode)) .then(r => r.wallet.balance(r.account, cryptoCode))
.then(balance => ({balance, timestamp: Date.now()})) .then(balance => ({balance, timestamp: Date.now()}))
} }
function sendCoins (toAddress, cryptoAtoms, cryptoCode) { function sendCoins (settings, toAddress, cryptoAtoms, cryptoCode) {
return fetchWallet(cryptoCode) return fetchWallet(settings, cryptoCode)
.then(r => { .then(r => {
return r.wallet.sendCoins(r.account, toAddress, cryptoAtoms, cryptoCode) return r.wallet.sendCoins(r.account, toAddress, cryptoAtoms, cryptoCode)
.then(res => { .then(res => {
@ -33,13 +31,13 @@ function sendCoins (toAddress, cryptoAtoms, cryptoCode) {
}) })
} }
function newAddress (cryptoCode, info) { function newAddress (settings, cryptoCode, info) {
return fetchWallet(cryptoCode) return fetchWallet(settings, cryptoCode)
.then(r => r.wallet.newAddress(r.account, cryptoCode, info)) .then(r => r.wallet.newAddress(r.account, cryptoCode, info))
} }
function getStatus (toAddress, cryptoAtoms, cryptoCode) { function getStatus (settings, toAddress, cryptoAtoms, cryptoCode) {
return fetchWallet(cryptoCode) return fetchWallet(settings, cryptoCode)
.then(r => r.wallet.getStatus(r.account, toAddress, cryptoAtoms, cryptoCode)) .then(r => r.wallet.getStatus(r.account, toAddress, cryptoAtoms, cryptoCode))
} }

View file

@ -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()
}