taler-typescript-core

Wallet core logic and WebUIs for various components
Log | Files | Refs | Submodules | README | LICENSE

commit bec7ad84af7c5b343d078ea83d7db28f54b5d93c
parent 9be8e931ecba22572875a060f4d87db2527993fe
Author: Florian Dold <dold@taler.net>
Date:   Tue,  1 Sep 2026 21:49:46 +0200

wallet-core: filter legacy exchange transactions by URL

Diffstat:
Mpackages/taler-wallet-core/src/transactions.test.ts | 29++++++++++++++++++++++++++++-
Mpackages/taler-wallet-core/src/transactions.ts | 21+++++++--------------
2 files changed, 35 insertions(+), 15 deletions(-)

diff --git a/packages/taler-wallet-core/src/transactions.test.ts b/packages/taler-wallet-core/src/transactions.test.ts @@ -13,7 +13,7 @@ You should have received a copy of the GNU General Public License along with GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> */ -import { TransactionType } from "@gnu-taler/taler-util"; +import { ScopeType, TransactionType } from "@gnu-taler/taler-util"; import assert from "node:assert"; import { test } from "node:test"; import { @@ -22,6 +22,7 @@ import { ParsedTransactionIdentifier, parseTransactionIdentifier, resolveTransactionReference, + shouldSkipCurrency, } from "./transactions.js"; import type { WalletWithdrawalGroup } from "./db/records.js"; import type { WalletDbTransaction } from "./db/transaction.js"; @@ -131,3 +132,29 @@ test("an unknown or ambiguous bank withdrawal reference is not resolved", async }), ); }); + +test("legacy exchange scope conservatively matches transactions by URL", () => { + const request = { + scopeInfo: { + type: ScopeType.ExchangeLegacyKeys as const, + currency: "TESTKUDOS", + url: "https://exchange/", + masterPub: "superseded-key", + }, + }; + assert.equal( + shouldSkipCurrency(request, "TESTKUDOS", ["https://exchange/"]), + false, + "the matching URL must be included regardless of the master key", + ); + assert.equal( + shouldSkipCurrency(request, "TESTKUDOS", ["https://other-exchange/"]), + true, + "a different URL must still be excluded", + ); + assert.equal( + shouldSkipCurrency(request, "OTHER", ["https://exchange/"]), + true, + "a different currency must still be excluded", + ); +}); diff --git a/packages/taler-wallet-core/src/transactions.ts b/packages/taler-wallet-core/src/transactions.ts @@ -77,7 +77,7 @@ import { WalletDbTransaction } from "./db/transaction.js"; const logger = new Logger("taler-wallet-core:transactions.ts"); -function shouldSkipCurrency( +export function shouldSkipCurrency( transactionsRequest: | TransactionsRequest | GetTransactionsV2Request @@ -94,7 +94,12 @@ function shouldSkipCurrency( case ScopeType.Global: { return !sameCurrency; } - case ScopeType.Exchange: { + case ScopeType.Exchange: + case ScopeType.ExchangeLegacyKeys: { + // Transaction metadata only records the exchange URL. Treat every + // key set served at that URL as a conservative match. This can + // return false positives after a key rotation, but never hides older + // transactions whose exact key set was not recorded. return ( !sameCurrency || (exchangesInTransaction.length > 0 && @@ -109,18 +114,6 @@ function shouldSkipCurrency( "filtering transactions by auditor scope is not implemented", ); } - case ScopeType.ExchangeLegacyKeys: { - // A transaction records the exchanges it involved by URL, which does - // not say which key set signed the coins, so this filter cannot be - // answered accurately. Refusing beats quietly returning the - // transactions of the exchange's current key set as if they were the - // superseded ones. - throw TalerError.fromDetail( - TalerErrorCode.WALLET_CORE_API_BAD_REQUEST, - { parameter: "scopeInfo" }, - "filtering transactions by a superseded exchange key set is not implemented", - ); - } default: assertUnreachable(transactionsRequest.scopeInfo); }