This commit is contained in:
Josh Harvey 2016-05-27 01:19:29 +03:00
parent eabf32daf5
commit 7055104218
4 changed files with 69 additions and 153 deletions

View file

@ -276,7 +276,7 @@ function _sendCoins (toAddress, cryptoAtoms, cryptoCode, cb) {
} }
} }
function executeTx (session, tx, authority, cb) { function executeTx (session, tx, cb) {
db.addOutgoingTx(session, tx, function (err) { db.addOutgoingTx(session, tx, function (err) {
if (err) { if (err) {
logger.error(err) logger.error(err)
@ -292,7 +292,7 @@ function executeTx (session, tx, authority, cb) {
logger.error(_err) logger.error(_err)
toSend = {cryptoAtoms: new BigNumber(0), fiat: 0} toSend = {cryptoAtoms: new BigNumber(0), fiat: 0}
} }
db.sentCoins(session, tx, authority, toSend, fee, _err, txHash) db.sentCoins(session, tx, toSend, fee, _err, txHash)
if (_err) return cb(_err) if (_err) return cb(_err)
@ -350,7 +350,7 @@ exports.recordPing = function recordPing (session, rec, cb) {
exports.sendCoins = function sendCoins (session, rawTx, cb) { exports.sendCoins = function sendCoins (session, rawTx, cb) {
var _session = {id: rawTx.sessionId || session.id, fingerprint: session.fingerprint} var _session = {id: rawTx.sessionId || session.id, fingerprint: session.fingerprint}
executeTx(_session, rawTx, 'machine', cb) executeTx(_session, rawTx, cb)
} }
exports.cashOut = function cashOut (session, tx, cb) { exports.cashOut = function cashOut (session, tx, cb) {

View file

@ -5,7 +5,6 @@
var BigNumber = require('bignumber.js') var BigNumber = require('bignumber.js')
var pg = require('pg') var pg = require('pg')
var async = require('async') var async = require('async')
var R = require('ramda')
var logger = require('./logger') var logger = require('./logger')
@ -27,12 +26,6 @@ function isLowSeverity (err) {
var conString = null var conString = null
function rollback (client, done) {
client.query('ROLLBACK', function (err) {
return done(err)
})
}
function getInsertQuery (tableName, fields, hasId) { function getInsertQuery (tableName, fields, hasId) {
// outputs string like: '$1, $2, $3...' with proper No of items // outputs string like: '$1, $2, $3...' with proper No of items
var placeholders = fields.map(function (_, i) { var placeholders = fields.map(function (_, i) {
@ -134,69 +127,14 @@ function query (client, queryStr, values, cb) {
}) })
} }
function silentQuery (client, queryStr, values, cb) { function insertIncoming (client, session, tx, cryptoAtoms, fiat, cb) {
if (!cb) { var fields = ['session_id', 'device_fingerprint', 'to_address',
cb = values 'crypto_atoms', 'crypto_code', 'currency_code', 'fiat', 'tx_hash',
values = [] 'phone', 'error'
}
client.query(queryStr, values, function (err) {
if (err) {
if (!isLowSeverity(err)) {
console.error(err)
console.log(queryStr)
console.log(values)
}
cb(err)
}
cb()
})
}
function insertOutgoingCompleteTx (client, session, tx, cb) {
// Only relevant for machine source transactions, not timeouts
if (!tx.fiat) return cb()
var stage = 'final_request'
var authority = 'machine'
var cryptoAtoms = tx.cryptoAtoms
var fiat = tx.fiat
insertOutgoing(client, session, tx, cryptoAtoms, fiat, stage, authority, cb)
}
function insertIncoming (client, session, tx, cryptoAtoms, fiat, stage, authority, cb) {
var realCryptoAtoms = cryptoAtoms || new BigNumber(0)
insertTx(client, session, true, tx, realCryptoAtoms, fiat, stage, authority, cb)
}
function insertOutgoing (client, session, tx, cryptoAtoms, fiat, stage, authority,
cb) {
insertTx(client, session, false, tx, cryptoAtoms, fiat, stage, authority, cb)
}
function insertTx (client, session, incoming, tx, cryptoAtoms, fiat, stage,
authority, cb) {
var fields = [
'session_id',
'stage',
'authority',
'incoming',
'device_fingerprint',
'to_address',
'satoshis',
'currency_code',
'crypto_code',
'fiat',
'tx_hash',
'phone',
'error'
] ]
var values = [ var values = [
session.id, session.id,
stage,
authority,
incoming,
session.fingerprint, session.fingerprint,
tx.toAddress, tx.toAddress,
cryptoAtoms.toString(), cryptoAtoms.toString(),
@ -208,80 +146,64 @@ function insertTx (client, session, incoming, tx, cryptoAtoms, fiat, stage,
tx.error tx.error
] ]
query(client, getInsertQuery('transactions', fields, true), values, query(client, getInsertQuery('cash_out_txs', fields), values, cb)
function (err, results) {
if (err) return cb(err)
cb(null, results.rows[0].id)
})
} }
function addPendingTx (client, session, incoming, currencyCode, cryptoCode, toAddress, function insertOutgoing (client, session, tx, cryptoAtoms, fiat, cb) {
cryptoAtoms, cb) { var fields = ['session_id', 'device_fingerprint', 'to_address',
var fields = ['device_fingerprint', 'session_id', 'incoming', 'crypto_atoms', 'crypto_code', 'currency_code', 'fiat', 'tx_hash',
'currency_code', 'crypto_code', 'to_address', 'satoshis'] 'fee', 'phone', 'error'
var sql = getInsertQuery('pending_transactions', fields, false) ]
var values = [session.fingerprint, session.id, incoming, currencyCode,
cryptoCode, toAddress, cryptoAtoms.toString()] var values = [
query(client, sql, values, function (err) { session.id,
cb(err) session.fingerprint,
}) tx.toAddress,
cryptoAtoms.toString(),
tx.cryptoCode,
tx.currencyCode,
fiat,
tx.txHash,
null,
tx.phone,
tx.error
]
query(client, getInsertQuery('cash_in_txs', fields), values, cb)
} }
// Calling function should only send bitcoins if result.cryptoAtomsToSend > 0 // Calling function should only send bitcoins if result.cryptoAtomsToSend > 0
exports.addOutgoingTx = function addOutgoingTx (session, tx, cb) { exports.addOutgoingTx = function addOutgoingTx (session, tx, cb) {
connect(function (cerr, client, done) { connect(function (cerr, client, done) {
if (cerr) return cb(cerr) if (cerr) return cb(cerr)
insertOutgoingCompleteTx(client, session, tx, cb)
var cryptoAtoms = tx.cryptoAtoms
var fiat = tx.fiat
insertOutgoing(client, session, tx, cryptoAtoms, fiat, cb)
}) })
} }
exports.sentCoins = function sentCoins (session, tx, authority, toSend, fee, exports.sentCoins = function sentCoins (session, tx, toSend, fee, error, txHash) {
error, txHash) { var sql = `update cash_in_txs set tx_hash=$1, error=$2 where session_id=$3`
connect(function (cerr, client, done) { return pquery(sql, [txHash, error, session.id])
if (cerr) return logger.error(cerr)
var newTx = R.clone(tx)
newTx.txHash = txHash
newTx.error = error
insertOutgoing(client, session, newTx, toSend.cryptoAtoms, toSend.fiat,
'partial_send', authority, function (err) {
done()
if (err) logger.error(err)
})
})
} }
exports.addInitialIncoming = function addInitialIncoming (session, tx, cb) { exports.addInitialIncoming = function addInitialIncoming (session, tx, cb) {
connect(function (cerr, client, done) { connect(function (cerr, client, done) {
if (cerr) return cb(cerr) if (cerr) return cb(cerr)
async.series([ insertIncoming(client, session, tx, tx.cryptoAtoms, tx.fiat, cb)
async.apply(silentQuery, client, 'BEGIN', null),
async.apply(addPendingTx, client, session, true, tx.currencyCode,
tx.cryptoCode, tx.toAddress, tx.cryptoAtoms),
async.apply(insertIncoming, client, session, tx, tx.cryptoAtoms, tx.fiat,
'initial_request', 'pending')
], function (err) {
if (err) {
rollback(client, done)
return cb(err)
}
silentQuery(client, 'COMMIT', null, function () {
done()
cb()
})
})
}) })
} }
function insertDispense (client, session, tx, cartridges, transactionId, cb) { function insertDispense (client, session, tx, cartridges, cb) {
var fields = [ var fields = [
'device_fingerprint', 'transaction_id', 'device_fingerprint', 'session_id',
'dispense1', 'reject1', 'count1', 'dispense1', 'reject1', 'count1',
'dispense2', 'reject2', 'count2', 'dispense2', 'reject2', 'count2',
'refill', 'error' 'refill', 'error'
] ]
var sql = getInsertQuery('dispenses', fields, true) var sql = getInsertQuery('dispenses', fields)
var dispense1 = tx.bills[0].actualDispense var dispense1 = tx.bills[0].actualDispense
var dispense2 = tx.bills[1].actualDispense var dispense2 = tx.bills[1].actualDispense
@ -290,7 +212,7 @@ function insertDispense (client, session, tx, cartridges, transactionId, cb) {
var count1 = cartridges[0].count var count1 = cartridges[0].count
var count2 = cartridges[1].count var count2 = cartridges[1].count
var values = [ var values = [
session.fingerprint, transactionId, session.fingerprint, session.id,
dispense1, reject1, count1, dispense2, reject2, count2, dispense1, reject1, count1, dispense2, reject2, count2,
false, tx.error false, tx.error
] ]
@ -299,14 +221,13 @@ function insertDispense (client, session, tx, cartridges, transactionId, cb) {
exports.addIncomingPhone = function addIncomingPhone (session, tx, notified, cb) { exports.addIncomingPhone = function addIncomingPhone (session, tx, notified, cb) {
var sql = 'UPDATE transactions SET phone=$1, notified=$2 ' + var sql = 'UPDATE transactions SET phone=$1, notified=$2 ' +
'WHERE incoming=$3 AND device_fingerprint=$4 AND session_id=$5 ' + 'WHERE device_fingerprint=$3 AND session_id=$4 ' +
'AND stage=$6 AND authority=$7 AND phone IS NULL' 'AND phone IS NULL'
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
connect(function (cerr, client, done) { connect(function (cerr, client, done) {
if (cerr) return reject(cerr) if (cerr) return reject(cerr)
var values = [tx.phone, notified, true, session.fingerprint, var values = [tx.phone, notified, session.fingerprint, tx.sessionId]
tx.sessionId, 'initial_request', 'pending']
query(client, sql, values, function (err, results) { query(client, sql, values, function (err, results) {
done(err) done(err)
if (err) return reject(err) if (err) return reject(err)
@ -322,7 +243,7 @@ function normalizeTxs (txs) {
tx.currencyCode = tx.currency_code tx.currencyCode = tx.currency_code
tx.txHash = tx.tx_hash tx.txHash = tx.tx_hash
tx.cryptoCode = tx.crypto_code tx.cryptoCode = tx.crypto_code
tx.cryptoAtoms = new BigNumber(tx.satoshis) tx.cryptoAtoms = new BigNumber(tx.crypto_atoms)
tx.sessionId = tx.session_id tx.sessionId = tx.session_id
tx.to_address = undefined tx.to_address = undefined
@ -337,26 +258,24 @@ function normalizeTxs (txs) {
} }
exports.fetchPhoneTxs = function fetchPhoneTxs (phone, dispenseTimeout) { exports.fetchPhoneTxs = function fetchPhoneTxs (phone, dispenseTimeout) {
var sql = 'SELECT * FROM transactions ' + var sql = 'SELECT * FROM cash_out_txs ' +
'WHERE phone=$1 AND dispensed=$2 ' + 'WHERE phone=$1 AND dispensed=$2 ' +
'AND (EXTRACT(EPOCH FROM (COALESCE(confirmation_time, now()) - created))) * 1000 < $3 ' + 'AND (EXTRACT(EPOCH FROM (COALESCE(confirmation_time, now()) - created))) * 1000 < $3'
'AND stage=$4 AND authority=$5 AND incoming=$6'
var values = [phone, false, dispenseTimeout, 'initial_request', 'pending', true] var values = [phone, false, dispenseTimeout]
return pquery(sql, values) return pquery(sql, values)
.then(r => normalizeTxs(r.rows)) .then(r => normalizeTxs(r.rows))
} }
exports.fetchTx = function fetchTx (session) { exports.fetchTx = function fetchTx (session) {
var sql = 'SELECT * FROM transactions ' + var sql = 'SELECT * FROM cash_out_txs ' +
'WHERE device_fingerprint=$1 AND session_id=$2 ' + 'WHERE device_fingerprint=$1 AND session_id=$2'
'AND stage=$3 AND authority=$4 and incoming=$5'
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
connect(function (cerr, client, done) { connect(function (cerr, client, done) {
if (cerr) return reject(cerr) if (cerr) return reject(cerr)
var values = [session.fingerprint, session.id, 'initial_request', 'pending', true] var values = [session.fingerprint, session.id]
query(client, sql, values, function (err, results) { query(client, sql, values, function (err, results) {
done() done()
if (err) return reject(err) if (err) return reject(err)
@ -374,8 +293,7 @@ exports.addDispenseRequest = function addDispenseRequest (session, tx) {
const originalSession = {id: tx.sessionId, fingerprint: session.fingerprint} const originalSession = {id: tx.sessionId, fingerprint: session.fingerprint}
async.waterfall([ async.waterfall([
async.apply(updateDispense, client, originalSession, true), async.apply(updateDispense, client, originalSession, true),
async.apply(insertIncoming, client, originalSession, tx, 0, tx.fiat, async.apply(insertIncoming, client, originalSession, tx, 0, tx.fiat)
'dispense', 'pending')
], function (err) { ], function (err) {
done() done()
@ -388,9 +306,8 @@ exports.addDispenseRequest = function addDispenseRequest (session, tx) {
function updateDispense (client, session, dispensed, cb) { function updateDispense (client, session, dispensed, cb) {
var sql = 'UPDATE transactions SET dispensed=$1 ' + var sql = 'UPDATE transactions SET dispensed=$1 ' +
'WHERE stage=$2 AND authority=$3 AND device_fingerprint=$4 AND ' + 'WHERE device_fingerprint=$2 AND session_id=$3'
'session_id=$5 AND incoming=$6' var values = [dispensed, session.fingerprint, session.id]
var values = [dispensed, 'initial_request', 'pending', session.fingerprint, session.id, true]
query(client, sql, values, function (err, results) { query(client, sql, values, function (err, results) {
if (err) return cb(err) if (err) return cb(err)
if (results.rowCount === 0) return cb(new Error('No pending tx')) if (results.rowCount === 0) return cb(new Error('No pending tx'))
@ -403,8 +320,7 @@ exports.addDispense = function addDispense (session, tx, cartridges) {
if (cerr) return if (cerr) return
async.waterfall([ async.waterfall([
async.apply(insertIncoming, client, session, tx, 0, tx.fiat, async.apply(insertIncoming, client, session, tx, 0, tx.fiat),
'dispense', 'authorized'),
async.apply(insertDispense, client, session, tx, cartridges) async.apply(insertDispense, client, session, tx, cartridges)
], function (err) { ], function (err) {
done() done()
@ -483,15 +399,14 @@ exports.fetchOpenTxs = function fetchOpenTxs (statuses, age, cb) {
var _statuses = '(' + statuses.map(singleQuotify).join(',') + ')' var _statuses = '(' + statuses.map(singleQuotify).join(',') + ')'
var sql = 'SELECT * ' + var sql = 'SELECT * ' +
'FROM transactions ' + 'FROM cash_out_txs ' +
'WHERE incoming=$1 AND ((EXTRACT(EPOCH FROM (now() - created))) * 1000)<$2 ' + 'WHERE ((EXTRACT(EPOCH FROM (now() - created))) * 1000)<$1 ' +
'AND stage=$3 AND authority=$4 ' +
'AND status IN ' + _statuses 'AND status IN ' + _statuses
connect(function (cerr, client, done) { connect(function (cerr, client, done) {
if (cerr) return cb(cerr) if (cerr) return cb(cerr)
query(client, sql, [true, age, 'initial_request', 'pending'], function (err, results) { query(client, sql, [age], function (err, results) {
done() done()
if (err) return cb(err) if (err) return cb(err)
cb(null, normalizeTxs(results.rows)) cb(null, normalizeTxs(results.rows))
@ -502,17 +417,16 @@ exports.fetchOpenTxs = function fetchOpenTxs (statuses, age, cb) {
exports.fetchUnnotifiedTxs = function fetchUnnotifiedTxs (age, waitPeriod, cb) { exports.fetchUnnotifiedTxs = function fetchUnnotifiedTxs (age, waitPeriod, cb) {
var sql = 'SELECT * ' + var sql = 'SELECT * ' +
'FROM transactions ' + 'FROM transactions ' +
'WHERE incoming=$1 AND ' + 'WHERE ((EXTRACT(EPOCH FROM (now() - created))) * 1000)<$1 ' +
'((EXTRACT(EPOCH FROM (now() - created))) * 1000)<$2 ' + 'AND notified=$2 AND dispensed=$3 ' +
'AND stage=$3 AND authority=$4 AND notified=$5 AND dispensed=$6 ' +
'AND phone IS NOT NULL ' + 'AND phone IS NOT NULL ' +
"AND status IN ('instant', 'confirmed') " + "AND status IN ('instant', 'confirmed') " +
'AND (redeem=$7 OR ((EXTRACT(EPOCH FROM (now() - created))) * 1000)>$8)' 'AND (redeem=$4 OR ((EXTRACT(EPOCH FROM (now() - created))) * 1000)>$5)'
connect(function (cerr, client, done) { connect(function (cerr, client, done) {
if (cerr) return cb(cerr) if (cerr) return cb(cerr)
var values = [true, age, 'initial_request', 'pending', false, false, true, waitPeriod] var values = [age, false, false, true, waitPeriod]
query(client, sql, values, function (err, results) { query(client, sql, values, function (err, results) {
done() done()
if (err) return cb(err) if (err) return cb(err)
@ -546,13 +460,12 @@ exports.updateTxStatus = function updateTxStatus (tx, status, confirm) {
exports.updateRedeem = function updateRedeem (session, cb) { exports.updateRedeem = function updateRedeem (session, cb) {
var sql = 'UPDATE transactions SET redeem=$1 ' + var sql = 'UPDATE transactions SET redeem=$1 ' +
'WHERE incoming=$2 AND device_fingerprint=$3 AND session_id=$4 ' + 'WHERE device_fingerprint=$2 AND session_id=$3'
'AND stage=$5 AND authority=$6'
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
connect(function (cerr, client, done) { connect(function (cerr, client, done) {
if (cerr) return reject(cerr) if (cerr) return reject(cerr)
var values = [true, true, session.fingerprint, session.id, 'initial_request', 'pending'] var values = [true, session.fingerprint, session.id]
query(client, sql, values, function (err) { query(client, sql, values, function (err) {
done(err) done(err)
if (err) return reject(err) if (err) return reject(err)

View file

@ -49,7 +49,9 @@ exports.up = function (next) {
session_id uuid REFERENCES cash_out_txs(session_id), session_id uuid REFERENCES cash_out_txs(session_id),
action cash_out_action_types NOT NULL, action cash_out_action_types NOT NULL,
created timestamptz NOT NULL default now() created timestamptz NOT NULL default now()
)` )`,
`alter table dispenses add session_id uuid`,
`alter table dispenses drop constraint dispenses_transaction_id_fkey`
] ]
db.multi(sql, next) db.multi(sql, next)
} }

View file

@ -1 +1,2 @@
- change satoshis to crypto_atoms in db (ask neal about this) - test cash out
- l-m shouldn't keep polling l-s when not on pending screen