WIP 1st stage refactor to new DB
This commit is contained in:
parent
dc6740ddf2
commit
0cec3670a9
3 changed files with 96 additions and 64 deletions
|
|
@ -188,19 +188,17 @@ function _sendBitcoins(toAddress, satoshis, cb) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function executeTx(deviceFingerprint, tx, cb) {
|
function executeTx(deviceFingerprint, tx, cb) {
|
||||||
db.addTx(deviceFingerprint, tx, function(err, result) {
|
db.addOutgoingTx(deviceFingerprint, tx, function(err, satoshisToSend) {
|
||||||
if (err) return cb(err);
|
if (err) return cb(err);
|
||||||
var satoshisToSend = result.satoshisToSend;
|
|
||||||
var dbTxId = result.id;
|
|
||||||
|
|
||||||
if (satoshisToSend === 0)
|
if (satoshisToSend === 0)
|
||||||
return cb(null, {statusCode: 204, txId: tx.txId, txHash: null});
|
return cb(null, {statusCode: 204, txId: tx.txId, txHash: null});
|
||||||
|
|
||||||
_sendBitcoins(tx.toAddress, satoshisToSend, function(err, txHash) {
|
_sendBitcoins(tx.toAddress, satoshisToSend, function(_err, txHash) {
|
||||||
var fee = null; // Need to fill this out in plugins
|
var fee = null; // Need to fill this out in plugins
|
||||||
db.addDigitalTx(dbTxId, err, txHash, fee);
|
db.sentCoins(tx, satoshisToSend, fee, _err, txHash);
|
||||||
|
|
||||||
if (err) return cb(err);
|
if (_err) return cb(err);
|
||||||
|
|
||||||
pollBalance();
|
pollBalance();
|
||||||
cb(null, {
|
cb(null, {
|
||||||
|
|
@ -212,16 +210,16 @@ function executeTx(deviceFingerprint, tx, cb) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function reapIncomingTx(deviceFingerprint, tx) {
|
function reapOutgoingTx(deviceFingerprint, tx) {
|
||||||
executeTx(deviceFingerprint, tx, function(err) {
|
executeTx(deviceFingerprint, tx, function(err) {
|
||||||
if (err) logger.error(err);
|
if (err) logger.error(err);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function reapOutgoingTx(deviceFingerprint, tx) {
|
function reapIncomingTx(deviceFingerprint, tx) {
|
||||||
infoPlugin.checkAddress(tx.toAddress, function(err, status, satoshisReceived) {
|
infoPlugin.checkAddress(tx.toAddress, function(err, status, satoshisReceived) {
|
||||||
if (status === 'notSeen') return;
|
if (status === 'notSeen') return;
|
||||||
db.addOutgoingTx(deviceFingerprint, tx, status, satoshisReceived);
|
db.addIngoingTx(deviceFingerprint, tx, status, satoshisReceived);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -319,7 +317,7 @@ exports.cashOut = function cashOut(deviceFingerprint, tx, cb) {
|
||||||
return cb(new Error(err));
|
return cb(new Error(err));
|
||||||
|
|
||||||
tx.toAddress = address;
|
tx.toAddress = address;
|
||||||
tx.incoming = false;
|
tx.incoming = true;
|
||||||
|
|
||||||
db.addPendingTx(deviceFingerprint, tx, function(err) {
|
db.addPendingTx(deviceFingerprint, tx, function(err) {
|
||||||
cb(err, address);
|
cb(err, address);
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
var pg = require('pg');
|
var pg = require('pg');
|
||||||
var async = require('async');
|
var async = require('async');
|
||||||
var _ = require('lodash');
|
|
||||||
|
|
||||||
var logger = require('./logger');
|
var logger = require('./logger');
|
||||||
|
|
||||||
|
|
@ -110,17 +109,16 @@ function silentQuery(client, queryStr, values, cb) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// OPTIMIZE: No need to query bills if tx.fiat and tx.satoshis are set
|
// OPTIMIZE: No need to query bills if tx.fiat and tx.satoshis are set
|
||||||
function billsAndTxs(client, sessionId, currencyCode, deviceFingerprint, cb) {
|
function billsAndTxs(client, sessionId, cb) {
|
||||||
var billsQuery = 'SELECT COALESCE(SUM(denomination), 0) as fiat, ' +
|
var billsQuery = 'SELECT SUM(denomination) as fiat, ' +
|
||||||
'COALESCE(SUM(satoshis), 0) AS satoshis ' +
|
'SUM(satoshis) AS satoshis ' +
|
||||||
'FROM bills ' +
|
'FROM bills ' +
|
||||||
'WHERE transaction_id=$1 AND currency_code=$2 AND device_fingerprint=$3';
|
'WHERE session_id=$1';
|
||||||
var billsValues = [sessionId, currencyCode, deviceFingerprint];
|
var billsValues = [sessionId];
|
||||||
var txQuery = 'SELECT COALESCE(SUM(fiat), 0) AS fiat, ' +
|
var txQuery = 'SELECT SUM(fiat) AS fiat, SUM(satoshis) AS satoshis ' +
|
||||||
'COALESCE(SUM(satoshis), 0) AS satoshis ' +
|
|
||||||
'FROM transactions ' +
|
'FROM transactions ' +
|
||||||
'WHERE session_id=$1 AND currency_code=$2 AND device_fingerprint=$3';
|
'WHERE session_id=$1 AND stage=$2';
|
||||||
var txValues = billsValues; // They happen to be the same
|
var txValues = [sessionId, 'partialRequest'];
|
||||||
|
|
||||||
async.parallel([
|
async.parallel([
|
||||||
async.apply(query, client, billsQuery, billsValues),
|
async.apply(query, client, billsQuery, billsValues),
|
||||||
|
|
@ -155,12 +153,11 @@ function computeSendAmount(tx, totals) {
|
||||||
|
|
||||||
exports.pendingTxs = function pendingTxs(timeoutMS, cb) {
|
exports.pendingTxs = function pendingTxs(timeoutMS, cb) {
|
||||||
connect(function(err, client, done) {
|
connect(function(err, client, done) {
|
||||||
var sql = 'SELECT * FROM transactions ' +
|
var sql = 'SELECT * FROM pending_transactions ' +
|
||||||
'WHERE status=$1 AND ' +
|
'WHERE (incoming OR EXTRACT(EPOCH FROM now() - created > $2) ' +
|
||||||
'(NOT incoming OR EXTRACT(EPOCH FROM now() - created > $2) ' +
|
|
||||||
'ORDER BY created ASC';
|
'ORDER BY created ASC';
|
||||||
var timeoutS = timeoutMS / 1000;
|
var timeoutS = timeoutMS / 1000;
|
||||||
var values = ['pending', timeoutS];
|
var values = [timeoutS];
|
||||||
query(client, sql, values, function(err, results) {
|
query(client, sql, values, function(err, results) {
|
||||||
done();
|
done();
|
||||||
cb(err, results);
|
cb(err, results);
|
||||||
|
|
@ -169,26 +166,42 @@ exports.pendingTxs = function pendingTxs(timeoutMS, cb) {
|
||||||
};
|
};
|
||||||
|
|
||||||
function removePendingTx(client, tx, cb) {
|
function removePendingTx(client, tx, cb) {
|
||||||
silentQuery(client, 'DELETE FROM transactions WHERE session_id=$1 AND status=$2',
|
var sql = 'DELETE FROM pending_transactions WHERE session_id=$1';
|
||||||
[tx.txId, 'pending'], cb);
|
silentQuery(client, sql, [tx.txId], cb);
|
||||||
}
|
}
|
||||||
|
|
||||||
function maybeInsertTx(client, deviceFingerprint, tx, totals, cb) {
|
function insertOutgoingTx(client, deviceFingerprint, tx, totals, cb) {
|
||||||
var sendAmount = computeSendAmount(tx, totals);
|
var sendAmount = computeSendAmount(tx, totals);
|
||||||
var status = _.isNumber(tx.fiat) ? 'machineSend' : 'timeout';
|
var stage = 'partial_request';
|
||||||
|
var source = tx.fiat ? 'machine' : 'timeout';
|
||||||
var satoshis = sendAmount.satoshis;
|
var satoshis = sendAmount.satoshis;
|
||||||
var fiat = sendAmount.fiat;
|
var fiat = sendAmount.fiat;
|
||||||
insertTx(client, deviceFingerprint, tx, satoshis, fiat, status, function(err, results) {
|
insertTx(client, deviceFingerprint, tx, satoshis, fiat, stage, source,
|
||||||
// TODO: Don't worry about unique violation
|
function(err) {
|
||||||
|
|
||||||
if (err) return cb(err);
|
if (err) return cb(err);
|
||||||
cb(null, {id: results.rows[0].id, satoshisToSend: sendAmount.satoshis});
|
cb(null, satoshis);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function insertTx(client, deviceFingerprint, tx, satoshis, fiat, status, cb) {
|
function insertOutgoingCompleteTx(client, deviceFingerprint, tx, cb) {
|
||||||
|
|
||||||
|
// Only relevant for machine source transactions, not timeouts
|
||||||
|
if (!tx.fiat) return cb();
|
||||||
|
|
||||||
|
var stage = 'final_request';
|
||||||
|
var source = 'machine';
|
||||||
|
var satoshis = tx.satoshis;
|
||||||
|
var fiat = tx.fiat;
|
||||||
|
insertTx(client, deviceFingerprint, tx, satoshis, fiat, stage, source, cb);
|
||||||
|
}
|
||||||
|
|
||||||
|
function insertTx(client, deviceFingerprint, tx, satoshis, fiat, stage,
|
||||||
|
source, cb) {
|
||||||
var fields = [
|
var fields = [
|
||||||
'session_id',
|
'session_id',
|
||||||
'status',
|
'stage',
|
||||||
|
'source',
|
||||||
'incoming',
|
'incoming',
|
||||||
'device_fingerprint',
|
'device_fingerprint',
|
||||||
'to_address',
|
'to_address',
|
||||||
|
|
@ -199,8 +212,9 @@ function insertTx(client, deviceFingerprint, tx, satoshis, fiat, status, cb) {
|
||||||
|
|
||||||
var values = [
|
var values = [
|
||||||
tx.txId,
|
tx.txId,
|
||||||
status,
|
stage,
|
||||||
tx.incoming === false ? false : true,
|
source,
|
||||||
|
tx.incoming,
|
||||||
deviceFingerprint,
|
deviceFingerprint,
|
||||||
tx.toAddress,
|
tx.toAddress,
|
||||||
satoshis,
|
satoshis,
|
||||||
|
|
@ -211,32 +225,60 @@ function insertTx(client, deviceFingerprint, tx, satoshis, fiat, status, cb) {
|
||||||
query(client, getInsertQuery('transactions', fields, true), values, cb);
|
query(client, getInsertQuery('transactions', fields, true), values, cb);
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.addPendingTx = function addPendingTx(deviceFingerprint, tx, cb) {
|
exports.addPendingTx = function addPendingTx(deviceFingerprint, sessionId,
|
||||||
|
incoming, cb) {
|
||||||
connect(function(err, client, done) {
|
connect(function(err, client, done) {
|
||||||
if (err) return cb(err);
|
if (err) return cb(err);
|
||||||
insertTx(client, deviceFingerprint, tx, tx.satoshis, tx.fiat, 'pending',
|
var fields = ['session_id', 'incoming'];
|
||||||
function(err) {
|
var sql = getInsertQuery('pending_transactions', fields);
|
||||||
|
query(client, sql, [sessionId, incoming], function(_err) {
|
||||||
done();
|
done();
|
||||||
|
|
||||||
// If pending tx already exists, do nothing
|
// If pending tx already exists, do nothing
|
||||||
if (err && PG_ERRORS[err.code] !== 'uniqueViolation') {
|
if (_err && PG_ERRORS[err.code] !== 'uniqueViolation')
|
||||||
logger.error(err);
|
logger.error(err);
|
||||||
return cb(err);
|
|
||||||
}
|
cb(_err);
|
||||||
cb();
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// Calling function should only send bitcoins if result.satoshisToSend > 0
|
// Calling function should only send bitcoins if result.satoshisToSend > 0
|
||||||
exports.addTx = function addTx(deviceFingerprint, tx, cb) {
|
exports.addOutgoingTx = function addOutgoingTx(deviceFingerprint, tx, cb) {
|
||||||
connect(function(err, client, done) {
|
connect(function(err, client, done) {
|
||||||
if (err) return cb(err);
|
if (err) return cb(err);
|
||||||
async.waterfall([
|
async.waterfall([
|
||||||
async.apply(silentQuery, client, 'BEGIN', null),
|
async.apply(silentQuery, client, 'BEGIN', null),
|
||||||
|
async.apply(insertOutgoingCompleteTx, client, deviceFingerprint, tx)
|
||||||
async.apply(removePendingTx, client, tx),
|
async.apply(removePendingTx, client, tx),
|
||||||
async.apply(billsAndTxs, client, tx.txId, tx.currencyCode, deviceFingerprint),
|
async.apply(billsAndTxs, client, tx.txId, tx.currencyCode, deviceFingerprint),
|
||||||
async.apply(maybeInsertTx, client, deviceFingerprint, tx)
|
async.apply(insertOutgoingTx, client, deviceFingerprint, tx),
|
||||||
|
], function(err, satoshisToSend) {
|
||||||
|
if (err) {
|
||||||
|
rollback(client, done);
|
||||||
|
return cb(err);
|
||||||
|
}
|
||||||
|
silentQuery(client, 'COMMIT', null, function() {
|
||||||
|
done();
|
||||||
|
cb(null, satoshisToSend);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
function removeIncomingPendingTx(client, tx, status, cb) {
|
||||||
|
if (status !== 'published') return removePendingTx(client, tx, cb);
|
||||||
|
cb();
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.addIncomingTx = function addIncomingTx(deviceFingerprint, tx, status,
|
||||||
|
satoshisReceived, cb) {
|
||||||
|
connect(function(err, client, done) {
|
||||||
|
if (err) return cb(err);
|
||||||
|
async.waterfall([
|
||||||
|
async.apply(silentQuery, client, 'BEGIN', null),
|
||||||
|
async.apply(removeOutgoingPendingTx, client, tx, status),
|
||||||
|
async.apply(insertTx, client, tx, satoshisReceived, 0, 'deposit')
|
||||||
], function(err, result) {
|
], function(err, result) {
|
||||||
if (err) {
|
if (err) {
|
||||||
rollback(client, done);
|
rollback(client, done);
|
||||||
|
|
@ -250,19 +292,6 @@ exports.addTx = function addTx(deviceFingerprint, tx, cb) {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.addDigitalTx = function addDigitalTx(dbTxId, err, txHash) {
|
|
||||||
var keys = ['transaction_id', 'tx_hash', 'error'];
|
|
||||||
var values = [dbTxId, txHash, err && err.message];
|
|
||||||
var sql = getInsertQuery('digital_transactions', keys);
|
|
||||||
|
|
||||||
connect(function(err, client, done) {
|
|
||||||
query(client, sql, values, function(_err) {
|
|
||||||
done(_err);
|
|
||||||
if (_err) logger.error(_err);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
exports.decrementCartridges =
|
exports.decrementCartridges =
|
||||||
function decrementCartridges(fingerprint, cartridge1, cartridge2, cb) {
|
function decrementCartridges(fingerprint, cartridge1, cartridge2, cb) {
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,9 @@ var db = require('./db');
|
||||||
function singleQuotify(item) { return '\'' + item + '\''; }
|
function singleQuotify(item) { return '\'' + item + '\''; }
|
||||||
|
|
||||||
exports.up = function(next){
|
exports.up = function(next){
|
||||||
var stages = ['partial_request', 'partial_send', 'complete',
|
var stages = ['initial_request', 'partial_request', 'final_request',
|
||||||
'initial_request', 'deposit', 'supplemental_request', 'dispense_request',
|
'partial_send', 'deposit', 'dispense_request', 'dispense'].
|
||||||
'dispense'].map(singleQuotify).join(',');
|
map(singleQuotify).join(',');
|
||||||
var sources = ['timeout', 'machine', 'published', 'authorized', 'rejected'].
|
var sources = ['timeout', 'machine', 'published', 'authorized', 'rejected'].
|
||||||
map(singleQuotify).join(',');
|
map(singleQuotify).join(',');
|
||||||
var sqls = [
|
var sqls = [
|
||||||
|
|
@ -23,7 +23,12 @@ exports.up = function(next){
|
||||||
'ALTER TABLE transactions ADD COLUMN incoming boolean DEFAULT false',
|
'ALTER TABLE transactions ADD COLUMN incoming boolean DEFAULT false',
|
||||||
'ALTER TABLE transactions ADD COLUMN stage transaction_stage NULL',
|
'ALTER TABLE transactions ADD COLUMN stage transaction_stage NULL',
|
||||||
'ALTER TABLE transactions ADD COLUMN source transaction_source NULL',
|
'ALTER TABLE transactions ADD COLUMN source transaction_source NULL',
|
||||||
|
'ALTER TABLE transactions ADD COLUMN fee integer NOT NULL DEFAULT 0',
|
||||||
'ALTER TABLE transactions ADD COLUMN error text NULL',
|
'ALTER TABLE transactions ADD COLUMN error text NULL',
|
||||||
|
'ALTER TABLE transactions ALTER COLUMN fiat SET DEFAULT 0',
|
||||||
|
'ALTER TABLE transactions ALTER COLUMN satoshis SET DEFAULT 0',
|
||||||
|
'ALTER TABLE transactions ALTER COLUMN fiat SET NOT NULL',
|
||||||
|
'ALTER TABLE transactions ALTER COLUMN satoshis SET NOT NULL',
|
||||||
'ALTER TABLE transactions ADD CONSTRAINT transactions_unique_source ' +
|
'ALTER TABLE transactions ADD CONSTRAINT transactions_unique_source ' +
|
||||||
'UNIQUE (session_id,to_address,stage,source)',
|
'UNIQUE (session_id,to_address,stage,source)',
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue