diff --git a/lib/app.js b/lib/app.js index 847ae3e4..30c41f2f 100644 --- a/lib/app.js +++ b/lib/app.js @@ -5,7 +5,7 @@ var https = require('https'); var express = require('express'); var LamassuConfig = require('lamassu-config'); var routes = require('./routes'); -var Trader = require('./trader'); +var plugins = require('./plugins'); var PostgresqlInterface = require('./postgresql_interface'); var logger = require('./logger'); @@ -14,7 +14,6 @@ module.exports = function (options) { var connectionString; var server; var config; - var trader; var db; connectionString = options.postgres || @@ -22,7 +21,8 @@ module.exports = function (options) { config = new LamassuConfig(connectionString); db = new PostgresqlInterface(connectionString); - trader = new Trader(db); + plugins.init(db); + config.load(function (err, config) { if (err) { @@ -30,8 +30,8 @@ module.exports = function (options) { throw err; } - trader.configure(config); - trader.startPolling(); + plugins.configure(config); + plugins.startPolling(); }); config.on('configUpdate', function () { @@ -40,7 +40,7 @@ module.exports = function (options) { return logger.error('Error while reloading config'); } - trader.configure(config); + plugins.configure(config); logger.info('Config reloaded'); }); }); @@ -89,7 +89,7 @@ module.exports = function (options) { routes.init({ app: app, lamassuConfig: config, - trader: trader, + plugins: plugins, authMiddleware: authMiddleware, mock: options.mock }); diff --git a/lib/plugins.js b/lib/plugins.js new file mode 100644 index 00000000..5aedfc68 --- /dev/null +++ b/lib/plugins.js @@ -0,0 +1,401 @@ +'use strict'; + +var async = require('async'); + +var logger = require('./logger'); + + +var SATOSHI_FACTOR = 1e8; +var SESSION_TIMEOUT = 60 * 60 * 1000; +var POLLING_RATE = 60 * 1000; // poll each minute + +var db = null; + +var tickerPlugin = null; +var traderPlugin = null; +var walletPlugin = null; +var idVerifierPlugin = null; + +var cachedConfig = null; +var deviceCurrency = 'USD'; // Can 'USD' be set as default? + +var lastBalances = null; +var lastRates = {}; + +var balanceInterval = null; +var rateInterval = null; +var tradeInterval = null; + +var tradesQueue = []; +var sessions = {}; + + +// that's basically a constructor +exports.init = function init(databaseHandle) { + if (!databaseHandle) { + throw new Error('`db` is required'); + } + + db = databaseHandle; +} + + +function loadPlugin(name, config) { + + // plugins definitions + var moduleMethods = { + ticker: [ 'ticker' ], + trader: [ 'balance', 'purchase', 'sell' ], + wallet: [ 'balance', 'sendBitcoins' ], + idVerifier: [ 'verifyUser', 'verifyTransaction' ] + }; + + var plugin = null; + + // each used plugin MUST be installed + try { + plugin = require('lamassu-' + name); + + } catch(_) { + throw new Error(name + ' module is not installed. Try running \'npm install --save lamassu-' + name + '\' first'); + } + + + // each plugin MUST implement those + if (typeof plugin.SUPPORTED_MODULES !== 'undefined') { + if(plugin.SUPPORTED_MODULES === 'string') + plugin.SUPPORTED_MODULES = [plugin.SUPPORTED_MODULES]; + } + + if(!(plugin.SUPPORTED_MODULES instanceof Array)) + throw new Error('\'' + name + '\' fails to implement *required* \'SUPPORTED_MODULES\' constant'); + + plugin.SUPPORTED_MODULES.forEach(function(moduleName) { + moduleMethods[moduleName].forEach(function(methodName) { + if (typeof plugin[methodName] !== 'function') { + throw new Error('\'' + name + '\' declares \'' + moduleName + '\', but fails to implement \'' + methodName + '\' method'); + } + }); + }); + + + // each plugin SHOULD implement those + if (typeof plugin.NAME === 'undefined') + logger.warn(new Error('\'' + name + '\' fails to implement *recommended* \'NAME\' field')); + + if (typeof plugin.config !== 'function') { + logger.warn(new Error('\'' + name + '\' fails to implement *recommended* \'config\' method')); + plugin.config = function() {}; + } else if (config !== null) + plugin.config(config); // only when plugin supports it, and config is passed + + + return plugin; +}; + + +exports.configure = function configure(config) { + if (config.exchanges.settings.lowBalanceMargin < 1) { + throw new Error('`settings.lowBalanceMargin` has to be >= 1'); + } + + deviceCurrency = config.exchanges.settings.currency; + var plugins = config.exchanges.plugins + + // [required] configure (or load) ticker + var tickerName = plugins.current.ticker; + var tickerConfig = plugins.settings[tickerName] || {}; + tickerConfig.currency = deviceCurrency; + + if (tickerPlugin) tickerPlugin.config(tickerConfig); + else tickerPlugin = loadPlugin(tickerName, tickerConfig); + + + // [required] configure (or load) wallet + var walletName = plugins.current.transfer; + var walletConfig = plugins.settings[walletName] || {}; + + if (walletPlugin) walletPlugin.config(walletConfig); + else walletPlugin = loadPlugin(walletName, walletConfig); + + + // [optional] configure (or load) trader + var traderName = plugins.current.trade; + if (traderName) { // traderPlugin may be disabled + var traderConfig = plugins.settings[traderName] || {}; + + if (traderPlugin) traderPlugin.config(traderConfig); + else traderPlugin = loadPlugin(traderName, traderConfig); + } + + + // [optional] ID Verifier + var verifierName = plugins.current.idVerifier; + if (verifierName) { // idVerifierPlugin may be disabled + var verifierConfig = plugins.settings[verifierName] || {}; + + if (idVerifierPlugin) idVerifierPlugin.config(verifierConfig); + else loadPlugin(verifierName, verifierConfig); + } + + + cachedConfig = config; + + pollBalance(); + pollRate(); +}; + + +// This is where we record starting trade balance at the beginning +// of the user session +exports.trade = function trade(rawTrade, deviceFingerprint) { + var sessionInfo = sessions[deviceFingerprint]; + + if (!sessionInfo) { + sessions[deviceFingerprint] = { + timestamp: Date.now(), + reaper: setTimeout(function() { + delete sessions[deviceFingerprint]; + }, SESSION_TIMEOUT) + }; + } + + tradesQueue.push({ + currency: rawTrade.currency, + satoshis: rawTrade.satoshis + }); +}; + +exports.fiatBalance = function fiatBalance(deviceFingerprint) { + var rawRate = getDeviceRate().rates.ask; + var commision = cachedConfig.exchanges.settings.commision; + + if (!rawRate || !lastBalances) return null; + + // The rate is actually our commission times real rate. + var rate = commission * rawRate; + + // `lowBalanceMargin` is our safety net. It's a number > 1, and we divide + // all our balances by it to provide a safety margin. + var lowBalanceMargin = cachedConfig.exchanges.settings.lowBalanceMargin; + + // `balance.transferBalance` is the balance of our transfer account (the one + // we use to send Bitcoins to clients) in satoshis. + var transferBalance = balance.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; + + return fiatTransferBalance; +}; + +exports.sendBitcoins = function sendBitcoins(deviceFingerprint, tx, callback) { + db.summonTransaction(deviceFingerprint, tx, function(err, txInfo) { + if (err) return callback(err); + + if (!txInfo) { + clearSession(deviceFingerprint); + + return walletPlugin.sendBitcoins( + tx.toAddress, + tx.satoshis, + cachedConfig.exchanges.settings.transactionFee, + + function(err, txHash) { + if (err) { + var status = err.name === 'InsufficientFunds' ? + 'insufficientFunds' : + 'failed'; + + db.reportTransactionError(tx, err.message, status); + return callback(err); + } + + db.completeTransaction(tx, txHash); + pollBalance(); + callback(null, txHash); + } + ); + } + + // Out of bitcoins: special case + var txErr = null; + if (txInfo.err) { + txErr = new Error(txInfo.err); + if (txInfo.status === 'insufficientFunds') { + txErr.name = 'InsufficientFunds'; + } + } + + // transaction exists, but txHash might be null, + // in which case ATM should continue polling + pollBalance(); + callback(txErr, txInfo.txHash); + }); +}; + + +/* + * Polling livecycle + */ +exports.startPolling = function startPolling() { + executeTrades(); + + if (!balanceInterval) { + balanceInterval = setInterval(pollBalance, POLLING_RATE); + } + + if (!rateInterval) { + rateInterval = setInterval(pollRate, POLLING_RATE); + } + + // Always start trading, even if we don't have a trade exchange configured, + // since configuration can always change in `Trader#configure`. + // `Trader#executeTrades` returns early if we don't have a trade exchange + // configured at the moment. + if (!tradeInterval) { + tradeInterval = setInterval( + executeTrades, + cachedConfig.exchanges.settings.tradeInterval + ); + } +}; +function stopPolling() { + clearInterval(balanceInterval); + clearInterval(rateInterval); + // clearInterval(tradeInterval); // TODO: should this get cleared too? +}; + + +function pollBalance(callback) { + logger.debug('collecting balance'); + + 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) { + if (err) { + logger.error(err); + return callback && callback(err); + } + + logger.debug('Balance update:', balance); + balance.timestamp = Date.now(); + lastBalances = balance; + + return callback && callback(null, lastBalances); + }); +}; + +function pollRate(callback) { + logger.debug('polling for rates'); + + tickerPlugin.ticker(deviceCurrency, function(err, resRates) { + if (err) { + logger.error(err); + return callback && callback(err); + } + + logger.debug('got rates: %j', resRates); + resRates.timestamp = new Date(); + lastRates = resRates; + + return callback && callback(null, lastRates); + }); +}; + + +/* + * Getters | Helpers + */ +function getLastRate(currency) { + if (!lastRates) return null; + + var tmpCurrency = currency || deviceCurrency; + if (!lastRates[tmpCurrency]) return null; + + return lastRates[tmpCurrency]; +}; +function getDeviceRate() { + return getLastRate(deviceCurrency); +}; + +function getBalance() { + if (!lastBalances) return null; + + return lastBalances.transferBalance; +}; + +function clearSession(deviceFingerprint) { + var session = sessions[deviceFingerprint]; + if (session) { + clearTimeout(session.reaper); + delete sessions[deviceFingerprint]; + } +}; + + +/* + * Trader functions + */ +function purchase(trade, callback) { + traderPlugin.purchase(trade.satoshis, null, function(err, _) { + if (err) return callback(err); + pollBalance(); + callback && callback(); + }); +}; + +function consolidateTrades() { + // NOTE: value in satoshis stays the same no matter the currency + var consolidatedTrade = { + currency: deviceCurrency, + satoshis: tradesQueue.reduce(function(prev, current) { + return prev + current.satoshis; + }, 0) + }; + + logger.debug('consolidated: ', JSON.stringify(consolidatedTrade)); + return consolidatedTrade; +}; + +function executeTrades() { + if (!traderPlugin) return; + + logger.debug('checking for trades'); + + var trade = consolidateTrades(); + + if (trade.satoshis === 0) { + logger.debug('rejecting 0 trade'); + return; + } + + logger.trade.debug('making a trade: %d', trade.satoshis / SATOSHI_FACTOR); + purchase(trade, function(err) { + if (err) logger.error(err); + }); +}; + +/* + * ID Verifier functions + */ +exports.verifyUser = function verifyUser(data, callback) { + idVerifierPlugin.verifyUser(data, callback); +}; + +exports.verifyTransaction = function verifyTransaction(data, callback) { + idVerifier.verifyTransaction(data, callback); +}; diff --git a/lib/routes.js b/lib/routes.js index 9dc107de..1eafab92 100644 --- a/lib/routes.js +++ b/lib/routes.js @@ -1,12 +1,12 @@ 'use strict'; -var _trader; -var _lamassuConfig; -var _idVerifier = null; -var _trader = null; -var _mock = false; var logger = require('./logger'); +var mock = false; + +var plugins; +var config; + module.exports = { init: init, getFingerprint: getFingerprint @@ -18,8 +18,8 @@ var STALE_TICKER = 180000; var STALE_BALANCE = 180000; function poll(req, res) { - var rateRec = _trader.rate(); - var balanceRec = _trader.balance; + var rateRec = plugins.getDeviceRate(); + var balanceRec = plugins.getBalance(); var fingerprint = getFingerprint(req); logger.debug('poll request from: %s', fingerprint); @@ -39,9 +39,9 @@ function poll(req, res) { return res.json({err: 'Stale balance'}); } - var rate = rateRec.rate; + var rate = rateRec.rates.ask; if (rate === null) return res.json({err: 'No rate available'}); - var fiatBalance = _trader.fiatBalance(fingerprint); + var fiatBalance = plugins.fiatBalance(fingerprint); if (fiatBalance === null) return res.json({err: 'No balance available'}); var idVerificationLimit = _trader.config.exchanges.settings. @@ -51,18 +51,17 @@ function poll(req, res) { res.json({ err: null, - rate: rate * _trader.config.exchanges.settings.commission, + rate: rate * config.exchanges.settings.commission, fiat: fiatBalance, - locale: _trader.config.brain.locale, - txLimit: parseInt(_trader.config.exchanges.settings.compliance.maximum.limit, 10), + locale: config.brain.locale, + txLimit: parseInt(config.exchanges.settings.compliance.maximum.limit, 10), idVerificationLimit: idVerificationLimit }); } function trade(req, res) { var fingerprint = getFingerprint(req); - _trader.trade(req.body, fingerprint); - + plugins.trade(req.body, fingerprint); res.json({err: null}); } @@ -72,21 +71,35 @@ function deviceEvent(req, res) { res.json({err: null}); } -function idVerify(req, res) { - if (_mock) return res.json({success: true}); +function verifyUser(req, res) { + if (mock) return res.json({success: true}); - _idVerifier.verify(req.body, function (err, idResult) { + plugins.verifyUser(req.body, function (err, idResult) { if (err) { logger.error(err); return res.json({err: 'Verification failed'}); } + + res.json(idResult); + }); +} + +function verifyTransaction(req, res) { + if (mock) return res.json({success: true}); + + plugins.verifyTransaction(req.body, function (err, idResult) { + if (err) { + logger.error(err); + return res.json({err: 'Verification failed'}); + } + res.json(idResult); }); } function send(req, res) { var fingerprint = getFingerprint(req); - _trader.sendBitcoins(fingerprint, req.body, function(err, txHash) { + plugins.sendBitcoins(fingerprint, req.body, function(err, txHash) { res.json({ err: err && err.message, txHash: txHash, @@ -99,7 +112,7 @@ function pair(req, res) { var token = req.body.token; var name = req.body.name; - _lamassuConfig.pair( + config.pair( token, getFingerprint(req), name, @@ -113,23 +126,20 @@ function pair(req, res) { ); } -function init(config) { - _lamassuConfig = config.lamassuConfig; - _trader = config.trader; - _mock = config.mock; +function init(localConfig) { + config = localConfig.lamassuConfig; + plugins = localConfig.plugins; + mock = localConfig.mock; - var authMiddleware = config.authMiddleware; - var app = config.app; - _lamassuConfig.readExchangesConfig(function (err, res) { - var idVerifyConfig = res.exchanges.plugins.settings.identitymind; - _idVerifier = require('lamassu-identitymind').factory(idVerifyConfig); - }); + var authMiddleware = localConfig.authMiddleware; + var app = localConfig.app; app.get('/poll', authMiddleware, poll); app.post('/send', authMiddleware, send); app.post('/trade', authMiddleware, trade); app.post('/event', authMiddleware, deviceEvent); - app.post('/verify_id', authMiddleware, idVerify); + app.post('/verify_user', authMiddleware, verifyUser); + app.post('/verify_transaction', authMiddleware, verifyTransaction); app.post('/pair', pair); return app; diff --git a/lib/trader.js b/lib/trader.js deleted file mode 100644 index 0bed4a6e..00000000 --- a/lib/trader.js +++ /dev/null @@ -1,367 +0,0 @@ -'use strict'; - -var async = require('async'); -var logger = require('./logger'); - -var SATOSHI_FACTOR = 1e8; - -// TODO: Define this somewhere more global -var SESSION_TIMEOUT = 60 * 60 * 1000; // an hour - - -function findExchange(name) { - try { - return require('lamassu-' + name); - - } catch(_) { - throw new Error(name + ' module is not installed. Try running `npm install --save lamassu-' + name + '` first'); - } -}; - -function findTicker (name) { - var exchange = findExchange(name); - return exchange.ticker || exchange; -}; - -function findTrader (name) { - var exchange = findExchange(name); - return exchange.trader || exchange; -}; - -function findWallet (name) { - var exchange = findExchange(name); - return exchange.wallet || exchange; -}; - - -var Trader = module.exports = function (db) { - if (!db) { - throw new Error('`db` is required'); - } - - this.db = db; - this.rates = {}; - this._tradeQueue = []; - this._sessionInfo = {}; - this.rateInfo = null; -}; - -Trader.prototype._consolidateTrades = function () { - var queue = this._tradeQueue; - - // NOTE: value in satoshis stays the same no matter the currency - var consolidatedTrade = { - currency: this.config.exchanges.settings.currency, - satoshis: queue.reduce(function (prev, current) { - return prev + current.satoshis; - }, 0) - }; - - return consolidatedTrade; -}; - -Trader.prototype._purchase = function (trade, cb) { - var self = this; - var tradeCurrency = this.tradeExchange.currency(); - var rate = this.rate(tradeCurrency).rate; - this.tradeExchange.purchase(trade.satoshis, rate, function (err) { - if (err) return cb(err); - self.pollBalance(); - cb(); - }); -}; - -Trader.prototype.configure = function (config) { - if (config.exchanges.settings.lowBalanceMargin < 1) { - throw new Error('`settings.lowBalanceMargin` has to be >= 1'); - } - - var plugins = config.exchanges.plugins - - // source of current BTC price (init and configure) - var tickerName = plugins.current.ticker; - var tickerConfig = plugins.settings[tickerName] || {}; - tickerConfig.currency = config.exchanges.settings.currency; - this.tickerExchange = findTicker(tickerName).factory(tickerConfig); - - // Exchange used for trading (init and configure) - var traderName = plugins.current.trade; - if (traderName) { - var tradeConfig = plugins.settings[traderName]; - this.tradeExchange = findTrader(traderName).factory(tradeConfig); - } - - // Wallet (init and configure) - var walletName = plugins.current.transfer; - var walletConfig = plugins.settings[walletName]; - this.transferExchange = findWallet(walletName).factory(walletConfig); - - this.config = config; - - this.pollBalance(); - this.pollRate(); -}; - -// IMPORTANT: This function returns the estimated minimum available balance -// in fiat as of the start of the current user session on the device. User -// session starts when a user presses then Start button and ends when we -// send the bitcoins. -Trader.prototype.fiatBalance = function (deviceFingerprint) { - var rawRate = this.rate(this.config.exchanges.settings.currency).rate; - var balance = this.balance; - var commission = this.config.exchanges.settings.commission; - - if (!rawRate || !balance) { - return null; - } - - // The rate is actually our commission times real rate. - var rate = commission * rawRate; - - // `lowBalanceMargin` is our safety net. It's a number > 1, and we divide - // all our balances by it to provide a safety margin. - var lowBalanceMargin = this.config.exchanges.settings.lowBalanceMargin; - - // `balance.transferBalance` is the balance of our transfer account (the one - // we use to send Bitcoins to clients) in satoshis. - var transferBalance = balance.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; - - // If this server is also configured to trade received fiat for Bitcoins, - // we also need to calculate if we have enough funds on our trade exchange. - if (balance.tradeBalance === null) return fiatTransferBalance; - var tradeBalance = balance.tradeBalance; - - // We're reporting balance as of the start of the user session. - var sessionInfo = this._sessionInfo[deviceFingerprint]; - var sessionBalance = sessionInfo ? sessionInfo.tradeBalance : tradeBalance; - var fiatTradeBalance = sessionBalance / lowBalanceMargin; - - // And we return the smallest number. - return Math.min(fiatTransferBalance, fiatTradeBalance); -}; - -Trader.prototype._clearSession = function (deviceFingerprint) { - var sessionInfo = this._sessionInfo[deviceFingerprint]; - if (sessionInfo) { - clearTimeout(sessionInfo.reaper); - delete this._sessionInfo[deviceFingerprint]; - } -}; - -Trader.prototype.sendBitcoins = function (deviceFingerprint, tx, cb) { - var self = this; - - self.db.summonTransaction(deviceFingerprint, tx, function (err, txRec) { - if (err) return cb(err); - - if (!txRec) { - self._clearSession(deviceFingerprint); - return self.transferExchange.sendBitcoins( - tx.toAddress, - tx.satoshis, - self.config.exchanges.settings.transactionFee, - function(err, txHash) { - if (err) { - var status = err.name === 'InsufficientFunds' ? - 'insufficientFunds' : - 'failed'; - self.db.reportTransactionError(tx, err.message, status); - return cb(err); - } - - self.db.completeTransaction(tx, txHash); - self.pollBalance(); - cb(null, txHash); - } - ); - } - - // Out of bitcoins: special case - var txErr = null; - if (txRec.err) { - txErr = new Error(txRec.err); - if (txRec.status === 'insufficientFunds') txErr.name = 'InsufficientFunds'; - } - - // transaction exists, but txHash might be null, - // in which case ATM should continue polling - self.pollBalance(); - cb(txErr, txRec.txHash); - }); -}; - -Trader.prototype.deviceEvent = function deviceEvent(rec, deviceFingerprint) { - this.db.recordDeviceEvent(deviceFingerprint, rec, function (err) { - if (err) logger.error(err); - }); -}; - -Trader.prototype.trade = function (rec, deviceFingerprint) { - this.db.recordBill(deviceFingerprint, rec, function (err) { - if (err) logger.error(err); - }); - - // This is where we record starting trade balance at the beginning - // of the user session - var sessionInfo = this._sessionInfo[deviceFingerprint]; - var self = this; - if (!sessionInfo) { - this._sessionInfo[deviceFingerprint] = { - tradeBalance: this.balance.tradeBalance, - timestamp: Date.now(), - reaper: setTimeout(function () { - delete self._sessionInfo[deviceFingerprint]; - }, SESSION_TIMEOUT) - }; - } - this._tradeQueue.push({ - satoshis: rec.satoshis, - currency: rec.currency - }); -}; - -Trader.prototype.executeTrades = function () { - if (!this.tradeExchange) return; - - logger.debug('checking for trades'); - - var trade = this._consolidateTrades(); - logger.debug('consolidated: ', JSON.stringify(trade)); - - if (trade.satoshis === 0) { - logger.debug('rejecting 0 trade'); - return; - } - - logger.debug('making a trade: %d', trade.satoshis / SATOSHI_FACTOR); - this._purchase(trade, function (err) { - if (err) logger.error(err); - }); -}; - -Trader.prototype.startPolling = function () { - this.executeTrades(); - - this.balanceInterval = setInterval(this.pollBalance.bind(this), 60 * 1000); - this.rateInterval = setInterval(this.pollRate.bind(this), 60 * 1000); - - // Always start trading, even if we don't have a trade exchange configured, - // since configuration can always change in `Trader#configure`. - // `Trader#executeTrades` returns early if we don't have a trade exchange - // configured at the moment. - this.tradeInterval = setInterval( - this.executeTrades.bind(this), - this.config.exchanges.settings.tradeInterval - ); -}; - -Trader.prototype.stopPolling = function () { - clearInterval(this.balanceInterval); - clearInterval(this.rateInterval); -}; - -// Trade exchange could be in a different currency than Bitcoin Machine. -// For instance, trade exchange could be Bitstamp, denominated in USD, -// while Bitcoin Machine is set to ILS. -// -// We need this function to convert the trade exchange balance into the -// Bitcoin Machine denomination, in the example case: ILS. -// -// The best way to do that with available data is to take the ratio between -// the exchange rates for the Bitcoin Machine and the trade exchange. -Trader.prototype._tradeForexMultiplier = function _tradeForexMultiplier() { - var deviceCurrency = this.config.exchanges.settings.currency; - var tradeCurrency = this.tradeExchange.currency(); - if (deviceCurrency === tradeCurrency) - return 1; - - var deviceRate = this._deviceRate(); - var tradeRate = this._tradeRate(); - return deviceRate && tradeRate ? - deviceRate / tradeRate : - null; -}; - -Trader.prototype._tradeBalanceFunc = function _tradeBalanceFunc(callback) { - if (!this.tradeExchange) return callback(null, null); - var forexMultiplier = this._tradeForexMultiplier(); - if (!forexMultiplier) return callback(new Error('Can\'t compute balance, no tickers yet.')); - this.tradeExchange.balance(function (err, localBalance) { - if (err) return callback(err); - callback(null, localBalance * forexMultiplier); - }); -}; - -Trader.prototype.pollBalance = function (callback) { - var self = this; - - logger.debug('collecting balance'); - - var transferBalanceFunc = this.transferExchange.balance.bind(this.transferExchange); - var tradeBalanceFunc = this._tradeBalanceFunc.bind(this); - - async.parallel({ - transferBalance: transferBalanceFunc, - tradeBalance: tradeBalanceFunc - }, function (err, balance) { - if (err) { - return callback && callback(err); - } - - balance.timestamp = Date.now(); - logger.debug('Balance update:', balance); - self.balance = balance; - - return callback && callback(); - }); -}; - -Trader.prototype.pollRate = function (callback) { - var self = this; - - logger.debug('polling for rates...'); - var deviceCurrency = this.config.exchanges.settings.currency; - var currencies = [deviceCurrency]; - if (this.tradeExchange) { - var tradeCurrency = this.tradeExchange.currency(); - if (tradeCurrency !== deviceCurrency) currencies.push(tradeCurrency); - } - - self.tickerExchange.ticker(currencies, function(err, resRates) { - if (err) { - logger.error(err); - return callback && callback(err); - } - - logger.debug('got rates: %j', resRates); - self.rateInfo = {rates: resRates, timestamp: new Date()}; - if (callback) callback(); - }); -}; - -Trader.prototype._deviceRate = function _deviceRate() { - if (!this.rateInfo) return null; - return this.rateInfo.rates[this.config.exchanges.settings.currency].rate; -}; - -Trader.prototype._tradeRate = function _tradeRate() { - if (!this.tradeExchange || !this.rateInfo) return null; - return this.rateInfo.rates[this.tradeExchange.currency()].rate; -}; - -// This is the rate in local currency to quote to the user -Trader.prototype.rate = function () { - if (!this.rateInfo) return null; - return { - rate: this._deviceRate(), - timestamp: this.rateInfo.timestamp - }; -}; diff --git a/package.json b/package.json index ae0790ce..16e2a7df 100644 --- a/package.json +++ b/package.json @@ -9,35 +9,34 @@ "node": "0.10.x" }, "dependencies": { - "express": "~3.4.7", "async": "~0.2.9", - "pg": "~2.11.1", - "lamassu-config": "~0.4.0", - "lamassu-bitpay": "~0.3.0", - "lamassu-bitstamp": "~0.2.0", - "lamassu-blockchain": "~0.1.0", "bunyan": "~0.22.3", + "express": "~3.4.7", + "lamassu-bitcoinaverage": "~1.0.0", + "lamassu-bitcoind": "~1.0.0", + "lamassu-bitpay": "~1.0.0", + "lamassu-bitstamp": "~1.0.0", + "lamassu-blockchain": "~1.0.0", + "lamassu-coindesk": "~1.0.0", + "lamassu-config": "~0.4.0", + "lamassu-identitymind": "^1.0.1", "minimist": "0.0.8", - "lamassu-bitcoind": "~0.1.0", - "lamassu-coindesk": "~0.2.0", - "lamassu-bitcoinaverage": "~0.2.0" + "pg": "~2.11.1" }, "repository": { "type": "git", "url": "https://github.com/lamassu/lamassu-server.git" }, - "devDependencies": { - "chai": "~1.8.1", - "matchdep": "~0.3.0", - "mocha": "~1.13.0", - "hock": "git+https://github.com/mmalecki/hock.git#no-http-server", - "jsonquest": "^0.2.2", - "node-uuid": "^1.4.1" - }, "bin": { "lamassu-server": "./bin/lamassu-server" }, "scripts": { "test": "mocha --recursive test" + }, + "devDependencies": { + "chai": "^1.9.1", + "lodash": "^2.4.1", + "mocha": "^1.21.4", + "mockery": "^1.4.0" } } diff --git a/test/helpers/create-https-server.js b/test/helpers/create-https-server.js index a7c9cf9f..d003ebcb 100644 --- a/test/helpers/create-https-server.js +++ b/test/helpers/create-https-server.js @@ -1,14 +1,14 @@ -var fs = require('fs'); -var path = require('path'); -var https = require('https'); -var fixtures = path.join(__dirname, '..', 'fixtures'); +// var fs = require('fs'); +// var path = require('path'); +// var https = require('https'); +// var fixtures = path.join(__dirname, '..', 'fixtures'); -module.exports = function(handler, callback) { - var server = https.createServer({ - key: fs.readFileSync(path.join(fixtures, 'privatekey.pem')), - cert: fs.readFileSync(path.join(fixtures, 'certificate.pem')) - }, handler); - server.listen(0, function() { - callback(null, server); - }); -}; +// module.exports = function(handler, callback) { +// var server = https.createServer({ +// key: fs.readFileSync(path.join(fixtures, 'privatekey.pem')), +// cert: fs.readFileSync(path.join(fixtures, 'certificate.pem')) +// }, handler); +// server.listen(0, function() { +// callback(null, server); +// }); +// }; diff --git a/test/index.js b/test/index.js new file mode 100644 index 00000000..840e92b3 --- /dev/null +++ b/test/index.js @@ -0,0 +1,136 @@ +'use strict' + +var _ = require('lodash'); +var should = require('chai').should(); +var mockery = require('mockery'); + + +var config = require('./mocks/config'); +var CONFIG = _.cloneDeep(config); +function requireFreshConfig() { + return _.cloneDeep(CONFIG); +}; + + +var currents = config.exchanges.plugins.current; + +var walletMock = require('./mocks/wallet'); +var tickerMock = require('./mocks/ticker'); +var traderMock = require('./mocks/trader'); +var verifierMock = require('./mocks/verifier'); + +mockery.registerMock('lamassu-mockWallet', walletMock); +mockery.registerMock('lamassu-mockTicker', tickerMock); +mockery.registerMock('lamassu-mockTrader', traderMock); +mockery.registerMock('lamassu-mockVerifier', verifierMock); + + +describe('Plugins', function() { + var plugins = null; + + before(function() { + mockery.enable({ + useCleanCache: true, + warnOnReplace: false, + warnOnUnregistered: false + }); + + plugins = require('../lib/plugins'); + }); + + afterEach(function() { + config = requireFreshConfig(); + }); + + it('should properly load', function() { + should.exist(plugins); + }); + + it('should throw when db is not provided', function() { + plugins.init.should.throw(/db.*required/); + }); + + it('should throw when invalid balance margin', function() { + config.exchanges.settings.lowBalanceMargin = .99; + + function configurer() { + plugins.configure(config); + }; + configurer.should.throw(/lowBalanceMargin/); + }); + + it('should throw when module is not installed', function() { + config.exchanges.plugins.current.ticker = 'inexistent-plugin'; + + function configurer() { + plugins.configure(config); + }; + configurer.should.throw(/module.*not installed/); + }); + + it('should throw when used plugin has no SUPPORTED_MODULES', function() { + var tmp = tickerMock.SUPPORTED_MODULES; + delete tickerMock.SUPPORTED_MODULES; + + function configurer() { + plugins.configure(config); + }; + configurer.should.throw(/required.*SUPPORTED_MODULES/); + + tickerMock.SUPPORTED_MODULES = tmp; + }); + + it('should throw when used plugin has required method missing', function() { + var tmp = tickerMock.ticker; + delete tickerMock.ticker; + + function configurer() { + plugins.configure(config); + }; + configurer.should.throw(/fails.*implement.*method/); + + tickerMock.ticker = tmp; + }); + + describe('should configure all enabled plugins', function() { + var confList = {}; + + before(function() { + function configTest(name) { + return function config(config) { + should.exist(config); + config.should.be.an.Object; + confList[name] = config; + }; + }; + + walletMock.config = configTest('wallet'); + tickerMock.config = configTest('ticker'); + traderMock.config = configTest('trader'); + verifierMock.config = configTest('verifier'); + + plugins.configure(config); + }); + + ['wallet', 'ticker', 'trader', 'verifier'].forEach(function(name) { + it('should configure ' + name, function() { + confList.should.have.property(name); + should.exist(confList[name]); + confList[name].should.be.an.Object; + }); + }); + + }); + + this.timeout(9000); + + describe('Ticker', function() { + it('should have .ticker() called at least once', function() { + tickerMock.tickerCalls.should.be.at.least(1); + }); + + }); + + +}); + diff --git a/test/mocks/config.json b/test/mocks/config.json new file mode 100644 index 00000000..a2ab8664 --- /dev/null +++ b/test/mocks/config.json @@ -0,0 +1,75 @@ +{ + "exchanges": { + "settings": { + "compliance": { + "maximum": { + "limit": null + } + }, + "commission": 1, + "fastPoll": 5000, + "fastPollLimit": 10, + "tickerInterval": 5000, + "balanceInterval": 5000, + "tradeInterval": 5000, + "retryInterval": 5000, + "retries": 3, + "lowBalanceMargin": 1.05, + "transactionFee": 10000, + "tickerDelta": 0, + "minimumTradeFiat": 0, + "currency": "PLN", + "networkTimeout": 20000 + }, + "plugins": { + "current": { + "ticker": "mockTicker", + "trade": "mockTrader", + "wallet": "mockWallet", + "transfer": "mockWallet", + "idVerifier": "mockVerifier" + }, + "settings": { + "bitpay": { }, + "bitstamp": { + "currency": "USD", + "key": "", + "secret": "", + "clientId": "" + }, + "blockchain": { + "retryInterval": 10000, + "retryTimeout": 60000, + "guid": "", + "password": "", + "fromAddress": "" + } + } + } + }, + "brain": { + "qrTimeout": 60000, + "goodbyeTimeout": 2000, + "billTimeout": 60000, + "completedTimeout": 60000, + "networkTimeout": 20000, + "triggerRetry": 5000, + "idleTime": 600000, + "checkIdleTime": 60000, + "maxProcessSize": 104857600, + "freeMemRatio": 0.15, + "unit": { + "ssn": "xx-1234-45", + "owner": "Lamassu, Inc. \/ Trofa \/ Portugal" + }, + "locale": { + "currency": "PLN", + "localeInfo": { + "primaryLocale": "pl-PL", + "primaryLocales": [ + "pl-PL" + ] + } + } + } +} diff --git a/test/mocks/ticker.js b/test/mocks/ticker.js new file mode 100644 index 00000000..65addc3e --- /dev/null +++ b/test/mocks/ticker.js @@ -0,0 +1,23 @@ +'use strict'; + +module.exports = { + SUPPORTED_MODULES: [ 'ticker' ], + NAME: 'Mock Ticker', + + tickerCalls: 0, + + config: function() {}, + ticker: function(currency, callback) { + this.tickerCalls++; + + var out = {}; + out[currency] = { + currency: currency, + rates: { + ask: 1001.0, + bid: 999.0 + } + }; + callback(null, out); + } +}; diff --git a/test/mocks/trader.js b/test/mocks/trader.js new file mode 100644 index 00000000..86359c7d --- /dev/null +++ b/test/mocks/trader.js @@ -0,0 +1,11 @@ +'use strict'; + +module.exports = { + SUPPORTED_MODULES: ['trader'], + NAME: 'Mock Trader', + + config: function() {}, + balance: function() {}, + purchase: function() {}, + sell: function() {} +}; diff --git a/test/mocks/verifier.js b/test/mocks/verifier.js new file mode 100644 index 00000000..9e3ef7b5 --- /dev/null +++ b/test/mocks/verifier.js @@ -0,0 +1,10 @@ +'use strict'; + +module.exports = { + SUPPORTED_MODULES: ['idVerifier'], + NAME: 'Mock Verifier', + + config: function() {}, + verifyUser: function() {}, + verifyTransaction: function() {} +}; diff --git a/test/mocks/wallet.js b/test/mocks/wallet.js new file mode 100644 index 00000000..7fc3ae59 --- /dev/null +++ b/test/mocks/wallet.js @@ -0,0 +1,10 @@ +'use strict'; + +module.exports = { + SUPPORTED_MODULES: ['wallet'], + NAME: 'Mock Wallet', + + config: function() {}, + balance: function() {}, + sendBitcoins: function() {} +}; diff --git a/test/unit/traderApiTest.js b/test/unit/traderApiTest.js index ae8ed901..215c61e6 100644 --- a/test/unit/traderApiTest.js +++ b/test/unit/traderApiTest.js @@ -1,54 +1,54 @@ -'use strict'; +// 'use strict'; -var assert = require('chai').assert; -var Trader = require('../../lib/trader.js'); -var PostgresqlInterface = require('../../lib/postgresql_interface.js'); +// var assert = require('chai').assert; +// var Trader = require('../../lib/trader.js'); +// var PostgresqlInterface = require('../../lib/postgresql_interface.js'); -var db = 'psql://lamassu:lamassu@localhost/lamassu-test'; -var psqlInterface = new PostgresqlInterface(db); +// var db = 'psql://lamassu:lamassu@localhost/lamassu-test'; +// var psqlInterface = new PostgresqlInterface(db); -describe('trader/api', function () { - it('should throw when trying to create a trader with no DB', function () { - assert.throws(function () { - new Trader(); - }); - }); +// describe('trader/api', function () { +// it('should throw when trying to create a trader with no DB', function () { +// assert.throws(function () { +// new Trader(); +// }); +// }); - it('should throw when trying to configure a trader with `lowBalanceMargin` < 1', function () { - var trader = new Trader(psqlInterface); - assert.throws(function () { - trader.configure({ - exchanges: { - settings: { - lowBalanceMargin: 0.8 - } - } - }); - }); - }); +// it('should throw when trying to configure a trader with `lowBalanceMargin` < 1', function () { +// var trader = new Trader(psqlInterface); +// assert.throws(function () { +// trader.configure({ +// exchanges: { +// settings: { +// lowBalanceMargin: 0.8 +// } +// } +// }); +// }); +// }); - it('should find and instantiate ticker and trade exchanges', function () { - var trader = new Trader(psqlInterface); - trader.configure({ - exchanges: { - plugins: { - current: { - ticker: 'bitpay', - transfer: 'blockchain' - }, - settings: { - bitpay: {}, - blockchain: {} - } - }, - settings: { - currency: 'USD', - lowBalanceMargin: 2 - } - } - }); +// it('should find and instantiate ticker and trade exchanges', function () { +// var trader = new Trader(psqlInterface); +// trader.configure({ +// exchanges: { +// plugins: { +// current: { +// ticker: 'bitpay', +// transfer: 'blockchain' +// }, +// settings: { +// bitpay: {}, +// blockchain: {} +// } +// }, +// settings: { +// currency: 'USD', +// lowBalanceMargin: 2 +// } +// } +// }); - assert.ok(trader.tickerExchange); - assert.ok(trader.transferExchange); - }); -}); +// assert.ok(trader.tickerExchange); +// assert.ok(trader.transferExchange); +// }); +// }); diff --git a/test/unit/traderFiatBalanceTest.js b/test/unit/traderFiatBalanceTest.js index 923e3671..a4003c88 100644 --- a/test/unit/traderFiatBalanceTest.js +++ b/test/unit/traderFiatBalanceTest.js @@ -1,107 +1,107 @@ -/*global describe, it */ -'use strict'; +// /*global describe, it */ +// 'use strict'; -var assert = require('chai').assert; -var Trader = require('../../lib/trader.js'); +// var assert = require('chai').assert; +// var Trader = require('../../lib/trader.js'); -var db = 'psql://lamassu:lamassu@localhost/lamassu-test'; +// var db = 'psql://lamassu:lamassu@localhost/lamassu-test'; -var RATE = 101; -var CURRENCY = 'USD'; -var SATOSHI_FACTOR = 1e8; -var LOW_BALANCE_MARGIN = 1.2; -var COMMISSION = 1.1; -var FINGERPRINT = '00:7A:5A:B3:02:F1:44:46:E2:EA:24:D3:A8:29:DE:22:BA:1B:F9:50'; +// var RATE = 101; +// var CURRENCY = 'USD'; +// var SATOSHI_FACTOR = 1e8; +// var LOW_BALANCE_MARGIN = 1.2; +// var COMMISSION = 1.1; +// var FINGERPRINT = '00:7A:5A:B3:02:F1:44:46:E2:EA:24:D3:A8:29:DE:22:BA:1B:F9:50'; -var settings = { - currency: CURRENCY, - lowBalanceMargin: LOW_BALANCE_MARGIN, - commission: COMMISSION -}; +// var settings = { +// currency: CURRENCY, +// lowBalanceMargin: LOW_BALANCE_MARGIN, +// commission: COMMISSION +// }; -describe('trader/fiatBalance', function() { - it('should calculate balance correctly with transfer exchange only', function() { - var trader = new Trader(db); - trader.configure({ - exchanges: { - plugins: { - current: { - transfer: 'blockchain', - ticker: 'bitpay' - }, - settings: { blockchain: {}, bitpay: {} } - }, - settings: settings - } - }); +// describe('trader/fiatBalance', function() { +// it('should calculate balance correctly with transfer exchange only', function() { +// var trader = new Trader(db); +// trader.configure({ +// exchanges: { +// plugins: { +// current: { +// transfer: 'blockchain', +// ticker: 'bitpay' +// }, +// settings: { blockchain: {}, bitpay: {} } +// }, +// settings: settings +// } +// }); - // We have 3 bitcoins, want to trade 1 bitcoin for 100 fiat - trader.balance = { - transferBalance: 3 * SATOSHI_FACTOR, - tradeBalance: null - }; - trader.rates[CURRENCY] = { rate: RATE }; - trader.rateInfo = {rates: {USD: {rate: RATE}}}; - var fiatBalance = trader.fiatBalance(FINGERPRINT); - assert.equal(fiatBalance, (3 * RATE * COMMISSION / LOW_BALANCE_MARGIN)); - }); +// // We have 3 bitcoins, want to trade 1 bitcoin for 100 fiat +// trader.balance = { +// transferBalance: 3 * SATOSHI_FACTOR, +// tradeBalance: null +// }; +// trader.rates[CURRENCY] = { rate: RATE }; +// trader.rateInfo = {rates: {USD: {rate: RATE}}}; +// var fiatBalance = trader.fiatBalance(FINGERPRINT); +// assert.equal(fiatBalance, (3 * RATE * COMMISSION / LOW_BALANCE_MARGIN)); +// }); - it('should calculate balance correctly with transfer and trade exchange', function() { - var trader = new Trader(db); - trader.configure({ - exchanges: { - plugins: { - current: { - transfer: 'blockchain', - ticker: 'bitpay', - trade: 'bitstamp' - }, - settings: { blockchain: {}, bitpay: {}, bitstamp: {} } - }, - settings: settings - } - }); +// it('should calculate balance correctly with transfer and trade exchange', function() { +// var trader = new Trader(db); +// trader.configure({ +// exchanges: { +// plugins: { +// current: { +// transfer: 'blockchain', +// ticker: 'bitpay', +// trade: 'bitstamp' +// }, +// settings: { blockchain: {}, bitpay: {}, bitstamp: {} } +// }, +// settings: settings +// } +// }); - // We have 3 bitcoins in transfer, worth 3 * RATE * COMMISSION = 333.3 - // We have 150 USD in trade - trader.balance = { - transferBalance: 3 * SATOSHI_FACTOR, - tradeBalance: 150 - }; - trader.rates[CURRENCY] = { rate: RATE }; - trader.rateInfo = {rates: {USD: {rate: RATE}}}; - var fiatBalance = trader.fiatBalance(FINGERPRINT); - assert.equal(fiatBalance, 150 / LOW_BALANCE_MARGIN); - }); +// // We have 3 bitcoins in transfer, worth 3 * RATE * COMMISSION = 333.3 +// // We have 150 USD in trade +// trader.balance = { +// transferBalance: 3 * SATOSHI_FACTOR, +// tradeBalance: 150 +// }; +// trader.rates[CURRENCY] = { rate: RATE }; +// trader.rateInfo = {rates: {USD: {rate: RATE}}}; +// var fiatBalance = trader.fiatBalance(FINGERPRINT); +// assert.equal(fiatBalance, 150 / LOW_BALANCE_MARGIN); +// }); - it('should calculate balance correctly with transfer and ' + - 'trade exchange with different currencies', function() { - var trader = new Trader(db); - trader.configure({ - exchanges: { - plugins: { - current: { - transfer: 'blockchain', - ticker: 'bitpay', - trade: 'bitstamp' - }, - settings: { blockchain: {}, bitpay: {}, bitstamp: {} } - }, - settings: settings - } - }); +// it('should calculate balance correctly with transfer and ' + +// 'trade exchange with different currencies', function() { +// var trader = new Trader(db); +// trader.configure({ +// exchanges: { +// plugins: { +// current: { +// transfer: 'blockchain', +// ticker: 'bitpay', +// trade: 'bitstamp' +// }, +// settings: { blockchain: {}, bitpay: {}, bitstamp: {} } +// }, +// settings: settings +// } +// }); - // We have 6 bitcoins in transfer, worth 6 * RATE * COMMISSION = 666.6 - // We have 150 USD in trade, 1 USD = 4 ILS => 600 ILS in trade - trader.balance = { - transferBalance: 6 * SATOSHI_FACTOR, - tradeBalance: 600 - }; - trader.rates = {USD: {rate: RATE}, ILS: {rate: RATE * 4} }; - trader.rateInfo = {rates: {USD: {rate: RATE}}}; - var fiatBalance = trader.fiatBalance(FINGERPRINT); - assert.equal(fiatBalance, 600 / LOW_BALANCE_MARGIN); - }); +// // We have 6 bitcoins in transfer, worth 6 * RATE * COMMISSION = 666.6 +// // We have 150 USD in trade, 1 USD = 4 ILS => 600 ILS in trade +// trader.balance = { +// transferBalance: 6 * SATOSHI_FACTOR, +// tradeBalance: 600 +// }; +// trader.rates = {USD: {rate: RATE}, ILS: {rate: RATE * 4} }; +// trader.rateInfo = {rates: {USD: {rate: RATE}}}; +// var fiatBalance = trader.fiatBalance(FINGERPRINT); +// assert.equal(fiatBalance, 600 / LOW_BALANCE_MARGIN); +// }); -}); +// }); diff --git a/test/unit/traderSendTest.js b/test/unit/traderSendTest.js index 3bf65091..d3faea58 100644 --- a/test/unit/traderSendTest.js +++ b/test/unit/traderSendTest.js @@ -1,77 +1,77 @@ -'use strict'; +// 'use strict'; -var assert = require('chai').assert; -var hock = require('hock'); -var uuid = require('node-uuid').v4; -var Trader = require('../../lib/trader.js'); -var PostgresqlInterface = require('../../lib/postgresql_interface.js'); +// var assert = require('chai').assert; +// var hock = require('hock'); +// var uuid = require('node-uuid').v4; +// var Trader = require('../../lib/trader.js'); +// var PostgresqlInterface = require('../../lib/postgresql_interface.js'); -var db = 'psql://lamassu:lamassu@localhost/lamassu-test'; -var psqlInterface = new PostgresqlInterface(db); +// var db = 'psql://lamassu:lamassu@localhost/lamassu-test'; +// var psqlInterface = new PostgresqlInterface(db); -var TRANSACTION_FEE = 1; -var FINGERPRINT = 'CB:3D:78:49:03:39:BA:47:0A:33:29:3E:31:25:F7:C6:4F:74:71:D7'; -var TXID = '216dabdb692670bae940deb71e59486038a575f637903d3c9af601ddd48057fc'; -var ADDRESS = '1LhkU2R8nJaU8Zj6jB8VjWrMpvVKGqCZ64'; -var SATOSHIS = 1337; -var CURRENCY = 'USD'; +// var TRANSACTION_FEE = 1; +// var FINGERPRINT = 'CB:3D:78:49:03:39:BA:47:0A:33:29:3E:31:25:F7:C6:4F:74:71:D7'; +// var TXID = '216dabdb692670bae940deb71e59486038a575f637903d3c9af601ddd48057fc'; +// var ADDRESS = '1LhkU2R8nJaU8Zj6jB8VjWrMpvVKGqCZ64'; +// var SATOSHIS = 1337; +// var CURRENCY = 'USD'; -var OUR_TXID = uuid(); +// var OUR_TXID = uuid(); -describe('trader/send', function () { - var trader = new Trader(psqlInterface); - trader.config = { - exchanges: { - settings: { - transactionFee: TRANSACTION_FEE - } - } - }; +// describe('trader/send', function () { +// var trader = new Trader(psqlInterface); +// trader.config = { +// exchanges: { +// settings: { +// transactionFee: TRANSACTION_FEE +// } +// } +// }; - trader.pollRate = function () {}; +// trader.pollRate = function () {}; - it('should call `sendBitcoins` on the transfer exchange', function (done) { - trader.transferExchange = { - sendBitcoins: function (address, satoshis, transactionFee, callback) { - assert.equal(ADDRESS, address); - assert.equal(SATOSHIS, satoshis); - assert.equal(transactionFee, TRANSACTION_FEE); - callback(null, TXID); - }, - balance: function () {} - }; +// it('should call `sendBitcoins` on the transfer exchange', function (done) { +// trader.transferExchange = { +// sendBitcoins: function (address, satoshis, transactionFee, callback) { +// assert.equal(ADDRESS, address); +// assert.equal(SATOSHIS, satoshis); +// assert.equal(transactionFee, TRANSACTION_FEE); +// callback(null, TXID); +// }, +// balance: function () {} +// }; - trader.sendBitcoins(FINGERPRINT, { - fiat: 100, - txId: OUR_TXID, - currencyCode: CURRENCY, - toAddress: ADDRESS, - satoshis: SATOSHIS - }, function (err, txId) { - assert.notOk(err); - assert.equal(txId, TXID); - done(); - }); - }); +// trader.sendBitcoins(FINGERPRINT, { +// fiat: 100, +// txId: OUR_TXID, +// currencyCode: CURRENCY, +// toAddress: ADDRESS, +// satoshis: SATOSHIS +// }, function (err, txId) { +// assert.notOk(err); +// assert.equal(txId, TXID); +// done(); +// }); +// }); - it('should not call `sendBitcoins` on the transfer exchange with same send', function (done) { - trader.transferExchange = { - sendBitcoins: function () { - throw new Error('This should not have been called'); - }, - balance: function () {} - }; +// it('should not call `sendBitcoins` on the transfer exchange with same send', function (done) { +// trader.transferExchange = { +// sendBitcoins: function () { +// throw new Error('This should not have been called'); +// }, +// balance: function () {} +// }; - trader.sendBitcoins(FINGERPRINT, { - fiat: 100, - txId: OUR_TXID, - currencyCode: CURRENCY, - toAddress: ADDRESS, - satoshis: SATOSHIS - }, function (err, txId) { - assert.notOk(err); - assert.equal(txId, TXID); - done(); - }); - }); -}); +// trader.sendBitcoins(FINGERPRINT, { +// fiat: 100, +// txId: OUR_TXID, +// currencyCode: CURRENCY, +// toAddress: ADDRESS, +// satoshis: SATOSHIS +// }, function (err, txId) { +// assert.notOk(err); +// assert.equal(txId, TXID); +// done(); +// }); +// }); +// }); diff --git a/test/unit/traderTickerTest.js b/test/unit/traderTickerTest.js index 5256a5bf..026e797b 100644 --- a/test/unit/traderTickerTest.js +++ b/test/unit/traderTickerTest.js @@ -1,52 +1,52 @@ -/*global describe, it */ -'use strict'; +// /*global describe, it */ +// 'use strict'; -var assert = require('chai').assert; -var Trader = require('../../lib/trader.js'); -var PostgresqlInterface = require('../../lib/postgresql_interface.js'); +// var assert = require('chai').assert; +// var Trader = require('../../lib/trader.js'); +// var PostgresqlInterface = require('../../lib/postgresql_interface.js'); -var db = 'psql://lamassu:lamassu@localhost/lamassu-test'; -var psqlInterface = new PostgresqlInterface(db); +// var db = 'psql://lamassu:lamassu@localhost/lamassu-test'; +// var psqlInterface = new PostgresqlInterface(db); -var CURRENCY = 'USD'; +// var CURRENCY = 'USD'; -describe('trader/send', function () { - var trader = new Trader(psqlInterface); - trader.config = { - exchanges: { - settings: { currency: CURRENCY } - } - }; +// describe('trader/send', function () { +// var trader = new Trader(psqlInterface); +// trader.config = { +// exchanges: { +// settings: { currency: CURRENCY } +// } +// }; - it('should call `balance` on the transfer exchange', function (done) { - trader.transferExchange = { - balance: function (callback) { - callback(null, 100); - } - }; +// it('should call `balance` on the transfer exchange', function (done) { +// trader.transferExchange = { +// balance: function (callback) { +// callback(null, 100); +// } +// }; - trader.pollBalance(function (err) { - assert.notOk(err); - assert.equal(trader.balance.transferBalance, 100); - assert.ok(trader.balance.timestamp); - done(); - }); - }); +// trader.pollBalance(function (err) { +// assert.notOk(err); +// assert.equal(trader.balance.transferBalance, 100); +// assert.ok(trader.balance.timestamp); +// done(); +// }); +// }); - it('should call `ticker` on the ticker exchange', function (done) { - trader.tickerExchange = { - ticker: function (currencies, callback) { - assert.equal(currencies[0], CURRENCY); - callback(null, {USD: {rate: 100}}); - } - }; +// it('should call `ticker` on the ticker exchange', function (done) { +// trader.tickerExchange = { +// ticker: function (currencies, callback) { +// assert.equal(currencies[0], CURRENCY); +// callback(null, {USD: {rate: 100}}); +// } +// }; - trader.pollRate(function (err) { - assert.notOk(err); - var rate = trader.rate(CURRENCY); - assert.equal(rate.rate, 100); - assert.ok(rate.timestamp); - done(); - }); - }); -}); +// trader.pollRate(function (err) { +// assert.notOk(err); +// var rate = trader.rate(CURRENCY); +// assert.equal(rate.rate, 100); +// assert.ok(rate.timestamp); +// done(); +// }); +// }); +// });