feat(santo-tirso): 2nd direction w/now plugin type working
This commit is contained in:
parent
0c0fb74dcd
commit
4ebc3db302
2 changed files with 90 additions and 98 deletions
183
lib/plugins.js
183
lib/plugins.js
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue