This commit is contained in:
Josh Harvey 2016-03-27 16:57:18 +02:00
parent 668ba7d08c
commit c8ba96515f

View file

@ -18,9 +18,9 @@ var DEPOSIT_TIMEOUT = 130 * 1000;
var db = null;
var tickerPlugin = null;
var tickerPlugins = {};
var traderPlugin = null;
var walletPlugin = null;
var walletPlugins = {};
var idVerifierPlugin = null;
var infoPlugin = null;
@ -29,14 +29,9 @@ var currentlyUsedPlugins = {};
var cachedConfig = null;
var deviceCurrency = 'USD';
var lastBalances = null;
var lastBalances = {};
var lastRates = {};
var balanceInterval = null;
var rateInterval = null;
var tradeInterval = null;
var reapTxInterval = null;
var tradesQueue = [];
// that's basically a constructor
@ -104,10 +99,15 @@ function loadPlugin(name, config) {
return plugin;
}
function loadOrConfigPlugin(pluginHandle, pluginType, currency,
function loadOrConfigPlugin(pluginHandle, pluginType, cryptoCoin, currency,
onChangeCallback) {
var currentName = cachedConfig.exchanges.plugins.current[pluginType];
var pluginChanged = currentlyUsedPlugins[pluginType] !== currentName;
if (!cryptoCoin) cryptoCoin = 'any'
var currentName = cryptoCoin === 'any' || cryptoCoin === 'BTC'
? cachedConfig.exchanges.plugins.current[pluginType]
: cachedConfig.exchanges.plugins.current[cryptoCoin][pluginType]
var pluginChanged = currentlyUsedPlugins[cryptoCoin][pluginType] !== currentName;
if (!currentName) pluginHandle = null;
else { // some plugins may be disabled
@ -119,8 +119,9 @@ function loadOrConfigPlugin(pluginHandle, pluginType, currency,
if (pluginHandle && !pluginChanged) pluginHandle.config(pluginConfig);
else {
pluginHandle = loadPlugin(currentName, pluginConfig);
currentlyUsedPlugins[pluginType] = currentName
logger.debug('plugin(%s) loaded: %s', pluginType, pluginHandle.NAME ||
currentlyUsedPlugins[cryptoCoin] ||= {}
currentlyUsedPlugins[cryptoCoin][pluginType] = currentName
logger.debug('[%s] plugin(%s) loaded: %s', cryptoCoin, pluginType, pluginHandle.NAME ||
currentName);
}
}
@ -138,34 +139,40 @@ exports.configure = function configure(config) {
cachedConfig = config;
deviceCurrency = config.exchanges.settings.currency;
cryptoCoins = config.exchanges.settings.coins || ['BTC'];
cryptoCoins.forEach(function (cryptoCoin) {
// TICKER [required] configure (or load)
loadOrConfigPlugin(
tickerPlugin,
tickerPlugins[cryptoCoin],
'ticker',
cryptoCoin,
deviceCurrency, // device currency
function onTickerChange(newTicker) {
tickerPlugin = newTicker;
pollRate();
tickerPlugins[cryptoCoin] = newTicker;
pollRate(cryptoCoin);
}
);
// WALLET [required] configure (or load)
loadOrConfigPlugin(
walletPlugin,
walletPlugins[cryptoCoin],
'transfer',
cryptoCoin,
null,
function onWalletChange(newWallet) {
walletPlugin = newWallet;
pollBalance();
walletPlugins[cryptoCoin] = newWallet;
pollBalance(cryptoCoin);
}
);
})
// TRADER [optional] configure (or load)
traderPlugin = loadOrConfigPlugin(
traderPlugin,
'trade',
null,
null,
function onTraderChange(newTrader) {
traderPlugin = newTrader;
if (newTrader === null) stopTrader();
@ -222,26 +229,35 @@ exports.pollQueries = function pollQueries(session, cb) {
});
};
function _sendBitcoins(toAddress, satoshis, cb) {
function _sendCoins(toAddress, cryptoUnits, cryptoCoin, cb) {
var walletPlugin = walletPlugins[cryptoCoin]
var transactionFee = cachedConfig.exchanges.settings.transactionFee;
walletPlugin.sendBitcoins(toAddress, satoshis, transactionFee, cb);
if (cryptoCoin === 'BTC')
walletPlugin.sendBitcoins(toAddress, cryptoUnits, transactionFee, cb);
else
walletPlugin.sendCoins(toAddress, cryptoUnits, cryptoCoin, transactionFee, cb);
}
function executeTx(session, tx, authority, cb) {
db.addOutgoingTx(session, tx, function(err, toSend) {
if (err) return cb(err);
var satoshisToSend = toSend.satoshis;
if (satoshisToSend === 0)
var cryptoUnitsToSend = toSend.cryptoUnits;
if (cryptoUnitsToSend === 0)
return cb(null, {statusCode: 204, txId: tx.txId, txHash: null});
_sendBitcoins(tx.toAddress, satoshisToSend, function(_err, txHash) {
_sendCoins(tx.toAddress, cryptoUnitsToSend, function(_err, txHash) {
var fee = null; // Need to fill this out in plugins
if (_err) toSend = {satoshis: 0, fiat: 0};
if (_err) toSend = {cryptoUnits: new BigNumber(0), fiat: 0};
db.sentCoins(session, tx, authority, toSend, fee, _err, txHash);
if (_err) return cb(_err);
pollBalance();
var cryptoCoin = tx.coin
? tx.coin.unitCode
: 'BTC'
pollBalance('BTC');
cb(null, {
statusCode: 201, // Created
txHash: txHash,
@ -326,6 +342,13 @@ exports.cashOut = function cashOut(session, tx, cb) {
label: 'TX ' + Date.now(),
account: 'deposit'
};
var cryptoCoin = tx.coin
? tx.coin.unitCode
: 'BTC'
var walletPlugin = walletPlugins[cryptoCoin]
walletPlugin.newAddress(tmpInfo, function(err, address) {
if (err) return cb(err);
@ -341,11 +364,12 @@ exports.dispenseAck = function dispenseAck(session, rec) {
db.addDispense(session, rec.tx, rec.cartridges);
};
exports.fiatBalance = function fiatBalance() {
var rawRate = exports.getDeviceRate().rates.ask;
exports.fiatBalance = function fiatBalance(cryptoCoin) {
var rawRate = exports.getDeviceRate(cryptoCoin).rates.ask;
var commission = cachedConfig.exchanges.settings.commission;
var lastBalance = lastBalances[cryptoCoin]
if (!rawRate || !lastBalances) return null;
if (!rawRate || !lastBalance) return null;
// The rate is actually our commission times real rate.
var rate = commission * rawRate;
@ -356,16 +380,9 @@ exports.fiatBalance = function fiatBalance() {
// `balance.transferBalance` is the balance of our transfer account (the one
// we use to send Bitcoins to clients) in satoshis.
var transferBalance = lastBalances.transferBalance.BTC;
var transferBalance = lastBalances.transferBalance;
// Since `transferBalance` is in satoshis, we need to turn it into
// bitcoins and then fiat to learn how much fiat currency we can exchange.
//
// Unit validity proof: [ $ ] = [ (B * 10^8) / 10^8 * $/B ]
// [ $ ] = [ B * $/B ]
// [ $ ] = [ $ ]
var fiatTransferBalance = ((transferBalance / SATOSHI_FACTOR) * rate) /
lowBalanceMargin;
var fiatTransferBalance = (transferBalance * rate) / lowBalanceMargin;
return fiatTransferBalance;
};
@ -376,14 +393,12 @@ exports.fiatBalance = function fiatBalance() {
exports.startPolling = function startPolling() {
executeTrades();
if (!balanceInterval)
balanceInterval = setInterval(pollBalance, POLLING_RATE);
cryptoCoins.forEach(function (coin) {
setInterval(async.apply(pollBalance, coin), POLLING_RATE);
setInterval(async.apply(pollRate, coin), POLLING_RATE);
});
if (!rateInterval)
rateInterval = setInterval(pollRate, POLLING_RATE);
if (!reapTxInterval)
reapTxInterval = setInterval(reapTxs, REAP_RATE);
setInterval(reapTxs, REAP_RATE);
startTrader();
};
@ -400,6 +415,7 @@ function startTrader() {
);
}
}
function stopTrader() {
if (tradeInterval) {
clearInterval(tradeInterval);
@ -408,35 +424,39 @@ function stopTrader() {
}
}
function pollBalance(cb) {
logger.debug('collecting balance');
function pollBalance(cryptoCoin, cb) {
logger.debug('[%s] collecting balance', cryptoCoin);
var walletPlugin = walletPlugins[cryptoCoin]
var jobs = {
transferBalance: walletPlugin.balance
};
// only add if trader is enabled
// if (traderPlugin) {
// // NOTE: we would need to handle when traderCurrency!=deviceCurrency
// jobs.tradeBalance = traderPlugin.balance;
// }
async.parallel(jobs, function(err, balance) {
walletPlugin.balance(function(err, balance) {
if (err) {
logger.error(err);
return cb && cb(err);
}
logger.debug('Balance update:', balance);
logger.debug('[%s] Balance update:', cryptoCoin, balance);
balance.timestamp = Date.now();
lastBalances = balance;
lastBalances[cryptoCoin] = balance;
return cb && cb(null, lastBalances);
});
}
function pollRate(cb) {
logger.debug('polling for rates (%s)', tickerPlugin.NAME);
function pollRates (cb) {
var polls = cryptoCoins.map(function (cryptoCoin) {
async.apply(pollRate, cryptoCoin)
});
async.parallel(polls, cb);
}
function pollRate(cryptoCoin, cb) {
logger.debug('[%s] polling for rates (%s)', cryptoCoin, tickerPlugin.NAME);
var tickerPlugin = tickerPlugins[cryptoCoin];
tickerPlugin.ticker(deviceCurrency, function(err, resRates) {
if (err) {
@ -446,7 +466,7 @@ function pollRate(cb) {
logger.debug('got rates: %j', resRates);
resRates.timestamp = new Date();
lastRates = resRates;
lastRates[cryptoCoin] = resRates;
return cb && cb(null, lastRates);
});
@ -455,16 +475,14 @@ function pollRate(cb) {
/*
* Getters | Helpers
*/
function getLastRate(currency) {
if (!lastRates) return null;
var tmpCurrency = currency || deviceCurrency;
if (!lastRates[tmpCurrency]) return null;
exports.getDeviceRate = function getDeviceRate(cryptoCoin) {
if (!lastRates[cryptoCoin]) return null;
return lastRates[tmpCurrency];
}
exports.getDeviceRate = function getDeviceRate() {
return getLastRate(deviceCurrency);
var lastRate = lastRates[cryptoCoin]
if (!lastRate) return null;
return lastRate[deviceCurrency];
};
exports.getBalance = function getBalance() {
@ -479,7 +497,7 @@ exports.getBalance = function getBalance() {
function purchase(trade, cb) {
traderPlugin.purchase(trade.satoshis, null, function(err) {
if (err) return cb(err);
pollBalance();
pollBalance('BTC');
if (typeof cb === 'function') cb();
});
}