WIPP
This commit is contained in:
parent
0bf54fa1d8
commit
30071151ff
6 changed files with 107 additions and 242 deletions
|
|
@ -3,17 +3,19 @@ const pgp = require('pg-promise')()
|
||||||
const db = require('./db')
|
const db = require('./db')
|
||||||
const BN = require('./bn')
|
const BN = require('./bn')
|
||||||
|
|
||||||
module.exports = {postCashIn}
|
module.exports = {post}
|
||||||
|
|
||||||
const UPDATEABLE_FIELDS = ['fee', 'txHash', 'phone', 'error']
|
const UPDATEABLE_FIELDS = ['fee', 'txHash', 'phone', 'error', 'send']
|
||||||
|
|
||||||
function postCashIn (tx, pi) {
|
function post (tx, pi) {
|
||||||
const TransactionMode = pgp.txMode.TransactionMode
|
const TransactionMode = pgp.txMode.TransactionMode
|
||||||
const isolationLevel = pgp.txMode.isolationLevel
|
const isolationLevel = pgp.txMode.isolationLevel
|
||||||
const tmSRD = new TransactionMode({tiLevel: isolationLevel.serializable})
|
const tmSRD = new TransactionMode({tiLevel: isolationLevel.serializable})
|
||||||
|
|
||||||
function transaction (t) {
|
function transaction (t) {
|
||||||
const sql = 'select * from cash_in_txs where id=$1'
|
const sql = 'select * from cash_in_txs where id=$1'
|
||||||
|
|
||||||
|
console.log('DEBUG888: %j', tx)
|
||||||
return t.oneOrNone(sql, [tx.id])
|
return t.oneOrNone(sql, [tx.id])
|
||||||
.then(row => upsert(row, tx))
|
.then(row => upsert(row, tx))
|
||||||
}
|
}
|
||||||
|
|
@ -21,15 +23,18 @@ function postCashIn (tx, pi) {
|
||||||
transaction.txMode = tmSRD
|
transaction.txMode = tmSRD
|
||||||
|
|
||||||
return db.tx(transaction)
|
return db.tx(transaction)
|
||||||
.then(txVector => postProcess(txVector, pi))
|
.then(txVector => {
|
||||||
.then(changes => update(tx.id, changes))
|
const [, newTx] = txVector
|
||||||
|
postProcess(txVector, pi)
|
||||||
|
.then(changes => update(newTx, changes))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function diff (oldTx, newTx) {
|
function diff (oldTx, newTx) {
|
||||||
let updatedTx = {}
|
let updatedTx = {}
|
||||||
|
|
||||||
UPDATEABLE_FIELDS.forEach(fieldKey => {
|
UPDATEABLE_FIELDS.forEach(fieldKey => {
|
||||||
if (_.isEqual(oldTx[fieldKey], newTx[fieldKey])) return
|
if (oldTx && _.isEqual(oldTx[fieldKey], newTx[fieldKey])) return
|
||||||
updatedTx[fieldKey] = newTx[fieldKey]
|
updatedTx[fieldKey] = newTx[fieldKey]
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -37,6 +42,8 @@ function diff (oldTx, newTx) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function toObj (row) {
|
function toObj (row) {
|
||||||
|
if (!row) return null
|
||||||
|
|
||||||
const keys = _.keys(row)
|
const keys = _.keys(row)
|
||||||
let newObj = {}
|
let newObj = {}
|
||||||
|
|
||||||
|
|
@ -56,31 +63,48 @@ function toObj (row) {
|
||||||
function upsert (row, tx) {
|
function upsert (row, tx) {
|
||||||
const oldTx = toObj(row)
|
const oldTx = toObj(row)
|
||||||
|
|
||||||
if (oldTx) return insert(tx)
|
// insert bills
|
||||||
return update(tx.id, diff(oldTx, tx))
|
|
||||||
|
if (!oldTx) {
|
||||||
|
return insert(tx)
|
||||||
|
.then(newTx => [oldTx, newTx])
|
||||||
|
}
|
||||||
|
|
||||||
|
return update(tx, diff(oldTx, tx))
|
||||||
|
.then(newTx => [oldTx, newTx])
|
||||||
}
|
}
|
||||||
|
|
||||||
function insert (tx) {
|
function insert (tx) {
|
||||||
const dbTx = _.mapKeys(_.snakeCase, tx)
|
const dbTx = _.mapKeys(_.snakeCase, _.omit(['direction', 'bills'], tx))
|
||||||
|
|
||||||
const sql = pgp.helpers.insert(dbTx, null, 'cash_in_txs')
|
const sql = pgp.helpers.insert(dbTx, null, 'cash_in_txs') + ' returning *'
|
||||||
return db.none(sql)
|
return db.one(sql)
|
||||||
|
.then(toObj)
|
||||||
}
|
}
|
||||||
|
|
||||||
function update (txId, changes) {
|
function update (tx, changes) {
|
||||||
const dbChanges = _.mapKeys(_.snakeCase, changes)
|
if (_.isEmpty(changes)) return Promise.resolve(tx)
|
||||||
const sql = pgp.helpers.update(dbChanges, null, 'cash_in_txs') +
|
|
||||||
pgp.as.format(' where id=$1', [txId])
|
|
||||||
|
|
||||||
return db.none(sql)
|
const dbChanges = _.mapKeys(_.snakeCase, _.omit(['direction', 'bills'], changes))
|
||||||
|
console.log('DEBUG893: %j', dbChanges)
|
||||||
|
const sql = pgp.helpers.update(dbChanges, null, 'cash_in_txs') +
|
||||||
|
pgp.as.format(' where id=$1', [tx.id]) + ' returning *'
|
||||||
|
|
||||||
|
return db.one(sql)
|
||||||
|
.then(toObj)
|
||||||
}
|
}
|
||||||
|
|
||||||
function postProcess (txVector, pi) {
|
function postProcess (txVector, pi) {
|
||||||
const [oldTx, newTx] = txVector
|
const [oldTx, newTx] = txVector
|
||||||
|
|
||||||
if (newTx.sent && !oldTx.sent) {
|
if (newTx.send && !oldTx.send) {
|
||||||
return pi.sendCoins(newTx)
|
return pi.sendCoins(newTx)
|
||||||
.then(txHash => ({txHash}))
|
.then(txHash => ({txHash}))
|
||||||
.catch(error => ({error}))
|
.catch(error => {
|
||||||
|
console.log('DEBUG895: %j', error)
|
||||||
|
return {error}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return Promise.resolve({})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,8 +30,8 @@ const coins = {
|
||||||
ETH: {unitScale: 18}
|
ETH: {unitScale: 18}
|
||||||
}
|
}
|
||||||
|
|
||||||
function plugins (settings) {
|
function plugins (settings, deviceId) {
|
||||||
function buildRates (deviceId, tickers) {
|
function buildRates (tickers) {
|
||||||
const config = configManager.machineScoped(deviceId, settings.config)
|
const config = configManager.machineScoped(deviceId, settings.config)
|
||||||
const cryptoCodes = config.cryptoCurrencies
|
const cryptoCodes = config.cryptoCurrencies
|
||||||
const cashOut = config.cashOutEnabled
|
const cashOut = config.cashOutEnabled
|
||||||
|
|
@ -41,6 +41,7 @@ function plugins (settings) {
|
||||||
|
|
||||||
const rates = {}
|
const rates = {}
|
||||||
|
|
||||||
|
console.log('DEBUG444: %j', tickers)
|
||||||
cryptoCodes.forEach((cryptoCode, i) => {
|
cryptoCodes.forEach((cryptoCode, i) => {
|
||||||
const rateRec = tickers[i]
|
const rateRec = tickers[i]
|
||||||
if (Date.now() - rateRec.timestamp > STALE_TICKER) return logger.warn('Stale rate for ' + cryptoCode)
|
if (Date.now() - rateRec.timestamp > STALE_TICKER) return logger.warn('Stale rate for ' + cryptoCode)
|
||||||
|
|
@ -54,7 +55,7 @@ function plugins (settings) {
|
||||||
return rates
|
return rates
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildBalances (deviceId, balanceRecs) {
|
function buildBalances (balanceRecs) {
|
||||||
const config = configManager.machineScoped(deviceId, settings.config)
|
const config = configManager.machineScoped(deviceId, settings.config)
|
||||||
const cryptoCodes = config.cryptoCurrencies
|
const cryptoCodes = config.cryptoCurrencies
|
||||||
|
|
||||||
|
|
@ -97,7 +98,7 @@ function plugins (settings) {
|
||||||
.then(row => row.id)
|
.then(row => row.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
function pollQueries (deviceTime, deviceId, deviceRec) {
|
function pollQueries (deviceTime, deviceRec) {
|
||||||
const config = configManager.machineScoped(deviceId, settings.config)
|
const config = configManager.machineScoped(deviceId, settings.config)
|
||||||
const fiatCode = config.fiatCurrency
|
const fiatCode = config.fiatCurrency
|
||||||
const cryptoCodes = config.cryptoCurrencies
|
const cryptoCodes = config.cryptoCurrencies
|
||||||
|
|
@ -106,8 +107,8 @@ function plugins (settings) {
|
||||||
const virtualCartridges = [config.virtualCashOutDenomination]
|
const virtualCartridges = [config.virtualCashOutDenomination]
|
||||||
|
|
||||||
const tickerPromises = cryptoCodes.map(c => ticker.getRates(settings, fiatCode, c))
|
const tickerPromises = cryptoCodes.map(c => ticker.getRates(settings, fiatCode, c))
|
||||||
const balancePromises = cryptoCodes.map(c => fiatBalance(fiatCode, c, deviceId))
|
const balancePromises = cryptoCodes.map(c => fiatBalance(fiatCode, c))
|
||||||
const pingPromise = recordPing(deviceId, deviceTime, deviceRec)
|
const pingPromise = recordPing(deviceTime, deviceRec)
|
||||||
const currentConfigVersionPromise = fetchCurrentConfigVersion()
|
const currentConfigVersionPromise = fetchCurrentConfigVersion()
|
||||||
|
|
||||||
const promises = [
|
const promises = [
|
||||||
|
|
@ -125,8 +126,8 @@ function plugins (settings) {
|
||||||
|
|
||||||
return {
|
return {
|
||||||
cartridges: buildCartridges(cartridges, virtualCartridges, cartridgeCounts),
|
cartridges: buildCartridges(cartridges, virtualCartridges, cartridgeCounts),
|
||||||
rates: buildRates(deviceId, tickers),
|
rates: buildRates(tickers),
|
||||||
balances: buildBalances(deviceId, balances),
|
balances: buildBalances(balances),
|
||||||
currentConfigVersion
|
currentConfigVersion
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
@ -134,23 +135,12 @@ function plugins (settings) {
|
||||||
|
|
||||||
// NOTE: This will fail if we have already sent coins because there will be
|
// NOTE: This will fail if we have already sent coins because there will be
|
||||||
// a unique dbm record in the table already.
|
// a unique dbm record in the table already.
|
||||||
function sendCoins (deviceId, tx) {
|
function sendCoins (tx) {
|
||||||
return dbm.addOutgoingTx(deviceId, tx)
|
console.log('DEBUG50: %j', settings)
|
||||||
.then(() => wallet.sendCoins(settings, tx.toAddress, tx.cryptoAtoms, tx.cryptoCode))
|
return 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}
|
|
||||||
|
|
||||||
return dbm.sentCoins(tx, toSend, fee, null, txHash)
|
|
||||||
.then(() => ({
|
|
||||||
statusCode: 201, // Created
|
|
||||||
txHash,
|
|
||||||
txId: tx.id
|
|
||||||
}))
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function trade (deviceId, rawTrade) {
|
function trade (rawTrade) {
|
||||||
// TODO: move this to dbm, too
|
// TODO: move this to dbm, too
|
||||||
// add bill to trader queue (if trader is enabled)
|
// add bill to trader queue (if trader is enabled)
|
||||||
const cryptoCode = rawTrade.cryptoCode
|
const cryptoCode = rawTrade.cryptoCode
|
||||||
|
|
@ -174,18 +164,18 @@ function plugins (settings) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function recordPing (deviceId, deviceTime, rec) {
|
function recordPing (deviceTime, rec) {
|
||||||
const event = {
|
const event = {
|
||||||
id: uuid.v4(),
|
id: uuid.v4(),
|
||||||
deviceId: deviceId,
|
deviceId,
|
||||||
eventType: 'ping',
|
eventType: 'ping',
|
||||||
note: JSON.stringify({state: rec.state, isIdle: rec.idle === 'true', txId: rec.txId}),
|
note: JSON.stringify({state: rec.state, isIdle: rec.idle === 'true', txId: rec.txId}),
|
||||||
deviceTime: deviceTime
|
deviceTime
|
||||||
}
|
}
|
||||||
return dbm.machineEvent(event)
|
return dbm.machineEvent(event)
|
||||||
}
|
}
|
||||||
|
|
||||||
function cashOut (deviceId, tx) {
|
function cashOut (tx) {
|
||||||
const cryptoCode = tx.cryptoCode
|
const cryptoCode = tx.cryptoCode
|
||||||
|
|
||||||
const serialPromise = wallet.supportsHD
|
const serialPromise = wallet.supportsHD
|
||||||
|
|
@ -210,7 +200,7 @@ function plugins (settings) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function dispenseAck (deviceId, tx) {
|
function dispenseAck (tx) {
|
||||||
const config = configManager.machineScoped(deviceId, settings.config)
|
const config = configManager.machineScoped(deviceId, settings.config)
|
||||||
const cartridges = [ config.topCashOutDenomination,
|
const cartridges = [ config.topCashOutDenomination,
|
||||||
config.bottomCashOutDenomination ]
|
config.bottomCashOutDenomination ]
|
||||||
|
|
@ -218,7 +208,7 @@ function plugins (settings) {
|
||||||
return dbm.addDispense(deviceId, tx, cartridges)
|
return dbm.addDispense(deviceId, tx, cartridges)
|
||||||
}
|
}
|
||||||
|
|
||||||
function fiatBalance (fiatCode, cryptoCode, deviceId) {
|
function fiatBalance (fiatCode, cryptoCode) {
|
||||||
const config = configManager.scoped(cryptoCode, deviceId, settings.config)
|
const config = configManager.scoped(cryptoCode, deviceId, settings.config)
|
||||||
|
|
||||||
return Promise.all([
|
return Promise.all([
|
||||||
|
|
@ -404,11 +394,11 @@ function plugins (settings) {
|
||||||
return Promise.all(promises)
|
return Promise.all(promises)
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkDeviceBalances (deviceId) {
|
function checkDeviceBalances (_deviceId) {
|
||||||
const config = configManager.machineScoped(deviceId, settings.config)
|
const config = configManager.machineScoped(_deviceId, settings.config)
|
||||||
const cryptoCodes = config.cryptoCurrencies
|
const cryptoCodes = config.cryptoCurrencies
|
||||||
const fiatCode = config.fiatCurrency
|
const fiatCode = config.fiatCurrency
|
||||||
const fiatBalancePromises = cryptoCodes.map(c => fiatBalance(fiatCode, c, deviceId))
|
const fiatBalancePromises = cryptoCodes.map(c => fiatBalance(fiatCode, c))
|
||||||
|
|
||||||
return Promise.all(fiatBalancePromises)
|
return Promise.all(fiatBalancePromises)
|
||||||
.then(arr => {
|
.then(arr => {
|
||||||
|
|
@ -416,7 +406,7 @@ function plugins (settings) {
|
||||||
fiatBalance: balance,
|
fiatBalance: balance,
|
||||||
cryptoCode: cryptoCodes[i],
|
cryptoCode: cryptoCodes[i],
|
||||||
fiatCode,
|
fiatCode,
|
||||||
deviceId
|
_deviceId
|
||||||
}))
|
}))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
180
lib/routes.js
180
lib/routes.js
|
|
@ -3,15 +3,12 @@
|
||||||
const morgan = require('morgan')
|
const morgan = require('morgan')
|
||||||
const helmet = require('helmet')
|
const helmet = require('helmet')
|
||||||
const bodyParser = require('body-parser')
|
const bodyParser = require('body-parser')
|
||||||
const BigNumber = require('bignumber.js')
|
|
||||||
const _ = require('lodash/fp')
|
const _ = require('lodash/fp')
|
||||||
const express = require('express')
|
const express = require('express')
|
||||||
|
|
||||||
const options = require('./options')
|
const options = require('./options')
|
||||||
const logger = require('./logger')
|
const logger = require('./logger')
|
||||||
const configManager = require('./config-manager')
|
const configManager = require('./config-manager')
|
||||||
const db = require('./db')
|
|
||||||
const dbm = require('./postgresql_interface')
|
|
||||||
const pairing = require('./pairing')
|
const pairing = require('./pairing')
|
||||||
const settingsLoader = require('./settings-loader')
|
const settingsLoader = require('./settings-loader')
|
||||||
const plugins = require('./plugins')
|
const plugins = require('./plugins')
|
||||||
|
|
@ -35,11 +32,11 @@ function poll (req, res, next) {
|
||||||
const pid = req.query.pid
|
const pid = req.query.pid
|
||||||
const settings = req.settings
|
const settings = req.settings
|
||||||
const config = configManager.machineScoped(deviceId, settings.config)
|
const config = configManager.machineScoped(deviceId, settings.config)
|
||||||
const pi = plugins(settings)
|
const pi = plugins(settings, deviceId)
|
||||||
|
|
||||||
pids[deviceId] = {pid, ts: Date.now()}
|
pids[deviceId] = {pid, ts: Date.now()}
|
||||||
|
|
||||||
pi.pollQueries(deviceTime, deviceId, req.query)
|
pi.pollQueries(deviceTime, req.query)
|
||||||
.then(results => {
|
.then(results => {
|
||||||
const cartridges = results.cartridges
|
const cartridges = results.cartridges
|
||||||
|
|
||||||
|
|
@ -82,77 +79,38 @@ function poll (req, res, next) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function postTx (req, res, next) {
|
function postTx (req, res, next) {
|
||||||
return Tx.post(req.body)
|
console.log('DEBUG60: %j', req.settings)
|
||||||
|
const pi = plugins(req.settings, req.deviceId)
|
||||||
|
|
||||||
|
return Tx.post(_.set('deviceId', req.deviceId, req.body), pi)
|
||||||
.then(tx => res.json(tx))
|
.then(tx => res.json(tx))
|
||||||
.catch(next)
|
.catch(next)
|
||||||
}
|
}
|
||||||
|
|
||||||
function trade (req, res, next) {
|
|
||||||
const tx = req.body
|
|
||||||
const pi = plugins(req.settings)
|
|
||||||
|
|
||||||
tx.cryptoAtoms = new BigNumber(tx.cryptoAtoms)
|
|
||||||
|
|
||||||
pi.trade(req.deviceId, tx)
|
|
||||||
.then(() => cacheAndRespond(req, res))
|
|
||||||
.catch(next)
|
|
||||||
}
|
|
||||||
|
|
||||||
function stateChange (req, res, next) {
|
function stateChange (req, res, next) {
|
||||||
helpers.stateChange(req.deviceId, req.deviceTime, req.body)
|
helpers.stateChange(req.deviceId, req.deviceTime, req.body)
|
||||||
.then(() => cacheAndRespond(req, res))
|
.then(() => respond(req, res))
|
||||||
.catch(next)
|
|
||||||
}
|
|
||||||
|
|
||||||
function send (req, res, next) {
|
|
||||||
const pi = plugins(req.settings)
|
|
||||||
const tx = req.body
|
|
||||||
tx.cryptoAtoms = new BigNumber(tx.cryptoAtoms)
|
|
||||||
|
|
||||||
return pi.sendCoins(req.deviceId, tx)
|
|
||||||
.then(status => {
|
|
||||||
const body = {txId: status && status.txId}
|
|
||||||
return cacheAndRespond(req, res, body)
|
|
||||||
})
|
|
||||||
.catch(next)
|
|
||||||
}
|
|
||||||
|
|
||||||
function cashOut (req, res, next) {
|
|
||||||
const pi = plugins(req.settings)
|
|
||||||
logger.info({tx: req.body, cmd: 'cashOut'})
|
|
||||||
const tx = req.body
|
|
||||||
tx.cryptoAtoms = new BigNumber(tx.cryptoAtoms)
|
|
||||||
|
|
||||||
return pi.cashOut(req.deviceId, tx)
|
|
||||||
.then(cryptoAddress => cacheAndRespond(req, res, {toAddress: cryptoAddress}))
|
|
||||||
.catch(next)
|
|
||||||
}
|
|
||||||
|
|
||||||
function dispenseAck (req, res, next) {
|
|
||||||
const pi = plugins(req.settings)
|
|
||||||
pi.dispenseAck(req.deviceId, req.body.tx)
|
|
||||||
.then(() => cacheAndRespond(req, res))
|
|
||||||
.catch(next)
|
.catch(next)
|
||||||
}
|
}
|
||||||
|
|
||||||
function deviceEvent (req, res, next) {
|
function deviceEvent (req, res, next) {
|
||||||
const pi = plugins(req.settings)
|
const pi = plugins(req.settings, req.deviceId)
|
||||||
pi.logEvent(req.deviceId, req.body)
|
pi.logEvent(req.body)
|
||||||
.then(() => cacheAndRespond(req, res))
|
.then(() => respond(req, res))
|
||||||
.catch(next)
|
.catch(next)
|
||||||
}
|
}
|
||||||
|
|
||||||
function verifyUser (req, res, next) {
|
function verifyUser (req, res, next) {
|
||||||
const pi = plugins(req.settings)
|
const pi = plugins(req.settings, req.deviceId)
|
||||||
pi.verifyUser(req.body)
|
pi.verifyUser(req.body)
|
||||||
.then(idResult => cacheAndRespond(req, res, idResult))
|
.then(idResult => respond(req, res, idResult))
|
||||||
.catch(next)
|
.catch(next)
|
||||||
}
|
}
|
||||||
|
|
||||||
function verifyTx (req, res, next) {
|
function verifyTx (req, res, next) {
|
||||||
const pi = plugins(req.settings)
|
const pi = plugins(req.settings, req.deviceId)
|
||||||
pi.verifyTransaction(req.body)
|
pi.verifyTransaction(req.body)
|
||||||
.then(idResult => cacheAndRespond(req, res, idResult))
|
.then(idResult => respond(req, res, idResult))
|
||||||
.catch(next)
|
.catch(next)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -177,11 +135,11 @@ function pair (req, res, next) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function phoneCode (req, res, next) {
|
function phoneCode (req, res, next) {
|
||||||
const pi = plugins(req.settings)
|
const pi = plugins(req.settings, req.deviceId)
|
||||||
const phone = req.body.phone
|
const phone = req.body.phone
|
||||||
|
|
||||||
return pi.getPhoneCode(phone)
|
return pi.getPhoneCode(phone)
|
||||||
.then(code => cacheAndRespond(req, res, {code}))
|
.then(code => respond(req, res, {code}))
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
if (err.name === 'BadNumberError') throw httpError('Bad number', 410)
|
if (err.name === 'BadNumberError') throw httpError('Bad number', 410)
|
||||||
throw err
|
throw err
|
||||||
|
|
@ -189,88 +147,6 @@ function phoneCode (req, res, next) {
|
||||||
.catch(next)
|
.catch(next)
|
||||||
}
|
}
|
||||||
|
|
||||||
function updatePhone (req, res, next) {
|
|
||||||
const notified = req.query.notified === 'true'
|
|
||||||
const tx = req.body
|
|
||||||
|
|
||||||
return dbm.updatePhone(tx, notified)
|
|
||||||
.then(r => cacheAndRespond(req, res, r))
|
|
||||||
.catch(next)
|
|
||||||
}
|
|
||||||
|
|
||||||
function fetchPhoneTx (req, res, next) {
|
|
||||||
return helpers.fetchPhoneTx(req.query.phone)
|
|
||||||
.then(r => res.json(r))
|
|
||||||
.catch(next)
|
|
||||||
}
|
|
||||||
|
|
||||||
function registerRedeem (req, res, next) {
|
|
||||||
const txId = req.params.txId
|
|
||||||
return dbm.registerRedeem(txId)
|
|
||||||
.then(() => cacheAndRespond(req, res))
|
|
||||||
.catch(next)
|
|
||||||
}
|
|
||||||
|
|
||||||
function waitForDispense (req, res, next) {
|
|
||||||
logger.debug('waitForDispense')
|
|
||||||
return dbm.fetchTx(req.params.txId)
|
|
||||||
.then(tx => {
|
|
||||||
logger.debug('tx fetched')
|
|
||||||
logger.debug(tx)
|
|
||||||
if (!tx) return res.sendStatus(404)
|
|
||||||
if (tx.status === req.query.status) return res.sendStatus(304)
|
|
||||||
res.json({tx})
|
|
||||||
})
|
|
||||||
.catch(next)
|
|
||||||
}
|
|
||||||
|
|
||||||
function dispense (req, res, next) {
|
|
||||||
const tx = req.body.tx
|
|
||||||
|
|
||||||
return dbm.addDispenseRequest(tx)
|
|
||||||
.then(dispenseRec => cacheAndRespond(req, res, dispenseRec))
|
|
||||||
.catch(next)
|
|
||||||
}
|
|
||||||
|
|
||||||
function isUniqueViolation (err) {
|
|
||||||
return err.code === '23505'
|
|
||||||
}
|
|
||||||
|
|
||||||
function cacheAction (req, res, next) {
|
|
||||||
const requestId = req.headers['request-id']
|
|
||||||
if (!requestId) return next()
|
|
||||||
|
|
||||||
const sql = `insert into idempotents (request_id, device_id, body, status, pending)
|
|
||||||
values ($1, $2, $3, $4, $5)`
|
|
||||||
|
|
||||||
const deviceId = req.deviceId
|
|
||||||
|
|
||||||
db.none(sql, [requestId, deviceId, {}, 204, true])
|
|
||||||
.then(() => next())
|
|
||||||
.catch(err => {
|
|
||||||
if (!isUniqueViolation(err)) throw err
|
|
||||||
|
|
||||||
const sql2 = 'select body, status, pending from idempotents where request_id=$1'
|
|
||||||
return db.one(sql2, [requestId])
|
|
||||||
.then(row => {
|
|
||||||
if (row.pending) return res.status(204).end()
|
|
||||||
return res.status(row.status).json(row.body)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateCachedAction (req, body, status) {
|
|
||||||
const requestId = req.headers['request-id']
|
|
||||||
if (!requestId) return Promise.resolve()
|
|
||||||
|
|
||||||
const sql = `update idempotents set body=$1, status=$2, pending=$3
|
|
||||||
where request_id=$4 and device_id=$5 and pending=$6`
|
|
||||||
|
|
||||||
const deviceId = req.deviceId
|
|
||||||
|
|
||||||
return db.none(sql, [body, status, false, requestId, deviceId, true])
|
|
||||||
}
|
|
||||||
|
|
||||||
function errorHandler (err, req, res, next) {
|
function errorHandler (err, req, res, next) {
|
||||||
const statusCode = err.name === 'HttpError'
|
const statusCode = err.name === 'HttpError'
|
||||||
? err.code || 500
|
? err.code || 500
|
||||||
|
|
@ -280,22 +156,14 @@ function errorHandler (err, req, res, next) {
|
||||||
|
|
||||||
logger.error(err)
|
logger.error(err)
|
||||||
|
|
||||||
return updateCachedAction(req, json, statusCode)
|
return res.status(statusCode).json(json)
|
||||||
.then(() => res.status(statusCode).json(json))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function cacheAndRespond (req, res, _body, _status) {
|
function respond (req, res, _body, _status) {
|
||||||
const status = _status || 200
|
const status = _status || 200
|
||||||
const body = _body || {}
|
const body = _body || {}
|
||||||
|
|
||||||
return updateCachedAction(req, body, status)
|
return res.status(status).json(body)
|
||||||
.then(() => res.status(status).json(body))
|
|
||||||
}
|
|
||||||
|
|
||||||
function pruneIdempotents () {
|
|
||||||
const sql = "delete from idempotents where created < now() - interval '24 hours'"
|
|
||||||
|
|
||||||
return db.none(sql)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function httpError (msg, code) {
|
function httpError (msg, code) {
|
||||||
|
|
@ -339,14 +207,11 @@ const skip = options.logLevel === 'debug'
|
||||||
|
|
||||||
const configRequiredRoutes = [
|
const configRequiredRoutes = [
|
||||||
'/poll',
|
'/poll',
|
||||||
'/trade',
|
|
||||||
'/send',
|
|
||||||
'/cash_out',
|
|
||||||
'/dispense_ack',
|
|
||||||
'/event',
|
'/event',
|
||||||
'/verify_user',
|
'/verify_user',
|
||||||
'/verify_transaction',
|
'/verify_transaction',
|
||||||
'/phone_code'
|
'/phone_code',
|
||||||
|
'/tx'
|
||||||
]
|
]
|
||||||
|
|
||||||
const app = express()
|
const app = express()
|
||||||
|
|
@ -364,7 +229,6 @@ app.use(populateDeviceId)
|
||||||
if (!devMode) app.use(authorize)
|
if (!devMode) app.use(authorize)
|
||||||
app.use(configRequiredRoutes, populateSettings)
|
app.use(configRequiredRoutes, populateSettings)
|
||||||
app.use(filterOldRequests)
|
app.use(filterOldRequests)
|
||||||
app.post('*', cacheAction)
|
|
||||||
|
|
||||||
app.get('/poll', poll)
|
app.get('/poll', poll)
|
||||||
app.post('/state', stateChange)
|
app.post('/state', stateChange)
|
||||||
|
|
@ -435,6 +299,4 @@ function populateSettings (req, res, next) {
|
||||||
.catch(next)
|
.catch(next)
|
||||||
}
|
}
|
||||||
|
|
||||||
setInterval(pruneIdempotents, 60000)
|
|
||||||
|
|
||||||
module.exports = {app, localApp}
|
module.exports = {app, localApp}
|
||||||
|
|
|
||||||
35
lib/tx.js
35
lib/tx.js
|
|
@ -1,35 +1,8 @@
|
||||||
const db = require('./db')
|
const CashInTx = require('./cash-in-tx')
|
||||||
const pgp = require('pg-promise')()
|
|
||||||
|
|
||||||
function postCashIn (tx) {
|
function post (tx, pi) {
|
||||||
const TransactionMode = pgp.txMode.TransactionMode
|
if (tx.direction === 'cashIn') return CashInTx.post(tx, pi)
|
||||||
const isolationLevel = pgp.txMode.isolationLevel
|
if (tx.direction === 'cashOut') throw new Error('not implemented')
|
||||||
const tmSRD = new TransactionMode({tiLevel: isolationLevel.serializable})
|
|
||||||
|
|
||||||
function transaction (t) {
|
|
||||||
const sql = 'select * from cash_in_txs where id=$1'
|
|
||||||
return t.oneOrNone(sql, [tx.id])
|
|
||||||
.then(row => {
|
|
||||||
const newTx = executeCashInTxChange(tx, row)
|
|
||||||
|
|
||||||
if (row) return updateCashInTx(newTx)
|
|
||||||
insertCashInTx(newTx)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
transaction.txMode = tmSRD
|
|
||||||
|
|
||||||
return db.tx(transaction)
|
|
||||||
// retry failed
|
|
||||||
}
|
|
||||||
|
|
||||||
function postCashOut (tx) {
|
|
||||||
throw new Error('not implemented')
|
|
||||||
}
|
|
||||||
|
|
||||||
function post (tx) {
|
|
||||||
if (tx.direction === 'cashIn') return postCashIn(tx)
|
|
||||||
if (tx.direction === 'cashOut') return postCashOut(tx)
|
|
||||||
|
|
||||||
return Promise.reject(new Error('No such tx direction: %s', tx.direction))
|
return Promise.reject(new Error('No such tx direction: %s', tx.direction))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,10 +6,21 @@ const FETCH_INTERVAL = 5000
|
||||||
function fetchWallet (settings, cryptoCode) {
|
function fetchWallet (settings, cryptoCode) {
|
||||||
return Promise.resolve()
|
return Promise.resolve()
|
||||||
.then(() => {
|
.then(() => {
|
||||||
|
console.log('DEBUG44')
|
||||||
|
console.log('DEBUG44.0.0: %j', cryptoCode)
|
||||||
|
try {
|
||||||
|
console.log('DEBUG44.0: %j', configManager.cryptoScoped(cryptoCode, settings.config).wallet)
|
||||||
|
} catch (err) {
|
||||||
|
console.log('DEBUG44.0.e: %s', err.stack)
|
||||||
|
}
|
||||||
const plugin = configManager.cryptoScoped(cryptoCode, settings.config).wallet
|
const plugin = configManager.cryptoScoped(cryptoCode, settings.config).wallet
|
||||||
|
console.log('DEBUG44.1')
|
||||||
const account = settings.accounts[plugin]
|
const account = settings.accounts[plugin]
|
||||||
|
console.log('DEBUG44.2')
|
||||||
const wallet = require('lamassu-' + plugin)
|
const wallet = require('lamassu-' + plugin)
|
||||||
|
|
||||||
|
console.log('DEBUG45: %j', {wallet, account})
|
||||||
|
|
||||||
return {wallet, account}
|
return {wallet, account}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -21,11 +32,15 @@ function balance (settings, cryptoCode) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function sendCoins (settings, toAddress, cryptoAtoms, cryptoCode) {
|
function sendCoins (settings, toAddress, cryptoAtoms, cryptoCode) {
|
||||||
|
console.log('DEBUG40')
|
||||||
return fetchWallet(settings, cryptoCode)
|
return fetchWallet(settings, cryptoCode)
|
||||||
.then(r => {
|
.then(r => {
|
||||||
|
console.log('DEBUG41')
|
||||||
return r.wallet.sendCoins(r.account, toAddress, cryptoAtoms, cryptoCode)
|
return r.wallet.sendCoins(r.account, toAddress, cryptoAtoms, cryptoCode)
|
||||||
.then(res => {
|
.then(res => {
|
||||||
|
console.log('DEBUG42')
|
||||||
mem.clear(module.exports.balance)
|
mem.clear(module.exports.balance)
|
||||||
|
console.log('DEBUG43: %j', res)
|
||||||
return res
|
return res
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,8 @@ var db = require('./db')
|
||||||
|
|
||||||
exports.up = function (next) {
|
exports.up = function (next) {
|
||||||
var sql = [
|
var sql = [
|
||||||
'alter table cash_in_txs add column sent boolean not null default false'
|
'alter table cash_in_txs add column send boolean not null default false',
|
||||||
|
'alter table cash_in_txs rename currency_code to fiat_code'
|
||||||
]
|
]
|
||||||
db.multi(sql, next)
|
db.multi(sql, next)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue