From 546f446523bb75e12f09ef2bfa816dd38147148c Mon Sep 17 00:00:00 2001 From: Rafael Taranto Date: Mon, 26 May 2025 21:43:08 +0100 Subject: [PATCH 1/3] feat: address reuse is now per customer --- packages/server/lib/cash-in/cash-in-tx.js | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/packages/server/lib/cash-in/cash-in-tx.js b/packages/server/lib/cash-in/cash-in-tx.js index 8ca54625..3cf1e053 100644 --- a/packages/server/lib/cash-in/cash-in-tx.js +++ b/packages/server/lib/cash-in/cash-in-tx.js @@ -9,6 +9,7 @@ const logger = require('../logger') const settingsLoader = require('../new-settings-loader') const configManager = require('../new-config-manager') const notifier = require('../notifier') +const constants = require('../constants') const cashInAtomic = require('./cash-in-atomic') const cashInLow = require('./cash-in-low') @@ -197,11 +198,21 @@ function postProcess(r, pi, isBlacklisted, addressReuse, walletScore) { function doesTxReuseAddress(tx) { 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) + SELECT 1 + FROM cash_in_txs + WHERE id != $1 + AND to_address = $2 + AND customer_id != $3 + AND customer_id != $4 + );` + return db + .one(sql, [ + tx.id, + tx.toAddress, + tx.customerId, + constants.anonymousCustomer.uuid, + ]) + .then(({ exists }) => exists) } function getWalletScore(tx, pi) { From 1cf692946e6d8b786c5c71ba69d3012accfea0e2 Mon Sep 17 00:00:00 2001 From: Rafael Taranto Date: Tue, 27 May 2025 07:21:15 +0100 Subject: [PATCH 2/3] feat: allow address reuse if same customer --- packages/server/lib/cash-in/cash-in-tx.js | 28 +++++++++++------------ 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/packages/server/lib/cash-in/cash-in-tx.js b/packages/server/lib/cash-in/cash-in-tx.js index 3cf1e053..1b987754 100644 --- a/packages/server/lib/cash-in/cash-in-tx.js +++ b/packages/server/lib/cash-in/cash-in-tx.js @@ -195,23 +195,23 @@ function postProcess(r, pi, isBlacklisted, addressReuse, walletScore) { }) } +// At most only one authenticated customer can use an address. +// We count distinct customers plus the current customer if they are not anonymous +// To prevent malicious blocking of address, we only check for txs with actual fiat function doesTxReuseAddress(tx) { const sql = ` - SELECT EXISTS ( - SELECT 1 - FROM cash_in_txs - WHERE id != $1 - AND to_address = $2 - AND customer_id != $3 - AND customer_id != $4 - );` + SELECT COUNT(*) > 1 as exists + FROM (SELECT DISTINCT customer_id + FROM cash_in_txs + WHERE to_address = $1 + AND customer_id != $3 + AND fiat > 0 + UNION + SELECT $2 + WHERE $2 != $3) t; + ` return db - .one(sql, [ - tx.id, - tx.toAddress, - tx.customerId, - constants.anonymousCustomer.uuid, - ]) + .one(sql, [tx.toAddress, tx.customerId, constants.anonymousCustomer.uuid]) .then(({ exists }) => exists) } From dff4fadc9c3e782095993b4b0f3cb2ec6c1cc0a9 Mon Sep 17 00:00:00 2001 From: Rafael Taranto Date: Tue, 27 May 2025 13:52:29 +0100 Subject: [PATCH 3/3] chore: clarify requirements on comment --- packages/server/lib/cash-in/cash-in-tx.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/server/lib/cash-in/cash-in-tx.js b/packages/server/lib/cash-in/cash-in-tx.js index 1b987754..e454efa5 100644 --- a/packages/server/lib/cash-in/cash-in-tx.js +++ b/packages/server/lib/cash-in/cash-in-tx.js @@ -195,8 +195,11 @@ function postProcess(r, pi, isBlacklisted, addressReuse, walletScore) { }) } +// This feels like it can be simplified, +// but it's the most concise query to express the requirement and its edge cases. // At most only one authenticated customer can use an address. -// We count distinct customers plus the current customer if they are not anonymous +// If the current customer is anon, we can still allow one other customer to use the address, +// So we count distinct customers plus the current customer if they are not anonymous. // To prevent malicious blocking of address, we only check for txs with actual fiat function doesTxReuseAddress(tx) { const sql = `