diff --git a/lib/plugins.js b/lib/plugins.js index 603b4283..ebc349fd 100644 --- a/lib/plugins.js +++ b/lib/plugins.js @@ -16,8 +16,7 @@ var tickerPlugin = null; var traderPlugin = null; var walletPlugin = null; var idVerifierPlugin = null; - -var blockchainUtil = null; +var infoPlugin = null; var currentlyUsedPlugins = {}; @@ -53,8 +52,9 @@ function loadPlugin(name, config) { var moduleMethods = { ticker: [ 'ticker' ], trader: [ 'balance', 'purchase', 'sell' ], - wallet: [ 'balance', 'sendBitcoins' ], - idVerifier: [ 'verifyUser', 'verifyTransaction' ] + wallet: [ 'balance', 'sendBitcoins', 'newAddress' ], + idVerifier: [ 'verifyUser', 'verifyTransaction' ], + info: [ 'getLastTx', 'getTxStatus' ] }; var plugin = null; @@ -168,9 +168,10 @@ exports.configure = function configure(config) { 'idVerifier' ); - // NOTE: temp solution - if (blockchainUtil === null) - blockchainUtil = require('./blockchain_util'); + infoPlugin = loadOrConfigPlugin( + infoPlugin, + 'info' + ); }; exports.getCachedConfig = function getCachedConfig() { return cachedConfig; @@ -305,8 +306,9 @@ function _monitorAddress(address, cb) { var interval = 300; // TODO make config function checkAddress(_cb) { - blockchainUtil.addressReceived(address, confs, function(err, _received) { + infoPlugin.getLastTx(address, function(err, tx) { if (err) logger.error(err); + if (_received > 0) received = _received; setTimeout(_cb, interval); }); @@ -326,7 +328,7 @@ function _monitorAddress(address, cb) { async.doUntil(checkAddress, test, handler); } -function _waitDeposit(deviceFingerprint, tx) { +function _awaitDeposit(deviceFingerprint, tx) { _monitorAddress(tx.toAddress, function(err, received) { var status = 'fullDeposit'; @@ -346,24 +348,84 @@ function _waitDeposit(deviceFingerprint, tx) { }); } +function _monitorTx(deviceFingerprint, tx) { + infoPlugin.getTxStatus(tx.txHash, function(err, txStatus) { + if (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'); + } + }); +} + +function _monitorAddress(deviceFingerprint, tx) { + infoPlugin.getLastTx(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) + 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); + } + }); +} + + exports.cashOut = function cashOut(deviceFingerprint, tx, cb) { var tmpInfo = { label: 'TX ' + Date.now(), account: 'deposit' }; - walletPlugin.newAddress('deposit', function(err, address) { - if (err) return cb(new Error(err)); + walletPlugin.newAddress(tmpInfo, function(err, address) { + if (err) + return cb(new Error(err)); tx.toAddress = address; - // WARN: final db structure will determine if we can use this method + tx.tx_type = 'sell'; + db.insertTx(deviceFingerprint, tx, function(err) { - if (err) return cb(new Error(err)); + if (err) + return cb(new Error(err)); - _waitDeposit(deviceFingerprint, tx); + // start watching address for incoming txs + _awaitDeposit(deviceFingerprint, tx); + + // return address to the machine return cb(null, address); - // NOTE: logic here will depend on a way we want to handle those txs }); - }); }; diff --git a/lib/postgresql_interface.js b/lib/postgresql_interface.js index cd16e206..f9867060 100644 --- a/lib/postgresql_interface.js +++ b/lib/postgresql_interface.js @@ -156,6 +156,7 @@ exports.insertTx = function insertTx(deviceFingerprint, tx, cb) { var fields = [ 'id', 'status', + 'tx_type', 'device_fingerprint', 'to_address', 'satoshis', @@ -166,6 +167,7 @@ exports.insertTx = function insertTx(deviceFingerprint, tx, cb) { var values = [ tx.txId, tx.status || 'pending', + tx.tx_type || 'buy', deviceFingerprint, tx.toAddress, tx.satoshis, @@ -219,20 +221,19 @@ exports.changeTxStatus = function changeTxStatus(txId, newStatus, data, cb) { values.push(data.error); } - if (newStatus === 'completed') { - // set tx_hash (if available) - if (typeof data.hash !== 'undefined') { - query += ', tx_hash=$' + n++; - values.push(data.hash); - } - - // indicates if tx was finished by a `/send` call (and not timeout) - if (typeof data.is_completed !== 'undefined') { - query += ', is_completed=$' + n++; - values.push(data.is_completed); - } + // set tx_hash (if available) + if (typeof data.hash !== 'undefined') { + query += ', tx_hash=$' + n++; + values.push(data.hash); } + // indicates if tx was finished by a `/send` call (and not timeout) + if (typeof data.is_completed !== 'undefined') { + query += ', is_completed=$' + n++; + values.push(data.is_completed); + } + + query += ' WHERE id=$' + n++; values.push(txId); diff --git a/package.json b/package.json index 6d391987..92063595 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ "lamassu-bitpay": "~1.0.0", "lamassu-bitstamp": "~1.0.0", "lamassu-blockchain": "~1.0.0", + "lamassu-chain": "chester1000/lamassu-chain", "lamassu-coindesk": "~1.0.0", "lamassu-config": "~0.4.0", "lamassu-identitymind": "^1.0.1",