This commit is contained in:
Josh Harvey 2016-07-27 17:18:42 +03:00
parent b62cd590ef
commit 5312086622
3 changed files with 127 additions and 131 deletions

View file

@ -243,9 +243,7 @@ exports.getConfig = function getConfig () {
return cachedConfig return cachedConfig
} }
exports.logEvent = function event (session, rawEvent) { exports.logEvent = db.recordDeviceEvent
return db.recordDeviceEvent(session, rawEvent)
}
function buildCartridges (cartridges, virtualCartridges, rec) { function buildCartridges (cartridges, virtualCartridges, rec) {
return { return {
@ -264,12 +262,12 @@ function buildCartridges (cartridges, virtualCartridges, rec) {
} }
} }
exports.pollQueries = function pollQueries (session) { exports.pollQueries = function pollQueries (deviceId) {
const cartridges = cachedConfig.exchanges.settings.cartridges const cartridges = cachedConfig.exchanges.settings.cartridges
if (!cartridges) return Promise.resolve({}) if (!cartridges) return Promise.resolve({})
const virtualCartridges = cachedConfig.exchanges.settings.virtualCartridges const virtualCartridges = cachedConfig.exchanges.settings.virtualCartridges
return db.cartridgeCounts(session) return db.cartridgeCounts(deviceId)
.then(result => ({ .then(result => ({
cartridges: buildCartridges(cartridges, virtualCartridges, result) cartridges: buildCartridges(cartridges, virtualCartridges, result)
})) }))
@ -298,8 +296,8 @@ function _sendCoinsCb (toAddress, cryptoAtoms, cryptoCode, cb) {
// 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 db unique db record in the table already. // a db unique db record in the table already.
function executeTx (fingerprint, tx) { function executeTx (deviceId, tx) {
return db.addOutgoingTx(fingerprint, tx) return db.addOutgoingTx(deviceId, tx)
.then(() => _sendCoins(tx.toAddress, tx.cryptoAtoms, tx.cryptoCode)) .then(() => _sendCoins(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
@ -316,7 +314,7 @@ function executeTx (fingerprint, tx) {
} }
// TODO: Run these in parallel and return success // TODO: Run these in parallel and return success
exports.trade = function trade (session, rawTrade) { exports.trade = function trade (deviceId, rawTrade) {
// TODO: move this to DB, too // TODO: move this to DB, too
// add bill to trader queue (if trader is enabled) // add bill to trader queue (if trader is enabled)
const cryptoCode = rawTrade.cryptoCode || 'BTC' const cryptoCode = rawTrade.cryptoCode || 'BTC'
@ -332,41 +330,41 @@ exports.trade = function trade (session, rawTrade) {
}) })
} }
return db.recordBill(session, rawTrade) return db.recordBill(deviceId, rawTrade)
} }
exports.stateChange = function stateChange (session, rec, cb) { exports.stateChange = function stateChange (deviceId, deviceTime, rec, cb) {
const event = { const event = {
id: rec.uuid, id: rec.uuid,
fingerprint: session.fingerprint, fingerprint: deviceId,
eventType: 'stateChange', eventType: 'stateChange',
note: JSON.stringify({state: rec.state, isIdle: rec.isIdle, sessionId: session.id}), note: JSON.stringify({state: rec.state, isIdle: rec.isIdle, txId: rec.txId}),
deviceTime: session.deviceTime deviceTime: deviceTime
} }
return db.machineEvent(event) return db.machineEvent(event)
} }
exports.recordPing = function recordPing (session, rec, cb) { exports.recordPing = function recordPing (deviceId, deviceTime, rec, cb) {
const event = { const event = {
id: uuid.v4(), id: uuid.v4(),
fingerprint: session.fingerprint, fingerprint: deviceId,
eventType: 'ping', eventType: 'ping',
note: JSON.stringify({state: rec.state, isIdle: rec.idle === 'true', sessionId: session.id}), note: JSON.stringify({state: rec.state, isIdle: rec.idle === 'true', txId: rec.txId}),
deviceTime: session.deviceTime deviceTime: deviceTime
} }
return db.machineEvent(event) return db.machineEvent(event)
} }
exports.sendCoins = function sendCoins (session, rawTx) { exports.sendCoins = function sendCoins (rawTx) {
return executeTx(session.fingerprint, rawTx) return executeTx(rawTx)
} }
exports.cashOut = function cashOut (session, tx) { exports.cashOut = function cashOut (tx) {
const cryptoCode = tx.cryptoCode || 'BTC' const cryptoCode = tx.cryptoCode || 'BTC'
const walletPlugin = walletPlugins[cryptoCode] const walletPlugin = walletPlugins[cryptoCode]
const serialPromise = walletPlugin.supportsHD const serialPromise = walletPlugin.supportsHD
? db.nextCashOutSerialHD(tx.sessionId, cryptoCode) ? db.nextCashOutSerialHD(tx.id, cryptoCode)
: Promise.resolve() : Promise.resolve()
return serialPromise return serialPromise
@ -381,15 +379,13 @@ exports.cashOut = function cashOut (session, tx) {
if (err) return reject(err) if (err) return reject(err)
const newTx = R.assoc('toAddress', address, tx) const newTx = R.assoc('toAddress', address, tx)
return db.addInitialIncoming(session, newTx, address) return db.addInitialIncoming(newTx, address)
.then(() => resolve(address)) .then(() => resolve(address))
}) })
})) }))
} }
exports.dispenseAck = function dispenseAck (session, rec) { exports.dispenseAck = db.addDispense
return db.addDispense(session, rec.tx, rec.cartridges)
}
exports.fiatBalance = function fiatBalance (cryptoCode) { exports.fiatBalance = function fiatBalance (cryptoCode) {
const deviceRate = exports.getDeviceRate(cryptoCode) const deviceRate = exports.getDeviceRate(cryptoCode)
@ -765,13 +761,8 @@ exports.getPhoneCode = function getPhoneCode (phone) {
.then(() => code) .then(() => code)
} }
exports.updatePhone = function updatePhone (session, tx, notified) { exports.updatePhone = db.addIncomingPhone
return db.addIncomingPhone(session, tx, notified) exports.registerRedeem = db.updateRedeem
}
exports.registerRedeem = function registerRedeem (session) {
return db.updateRedeem(session)
}
exports.fetchPhoneTx = function fetchPhoneTx (phone) { exports.fetchPhoneTx = function fetchPhoneTx (phone) {
return db.fetchPhoneTxs(phone, TRANSACTION_EXPIRATION) return db.fetchPhoneTxs(phone, TRANSACTION_EXPIRATION)
@ -798,9 +789,9 @@ exports.requestDispense = function requestDispense (tx) {
return db.addDispenseRequest(tx) return db.addDispenseRequest(tx)
} }
exports.cachedResponse = (session, path, method) => db.cachedResponse(session, path, method) exports.cachedResponse = db.cachedResponse
exports.cacheResponse = (session, path, method, body) => db.cacheResponse(session, path, method, body) exports.cacheResponse = db.cacheResponse
function sweepHD (row) { function sweepHD (row) {
const cryptoCode = row.crypto_code const cryptoCode = row.crypto_code

View file

@ -41,14 +41,14 @@ exports.init = function init (conString) {
} }
// logs inputted bill and overall tx status (if available) // logs inputted bill and overall tx status (if available)
exports.recordBill = function recordBill (session, rec) { exports.recordBill = function recordBill (deviceId, rec) {
const fields = [ const fields = [
'id', 'id',
'device_fingerprint', 'device_fingerprint',
'currency_code', 'currency_code',
'crypto_code', 'crypto_code',
'to_address', 'to_address',
'session_id', 'tx_id',
'device_time', 'device_time',
'satoshis', 'satoshis',
'denomination' 'denomination'
@ -56,11 +56,11 @@ exports.recordBill = function recordBill (session, rec) {
const values = [ const values = [
rec.uuid, rec.uuid,
session.fingerprint, deviceId,
rec.currency, rec.currency,
rec.cryptoCode, rec.cryptoCode,
rec.toAddress, rec.toAddress,
session.id, rec.txId,
rec.deviceTime, rec.deviceTime,
rec.cryptoAtoms.toString(), rec.cryptoAtoms.toString(),
rec.fiat rec.fiat
@ -73,10 +73,10 @@ exports.recordBill = function recordBill (session, rec) {
}) })
} }
exports.recordDeviceEvent = function recordDeviceEvent (session, event) { exports.recordDeviceEvent = function recordDeviceEvent (deviceId, event) {
const sql = 'INSERT INTO device_events (device_fingerprint, event_type, ' + const sql = 'INSERT INTO device_events (device_id, event_type, ' +
'note, device_time) VALUES ($1, $2, $3, $4)' 'note, device_time) VALUES ($1, $2, $3, $4)'
const values = [session.fingerprint, event.eventType, event.note, const values = [deviceId, event.eventType, event.note,
event.deviceTime] event.deviceTime]
return db.none(sql, values) return db.none(sql, values)
} }
@ -158,11 +158,11 @@ function insertDispense (session, tx, cartridges) {
return db.none(sql, values) return db.none(sql, values)
} }
exports.addIncomingPhone = function addIncomingPhone (session, tx, notified) { exports.addIncomingPhone = function addIncomingPhone (tx, notified) {
const sql = `UPDATE cash_out_txs SET phone=$1, notified=$2 const sql = `UPDATE cash_out_txs SET phone=$1, notified=$2
WHERE session_id=$3 WHERE tx_id=$3
AND phone IS NULL` AND phone IS NULL`
const values = [tx.phone, notified, tx.sessionId] const values = [tx.phone, notified, tx.id]
return db.result(sql, values) return db.result(sql, values)
.then(results => { .then(results => {
@ -171,7 +171,7 @@ exports.addIncomingPhone = function addIncomingPhone (session, tx, notified) {
if (noPhone) return {noPhone: noPhone} if (noPhone) return {noPhone: noPhone}
return db.none(sql2, [tx.sessionId, 'addedPhone']) return db.none(sql2, [tx.txId, 'addedPhone'])
.then(() => ({noPhone: noPhone})) .then(() => ({noPhone: noPhone}))
}) })
} }
@ -244,11 +244,11 @@ exports.addDispense = function addDispense (session, tx, cartridges) {
}) })
} }
exports.cartridgeCounts = function cartridgeCounts (session) { exports.cartridgeCounts = function cartridgeCounts (deviceId) {
const sql = 'SELECT id, count1, count2 FROM dispenses ' + const sql = 'SELECT id, count1, count2 FROM dispenses ' +
'WHERE device_fingerprint=$1 AND refill=$2 ' + 'WHERE device_fingerprint=$1 AND refill=$2 ' +
'ORDER BY id DESC LIMIT 1' 'ORDER BY id DESC LIMIT 1'
return db.oneOrNone(sql, [session.fingerprint, true]) return db.oneOrNone(sql, [deviceId, true])
.then(row => { .then(row => {
const counts = row ? [row.count1, row.count2] : [0, 0] const counts = row ? [row.count1, row.count2] : [0, 0]
return {id: row.id, counts: counts} return {id: row.id, counts: counts}
@ -360,14 +360,14 @@ exports.updateTxStatus = function updateTxStatus (tx, status) {
}) })
} }
exports.updateRedeem = function updateRedeem (session) { exports.updateRedeem = function updateRedeem (txId) {
const sql = 'UPDATE cash_out_txs SET redeem=$1 WHERE session_id=$2' const sql = 'UPDATE cash_out_txs SET redeem=$1 WHERE txId=$2'
const values = [true, session.id] const values = [true, txId]
return db.none(sql, values) return db.none(sql, values)
.then(() => { .then(() => {
const sql2 = 'insert into cash_out_actions (session_id, action) values ($1, $2)' const sql2 = 'insert into cash_out_actions (session_id, action) values ($1, $2)'
return db.none(sql2, [session.id, 'redeem']) return db.none(sql2, [txId, 'redeem'])
}) })
} }
@ -382,29 +382,29 @@ exports.updateNotify = function updateNotify (tx) {
}) })
} }
function insertCachedRequest (session, path, method, body) { function insertCachedRequest (deviceId, txId, path, method, body) {
const fields = [ const fields = [
'device_fingerprint', 'device_id',
'session_id', 'tx_id',
'path', 'path',
'method', 'method',
'body' 'body'
] ]
const sql = getInsertQuery('cached_responses', fields) const sql = getInsertQuery('cached_responses', fields)
return db.none(sql, [session.fingerprint, session.id, path, method, body]) return db.none(sql, [deviceId, txId, path, method, body])
} }
exports.cachedResponse = function (session, path, method) { exports.cachedResponse = function (deviceId, txId, path, method) {
const sql = `select body from cached_responses const sql = `select body from cached_responses
where device_fingerprint=$1 where device_id=$1
and session_id=$2 and tx_id=$2
and path=$3 and path=$3
and method=$4` and method=$4`
const values = [session.fingerprint, session.id, path, method] const values = [deviceId, txId, path, method]
return insertCachedRequest(session, path, method, {pendingRequest: true}) return insertCachedRequest(deviceId, txId, path, method, {pendingRequest: true})
.then(() => ({})) .then(() => ({}))
.catch(err => { .catch(err => {
if (!isUniqueViolation(err)) throw err if (!isUniqueViolation(err)) throw err
@ -422,15 +422,15 @@ function pruneCachedResponses () {
return db.none(sql, values) return db.none(sql, values)
} }
exports.cacheResponse = function (session, path, method, body) { exports.cacheResponse = function (deviceId, txId, path, method, body) {
const sql = `update cached_responses const sql = `update cached_responses
set body=$1 set body=$1
where device_fingerprint=$2 where device_id=$2
and session_id=$3 and tx_id=$3
and path=$4 and path=$4
and method=$5` and method=$5`
const values = [body, session.fingerprint, session.id, path, method] const values = [body, deviceId, txId, path, method]
return db.none(sql, values) return db.none(sql, values)
} }

View file

@ -10,7 +10,7 @@ let lamassuConfig
module.exports = { module.exports = {
init, init,
getFingerprint getDeviceId
} }
const STALE_TICKER = 3 * 60 * 1000 const STALE_TICKER = 3 * 60 * 1000
@ -57,12 +57,13 @@ function buildBalances () {
} }
function poll (req, res) { function poll (req, res) {
const fingerprint = getFingerprint(req) const deviceId = getDeviceId(req)
const deviceTime = getDeviceTime(req)
const pid = req.query.pid const pid = req.query.pid
pids[fingerprint] = {pid, ts: Date.now()} pids[deviceId] = {pid, ts: Date.now()}
logger.debug('poll request from: %s', fingerprint) logger.debug('poll request from: %s', deviceId)
let rates = {} let rates = {}
let balances = {} let balances = {}
@ -74,36 +75,36 @@ function poll (req, res) {
const settings = config.exchanges.settings const settings = config.exchanges.settings
const complianceSettings = settings.compliance const complianceSettings = settings.compliance
plugins.pollQueries(session(req)) plugins.pollQueries(deviceId)
.then(results => { .then(results => {
const cartridges = results.cartridges const cartridges = results.cartridges
const reboot = reboots[fingerprint] === pid const reboot = reboots[deviceId] === pid
const response = { const response = {
err: null, err: null,
locale: config.brain.locale, locale: config.brain.locale,
txLimit: parseInt(complianceSettings.maximum.limit, 10), txLimit: parseInt(complianceSettings.maximum.limit, 10),
idVerificationEnabled: complianceSettings.idVerificationEnabled, idVerificationEnabled: complianceSettings.idVerificationEnabled,
cartridges, cartridges,
twoWayMode: !!cartridges, twoWayMode: !!cartridges,
zeroConfLimit: settings.zeroConfLimit, zeroConfLimit: settings.zeroConfLimit,
fiatTxLimit: settings.fiatTxLimit, fiatTxLimit: settings.fiatTxLimit,
reboot, reboot,
rates, rates,
balances, balances,
coins: settings.coins coins: settings.coins
} }
if (response.idVerificationEnabled) { if (response.idVerificationEnabled) {
response.idVerificationLimit = complianceSettings.idVerificationLimit response.idVerificationLimit = complianceSettings.idVerificationLimit
} }
res.json(response) res.json(response)
}) })
.catch(logger.error) .catch(logger.error)
plugins.recordPing(session(req), req.query) plugins.recordPing(deviceId, deviceTime, req.query)
.catch(logger.error) .catch(logger.error)
} }
@ -111,7 +112,7 @@ function trade (req, res) {
const tx = req.body const tx = req.body
tx.cryptoAtoms = new BigNumber(tx.cryptoAtoms) tx.cryptoAtoms = new BigNumber(tx.cryptoAtoms)
plugins.trade(session(req), tx) plugins.trade(getDeviceId(req), tx)
.then(() => res.status(201).json({})) .then(() => res.status(201).json({}))
.catch(err => { .catch(err => {
logger.error(err) logger.error(err)
@ -120,7 +121,7 @@ function trade (req, res) {
} }
function stateChange (req, res) { function stateChange (req, res) {
plugins.stateChange(session(req), req.body) plugins.stateChange(getDeviceId(req), getDeviceTime(req), req.body)
.then(() => res.json({success: true})) .then(() => res.json({success: true}))
.catch(err => { .catch(err => {
console.error(err) console.error(err)
@ -135,7 +136,7 @@ function send (req, res) {
// TODO: use status.statusCode here after confirming machine compatibility // TODO: use status.statusCode here after confirming machine compatibility
// FIX: (joshm) set txHash to status.txId instead of previous status.txHash which wasn't being set // FIX: (joshm) set txHash to status.txId instead of previous status.txHash which wasn't being set
// Need to clean up txHash vs txId // Need to clean up txHash vs txId
return plugins.sendCoins(session(req), tx) return plugins.sendCoins(getDeviceId(req), tx)
.then(status => res.json({ .then(status => res.json({
txHash: status && status.txHash, txHash: status && status.txHash,
txId: status && status.txId txId: status && status.txId
@ -154,24 +155,24 @@ function cashOut (req, res) {
const tx = req.body const tx = req.body
tx.cryptoAtoms = new BigNumber(tx.cryptoAtoms) tx.cryptoAtoms = new BigNumber(tx.cryptoAtoms)
return plugins.cashOut(session(req), req.body) return plugins.cashOut(tx)
.then(cryptoAddress => res.json({bitcoinAddress: cryptoAddress})) .then(cryptoAddress => res.json({toAddress: cryptoAddress}))
.catch(err => { .catch(err => {
res.json({ res.json({
err: err.message, err: err.message,
errType: err.name errType: err.name
})
logger.error(err)
}) })
logger.error(err)
})
} }
function dispenseAck (req, res) { function dispenseAck (req, res) {
plugins.dispenseAck(session(req), req.body) plugins.dispenseAck(req.body)
res.json({success: true}) res.json({success: true})
} }
function deviceEvent (req, res) { function deviceEvent (req, res) {
plugins.logEvent(session(req), req.body) plugins.logEvent(getDeviceId(req), req.body)
res.json({err: null}) res.json({err: null})
} }
@ -207,7 +208,7 @@ function pair (req, res) {
lamassuConfig.pair( lamassuConfig.pair(
token, token,
getFingerprint(req), getDeviceId(req),
name, name,
err => { err => {
if (err) { if (err) {
@ -235,7 +236,9 @@ function phoneCode (req, res) {
function updatePhone (req, res) { function updatePhone (req, res) {
const notified = req.query.notified === 'true' const notified = req.query.notified === 'true'
return plugins.updatePhone(session(req), req.body, notified) const tx = req.body
return plugins.updatePhone(tx, notified)
.then(r => res.json(r)) .then(r => res.json(r))
.catch(err => { .catch(err => {
logger.error(err) logger.error(err)
@ -253,7 +256,8 @@ function fetchPhoneTx (req, res) {
} }
function registerRedeem (req, res) { function registerRedeem (req, res) {
return plugins.registerRedeem(session(req)) const txId = req.params.txId
return plugins.registerRedeem(txId)
.then(() => res.json({success: true})) .then(() => res.json({success: true}))
.catch(err => { .catch(err => {
logger.error(err) logger.error(err)
@ -263,7 +267,7 @@ function registerRedeem (req, res) {
function waitForDispense (req, res) { function waitForDispense (req, res) {
logger.debug('waitForDispense') logger.debug('waitForDispense')
return plugins.fetchTx(session(req)) return plugins.fetchTx(req.params.txId)
.then(tx => { .then(tx => {
logger.debug('tx fetched') logger.debug('tx fetched')
logger.debug(tx) logger.debug(tx)
@ -279,16 +283,25 @@ function waitForDispense (req, res) {
function dispense (req, res) { function dispense (req, res) {
const tx = req.body.tx const tx = req.body.tx
const deviceId = getDeviceId(req)
return plugins.requestDispense(tx) return cachedResponse(deviceId, tx.id, req)
.then(r => {
// Cache hit
if (r.body && r.body.pendingRequest) return res.sendStatus(409)
if (r.body) res.json(r.body)
// No cache hit
return plugins.requestDispense(tx)
.then(r => { .then(r => {
return cacheResponse(req, r) return cacheResponse(req, r)
.then(() => res.json(r)) .then(() => res.json(r))
}) })
.catch(err => { .catch(err => {
logger.error(err) logger.error(err)
res.sendStatus(500) res.sendStatus(500)
}) })
})
} }
function init (localConfig) { function init (localConfig) {
@ -316,9 +329,9 @@ function init (localConfig) {
app.post('/phone_code', authMiddleware, phoneCode) app.post('/phone_code', authMiddleware, phoneCode)
app.post('/update_phone', authMiddleware, updatePhone) app.post('/update_phone', authMiddleware, updatePhone)
app.get('/phone_tx', authMiddleware, fetchPhoneTx) app.get('/phone_tx', authMiddleware, fetchPhoneTx)
app.post('/register_redeem', authMiddleware, registerRedeem) app.post('/register_redeem/:txId', authMiddleware, registerRedeem)
app.get('/await_dispense', authMiddleware, waitForDispense) app.get('/await_dispense/:txId', authMiddleware, waitForDispense)
app.post('/dispense', authMiddleware, cachedResponse, dispense) app.post('/dispense', authMiddleware, dispense)
localApp.get('/pid', (req, res) => { localApp.get('/pid', (req, res) => {
const machineFingerprint = req.query.fingerprint const machineFingerprint = req.query.fingerprint
@ -342,28 +355,20 @@ function init (localConfig) {
return app return app
} }
function session (req) { function getDeviceTime (req) {
return { return Date.parse(req.get('date'))
fingerprint: getFingerprint(req),
id: req.get('session-id'),
deviceTime: Date.parse(req.get('date'))
}
} }
function getFingerprint (req) { function getDeviceId (req) {
return (typeof req.connection.getPeerCertificate === 'function' && return (typeof req.connection.getPeerCertificate === 'function' &&
req.connection.getPeerCertificate().fingerprint) || 'unknown' req.connection.getPeerCertificate().fingerprint) || 'unknown'
} }
function cachedResponse (req, res, next) { function cachedResponse (deviceId, txId, req) {
return plugins.cachedResponse(session(req), req.path, req.method) return plugins.cachedResponse(deviceId, txId, req.path, req.method)
.then(r => { .then(r => r.body)
if (!r.body) return next()
if (r.body.pendingRequest) return res.sendStatus(409)
res.json(r.body)
})
} }
function cacheResponse (req, body) { function cacheResponse (deviceId, txId, req, body) {
return plugins.cacheResponse(session(req), req.path, req.method, body) return plugins.cacheResponse(deviceId, txId, req.path, req.method, body)
} }