From 38e562bdd6f96b57d7a1130d4a041659190ea547 Mon Sep 17 00:00:00 2001 From: Josh Harvey Date: Tue, 31 May 2016 19:13:35 +0300 Subject: [PATCH] improve incoming tx status updating --- lib/plugins.js | 7 +---- lib/postgresql_interface.js | 54 ++++++++++++++++++++++++++++++------- lib/routes.js | 5 +++- 3 files changed, 49 insertions(+), 17 deletions(-) diff --git a/lib/plugins.js b/lib/plugins.js index 01eb2439..7aa31741 100644 --- a/lib/plugins.js +++ b/lib/plugins.js @@ -406,12 +406,7 @@ function processTxStatus (tx) { return resolve() // Resolve on error because we ignore errors } - var status = res.status - if (tx.status === status) return resolve() - - const confirm = (status === 'instant' && tx.status !== 'confirmed') || - (status === 'confirmed' && tx.status !== 'instant') - db.updateTxStatus(tx, status, confirm).then(resolve).catch(reject) + db.updateTxStatus(tx, res.status).then(resolve).catch(reject) }) }) } diff --git a/lib/postgresql_interface.js b/lib/postgresql_interface.js index d03ff676..bd2137a1 100644 --- a/lib/postgresql_interface.js +++ b/lib/postgresql_interface.js @@ -1,3 +1,4 @@ +// @flow weak 'use strict' const BigNumber = require('bignumber.js') @@ -302,18 +303,51 @@ exports.fetchUnnotifiedTxs = function fetchUnnotifiedTxs (age, waitPeriod) { .then(rows => normalizeTxs(rows)) } -exports.updateTxStatus = function updateTxStatus (tx, status, confirm) { - const sql = confirm - ? 'UPDATE cash_out_txs SET status=$1, confirmation_time=now() WHERE session_id=$2' - : 'UPDATE cash_out_txs SET status=$1 WHERE session_id=$2' +function ratchetStatus (oldStatus, newStatus) { + const statusOrder = ['notSeen', 'published', 'rejected', + 'authorized', 'instant', 'confirmed'] - const values = [status, tx.sessionId] + if (oldStatus === newStatus) return oldStatus + if (newStatus === 'insufficientFunds') return newStatus - return db.none(sql, values) - .then(() => { - const sql2 = 'insert into cash_out_actions (session_id, action) values ($1, $2)' - return db.none(sql2, [tx.sessionId, status]) - }) + const idx = Math.max(statusOrder.indexOf(oldStatus), statusOrder.indexOf(newStatus)) + return statusOrder[idx] +} + +exports.updateTxStatus = function updateTxStatus (tx, status) { + const TransactionMode = pgp.txMode.TransactionMode + const isolationLevel = pgp.txMode.isolationLevel + const tmSRD = new TransactionMode({tiLevel: isolationLevel.serializable}) + + function transaction (t) { + const sql = 'select status, confirmation_time from cash_out_txs where session_id=$1' + return t.one(sql, [tx.sessionId]) + .then(row => { + const newStatus = ratchetStatus(row.status, status) + if (row.status === newStatus) return + + const setConfirmationTime = !row.confirmation_time && + (newStatus === 'instant' || newStatus === 'confirmed') + + const sql2 = setConfirmationTime + ? 'UPDATE cash_out_txs SET status=$1, confirmation_time=now() WHERE session_id=$2' + : 'UPDATE cash_out_txs SET status=$1 WHERE session_id=$2' + + const values2 = [newStatus, tx.sessionId] + + return t.none(sql2, values2) + .then(() => { + const sql3 = 'insert into cash_out_actions (session_id, action) values ($1, $2)' + return t.none(sql3, [tx.sessionId, newStatus]) + }) + }) + } + + transaction.txMode = tmSRD + + // Note: don't worry about retrying failed transaction here + // It will be tried again on the next status check + return db.tx(transaction) } exports.updateRedeem = function updateRedeem (session) { diff --git a/lib/routes.js b/lib/routes.js index 6e4f9d0b..14714570 100644 --- a/lib/routes.js +++ b/lib/routes.js @@ -210,7 +210,10 @@ function pair (req, res) { getFingerprint(req), name, function (err) { - if (err) return res.send(500).json({ err: err.message }) + if (err) { + logger.error(err) + return res.json({err: err.message}) + } res.json({success: true}) }