feat(santo-tirso): 2nd direction w/now plugin type working

This commit is contained in:
Damian Mee 2014-11-11 05:46:12 +01:00
parent 0c0fb74dcd
commit 4ebc3db302
2 changed files with 90 additions and 98 deletions

View file

@ -9,6 +9,10 @@ var SATOSHI_FACTOR = 1e8;
var SESSION_TIMEOUT = 60 * 60 * 1000;
var POLLING_RATE = 60 * 1000; // poll each minute
var RECOMMENDED_FEE = 10000;
var TX_0CONF_WAIT_TIME = 20 * 1000; // wait 20 seconds
var MIN_CONFIDENCE = 0.7;
var db = null;
@ -54,7 +58,7 @@ function loadPlugin(name, config) {
trader: [ 'balance', 'purchase', 'sell' ],
wallet: [ 'balance', 'sendBitcoins', 'newAddress' ],
idVerifier: [ 'verifyUser', 'verifyTransaction' ],
info: [ 'getLastTx', 'getTxStatus' ]
info: [ 'getAddressLastTx', 'getTx' ]
};
var plugin = null;
@ -111,7 +115,10 @@ function loadOrConfigPlugin(pluginHandle, pluginType, currency, onChangeCallback
if (currency) pluginConfig.currency = currency;
if (pluginHandle && !pluginChanged) pluginHandle.config(pluginConfig);
else pluginHandle = loadPlugin(currentName, pluginConfig);
else {
pluginHandle = loadPlugin(currentName, pluginConfig);
logger.debug('plugin(%s) loaded: %s', pluginType, pluginHandle.NAME || currentName);
}
}
if (typeof onChangeCallback === 'function') onChangeCallback(pluginHandle, currency);
@ -298,112 +305,111 @@ exports.sendBitcoins = function sendBitcoins(deviceFingerprint, rawTx, cb) {
executeTx(deviceFingerprint, rawTx.txId, true, cb);
};
function _monitorAddress(address, cb) {
var confs = 0;
var received = 0;
var t0 = Date.now();
var timeOut = 90000; // TODO make config
var interval = 300; // TODO make config
function checkAddress(_cb) {
infoPlugin.getLastTx(address, function(err, tx) {
if (err) logger.error(err);
// sets given status both "locally" (dispenseStatuses) and saves to db
function _setDispenseStatus(deviceFingerprint, tx, status, deposit) {
tx.status = status;
if (_received > 0) received = _received;
setTimeout(_cb, interval);
// No need to set default state again
if (status !== 'noDeposit')
// save to db ASAP
db.changeTxStatus(tx.txId, status, {
hash: tx.txHash
});
}
function test() {
return received > 0 || Date.now() - t0 > timeOut;
}
var fiat = 0;
if (status === 'authorizedDeposit' || status === 'confirmedDeposit')
fiat = tx.fiat;
function handler() {
if (received === 0)
return cb(new Error('Timeout while monitoring address'));
cb(null, received);
}
async.doUntil(checkAddress, test, handler);
}
function _awaitDeposit(deviceFingerprint, tx) {
_monitorAddress(tx.toAddress, function(err, received) {
var status = 'fullDeposit';
if (err) status = 'timeout';
else if (received < tx.satoshis) status = 'insufficientDeposit';
var dispenseFiat = received >= tx.satoshis ? tx.fiat : 0;
dispenseStatuses[deviceFingerprint] = {
var statusObject = null;
if (status !== 'dispensedDeposit')
statusObject = {
status: status,
txId: tx.txId,
deposit: received,
dispenseFiat: dispenseFiat,
deposit: deposit || 0,
dispenseFiat: fiat,
expectedDeposit: tx.satoshis
};
// TODO db.dispenseReady(tx);
});
// keep local copy
dispenseStatuses[deviceFingerprint] = statusObject;
}
function _checkTx(deviceFingerprint, tx, txInfo) {
// accept if tx is already confirmed
if (txInfo.confirmations > 0) {
_setDispenseStatus(deviceFingerprint, tx, 'confirmedDeposit', txInfo.amount);
return true;
}
// NOTE: we can put some heuristics here
// consider authorization raported by the 'info' plugin
if (txInfo.authorized === true && txInfo.confidence >= MIN_CONFIDENCE) {
_setDispenseStatus(deviceFingerprint, tx, 'authorizedDeposit', txInfo.amount);
return true;
}
// SHOULD TAKE MUCH MORE FACTORS INTO ACCOUNT HERE
// accept txs with recommended fee and with at least 20s of propagation time
if (txInfo.fees >= RECOMMENDED_FEE && txInfo.tsReceived + TX_0CONF_WAIT_TIME < Date.now()) {
_setDispenseStatus(deviceFingerprint, tx, 'authorizedDeposit', txInfo.amount);
return true;
}
return false;
}
// this is invoked only when tx is fresh enough AND is for a right amount
function _monitorTx(deviceFingerprint, tx) {
infoPlugin.getTxStatus(tx.txHash, function(err, txStatus) {
if (err)
infoPlugin.getTx(tx.txHash, tx.toAddress, function(err, txInfo) {
if (err) {
logger.error(err);
return setTimeout(_monitorTx, 300, [deviceFingerprint, tx]);
if (!txStatus || txStatus === 'fullDeposit')
return setTimeout(_monitorTx, 300, [deviceFingerprint, tx]);
if (txStatus.status === 'confirmedDeposit')
return db.changeTxStatus(tx.txId, 'confirmedDeposit');
if (txStatus.status === 'authorizedDeposit') {
logger.info('Proceeding with confidence level:' + txStatus.confidence);
db.changeTxStatus(tx.txId, 'confirmedDeposit');
}
if (_checkTx(deviceFingerprint, tx, txInfo))
return;
setTimeout(_monitorTx, 300, [deviceFingerprint, tx]);
});
}
function _monitorAddress(deviceFingerprint, tx) {
infoPlugin.getLastTx(tx.toAddress, function(err, txInfo) {
infoPlugin.getAddressLastTx(tx.toAddress, function(err, txInfo) {
if (err) {
logger.error(err);
return setTimeout(_monitorAddress, 300, [deviceFingerprint, tx]);
}
// no tx occured at all or deposit address was reused; some previous tx was returned
if (!txInfo || txInfo.tsReceived < tx.created)
if (!txInfo || txInfo.tsReceived < tx.created) {
return setTimeout(_monitorAddress, 300, [deviceFingerprint, tx]);
// enough was sent
if (txInfo.amount >= tx.satoshis) {
tx.txHash = txInfo.txHash;
// tx is already confirmed
if (txInfo.confirmations > 0)
return db.changeTxStatus(tx.txId, 'confirmedDeposit', {
hash: tx.txHash
});
// warn about dangerous TX
if (txInfo.fees === 0)
logger.warn('TXs w/o fee can take forever to confirm!');
// update tx status and save txHash
db.changeTxStatus(tx.txId, 'fullDeposit', {
hash: tx.txHash
});
// start monitoring TX
_monitorTx(deviceFingerprint, tx);
}
// when sent TX is not enough
if (txInfo.amount < tx.satoshis)
return _setDispenseStatus(deviceFingerprint, tx, 'insufficientDeposit', txInfo.amount);
// store txHash for later reference
tx.txHash = txInfo.txHash;
// warn about dangerous TX
if (txInfo.fees < RECOMMENDED_FEE)
logger.warn('TXs w/o fee can take forever to confirm!');
// make sure tx isn't already in an acceptable state
if (_checkTx(deviceFingerprint, tx, txInfo))
return;
// update tx status and save first txHash
_setDispenseStatus(deviceFingerprint, tx, 'fullDeposit', txInfo.amount);
// start monitoring TX (instead of an address)
setTimeout(_monitorTx, 300, [deviceFingerprint, tx]);
});
}
exports.cashOut = function cashOut(deviceFingerprint, tx, cb) {
var tmpInfo = {
label: 'TX ' + Date.now(),
@ -420,8 +426,10 @@ exports.cashOut = function cashOut(deviceFingerprint, tx, cb) {
if (err)
return cb(new Error(err));
_setDispenseStatus(deviceFingerprint, tx, 'noDeposit');
// start watching address for incoming txs
_awaitDeposit(deviceFingerprint, tx);
_monitorAddress(deviceFingerprint, tx);
// return address to the machine
return cb(null, address);
@ -429,21 +437,8 @@ exports.cashOut = function cashOut(deviceFingerprint, tx, cb) {
});
};
exports.depositAck = function depositAck(deviceFingerprint, tx, cb) {
/* TODO
var status = dispenseStatuses[deviceFingerprint];
if (status === 'dispense') {
db.dispensing(tx, function (err) {
if (err) return cb(new Error(err));
dispenseStatuses[deviceFingerprint] = null;
return cb();
});
}
*/
dispenseStatuses[deviceFingerprint] = null;
cb();
exports.depositAck = function depositAck(deviceFingerprint, tx) {
_setDispenseStatus(deviceFingerprint, tx, 'dispensedDeposit');
};
exports.dispenseStatus = function dispenseStatus(deviceFingerprint) {

View file

@ -101,10 +101,7 @@ function cashOut(req, res) {
function depositAck(req, res) {
plugins.depositAck(getFingerprint(req), req.body, function(err) {
res.json({
err: err && err.message,
errType: err && err.name
});
res.json(200);
});
}