diff --git a/packages/taler-util/src/walletTypes.ts b/packages/taler-util/src/walletTypes.ts index d1453658b..657e6568c 100644 --- a/packages/taler-util/src/walletTypes.ts +++ b/packages/taler-util/src/walletTypes.ts @@ -933,3 +933,16 @@ export const codecForWithdrawUriInfoResponse = (): Codec { >(this, "exchangeBaseUrlIndex", "exchangeBaseUrl"); } -class CurrenciesStore extends Store<"currencies", CurrencyRecord> { +class AuditorTrustStore extends Store<"auditorTrust", AuditorTrustRecord> { constructor() { - super("currencies", { keyPath: "name" }); + super("auditorTrust", { + keyPath: ["currency", "auditorBaseUrl", "auditorPub"], + }); } + auditorPubIndex = new Index< + "auditorTrust", + "auditorPubIndex", + string, + AuditorTrustRecord + >(this, "auditorPubIndex", "auditorPub"); + uidIndex = new Index<"auditorTrust", "uidIndex", string, AuditorTrustRecord>( + this, + "uidIndex", + "uids", + { multiEntry: true }, + ); +} + +class ExchangeTrustStore extends Store<"exchangeTrust", ExchangeTrustRecord> { + constructor() { + super("exchangeTrust", { + keyPath: ["currency", "exchangeBaseUrl", "exchangePub"], + }); + } + exchangeMasterPubIndex = new Index< + "exchangeTrust", + "exchangeMasterPubIndex", + string, + ExchangeTrustRecord + >(this, "exchangeMasterPubIndex", "exchangePub"); + uidIndex = new Index< + "exchangeTrust", + "uidIndex", + string, + ExchangeTrustRecord + >(this, "uidIndex", "uids", { multiEntry: true }); } class ConfigStore extends Store<"config", ConfigRecord> { @@ -1891,7 +1910,8 @@ class TombstonesStore extends Store<"tombstones", TombstoneRecord> { export const Stores = { coins: new CoinsStore(), config: new ConfigStore(), - currencies: new CurrenciesStore(), + auditorTrustStore: new AuditorTrustStore(), + exchangeTrustStore: new ExchangeTrustStore(), denominations: new DenominationsStore(), exchanges: new ExchangesStore(), proposals: new ProposalsStore(), diff --git a/packages/taler-wallet-core/src/operations/currencies.ts b/packages/taler-wallet-core/src/operations/currencies.ts new file mode 100644 index 000000000..1af30dfb5 --- /dev/null +++ b/packages/taler-wallet-core/src/operations/currencies.ts @@ -0,0 +1,68 @@ +/* + This file is part of GNU Taler + (C) 2021 Taler Systems S.A. + + GNU Taler is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + GNU Taler; see the file COPYING. If not, see + */ + +/** + * Imports. + */ +import { ExchangeRecord, Stores } from "../db.js"; +import { Logger } from "../index.js"; +import { InternalWalletState } from "./state.js"; + +const logger = new Logger("currencies.ts"); + +export interface TrustInfo { + isTrusted: boolean; + isAudited: boolean; +} + +/** + * Check if and how an exchange is trusted and/or audited. + */ +export async function getExchangeTrust( + ws: InternalWalletState, + exchangeInfo: ExchangeRecord, +): Promise { + let isTrusted = false; + let isAudited = false; + const exchangeDetails = exchangeInfo.details; + if (!exchangeDetails) { + throw Error(`exchange ${exchangeInfo.baseUrl} details not available`); + } + const exchangeTrustRecord = await ws.db.getIndexed( + Stores.exchangeTrustStore.exchangeMasterPubIndex, + exchangeDetails.masterPublicKey, + ); + if ( + exchangeTrustRecord && + exchangeTrustRecord.uids.length > 0 && + exchangeTrustRecord.currency === exchangeDetails.currency + ) { + isTrusted = true; + } + + for (const auditor of exchangeDetails.auditors) { + const auditorTrustRecord = await ws.db.getIndexed( + Stores.auditorTrustStore.auditorPubIndex, + auditor.auditor_pub, + ); + if (auditorTrustRecord && auditorTrustRecord.uids.length > 0) { + isAudited = true; + break; + } + } + + return { isTrusted, isAudited }; +} diff --git a/packages/taler-wallet-core/src/operations/exchanges.ts b/packages/taler-wallet-core/src/operations/exchanges.ts index f48b08ff7..e8833699d 100644 --- a/packages/taler-wallet-core/src/operations/exchanges.ts +++ b/packages/taler-wallet-core/src/operations/exchanges.ts @@ -527,43 +527,6 @@ async function updateExchangeFromUrlImpl( return updatedExchange; } -/** - * Check if and how an exchange is trusted and/or audited. - */ -export async function getExchangeTrust( - ws: InternalWalletState, - exchangeInfo: ExchangeRecord, -): Promise<{ isTrusted: boolean; isAudited: boolean }> { - let isTrusted = false; - let isAudited = false; - const exchangeDetails = exchangeInfo.details; - if (!exchangeDetails) { - throw Error(`exchange ${exchangeInfo.baseUrl} details not available`); - } - const currencyRecord = await ws.db.get( - Stores.currencies, - exchangeDetails.currency, - ); - if (currencyRecord) { - for (const trustedExchange of currencyRecord.exchanges) { - if ( - trustedExchange.exchangeMasterPub === exchangeDetails.masterPublicKey - ) { - isTrusted = true; - break; - } - } - for (const trustedAuditor of currencyRecord.auditors) { - for (const exchangeAuditor of exchangeDetails.auditors) { - if (trustedAuditor.auditorPub === exchangeAuditor.auditor_pub) { - isAudited = true; - break; - } - } - } - } - return { isTrusted, isAudited }; -} export async function getExchangePaytoUri( ws: InternalWalletState, diff --git a/packages/taler-wallet-core/src/operations/reserves.ts b/packages/taler-wallet-core/src/operations/reserves.ts index 9a479427e..9addca975 100644 --- a/packages/taler-wallet-core/src/operations/reserves.ts +++ b/packages/taler-wallet-core/src/operations/reserves.ts @@ -38,7 +38,6 @@ import { ReserveRecordStatus, ReserveBankInfo, ReserveRecord, - CurrencyRecord, WithdrawalGroupRecord, } from "../db.js"; import { @@ -158,31 +157,9 @@ export async function createReserve( throw Error("exchange not updated"); } const { isAudited, isTrusted } = await getExchangeTrust(ws, exchangeInfo); - let currencyRecord = await ws.db.get( - Stores.currencies, - exchangeDetails.currency, - ); - if (!currencyRecord) { - currencyRecord = { - auditors: [], - exchanges: [], - fractionalDigits: 2, - name: exchangeDetails.currency, - }; - } - - if (!isAudited && !isTrusted) { - currencyRecord.exchanges.push({ - exchangeBaseUrl: req.exchange, - exchangeMasterPub: exchangeDetails.masterPublicKey, - uids: [encodeCrock(getRandomBytes(32))], - }); - } - - const cr: CurrencyRecord = currencyRecord; const resp = await ws.db.runWithWriteTransaction( - [Stores.currencies, Stores.reserves, Stores.bankWithdrawUris], + [Stores.exchangeTrustStore, Stores.reserves, Stores.bankWithdrawUris], async (tx) => { // Check if we have already created a reserve for that bankWithdrawStatusUrl if (reserveRecord.bankInfo?.statusUrl) { @@ -207,7 +184,14 @@ export async function createReserve( talerWithdrawUri: reserveRecord.bankInfo.statusUrl, }); } - await tx.put(Stores.currencies, cr); + if (!isAudited && !isAudited) { + await tx.put(Stores.exchangeTrustStore, { + currency: reserveRecord.currency, + exchangeBaseUrl: reserveRecord.exchangeBaseUrl, + exchangeMasterPub: exchangeDetails.masterPublicKey, + uids: [encodeCrock(getRandomBytes(32))], + }); + } await tx.put(Stores.reserves, reserveRecord); const r: CreateReserveResponse = { exchange: canonExchange, diff --git a/packages/taler-wallet-core/src/operations/transactions.ts b/packages/taler-wallet-core/src/operations/transactions.ts index 8ee02c059..dcd3ddbd9 100644 --- a/packages/taler-wallet-core/src/operations/transactions.ts +++ b/packages/taler-wallet-core/src/operations/transactions.ts @@ -82,7 +82,6 @@ export async function getTransactions( await ws.db.runWithReadTransaction( [ - Stores.currencies, Stores.coins, Stores.denominations, Stores.exchanges, diff --git a/packages/taler-wallet-core/src/operations/withdraw.ts b/packages/taler-wallet-core/src/operations/withdraw.ts index 237ef9fc6..0ff69cb5a 100644 --- a/packages/taler-wallet-core/src/operations/withdraw.ts +++ b/packages/taler-wallet-core/src/operations/withdraw.ts @@ -51,7 +51,7 @@ import { } from "@gnu-taler/taler-util"; import { InternalWalletState } from "./state"; import { Logger } from "../util/logging"; -import { updateExchangeFromUrl, getExchangeTrust } from "./exchanges"; +import { updateExchangeFromUrl } from "./exchanges"; import { WALLET_EXCHANGE_PROTOCOL_VERSION, WALLET_BANK_INTEGRATION_PROTOCOL_VERSION, @@ -76,6 +76,7 @@ import { TalerErrorCode } from "@gnu-taler/taler-util"; import { updateRetryInfoTimeout, initRetryInfo } from "../util/retries"; import { compare } from "@gnu-taler/taler-util"; import { walletCoreDebugFlags } from "../util/debugFlags.js"; +import { getExchangeTrust } from "./currencies.js"; /** * Logger for this file. @@ -882,14 +883,6 @@ export async function getExchangeWithdrawalInfo( .iterIndex(Stores.denominations.exchangeBaseUrlIndex, baseUrl) .filter((d) => d.isOffered); - const trustedAuditorPubs = []; - const currencyRecord = await ws.db.get(Stores.currencies, amount.currency); - if (currencyRecord) { - trustedAuditorPubs.push( - ...currencyRecord.auditors.map((a) => a.auditorPub), - ); - } - let versionMatch; if (exchangeDetails.protocolVersion) { versionMatch = LibtoolVersion.compare( @@ -935,7 +928,8 @@ export async function getExchangeWithdrawalInfo( numOfferedDenoms: possibleDenoms.length, overhead: Amounts.sub(amount, selectedDenoms.totalWithdrawCost).amount, selectedDenoms, - trustedAuditorPubs, + // FIXME: delete this field / replace by something we can display to the user + trustedAuditorPubs: [], versionMatch, walletVersion: WALLET_EXCHANGE_PROTOCOL_VERSION, wireFees: exchangeWireInfo, diff --git a/packages/taler-wallet-core/src/wallet.ts b/packages/taler-wallet-core/src/wallet.ts index f69d26e84..192b54926 100644 --- a/packages/taler-wallet-core/src/wallet.ts +++ b/packages/taler-wallet-core/src/wallet.ts @@ -26,6 +26,7 @@ import { BackupRecovery, codecForAny, TalerErrorCode, + WalletCurrencyInfo, } from "@gnu-taler/taler-util"; import { CryptoWorkerFactory } from "./crypto/workers/cryptoApi"; import { @@ -56,7 +57,6 @@ import { import { acceptExchangeTermsOfService, getExchangePaytoUri, - getExchangeTrust, updateExchangeFromUrl, } from "./operations/exchanges"; import { @@ -99,9 +99,9 @@ import { processWithdrawGroup, } from "./operations/withdraw"; import { + AuditorTrustRecord, CoinRecord, CoinSourceType, - CurrencyRecord, DenominationRecord, ExchangeRecord, PurchaseRecord, @@ -179,19 +179,14 @@ import { AsyncCondition } from "./util/promiseUtils"; import { Database } from "./util/query"; import { Duration, durationMin } from "@gnu-taler/taler-util"; import { TimerGroup } from "./util/timer"; +import { getExchangeTrust } from "./operations/currencies.js"; -const builtinCurrencies: CurrencyRecord[] = [ +const builtinAuditors: AuditorTrustRecord[] = [ { - auditors: [ - { - auditorPub: "BW9DC48PHQY4NH011SHHX36DZZ3Q22Y6X7FZ1VD1CMZ2PTFZ6PN0", - auditorBaseUrl: "https://auditor.demo.taler.net/", - uids: ["5P25XF8TVQP9AW6VYGY2KV47WT5Y3ZXFSJAA570GJPX5SVJXKBVG"], - }, - ], - exchanges: [], - fractionalDigits: 2, - name: "KUDOS", + currency: "KUDOS", + auditorPub: "BW9DC48PHQY4NH011SHHX36DZZ3Q22Y6X7FZ1VD1CMZ2PTFZ6PN0", + auditorBaseUrl: "https://auditor.demo.taler.net/", + uids: ["5P25XF8TVQP9AW6VYGY2KV47WT5Y3ZXFSJAA570GJPX5SVJXKBVG"], }, ]; @@ -484,7 +479,7 @@ export class Wallet { */ async fillDefaults(): Promise { await this.db.runWithWriteTransaction( - [Stores.config, Stores.currencies], + [Stores.config, Stores.auditorTrustStore], async (tx) => { let applied = false; await tx.iter(Stores.config).forEach((x) => { @@ -493,8 +488,8 @@ export class Wallet { } }); if (!applied) { - for (const c of builtinCurrencies) { - await tx.put(Stores.currencies, c); + for (const c of builtinAuditors) { + await tx.put(Stores.auditorTrustStore, c); } } }, @@ -676,7 +671,6 @@ export class Wallet { return await this.db.iter(Stores.exchanges).toArray(); } - async getExchanges(): Promise { const exchanges: (ExchangeListItem | undefined)[] = await this.db .iter(Stores.exchanges) @@ -702,13 +696,25 @@ export class Wallet { }; } - async getCurrencies(): Promise { - return await this.db.iter(Stores.currencies).toArray(); - } - - async updateCurrency(currencyRecord: CurrencyRecord): Promise { - logger.trace("updating currency to", currencyRecord); - await this.db.put(Stores.currencies, currencyRecord); + async getCurrencies(): Promise { + const trustedAuditors = await this.db + .iter(Stores.auditorTrustStore) + .toArray(); + const trustedExchanges = await this.db + .iter(Stores.exchangeTrustStore) + .toArray(); + return { + trustedAuditors: trustedAuditors.map((x) => ({ + currency: x.currency, + auditorBaseUrl: x.auditorBaseUrl, + auditorPub: x.auditorPub, + })), + trustedExchanges: trustedExchanges.map((x) => ({ + currency: x.currency, + exchangeBaseUrl: x.exchangeBaseUrl, + exchangeMasterPub: x.exchangeMasterPub, + })), + }; } async getReserves(exchangeBaseUrl?: string): Promise {