diff --git a/lib/cash-in/cash-in-low.js b/lib/cash-in/cash-in-low.js index a3439df3..42f8d3bd 100644 --- a/lib/cash-in/cash-in-low.js +++ b/lib/cash-in/cash-in-low.js @@ -8,7 +8,7 @@ const E = require('../error') const PENDING_INTERVAL_MS = 60 * T.minutes -const massageFields = ['direction', 'cryptoNetwork', 'bills', 'blacklisted', 'addressReuse', 'promoCodeApplied'] +const massageFields = ['direction', 'cryptoNetwork', 'bills', 'blacklisted', 'addressReuse', 'promoCodeApplied', 'failedWalletScore'] const massageUpdateFields = _.concat(massageFields, 'cryptoAtoms') const massage = _.flow(_.omit(massageFields), diff --git a/lib/cash-in/cash-in-tx.js b/lib/cash-in/cash-in-tx.js index 55962757..19363b04 100644 --- a/lib/cash-in/cash-in-tx.js +++ b/lib/cash-in/cash-in-tx.js @@ -15,6 +15,7 @@ const cashInLow = require('./cash-in-low') const PENDING_INTERVAL = '60 minutes' const MAX_PENDING = 10 +const WALLET_SCORE_THRESHOLD = 10 const TRANSACTION_STATES = ` case @@ -33,11 +34,14 @@ function post (machineTx, pi) { const updatedTx = r.tx let blacklisted = false let addressReuse = false + let failedWalletScore = false - return Promise.all([settingsLoader.loadLatest(), checkForBlacklisted(updatedTx), doesTxReuseAddress(updatedTx)]) - .then(([{ config }, blacklistItems, isReusedAddress]) => { + return Promise.all([settingsLoader.loadLatest(), checkForBlacklisted(updatedTx), doesTxReuseAddress(updatedTx), doesWalletScoreFail(updatedTx, pi)]) + .then(([{ config }, blacklistItems, isReusedAddress, walletScoreFailed]) => { const rejectAddressReuse = configManager.getCompliance(config).rejectAddressReuse + failedWalletScore = walletScoreFailed + if (_.some(it => it.address === updatedTx.toAddress)(blacklistItems)) { blacklisted = true notifier.notifyIfActive('compliance', 'blacklistNotify', r.tx, false) @@ -45,12 +49,13 @@ function post (machineTx, pi) { notifier.notifyIfActive('compliance', 'blacklistNotify', r.tx, true) addressReuse = true } - return postProcess(r, pi, blacklisted, addressReuse) + return postProcess(r, pi, blacklisted, addressReuse, failedWalletScore) }) .then(changes => cashInLow.update(db, updatedTx, changes)) .then(tx => _.set('bills', machineTx.bills, tx)) .then(tx => _.set('blacklisted', blacklisted, tx)) .then(tx => _.set('addressReuse', addressReuse, tx)) + .then(tx => _.set('failedWalletScore', failedWalletScore, tx)) }) } @@ -88,7 +93,7 @@ function checkForBlacklisted (tx) { return Promise.resolve(false) } -function postProcess (r, pi, isBlacklisted, addressReuse) { +function postProcess (r, pi, isBlacklisted, addressReuse, failedWalletScore) { if (addressReuse) { return Promise.resolve({ operatorCompleted: true, @@ -103,6 +108,13 @@ function postProcess (r, pi, isBlacklisted, addressReuse) { }) } + if (failedWalletScore) { + return Promise.resolve({ + operatorCompleted: true, + error: 'Failed wallet score' + }) + } + registerTrades(pi, r) if (!cashInLow.isClearToSend(r.dbTx, r.tx)) return Promise.resolve({}) @@ -147,6 +159,14 @@ function doesTxReuseAddress (tx) { return Promise.resolve(false) } +function doesWalletScoreFail (tx, pi) { + if (!tx.fiat || tx.fiat.isZero()) { + return pi.rateWallet(tx.toAddress) + .then(res => res >= WALLET_SCORE_THRESHOLD) + } + return Promise.resolve(false) +} + function monitorPending (settings) { const sql = `select * from cash_in_txs where created > now() - interval $1 diff --git a/lib/plugin-helper.js b/lib/plugin-helper.js index 1dc4a9ca..6af93c74 100644 --- a/lib/plugin-helper.js +++ b/lib/plugin-helper.js @@ -7,6 +7,7 @@ const pluginCodes = { TICKER: 'ticker', EXCHANGE: 'exchange', WALLET: 'wallet', + WALLET_SCORING: 'wallet-scoring', LAYER2: 'layer2', SMS: 'sms', EMAIL: 'email', diff --git a/lib/plugins.js b/lib/plugins.js index c2262476..a8dad677 100644 --- a/lib/plugins.js +++ b/lib/plugins.js @@ -13,6 +13,7 @@ const T = require('./time') const configManager = require('./new-config-manager') const ticker = require('./ticker') const wallet = require('./wallet') +const walletScoring = require('./wallet-scoring') const exchange = require('./exchange') const sms = require('./sms') const email = require('./email') @@ -776,6 +777,11 @@ function plugins (settings, deviceId) { .then(buildRates) } + function rateWallet (address) { + return walletScoring.rateWallet(settings, address) + .then(res => res.rating) + } + return { getRates, buildRates, @@ -803,7 +809,8 @@ function plugins (settings, deviceId) { getNotificationConfig, notifyOperator, fetchCurrentConfigVersion, - pruneMachinesHeartbeat + pruneMachinesHeartbeat, + rateWallet } } diff --git a/lib/plugins/wallet-scoring/mock-scoring/mock-scoring.js b/lib/plugins/wallet-scoring/mock-scoring/mock-scoring.js new file mode 100644 index 00000000..48b2240c --- /dev/null +++ b/lib/plugins/wallet-scoring/mock-scoring/mock-scoring.js @@ -0,0 +1,15 @@ +const NAME = 'FakeScoring' + +function rateWallet (account, address) { + return new Promise((resolve, _) => { + setTimeout(() => { + console.log('[WALLET-SCORING] DEBUG: Mock scoring rating wallet address %s', address) + return resolve({ address, rating: 5 }) + }, 100) + }) +} + +module.exports = { + NAME, + rateWallet +} diff --git a/lib/wallet-scoring.js b/lib/wallet-scoring.js new file mode 100644 index 00000000..2b0dbe8b --- /dev/null +++ b/lib/wallet-scoring.js @@ -0,0 +1,27 @@ +const ph = require('./plugin-helper') +const _ = require('lodash/fp') +const argv = require('minimist')(process.argv.slice(2)) + +function loadWalletScoring (settings) { + if (_.isNil(argv.mockScoring)) { + throw new Error('No wallet scoring API set!') + } + const pluginCode = argv.mockScoring ? 'mock-scoring' : '' + const plugin = ph.load(ph.WALLET_SCORING, pluginCode) + const account = settings.accounts[pluginCode] + + return { plugin, account } +} + +function rateWallet (settings, address) { + return Promise.resolve() + .then(() => { + const { plugin, account } = loadWalletScoring(settings) + + return plugin.rateWallet(account, address) + }) +} + +module.exports = { + rateWallet +}