diff --git a/lib/plugins.js b/lib/plugins.js index 05a12c3b..e87dd007 100644 --- a/lib/plugins.js +++ b/lib/plugins.js @@ -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) { if (err) { logger.error(err) @@ -292,7 +292,7 @@ function executeTx (session, tx, authority, cb) { logger.error(_err) 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) @@ -350,7 +350,7 @@ exports.recordPing = function recordPing (session, rec, cb) { exports.sendCoins = function sendCoins (session, rawTx, cb) { 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) { diff --git a/lib/postgresql_interface.js b/lib/postgresql_interface.js index d115e4fe..5c4676fd 100644 --- a/lib/postgresql_interface.js +++ b/lib/postgresql_interface.js @@ -5,7 +5,6 @@ var BigNumber = require('bignumber.js') var pg = require('pg') var async = require('async') -var R = require('ramda') var logger = require('./logger') @@ -27,12 +26,6 @@ function isLowSeverity (err) { var conString = null -function rollback (client, done) { - client.query('ROLLBACK', function (err) { - return done(err) - }) -} - function getInsertQuery (tableName, fields, hasId) { // outputs string like: '$1, $2, $3...' with proper No of items var placeholders = fields.map(function (_, i) { @@ -134,69 +127,14 @@ function query (client, queryStr, values, cb) { }) } -function silentQuery (client, queryStr, values, cb) { - if (!cb) { - cb = values - values = [] - } - - 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' +function insertIncoming (client, session, tx, cryptoAtoms, fiat, cb) { + var fields = ['session_id', 'device_fingerprint', 'to_address', + 'crypto_atoms', 'crypto_code', 'currency_code', 'fiat', 'tx_hash', + 'phone', 'error' ] var values = [ session.id, - stage, - authority, - incoming, session.fingerprint, tx.toAddress, cryptoAtoms.toString(), @@ -208,80 +146,64 @@ function insertTx (client, session, incoming, tx, cryptoAtoms, fiat, stage, tx.error ] - query(client, getInsertQuery('transactions', fields, true), values, - function (err, results) { - if (err) return cb(err) - cb(null, results.rows[0].id) - }) + query(client, getInsertQuery('cash_out_txs', fields), values, cb) } -function addPendingTx (client, session, incoming, currencyCode, cryptoCode, toAddress, - cryptoAtoms, cb) { - var fields = ['device_fingerprint', 'session_id', 'incoming', - 'currency_code', 'crypto_code', 'to_address', 'satoshis'] - var sql = getInsertQuery('pending_transactions', fields, false) - var values = [session.fingerprint, session.id, incoming, currencyCode, - cryptoCode, toAddress, cryptoAtoms.toString()] - query(client, sql, values, function (err) { - cb(err) - }) +function insertOutgoing (client, session, tx, cryptoAtoms, fiat, cb) { + var fields = ['session_id', 'device_fingerprint', 'to_address', + 'crypto_atoms', 'crypto_code', 'currency_code', 'fiat', 'tx_hash', + 'fee', 'phone', 'error' + ] + + var values = [ + session.id, + 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 exports.addOutgoingTx = function addOutgoingTx (session, tx, cb) { connect(function (cerr, client, done) { 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, - error, txHash) { - connect(function (cerr, client, done) { - 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.sentCoins = function sentCoins (session, tx, toSend, fee, error, txHash) { + var sql = `update cash_in_txs set tx_hash=$1, error=$2 where session_id=$3` + return pquery(sql, [txHash, error, session.id]) } exports.addInitialIncoming = function addInitialIncoming (session, tx, cb) { connect(function (cerr, client, done) { if (cerr) return cb(cerr) - async.series([ - 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() - }) - }) + insertIncoming(client, session, tx, tx.cryptoAtoms, tx.fiat, cb) }) } -function insertDispense (client, session, tx, cartridges, transactionId, cb) { +function insertDispense (client, session, tx, cartridges, cb) { var fields = [ - 'device_fingerprint', 'transaction_id', + 'device_fingerprint', 'session_id', 'dispense1', 'reject1', 'count1', 'dispense2', 'reject2', 'count2', 'refill', 'error' ] - var sql = getInsertQuery('dispenses', fields, true) + var sql = getInsertQuery('dispenses', fields) var dispense1 = tx.bills[0].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 count2 = cartridges[1].count var values = [ - session.fingerprint, transactionId, + session.fingerprint, session.id, dispense1, reject1, count1, dispense2, reject2, count2, false, tx.error ] @@ -299,14 +221,13 @@ function insertDispense (client, session, tx, cartridges, transactionId, cb) { exports.addIncomingPhone = function addIncomingPhone (session, tx, notified, cb) { var sql = 'UPDATE transactions SET phone=$1, notified=$2 ' + - 'WHERE incoming=$3 AND device_fingerprint=$4 AND session_id=$5 ' + - 'AND stage=$6 AND authority=$7 AND phone IS NULL' + 'WHERE device_fingerprint=$3 AND session_id=$4 ' + + 'AND phone IS NULL' return new Promise((resolve, reject) => { connect(function (cerr, client, done) { if (cerr) return reject(cerr) - var values = [tx.phone, notified, true, session.fingerprint, - tx.sessionId, 'initial_request', 'pending'] + var values = [tx.phone, notified, session.fingerprint, tx.sessionId] query(client, sql, values, function (err, results) { done(err) if (err) return reject(err) @@ -322,7 +243,7 @@ function normalizeTxs (txs) { tx.currencyCode = tx.currency_code tx.txHash = tx.tx_hash tx.cryptoCode = tx.crypto_code - tx.cryptoAtoms = new BigNumber(tx.satoshis) + tx.cryptoAtoms = new BigNumber(tx.crypto_atoms) tx.sessionId = tx.session_id tx.to_address = undefined @@ -337,26 +258,24 @@ function normalizeTxs (txs) { } exports.fetchPhoneTxs = function fetchPhoneTxs (phone, dispenseTimeout) { - var sql = 'SELECT * FROM transactions ' + + var sql = 'SELECT * FROM cash_out_txs ' + 'WHERE phone=$1 AND dispensed=$2 ' + - 'AND (EXTRACT(EPOCH FROM (COALESCE(confirmation_time, now()) - created))) * 1000 < $3 ' + - 'AND stage=$4 AND authority=$5 AND incoming=$6' + 'AND (EXTRACT(EPOCH FROM (COALESCE(confirmation_time, now()) - created))) * 1000 < $3' - var values = [phone, false, dispenseTimeout, 'initial_request', 'pending', true] + var values = [phone, false, dispenseTimeout] return pquery(sql, values) .then(r => normalizeTxs(r.rows)) } exports.fetchTx = function fetchTx (session) { - var sql = 'SELECT * FROM transactions ' + - 'WHERE device_fingerprint=$1 AND session_id=$2 ' + - 'AND stage=$3 AND authority=$4 and incoming=$5' + var sql = 'SELECT * FROM cash_out_txs ' + + 'WHERE device_fingerprint=$1 AND session_id=$2' return new Promise((resolve, reject) => { connect(function (cerr, client, done) { 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) { done() if (err) return reject(err) @@ -374,8 +293,7 @@ exports.addDispenseRequest = function addDispenseRequest (session, tx) { const originalSession = {id: tx.sessionId, fingerprint: session.fingerprint} async.waterfall([ async.apply(updateDispense, client, originalSession, true), - async.apply(insertIncoming, client, originalSession, tx, 0, tx.fiat, - 'dispense', 'pending') + async.apply(insertIncoming, client, originalSession, tx, 0, tx.fiat) ], function (err) { done() @@ -388,9 +306,8 @@ exports.addDispenseRequest = function addDispenseRequest (session, tx) { function updateDispense (client, session, dispensed, cb) { var sql = 'UPDATE transactions SET dispensed=$1 ' + - 'WHERE stage=$2 AND authority=$3 AND device_fingerprint=$4 AND ' + - 'session_id=$5 AND incoming=$6' - var values = [dispensed, 'initial_request', 'pending', session.fingerprint, session.id, true] + 'WHERE device_fingerprint=$2 AND session_id=$3' + var values = [dispensed, session.fingerprint, session.id] query(client, sql, values, function (err, results) { if (err) return cb(err) 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 async.waterfall([ - async.apply(insertIncoming, client, session, tx, 0, tx.fiat, - 'dispense', 'authorized'), + async.apply(insertIncoming, client, session, tx, 0, tx.fiat), async.apply(insertDispense, client, session, tx, cartridges) ], function (err) { done() @@ -483,15 +399,14 @@ exports.fetchOpenTxs = function fetchOpenTxs (statuses, age, cb) { var _statuses = '(' + statuses.map(singleQuotify).join(',') + ')' var sql = 'SELECT * ' + - 'FROM transactions ' + - 'WHERE incoming=$1 AND ((EXTRACT(EPOCH FROM (now() - created))) * 1000)<$2 ' + - 'AND stage=$3 AND authority=$4 ' + + 'FROM cash_out_txs ' + + 'WHERE ((EXTRACT(EPOCH FROM (now() - created))) * 1000)<$1 ' + 'AND status IN ' + _statuses connect(function (cerr, client, done) { if (cerr) return cb(cerr) - query(client, sql, [true, age, 'initial_request', 'pending'], function (err, results) { + query(client, sql, [age], function (err, results) { done() if (err) return cb(err) cb(null, normalizeTxs(results.rows)) @@ -502,17 +417,16 @@ exports.fetchOpenTxs = function fetchOpenTxs (statuses, age, cb) { exports.fetchUnnotifiedTxs = function fetchUnnotifiedTxs (age, waitPeriod, cb) { var sql = 'SELECT * ' + 'FROM transactions ' + - 'WHERE incoming=$1 AND ' + - '((EXTRACT(EPOCH FROM (now() - created))) * 1000)<$2 ' + - 'AND stage=$3 AND authority=$4 AND notified=$5 AND dispensed=$6 ' + + 'WHERE ((EXTRACT(EPOCH FROM (now() - created))) * 1000)<$1 ' + + 'AND notified=$2 AND dispensed=$3 ' + 'AND phone IS NOT NULL ' + "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) { 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) { done() if (err) return cb(err) @@ -546,13 +460,12 @@ exports.updateTxStatus = function updateTxStatus (tx, status, confirm) { exports.updateRedeem = function updateRedeem (session, cb) { var sql = 'UPDATE transactions SET redeem=$1 ' + - 'WHERE incoming=$2 AND device_fingerprint=$3 AND session_id=$4 ' + - 'AND stage=$5 AND authority=$6' + 'WHERE device_fingerprint=$2 AND session_id=$3' return new Promise((resolve, reject) => { connect(function (cerr, client, done) { 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) { done(err) if (err) return reject(err) diff --git a/migrations/011-transactions-reload-2.js b/migrations/011-transactions-reload-2.js index 8835583c..440ccf44 100644 --- a/migrations/011-transactions-reload-2.js +++ b/migrations/011-transactions-reload-2.js @@ -49,7 +49,9 @@ exports.up = function (next) { session_id uuid REFERENCES cash_out_txs(session_id), action cash_out_action_types NOT NULL, 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) } diff --git a/todo.txt b/todo.txt index a1965965..2844c1a6 100644 --- a/todo.txt +++ b/todo.txt @@ -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