diff --git a/lib/app.js b/lib/app.js index 4dda97d3..6d21ec5a 100644 --- a/lib/app.js +++ b/lib/app.js @@ -8,7 +8,6 @@ var routes = require('./routes'); var plugins = require('./plugins'); var db = require('./postgresql_interface'); var logger = require('./logger'); -require('longjohn'); module.exports = function(options) { var app = express(); diff --git a/lib/plugins.js b/lib/plugins.js index 7b8803ec..f51a2c6f 100644 --- a/lib/plugins.js +++ b/lib/plugins.js @@ -4,11 +4,15 @@ var _ = require('lodash'); var async = require('async'); var logger = require('./logger'); +var argv = require('minimist')(process.argv.slice(2)); + var SATOSHI_FACTOR = 1e8; var POLLING_RATE = 60 * 1000; // poll each minute var REAP_RATE = 2 * 1000; var PENDING_TIMEOUT = 70 * 1000; +if (argv.timeout) PENDING_TIMEOUT = argv.timeout / 1000; + // TODO: might have to update this if user is allowed to extend monitoring time var DEPOSIT_TIMEOUT = 130 * 1000; diff --git a/lib/postgresql_interface.js b/lib/postgresql_interface.js index b128d9b5..9853b4c1 100644 --- a/lib/postgresql_interface.js +++ b/lib/postgresql_interface.js @@ -182,6 +182,7 @@ function bitcoinFractionalDigits(amount) { } function truncateBitcoins(bitcoins) { + if (bitcoins < 0) logger.error('Negative amount in truncateBitcoins'); var decimalDigits = bitcoinFractionalDigits(bitcoins); var adjuster = Math.pow(10, decimalDigits); return (Math.round(bitcoins * adjuster) / adjuster); @@ -339,12 +340,10 @@ function addPendingTx(client, session, incoming, currencyCode, toAddress, toAddress, satoshis]; query(client, sql, values, function(err) { - // If pending tx already exists, do nothing - if (err && isUniqueViolation(err)) + if (err && isUniqueViolation(err)) { return refreshPendingTx(client, session, cb); - - if (err && !isUniqueViolation(err)) return cb(err); - + } + if (err) return cb(err); cb(); }); } @@ -429,15 +428,49 @@ exports.addIncomingTx = function addIncomingTx(session, tx, authority, }); }; +function ensureNotFinal(client, session, cb) { + var sql = 'SELECT id FROM transactions ' + + 'WHERE device_fingerprint=$1 AND session_id=$2 AND incoming=$3 ' + + 'AND stage=$4' + + 'LIMIT 1'; + var values = [session.fingerprint, session.id, false, 'final_request']; + + client.query(sql, values, function(err, results) { + var error; + if (err) return cb(err); + if (results.rows.length > 0) { + error = new Error('Final request already exists'); + error.name = 'staleBill'; + return cb(error); + } + cb(); + }); +} + exports.addOutgoingPending = function addOutgoingPending(session, currencyCode, toAddress, cb) { connect(function(cerr, client, done) { if (cerr) return cb(cerr); - addPendingTx(client, session, false, currencyCode, toAddress, 0, - function(err) { - done(); - cb(err); + async.series([ + async.apply(silentQuery, client, 'BEGIN', null), + async.apply(ensureNotFinal, client, session), + async.apply(addPendingTx, client, session, false, currencyCode, toAddress, + 0) + ], function(err) { + if (err) { + rollback(client, done); + if (err.name === 'staleBill') { + logger.info('Received a bill insert after send coins request'); + return cb(); + } + logger.error(err); + return cb(err); + } + silentQuery(client, 'COMMIT', null, function() { + done(); + cb(); + }); }); }); };