Fix up various flow issues.

This commit is contained in:
Josh Harvey 2017-09-12 00:00:24 +03:00
parent 08bd7da33f
commit 0d8a8547f5
8 changed files with 60 additions and 40 deletions

View file

@ -88,7 +88,7 @@ function redeemableTxs (deviceId) {
and redeem=$2 and redeem=$2
and dispense=$3 and dispense=$3
and provisioned_1 is not null and provisioned_1 is not null
and (extract(epoch from (now() - greatest(created, confirmation_time))) * 1000) < $4` and (extract(epoch from (now() - greatest(created, confirmed_at))) * 1000) < $4`
return db.any(sql, [deviceId, true, false, REDEEMABLE_AGE]) return db.any(sql, [deviceId, true, false, REDEEMABLE_AGE])
.then(_.map(toObj)) .then(_.map(toObj))

View file

@ -8,6 +8,7 @@ const logger = require('./logger')
const plugins = require('./plugins') const plugins = require('./plugins')
const helper = require('./cash-out-helper') const helper = require('./cash-out-helper')
const socket = require('./socket-client') const socket = require('./socket-client')
const E = require('./error')
module.exports = { module.exports = {
post, post,
@ -17,8 +18,8 @@ module.exports = {
cancel cancel
} }
const UPDATEABLE_FIELDS = ['txHash', 'status', 'dispense', 'dispenseConfirmed', const UPDATEABLE_FIELDS = ['txHash', 'txVersion', 'status', 'dispense', 'dispenseConfirmed',
'notified', 'redeem', 'phone', 'error', 'swept'] 'notified', 'redeem', 'phone', 'error', 'swept', 'publishedAt', 'confirmedAt']
const STALE_INCOMING_TX_AGE = T.week const STALE_INCOMING_TX_AGE = T.week
const STALE_LIVE_INCOMING_TX_AGE = 10 * T.minutes const STALE_LIVE_INCOMING_TX_AGE = 10 * T.minutes
@ -37,7 +38,11 @@ function httpError (msg, code) {
return err return err
} }
function post (tx, pi) { function selfPost (tx, pi) {
return post(tx, pi, false)
}
function post (tx, pi, fromClient = true) {
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})
@ -48,6 +53,9 @@ function post (tx, pi) {
return t.oneOrNone(sql, [tx.id]) return t.oneOrNone(sql, [tx.id])
.then(toObj) .then(toObj)
.then(oldTx => { .then(oldTx => {
const isStale = fromClient && oldTx && (oldTx.txVersion >= tx.txVersion)
if (isStale) throw new E.StaleTxError('Stale tx')
return preProcess(oldTx, tx, pi) return preProcess(oldTx, tx, pi)
.then(preProcessedTx => upsert(oldTx, preProcessedTx)) .then(preProcessedTx => upsert(oldTx, preProcessedTx))
}) })
@ -280,8 +288,33 @@ function postProcess (txVector, pi) {
return Promise.resolve({}) return Promise.resolve({})
} }
function isPublished (status) {
return _.includes(status, ['published', 'rejected', 'authorized', 'instant', 'confirmed'])
}
function isConfirmed (status) {
return status === 'confirmed'
}
function updateStatus (oldTx, newTx) { function updateStatus (oldTx, newTx) {
return _.set('status', ratchetStatus(oldTx.status, newTx.status), newTx) const oldStatus = oldTx.status
const newStatus = ratchetStatus(oldStatus, newTx.status)
const publishedAt = !oldTx.publishedAt && isPublished(newStatus)
? 'now()^'
: undefined
const confirmedAt = !oldTx.confirmedAt && isConfirmed(newStatus)
? 'now()^'
: undefined
const updateRec = {
publishedAt,
confirmedAt,
status: newStatus
}
return _.merge(newTx, updateRec)
} }
function ratchetStatus (oldStatus, newStatus) { function ratchetStatus (oldStatus, newStatus) {
@ -311,8 +344,8 @@ function processTxStatus (tx, settings) {
const pi = plugins(settings, tx.deviceId) const pi = plugins(settings, tx.deviceId)
return pi.getStatus(tx) return pi.getStatus(tx)
.then(res => _.set('status', res.status, tx)) .then(res => _.assign(tx, {status: res.status}))
.then(_tx => post(_tx, pi)) .then(_tx => selfPost(_tx, pi))
} }
function monitorLiveIncoming (settings) { function monitorLiveIncoming (settings) {

View file

@ -7,7 +7,7 @@ const NAME = 'FakeWallet'
const SECONDS = 1000 const SECONDS = 1000
const PUBLISH_TIME = 2 * SECONDS const PUBLISH_TIME = 2 * SECONDS
const AUTHORIZE_TIME = PUBLISH_TIME + 6 * SECONDS const AUTHORIZE_TIME = PUBLISH_TIME + 6 * SECONDS
const CONFIRM_TIME = AUTHORIZE_TIME + 180 * SECONDS const CONFIRM_TIME = AUTHORIZE_TIME + 120 * SECONDS
let t0 let t0

View file

@ -5,7 +5,7 @@ function authorize (account, toAddress, cryptoAtoms, cryptoCode) {
.then(() => { .then(() => {
if (cryptoCode !== 'BTC') throw new Error('Unsupported crypto: ' + cryptoCode) if (cryptoCode !== 'BTC') throw new Error('Unsupported crypto: ' + cryptoCode)
const isAuthorized = true const isAuthorized = false
return isAuthorized return isAuthorized
}) })
} }

View file

@ -50,7 +50,7 @@ function toCashOutTx (row) {
function fetchPhoneTx (phone) { function fetchPhoneTx (phone) {
const sql = `select * from cash_out_txs const sql = `select * from cash_out_txs
where phone=$1 and dispense=$2 where phone=$1 and dispense=$2
and (extract(epoch from (coalesce(confirmation_time, now()) - created))) * 1000 < $3` and (extract(epoch from (coalesce(confirmed_at, now()) - created))) * 1000 < $3`
const values = [phone, false, TRANSACTION_EXPIRATION] const values = [phone, false, TRANSACTION_EXPIRATION]

View file

@ -188,19 +188,6 @@ function pair (req, res, next) {
.catch(next) .catch(next)
} }
function phoneCode (req, res, next) {
const pi = plugins(req.settings, req.deviceId)
const phone = req.body.phone
return pi.getPhoneCode(phone)
.then(code => respond(req, res, {code}))
.catch(err => {
if (err.name === 'BadNumberError') throw httpError('Bad number', 410)
throw err
})
.catch(next)
}
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

View file

@ -7,7 +7,6 @@ const pify = require('pify')
const fs = pify(require('fs')) const fs = pify(require('fs'))
const options = require('./options') const options = require('./options')
const ph = require('./plugin-helper') const ph = require('./plugin-helper')
const db = require('./db')
const FETCH_INTERVAL = 5000 const FETCH_INTERVAL = 5000
const INSUFFICIENT_FUNDS_CODE = 570 const INSUFFICIENT_FUNDS_CODE = 570
@ -111,27 +110,14 @@ function authorizeZeroConf (settings, tx, machineId) {
return zeroConf.authorize(account, tx.toAddress, tx.cryptoAtoms, tx.cryptoCode) return zeroConf.authorize(account, tx.toAddress, tx.cryptoAtoms, tx.cryptoCode)
} }
function getPublishAge (txId) {
const sql = `select extract(epoch from (now() - created)) * 1000 as age
from cash_out_actions
where tx_id=$1
and action=$2`
return db.oneOrNone(sql, [txId, 'published'])
.then(row => row && row.age)
}
function getStatus (settings, tx, machineId) { function getStatus (settings, tx, machineId) {
return getWalletStatus(settings, tx) return getWalletStatus(settings, tx)
.then((statusRec) => { .then((statusRec) => {
if (statusRec.status === 'authorized') { if (statusRec.status === 'authorized') {
const promises = [ return authorizeZeroConf(settings, tx, machineId)
getPublishAge(tx.id), .then(isAuthorized => {
authorizeZeroConf(settings, tx, machineId) const publishAge = Date.now() - tx.publishedAt
]
return Promise.all(promises)
.then(([publishAge, isAuthorized]) => {
const unauthorizedStatus = publishAge < ZERO_CONF_EXPIRATION const unauthorizedStatus = publishAge < ZERO_CONF_EXPIRATION
? 'published' ? 'published'
: 'rejected' : 'rejected'

View file

@ -0,0 +1,14 @@
var db = require('./db')
exports.up = function (next) {
const sql = [
'alter table cash_out_txs add column published_at timestamptz',
'alter table cash_out_txs rename column confirmation_time to confirmed_at'
]
db.multi(sql, next)
}
exports.down = function (next) {
next()
}