From 501da5f54a620cc16b63d02f8bc0f824bf33783e Mon Sep 17 00:00:00 2001
From: Rafael Taranto
Date: Tue, 30 Apr 2024 17:18:36 +0100
Subject: [PATCH] feat: scorechain address analysis
---
lib/cash-in/cash-in-tx.js | 47 +++---
lib/cash-out/cash-out-tx.js | 22 +--
lib/customers.js | 2 +-
lib/new-admin/config/accounts.js | 2 +-
lib/plugins.js | 21 +--
.../wallet-scoring/ciphertrace/ciphertrace.js | 150 ------------------
.../mock-scoring/mock-scoring.js | 33 +---
.../wallet-scoring/scorechain/scorechain.js | 74 +++++++++
lib/routes/txRoutes.js | 2 +-
lib/tx.js | 2 +-
lib/wallet-scoring.js | 34 +---
.../src/pages/Services/schemas/index.js | 4 +-
.../schemas/{ciphertrace.js => scorechain.js} | 21 ++-
.../src/pages/Transactions/DetailsCard.js | 16 +-
package-lock.json | 36 +++--
15 files changed, 158 insertions(+), 308 deletions(-)
delete mode 100644 lib/plugins/wallet-scoring/ciphertrace/ciphertrace.js
create mode 100644 lib/plugins/wallet-scoring/scorechain/scorechain.js
rename new-lamassu-admin/src/pages/Services/schemas/{ciphertrace.js => scorechain.js} (69%)
diff --git a/lib/cash-in/cash-in-tx.js b/lib/cash-in/cash-in-tx.js
index 2da9f8da..57ea8ccf 100644
--- a/lib/cash-in/cash-in-tx.js
+++ b/lib/cash-in/cash-in-tx.js
@@ -36,8 +36,15 @@ function post (machineTx, pi) {
let addressReuse = false
let walletScore = {}
- return Promise.all([settingsLoader.loadLatest(), checkForBlacklisted(updatedTx), doesTxReuseAddress(updatedTx), getWalletScore(updatedTx, pi)])
- .then(([{ config }, blacklistItems, isReusedAddress, fetchedWalletScore]) => {
+ const promises = [settingsLoader.loadLatest()]
+
+ const isFirstPost = !r.tx.fiat || r.tx.fiat.isZero()
+ if (isFirstPost) {
+ promises.push(checkForBlacklisted(updatedTx), doesTxReuseAddress(updatedTx), getWalletScore(updatedTx, pi))
+ }
+
+ return Promise.all(promises)
+ .then(([{ config }, blacklistItems = false, isReusedAddress = false, fetchedWalletScore = null]) => {
const rejectAddressReuse = configManager.getCompliance(config).rejectAddressReuse
walletScore = fetchedWalletScore
@@ -87,11 +94,7 @@ function logActionById (action, _rec, txId) {
}
function checkForBlacklisted (tx) {
- // Check only on addressScan and avoid testing for blacklist on every bill inserted
- if (!tx.fiat || tx.fiat.isZero()) {
- return blacklist.blocked(tx.toAddress, tx.cryptoCode)
- }
- return Promise.resolve(false)
+ return blacklist.blocked(tx.toAddress, tx.cryptoCode)
}
function postProcess (r, pi, isBlacklisted, addressReuse, walletScore) {
@@ -113,7 +116,7 @@ function postProcess (r, pi, isBlacklisted, addressReuse, walletScore) {
return Promise.resolve({
walletScore: walletScore.score,
operatorCompleted: true,
- error: 'Ciphertrace score is above defined threshold',
+ error: 'Chain analysis score is above defined threshold',
errorCode: 'scoreThresholdReached'
})
}
@@ -167,32 +170,20 @@ function postProcess (r, pi, isBlacklisted, addressReuse, walletScore) {
}
function doesTxReuseAddress (tx) {
- if (!tx.fiat || tx.fiat.isZero()) {
- const sql = `
- SELECT EXISTS (
- SELECT DISTINCT to_address FROM (
- SELECT to_address FROM cash_in_txs WHERE id != $1
- ) AS x WHERE to_address = $2
- )`
- return db.one(sql, [tx.id, tx.toAddress]).then(({ exists }) => exists)
- }
- return Promise.resolve(false)
+ const sql = `
+ SELECT EXISTS (
+ SELECT DISTINCT to_address FROM (
+ SELECT to_address FROM cash_in_txs WHERE id != $1
+ ) AS x WHERE to_address = $2
+ )`
+ return db.one(sql, [tx.id, tx.toAddress]).then(({ exists }) => exists)
}
function getWalletScore (tx, pi) {
return pi.isWalletScoringEnabled(tx)
.then(isEnabled => {
if (!isEnabled) return null
- if (!tx.fiat || tx.fiat.isZero()) {
- return pi.rateWallet(tx.cryptoCode, tx.toAddress)
- }
- // Passthrough the previous result
- return pi.isValidWalletScore(tx.walletScore)
- .then(isValid => ({
- address: tx.toAddress,
- score: tx.walletScore,
- isValid
- }))
+ return pi.rateAddress(tx.cryptoCode, tx.toAddress)
})
}
diff --git a/lib/cash-out/cash-out-tx.js b/lib/cash-out/cash-out-tx.js
index f0437f3c..93274e4d 100644
--- a/lib/cash-out/cash-out-tx.js
+++ b/lib/cash-out/cash-out-tx.js
@@ -117,7 +117,6 @@ function processTxStatus (tx, settings) {
}
function getWalletScore (tx, pi) {
- const rejectEmpty = message => x => _.isNil(x) || _.isEmpty(x) ? Promise.reject(new Error(message)) : x
const statuses = ['published', 'authorized', 'confirmed', 'insufficientFunds']
if (!_.includes(tx.status, statuses) || !_.isNil(tx.walletScore)) {
@@ -128,20 +127,13 @@ function getWalletScore (tx, pi) {
return pi.isWalletScoringEnabled(tx)
.then(isEnabled => {
if (!isEnabled) return tx
- return pi.getTransactionHash(tx)
- .then(rejectEmpty('No transaction hashes'))
- .then(txHashes => pi.getInputAddresses(tx, txHashes))
- .then(rejectEmpty('No input addresses'))
- .then(addresses => Promise.all(_.map(it => pi.rateWallet(tx.cryptoCode, it), addresses)))
- .then(rejectEmpty('No score ratings'))
- .then(_.maxBy(_.get(['score'])))
- .then(highestScore =>
- // Conservatively assign the highest risk of all input addresses to the risk of this transaction
- highestScore.isValid
- ? _.assign(tx, { walletScore: highestScore.score })
+ return pi.rateTransaction(tx)
+ .then(res =>
+ res.isValid
+ ? _.assign(tx, { walletScore: res.score })
: _.assign(tx, {
- walletScore: highestScore.score,
- error: 'Ciphertrace score is above defined threshold',
+ walletScore: res.score,
+ error: 'Chain analysis score is above defined threshold',
errorCode: 'scoreThresholdReached',
dispense: true
})
@@ -149,7 +141,7 @@ function getWalletScore (tx, pi) {
.catch(error => _.assign(tx, {
walletScore: 10,
error: `Failure getting address score: ${error.message}`,
- errorCode: 'ciphertraceError',
+ errorCode: 'walletScoringError',
dispense: true
}))
})
diff --git a/lib/customers.js b/lib/customers.js
index 371e5634..071604cd 100644
--- a/lib/customers.js
+++ b/lib/customers.js
@@ -18,7 +18,7 @@ const sms = require('./sms')
const settingsLoader = require('./new-settings-loader')
const logger = require('./logger')
-const TX_PASSTHROUGH_ERROR_CODES = ['operatorCancel', 'scoreThresholdReached', 'ciphertraceError']
+const TX_PASSTHROUGH_ERROR_CODES = ['operatorCancel', 'scoreThresholdReached', 'walletScoringError']
const ID_PHOTO_CARD_DIR = process.env.ID_PHOTO_CARD_DIR
const FRONT_CAMERA_DIR = process.env.FRONT_CAMERA_DIR
diff --git a/lib/new-admin/config/accounts.js b/lib/new-admin/config/accounts.js
index 36bcee22..0041ba93 100644
--- a/lib/new-admin/config/accounts.js
+++ b/lib/new-admin/config/accounts.js
@@ -59,7 +59,7 @@ const ALL_ACCOUNTS = [
{ code: 'none', display: 'None', class: ZERO_CONF, cryptos: ALL_CRYPTOS },
{ code: 'blockcypher', display: 'Blockcypher', class: ZERO_CONF, cryptos: [BTC] },
{ code: 'mock-zero-conf', display: 'Mock 0-conf', class: ZERO_CONF, cryptos: ALL_CRYPTOS, dev: true },
- { code: 'ciphertrace', display: 'CipherTrace', class: WALLET_SCORING, cryptos: [BTC, ETH, LTC, BCH] },
+ { code: 'scorechain', display: 'Scorechain', class: WALLET_SCORING, cryptos: [BTC, ETH, LTC, BCH, DASH, USDT, USDT_TRON, TRX] },
{ code: 'mock-scoring', display: 'Mock scoring', class: WALLET_SCORING, cryptos: ALL_CRYPTOS, dev: true }
]
diff --git a/lib/plugins.js b/lib/plugins.js
index 18aaee5f..fe6b5b79 100644
--- a/lib/plugins.js
+++ b/lib/plugins.js
@@ -994,20 +994,8 @@ function plugins (settings, deviceId) {
.then(buildRates)
}
- function rateWallet (cryptoCode, address) {
- return walletScoring.rateWallet(settings, cryptoCode, address)
- }
-
- function isValidWalletScore (score) {
- return walletScoring.isValidWalletScore(settings, score)
- }
-
- function getTransactionHash (tx) {
- return walletScoring.getTransactionHash(settings, tx.cryptoCode, tx.toAddress)
- }
-
- function getInputAddresses (tx, txHashes) {
- return walletScoring.getInputAddresses(settings, tx.cryptoCode, txHashes)
+ function rateAddress (cryptoCode, address) {
+ return walletScoring.rateAddress(settings, cryptoCode, address)
}
function isWalletScoringEnabled (tx) {
@@ -1046,10 +1034,7 @@ function plugins (settings, deviceId) {
notifyOperator,
fetchCurrentConfigVersion,
pruneMachinesHeartbeat,
- rateWallet,
- isValidWalletScore,
- getTransactionHash,
- getInputAddresses,
+ rateAddress,
isWalletScoringEnabled,
probeLN,
buildAvailableUnits
diff --git a/lib/plugins/wallet-scoring/ciphertrace/ciphertrace.js b/lib/plugins/wallet-scoring/ciphertrace/ciphertrace.js
deleted file mode 100644
index d3922525..00000000
--- a/lib/plugins/wallet-scoring/ciphertrace/ciphertrace.js
+++ /dev/null
@@ -1,150 +0,0 @@
-const coins = require('@lamassu/coins')
-const axios = require('axios')
-const _ = require('lodash/fp')
-
-const logger = require('../../../logger')
-
-const NAME = 'CipherTrace'
-const SUPPORTED_COINS = ['BTC', 'ETH', 'BCH', 'LTC']
-
-function getClient (account) {
- if (_.isNil(account) || !account.enabled) return null
-
- const [ctv1, username, secretKey] = account.authorizationValue.split(':')
- if (_.isNil(ctv1) || _.isNil(username) || _.isNil(secretKey)) {
- throw new Error('Invalid CipherTrace configuration')
- }
-
- const apiVersion = ctv1.slice(-2)
- const authHeader = {
- 'Authorization': account.authorizationValue
- }
- return { apiVersion, authHeader }
-}
-
-function rateWallet (account, cryptoCode, address) {
- const client = getClient(account)
- if (!_.includes(_.toUpper(cryptoCode), SUPPORTED_COINS) || _.isNil(client)) return Promise.resolve(null)
-
- const { apiVersion, authHeader } = client
- const threshold = account.scoreThreshold
-
- logger.info(`** DEBUG ** rateWallet ENDPOINT: https://rest.ciphertrace.com/aml/${apiVersion}/${_.toLower(cryptoCode)}/risk?address=${address}`)
-
- return axios.get(`https://rest.ciphertrace.com/aml/${apiVersion}/${_.toLower(cryptoCode)}/risk?address=${address}`, {
- headers: authHeader
- })
- .then(res => ({ address, score: res.data.risk, isValid: res.data.risk < threshold }))
- .then(result => {
- logger.info(`** DEBUG ** rateWallet RETURN: ${result}`)
- return result
- })
- .catch(err => {
- logger.error(`** DEBUG ** rateWallet ERROR: ${err.message}`)
- throw err
- })
-}
-
-function isValidWalletScore (account, score) {
- const client = getClient(account)
- if (_.isNil(client)) return Promise.resolve(true)
-
- const threshold = account.scoreThreshold
- return _.isNil(account) ? Promise.resolve(true) : Promise.resolve(score < threshold)
-}
-
-function getAddressTransactionsHashes (receivingAddress, cryptoCode, client, wallet) {
- const { apiVersion, authHeader } = client
-
- logger.info(`** DEBUG ** getTransactionHash ENDPOINT: https://rest.ciphertrace.com/api/${apiVersion}/${_.toLower(cryptoCode) !== 'btc' ? `${_.toLower(cryptoCode)}_` : ``}address/search?features=tx&address=${receivingAddress}&mempool=true`)
-
- return axios.get(`https://rest.ciphertrace.com/api/${apiVersion}/${_.toLower(cryptoCode) !== 'btc' ? `${_.toLower(cryptoCode)}_` : ``}address/search?features=tx&address=${receivingAddress}&mempool=true`, {
- headers: authHeader
- })
- .then(_.flow(
- _.get(['data', 'txHistory']),
- _.map(_.get(['txHash']))
- ))
- .catch(err => {
- logger.error(`** DEBUG ** getTransactionHash ERROR: ${err}`)
- logger.error(`** DEBUG ** Fetching transactions hashes via wallet node...`)
- return wallet.getTxHashesByAddress(cryptoCode, receivingAddress)
- })
-}
-
-function getTransactionHash (account, cryptoCode, receivingAddress, wallet) {
- const client = getClient(account)
- if (!_.includes(_.toUpper(cryptoCode), SUPPORTED_COINS) || _.isNil(client)) return Promise.resolve(null)
- return getAddressTransactionsHashes(receivingAddress, cryptoCode, client, wallet)
- .then(txHashes => {
- if (_.size(txHashes) > 1) {
- logger.warn('An address generated by this wallet was used in more than one transaction')
- }
- logger.info('** DEBUG ** getTransactionHash RETURN: ', _.join(', ', txHashes))
- return txHashes
- })
- .catch(err => {
- logger.error('** DEBUG ** getTransactionHash from wallet node ERROR: ', err)
- throw err
- })
-}
-
-function getInputAddresses (account, cryptoCode, txHashes) {
- const client = getClient(account)
- if (_.isEmpty(txHashes) || !_.includes(_.toUpper(cryptoCode), SUPPORTED_COINS) || _.isNil(client))
- return Promise.resolve([])
-
- /* NOTE: The API accepts at most 10 hashes, and for us here more than 1 is already an exception, not the norm. */
- if (_.size(txHashes) > 10)
- return Promise.reject(new Error("Too many tx hashes -- shouldn't happen!"))
-
- const { apiVersion, authHeader } = client
-
- cryptoCode = _.toLower(cryptoCode)
- const lastPathComp = cryptoCode !== 'btc' ? cryptoCode + '_tx' : 'tx'
-
- txHashes = _(txHashes).take(10).join(',')
-
- const url = `https://rest.ciphertrace.com/api/${apiVersion}/${lastPathComp}?txhashes=${txHashes}`
- console.log('** DEBUG ** getInputAddresses ENDPOINT: ', url)
-
- return axios.get(url, { headers: authHeader })
- .then(res => {
- const data = res.data
- if (_.size(data.transactions) > 1) {
- logger.warn('An address generated by this wallet was used in more than one transaction')
- }
-
- const transactionInputs = _.flatMap(it => it.inputs, data.transactions)
- const inputAddresses = _.map(it => it.address, transactionInputs)
-
- logger.info(`** DEBUG ** getInputAddresses RETURN: ${inputAddresses}`)
-
- return inputAddresses
- })
- .catch(err => {
- logger.error(`** DEBUG ** getInputAddresses ERROR: ${err.message}`)
- throw err
- })
-}
-
-function isWalletScoringEnabled (account, cryptoCode) {
- const isAccountEnabled = !_.isNil(account) && account.enabled
-
- if (!isAccountEnabled) return Promise.resolve(false)
-
- if (!SUPPORTED_COINS.includes(cryptoCode) && !coins.utils.isErc20Token(cryptoCode)) {
- return Promise.reject(new Error('Unsupported crypto: ' + cryptoCode))
- }
-
- return Promise.resolve(true)
-}
-
-module.exports = {
- NAME,
- rateWallet,
- isValidWalletScore,
- getTransactionHash,
- getInputAddresses,
- isWalletScoringEnabled
-}
diff --git a/lib/plugins/wallet-scoring/mock-scoring/mock-scoring.js b/lib/plugins/wallet-scoring/mock-scoring/mock-scoring.js
index 455ff4bf..0f154ffc 100644
--- a/lib/plugins/wallet-scoring/mock-scoring/mock-scoring.js
+++ b/lib/plugins/wallet-scoring/mock-scoring/mock-scoring.js
@@ -2,7 +2,7 @@ const NAME = 'FakeScoring'
const { WALLET_SCORE_THRESHOLD } = require('../../../constants')
-function rateWallet (account, cryptoCode, address) {
+function rateAddress (account, cryptoCode, address) {
return new Promise((resolve, _) => {
setTimeout(() => {
console.log('[WALLET-SCORING] DEBUG: Mock scoring rating wallet address %s', address)
@@ -12,30 +12,6 @@ function rateWallet (account, cryptoCode, address) {
})
}
-function isValidWalletScore (account, score) {
- return new Promise((resolve, _) => {
- setTimeout(() => {
- return resolve(score < WALLET_SCORE_THRESHOLD)
- }, 100)
- })
-}
-
-function getTransactionHash (account, cryptoCode, receivingAddress) {
- return new Promise((resolve, _) => {
- setTimeout(() => {
- return resolve('')
- }, 100)
- })
-}
-
-function getInputAddresses (account, cryptoCode, txHashes) {
- return new Promise((resolve, _) => {
- setTimeout(() => {
- return resolve([''])
- }, 100)
- })
-}
-
function isWalletScoringEnabled (account, cryptoCode) {
return new Promise((resolve, _) => {
setTimeout(() => {
@@ -44,12 +20,9 @@ function isWalletScoringEnabled (account, cryptoCode) {
})
}
-
module.exports = {
NAME,
- rateWallet,
- isValidWalletScore,
- getTransactionHash,
- getInputAddresses,
+ rateAddress,
+ rateTransaction:rateAddress,
isWalletScoringEnabled
}
diff --git a/lib/plugins/wallet-scoring/scorechain/scorechain.js b/lib/plugins/wallet-scoring/scorechain/scorechain.js
new file mode 100644
index 00000000..dd0a82f2
--- /dev/null
+++ b/lib/plugins/wallet-scoring/scorechain/scorechain.js
@@ -0,0 +1,74 @@
+const axios = require('axios')
+const _ = require('lodash/fp')
+
+
+const NAME = 'Scorechain'
+const SUPPORTED_COINS = {
+ BTC: 'BITCOIN',
+ ETH: 'ETHEREUM',
+ USDT: 'ETHEREUM',
+ BCH: 'BITCOINCASH',
+ LTC: 'LITECOIN',
+ DASH: 'DASH',
+ TRX: 'TRON',
+ USDT_TRON: 'TRON'
+}
+
+const TYPE = {
+ TRANSACTION: 'TRANSACTION',
+ ADDRESS: 'ADDRESS'
+}
+
+function rate (account, objectType, cryptoCode, objectId) {
+ if (_.isNil(account) || !account.enabled || !Object.keys(SUPPORTED_COINS).includes(cryptoCode)) return Promise.resolve(null)
+
+ const threshold = account.scoreThreshold
+ const payload = {
+ analysisType: 'ASSIGNED',
+ objectType,
+ objectId,
+ blockchain: SUPPORTED_COINS[cryptoCode],
+ coin: "ALL"
+ }
+
+ return axios.post(`https://api.scorechain.com/v1/scoringAnalysis`, payload, { headers: { 'X-API-KEY': account.apiKey }
+ })
+ .then(res => {
+ const resScore = res.data?.analysis?.assigned?.result?.score
+ if (!resScore) throw new Error('Failed to get score from Scorechain API')
+
+ // normalize score to 0-10 where 0 is the highest risk
+ // use 101 instead of 100 to avoid division by zero
+ return { score: (101 - resScore) / 10 - 0.1, isValid: resScore >= threshold }
+ })
+ .catch(err => {
+ throw err
+ })
+}
+
+function rateTransaction (account, cryptoCode, transactionId) {
+ rate(account, TYPE.TRANSACTION, cryptoCode, transactionId)
+}
+
+function rateAddress (account, cryptoCode, address) {
+ rate(account, TYPE.ADDRESS, cryptoCode, address)
+}
+
+function isWalletScoringEnabled (account, cryptoCode) {
+ const isAccountEnabled = !_.isNil(account) && account.enabled
+
+ if (!isAccountEnabled) return Promise.resolve(false)
+
+ if (!Object.keys(SUPPORTED_COINS).includes(cryptoCode)) {
+ return Promise.reject(new Error('Unsupported crypto: ' + cryptoCode))
+ }
+
+ return Promise.resolve(true)
+}
+
+module.exports = {
+ NAME,
+ rateAddress,
+ rateTransaction,
+ isWalletScoringEnabled
+}
diff --git a/lib/routes/txRoutes.js b/lib/routes/txRoutes.js
index a3261147..f63755a0 100644
--- a/lib/routes/txRoutes.js
+++ b/lib/routes/txRoutes.js
@@ -22,7 +22,7 @@ function postTx (req, res, next) {
throw httpError(tx.error, 570)
case 'scoreThresholdReached':
throw httpError(tx.error, 571)
- case 'ciphertraceError':
+ case 'walletScoringError':
throw httpError(tx.error, 572)
default:
throw httpError(tx.error, 500)
diff --git a/lib/tx.js b/lib/tx.js
index ab07e0c0..219ec5cf 100644
--- a/lib/tx.js
+++ b/lib/tx.js
@@ -82,7 +82,7 @@ function customerHistory (customerId, thresholdDays) {
FROM cash_out_txs txOut
WHERE txOut.customer_id = $1
AND txOut.created > now() - interval $2
- AND (error_code IS NULL OR error_code NOT IN ('operatorCancel', 'scoreThresholdReached', 'ciphertraceError'))
+ AND (error_code IS NULL OR error_code NOT IN ('operatorCancel', 'scoreThresholdReached', 'walletScoringError', 'ciphertraceError'))
AND fiat > 0
) ch WHERE NOT ch.expired ORDER BY ch.created`
diff --git a/lib/wallet-scoring.js b/lib/wallet-scoring.js
index 74de6130..516ac19b 100644
--- a/lib/wallet-scoring.js
+++ b/lib/wallet-scoring.js
@@ -3,7 +3,7 @@ const argv = require('minimist')(process.argv.slice(2))
const configManager = require('./new-config-manager')
function loadWalletScoring (settings, cryptoCode) {
- const pluginCode = argv.mockScoring ? 'mock-scoring' : 'ciphertrace'
+ const pluginCode = argv.mockScoring ? 'mock-scoring' : 'scorechain'
const wallet = cryptoCode ? ph.load(ph.WALLET, configManager.getWalletSettings(cryptoCode, settings.config).wallet) : null
const plugin = ph.load(ph.WALLET_SCORING, pluginCode)
const account = settings.accounts[pluginCode]
@@ -11,39 +11,21 @@ function loadWalletScoring (settings, cryptoCode) {
return { plugin, account, wallet }
}
-function rateWallet (settings, cryptoCode, address) {
+function rateTransaction (settings, cryptoCode, address) {
return Promise.resolve()
.then(() => {
const { plugin, account } = loadWalletScoring(settings)
- return plugin.rateWallet(account, cryptoCode, address)
+ return plugin.rateAddress(account, cryptoCode, address)
})
}
-function isValidWalletScore (settings, score) {
+function rateAddress (settings, cryptoCode, address) {
return Promise.resolve()
.then(() => {
const { plugin, account } = loadWalletScoring(settings)
- return plugin.isValidWalletScore(account, score)
- })
-}
-
-function getTransactionHash (settings, cryptoCode, receivingAddress) {
- return Promise.resolve()
- .then(() => {
- const { plugin, account, wallet } = loadWalletScoring(settings, cryptoCode)
-
- return plugin.getTransactionHash(account, cryptoCode, receivingAddress, wallet)
- })
-}
-
-function getInputAddresses (settings, cryptoCode, txHashes) {
- return Promise.resolve()
- .then(() => {
- const { plugin, account } = loadWalletScoring(settings)
-
- return plugin.getInputAddresses(account, cryptoCode, txHashes)
+ return plugin.rateAddress(account, cryptoCode, address)
})
}
@@ -57,9 +39,7 @@ function isWalletScoringEnabled (settings, cryptoCode) {
}
module.exports = {
- rateWallet,
- isValidWalletScore,
- getTransactionHash,
- getInputAddresses,
+ rateAddress,
+ rateTransaction,
isWalletScoringEnabled
}
diff --git a/new-lamassu-admin/src/pages/Services/schemas/index.js b/new-lamassu-admin/src/pages/Services/schemas/index.js
index e077222a..fdcf2097 100644
--- a/new-lamassu-admin/src/pages/Services/schemas/index.js
+++ b/new-lamassu-admin/src/pages/Services/schemas/index.js
@@ -5,12 +5,12 @@ import bitgo from './bitgo'
import bitstamp from './bitstamp'
import blockcypher from './blockcypher'
import cex from './cex'
-import ciphertrace from './ciphertrace'
import galoy from './galoy'
import infura from './infura'
import itbit from './itbit'
import kraken from './kraken'
import mailgun from './mailgun'
+import scorechain from './scorechain'
import telnyx from './telnyx'
import trongrid from './trongrid'
import twilio from './twilio'
@@ -30,7 +30,7 @@ export default {
[twilio.code]: twilio,
[binanceus.code]: binanceus,
[cex.code]: cex,
- [ciphertrace.code]: ciphertrace,
+ [scorechain.code]: scorechain,
[trongrid.code]: trongrid,
[binance.code]: binance,
[bitfinex.code]: bitfinex
diff --git a/new-lamassu-admin/src/pages/Services/schemas/ciphertrace.js b/new-lamassu-admin/src/pages/Services/schemas/scorechain.js
similarity index 69%
rename from new-lamassu-admin/src/pages/Services/schemas/ciphertrace.js
rename to new-lamassu-admin/src/pages/Services/schemas/scorechain.js
index d48c8e41..ed991692 100644
--- a/new-lamassu-admin/src/pages/Services/schemas/ciphertrace.js
+++ b/new-lamassu-admin/src/pages/Services/schemas/scorechain.js
@@ -7,13 +7,13 @@ import SecretInputFormik from 'src/components/inputs/formik/SecretInput'
import { secretTest, leadingZerosTest } from './helper'
export default {
- code: 'ciphertrace',
- name: 'CipherTrace',
- title: 'CipherTrace (Scoring)',
+ code: 'scorechain',
+ name: 'Scorechain',
+ title: 'Scorechain (Scoring)',
elements: [
{
- code: 'authorizationValue',
- display: 'Authorization value',
+ code: 'apikey',
+ display: 'API Key',
component: SecretInputFormik
},
{
@@ -29,8 +29,7 @@ export default {
settings: {
enabled: true,
disabledMessage: 'This plugin is disabled',
- label:
- 'Enabled (Supported coins: BTC, ETH, BCH, LTC and all active ERC-20 tokens)',
+ label: 'Enabled',
requirement: null,
rightSideLabel: true
},
@@ -39,13 +38,13 @@ export default {
],
getValidationSchema: account => {
return Yup.object().shape({
- authorizationValue: Yup.string('The authorization value must be a string')
+ apiKey: Yup.string('The API key must be a string')
.max(100, 'Too long')
- .test(secretTest(account?.authorizationValue, 'authorization value')),
+ .test(secretTest(account?.apiKey, 'API key')),
scoreThreshold: Yup.number('The score threshold must be a number')
.required('A score threshold is required')
- .min(1, 'The score threshold must be between 1 and 10')
- .max(10, 'The score threshold must be between 1 and 10')
+ .min(1, 'The score threshold must be between 1 and 100')
+ .max(100, 'The score threshold must be between 1 and 100')
.integer('The score threshold must be an integer')
.test(
'no-leading-zeros',
diff --git a/new-lamassu-admin/src/pages/Transactions/DetailsCard.js b/new-lamassu-admin/src/pages/Transactions/DetailsCard.js
index 1cfeacc9..74c5451f 100644
--- a/new-lamassu-admin/src/pages/Transactions/DetailsCard.js
+++ b/new-lamassu-admin/src/pages/Transactions/DetailsCard.js
@@ -183,9 +183,13 @@ const DetailsRow = ({ it: tx, timezone }) => {
FileSaver.saveAs(content, zipFilename)
}
- const hasCiphertraceError = tx =>
+ const hasChainAnalysisError = tx =>
!R.isNil(tx.errorCode) &&
- R.includes(tx.errorCode, ['scoreThresholdReached', 'ciphertraceError'])
+ R.includes(tx.errorCode, [
+ 'scoreThresholdReached',
+ 'walletScoringError',
+ 'ciphertraceError'
+ ])
const errorElements = (
<>
@@ -205,10 +209,10 @@ const DetailsRow = ({ it: tx, timezone }) => {
r={3.5}
fill={
it < tx.walletScore
- ? !hasCiphertraceError(tx)
+ ? !hasChainAnalysisError(tx)
? primaryColor
: errorColor
- : !hasCiphertraceError(tx)
+ : !hasChainAnalysisError(tx)
? subheaderColor
: offErrorColor
}
@@ -221,7 +225,7 @@ const DetailsRow = ({ it: tx, timezone }) => {
noMargin
className={classNames({
[classes.bold]: true,
- [classes.error]: hasCiphertraceError(tx)
+ [classes.error]: hasChainAnalysisError(tx)
})}>
{tx.walletScore}
@@ -359,7 +363,7 @@ const DetailsRow = ({ it: tx, timezone }) => {
{!R.isNil(tx.walletScore) && (
- {`CipherTrace score: ${tx.walletScore}/10`}
+ {`Chain analysis score: ${tx.walletScore}/10`}
)}
diff --git a/package-lock.json b/package-lock.json
index 358c3058..03365f19 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1148,6 +1148,25 @@
"fastpriorityqueue": "^0.7.1",
"typeforce": "^1.11.3",
"varuint-bitcoin": "^1.1.2"
+ },
+ "dependencies": {
+ "bitcoinjs-lib": {
+ "version": "npm:@bitgo-forks/bitcoinjs-lib@7.1.0-master.7",
+ "resolved": "https://registry.npmjs.org/@bitgo-forks/bitcoinjs-lib/-/bitcoinjs-lib-7.1.0-master.7.tgz",
+ "integrity": "sha512-FZle7954KnbbVXFCc5uYGtjq+0PFOnFxVchNwt3Kcv2nVusezTp29aeQwDi2Y+lM1dCoup2gJGXMkkREenY7KQ==",
+ "requires": {
+ "bech32": "^2.0.0",
+ "bip174": "npm:@bitgo-forks/bip174@3.1.0-master.4",
+ "bs58check": "^2.1.2",
+ "create-hash": "^1.1.0",
+ "fastpriorityqueue": "^0.7.1",
+ "json5": "^2.2.3",
+ "ripemd160": "^2.0.2",
+ "typeforce": "^1.11.3",
+ "varuint-bitcoin": "^1.1.2",
+ "wif": "^2.0.1"
+ }
+ }
}
},
"@bitgo/utxo-ord": {
@@ -4610,23 +4629,6 @@
"resolved": "https://registry.npmjs.org/bitcoin-ops/-/bitcoin-ops-1.4.1.tgz",
"integrity": "sha512-pef6gxZFztEhaE9RY9HmWVmiIHqCb2OyS4HPKkpc6CIiiOa3Qmuoylxc5P2EkU3w+5eTSifI9SEZC88idAIGow=="
},
- "bitcoinjs-lib": {
- "version": "npm:@bitgo-forks/bitcoinjs-lib@7.1.0-master.7",
- "resolved": "https://registry.npmjs.org/@bitgo-forks/bitcoinjs-lib/-/bitcoinjs-lib-7.1.0-master.7.tgz",
- "integrity": "sha512-FZle7954KnbbVXFCc5uYGtjq+0PFOnFxVchNwt3Kcv2nVusezTp29aeQwDi2Y+lM1dCoup2gJGXMkkREenY7KQ==",
- "requires": {
- "bech32": "^2.0.0",
- "bip174": "npm:@bitgo-forks/bip174@3.1.0-master.4",
- "bs58check": "^2.1.2",
- "create-hash": "^1.1.0",
- "fastpriorityqueue": "^0.7.1",
- "json5": "^2.2.3",
- "ripemd160": "^2.0.2",
- "typeforce": "^1.11.3",
- "varuint-bitcoin": "^1.1.2",
- "wif": "^2.0.1"
- }
- },
"bitcoinjs-message": {
"version": "npm:@bitgo-forks/bitcoinjs-message@1.0.0-master.2",
"resolved": "https://registry.npmjs.org/@bitgo-forks/bitcoinjs-message/-/bitcoinjs-message-1.0.0-master.2.tgz",