diff options
author | Özgür Kesim <oec-taler@kesim.org> | 2023-09-09 07:34:11 +0200 |
---|---|---|
committer | Özgür Kesim <oec-taler@kesim.org> | 2023-09-09 07:34:11 +0200 |
commit | 5495551071a3fdc36c38deb4c1cf6f4aa5b98bd4 (patch) | |
tree | adf7730b190618a0499e50a2d43cf1b850cddd16 /packages/taler-wallet-core/src | |
parent | 94cfcc875065f988815c31aaf8ebf36f75ac5983 (diff) | |
parent | 6c3cfa9be7a332c2cc8490f25ebd6c73c8244842 (diff) |
Merge branch 'master' into age-withdraw
Diffstat (limited to 'packages/taler-wallet-core/src')
32 files changed, 1465 insertions, 2330 deletions
diff --git a/packages/taler-wallet-core/src/bank-api-client.ts b/packages/taler-wallet-core/src/bank-api-client.ts deleted file mode 100644 index 3174667f1..000000000 --- a/packages/taler-wallet-core/src/bank-api-client.ts +++ /dev/null @@ -1,500 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2022 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 <http://www.gnu.org/licenses/> - */ - -/** - * Client for the Taler (demo-)bank. - */ - -/** - * Imports. - */ -import { - AmountString, - base64FromArrayBuffer, - buildCodecForObject, - Codec, - codecForAny, - codecForString, - encodeCrock, - generateIban, - getRandomBytes, - j2s, - Logger, - stringToBytes, - TalerError, - TalerErrorCode, -} from "@gnu-taler/taler-util"; -import { - checkSuccessResponseOrThrow, - createPlatformHttpLib, - HttpRequestLibrary, - readSuccessResponseJsonOrThrow, -} from "@gnu-taler/taler-util/http"; - -const logger = new Logger("bank-api-client.ts"); - -export enum CreditDebitIndicator { - Credit = "credit", - Debit = "debit", -} - -export interface BankAccountBalanceResponse { - balance: { - amount: AmountString; - credit_debit_indicator: CreditDebitIndicator; - }; -} - -export interface BankServiceHandle { - readonly bankAccessApiBaseUrl: string; - readonly http: HttpRequestLibrary; -} - -export interface BankUser { - username: string; - password: string; - accountPaytoUri: string; -} - -export interface WithdrawalOperationInfo { - withdrawal_id: string; - taler_withdraw_uri: string; -} - -/** - * FIXME: Rename, this is not part of the integration test harness anymore. - */ -export interface HarnessExchangeBankAccount { - accountName: string; - accountPassword: string; - accountPaytoUri: string; - wireGatewayApiBaseUrl: string; -} - -/** - * Helper function to generate the "Authorization" HTTP header. - */ -function makeBasicAuthHeader(username: string, password: string): string { - const auth = `${username}:${password}`; - const authEncoded: string = base64FromArrayBuffer(stringToBytes(auth)); - return `Basic ${authEncoded}`; -} - -const codecForWithdrawalOperationInfo = (): Codec<WithdrawalOperationInfo> => - buildCodecForObject<WithdrawalOperationInfo>() - .property("withdrawal_id", codecForString()) - .property("taler_withdraw_uri", codecForString()) - .build("WithdrawalOperationInfo"); - -/** - * @deprecated Use BankAccessApiClient or WireGatewayApi - */ -export namespace BankApi { - // FIXME: Move to BankAccessApi?! - export async function registerAccount( - bank: BankServiceHandle, - username: string, - password: string, - options: { - iban?: string; - }, - ): Promise<BankUser> { - const url = new URL("testing/register", bank.bankAccessApiBaseUrl); - const resp = await bank.http.postJson(url.href, { - username, - password, - iban: options?.iban, - }); - let paytoUri = `payto://x-taler-bank/localhost/${username}`; - if (resp.status !== 200 && resp.status !== 202 && resp.status !== 204) { - logger.error(`${j2s(await resp.json())}`); - throw TalerError.fromDetail( - TalerErrorCode.GENERIC_UNEXPECTED_REQUEST_ERROR, - { - httpStatusCode: resp.status, - }, - ); - } - try { - // Pybank has no body, thus this might throw. - const respJson = await resp.json(); - // LibEuFin demobank returns payto URI in response - if (respJson.paytoUri) { - paytoUri = respJson.paytoUri; - } - } catch (e) { - // Do nothing - } - return { - password, - username, - accountPaytoUri: paytoUri, - }; - } - - // FIXME: Move to BankAccessApi?! - export async function createRandomBankUser( - bank: BankServiceHandle, - ): Promise<BankUser> { - const username = "user-" + encodeCrock(getRandomBytes(10)).toLowerCase(); - const password = "pw-" + encodeCrock(getRandomBytes(10)).toLowerCase(); - // FIXME: This is just a temporary workaround, because demobank is running out of short IBANs - const iban = generateIban("DE", 15); - return await registerAccount(bank, username, password, { - iban, - }); - } - - export async function confirmWithdrawalOperation( - bank: BankServiceHandle, - bankUser: BankUser, - wopi: WithdrawalOperationInfo, - ): Promise<void> { - const url = new URL( - `accounts/${bankUser.username}/withdrawals/${wopi.withdrawal_id}/confirm`, - bank.bankAccessApiBaseUrl, - ); - logger.info(`confirming withdrawal operation via ${url.href}`); - const resp = await bank.http.postJson( - url.href, - {}, - { - headers: { - Authorization: makeBasicAuthHeader( - bankUser.username, - bankUser.password, - ), - }, - }, - ); - - logger.info(`response status ${resp.status}`); - const respJson = await readSuccessResponseJsonOrThrow(resp, codecForAny()); - - // FIXME: We don't check the status here! - } - - export async function abortWithdrawalOperation( - bank: BankServiceHandle, - bankUser: BankUser, - wopi: WithdrawalOperationInfo, - ): Promise<void> { - const url = new URL( - `accounts/${bankUser.username}/withdrawals/${wopi.withdrawal_id}/abort`, - bank.bankAccessApiBaseUrl, - ); - const resp = await bank.http.postJson( - url.href, - {}, - { - headers: { - Authorization: makeBasicAuthHeader( - bankUser.username, - bankUser.password, - ), - }, - }, - ); - await readSuccessResponseJsonOrThrow(resp, codecForAny()); - } -} - -/** - * @deprecated use BankAccessApiClient - */ -export namespace BankAccessApi { - export async function getAccountBalance( - bank: BankServiceHandle, - bankUser: BankUser, - ): Promise<BankAccountBalanceResponse> { - const url = new URL( - `accounts/${bankUser.username}`, - bank.bankAccessApiBaseUrl, - ); - const resp = await bank.http.fetch(url.href, { - headers: { - Authorization: makeBasicAuthHeader( - bankUser.username, - bankUser.password, - ), - }, - }); - return await resp.json(); - } - - export async function createWithdrawalOperation( - bank: BankServiceHandle, - bankUser: BankUser, - amount: string, - ): Promise<WithdrawalOperationInfo> { - const url = new URL( - `accounts/${bankUser.username}/withdrawals`, - bank.bankAccessApiBaseUrl, - ); - const resp = await bank.http.postJson( - url.href, - { - amount, - }, - { - headers: { - Authorization: makeBasicAuthHeader( - bankUser.username, - bankUser.password, - ), - }, - }, - ); - return readSuccessResponseJsonOrThrow( - resp, - codecForWithdrawalOperationInfo(), - ); - } -} - -export interface BankAccessApiClientArgs { - baseUrl: string; - auth?: { username: string; password: string }; - enableThrottling?: boolean; - allowHttp?: boolean; -} - -export interface BankAccessApiCreateTransactionRequest { - amount: AmountString; - paytoUri: string; -} - -export class WireGatewayApiClientArgs { - accountName: string; - accountPassword: string; - wireGatewayApiBaseUrl: string; - enableThrottling?: boolean; - allowHttp?: boolean; -} - -/** - * This API look like it belongs to harness - * but it will be nice to have in utils to be used by others - */ -export class WireGatewayApiClient { - httpLib; - - constructor(private args: WireGatewayApiClientArgs) { - this.httpLib = createPlatformHttpLib({ - enableThrottling: !!args.enableThrottling, - allowHttp: !!args.allowHttp, - }); - } - - async adminAddIncoming(params: { - amount: string; - reservePub: string; - debitAccountPayto: string; - }): Promise<void> { - let url = new URL(`admin/add-incoming`, this.args.wireGatewayApiBaseUrl); - const resp = await this.httpLib.fetch(url.href, { - method: "POST", - body: { - amount: params.amount, - reserve_pub: params.reservePub, - debit_account: params.debitAccountPayto, - }, - headers: { - Authorization: makeBasicAuthHeader( - this.args.accountName, - this.args.accountPassword, - ), - }, - }); - logger.info(`add-incoming response status: ${resp.status}`); - await checkSuccessResponseOrThrow(resp); - } -} - -/** - * This API look like it belongs to harness - * but it will be nice to have in utils to be used by others - */ -export class BankAccessApiClient { - httpLib: HttpRequestLibrary; - - constructor(private args: BankAccessApiClientArgs) { - this.httpLib = createPlatformHttpLib({ - enableThrottling: !!args.enableThrottling, - allowHttp: !!args.allowHttp, - }); - } - - setAuth(auth: { username: string; password: string }) { - this.args.auth = auth; - } - - private makeAuthHeader(): Record<string, string> { - if (!this.args.auth) { - return {}; - } - const authHeaderValue = makeBasicAuthHeader( - this.args.auth.username, - this.args.auth.password, - ); - return { - Authorization: authHeaderValue, - }; - } - - async getTransactions(username: string): Promise<void> { - const auth = this.args.auth; - const reqUrl = new URL( - `accounts/${username}/transactions`, - this.args.baseUrl, - ); - const resp = await this.httpLib.fetch(reqUrl.href, { - method: "GET", - headers: { - ...this.makeAuthHeader(), - }, - }); - - const res = await readSuccessResponseJsonOrThrow(resp, codecForAny()); - logger.info(`result: ${j2s(res)}`); - } - - async createTransaction( - username: string, - req: BankAccessApiCreateTransactionRequest, - ): Promise<any> { - const reqUrl = new URL( - `accounts/${username}/transactions`, - this.args.baseUrl, - ); - - const resp = await this.httpLib.fetch(reqUrl.href, { - method: "POST", - body: req, - headers: this.makeAuthHeader(), - }); - - return await readSuccessResponseJsonOrThrow(resp, codecForAny()); - } - - async registerAccount( - username: string, - password: string, - options: { - iban?: string; - }, - ): Promise<BankUser> { - const url = new URL("testing/register", this.args.baseUrl); - const resp = await this.httpLib.fetch(url.href, { - method: "POST", - body: { - username, - password, - iban: options?.iban, - }, - }); - let paytoUri = `payto://x-taler-bank/localhost/${username}`; - if (resp.status !== 200 && resp.status !== 202 && resp.status !== 204) { - logger.error(`${j2s(await resp.json())}`); - throw TalerError.fromDetail( - TalerErrorCode.GENERIC_UNEXPECTED_REQUEST_ERROR, - { - httpStatusCode: resp.status, - }, - ); - } - try { - // Pybank has no body, thus this might throw. - const respJson = await resp.json(); - // LibEuFin demobank returns payto URI in response - if (respJson.paytoUri) { - paytoUri = respJson.paytoUri; - } - } catch (e) { - // Do nothing - } - return { - password, - username, - accountPaytoUri: paytoUri, - }; - } - - async createRandomBankUser(): Promise<BankUser> { - const username = "user-" + encodeCrock(getRandomBytes(10)).toLowerCase(); - const password = "pw-" + encodeCrock(getRandomBytes(10)).toLowerCase(); - // FIXME: This is just a temporary workaround, because demobank is running out of short IBANs - const iban = generateIban("DE", 15); - return await this.registerAccount(username, password, { - iban, - }); - } - - async createWithdrawalOperation( - user: string, - amount: string, - ): Promise<WithdrawalOperationInfo> { - const url = new URL(`accounts/${user}/withdrawals`, this.args.baseUrl); - const resp = await this.httpLib.fetch(url.href, { - method: "POST", - body: { - amount, - }, - headers: this.makeAuthHeader(), - }); - return readSuccessResponseJsonOrThrow( - resp, - codecForWithdrawalOperationInfo(), - ); - } - - async confirmWithdrawalOperation( - username: string, - wopi: WithdrawalOperationInfo, - ): Promise<void> { - const url = new URL( - `accounts/${username}/withdrawals/${wopi.withdrawal_id}/confirm`, - this.args.baseUrl, - ); - logger.info(`confirming withdrawal operation via ${url.href}`); - const resp = await this.httpLib.fetch(url.href, { - method: "POST", - body: {}, - headers: this.makeAuthHeader(), - }); - - logger.info(`response status ${resp.status}`); - const respJson = await readSuccessResponseJsonOrThrow(resp, codecForAny()); - - // FIXME: We don't check the status here! - } - - async abortWithdrawalOperation( - accountName: string, - wopi: WithdrawalOperationInfo, - ): Promise<void> { - const url = new URL( - `accounts/${accountName}/withdrawals/${wopi.withdrawal_id}/abort`, - this.args.baseUrl, - ); - const resp = await this.httpLib.fetch(url.href, { - method: "POST", - body: {}, - headers: this.makeAuthHeader(), - }); - await readSuccessResponseJsonOrThrow(resp, codecForAny()); - } -} diff --git a/packages/taler-wallet-core/src/crypto/cryptoImplementation.ts b/packages/taler-wallet-core/src/crypto/cryptoImplementation.ts index c1a761fb6..35777e714 100644 --- a/packages/taler-wallet-core/src/crypto/cryptoImplementation.ts +++ b/packages/taler-wallet-core/src/crypto/cryptoImplementation.ts @@ -959,11 +959,7 @@ export const nativeCryptoR: TalerCryptoInterfaceR = { req: DenominationValidationRequest, ): Promise<ValidationResult> { const { masterPub, denom } = req; - const value: AmountJson = { - currency: denom.currency, - fraction: denom.amountFrac, - value: denom.amountVal, - }; + const value: AmountJson = Amounts.parseOrThrow(denom.value); const p = buildSigPS(TalerSignaturePurpose.MASTER_DENOMINATION_KEY_VALIDITY) .put(decodeCrock(masterPub)) .put(timestampRoundedToBuffer(denom.stampStart)) diff --git a/packages/taler-wallet-core/src/db.ts b/packages/taler-wallet-core/src/db.ts index b52a503bc..25757ef25 100644 --- a/packages/taler-wallet-core/src/db.ts +++ b/packages/taler-wallet-core/src/db.ts @@ -22,59 +22,59 @@ import { IDBDatabase, IDBFactory, IDBObjectStore, + IDBRequest, IDBTransaction, structuredEncapsulate, } from "@gnu-taler/idb-bridge"; import { AgeCommitmentProof, - AmountJson, AmountString, + Amounts, + AttentionInfo, + Codec, CoinEnvelope, + CoinPublicKeyString, CoinRefreshRequest, CoinStatus, - MerchantContractTerms, + DenomSelectionState, DenominationInfo, DenominationPubKey, - DenomSelectionState, EddsaPublicKeyString, EddsaSignatureString, ExchangeAuditor, ExchangeGlobalFees, + HashCodeString, InternationalizedString, - Location, + Logger, + MerchantContractTerms, MerchantInfo, PayCoinSelection, PeerContractTerms, - Product, RefreshReason, TalerErrorDetail, + TalerPreciseTimestamp, TalerProtocolDuration, TalerProtocolTimestamp, TransactionIdStr, UnblindedSignature, WireInfo, - HashCodeString, - Amounts, - AttentionInfo, - Logger, - CoinPublicKeyString, - TalerPreciseTimestamp, + codecForAny, } from "@gnu-taler/taler-util"; +import { RetryInfo, TaskIdentifiers } from "./operations/common.js"; import { DbAccess, DbReadOnlyTransaction, DbReadWriteTransaction, - describeContents, - describeIndex, - describeStore, GetReadWriteAccess, IndexDescriptor, - openDatabase, StoreDescriptor, StoreNames, StoreWithIndexes, + describeContents, + describeIndex, + describeStore, + openDatabase, } from "./util/query.js"; -import { RetryInfo, TaskIdentifiers } from "./operations/common.js"; /** * This file contains the database schema of the Taler wallet together @@ -99,12 +99,25 @@ import { RetryInfo, TaskIdentifiers } from "./operations/common.js"; */ /** + FIXMEs: + - Contract terms can be quite large. We currently tend to read the + full contract terms from the DB quite often. + Instead, we should probably extract what we need into a separate object + store. + - More object stores should have an "id" primary key, + as this makes referencing less expensive. + - Coin selections should probably go into a separate object store. + - Some records should be split up into an extra "details" record + that we don't always need to iterate over. + */ + +/** * Name of the Taler database. This is effectively the major * version of the DB schema. Whenever it changes, custom import logic * for all previous versions must be written, which should be * avoided. */ -export const TALER_WALLET_MAIN_DB_NAME = "taler-wallet-main-v9"; +export const TALER_WALLET_MAIN_DB_NAME = "taler-wallet-main-v10"; /** * Name of the metadata database. This database is used @@ -115,10 +128,17 @@ export const TALER_WALLET_MAIN_DB_NAME = "taler-wallet-main-v9"; export const TALER_WALLET_META_DB_NAME = "taler-wallet-meta"; /** - * Stored backups, mainly created when manually importing a backup. + * Name of the "stored backups" database. + * Stored backups are created before manually importing a backup. + * We use IndexedDB for this purpose, since we don't have file system + * access on some platforms. */ -export const TALER_WALLET_STORED_BACKUPS_DB_NAME = "taler-wallet-stored-backups"; +export const TALER_WALLET_STORED_BACKUPS_DB_NAME = + "taler-wallet-stored-backups"; +/** + * Name of the "meta config" database. + */ export const CURRENT_DB_CONFIG_KEY = "currentMainDbName"; /** @@ -128,26 +148,26 @@ export const CURRENT_DB_CONFIG_KEY = "currentMainDbName"; * backwards-compatible way or object stores and indices * are added. */ -export const WALLET_DB_MINOR_VERSION = 10; +export const WALLET_DB_MINOR_VERSION = 1; /** - * Ranges for operation status fields. - * - * All individual enums should make sure that the values they - * defined are in the right range. + * Format of the operation status code: 0x0abc_nnnn + + * a=1: active + * 0x0100_nnnn: pending + * 0x0101_nnnn: dialog + * 0x0102_nnnn: (reserved) + * 0x0103_nnnn: aborting + * 0x0110_nnnn: suspended + * 0x0113_nnnn: suspended-aborting + * a=5: final + * 0x0500_nnnn: done + * 0x0501_nnnn: failed + * 0x0502_nnnn: expired + * 0x0503_nnnn: aborted + * + * nnnn=0000 should always be the most generic minor state for the major state */ -export enum OperationStatusRange { - // Operations that need to be actively processed. - ACTIVE_START = 10, - ACTIVE_END = 29, - // Operations that are suspended and might - // expire, but nothing else can be done. - SUSPENDED_START = 30, - SUSPENDED_END = 49, - // Operations that don't need any attention or processing. - DORMANT_START = 50, - DORMANT_END = 69, -} /** * Status of a withdrawal. @@ -156,71 +176,70 @@ export enum WithdrawalGroupStatus { /** * Reserve must be registered with the bank. */ - PendingRegisteringBank = 10, + PendingRegisteringBank = 0x0100_0001, + SuspendedRegisteringBank = 0x0110_0001, /** * We've registered reserve's information with the bank * and are now waiting for the user to confirm the withdraw * with the bank (typically 2nd factor auth). */ - PendingWaitConfirmBank = 11, + PendingWaitConfirmBank = 0x0100_0002, + SuspendedWaitConfirmBank = 0x0110_0002, /** * Querying reserve status with the exchange. */ - PendingQueryingStatus = 12, + PendingQueryingStatus = 0x0100_0003, + SuspendedQueryingStatus = 0x0110_0003, /** * Ready for withdrawal. */ - PendingReady = 13, + PendingReady = 0x0100_0004, + SuspendedReady = 0x0110_0004, /** * We are telling the bank that we don't want to complete * the withdrawal! */ - AbortingBank = 14, + AbortingBank = 0x0103_0001, + SuspendedAbortingBank = 0x0113_0001, /** * Exchange wants KYC info from the user. */ - PendingKyc = 16, + PendingKyc = 0x0100_0005, + SuspendedKyc = 0x0110_005, /** * Exchange is doing AML checks. */ - PendingAml = 17, - - SuspendedRegisteringBank = 30, - SuspendedWaitConfirmBank = 31, - SuspendedQueryingStatus = 32, - SuspendedReady = 33, - SuspendedAbortingBank = 34, - SuspendedKyc = 35, - SuspendedAml = 36, + PendingAml = 0x0100_0006, + SuspendedAml = 0x0100_0006, /** * The corresponding withdraw record has been created. * No further processing is done, unless explicitly requested * by the user. */ - Finished = 50, + Done = 0x0500_0000, /** * The bank aborted the withdrawal. */ - FailedBankAborted = 51, + FailedBankAborted = 0x0501_0001, - FailedAbortingBank = 59, + FailedAbortingBank = 0x0501_0002, /** * Aborted in a state where we were supposed to * talk to the exchange. Money might have been * wired or not. */ - AbortedExchange = 60, + AbortedExchange = 0x0503_0001, - AbortedBank = 61, + AbortedBank = 0x0503_0002, } /** @@ -259,78 +278,23 @@ export interface ReserveBankInfo { } /** - * Record that indicates the wallet trusts - * a particular auditor. - */ -export interface AuditorTrustRecord { - /** - * Currency that we trust this auditor for. - */ - currency: string; - - /** - * Base URL of the auditor. - */ - auditorBaseUrl: string; - - /** - * Public key of the auditor. - */ - auditorPub: string; - - /** - * UIDs for the operation of adding this auditor - * as a trusted auditor. - * - * (Used for backup/sync merging and tombstones.) - */ - uids: string[]; -} - -/** - * Record to indicate trust for a particular exchange. - */ -export interface ExchangeTrustRecord { - /** - * Currency that we trust this exchange for. - */ - currency: string; - - /** - * Canonicalized exchange base URL. - */ - exchangeBaseUrl: string; - - /** - * Master public key of the exchange. - */ - exchangeMasterPub: string; - - /** - * UIDs for the operation of adding this exchange - * as trusted. - */ - uids: string[]; -} - -/** * Status of a denomination. */ export enum DenominationVerificationStatus { /** - * Verification was delayed. + * Verification was delayed (pending). */ - Unverified = OperationStatusRange.ACTIVE_START, + Unverified = 0x0100_0000, /** * Verified as valid. */ - VerifiedGood = OperationStatusRange.DORMANT_START, + VerifiedGood = 0x0500_0000, /** * Verified as invalid. */ - VerifiedBad = OperationStatusRange.DORMANT_START + 1, + VerifiedBad = 0x0501_0000, } export interface DenomFees { @@ -359,12 +323,14 @@ export interface DenomFees { * Denomination record as stored in the wallet's database. */ export interface DenominationRecord { + /** + * Currency of the denomination. + * + * Stored separately as we have an index on it. + */ currency: string; - // FIXME: Use binary encoding of amount instead? - amountVal: number; - - amountFrac: number; + value: AmountString; /** * The denomination public key. @@ -443,14 +409,6 @@ export interface DenominationRecord { } export namespace DenominationRecord { - export function getValue(d: DenominationRecord): AmountJson { - return { - currency: d.currency, - fraction: d.amountFrac, - value: d.amountVal, - }; - } - export function toDenomInfo(d: DenominationRecord): DenominationInfo { return { denomPub: d.denomPub, @@ -463,7 +421,7 @@ export namespace DenominationRecord { stampExpireLegal: d.stampExpireLegal, stampExpireWithdraw: d.stampExpireWithdraw, stampStart: d.stampStart, - value: Amounts.stringify(DenominationRecord.getValue(d)), + value: Amounts.stringify(d.value), exchangeBaseUrl: d.exchangeBaseUrl, }; } @@ -578,6 +536,7 @@ export enum ExchangeEntryDbRecordStatus { Used = 3, } +// FIXME: Use status ranges for this as well? export enum ExchangeEntryDbUpdateStatus { Initial = 1, InitialUpdate = 2, @@ -658,9 +617,9 @@ export interface ExchangeEntryRecord { } export enum PlanchetStatus { - Pending = 10 /* ACTIVE_START */, - KycRequired = 11 /* ACTIVE_START + 1 */, - WithdrawalDone = 50 /* DORMANT_START */, + Pending = 0x0100_0000, + KycRequired = 0x0100_0001, + WithdrawalDone = 0x0500_000, } /** @@ -933,50 +892,45 @@ export interface RewardRecord { } export enum RewardRecordStatus { - PendingPickup = 10, - - SuspendidPickup = 20, - - DialogAccept = 30, - - Done = 50, - Aborted = 51, + PendingPickup = 0x0100_0000, + SuspendedPickup = 0x0110_0000, + DialogAccept = 0x0101_0000, + Done = 0x0500_0000, + Aborted = 0x0500_0000, } export enum RefreshCoinStatus { - Pending = OperationStatusRange.ACTIVE_START, - Finished = OperationStatusRange.DORMANT_START, + Pending = 0x0100_0000, + Finished = 0x0500_0000, /** * The refresh for this coin has been frozen, because of a permanent error. * More info in lastErrorPerCoin. */ - Failed = OperationStatusRange.DORMANT_START + 1, -} - -export enum OperationStatus { - Finished = OperationStatusRange.DORMANT_START, - Pending = OperationStatusRange.ACTIVE_START, + Failed = 0x0501_000, } export enum RefreshOperationStatus { - Pending = 10 /* ACTIVE_START */, - Suspended = 20 /* DORMANT_START + 2 */, + Pending = 0x0100_0000, + Suspended = 0x0110_0000, - Finished = 50 /* DORMANT_START */, - Failed = 51 /* DORMANT_START + 1 */, + Finished = 0x0500_000, + Failed = 0x0501_000, } /** * Status of a single element of a deposit group. */ export enum DepositElementStatus { - Unknown = 10, - Accepted = 20, - KycRequired = 30, - Wired = 40, - RefundSuccess = 50, - RefundFailed = 51, + DepositPending = 0x0100_0000, + /** + * Accepted, but tracking. + */ + Tracking = 0x0100_0001, + KycRequired = 0x0100_0002, + Wired = 0x0500_0000, + RefundSuccess = 0x0503_0000, + RefundFailed = 0x0501_0000, } /** @@ -991,16 +945,10 @@ export interface RefreshReasonDetails { * Group of refresh operations. The refreshed coins do not * have to belong to the same exchange, but must have the same * currency. - * - * FIXME: Should include the currency as a top-level field, - * but we need to write a migration for that. */ export interface RefreshGroupRecord { operationStatus: RefreshOperationStatus; - // FIXME: Put this into a different object store? - lastErrorPerCoin: { [coinIndex: number]: TalerErrorDetail }; - /** * Unique, randomly generated identifier for this group of * refresh operations. @@ -1009,8 +957,6 @@ export interface RefreshGroupRecord { /** * Currency of this refresh group. - * - * FIXME: Write a migration to add this to earlier DB versions. */ currency: string; @@ -1026,13 +972,9 @@ export interface RefreshGroupRecord { oldCoinPubs: string[]; - // FIXME: Should this go into a separate - // object store for faster updates? - refreshSessionPerCoin: (RefreshSessionRecord | undefined)[]; - inputPerCoin: AmountString[]; - estimatedOutputPerCoin: AmountString[]; + expectedOutputPerCoin: AmountString[]; /** * Flag for each coin whether refreshing finished. @@ -1054,6 +996,13 @@ export interface RefreshGroupRecord { * Ongoing refresh */ export interface RefreshSessionRecord { + refreshGroupId: string; + + /** + * Index of the coin in the refresh group. + */ + coinIndex: number; + /** * 512-bit secret that can be used to derive * the other cryptographic material for the refresh session. @@ -1078,6 +1027,8 @@ export interface RefreshSessionRecord { * The no-reveal-index after we've done the melting. */ norevealIndex?: number; + + lastError?: TalerErrorDetail; } export enum RefundReason { @@ -1106,9 +1057,6 @@ export interface AllowedExchangeInfo { * processing in the wallet. */ export interface WalletContractData { - products?: Product[]; - summaryI18n: { [lang_tag: string]: string } | undefined; - /** * Fulfillment URL, or the empty string if the order has no fulfillment URL. * @@ -1126,6 +1074,7 @@ export interface WalletContractData { orderId: string; merchantBaseUrl: string; summary: string; + summaryI18n: { [lang_tag: string]: string } | undefined; autoRefund: TalerProtocolDuration | undefined; maxWireFee: AmountString; wireFeeAmortization: number; @@ -1137,89 +1086,90 @@ export interface WalletContractData { wireInfoHash: string; maxDepositFee: AmountString; minimumAge?: number; - deliveryDate: TalerProtocolTimestamp | undefined; - deliveryLocation: Location | undefined; } export enum PurchaseStatus { /** * Not downloaded yet. */ - PendingDownloadingProposal = 10, + PendingDownloadingProposal = 0x0100_0000, + SuspendedDownloadingProposal = 0x0110_0000, /** * The user has accepted the proposal. */ - PendingPaying = 11, + PendingPaying = 0x0100_0001, + SuspendedPaying = 0x0110_0001, /** * Currently in the process of aborting with a refund. */ - AbortingWithRefund = 12, + AbortingWithRefund = 0x0103_0000, + SuspendedAbortingWithRefund = 0x0113_0000, /** * Paying a second time, likely with different session ID */ - PendingPayingReplay = 13, + PendingPayingReplay = 0x0100_0002, + SuspendedPayingReplay = 0x0110_0002, /** * Query for refunds (until query succeeds). */ - PendingQueryingRefund = 14, + PendingQueryingRefund = 0x0100_0003, + SuspendedQueryingRefund = 0x0110_0003, /** * Query for refund (until auto-refund deadline is reached). */ - PendingQueryingAutoRefund = 15, + PendingQueryingAutoRefund = 0x0100_0004, + SuspendedQueryingAutoRefund = 0x0110_0004, - PendingAcceptRefund = 16, - - SuspendedDownloadingProposal = 20, - SuspendedPaying = 21, - SuspendedAbortingWithRefund = 22, - SuspendedPayingReplay = 23, - SuspendedQueryingRefund = 24, - SuspendedQueryingAutoRefund = 25, - SuspendedPendingAcceptRefund = 26, + PendingAcceptRefund = 0x0100_0005, + SuspendedPendingAcceptRefund = 0x0100_0005, /** * Proposal downloaded, but the user needs to accept/reject it. */ - DialogProposed = 30, + DialogProposed = 0x0101_0000, + /** * Proposal shared to other wallet or read from other wallet * the user needs to accept/reject it. */ - DialogShared = 31, + DialogShared = 0x0101_0001, /** * The user has rejected the proposal. */ - AbortedProposalRefused = 50, + AbortedProposalRefused = 0x0503_0000, /** * Downloading or processing the proposal has failed permanently. */ - FailedClaim = 51, + FailedClaim = 0x0501_0000, + + /** + * Payment was successful. + */ + Done = 0x0500_0000, /** * Downloaded proposal was detected as a re-purchase. */ - RepurchaseDetected = 52, + DoneRepurchaseDetected = 0x0500_0001, /** * The payment has been aborted. */ - AbortedIncompletePayment = 53, + AbortedIncompletePayment = 0x0503_0000, /** - * Payment was successful. + * Tried to abort, but aborting failed or was cancelled. */ - Done = 54, + FailedAbort = 0x0501_0001, - FailedAbort = 55, - - AbortedRefunded = 56, + AbortedRefunded = 0x0503_0000, } /** @@ -1334,14 +1284,6 @@ export interface PurchaseRecord { timestampAccept: TalerPreciseTimestamp | undefined; /** - * Pending refunds for the purchase. A refund is pending - * when the merchant reports a transient error from the exchange. - * - * FIXME: Put this into a separate object store? - */ - // refunds: { [refundKey: string]: WalletRefundItem }; - - /** * When was the last refund made? * Set to 0 if no refund was made on the purchase. */ @@ -1404,6 +1346,7 @@ export interface WalletBackupConfState { lastBackupNonce?: string; } +// FIXME: Should these be numeric codes? export const enum WithdrawalRecordType { BankManual = "bank-manual", BankIntegrated = "bank-integrated", @@ -1428,7 +1371,8 @@ export interface WgInfoBankManual { export interface WgInfoBankPeerPull { withdrawalType: WithdrawalRecordType.PeerPullCredit; - contractTerms: any; + // FIXME: include a transaction ID here? + /** * Needed to quickly construct the taler:// URI for the counterparty * without a join. @@ -1439,7 +1383,7 @@ export interface WgInfoBankPeerPull { export interface WgInfoBankPeerPush { withdrawalType: WithdrawalRecordType.PeerPushCredit; - contractTerms: any; + // FIXME: include a transaction ID here? } export interface WgInfoBankRecoup { @@ -1713,19 +1657,21 @@ export interface BackupProviderRecord { } export enum DepositOperationStatus { - PendingDeposit = 10, - Aborting = 11, - PendingTrack = 12, - PendingKyc = 13, + PendingDeposit = 0x0100_0000, + PendingTrack = 0x0100_0001, + PendingKyc = 0x0100_0002, + + Aborting = 0x0103_0000, - SuspendedDeposit = 20, - SuspendedAborting = 21, - SuspendedTrack = 22, - SuspendedKyc = 23, + SuspendedDeposit = 0x0110_0000, + SuspendedTrack = 0x0110_0001, + SuspendedKyc = 0x0110_0002, - Finished = 50, - Failed = 51, - Aborted = 52, + SuspendedAborting = 0x0113_0000, + + Finished = 0x0500_0000, + Failed = 0x0501_0000, + Aborted = 0x0503_0000, } export interface DepositTrackingInfo { @@ -1777,10 +1723,8 @@ export interface DepositGroupRecord { /** * The counterparty effective deposit amount. - * - * FIXME: If possible, rename to counterpartyEffectiveDepositAmount. */ - effectiveDepositAmount: AmountString; + counterpartyEffectiveDepositAmount: AmountString; timestampCreated: TalerPreciseTimestamp; @@ -1788,11 +1732,7 @@ export interface DepositGroupRecord { operationStatus: DepositOperationStatus; - // FIXME: Duplication between this and transactionPerCoin! - depositedPerCoin: boolean[]; - - // FIXME: Improve name! - transactionPerCoin: DepositElementStatus[]; + statusPerCoin: DepositElementStatus[]; /** * When the deposit transaction was aborted and @@ -1816,31 +1756,6 @@ export interface DepositKycInfo { exchangeBaseUrl: string; } -/** - * Record for a deposits that the wallet observed - * as a result of double spending, but which is not - * present in the wallet's own database otherwise. - */ -export interface GhostDepositGroupRecord { - /** - * When multiple deposits for the same contract terms hash - * have a different timestamp, we choose the earliest one. - */ - timestamp: TalerPreciseTimestamp; - - contractTermsHash: string; - - deposits: { - coinPub: string; - amount: AmountString; - timestamp: TalerProtocolTimestamp; - depositFee: AmountString; - merchantPub: string; - coinSig: string; - wireHash: string; - }[]; -} - export interface TombstoneRecord { /** * Tombstone ID, with the syntax "tmb:<type>:<key>". @@ -1848,24 +1763,24 @@ export interface TombstoneRecord { id: string; } -export enum PeerPushPaymentInitiationStatus { +export enum PeerPushDebitStatus { /** * Initiated, but no purse created yet. */ - PendingCreatePurse = 10 /* ACTIVE_START */, - PendingReady = 11, - AbortingDeletePurse = 12, - AbortingRefresh = 13, + PendingCreatePurse = 0x0100_0000 /* ACTIVE_START */, + PendingReady = 0x0100_0001, + AbortingDeletePurse = 0x0103_0000, + AbortingRefresh = 0x0103_0001, - SuspendedCreatePurse = 30, - SuspendedReady = 31, - SuspendedAbortingDeletePurse = 32, - SuspendedAbortingRefresh = 33, + SuspendedCreatePurse = 0x0110_0000, + SuspendedReady = 0x0110_0001, + SuspendedAbortingDeletePurse = 0x0113_0000, + SuspendedAbortingRefresh = 0x0113_0001, - Done = 50 /* DORMANT_START */, - Aborted = 51, - Failed = 52, - Expired = 53, + Done = 0x0500_0000, + Aborted = 0x0503_0000, + Failed = 0x0501_0000, + Expired = 0x0502_0000, } export interface PeerPushPaymentCoinSelection { @@ -1876,7 +1791,7 @@ export interface PeerPushPaymentCoinSelection { /** * Record for a push P2P payment that this wallet initiated. */ -export interface PeerPushPaymentInitiationRecord { +export interface PeerPushDebitRecord { /** * What exchange are funds coming from? */ @@ -1922,11 +1837,6 @@ export interface PeerPushPaymentInitiationRecord { */ contractEncNonce: string; - /** - * FIXME: Put those in a different object store! - */ - contractTerms: PeerContractTerms; - purseExpiration: TalerProtocolTimestamp; timestampCreated: TalerPreciseTimestamp; @@ -1936,32 +1846,34 @@ export interface PeerPushPaymentInitiationRecord { /** * Status of the peer push payment initiation. */ - status: PeerPushPaymentInitiationStatus; + status: PeerPushDebitStatus; } -export enum PeerPullPaymentInitiationStatus { - PendingCreatePurse = 10 /* ACTIVE_START */, +export enum PeerPullPaymentCreditStatus { + PendingCreatePurse = 0x0100_0000, /** * Purse created, waiting for the other party to accept the * invoice and deposit money into it. */ - PendingReady = 11 /* ACTIVE_START + 1 */, - PendingMergeKycRequired = 12 /* ACTIVE_START + 2 */, - PendingWithdrawing = 13, - AbortingDeletePurse = 14, + PendingReady = 0x0100_0001, + PendingMergeKycRequired = 0x0100_0002, + PendingWithdrawing = 0x0100_0003, + + AbortingDeletePurse = 0x0103_0000, + + SuspendedCreatePurse = 0x0110_0000, + SuspendedReady = 0x0110_0001, + SuspendedMergeKycRequired = 0x0110_0002, + SuspendedWithdrawing = 0x0110_0000, - SuspendedCreatePurse = 30, - SuspendedReady = 31, - SuspendedMergeKycRequired = 32, - SuspendedWithdrawing = 33, - SuspendedAbortingDeletePurse = 34, + SuspendedAbortingDeletePurse = 0x0113_0000, - Done = 50 /* DORMANT_START */, - Failed = 51, - Aborted = 52, + Done = 0x0500_0000, + Failed = 0x0501_0000, + Aborted = 0x0503_0000, } -export interface PeerPullPaymentInitiationRecord { +export interface PeerPullCreditRecord { /** * What exchange are we using for the payment request? */ @@ -1969,6 +1881,7 @@ export interface PeerPullPaymentInitiationRecord { /** * Amount requested. + * FIXME: What type of instructed amount is i? */ amount: AmountString; @@ -1999,11 +1912,6 @@ export interface PeerPullPaymentInitiationRecord { contractEncNonce: string; - /** - * FIXME: Put in separate object store! - */ - contractTerms: PeerContractTerms; - mergeTimestamp: TalerPreciseTimestamp; mergeReserveRowId: number; @@ -2011,7 +1919,7 @@ export interface PeerPullPaymentInitiationRecord { /** * Status of the peer pull payment initiation. */ - status: PeerPullPaymentInitiationStatus; + status: PeerPullPaymentCreditStatus; kycInfo?: KycPendingInfo; @@ -2020,24 +1928,24 @@ export interface PeerPullPaymentInitiationRecord { withdrawalGroupId: string | undefined; } -export enum PeerPushPaymentIncomingStatus { - PendingMerge = 10 /* ACTIVE_START */, - PendingMergeKycRequired = 11 /* ACTIVE_START + 1 */, +export enum PeerPushCreditStatus { + PendingMerge = 0x0100_0000, + PendingMergeKycRequired = 0x0100_0001, /** * Merge was successful and withdrawal group has been created, now * everything is in the hand of the withdrawal group. */ - PendingWithdrawing = 12, + PendingWithdrawing = 0x0100_0002, - SuspendedMerge = 20, - SuspendedMergeKycRequired = 21, - SuspendedWithdrawing = 22, + SuspendedMerge = 0x0110_0000, + SuspendedMergeKycRequired = 0x0110_0001, + SuspendedWithdrawing = 0x0110_0002, - DialogProposed = 30 /* USER_ATTENTION_START */, + DialogProposed = 0x0101_0000, - Done = 50 /* DORMANT_START */, - Aborted = 51, - Failed = 52, + Done = 0x0500_0000, + Aborted = 0x0503_0000, + Failed = 0x0501_0000, } /** @@ -2046,7 +1954,7 @@ export enum PeerPushPaymentIncomingStatus { * Unique: (exchangeBaseUrl, pursePub) */ export interface PeerPushPaymentIncomingRecord { - peerPushPaymentIncomingId: string; + peerPushCreditId: string; exchangeBaseUrl: string; @@ -2069,7 +1977,7 @@ export interface PeerPushPaymentIncomingRecord { /** * Status of the peer push payment incoming initiation. */ - status: PeerPushPaymentIncomingStatus; + status: PeerPushCreditStatus; /** * Associated withdrawal group. @@ -2090,17 +1998,17 @@ export interface PeerPushPaymentIncomingRecord { } export enum PeerPullDebitRecordStatus { - PendingDeposit = 10 /* ACTIVE_START */, - AbortingRefresh = 11, + PendingDeposit = 0x0100_0001, + AbortingRefresh = 0x0103_0001, - SuspendedDeposit = 20, - SuspendedAbortingRefresh = 21, + SuspendedDeposit = 0x0110_0001, + SuspendedAbortingRefresh = 0x0113_0001, - DialogProposed = 30 /* USER_ATTENTION_START */, + DialogProposed = 0x0101_0001, - DonePaid = 50 /* DORMANT_START */, - Aborted = 51, - Failed = 52, + Done = 0x0500_0000, + Aborted = 0x0503_0000, + Failed = 0x0501_0000, } export interface PeerPullPaymentCoinSelection { @@ -2118,7 +2026,7 @@ export interface PeerPullPaymentCoinSelection { * AKA PeerPullDebit. */ export interface PeerPullPaymentIncomingRecord { - peerPullPaymentIncomingId: string; + peerPullDebitId: string; pursePub: string; @@ -2184,8 +2092,7 @@ export interface OperationRetryRecord { */ export interface CoinAvailabilityRecord { currency: string; - amountVal: number; - amountFrac: number; + value: AmountString; denomPubHash: string; exchangeBaseUrl: string; @@ -2263,10 +2170,10 @@ export interface CurrencySettingsRecord { } export enum RefundGroupStatus { - Pending = 10, - Done = 50, - Failed = 51, - Aborted = 52, + Pending = 0x0100_0000, + Done = 0x0500_0000, + Failed = 0x0501_0000, + Aborted = 0x0503_0000, } /** @@ -2302,16 +2209,16 @@ export enum RefundItemStatus { * * We'll try again! */ - Pending = 10, + Pending = 0x0100_0000, /** * Refund was obtained successfully. */ - Done = 50, + Done = 0x0500_0000, /** * Permanent error reported by the exchange * for the refund. */ - Failed = 51, + Failed = 0x0501_0000, } /** @@ -2327,7 +2234,9 @@ export interface RefundItemRecord { refundGroupId: string; - // Execution time as claimed by the merchant + /** + * Execution time as claimed by the merchant + */ executionTime: TalerProtocolTimestamp; /** @@ -2337,22 +2246,15 @@ export interface RefundItemRecord { refundAmount: AmountString; - //refundFee: AmountString; - - /** - * Upper bound on the refresh cost incurred by - * applying this refund. - * - * Might be lower in practice when two refunds on the same - * coin are refreshed in the same refresh operation. - */ - //totalRefreshCostBound: AmountString; - coinPub: string; rtxid: number; } +export function passthroughCodec<T>(): Codec<T> { + return codecForAny(); +} + /** * Schema definition for the IndexedDB * wallet database. @@ -2362,7 +2264,6 @@ export const WalletStoresV1 = { "currencySettings", describeContents<CurrencySettingsRecord>({ keyPath: ["currency"], - versionAdded: 3, }), {}, ), @@ -2411,42 +2312,11 @@ export const WalletStoresV1 = { byReservePub: describeIndex("byReservePub", "reservePub", {}), }, ), - exchangeTos: describeStore( - "exchangeTos", - describeContents<ExchangeTosRecord>({ - keyPath: ["exchangeBaseUrl", "etag"], - }), - {}, - ), config: describeStore( "config", describeContents<ConfigRecord>({ keyPath: "key" }), {}, ), - auditorTrust: describeStore( - "auditorTrust", - describeContents<AuditorTrustRecord>({ - keyPath: ["currency", "auditorBaseUrl"], - }), - { - byAuditorPub: describeIndex("byAuditorPub", "auditorPub"), - byUid: describeIndex("byUid", "uids", { - multiEntry: true, - }), - }, - ), - exchangeTrust: describeStore( - "exchangeTrust", - describeContents<ExchangeTrustRecord>({ - keyPath: ["currency", "exchangeBaseUrl"], - }), - { - byExchangeMasterPub: describeIndex( - "byExchangeMasterPub", - "exchangeMasterPub", - ), - }, - ), denominations: describeStore( "denominations", describeContents<DenominationRecord>({ @@ -2499,6 +2369,13 @@ export const WalletStoresV1 = { byStatus: describeIndex("byStatus", "operationStatus"), }, ), + refreshSessions: describeStore( + "refreshSessions", + describeContents<RefreshSessionRecord>({ + keyPath: ["refreshGroupId", "coinIndex"], + }), + {}, + ), recoupGroups: describeStore( "recoupGroups", describeContents<RecoupGroupRecord>({ @@ -2607,17 +2484,10 @@ export const WalletStoresV1 = { }), {}, ), - ghostDepositGroups: describeStore( - "ghostDepositGroups", - describeContents<GhostDepositGroupRecord>({ - keyPath: "contractTermsHash", - }), - {}, - ), - peerPushPaymentIncoming: describeStore( - "peerPushPaymentIncoming", + peerPushCredit: describeStore( + "peerPushCredit", describeContents<PeerPushPaymentIncomingRecord>({ - keyPath: "peerPushPaymentIncomingId", + keyPath: "peerPushCreditId", }), { byExchangeAndPurse: describeIndex("byExchangeAndPurse", [ @@ -2628,24 +2498,21 @@ export const WalletStoresV1 = { "byExchangeAndContractPriv", ["exchangeBaseUrl", "contractPriv"], { - versionAdded: 5, unique: true, }, ), byWithdrawalGroupId: describeIndex( "byWithdrawalGroupId", "withdrawalGroupId", - { - versionAdded: 5, - }, + {}, ), byStatus: describeIndex("byStatus", "status"), }, ), - peerPullPaymentIncoming: describeStore( - "peerPullPaymentIncoming", + peerPullDebit: describeStore( + "peerPullDebit", describeContents<PeerPullPaymentIncomingRecord>({ - keyPath: "peerPullPaymentIncomingId", + keyPath: "peerPullDebitId", }), { byExchangeAndPurse: describeIndex("byExchangeAndPurse", [ @@ -2656,16 +2523,15 @@ export const WalletStoresV1 = { "byExchangeAndContractPriv", ["exchangeBaseUrl", "contractPriv"], { - versionAdded: 5, unique: true, }, ), byStatus: describeIndex("byStatus", "status"), }, ), - peerPullPaymentInitiations: describeStore( - "peerPullPaymentInitiations", - describeContents<PeerPullPaymentInitiationRecord>({ + peerPullCredit: describeStore( + "peerPullCredit", + describeContents<PeerPullCreditRecord>({ keyPath: "pursePub", }), { @@ -2673,15 +2539,13 @@ export const WalletStoresV1 = { byWithdrawalGroupId: describeIndex( "byWithdrawalGroupId", "withdrawalGroupId", - { - versionAdded: 5, - }, + {}, ), }, ), - peerPushPaymentInitiations: describeStore( - "peerPushPaymentInitiations", - describeContents<PeerPushPaymentInitiationRecord>({ + peerPushDebit: describeStore( + "peerPushDebit", + describeContents<PeerPushDebitRecord>({ keyPath: "pursePub", }), { @@ -2706,7 +2570,6 @@ export const WalletStoresV1 = { "userAttention", describeContents<UserAttentionRecord>({ keyPath: ["entityId", "info.type"], - versionAdded: 2, }), {}, ), @@ -2714,20 +2577,16 @@ export const WalletStoresV1 = { "refundGroups", describeContents<RefundGroupRecord>({ keyPath: "refundGroupId", - versionAdded: 7, }), { byProposalId: describeIndex("byProposalId", "proposalId"), - byStatus: describeIndex("byStatus", "status", { - versionAdded: 10, - }), + byStatus: describeIndex("byStatus", "status", {}), }, ), refundItems: describeStore( "refundItems", describeContents<RefundItemRecord>({ keyPath: "id", - versionAdded: 7, autoIncrement: true, }), { @@ -2742,7 +2601,6 @@ export const WalletStoresV1 = { "fixups", describeContents<FixupRecord>({ keyPath: "fixupName", - versionAdded: 2, }), {}, ), @@ -2845,11 +2703,10 @@ export async function exportSingleDb( dbName, undefined, () => { - // May not happen, since we're not requesting a specific version - throw Error("unexpected version change"); + logger.info(`unexpected onversionchange in exportSingleDb of ${dbName}`); }, () => { - logger.info("unexpected onupgradeneeded"); + logger.info(`unexpected onupgradeneeded in exportSingleDb of ${dbName}`); }, ); @@ -2861,7 +2718,7 @@ export async function exportSingleDb( return new Promise((resolve, reject) => { const tx = myDb.transaction(Array.from(myDb.objectStoreNames)); tx.addEventListener("complete", () => { - myDb.close(); + //myDb.close(); resolve(singleDbDump); }); // tslint:disable-next-line:prefer-for-of @@ -2897,6 +2754,7 @@ export async function exportSingleDb( if (store.keyPath == null) { rec.key = structuredEncapsulate(cursor.key); } + storeDump.records.push(rec); cursor.continue(); } }); @@ -2925,21 +2783,22 @@ async function recoverFromDump( db: IDBDatabase, dbDump: DbDumpDatabase, ): Promise<void> { - return new Promise((resolve, reject) => { - const tx = db.transaction(Array.from(db.objectStoreNames), "readwrite"); - tx.addEventListener("complete", () => { - resolve(); - }); - for (let i = 0; i < db.objectStoreNames.length; i++) { - const name = db.objectStoreNames[i]; - const storeDump = dbDump.stores[name]; - if (!storeDump) continue; - for (let rec of storeDump.records) { - tx.objectStore(name).put(rec.value, rec.key); - } + const tx = db.transaction(Array.from(db.objectStoreNames), "readwrite"); + const txProm = promiseFromTransaction(tx); + const storeNames = db.objectStoreNames; + for (let i = 0; i < storeNames.length; i++) { + const name = db.objectStoreNames[i]; + const storeDump = dbDump.stores[name]; + if (!storeDump) continue; + await promiseFromRequest(tx.objectStore(name).clear()); + logger.info(`importing ${storeDump.records.length} records into ${name}`); + for (let rec of storeDump.records) { + await promiseFromRequest(tx.objectStore(name).put(rec.value, rec.key)); + logger.info("importing record done"); } - tx.commit(); - }); + } + tx.commit(); + return await txProm; } function checkDbDump(x: any): x is DbDump { @@ -2969,106 +2828,7 @@ export interface FixupDescription { /** * Manual migrations between minor versions of the DB schema. */ -export const walletDbFixups: FixupDescription[] = [ - { - name: "RefreshGroupRecord_currency", - async fn(tx): Promise<void> { - await tx.refreshGroups.iter().forEachAsync(async (rg) => { - if (rg.currency) { - return; - } - // Empty refresh group without input coin, delete it! - if (rg.inputPerCoin.length === 0) { - await tx.refreshGroups.delete(rg.refreshGroupId); - return; - } - rg.currency = Amounts.parseOrThrow(rg.inputPerCoin[0]).currency; - await tx.refreshGroups.put(rg); - }); - }, - }, - { - name: "DepositGroupRecord_transactionPerCoin", - async fn(tx): Promise<void> { - await tx.depositGroups.iter().forEachAsync(async (dg) => { - if (dg.transactionPerCoin) { - return; - } - dg.transactionPerCoin = dg.depositedPerCoin.map( - (c) => DepositElementStatus.Unknown, - ); - await tx.depositGroups.put(dg); - }); - }, - }, - { - name: "PeerPullPaymentIncomingRecord_totalCostEstimated_add", - async fn(tx): Promise<void> { - await tx.peerPullPaymentIncoming.iter().forEachAsync(async (pi) => { - if (pi.totalCostEstimated) { - return; - } - // Not really the cost, but a good substitute for older transactions - // that don't sture the effective cost of the transaction. - pi.totalCostEstimated = pi.contractTerms.amount; - await tx.peerPullPaymentIncoming.put(pi); - }); - }, - }, - { - name: "PeerPushPaymentIncomingRecord_totalCostEstimated_add", - async fn(tx): Promise<void> { - await tx.peerPushPaymentIncoming.iter().forEachAsync(async (pi) => { - if (pi.estimatedAmountEffective) { - return; - } - const contractTerms = await tx.contractTerms.get(pi.contractTermsHash); - if (!contractTerms) { - // Not sure what we can do here! - } else { - // Not really the cost, but a good substitute for older transactions - // that don't sture the effective cost of the transaction. - pi.estimatedAmountEffective = contractTerms.contractTermsRaw.amount; - await tx.peerPushPaymentIncoming.put(pi); - } - }); - }, - }, - { - name: "PeerPullPaymentInitiationRecord_estimatedAmountEffective_add", - async fn(tx): Promise<void> { - await tx.peerPullPaymentInitiations.iter().forEachAsync(async (pi) => { - if (pi.estimatedAmountEffective) { - return; - } - pi.estimatedAmountEffective = pi.amount; - await tx.peerPullPaymentInitiations.put(pi); - }); - }, - }, - { - name: "PeerPushPaymentInitiationRecord_ALL_removeLegacyTx", - async fn(tx): Promise<void> { - await tx.peerPushPaymentInitiations.iter().forEachAsync(async (pi) => { - // Remove legacy transactions that don't have the totalCost field yet. - if (!pi.totalCost) { - await tx.peerPushPaymentInitiations.delete(pi.pursePub); - } - }); - }, - }, - { - name: "CoinAvailabilityRecord_visibleCoinCount_add", - async fn(tx): Promise<void> { - await tx.coinAvailability.iter().forEachAsync(async (r) => { - if (r.visibleCoinCount == null) { - r.visibleCoinCount = r.freshCoinCount; - await tx.coinAvailability.put(r); - } - }); - }, - }, -]; +export const walletDbFixups: FixupDescription[] = []; const logger = new Logger("db.ts"); @@ -3196,6 +2956,17 @@ function promiseFromTransaction(transaction: IDBTransaction): Promise<void> { }); } +export function promiseFromRequest(request: IDBRequest): Promise<any> { + return new Promise((resolve, reject) => { + request.onsuccess = () => { + resolve(request.result); + }; + request.onerror = () => { + reject(request.error); + }; + }); +} + /** * Purge all data in the given database. */ diff --git a/packages/taler-wallet-core/src/dbless.ts b/packages/taler-wallet-core/src/dbless.ts index 5532345ae..11c6c0f74 100644 --- a/packages/taler-wallet-core/src/dbless.ts +++ b/packages/taler-wallet-core/src/dbless.ts @@ -48,24 +48,20 @@ import { parsePaytoUri, UnblindedSignature, } from "@gnu-taler/taler-util"; -import { TalerCryptoInterface } from "./crypto/cryptoImplementation.js"; -import { DenominationRecord } from "./db.js"; -import { - BankAccessApi, - BankApi, - BankServiceHandle, -} from "./bank-api-client.js"; import { HttpRequestLibrary, readSuccessResponseJsonOrThrow, } from "@gnu-taler/taler-util/http"; +import { BankAccessApiClient } from "../../taler-util/src/bank-api-client.js"; +import { TalerCryptoInterface } from "./crypto/cryptoImplementation.js"; +import { DenominationRecord } from "./db.js"; +import { isWithdrawableDenom } from "./index.js"; +import { ExchangeInfo } from "./operations/exchanges.js"; +import { assembleRefreshRevealRequest } from "./operations/refresh.js"; import { getBankStatusUrl, getBankWithdrawalInfo, } from "./operations/withdraw.js"; -import { ExchangeInfo } from "./operations/exchanges.js"; -import { assembleRefreshRevealRequest } from "./operations/refresh.js"; -import { isWithdrawableDenom, WalletConfig } from "./index.js"; const logger = new Logger("dbless.ts"); @@ -121,14 +117,10 @@ export async function topupReserveWithDemobank( args: TopupReserveWithDemobankArgs, ) { const { http, bankAccessApiBaseUrl, amount, exchangeInfo, reservePub } = args; - const bankHandle: BankServiceHandle = { - bankAccessApiBaseUrl: bankAccessApiBaseUrl, - http, - }; - const bankUser = await BankApi.createRandomBankUser(bankHandle); - const wopi = await BankAccessApi.createWithdrawalOperation( - bankHandle, - bankUser, + const bankClient = new BankAccessApiClient(bankAccessApiBaseUrl); + const bankUser = await bankClient.createRandomBankUser(); + const wopi = await bankClient.createWithdrawalOperation( + bankUser.username, amount, ); const bankInfo = await getBankWithdrawalInfo(http, wopi.taler_withdraw_uri); @@ -149,7 +141,7 @@ export async function topupReserveWithDemobank( httpResp, codecForBankWithdrawalOperationPostResponse(), ); - await BankApi.confirmWithdrawalOperation(bankHandle, bankUser, wopi); + await bankClient.confirmWithdrawalOperation(bankUser.username, wopi); } export async function withdrawCoin(args: { @@ -167,11 +159,7 @@ export async function withdrawCoin(args: { reservePriv: reserveKeyPair.reservePriv, reservePub: reserveKeyPair.reservePub, secretSeed: encodeCrock(getRandomBytes(32)), - value: { - currency: denom.currency, - fraction: denom.amountFrac, - value: denom.amountVal, - }, + value: Amounts.parseOrThrow(denom.value), }); const reqBody: ExchangeWithdrawRequest = { @@ -219,11 +207,7 @@ export function findDenomOrThrow( ): DenominationRecord { const denomselAllowLate = options.denomselAllowLate ?? false; for (const d of exchangeInfo.keys.currentDenominations) { - const value: AmountJson = { - currency: d.currency, - fraction: d.amountFrac, - value: d.amountVal, - }; + const value: AmountJson = Amounts.parseOrThrow(d.value); if ( Amounts.cmp(value, amount) === 0 && isWithdrawableDenom(d, denomselAllowLate) @@ -311,11 +295,7 @@ export async function refreshCoin(req: { denomPub: x.denomPub, denomPubHash: x.denomPubHash, feeWithdraw: x.fees.feeWithdraw, - value: Amounts.stringify({ - currency: x.currency, - fraction: x.amountFrac, - value: x.amountVal, - }), + value: x.value, })), meltCoinMaxAge: oldCoin.maxAge, }); diff --git a/packages/taler-wallet-core/src/host-impl.node.ts b/packages/taler-wallet-core/src/host-impl.node.ts index 0b6539306..a6dae58a1 100644 --- a/packages/taler-wallet-core/src/host-impl.node.ts +++ b/packages/taler-wallet-core/src/host-impl.node.ts @@ -52,7 +52,6 @@ interface MakeDbResult { async function makeFileDb( args: DefaultNodeWalletArgs = {}, ): Promise<MakeDbResult> { - BridgeIDBFactory.enableTracing = false; const myBackend = new MemoryBackend(); myBackend.enableTracing = false; const storagePath = args.persistentStoragePath; @@ -135,13 +134,16 @@ export async function createNativeWalletHost2( } else { myHttpLib = createPlatformHttpLib({ enableThrottling: true, - allowHttp: args.config?.features?.allowHttp, + requireTls: !args.config?.features?.allowHttp, }); } let dbResp: MakeDbResult; - if (args.persistentStoragePath &&args.persistentStoragePath.endsWith(".json")) { + if ( + args.persistentStoragePath && + args.persistentStoragePath.endsWith(".json") + ) { logger.info("using legacy file-based DB backend"); dbResp = await makeFileDb(args); } else { diff --git a/packages/taler-wallet-core/src/host-impl.qtart.ts b/packages/taler-wallet-core/src/host-impl.qtart.ts index 81dbe0acd..85f8df6e5 100644 --- a/packages/taler-wallet-core/src/host-impl.qtart.ts +++ b/packages/taler-wallet-core/src/host-impl.qtart.ts @@ -188,7 +188,7 @@ export async function createNativeWalletHost2( } else { myHttpLib = createPlatformHttpLib({ enableThrottling: true, - allowHttp: args.config?.features?.allowHttp, + requireTls: !args.config?.features?.allowHttp, }); } diff --git a/packages/taler-wallet-core/src/index.ts b/packages/taler-wallet-core/src/index.ts index d64f7d5e6..643d65620 100644 --- a/packages/taler-wallet-core/src/index.ts +++ b/packages/taler-wallet-core/src/index.ts @@ -44,8 +44,6 @@ export * from "./operations/backup/index.js"; export * from "./operations/exchanges.js"; -export * from "./bank-api-client.js"; - export * from "./operations/withdraw.js"; export * from "./operations/refresh.js"; diff --git a/packages/taler-wallet-core/src/internal-wallet-state.ts b/packages/taler-wallet-core/src/internal-wallet-state.ts index a189c9cb3..20f8a7511 100644 --- a/packages/taler-wallet-core/src/internal-wallet-state.ts +++ b/packages/taler-wallet-core/src/internal-wallet-state.ts @@ -107,10 +107,6 @@ export interface ExchangeOperations { }>, exchangeBaseUrl: string, ): Promise<ExchangeDetailsRecord | undefined>; - getExchangeTrust( - ws: InternalWalletState, - exchangeInfo: ExchangeEntryRecord, - ): Promise<TrustInfo>; updateExchangeFromUrl( ws: InternalWalletState, baseUrl: string, diff --git a/packages/taler-wallet-core/src/operations/balance.ts b/packages/taler-wallet-core/src/operations/balance.ts index 0fcab0542..a20ded2af 100644 --- a/packages/taler-wallet-core/src/operations/balance.ts +++ b/packages/taler-wallet-core/src/operations/balance.ts @@ -95,14 +95,7 @@ function computeRefreshGroupAvailableAmount(r: RefreshGroupRecord): AmountJson { return available; } for (let i = 0; i < r.oldCoinPubs.length; i++) { - const session = r.refreshSessionPerCoin[i]; - if (session) { - // We are always assuming the refresh will succeed, thus we - // report the output as available balance. - available = Amounts.add(available, session.amountRefreshOutput).amount; - } else { - available = Amounts.add(available, r.estimatedOutputPerCoin[i]).amount; - } + available = Amounts.add(available, r.expectedOutputPerCoin[i]).amount; } return available; } @@ -140,11 +133,7 @@ export async function getBalancesInsideTransaction( const b = initBalance(ca.currency); const count = ca.visibleCoinCount ?? 0; for (let i = 0; i < count; i++) { - b.available = Amounts.add(b.available, { - currency: ca.currency, - fraction: ca.amountFrac, - value: ca.amountVal, - }).amount; + b.available = Amounts.add(b.available, ca.value).amount; } }); @@ -163,7 +152,7 @@ export async function getBalancesInsideTransaction( case WithdrawalGroupStatus.AbortedExchange: case WithdrawalGroupStatus.FailedAbortingBank: case WithdrawalGroupStatus.FailedBankAborted: - case WithdrawalGroupStatus.Finished: + case WithdrawalGroupStatus.Done: // Does not count as pendingIncoming return; case WithdrawalGroupStatus.PendingReady: @@ -281,7 +270,7 @@ export async function getAcceptableExchangeBaseUrls( const acceptableExchangeUrls = new Set<string>(); const depositableExchangeUrls = new Set<string>(); await ws.db - .mktx((x) => [x.exchanges, x.exchangeDetails, x.auditorTrust]) + .mktx((x) => [x.exchanges, x.exchangeDetails]) .runReadOnly(async (tx) => { // FIXME: We should have a DB index to look up all exchanges // for a particular auditor ... @@ -415,11 +404,7 @@ export async function getMerchantPaymentBalanceDetails( if (ca.currency != req.currency) { return; } - const singleCoinAmount: AmountJson = { - currency: ca.currency, - fraction: ca.amountFrac, - value: ca.amountVal, - }; + const singleCoinAmount: AmountJson = Amounts.parseOrThrow(ca.value); const coinAmount: AmountJson = Amounts.mult( singleCoinAmount, ca.freshCoinCount, @@ -537,11 +522,7 @@ export async function getPeerPaymentBalanceDetailsInTx( ) { return; } - const singleCoinAmount: AmountJson = { - currency: ca.currency, - fraction: ca.amountFrac, - value: ca.amountVal, - }; + const singleCoinAmount: AmountJson = Amounts.parseOrThrow(ca.value); const coinAmount: AmountJson = Amounts.mult( singleCoinAmount, ca.freshCoinCount, diff --git a/packages/taler-wallet-core/src/operations/common.ts b/packages/taler-wallet-core/src/operations/common.ts index e96beb5b2..50dd3dc5c 100644 --- a/packages/taler-wallet-core/src/operations/common.ts +++ b/packages/taler-wallet-core/src/operations/common.ts @@ -26,7 +26,6 @@ import { CoinRefreshRequest, CoinStatus, Duration, - ErrorInfoSummary, ExchangeEntryStatus, ExchangeListItem, ExchangeTosStatus, @@ -34,9 +33,11 @@ import { getErrorDetailFromException, j2s, Logger, + makeErrorDetail, NotificationType, OperationErrorInfo, RefreshReason, + TalerError, TalerErrorCode, TalerErrorDetail, TombstoneIdStr, @@ -44,32 +45,31 @@ import { TransactionType, WalletNotification, } from "@gnu-taler/taler-util"; +import { CryptoApiStoppedError } from "../crypto/workers/crypto-dispatcher.js"; import { - WalletStoresV1, + BackupProviderRecord, CoinRecord, + DepositGroupRecord, ExchangeDetailsRecord, + ExchangeEntryDbRecordStatus, + ExchangeEntryDbUpdateStatus, ExchangeEntryRecord, - BackupProviderRecord, - DepositGroupRecord, + PeerPullCreditRecord, PeerPullPaymentIncomingRecord, - PeerPullPaymentInitiationRecord, + PeerPushDebitRecord, PeerPushPaymentIncomingRecord, - PeerPushPaymentInitiationRecord, PurchaseRecord, RecoupGroupRecord, RefreshGroupRecord, RewardRecord, + WalletStoresV1, WithdrawalGroupRecord, - ExchangeEntryDbUpdateStatus, - ExchangeEntryDbRecordStatus, } from "../db.js"; -import { makeErrorDetail, TalerError } from "@gnu-taler/taler-util"; import { InternalWalletState } from "../internal-wallet-state.js"; -import { checkDbInvariant, checkLogicInvariant } from "../util/invariants.js"; -import { GetReadOnlyAccess, GetReadWriteAccess } from "../util/query.js"; -import { CryptoApiStoppedError } from "../crypto/workers/crypto-dispatcher.js"; import { PendingTaskType, TaskId } from "../pending-types.js"; import { assertUnreachable } from "../util/assertUnreachable.js"; +import { checkDbInvariant, checkLogicInvariant } from "../util/invariants.js"; +import { GetReadOnlyAccess, GetReadWriteAccess } from "../util/query.js"; import { constructTransactionIdentifier } from "./transactions.js"; const logger = new Logger("operations/common.ts"); @@ -144,8 +144,7 @@ export async function makeCoinAvailable( if (!car) { car = { maxAge: ageRestriction, - amountFrac: denom.amountFrac, - amountVal: denom.amountVal, + value: denom.value, currency: denom.currency, denomPubHash: denom.denomPubHash, exchangeBaseUrl: denom.exchangeBaseUrl, @@ -271,7 +270,7 @@ function convertTaskToTransactionId( case PendingTaskType.PeerPullDebit: return constructTransactionIdentifier({ tag: TransactionType.PeerPullDebit, - peerPullPaymentIncomingId: parsedTaskId.peerPullPaymentIncomingId, + peerPullDebitId: parsedTaskId.peerPullDebitId, }); // FIXME: This doesn't distinguish internal-withdrawal. // Maybe we should have a different task type for that as well? @@ -284,7 +283,7 @@ function convertTaskToTransactionId( case PendingTaskType.PeerPushCredit: return constructTransactionIdentifier({ tag: TransactionType.PeerPushCredit, - peerPushPaymentIncomingId: parsedTaskId.peerPushPaymentIncomingId, + peerPushCreditId: parsedTaskId.peerPushCreditId, }); case PendingTaskType.Deposit: return constructTransactionIdentifier({ @@ -836,9 +835,9 @@ export type ParsedTaskIdentifier = | { tag: PendingTaskType.Deposit; depositGroupId: string } | { tag: PendingTaskType.ExchangeCheckRefresh; exchangeBaseUrl: string } | { tag: PendingTaskType.ExchangeUpdate; exchangeBaseUrl: string } - | { tag: PendingTaskType.PeerPullDebit; peerPullPaymentIncomingId: string } + | { tag: PendingTaskType.PeerPullDebit; peerPullDebitId: string } | { tag: PendingTaskType.PeerPullCredit; pursePub: string } - | { tag: PendingTaskType.PeerPushCredit; peerPushPaymentIncomingId: string } + | { tag: PendingTaskType.PeerPushCredit; peerPushCreditId: string } | { tag: PendingTaskType.PeerPushDebit; pursePub: string } | { tag: PendingTaskType.Purchase; proposalId: string } | { tag: PendingTaskType.Recoup; recoupGroupId: string } @@ -865,9 +864,9 @@ export function parseTaskIdentifier(x: string): ParsedTaskIdentifier { case PendingTaskType.PeerPullCredit: return { tag: type, pursePub: rest[0] }; case PendingTaskType.PeerPullDebit: - return { tag: type, peerPullPaymentIncomingId: rest[0] }; + return { tag: type, peerPullDebitId: rest[0] }; case PendingTaskType.PeerPushCredit: - return { tag: type, peerPushPaymentIncomingId: rest[0] }; + return { tag: type, peerPushCreditId: rest[0] }; case PendingTaskType.PeerPushDebit: return { tag: type, pursePub: rest[0] }; case PendingTaskType.Purchase: @@ -896,9 +895,9 @@ export function constructTaskIdentifier(p: ParsedTaskIdentifier): TaskId { case PendingTaskType.ExchangeUpdate: return `${p.tag}:${p.exchangeBaseUrl}` as TaskId; case PendingTaskType.PeerPullDebit: - return `${p.tag}:${p.peerPullPaymentIncomingId}` as TaskId; + return `${p.tag}:${p.peerPullDebitId}` as TaskId; case PendingTaskType.PeerPushCredit: - return `${p.tag}:${p.peerPushPaymentIncomingId}` as TaskId; + return `${p.tag}:${p.peerPushCreditId}` as TaskId; case PendingTaskType.PeerPullCredit: return `${p.tag}:${p.pursePub}` as TaskId; case PendingTaskType.PeerPushDebit: @@ -950,23 +949,23 @@ export namespace TaskIdentifiers { return `${PendingTaskType.Backup}:${backupRecord.baseUrl}` as TaskId; } export function forPeerPushPaymentInitiation( - ppi: PeerPushPaymentInitiationRecord, + ppi: PeerPushDebitRecord, ): TaskId { return `${PendingTaskType.PeerPushDebit}:${ppi.pursePub}` as TaskId; } export function forPeerPullPaymentInitiation( - ppi: PeerPullPaymentInitiationRecord, + ppi: PeerPullCreditRecord, ): TaskId { return `${PendingTaskType.PeerPullCredit}:${ppi.pursePub}` as TaskId; } export function forPeerPullPaymentDebit( ppi: PeerPullPaymentIncomingRecord, ): TaskId { - return `${PendingTaskType.PeerPullDebit}:${ppi.peerPullPaymentIncomingId}` as TaskId; + return `${PendingTaskType.PeerPullDebit}:${ppi.peerPullDebitId}` as TaskId; } export function forPeerPushCredit( ppi: PeerPushPaymentIncomingRecord, ): TaskId { - return `${PendingTaskType.PeerPushCredit}:${ppi.peerPushPaymentIncomingId}` as TaskId; + return `${PendingTaskType.PeerPushCredit}:${ppi.peerPushCreditId}` as TaskId; } } diff --git a/packages/taler-wallet-core/src/operations/deposits.ts b/packages/taler-wallet-core/src/operations/deposits.ts index a8ec859cf..8ea792d91 100644 --- a/packages/taler-wallet-core/src/operations/deposits.ts +++ b/packages/taler-wallet-core/src/operations/deposits.ts @@ -510,10 +510,10 @@ async function refundDepositGroup( ws: InternalWalletState, depositGroup: DepositGroupRecord, ): Promise<TaskRunResult> { - const newTxPerCoin = [...depositGroup.transactionPerCoin]; - logger.info(`status per coin: ${j2s(depositGroup.transactionPerCoin)}`); - for (let i = 0; i < depositGroup.transactionPerCoin.length; i++) { - const st = depositGroup.transactionPerCoin[i]; + const newTxPerCoin = [...depositGroup.statusPerCoin]; + logger.info(`status per coin: ${j2s(depositGroup.statusPerCoin)}`); + for (let i = 0; i < depositGroup.statusPerCoin.length; i++) { + const st = depositGroup.statusPerCoin[i]; switch (st) { case DepositElementStatus.RefundFailed: case DepositElementStatus.RefundSuccess: @@ -593,7 +593,7 @@ async function refundDepositGroup( if (!newDg) { return; } - newDg.transactionPerCoin = newTxPerCoin; + newDg.statusPerCoin = newTxPerCoin; const refreshCoins: CoinRefreshRequest[] = []; for (let i = 0; i < newTxPerCoin.length; i++) { refreshCoins.push({ @@ -766,7 +766,7 @@ async function processDepositGroupPendingTrack( cancellationToken?: CancellationToken, ): Promise<TaskRunResult> { const { depositGroupId } = depositGroup; - for (let i = 0; i < depositGroup.depositedPerCoin.length; i++) { + for (let i = 0; i < depositGroup.statusPerCoin.length; i++) { const coinPub = depositGroup.payCoinSelection.coinPubs[i]; // FIXME: Make the URL part of the coin selection? const exchangeBaseUrl = await ws.db @@ -785,7 +785,7 @@ async function processDepositGroupPendingTrack( } | undefined; - if (depositGroup.transactionPerCoin[i] !== DepositElementStatus.Wired) { + if (depositGroup.statusPerCoin[i] !== DepositElementStatus.Wired) { const track = await trackDeposit( ws, depositGroup, @@ -810,7 +810,7 @@ async function processDepositGroupPendingTrack( exchangeBaseUrl, ); } else { - updatedTxStatus = DepositElementStatus.Accepted; + updatedTxStatus = DepositElementStatus.Tracking; } } else if (track.type === "wired") { updatedTxStatus = DepositElementStatus.Wired; @@ -840,7 +840,7 @@ async function processDepositGroupPendingTrack( id: track.exchange_sig, }; } else { - updatedTxStatus = DepositElementStatus.Unknown; + updatedTxStatus = DepositElementStatus.DepositPending; } } @@ -853,7 +853,7 @@ async function processDepositGroupPendingTrack( return; } if (updatedTxStatus !== undefined) { - dg.transactionPerCoin[i] = updatedTxStatus; + dg.statusPerCoin[i] = updatedTxStatus; } if (newWiredCoin) { /** @@ -885,8 +885,8 @@ async function processDepositGroupPendingTrack( return undefined; } const oldTxState = computeDepositTransactionStatus(dg); - for (let i = 0; i < depositGroup.depositedPerCoin.length; i++) { - if (depositGroup.transactionPerCoin[i] !== DepositElementStatus.Wired) { + for (let i = 0; i < depositGroup.statusPerCoin.length; i++) { + if (depositGroup.statusPerCoin[i] !== DepositElementStatus.Wired) { allWired = false; break; } @@ -943,7 +943,7 @@ async function processDepositGroupPendingDeposit( for (let i = 0; i < depositPermissions.length; i++) { const perm = depositPermissions[i]; - if (depositGroup.depositedPerCoin[i]) { + if (depositGroup.statusPerCoin[i] !== DepositElementStatus.DepositPending) { continue; } @@ -980,8 +980,12 @@ async function processDepositGroupPendingDeposit( if (!dg) { return; } - dg.depositedPerCoin[i] = true; - await tx.depositGroups.put(dg); + const coinStatus = dg.statusPerCoin[i]; + switch (coinStatus) { + case DepositElementStatus.DepositPending: + dg.statusPerCoin[i] = DepositElementStatus.Tracking; + await tx.depositGroups.put(dg); + } }); } @@ -1373,16 +1377,15 @@ export async function createDepositGroup( noncePub: noncePair.pub, timestampCreated: AbsoluteTime.toPreciseTimestamp(now), timestampFinished: undefined, - transactionPerCoin: payCoinSel.coinSel.coinPubs.map( - () => DepositElementStatus.Unknown, + statusPerCoin: payCoinSel.coinSel.coinPubs.map( + () => DepositElementStatus.DepositPending, ), payCoinSelection: payCoinSel.coinSel, payCoinSelectionUid: encodeCrock(getRandomBytes(32)), - depositedPerCoin: payCoinSel.coinSel.coinPubs.map(() => false), merchantPriv: merchantPair.priv, merchantPub: merchantPair.pub, totalPayCost: Amounts.stringify(totalDepositCost), - effectiveDepositAmount: Amounts.stringify( + counterpartyEffectiveDepositAmount: Amounts.stringify( counterpartyEffectiveDepositAmount, ), wire: { @@ -1536,7 +1539,7 @@ async function getTotalFeesForDepositAmount( .iter(coin.exchangeBaseUrl) .filter((x) => Amounts.isSameCurrency( - DenominationRecord.getValue(x), + x.value, pcs.coinContributions[i], ), ); diff --git a/packages/taler-wallet-core/src/operations/exchanges.ts b/packages/taler-wallet-core/src/operations/exchanges.ts index 311a71a6e..43a08ed3b 100644 --- a/packages/taler-wallet-core/src/operations/exchanges.ts +++ b/packages/taler-wallet-core/src/operations/exchanges.ts @@ -158,35 +158,6 @@ getExchangeDetails.makeContext = (db: DbAccess<typeof WalletStoresV1>) => db.mktx((x) => [x.exchanges, x.exchangeDetails]); /** - * Update the database based on the download of the terms of service. - */ -export async function updateExchangeTermsOfService( - ws: InternalWalletState, - exchangeBaseUrl: string, - tos: ExchangeTosDownloadResult, -): Promise<void> { - await ws.db - .mktx((x) => [x.exchanges, x.exchangeTos, x.exchangeDetails]) - .runReadWrite(async (tx) => { - const d = await getExchangeDetails(tx, exchangeBaseUrl); - let tosRecord = await tx.exchangeTos.get([exchangeBaseUrl, tos.tosEtag]); - if (!tosRecord) { - tosRecord = { - etag: tos.tosEtag, - exchangeBaseUrl, - termsOfServiceContentType: tos.tosContentType, - termsOfServiceText: tos.tosText, - }; - await tx.exchangeTos.put(tosRecord); - } - if (d) { - d.tosCurrentEtag = tos.tosEtag; - await tx.exchangeDetails.put(d); - } - }); -} - -/** * Mark a ToS version as accepted by the user. * * @param etag version of the ToS to accept, or current ToS version of not given @@ -472,8 +443,7 @@ async function downloadExchangeKeysInfo( exchangeMasterPub: exchangeKeysJsonUnchecked.master_public_key, isOffered: true, isRevoked: false, - amountFrac: value.fraction, - amountVal: value.value, + value: Amounts.stringify(value), currency: value.currency, stampExpireDeposit: denomIn.stamp_expire_deposit, stampExpireLegal: denomIn.stamp_expire_legal, @@ -740,7 +710,6 @@ export async function updateExchangeFromUrlHandler( const updated = await ws.db .mktx((x) => [ x.exchanges, - x.exchangeTos, x.exchangeDetails, x.exchangeSignKeys, x.denominations, @@ -801,21 +770,6 @@ export async function updateExchangeFromUrlHandler( const drRowId = await tx.exchangeDetails.put(newDetails); checkDbInvariant(typeof drRowId.key === "number"); - let tosRecord = await tx.exchangeTos.get([ - exchangeBaseUrl, - tosDownload.tosEtag, - ]); - - if (!tosRecord || tosRecord.etag !== existingTosAccepted?.etag) { - tosRecord = { - etag: tosDownload.tosEtag, - exchangeBaseUrl, - termsOfServiceContentType: tosDownload.tosContentType, - termsOfServiceText: tosDownload.tosText, - }; - await tx.exchangeTos.put(tosRecord); - } - for (const sk of keysInfo.signingKeys) { // FIXME: validate signing keys before inserting them await tx.exchangeSignKeys.put({ @@ -972,54 +926,3 @@ export async function getExchangePaytoUri( )}`, ); } - -/** - * Check if and how an exchange is trusted and/or audited. - */ -export async function getExchangeTrust( - ws: InternalWalletState, - exchangeInfo: ExchangeEntryRecord, -): Promise<TrustInfo> { - let isTrusted = false; - let isAudited = false; - - return await ws.db - .mktx((x) => [ - x.exchanges, - x.exchangeDetails, - x.exchangeTrust, - x.auditorTrust, - ]) - .runReadOnly(async (tx) => { - const exchangeDetails = await getExchangeDetails( - tx, - exchangeInfo.baseUrl, - ); - - if (!exchangeDetails) { - throw Error(`exchange ${exchangeInfo.baseUrl} details not available`); - } - const exchangeTrustRecord = - await tx.exchangeTrust.indexes.byExchangeMasterPub.get( - exchangeDetails.masterPublicKey, - ); - if ( - exchangeTrustRecord && - exchangeTrustRecord.uids.length > 0 && - exchangeTrustRecord.currency === exchangeDetails.currency - ) { - isTrusted = true; - } - - for (const auditor of exchangeDetails.auditors) { - const auditorTrustRecord = - await tx.auditorTrust.indexes.byAuditorPub.get(auditor.auditor_pub); - if (auditorTrustRecord && auditorTrustRecord.uids.length > 0) { - isAudited = true; - break; - } - } - - return { isTrusted, isAudited }; - }); -} diff --git a/packages/taler-wallet-core/src/operations/pay-merchant.ts b/packages/taler-wallet-core/src/operations/pay-merchant.ts index 2580c97f5..57367bb20 100644 --- a/packages/taler-wallet-core/src/operations/pay-merchant.ts +++ b/packages/taler-wallet-core/src/operations/pay-merchant.ts @@ -174,12 +174,12 @@ export async function getTotalPaymentCost( .iter(coin.exchangeBaseUrl) .filter((x) => Amounts.isSameCurrency( - DenominationRecord.getValue(x), + x.value, pcs.coinContributions[i], ), ); const amountLeft = Amounts.sub( - DenominationRecord.getValue(denom), + denom.value, pcs.coinContributions[i], ).amount; const refreshCost = getTotalRefreshCost( @@ -317,11 +317,8 @@ export function extractContractData( wireInfoHash: parsedContractTerms.h_wire, maxDepositFee: Amounts.stringify(parsedContractTerms.max_fee), merchant: parsedContractTerms.merchant, - products: parsedContractTerms.products, summaryI18n: parsedContractTerms.summary_i18n, minimumAge: parsedContractTerms.minimum_age, - deliveryDate: parsedContractTerms.delivery_date, - deliveryLocation: parsedContractTerms.delivery_location, }; } @@ -541,7 +538,7 @@ async function processDownloadProposal( // if original order is refunded. if (otherPurchase && otherPurchase.refundAmountAwaiting === undefined) { logger.warn("repurchase detected"); - p.purchaseStatus = PurchaseStatus.RepurchaseDetected; + p.purchaseStatus = PurchaseStatus.DoneRepurchaseDetected; p.repurchaseProposalId = otherPurchase.proposalId; await tx.purchases.put(p); } else { @@ -974,7 +971,7 @@ export async function checkPaymentByProposalId( if (!proposal) { throw Error(`could not get proposal ${proposalId}`); } - if (proposal.purchaseStatus === PurchaseStatus.RepurchaseDetected) { + if (proposal.purchaseStatus === PurchaseStatus.DoneRepurchaseDetected) { const existingProposalId = proposal.repurchaseProposalId; if (existingProposalId) { logger.trace("using existing purchase for same product"); @@ -1527,7 +1524,7 @@ export async function processPurchase( return processPurchaseDialogShared(ws, purchase); case PurchaseStatus.FailedClaim: case PurchaseStatus.Done: - case PurchaseStatus.RepurchaseDetected: + case PurchaseStatus.DoneRepurchaseDetected: case PurchaseStatus.DialogProposed: case PurchaseStatus.AbortedProposalRefused: case PurchaseStatus.AbortedIncompletePayment: @@ -2099,7 +2096,7 @@ export function computePayMerchantTransactionState( return { major: TransactionMajorState.Done, }; - case PurchaseStatus.RepurchaseDetected: + case PurchaseStatus.DoneRepurchaseDetected: return { major: TransactionMajorState.Failed, minor: TransactionMinorState.Repurchase, @@ -2176,7 +2173,7 @@ export function computePayMerchantTransactionActions( return [TransactionAction.Delete]; case PurchaseStatus.Done: return [TransactionAction.Delete]; - case PurchaseStatus.RepurchaseDetected: + case PurchaseStatus.DoneRepurchaseDetected: return [TransactionAction.Delete]; case PurchaseStatus.AbortedIncompletePayment: return [TransactionAction.Delete]; diff --git a/packages/taler-wallet-core/src/operations/pay-peer-common.ts b/packages/taler-wallet-core/src/operations/pay-peer-common.ts index 9e05e43d8..6d425289d 100644 --- a/packages/taler-wallet-core/src/operations/pay-peer-common.ts +++ b/packages/taler-wallet-core/src/operations/pay-peer-common.ts @@ -108,16 +108,8 @@ export async function getTotalPeerPaymentCost( } const allDenoms = await tx.denominations.indexes.byExchangeBaseUrl .iter(coin.exchangeBaseUrl) - .filter((x) => - Amounts.isSameCurrency( - DenominationRecord.getValue(x), - pcs[i].contribution, - ), - ); - const amountLeft = Amounts.sub( - DenominationRecord.getValue(denom), - pcs[i].contribution, - ).amount; + .filter((x) => Amounts.isSameCurrency(x.value, pcs[i].contribution)); + const amountLeft = Amounts.sub(denom.value, pcs[i].contribution).amount; const refreshCost = getTotalRefreshCost( allDenoms, DenominationRecord.toDenomInfo(denom), diff --git a/packages/taler-wallet-core/src/operations/pay-peer-pull-credit.ts b/packages/taler-wallet-core/src/operations/pay-peer-pull-credit.ts index 29c0fff9e..0355eb152 100644 --- a/packages/taler-wallet-core/src/operations/pay-peer-pull-credit.ts +++ b/packages/taler-wallet-core/src/operations/pay-peer-pull-credit.ts @@ -27,6 +27,7 @@ import { InitiatePeerPullCreditResponse, Logger, NotificationType, + PeerContractTerms, TalerErrorCode, TalerPreciseTimestamp, TalerProtocolTimestamp, @@ -55,8 +56,8 @@ import { import { KycPendingInfo, KycUserType, - PeerPullPaymentInitiationRecord, - PeerPullPaymentInitiationStatus, + PeerPullCreditRecord, + PeerPullPaymentCreditStatus, WithdrawalGroupStatus, WithdrawalRecordType, updateExchangeFromUrl, @@ -90,7 +91,7 @@ const logger = new Logger("pay-peer-pull-credit.ts"); async function queryPurseForPeerPullCredit( ws: InternalWalletState, - pullIni: PeerPullPaymentInitiationRecord, + pullIni: PeerPullCreditRecord, cancellationToken: CancellationToken, ): Promise<LongpollResult> { const purseDepositUrl = new URL( @@ -143,7 +144,6 @@ async function queryPurseForPeerPullCredit( amount: Amounts.parseOrThrow(pullIni.amount), wgInfo: { withdrawalType: WithdrawalRecordType.PeerPullCredit, - contractTerms: pullIni.contractTerms, contractPriv: pullIni.contractPriv, }, forcedWithdrawalGroupId: pullIni.withdrawalGroupId, @@ -159,18 +159,18 @@ async function queryPurseForPeerPullCredit( pursePub: pullIni.pursePub, }); const transitionInfo = await ws.db - .mktx((x) => [x.peerPullPaymentInitiations]) + .mktx((x) => [x.peerPullCredit]) .runReadWrite(async (tx) => { - const finPi = await tx.peerPullPaymentInitiations.get(pullIni.pursePub); + const finPi = await tx.peerPullCredit.get(pullIni.pursePub); if (!finPi) { - logger.warn("peerPullPaymentInitiation not found anymore"); + logger.warn("peerPullCredit not found anymore"); return; } const oldTxState = computePeerPullCreditTransactionState(finPi); - if (finPi.status === PeerPullPaymentInitiationStatus.PendingReady) { - finPi.status = PeerPullPaymentInitiationStatus.PendingWithdrawing; + if (finPi.status === PeerPullPaymentCreditStatus.PendingReady) { + finPi.status = PeerPullPaymentCreditStatus.PendingWithdrawing; } - await tx.peerPullPaymentInitiations.put(finPi); + await tx.peerPullCredit.put(finPi); const newTxState = computePeerPullCreditTransactionState(finPi); return { oldTxState, newTxState }; }); @@ -214,22 +214,22 @@ async function longpollKycStatus( kycStatusRes.status === HttpStatusCode.NoContent ) { const transitionInfo = await ws.db - .mktx((x) => [x.peerPullPaymentInitiations]) + .mktx((x) => [x.peerPullCredit]) .runReadWrite(async (tx) => { - const peerIni = await tx.peerPullPaymentInitiations.get(pursePub); + const peerIni = await tx.peerPullCredit.get(pursePub); if (!peerIni) { return; } if ( peerIni.status !== - PeerPullPaymentInitiationStatus.PendingMergeKycRequired + PeerPullPaymentCreditStatus.PendingMergeKycRequired ) { return; } const oldTxState = computePeerPullCreditTransactionState(peerIni); - peerIni.status = PeerPullPaymentInitiationStatus.PendingCreatePurse; + peerIni.status = PeerPullPaymentCreditStatus.PendingCreatePurse; const newTxState = computePeerPullCreditTransactionState(peerIni); - await tx.peerPullPaymentInitiations.put(peerIni); + await tx.peerPullCredit.put(peerIni); return { oldTxState, newTxState }; }); notifyTransition(ws, transactionId, transitionInfo); @@ -250,7 +250,7 @@ async function longpollKycStatus( async function processPeerPullCreditAbortingDeletePurse( ws: InternalWalletState, - peerPullIni: PeerPullPaymentInitiationRecord, + peerPullIni: PeerPullCreditRecord, ): Promise<TaskRunResult> { const { pursePub, pursePriv } = peerPullIni; const transactionId = constructTransactionIdentifier({ @@ -272,24 +272,22 @@ async function processPeerPullCreditAbortingDeletePurse( const transitionInfo = await ws.db .mktx((x) => [ - x.peerPullPaymentInitiations, + x.peerPullCredit, x.refreshGroups, x.denominations, x.coinAvailability, x.coins, ]) .runReadWrite(async (tx) => { - const ppiRec = await tx.peerPullPaymentInitiations.get(pursePub); + const ppiRec = await tx.peerPullCredit.get(pursePub); if (!ppiRec) { return undefined; } - if ( - ppiRec.status !== PeerPullPaymentInitiationStatus.AbortingDeletePurse - ) { + if (ppiRec.status !== PeerPullPaymentCreditStatus.AbortingDeletePurse) { return undefined; } const oldTxState = computePeerPullCreditTransactionState(ppiRec); - ppiRec.status = PeerPullPaymentInitiationStatus.Aborted; + ppiRec.status = PeerPullPaymentCreditStatus.Aborted; const newTxState = computePeerPullCreditTransactionState(ppiRec); return { oldTxState, @@ -303,7 +301,7 @@ async function processPeerPullCreditAbortingDeletePurse( async function handlePeerPullCreditWithdrawing( ws: InternalWalletState, - pullIni: PeerPullPaymentInitiationRecord, + pullIni: PeerPullCreditRecord, ): Promise<TaskRunResult> { if (!pullIni.withdrawalGroupId) { throw Error("invalid db state (withdrawing, but no withdrawal group ID"); @@ -315,14 +313,14 @@ async function handlePeerPullCreditWithdrawing( const wgId = pullIni.withdrawalGroupId; let finished: boolean = false; const transitionInfo = await ws.db - .mktx((x) => [x.peerPullPaymentInitiations, x.withdrawalGroups]) + .mktx((x) => [x.peerPullCredit, x.withdrawalGroups]) .runReadWrite(async (tx) => { - const ppi = await tx.peerPullPaymentInitiations.get(pullIni.pursePub); + const ppi = await tx.peerPullCredit.get(pullIni.pursePub); if (!ppi) { finished = true; return; } - if (ppi.status !== PeerPullPaymentInitiationStatus.PendingWithdrawing) { + if (ppi.status !== PeerPullPaymentCreditStatus.PendingWithdrawing) { finished = true; return; } @@ -333,13 +331,13 @@ async function handlePeerPullCreditWithdrawing( return undefined; } switch (wg.status) { - case WithdrawalGroupStatus.Finished: + case WithdrawalGroupStatus.Done: finished = true; - ppi.status = PeerPullPaymentInitiationStatus.Done; + ppi.status = PeerPullPaymentCreditStatus.Done; break; // FIXME: Also handle other final states! } - await tx.peerPullPaymentInitiations.put(ppi); + await tx.peerPullCredit.put(ppi); const newTxState = computePeerPullCreditTransactionState(ppi); return { oldTxState, @@ -357,7 +355,7 @@ async function handlePeerPullCreditWithdrawing( async function handlePeerPullCreditCreatePurse( ws: InternalWalletState, - pullIni: PeerPullPaymentInitiationRecord, + pullIni: PeerPullCreditRecord, ): Promise<TaskRunResult> { const purseFee = Amounts.stringify(Amounts.zeroOfAmount(pullIni.amount)); const pursePub = pullIni.pursePub; @@ -371,6 +369,18 @@ async function handlePeerPullCreditCreatePurse( throw Error("merge reserve for peer pull payment not found in database"); } + const contractTermsRecord = await ws.db + .mktx((x) => [x.contractTerms]) + .runReadOnly(async (tx) => { + return tx.contractTerms.get(pullIni.contractTermsHash); + }); + + if (!contractTermsRecord) { + throw Error("contract terms for peer pull payment not found in database"); + } + + const contractTerms: PeerContractTerms = contractTermsRecord.contractTermsRaw; + const reservePayto = talerPaytoFromExchangeReserve( pullIni.exchangeBaseUrl, mergeReserve.reservePub, @@ -379,19 +389,19 @@ async function handlePeerPullCreditCreatePurse( const econtractResp = await ws.cryptoApi.encryptContractForDeposit({ contractPriv: pullIni.contractPriv, contractPub: pullIni.contractPub, - contractTerms: pullIni.contractTerms, + contractTerms: contractTermsRecord, pursePriv: pullIni.pursePriv, pursePub: pullIni.pursePub, nonce: pullIni.contractEncNonce, }); - const purseExpiration = pullIni.contractTerms.purse_expiration; + const purseExpiration = contractTerms.purse_expiration; const sigRes = await ws.cryptoApi.signReservePurseCreate({ contractTermsHash: pullIni.contractTermsHash, flags: WalletAccountMergeFlags.CreateWithPurseFee, mergePriv: pullIni.mergePriv, mergeTimestamp: TalerPreciseTimestamp.round(pullIni.mergeTimestamp), - purseAmount: pullIni.contractTerms.amount, + purseAmount: pullIni.amount, purseExpiration: purseExpiration, purseFee: purseFee, pursePriv: pullIni.pursePriv, @@ -410,7 +420,7 @@ async function handlePeerPullCreditCreatePurse( purse_fee: purseFee, purse_pub: pullIni.pursePub, purse_sig: sigRes.purseSig, - purse_value: pullIni.contractTerms.amount, + purse_value: pullIni.amount, reserve_sig: sigRes.accountSig, econtract: econtractResp.econtract, }; @@ -444,15 +454,15 @@ async function handlePeerPullCreditCreatePurse( }); const transitionInfo = await ws.db - .mktx((x) => [x.peerPullPaymentInitiations]) + .mktx((x) => [x.peerPullCredit]) .runReadWrite(async (tx) => { - const pi2 = await tx.peerPullPaymentInitiations.get(pursePub); + const pi2 = await tx.peerPullCredit.get(pursePub); if (!pi2) { return; } const oldTxState = computePeerPullCreditTransactionState(pi2); - pi2.status = PeerPullPaymentInitiationStatus.PendingReady; - await tx.peerPullPaymentInitiations.put(pi2); + pi2.status = PeerPullPaymentCreditStatus.PendingReady; + await tx.peerPullCredit.put(pi2); const newTxState = computePeerPullCreditTransactionState(pi2); return { oldTxState, newTxState }; }); @@ -466,9 +476,9 @@ export async function processPeerPullCredit( pursePub: string, ): Promise<TaskRunResult> { const pullIni = await ws.db - .mktx((x) => [x.peerPullPaymentInitiations]) + .mktx((x) => [x.peerPullCredit]) .runReadOnly(async (tx) => { - return tx.peerPullPaymentInitiations.get(pursePub); + return tx.peerPullCredit.get(pursePub); }); if (!pullIni) { throw Error("peer pull payment initiation not found in database"); @@ -490,10 +500,10 @@ export async function processPeerPullCredit( logger.trace(`processing ${retryTag}, status=${pullIni.status}`); switch (pullIni.status) { - case PeerPullPaymentInitiationStatus.Done: { + case PeerPullPaymentCreditStatus.Done: { return TaskRunResult.finished(); } - case PeerPullPaymentInitiationStatus.PendingReady: + case PeerPullPaymentCreditStatus.PendingReady: runLongpollAsync(ws, retryTag, async (cancellationToken) => queryPurseForPeerPullCredit(ws, pullIni, cancellationToken), ); @@ -503,7 +513,7 @@ export async function processPeerPullCredit( return { type: TaskRunResultType.Longpoll, }; - case PeerPullPaymentInitiationStatus.PendingMergeKycRequired: { + case PeerPullPaymentCreditStatus.PendingMergeKycRequired: { if (!pullIni.kycInfo) { throw Error("invalid state, kycInfo required"); } @@ -515,19 +525,19 @@ export async function processPeerPullCredit( "individual", ); } - case PeerPullPaymentInitiationStatus.PendingCreatePurse: + case PeerPullPaymentCreditStatus.PendingCreatePurse: return handlePeerPullCreditCreatePurse(ws, pullIni); - case PeerPullPaymentInitiationStatus.AbortingDeletePurse: + case PeerPullPaymentCreditStatus.AbortingDeletePurse: return await processPeerPullCreditAbortingDeletePurse(ws, pullIni); - case PeerPullPaymentInitiationStatus.PendingWithdrawing: + case PeerPullPaymentCreditStatus.PendingWithdrawing: return handlePeerPullCreditWithdrawing(ws, pullIni); - case PeerPullPaymentInitiationStatus.Aborted: - case PeerPullPaymentInitiationStatus.Failed: - case PeerPullPaymentInitiationStatus.SuspendedAbortingDeletePurse: - case PeerPullPaymentInitiationStatus.SuspendedCreatePurse: - case PeerPullPaymentInitiationStatus.SuspendedMergeKycRequired: - case PeerPullPaymentInitiationStatus.SuspendedReady: - case PeerPullPaymentInitiationStatus.SuspendedWithdrawing: + case PeerPullPaymentCreditStatus.Aborted: + case PeerPullPaymentCreditStatus.Failed: + case PeerPullPaymentCreditStatus.SuspendedAbortingDeletePurse: + case PeerPullPaymentCreditStatus.SuspendedCreatePurse: + case PeerPullPaymentCreditStatus.SuspendedMergeKycRequired: + case PeerPullPaymentCreditStatus.SuspendedReady: + case PeerPullPaymentCreditStatus.SuspendedWithdrawing: break; default: assertUnreachable(pullIni.status); @@ -538,7 +548,7 @@ export async function processPeerPullCredit( async function processPeerPullCreditKycRequired( ws: InternalWalletState, - peerIni: PeerPullPaymentInitiationRecord, + peerIni: PeerPullCreditRecord, kycPending: WalletKycUuid, ): Promise<TaskRunResult> { const transactionId = constructTransactionIdentifier({ @@ -570,9 +580,9 @@ async function processPeerPullCreditKycRequired( const kycStatus = await kycStatusRes.json(); logger.info(`kyc status: ${j2s(kycStatus)}`); const { transitionInfo, result } = await ws.db - .mktx((x) => [x.peerPullPaymentInitiations]) + .mktx((x) => [x.peerPullCredit]) .runReadWrite(async (tx) => { - const peerInc = await tx.peerPullPaymentInitiations.get(pursePub); + const peerInc = await tx.peerPullCredit.get(pursePub); if (!peerInc) { return { transitionInfo: undefined, @@ -585,10 +595,9 @@ async function processPeerPullCreditKycRequired( requirementRow: kycPending.requirement_row, }; peerInc.kycUrl = kycStatus.kyc_url; - peerInc.status = - PeerPullPaymentInitiationStatus.PendingMergeKycRequired; + peerInc.status = PeerPullPaymentCreditStatus.PendingMergeKycRequired; const newTxState = computePeerPullCreditTransactionState(peerInc); - await tx.peerPullPaymentInitiations.put(peerInc); + await tx.peerPullCredit.put(peerInc); // We'll remove this eventually! New clients should rely on the // kycUrl field of the transaction, not the error code. const res: TaskRunResult = { @@ -758,9 +767,9 @@ export async function initiatePeerPullPayment( ); const transitionInfo = await ws.db - .mktx((x) => [x.peerPullPaymentInitiations, x.contractTerms]) + .mktx((x) => [x.peerPullCredit, x.contractTerms]) .runReadWrite(async (tx) => { - const ppi: PeerPullPaymentInitiationRecord = { + const ppi: PeerPullCreditRecord = { amount: req.partialContractTerms.amount, contractTermsHash: hContractTerms, exchangeBaseUrl: exchangeBaseUrl, @@ -768,8 +777,7 @@ export async function initiatePeerPullPayment( pursePub: pursePair.pub, mergePriv: mergePair.priv, mergePub: mergePair.pub, - status: PeerPullPaymentInitiationStatus.PendingCreatePurse, - contractTerms: contractTerms, + status: PeerPullPaymentCreditStatus.PendingCreatePurse, mergeTimestamp, contractEncNonce, mergeReserveRowId: mergeReserveRowId, @@ -778,7 +786,7 @@ export async function initiatePeerPullPayment( withdrawalGroupId, estimatedAmountEffective: wi.withdrawalAmountEffective, }; - await tx.peerPullPaymentInitiations.put(ppi); + await tx.peerPullCredit.put(ppi); const oldTxState: TransactionState = { major: TransactionMajorState.None, }; @@ -826,39 +834,38 @@ export async function suspendPeerPullCreditTransaction( }); stopLongpolling(ws, taskId); const transitionInfo = await ws.db - .mktx((x) => [x.peerPullPaymentInitiations]) + .mktx((x) => [x.peerPullCredit]) .runReadWrite(async (tx) => { - const pullCreditRec = await tx.peerPullPaymentInitiations.get(pursePub); + const pullCreditRec = await tx.peerPullCredit.get(pursePub); if (!pullCreditRec) { logger.warn(`peer pull credit ${pursePub} not found`); return; } - let newStatus: PeerPullPaymentInitiationStatus | undefined = undefined; + let newStatus: PeerPullPaymentCreditStatus | undefined = undefined; switch (pullCreditRec.status) { - case PeerPullPaymentInitiationStatus.PendingCreatePurse: - newStatus = PeerPullPaymentInitiationStatus.SuspendedCreatePurse; + case PeerPullPaymentCreditStatus.PendingCreatePurse: + newStatus = PeerPullPaymentCreditStatus.SuspendedCreatePurse; break; - case PeerPullPaymentInitiationStatus.PendingMergeKycRequired: - newStatus = PeerPullPaymentInitiationStatus.SuspendedMergeKycRequired; + case PeerPullPaymentCreditStatus.PendingMergeKycRequired: + newStatus = PeerPullPaymentCreditStatus.SuspendedMergeKycRequired; break; - case PeerPullPaymentInitiationStatus.PendingWithdrawing: - newStatus = PeerPullPaymentInitiationStatus.SuspendedWithdrawing; + case PeerPullPaymentCreditStatus.PendingWithdrawing: + newStatus = PeerPullPaymentCreditStatus.SuspendedWithdrawing; break; - case PeerPullPaymentInitiationStatus.PendingReady: - newStatus = PeerPullPaymentInitiationStatus.SuspendedReady; + case PeerPullPaymentCreditStatus.PendingReady: + newStatus = PeerPullPaymentCreditStatus.SuspendedReady; break; - case PeerPullPaymentInitiationStatus.AbortingDeletePurse: - newStatus = - PeerPullPaymentInitiationStatus.SuspendedAbortingDeletePurse; + case PeerPullPaymentCreditStatus.AbortingDeletePurse: + newStatus = PeerPullPaymentCreditStatus.SuspendedAbortingDeletePurse; break; - case PeerPullPaymentInitiationStatus.Done: - case PeerPullPaymentInitiationStatus.SuspendedCreatePurse: - case PeerPullPaymentInitiationStatus.SuspendedMergeKycRequired: - case PeerPullPaymentInitiationStatus.SuspendedReady: - case PeerPullPaymentInitiationStatus.SuspendedWithdrawing: - case PeerPullPaymentInitiationStatus.Aborted: - case PeerPullPaymentInitiationStatus.Failed: - case PeerPullPaymentInitiationStatus.SuspendedAbortingDeletePurse: + case PeerPullPaymentCreditStatus.Done: + case PeerPullPaymentCreditStatus.SuspendedCreatePurse: + case PeerPullPaymentCreditStatus.SuspendedMergeKycRequired: + case PeerPullPaymentCreditStatus.SuspendedReady: + case PeerPullPaymentCreditStatus.SuspendedWithdrawing: + case PeerPullPaymentCreditStatus.Aborted: + case PeerPullPaymentCreditStatus.Failed: + case PeerPullPaymentCreditStatus.SuspendedAbortingDeletePurse: break; default: assertUnreachable(pullCreditRec.status); @@ -867,7 +874,7 @@ export async function suspendPeerPullCreditTransaction( const oldTxState = computePeerPullCreditTransactionState(pullCreditRec); pullCreditRec.status = newStatus; const newTxState = computePeerPullCreditTransactionState(pullCreditRec); - await tx.peerPullPaymentInitiations.put(pullCreditRec); + await tx.peerPullCredit.put(pullCreditRec); return { oldTxState, newTxState, @@ -892,33 +899,33 @@ export async function abortPeerPullCreditTransaction( }); stopLongpolling(ws, taskId); const transitionInfo = await ws.db - .mktx((x) => [x.peerPullPaymentInitiations]) + .mktx((x) => [x.peerPullCredit]) .runReadWrite(async (tx) => { - const pullCreditRec = await tx.peerPullPaymentInitiations.get(pursePub); + const pullCreditRec = await tx.peerPullCredit.get(pursePub); if (!pullCreditRec) { logger.warn(`peer pull credit ${pursePub} not found`); return; } - let newStatus: PeerPullPaymentInitiationStatus | undefined = undefined; + let newStatus: PeerPullPaymentCreditStatus | undefined = undefined; switch (pullCreditRec.status) { - case PeerPullPaymentInitiationStatus.PendingCreatePurse: - case PeerPullPaymentInitiationStatus.PendingMergeKycRequired: - newStatus = PeerPullPaymentInitiationStatus.AbortingDeletePurse; + case PeerPullPaymentCreditStatus.PendingCreatePurse: + case PeerPullPaymentCreditStatus.PendingMergeKycRequired: + newStatus = PeerPullPaymentCreditStatus.AbortingDeletePurse; break; - case PeerPullPaymentInitiationStatus.PendingWithdrawing: + case PeerPullPaymentCreditStatus.PendingWithdrawing: throw Error("can't abort anymore"); - case PeerPullPaymentInitiationStatus.PendingReady: - newStatus = PeerPullPaymentInitiationStatus.AbortingDeletePurse; + case PeerPullPaymentCreditStatus.PendingReady: + newStatus = PeerPullPaymentCreditStatus.AbortingDeletePurse; break; - case PeerPullPaymentInitiationStatus.Done: - case PeerPullPaymentInitiationStatus.SuspendedCreatePurse: - case PeerPullPaymentInitiationStatus.SuspendedMergeKycRequired: - case PeerPullPaymentInitiationStatus.SuspendedReady: - case PeerPullPaymentInitiationStatus.SuspendedWithdrawing: - case PeerPullPaymentInitiationStatus.Aborted: - case PeerPullPaymentInitiationStatus.AbortingDeletePurse: - case PeerPullPaymentInitiationStatus.Failed: - case PeerPullPaymentInitiationStatus.SuspendedAbortingDeletePurse: + case PeerPullPaymentCreditStatus.Done: + case PeerPullPaymentCreditStatus.SuspendedCreatePurse: + case PeerPullPaymentCreditStatus.SuspendedMergeKycRequired: + case PeerPullPaymentCreditStatus.SuspendedReady: + case PeerPullPaymentCreditStatus.SuspendedWithdrawing: + case PeerPullPaymentCreditStatus.Aborted: + case PeerPullPaymentCreditStatus.AbortingDeletePurse: + case PeerPullPaymentCreditStatus.Failed: + case PeerPullPaymentCreditStatus.SuspendedAbortingDeletePurse: break; default: assertUnreachable(pullCreditRec.status); @@ -927,7 +934,7 @@ export async function abortPeerPullCreditTransaction( const oldTxState = computePeerPullCreditTransactionState(pullCreditRec); pullCreditRec.status = newStatus; const newTxState = computePeerPullCreditTransactionState(pullCreditRec); - await tx.peerPullPaymentInitiations.put(pullCreditRec); + await tx.peerPullCredit.put(pullCreditRec); return { oldTxState, newTxState, @@ -952,30 +959,30 @@ export async function failPeerPullCreditTransaction( }); stopLongpolling(ws, taskId); const transitionInfo = await ws.db - .mktx((x) => [x.peerPullPaymentInitiations]) + .mktx((x) => [x.peerPullCredit]) .runReadWrite(async (tx) => { - const pullCreditRec = await tx.peerPullPaymentInitiations.get(pursePub); + const pullCreditRec = await tx.peerPullCredit.get(pursePub); if (!pullCreditRec) { logger.warn(`peer pull credit ${pursePub} not found`); return; } - let newStatus: PeerPullPaymentInitiationStatus | undefined = undefined; + let newStatus: PeerPullPaymentCreditStatus | undefined = undefined; switch (pullCreditRec.status) { - case PeerPullPaymentInitiationStatus.PendingCreatePurse: - case PeerPullPaymentInitiationStatus.PendingMergeKycRequired: - case PeerPullPaymentInitiationStatus.PendingWithdrawing: - case PeerPullPaymentInitiationStatus.PendingReady: - case PeerPullPaymentInitiationStatus.Done: - case PeerPullPaymentInitiationStatus.SuspendedCreatePurse: - case PeerPullPaymentInitiationStatus.SuspendedMergeKycRequired: - case PeerPullPaymentInitiationStatus.SuspendedReady: - case PeerPullPaymentInitiationStatus.SuspendedWithdrawing: - case PeerPullPaymentInitiationStatus.Aborted: - case PeerPullPaymentInitiationStatus.Failed: + case PeerPullPaymentCreditStatus.PendingCreatePurse: + case PeerPullPaymentCreditStatus.PendingMergeKycRequired: + case PeerPullPaymentCreditStatus.PendingWithdrawing: + case PeerPullPaymentCreditStatus.PendingReady: + case PeerPullPaymentCreditStatus.Done: + case PeerPullPaymentCreditStatus.SuspendedCreatePurse: + case PeerPullPaymentCreditStatus.SuspendedMergeKycRequired: + case PeerPullPaymentCreditStatus.SuspendedReady: + case PeerPullPaymentCreditStatus.SuspendedWithdrawing: + case PeerPullPaymentCreditStatus.Aborted: + case PeerPullPaymentCreditStatus.Failed: break; - case PeerPullPaymentInitiationStatus.AbortingDeletePurse: - case PeerPullPaymentInitiationStatus.SuspendedAbortingDeletePurse: - newStatus = PeerPullPaymentInitiationStatus.Failed; + case PeerPullPaymentCreditStatus.AbortingDeletePurse: + case PeerPullPaymentCreditStatus.SuspendedAbortingDeletePurse: + newStatus = PeerPullPaymentCreditStatus.Failed; break; default: assertUnreachable(pullCreditRec.status); @@ -984,7 +991,7 @@ export async function failPeerPullCreditTransaction( const oldTxState = computePeerPullCreditTransactionState(pullCreditRec); pullCreditRec.status = newStatus; const newTxState = computePeerPullCreditTransactionState(pullCreditRec); - await tx.peerPullPaymentInitiations.put(pullCreditRec); + await tx.peerPullCredit.put(pullCreditRec); return { oldTxState, newTxState, @@ -1009,38 +1016,38 @@ export async function resumePeerPullCreditTransaction( }); stopLongpolling(ws, taskId); const transitionInfo = await ws.db - .mktx((x) => [x.peerPullPaymentInitiations]) + .mktx((x) => [x.peerPullCredit]) .runReadWrite(async (tx) => { - const pullCreditRec = await tx.peerPullPaymentInitiations.get(pursePub); + const pullCreditRec = await tx.peerPullCredit.get(pursePub); if (!pullCreditRec) { logger.warn(`peer pull credit ${pursePub} not found`); return; } - let newStatus: PeerPullPaymentInitiationStatus | undefined = undefined; + let newStatus: PeerPullPaymentCreditStatus | undefined = undefined; switch (pullCreditRec.status) { - case PeerPullPaymentInitiationStatus.PendingCreatePurse: - case PeerPullPaymentInitiationStatus.PendingMergeKycRequired: - case PeerPullPaymentInitiationStatus.PendingWithdrawing: - case PeerPullPaymentInitiationStatus.PendingReady: - case PeerPullPaymentInitiationStatus.AbortingDeletePurse: - case PeerPullPaymentInitiationStatus.Done: - case PeerPullPaymentInitiationStatus.Failed: - case PeerPullPaymentInitiationStatus.Aborted: + case PeerPullPaymentCreditStatus.PendingCreatePurse: + case PeerPullPaymentCreditStatus.PendingMergeKycRequired: + case PeerPullPaymentCreditStatus.PendingWithdrawing: + case PeerPullPaymentCreditStatus.PendingReady: + case PeerPullPaymentCreditStatus.AbortingDeletePurse: + case PeerPullPaymentCreditStatus.Done: + case PeerPullPaymentCreditStatus.Failed: + case PeerPullPaymentCreditStatus.Aborted: break; - case PeerPullPaymentInitiationStatus.SuspendedCreatePurse: - newStatus = PeerPullPaymentInitiationStatus.PendingCreatePurse; + case PeerPullPaymentCreditStatus.SuspendedCreatePurse: + newStatus = PeerPullPaymentCreditStatus.PendingCreatePurse; break; - case PeerPullPaymentInitiationStatus.SuspendedMergeKycRequired: - newStatus = PeerPullPaymentInitiationStatus.PendingMergeKycRequired; + case PeerPullPaymentCreditStatus.SuspendedMergeKycRequired: + newStatus = PeerPullPaymentCreditStatus.PendingMergeKycRequired; break; - case PeerPullPaymentInitiationStatus.SuspendedReady: - newStatus = PeerPullPaymentInitiationStatus.PendingReady; + case PeerPullPaymentCreditStatus.SuspendedReady: + newStatus = PeerPullPaymentCreditStatus.PendingReady; break; - case PeerPullPaymentInitiationStatus.SuspendedWithdrawing: - newStatus = PeerPullPaymentInitiationStatus.PendingWithdrawing; + case PeerPullPaymentCreditStatus.SuspendedWithdrawing: + newStatus = PeerPullPaymentCreditStatus.PendingWithdrawing; break; - case PeerPullPaymentInitiationStatus.SuspendedAbortingDeletePurse: - newStatus = PeerPullPaymentInitiationStatus.AbortingDeletePurse; + case PeerPullPaymentCreditStatus.SuspendedAbortingDeletePurse: + newStatus = PeerPullPaymentCreditStatus.AbortingDeletePurse; break; default: assertUnreachable(pullCreditRec.status); @@ -1049,7 +1056,7 @@ export async function resumePeerPullCreditTransaction( const oldTxState = computePeerPullCreditTransactionState(pullCreditRec); pullCreditRec.status = newStatus; const newTxState = computePeerPullCreditTransactionState(pullCreditRec); - await tx.peerPullPaymentInitiations.put(pullCreditRec); + await tx.peerPullCredit.put(pullCreditRec); return { oldTxState, newTxState, @@ -1062,67 +1069,67 @@ export async function resumePeerPullCreditTransaction( } export function computePeerPullCreditTransactionState( - pullCreditRecord: PeerPullPaymentInitiationRecord, + pullCreditRecord: PeerPullCreditRecord, ): TransactionState { switch (pullCreditRecord.status) { - case PeerPullPaymentInitiationStatus.PendingCreatePurse: + case PeerPullPaymentCreditStatus.PendingCreatePurse: return { major: TransactionMajorState.Pending, minor: TransactionMinorState.CreatePurse, }; - case PeerPullPaymentInitiationStatus.PendingMergeKycRequired: + case PeerPullPaymentCreditStatus.PendingMergeKycRequired: return { major: TransactionMajorState.Pending, minor: TransactionMinorState.MergeKycRequired, }; - case PeerPullPaymentInitiationStatus.PendingReady: + case PeerPullPaymentCreditStatus.PendingReady: return { major: TransactionMajorState.Pending, minor: TransactionMinorState.Ready, }; - case PeerPullPaymentInitiationStatus.Done: + case PeerPullPaymentCreditStatus.Done: return { major: TransactionMajorState.Done, }; - case PeerPullPaymentInitiationStatus.PendingWithdrawing: + case PeerPullPaymentCreditStatus.PendingWithdrawing: return { major: TransactionMajorState.Pending, minor: TransactionMinorState.Withdraw, }; - case PeerPullPaymentInitiationStatus.SuspendedCreatePurse: + case PeerPullPaymentCreditStatus.SuspendedCreatePurse: return { major: TransactionMajorState.Suspended, minor: TransactionMinorState.CreatePurse, }; - case PeerPullPaymentInitiationStatus.SuspendedReady: + case PeerPullPaymentCreditStatus.SuspendedReady: return { major: TransactionMajorState.Suspended, minor: TransactionMinorState.Ready, }; - case PeerPullPaymentInitiationStatus.SuspendedWithdrawing: + case PeerPullPaymentCreditStatus.SuspendedWithdrawing: return { major: TransactionMajorState.Pending, minor: TransactionMinorState.Withdraw, }; - case PeerPullPaymentInitiationStatus.SuspendedMergeKycRequired: + case PeerPullPaymentCreditStatus.SuspendedMergeKycRequired: return { major: TransactionMajorState.Suspended, minor: TransactionMinorState.MergeKycRequired, }; - case PeerPullPaymentInitiationStatus.Aborted: + case PeerPullPaymentCreditStatus.Aborted: return { major: TransactionMajorState.Aborted, }; - case PeerPullPaymentInitiationStatus.AbortingDeletePurse: + case PeerPullPaymentCreditStatus.AbortingDeletePurse: return { major: TransactionMajorState.Aborting, minor: TransactionMinorState.DeletePurse, }; - case PeerPullPaymentInitiationStatus.Failed: + case PeerPullPaymentCreditStatus.Failed: return { major: TransactionMajorState.Failed, }; - case PeerPullPaymentInitiationStatus.SuspendedAbortingDeletePurse: + case PeerPullPaymentCreditStatus.SuspendedAbortingDeletePurse: return { major: TransactionMajorState.Aborting, minor: TransactionMinorState.DeletePurse, @@ -1131,34 +1138,34 @@ export function computePeerPullCreditTransactionState( } export function computePeerPullCreditTransactionActions( - pullCreditRecord: PeerPullPaymentInitiationRecord, + pullCreditRecord: PeerPullCreditRecord, ): TransactionAction[] { switch (pullCreditRecord.status) { - case PeerPullPaymentInitiationStatus.PendingCreatePurse: + case PeerPullPaymentCreditStatus.PendingCreatePurse: return [TransactionAction.Abort, TransactionAction.Suspend]; - case PeerPullPaymentInitiationStatus.PendingMergeKycRequired: + case PeerPullPaymentCreditStatus.PendingMergeKycRequired: return [TransactionAction.Abort, TransactionAction.Suspend]; - case PeerPullPaymentInitiationStatus.PendingReady: + case PeerPullPaymentCreditStatus.PendingReady: return [TransactionAction.Abort, TransactionAction.Suspend]; - case PeerPullPaymentInitiationStatus.Done: + case PeerPullPaymentCreditStatus.Done: return [TransactionAction.Delete]; - case PeerPullPaymentInitiationStatus.PendingWithdrawing: + case PeerPullPaymentCreditStatus.PendingWithdrawing: return [TransactionAction.Abort, TransactionAction.Suspend]; - case PeerPullPaymentInitiationStatus.SuspendedCreatePurse: + case PeerPullPaymentCreditStatus.SuspendedCreatePurse: return [TransactionAction.Resume, TransactionAction.Abort]; - case PeerPullPaymentInitiationStatus.SuspendedReady: + case PeerPullPaymentCreditStatus.SuspendedReady: return [TransactionAction.Abort, TransactionAction.Resume]; - case PeerPullPaymentInitiationStatus.SuspendedWithdrawing: + case PeerPullPaymentCreditStatus.SuspendedWithdrawing: return [TransactionAction.Resume, TransactionAction.Fail]; - case PeerPullPaymentInitiationStatus.SuspendedMergeKycRequired: + case PeerPullPaymentCreditStatus.SuspendedMergeKycRequired: return [TransactionAction.Resume, TransactionAction.Fail]; - case PeerPullPaymentInitiationStatus.Aborted: + case PeerPullPaymentCreditStatus.Aborted: return [TransactionAction.Delete]; - case PeerPullPaymentInitiationStatus.AbortingDeletePurse: + case PeerPullPaymentCreditStatus.AbortingDeletePurse: return [TransactionAction.Suspend, TransactionAction.Fail]; - case PeerPullPaymentInitiationStatus.Failed: + case PeerPullPaymentCreditStatus.Failed: return [TransactionAction.Delete]; - case PeerPullPaymentInitiationStatus.SuspendedAbortingDeletePurse: + case PeerPullPaymentCreditStatus.SuspendedAbortingDeletePurse: return [TransactionAction.Resume, TransactionAction.Fail]; } } diff --git a/packages/taler-wallet-core/src/operations/pay-peer-pull-debit.ts b/packages/taler-wallet-core/src/operations/pay-peer-pull-debit.ts index 0de91bf97..f357c41d5 100644 --- a/packages/taler-wallet-core/src/operations/pay-peer-pull-debit.ts +++ b/packages/taler-wallet-core/src/operations/pay-peer-pull-debit.ts @@ -140,10 +140,10 @@ async function handlePurseCreationConflict( ); await ws.db - .mktx((x) => [x.peerPullPaymentIncoming]) + .mktx((x) => [x.peerPullDebit]) .runReadWrite(async (tx) => { - const myPpi = await tx.peerPullPaymentIncoming.get( - peerPullInc.peerPullPaymentIncomingId, + const myPpi = await tx.peerPullDebit.get( + peerPullInc.peerPullDebitId, ); if (!myPpi) { return; @@ -162,7 +162,7 @@ async function handlePurseCreationConflict( default: return; } - await tx.peerPullPaymentIncoming.put(myPpi); + await tx.peerPullDebit.put(myPpi); }); return TaskRunResult.finished(); } @@ -171,7 +171,7 @@ async function processPeerPullDebitPendingDeposit( ws: InternalWalletState, peerPullInc: PeerPullPaymentIncomingRecord, ): Promise<TaskRunResult> { - const peerPullPaymentIncomingId = peerPullInc.peerPullPaymentIncomingId; + const peerPullDebitId = peerPullInc.peerPullDebitId; const pursePub = peerPullInc.pursePub; const coinSel = peerPullInc.coinSel; @@ -202,7 +202,7 @@ async function processPeerPullDebitPendingDeposit( const transactionId = constructTransactionIdentifier({ tag: TransactionType.PeerPullDebit, - peerPullPaymentIncomingId, + peerPullDebitId, }); const httpResp = await ws.http.fetch(purseDepositUrl.href, { @@ -218,10 +218,10 @@ async function processPeerPullDebitPendingDeposit( logger.trace(`purse deposit response: ${j2s(resp)}`); const transitionInfo = await ws.db - .mktx((x) => [x.peerPullPaymentIncoming]) + .mktx((x) => [x.peerPullDebit]) .runReadWrite(async (tx) => { - const pi = await tx.peerPullPaymentIncoming.get( - peerPullPaymentIncomingId, + const pi = await tx.peerPullDebit.get( + peerPullDebitId, ); if (!pi) { throw Error("peer pull payment not found anymore"); @@ -230,9 +230,9 @@ async function processPeerPullDebitPendingDeposit( return; } const oldTxState = computePeerPullDebitTransactionState(pi); - pi.status = PeerPullDebitRecordStatus.DonePaid; + pi.status = PeerPullDebitRecordStatus.Done; const newTxState = computePeerPullDebitTransactionState(pi); - await tx.peerPullPaymentIncoming.put(pi); + await tx.peerPullDebit.put(pi); return { oldTxState, newTxState }; }); notifyTransition(ws, transactionId, transitionInfo); @@ -241,15 +241,15 @@ async function processPeerPullDebitPendingDeposit( case HttpStatusCode.Gone: { const transitionInfo = await ws.db .mktx((x) => [ - x.peerPullPaymentIncoming, + x.peerPullDebit, x.refreshGroups, x.denominations, x.coinAvailability, x.coins, ]) .runReadWrite(async (tx) => { - const pi = await tx.peerPullPaymentIncoming.get( - peerPullPaymentIncomingId, + const pi = await tx.peerPullDebit.get( + peerPullDebitId, ); if (!pi) { throw Error("peer pull payment not found anymore"); @@ -284,7 +284,7 @@ async function processPeerPullDebitPendingDeposit( pi.status = PeerPullDebitRecordStatus.AbortingRefresh; pi.abortRefreshGroupId = refresh.refreshGroupId; const newTxState = computePeerPullDebitTransactionState(pi); - await tx.peerPullPaymentIncoming.put(pi); + await tx.peerPullDebit.put(pi); return { oldTxState, newTxState }; }); notifyTransition(ws, transactionId, transitionInfo); @@ -308,15 +308,15 @@ async function processPeerPullDebitAbortingRefresh( ws: InternalWalletState, peerPullInc: PeerPullPaymentIncomingRecord, ): Promise<TaskRunResult> { - const peerPullPaymentIncomingId = peerPullInc.peerPullPaymentIncomingId; + const peerPullDebitId = peerPullInc.peerPullDebitId; const abortRefreshGroupId = peerPullInc.abortRefreshGroupId; checkLogicInvariant(!!abortRefreshGroupId); const transactionId = constructTransactionIdentifier({ tag: TransactionType.PeerPullDebit, - peerPullPaymentIncomingId, + peerPullDebitId, }); const transitionInfo = await ws.db - .mktx((x) => [x.refreshGroups, x.peerPullPaymentIncoming]) + .mktx((x) => [x.refreshGroups, x.peerPullDebit]) .runReadWrite(async (tx) => { const refreshGroup = await tx.refreshGroups.get(abortRefreshGroupId); let newOpState: PeerPullDebitRecordStatus | undefined; @@ -335,8 +335,8 @@ async function processPeerPullDebitAbortingRefresh( } } if (newOpState) { - const newDg = await tx.peerPullPaymentIncoming.get( - peerPullPaymentIncomingId, + const newDg = await tx.peerPullDebit.get( + peerPullDebitId, ); if (!newDg) { return; @@ -344,7 +344,7 @@ async function processPeerPullDebitAbortingRefresh( const oldTxState = computePeerPullDebitTransactionState(newDg); newDg.status = newOpState; const newTxState = computePeerPullDebitTransactionState(newDg); - await tx.peerPullPaymentIncoming.put(newDg); + await tx.peerPullDebit.put(newDg); return { oldTxState, newTxState }; } return undefined; @@ -356,12 +356,12 @@ async function processPeerPullDebitAbortingRefresh( export async function processPeerPullDebit( ws: InternalWalletState, - peerPullPaymentIncomingId: string, + peerPullDebitId: string, ): Promise<TaskRunResult> { const peerPullInc = await ws.db - .mktx((x) => [x.peerPullPaymentIncoming]) + .mktx((x) => [x.peerPullDebit]) .runReadOnly(async (tx) => { - return tx.peerPullPaymentIncoming.get(peerPullPaymentIncomingId); + return tx.peerPullDebit.get(peerPullDebitId); }); if (!peerPullInc) { throw Error("peer pull debit not found"); @@ -380,31 +380,31 @@ export async function confirmPeerPullDebit( ws: InternalWalletState, req: ConfirmPeerPullDebitRequest, ): Promise<AcceptPeerPullPaymentResponse> { - let peerPullPaymentIncomingId: string; + let peerPullDebitId: string; if (req.transactionId) { const parsedTx = parseTransactionIdentifier(req.transactionId); if (!parsedTx || parsedTx.tag !== TransactionType.PeerPullDebit) { throw Error("invalid peer-pull-debit transaction identifier"); } - peerPullPaymentIncomingId = parsedTx.peerPullPaymentIncomingId; - } else if (req.peerPullPaymentIncomingId) { - peerPullPaymentIncomingId = req.peerPullPaymentIncomingId; + peerPullDebitId = parsedTx.peerPullDebitId; + } else if (req.peerPullDebitId) { + peerPullDebitId = req.peerPullDebitId; } else { throw Error( - "invalid request, transactionId or peerPullPaymentIncomingId required", + "invalid request, transactionId or peerPullDebitId required", ); } const peerPullInc = await ws.db - .mktx((x) => [x.peerPullPaymentIncoming]) + .mktx((x) => [x.peerPullDebit]) .runReadOnly(async (tx) => { - return tx.peerPullPaymentIncoming.get(peerPullPaymentIncomingId); + return tx.peerPullDebit.get(peerPullDebitId); }); if (!peerPullInc) { throw Error( - `can't accept unknown incoming p2p pull payment (${req.peerPullPaymentIncomingId})`, + `can't accept unknown incoming p2p pull payment (${req.peerPullDebitId})`, ); } @@ -437,15 +437,15 @@ export async function confirmPeerPullDebit( x.coins, x.denominations, x.refreshGroups, - x.peerPullPaymentIncoming, + x.peerPullDebit, x.coinAvailability, ]) .runReadWrite(async (tx) => { await spendCoins(ws, tx, { - // allocationId: `txn:peer-pull-debit:${req.peerPullPaymentIncomingId}`, + // allocationId: `txn:peer-pull-debit:${req.peerPullDebitId}`, allocationId: constructTransactionIdentifier({ tag: TransactionType.PeerPullDebit, - peerPullPaymentIncomingId, + peerPullDebitId, }), coinPubs: sel.coins.map((x) => x.coinPub), contributions: sel.coins.map((x) => @@ -454,8 +454,8 @@ export async function confirmPeerPullDebit( refreshReason: RefreshReason.PayPeerPull, }); - const pi = await tx.peerPullPaymentIncoming.get( - peerPullPaymentIncomingId, + const pi = await tx.peerPullDebit.get( + peerPullDebitId, ); if (!pi) { throw Error(); @@ -468,7 +468,7 @@ export async function confirmPeerPullDebit( totalCost: Amounts.stringify(totalAmount), }; } - await tx.peerPullPaymentIncoming.put(pi); + await tx.peerPullDebit.put(pi); return pi; }); @@ -476,7 +476,7 @@ export async function confirmPeerPullDebit( const transactionId = constructTransactionIdentifier({ tag: TransactionType.PeerPullDebit, - peerPullPaymentIncomingId, + peerPullDebitId, }); return { @@ -499,9 +499,9 @@ export async function preparePeerPullDebit( } const existingPullIncomingRecord = await ws.db - .mktx((x) => [x.peerPullPaymentIncoming]) + .mktx((x) => [x.peerPullDebit]) .runReadOnly(async (tx) => { - return tx.peerPullPaymentIncoming.indexes.byExchangeAndContractPriv.get([ + return tx.peerPullDebit.indexes.byExchangeAndContractPriv.get([ uri.exchangeBaseUrl, uri.contractPriv, ]); @@ -513,12 +513,12 @@ export async function preparePeerPullDebit( amountRaw: existingPullIncomingRecord.contractTerms.amount, amountEffective: existingPullIncomingRecord.totalCostEstimated, contractTerms: existingPullIncomingRecord.contractTerms, - peerPullPaymentIncomingId: - existingPullIncomingRecord.peerPullPaymentIncomingId, + peerPullDebitId: + existingPullIncomingRecord.peerPullDebitId, transactionId: constructTransactionIdentifier({ tag: TransactionType.PeerPullDebit, - peerPullPaymentIncomingId: - existingPullIncomingRecord.peerPullPaymentIncomingId, + peerPullDebitId: + existingPullIncomingRecord.peerPullDebitId, }), }; } @@ -553,7 +553,7 @@ export async function preparePeerPullDebit( codecForExchangePurseStatus(), ); - const peerPullPaymentIncomingId = encodeCrock(getRandomBytes(32)); + const peerPullDebitId = encodeCrock(getRandomBytes(32)); let contractTerms: PeerContractTerms; @@ -588,10 +588,10 @@ export async function preparePeerPullDebit( ); await ws.db - .mktx((x) => [x.peerPullPaymentIncoming]) + .mktx((x) => [x.peerPullDebit]) .runReadWrite(async (tx) => { - await tx.peerPullPaymentIncoming.add({ - peerPullPaymentIncomingId, + await tx.peerPullDebit.add({ + peerPullDebitId, contractPriv: contractPriv, exchangeBaseUrl: exchangeBaseUrl, pursePub: pursePub, @@ -607,42 +607,42 @@ export async function preparePeerPullDebit( amountEffective: Amounts.stringify(totalAmount), amountRaw: contractTerms.amount, contractTerms: contractTerms, - peerPullPaymentIncomingId, + peerPullDebitId, transactionId: constructTransactionIdentifier({ tag: TransactionType.PeerPullDebit, - peerPullPaymentIncomingId: peerPullPaymentIncomingId, + peerPullDebitId: peerPullDebitId, }), }; } export async function suspendPeerPullDebitTransaction( ws: InternalWalletState, - peerPullPaymentIncomingId: string, + peerPullDebitId: string, ) { const taskId = constructTaskIdentifier({ tag: PendingTaskType.PeerPullDebit, - peerPullPaymentIncomingId, + peerPullDebitId, }); const transactionId = constructTransactionIdentifier({ tag: TransactionType.PeerPullDebit, - peerPullPaymentIncomingId, + peerPullDebitId, }); stopLongpolling(ws, taskId); const transitionInfo = await ws.db - .mktx((x) => [x.peerPullPaymentIncoming]) + .mktx((x) => [x.peerPullDebit]) .runReadWrite(async (tx) => { - const pullDebitRec = await tx.peerPullPaymentIncoming.get( - peerPullPaymentIncomingId, + const pullDebitRec = await tx.peerPullDebit.get( + peerPullDebitId, ); if (!pullDebitRec) { - logger.warn(`peer pull debit ${peerPullPaymentIncomingId} not found`); + logger.warn(`peer pull debit ${peerPullDebitId} not found`); return; } let newStatus: PeerPullDebitRecordStatus | undefined = undefined; switch (pullDebitRec.status) { case PeerPullDebitRecordStatus.DialogProposed: break; - case PeerPullDebitRecordStatus.DonePaid: + case PeerPullDebitRecordStatus.Done: break; case PeerPullDebitRecordStatus.PendingDeposit: newStatus = PeerPullDebitRecordStatus.SuspendedDeposit; @@ -665,7 +665,7 @@ export async function suspendPeerPullDebitTransaction( const oldTxState = computePeerPullDebitTransactionState(pullDebitRec); pullDebitRec.status = newStatus; const newTxState = computePeerPullDebitTransactionState(pullDebitRec); - await tx.peerPullPaymentIncoming.put(pullDebitRec); + await tx.peerPullDebit.put(pullDebitRec); return { oldTxState, newTxState, @@ -678,25 +678,25 @@ export async function suspendPeerPullDebitTransaction( export async function abortPeerPullDebitTransaction( ws: InternalWalletState, - peerPullPaymentIncomingId: string, + peerPullDebitId: string, ) { const taskId = constructTaskIdentifier({ tag: PendingTaskType.PeerPullDebit, - peerPullPaymentIncomingId, + peerPullDebitId, }); const transactionId = constructTransactionIdentifier({ tag: TransactionType.PeerPullDebit, - peerPullPaymentIncomingId, + peerPullDebitId, }); stopLongpolling(ws, taskId); const transitionInfo = await ws.db - .mktx((x) => [x.peerPullPaymentIncoming]) + .mktx((x) => [x.peerPullDebit]) .runReadWrite(async (tx) => { - const pullDebitRec = await tx.peerPullPaymentIncoming.get( - peerPullPaymentIncomingId, + const pullDebitRec = await tx.peerPullDebit.get( + peerPullDebitId, ); if (!pullDebitRec) { - logger.warn(`peer pull debit ${peerPullPaymentIncomingId} not found`); + logger.warn(`peer pull debit ${peerPullDebitId} not found`); return; } let newStatus: PeerPullDebitRecordStatus | undefined = undefined; @@ -704,7 +704,7 @@ export async function abortPeerPullDebitTransaction( case PeerPullDebitRecordStatus.DialogProposed: newStatus = PeerPullDebitRecordStatus.Aborted; break; - case PeerPullDebitRecordStatus.DonePaid: + case PeerPullDebitRecordStatus.Done: break; case PeerPullDebitRecordStatus.PendingDeposit: newStatus = PeerPullDebitRecordStatus.AbortingRefresh; @@ -726,7 +726,7 @@ export async function abortPeerPullDebitTransaction( const oldTxState = computePeerPullDebitTransactionState(pullDebitRec); pullDebitRec.status = newStatus; const newTxState = computePeerPullDebitTransactionState(pullDebitRec); - await tx.peerPullPaymentIncoming.put(pullDebitRec); + await tx.peerPullDebit.put(pullDebitRec); return { oldTxState, newTxState, @@ -739,25 +739,25 @@ export async function abortPeerPullDebitTransaction( export async function failPeerPullDebitTransaction( ws: InternalWalletState, - peerPullPaymentIncomingId: string, + peerPullDebitId: string, ) { const taskId = constructTaskIdentifier({ tag: PendingTaskType.PeerPullDebit, - peerPullPaymentIncomingId, + peerPullDebitId, }); const transactionId = constructTransactionIdentifier({ tag: TransactionType.PeerPullDebit, - peerPullPaymentIncomingId, + peerPullDebitId, }); stopLongpolling(ws, taskId); const transitionInfo = await ws.db - .mktx((x) => [x.peerPullPaymentIncoming]) + .mktx((x) => [x.peerPullDebit]) .runReadWrite(async (tx) => { - const pullDebitRec = await tx.peerPullPaymentIncoming.get( - peerPullPaymentIncomingId, + const pullDebitRec = await tx.peerPullDebit.get( + peerPullDebitId, ); if (!pullDebitRec) { - logger.warn(`peer pull debit ${peerPullPaymentIncomingId} not found`); + logger.warn(`peer pull debit ${peerPullDebitId} not found`); return; } let newStatus: PeerPullDebitRecordStatus | undefined = undefined; @@ -765,7 +765,7 @@ export async function failPeerPullDebitTransaction( case PeerPullDebitRecordStatus.DialogProposed: newStatus = PeerPullDebitRecordStatus.Aborted; break; - case PeerPullDebitRecordStatus.DonePaid: + case PeerPullDebitRecordStatus.Done: break; case PeerPullDebitRecordStatus.PendingDeposit: break; @@ -787,7 +787,7 @@ export async function failPeerPullDebitTransaction( const oldTxState = computePeerPullDebitTransactionState(pullDebitRec); pullDebitRec.status = newStatus; const newTxState = computePeerPullDebitTransactionState(pullDebitRec); - await tx.peerPullPaymentIncoming.put(pullDebitRec); + await tx.peerPullDebit.put(pullDebitRec); return { oldTxState, newTxState, @@ -800,31 +800,31 @@ export async function failPeerPullDebitTransaction( export async function resumePeerPullDebitTransaction( ws: InternalWalletState, - peerPullPaymentIncomingId: string, + peerPullDebitId: string, ) { const taskId = constructTaskIdentifier({ tag: PendingTaskType.PeerPullDebit, - peerPullPaymentIncomingId, + peerPullDebitId, }); const transactionId = constructTransactionIdentifier({ tag: TransactionType.PeerPullDebit, - peerPullPaymentIncomingId, + peerPullDebitId, }); stopLongpolling(ws, taskId); const transitionInfo = await ws.db - .mktx((x) => [x.peerPullPaymentIncoming]) + .mktx((x) => [x.peerPullDebit]) .runReadWrite(async (tx) => { - const pullDebitRec = await tx.peerPullPaymentIncoming.get( - peerPullPaymentIncomingId, + const pullDebitRec = await tx.peerPullDebit.get( + peerPullDebitId, ); if (!pullDebitRec) { - logger.warn(`peer pull debit ${peerPullPaymentIncomingId} not found`); + logger.warn(`peer pull debit ${peerPullDebitId} not found`); return; } let newStatus: PeerPullDebitRecordStatus | undefined = undefined; switch (pullDebitRec.status) { case PeerPullDebitRecordStatus.DialogProposed: - case PeerPullDebitRecordStatus.DonePaid: + case PeerPullDebitRecordStatus.Done: case PeerPullDebitRecordStatus.PendingDeposit: break; case PeerPullDebitRecordStatus.SuspendedDeposit: @@ -846,7 +846,7 @@ export async function resumePeerPullDebitTransaction( const oldTxState = computePeerPullDebitTransactionState(pullDebitRec); pullDebitRec.status = newStatus; const newTxState = computePeerPullDebitTransactionState(pullDebitRec); - await tx.peerPullPaymentIncoming.put(pullDebitRec); + await tx.peerPullDebit.put(pullDebitRec); return { oldTxState, newTxState, @@ -872,7 +872,7 @@ export function computePeerPullDebitTransactionState( major: TransactionMajorState.Pending, minor: TransactionMinorState.Deposit, }; - case PeerPullDebitRecordStatus.DonePaid: + case PeerPullDebitRecordStatus.Done: return { major: TransactionMajorState.Done, }; @@ -910,7 +910,7 @@ export function computePeerPullDebitTransactionActions( return []; case PeerPullDebitRecordStatus.PendingDeposit: return [TransactionAction.Abort, TransactionAction.Suspend]; - case PeerPullDebitRecordStatus.DonePaid: + case PeerPullDebitRecordStatus.Done: return [TransactionAction.Delete]; case PeerPullDebitRecordStatus.SuspendedDeposit: return [TransactionAction.Resume, TransactionAction.Abort]; diff --git a/packages/taler-wallet-core/src/operations/pay-peer-push-credit.ts b/packages/taler-wallet-core/src/operations/pay-peer-push-credit.ts index 47e9eaddd..89d9e3b49 100644 --- a/packages/taler-wallet-core/src/operations/pay-peer-push-credit.ts +++ b/packages/taler-wallet-core/src/operations/pay-peer-push-credit.ts @@ -55,7 +55,7 @@ import { KycPendingInfo, KycUserType, PeerPushPaymentIncomingRecord, - PeerPushPaymentIncomingStatus, + PeerPushCreditStatus, PendingTaskType, WithdrawalGroupStatus, WithdrawalRecordType, @@ -99,10 +99,10 @@ export async function preparePeerPushCredit( } const existing = await ws.db - .mktx((x) => [x.contractTerms, x.peerPushPaymentIncoming]) + .mktx((x) => [x.contractTerms, x.peerPushCredit]) .runReadOnly(async (tx) => { const existingPushInc = - await tx.peerPushPaymentIncoming.indexes.byExchangeAndContractPriv.get([ + await tx.peerPushCredit.indexes.byExchangeAndContractPriv.get([ uri.exchangeBaseUrl, uri.contractPriv, ]); @@ -129,12 +129,12 @@ export async function preparePeerPushCredit( amountEffective: existing.existingPushInc.estimatedAmountEffective, amountRaw: existing.existingContractTerms.amount, contractTerms: existing.existingContractTerms, - peerPushPaymentIncomingId: - existing.existingPushInc.peerPushPaymentIncomingId, + peerPushCreditId: + existing.existingPushInc.peerPushCreditId, transactionId: constructTransactionIdentifier({ tag: TransactionType.PeerPushCredit, - peerPushPaymentIncomingId: - existing.existingPushInc.peerPushPaymentIncomingId, + peerPushCreditId: + existing.existingPushInc.peerPushCreditId, }), }; } @@ -172,7 +172,7 @@ export async function preparePeerPushCredit( codecForExchangePurseStatus(), ); - const peerPushPaymentIncomingId = encodeCrock(getRandomBytes(32)); + const peerPushCreditId = encodeCrock(getRandomBytes(32)); const contractTermsHash = ContractTermsUtil.hashContractTerms( dec.contractTerms, @@ -188,17 +188,17 @@ export async function preparePeerPushCredit( ); await ws.db - .mktx((x) => [x.contractTerms, x.peerPushPaymentIncoming]) + .mktx((x) => [x.contractTerms, x.peerPushCredit]) .runReadWrite(async (tx) => { - await tx.peerPushPaymentIncoming.add({ - peerPushPaymentIncomingId, + await tx.peerPushCredit.add({ + peerPushCreditId, contractPriv: contractPriv, exchangeBaseUrl: exchangeBaseUrl, mergePriv: dec.mergePriv, pursePub: pursePub, timestamp: TalerPreciseTimestamp.now(), contractTermsHash, - status: PeerPushPaymentIncomingStatus.DialogProposed, + status: PeerPushCreditStatus.DialogProposed, withdrawalGroupId, currency: Amounts.currencyOf(purseStatus.balance), estimatedAmountEffective: Amounts.stringify( @@ -219,28 +219,28 @@ export async function preparePeerPushCredit( amountEffective: wi.withdrawalAmountEffective, amountRaw: purseStatus.balance, contractTerms: dec.contractTerms, - peerPushPaymentIncomingId, + peerPushCreditId, transactionId: constructTransactionIdentifier({ tag: TransactionType.PeerPushCredit, - peerPushPaymentIncomingId, + peerPushCreditId, }), }; } async function longpollKycStatus( ws: InternalWalletState, - peerPushPaymentIncomingId: string, + peerPushCreditId: string, exchangeUrl: string, kycInfo: KycPendingInfo, userType: KycUserType, ): Promise<TaskRunResult> { const transactionId = constructTransactionIdentifier({ tag: TransactionType.PeerPushCredit, - peerPushPaymentIncomingId, + peerPushCreditId, }); const retryTag = constructTaskIdentifier({ tag: PendingTaskType.PeerPushCredit, - peerPushPaymentIncomingId, + peerPushCreditId, }); runLongpollAsync(ws, retryTag, async (ct) => { @@ -261,24 +261,24 @@ async function longpollKycStatus( kycStatusRes.status === HttpStatusCode.NoContent ) { const transitionInfo = await ws.db - .mktx((x) => [x.peerPushPaymentIncoming]) + .mktx((x) => [x.peerPushCredit]) .runReadWrite(async (tx) => { - const peerInc = await tx.peerPushPaymentIncoming.get( - peerPushPaymentIncomingId, + const peerInc = await tx.peerPushCredit.get( + peerPushCreditId, ); if (!peerInc) { return; } if ( peerInc.status !== - PeerPushPaymentIncomingStatus.PendingMergeKycRequired + PeerPushCreditStatus.PendingMergeKycRequired ) { return; } const oldTxState = computePeerPushCreditTransactionState(peerInc); - peerInc.status = PeerPushPaymentIncomingStatus.PendingMerge; + peerInc.status = PeerPushCreditStatus.PendingMerge; const newTxState = computePeerPushCreditTransactionState(peerInc); - await tx.peerPushPaymentIncoming.put(peerInc); + await tx.peerPushCredit.put(peerInc); return { oldTxState, newTxState }; }); notifyTransition(ws, transactionId, transitionInfo); @@ -304,9 +304,9 @@ async function processPeerPushCreditKycRequired( ): Promise<TaskRunResult> { const transactionId = constructTransactionIdentifier({ tag: TransactionType.PeerPushCredit, - peerPushPaymentIncomingId: peerInc.peerPushPaymentIncomingId, + peerPushCreditId: peerInc.peerPushCreditId, }); - const { peerPushPaymentIncomingId } = peerInc; + const { peerPushCreditId } = peerInc; const userType = "individual"; const url = new URL( @@ -331,10 +331,10 @@ async function processPeerPushCreditKycRequired( const kycStatus = await kycStatusRes.json(); logger.info(`kyc status: ${j2s(kycStatus)}`); const { transitionInfo, result } = await ws.db - .mktx((x) => [x.peerPushPaymentIncoming]) + .mktx((x) => [x.peerPushCredit]) .runReadWrite(async (tx) => { - const peerInc = await tx.peerPushPaymentIncoming.get( - peerPushPaymentIncomingId, + const peerInc = await tx.peerPushCredit.get( + peerPushCreditId, ); if (!peerInc) { return { @@ -348,9 +348,9 @@ async function processPeerPushCreditKycRequired( requirementRow: kycPending.requirement_row, }; peerInc.kycUrl = kycStatus.kyc_url; - peerInc.status = PeerPushPaymentIncomingStatus.PendingMergeKycRequired; + peerInc.status = PeerPushCreditStatus.PendingMergeKycRequired; const newTxState = computePeerPushCreditTransactionState(peerInc); - await tx.peerPushPaymentIncoming.put(peerInc); + await tx.peerPushCredit.put(peerInc); // We'll remove this eventually! New clients should rely on the // kycUrl field of the transaction, not the error code. const res: TaskRunResult = { @@ -379,10 +379,10 @@ async function handlePendingMerge( peerInc: PeerPushPaymentIncomingRecord, contractTerms: PeerContractTerms, ): Promise<TaskRunResult> { - const { peerPushPaymentIncomingId } = peerInc; + const { peerPushCreditId } = peerInc; const transactionId = constructTransactionIdentifier({ tag: TransactionType.PeerPushCredit, - peerPushPaymentIncomingId, + peerPushCreditId, }); const amount = Amounts.parseOrThrow(contractTerms.amount); @@ -446,7 +446,6 @@ async function handlePendingMerge( amount, wgInfo: { withdrawalType: WithdrawalRecordType.PeerPushCredit, - contractTerms, }, forcedWithdrawalGroupId: peerInc.withdrawalGroupId, exchangeBaseUrl: peerInc.exchangeBaseUrl, @@ -460,16 +459,15 @@ async function handlePendingMerge( const txRes = await ws.db .mktx((x) => [ x.contractTerms, - x.peerPushPaymentIncoming, + x.peerPushCredit, x.withdrawalGroups, x.reserves, x.exchanges, x.exchangeDetails, - x.exchangeTrust, ]) .runReadWrite(async (tx) => { - const peerInc = await tx.peerPushPaymentIncoming.get( - peerPushPaymentIncomingId, + const peerInc = await tx.peerPushCredit.get( + peerPushCreditId, ); if (!peerInc) { return undefined; @@ -477,9 +475,9 @@ async function handlePendingMerge( let withdrawalTransition: TransitionInfo | undefined; const oldTxState = computePeerPushCreditTransactionState(peerInc); switch (peerInc.status) { - case PeerPushPaymentIncomingStatus.PendingMerge: - case PeerPushPaymentIncomingStatus.PendingMergeKycRequired: { - peerInc.status = PeerPushPaymentIncomingStatus.PendingWithdrawing; + case PeerPushCreditStatus.PendingMerge: + case PeerPushCreditStatus.PendingMergeKycRequired: { + peerInc.status = PeerPushCreditStatus.PendingWithdrawing; const wgRes = await internalPerformCreateWithdrawalGroup( ws, tx, @@ -489,7 +487,7 @@ async function handlePendingMerge( break; } } - await tx.peerPushPaymentIncoming.put(peerInc); + await tx.peerPushCredit.put(peerInc); const newTxState = computePeerPushCreditTransactionState(peerInc); return { peerPushCreditTransition: { oldTxState, newTxState }, @@ -515,21 +513,21 @@ async function handlePendingWithdrawing( } const transactionId = constructTransactionIdentifier({ tag: TransactionType.PeerPushCredit, - peerPushPaymentIncomingId: peerInc.peerPushPaymentIncomingId, + peerPushCreditId: peerInc.peerPushCreditId, }); const wgId = peerInc.withdrawalGroupId; let finished: boolean = false; const transitionInfo = await ws.db - .mktx((x) => [x.peerPushPaymentIncoming, x.withdrawalGroups]) + .mktx((x) => [x.peerPushCredit, x.withdrawalGroups]) .runReadWrite(async (tx) => { - const ppi = await tx.peerPushPaymentIncoming.get( - peerInc.peerPushPaymentIncomingId, + const ppi = await tx.peerPushCredit.get( + peerInc.peerPushCreditId, ); if (!ppi) { finished = true; return; } - if (ppi.status !== PeerPushPaymentIncomingStatus.PendingWithdrawing) { + if (ppi.status !== PeerPushCreditStatus.PendingWithdrawing) { finished = true; return; } @@ -540,13 +538,13 @@ async function handlePendingWithdrawing( return undefined; } switch (wg.status) { - case WithdrawalGroupStatus.Finished: + case WithdrawalGroupStatus.Done: finished = true; - ppi.status = PeerPushPaymentIncomingStatus.Done; + ppi.status = PeerPushCreditStatus.Done; break; // FIXME: Also handle other final states! } - await tx.peerPushPaymentIncoming.put(ppi); + await tx.peerPushCredit.put(ppi); const newTxState = computePeerPushCreditTransactionState(ppi); return { oldTxState, @@ -564,14 +562,14 @@ async function handlePendingWithdrawing( export async function processPeerPushCredit( ws: InternalWalletState, - peerPushPaymentIncomingId: string, + peerPushCreditId: string, ): Promise<TaskRunResult> { let peerInc: PeerPushPaymentIncomingRecord | undefined; let contractTerms: PeerContractTerms | undefined; await ws.db - .mktx((x) => [x.contractTerms, x.peerPushPaymentIncoming]) + .mktx((x) => [x.contractTerms, x.peerPushCredit]) .runReadWrite(async (tx) => { - peerInc = await tx.peerPushPaymentIncoming.get(peerPushPaymentIncomingId); + peerInc = await tx.peerPushCredit.get(peerPushCreditId); if (!peerInc) { return; } @@ -579,35 +577,35 @@ export async function processPeerPushCredit( if (ctRec) { contractTerms = ctRec.contractTermsRaw; } - await tx.peerPushPaymentIncoming.put(peerInc); + await tx.peerPushCredit.put(peerInc); }); checkDbInvariant(!!contractTerms); if (!peerInc) { throw Error( - `can't accept unknown incoming p2p push payment (${peerPushPaymentIncomingId})`, + `can't accept unknown incoming p2p push payment (${peerPushCreditId})`, ); } switch (peerInc.status) { - case PeerPushPaymentIncomingStatus.PendingMergeKycRequired: { + case PeerPushCreditStatus.PendingMergeKycRequired: { if (!peerInc.kycInfo) { throw Error("invalid state, kycInfo required"); } return await longpollKycStatus( ws, - peerPushPaymentIncomingId, + peerPushCreditId, peerInc.exchangeBaseUrl, peerInc.kycInfo, "individual", ); } - case PeerPushPaymentIncomingStatus.PendingMerge: + case PeerPushCreditStatus.PendingMerge: return handlePendingMerge(ws, peerInc, contractTerms); - case PeerPushPaymentIncomingStatus.PendingWithdrawing: + case PeerPushCreditStatus.PendingWithdrawing: return handlePendingWithdrawing(ws, peerInc); default: @@ -620,9 +618,9 @@ export async function confirmPeerPushCredit( req: ConfirmPeerPushCreditRequest, ): Promise<AcceptPeerPushPaymentResponse> { let peerInc: PeerPushPaymentIncomingRecord | undefined; - let peerPushPaymentIncomingId: string; - if (req.peerPushPaymentIncomingId) { - peerPushPaymentIncomingId = req.peerPushPaymentIncomingId; + let peerPushCreditId: string; + if (req.peerPushCreditId) { + peerPushCreditId = req.peerPushCreditId; } else if (req.transactionId) { const parsedTx = parseTransactionIdentifier(req.transactionId); if (!parsedTx) { @@ -631,29 +629,29 @@ export async function confirmPeerPushCredit( if (parsedTx.tag !== TransactionType.PeerPushCredit) { throw Error("invalid transaction ID type"); } - peerPushPaymentIncomingId = parsedTx.peerPushPaymentIncomingId; + peerPushCreditId = parsedTx.peerPushCreditId; } else { throw Error( - "no transaction ID (or deprecated peerPushPaymentIncomingId) provided", + "no transaction ID (or deprecated peerPushCreditId) provided", ); } await ws.db - .mktx((x) => [x.contractTerms, x.peerPushPaymentIncoming]) + .mktx((x) => [x.contractTerms, x.peerPushCredit]) .runReadWrite(async (tx) => { - peerInc = await tx.peerPushPaymentIncoming.get(peerPushPaymentIncomingId); + peerInc = await tx.peerPushCredit.get(peerPushCreditId); if (!peerInc) { return; } - if (peerInc.status === PeerPushPaymentIncomingStatus.DialogProposed) { - peerInc.status = PeerPushPaymentIncomingStatus.PendingMerge; + if (peerInc.status === PeerPushCreditStatus.DialogProposed) { + peerInc.status = PeerPushCreditStatus.PendingMerge; } - await tx.peerPushPaymentIncoming.put(peerInc); + await tx.peerPushCredit.put(peerInc); }); if (!peerInc) { throw Error( - `can't accept unknown incoming p2p push payment (${req.peerPushPaymentIncomingId})`, + `can't accept unknown incoming p2p push payment (${req.peerPushCreditId})`, ); } @@ -661,7 +659,7 @@ export async function confirmPeerPushCredit( const transactionId = constructTransactionIdentifier({ tag: TransactionType.PeerPushCredit, - peerPushPaymentIncomingId, + peerPushCreditId, }); return { @@ -671,48 +669,48 @@ export async function confirmPeerPushCredit( export async function suspendPeerPushCreditTransaction( ws: InternalWalletState, - peerPushPaymentIncomingId: string, + peerPushCreditId: string, ) { const taskId = constructTaskIdentifier({ tag: PendingTaskType.PeerPushCredit, - peerPushPaymentIncomingId, + peerPushCreditId, }); const transactionId = constructTransactionIdentifier({ tag: TransactionType.PeerPushCredit, - peerPushPaymentIncomingId, + peerPushCreditId, }); stopLongpolling(ws, taskId); const transitionInfo = await ws.db - .mktx((x) => [x.peerPushPaymentIncoming]) + .mktx((x) => [x.peerPushCredit]) .runReadWrite(async (tx) => { - const pushCreditRec = await tx.peerPushPaymentIncoming.get( - peerPushPaymentIncomingId, + const pushCreditRec = await tx.peerPushCredit.get( + peerPushCreditId, ); if (!pushCreditRec) { - logger.warn(`peer push credit ${peerPushPaymentIncomingId} not found`); + logger.warn(`peer push credit ${peerPushCreditId} not found`); return; } - let newStatus: PeerPushPaymentIncomingStatus | undefined = undefined; + let newStatus: PeerPushCreditStatus | undefined = undefined; switch (pushCreditRec.status) { - case PeerPushPaymentIncomingStatus.DialogProposed: - case PeerPushPaymentIncomingStatus.Done: - case PeerPushPaymentIncomingStatus.SuspendedMerge: - case PeerPushPaymentIncomingStatus.SuspendedMergeKycRequired: - case PeerPushPaymentIncomingStatus.SuspendedWithdrawing: + case PeerPushCreditStatus.DialogProposed: + case PeerPushCreditStatus.Done: + case PeerPushCreditStatus.SuspendedMerge: + case PeerPushCreditStatus.SuspendedMergeKycRequired: + case PeerPushCreditStatus.SuspendedWithdrawing: break; - case PeerPushPaymentIncomingStatus.PendingMergeKycRequired: - newStatus = PeerPushPaymentIncomingStatus.SuspendedMergeKycRequired; + case PeerPushCreditStatus.PendingMergeKycRequired: + newStatus = PeerPushCreditStatus.SuspendedMergeKycRequired; break; - case PeerPushPaymentIncomingStatus.PendingMerge: - newStatus = PeerPushPaymentIncomingStatus.SuspendedMerge; + case PeerPushCreditStatus.PendingMerge: + newStatus = PeerPushCreditStatus.SuspendedMerge; break; - case PeerPushPaymentIncomingStatus.PendingWithdrawing: + case PeerPushCreditStatus.PendingWithdrawing: // FIXME: Suspend internal withdrawal transaction! - newStatus = PeerPushPaymentIncomingStatus.SuspendedWithdrawing; + newStatus = PeerPushCreditStatus.SuspendedWithdrawing; break; - case PeerPushPaymentIncomingStatus.Aborted: + case PeerPushCreditStatus.Aborted: break; - case PeerPushPaymentIncomingStatus.Failed: + case PeerPushCreditStatus.Failed: break; default: assertUnreachable(pushCreditRec.status); @@ -721,7 +719,7 @@ export async function suspendPeerPushCreditTransaction( const oldTxState = computePeerPushCreditTransactionState(pushCreditRec); pushCreditRec.status = newStatus; const newTxState = computePeerPushCreditTransactionState(pushCreditRec); - await tx.peerPushPaymentIncoming.put(pushCreditRec); + await tx.peerPushCredit.put(pushCreditRec); return { oldTxState, newTxState, @@ -734,51 +732,51 @@ export async function suspendPeerPushCreditTransaction( export async function abortPeerPushCreditTransaction( ws: InternalWalletState, - peerPushPaymentIncomingId: string, + peerPushCreditId: string, ) { const taskId = constructTaskIdentifier({ tag: PendingTaskType.PeerPushCredit, - peerPushPaymentIncomingId, + peerPushCreditId, }); const transactionId = constructTransactionIdentifier({ tag: TransactionType.PeerPushCredit, - peerPushPaymentIncomingId, + peerPushCreditId, }); stopLongpolling(ws, taskId); const transitionInfo = await ws.db - .mktx((x) => [x.peerPushPaymentIncoming]) + .mktx((x) => [x.peerPushCredit]) .runReadWrite(async (tx) => { - const pushCreditRec = await tx.peerPushPaymentIncoming.get( - peerPushPaymentIncomingId, + const pushCreditRec = await tx.peerPushCredit.get( + peerPushCreditId, ); if (!pushCreditRec) { - logger.warn(`peer push credit ${peerPushPaymentIncomingId} not found`); + logger.warn(`peer push credit ${peerPushCreditId} not found`); return; } - let newStatus: PeerPushPaymentIncomingStatus | undefined = undefined; + let newStatus: PeerPushCreditStatus | undefined = undefined; switch (pushCreditRec.status) { - case PeerPushPaymentIncomingStatus.DialogProposed: - newStatus = PeerPushPaymentIncomingStatus.Aborted; + case PeerPushCreditStatus.DialogProposed: + newStatus = PeerPushCreditStatus.Aborted; break; - case PeerPushPaymentIncomingStatus.Done: + case PeerPushCreditStatus.Done: break; - case PeerPushPaymentIncomingStatus.SuspendedMerge: - case PeerPushPaymentIncomingStatus.SuspendedMergeKycRequired: - case PeerPushPaymentIncomingStatus.SuspendedWithdrawing: - newStatus = PeerPushPaymentIncomingStatus.Aborted; + case PeerPushCreditStatus.SuspendedMerge: + case PeerPushCreditStatus.SuspendedMergeKycRequired: + case PeerPushCreditStatus.SuspendedWithdrawing: + newStatus = PeerPushCreditStatus.Aborted; break; - case PeerPushPaymentIncomingStatus.PendingMergeKycRequired: - newStatus = PeerPushPaymentIncomingStatus.Aborted; + case PeerPushCreditStatus.PendingMergeKycRequired: + newStatus = PeerPushCreditStatus.Aborted; break; - case PeerPushPaymentIncomingStatus.PendingMerge: - newStatus = PeerPushPaymentIncomingStatus.Aborted; + case PeerPushCreditStatus.PendingMerge: + newStatus = PeerPushCreditStatus.Aborted; break; - case PeerPushPaymentIncomingStatus.PendingWithdrawing: - newStatus = PeerPushPaymentIncomingStatus.Aborted; + case PeerPushCreditStatus.PendingWithdrawing: + newStatus = PeerPushCreditStatus.Aborted; break; - case PeerPushPaymentIncomingStatus.Aborted: + case PeerPushCreditStatus.Aborted: break; - case PeerPushPaymentIncomingStatus.Failed: + case PeerPushCreditStatus.Failed: break; default: assertUnreachable(pushCreditRec.status); @@ -787,7 +785,7 @@ export async function abortPeerPushCreditTransaction( const oldTxState = computePeerPushCreditTransactionState(pushCreditRec); pushCreditRec.status = newStatus; const newTxState = computePeerPushCreditTransactionState(pushCreditRec); - await tx.peerPushPaymentIncoming.put(pushCreditRec); + await tx.peerPushCredit.put(pushCreditRec); return { oldTxState, newTxState, @@ -800,7 +798,7 @@ export async function abortPeerPushCreditTransaction( export async function failPeerPushCreditTransaction( ws: InternalWalletState, - peerPushPaymentIncomingId: string, + peerPushCreditId: string, ) { // We don't have any "aborting" states! throw Error("can't run cancel-aborting on peer-push-credit transaction"); @@ -808,47 +806,47 @@ export async function failPeerPushCreditTransaction( export async function resumePeerPushCreditTransaction( ws: InternalWalletState, - peerPushPaymentIncomingId: string, + peerPushCreditId: string, ) { const taskId = constructTaskIdentifier({ tag: PendingTaskType.PeerPushCredit, - peerPushPaymentIncomingId, + peerPushCreditId, }); const transactionId = constructTransactionIdentifier({ tag: TransactionType.PeerPushCredit, - peerPushPaymentIncomingId, + peerPushCreditId, }); stopLongpolling(ws, taskId); const transitionInfo = await ws.db - .mktx((x) => [x.peerPushPaymentIncoming]) + .mktx((x) => [x.peerPushCredit]) .runReadWrite(async (tx) => { - const pushCreditRec = await tx.peerPushPaymentIncoming.get( - peerPushPaymentIncomingId, + const pushCreditRec = await tx.peerPushCredit.get( + peerPushCreditId, ); if (!pushCreditRec) { - logger.warn(`peer push credit ${peerPushPaymentIncomingId} not found`); + logger.warn(`peer push credit ${peerPushCreditId} not found`); return; } - let newStatus: PeerPushPaymentIncomingStatus | undefined = undefined; + let newStatus: PeerPushCreditStatus | undefined = undefined; switch (pushCreditRec.status) { - case PeerPushPaymentIncomingStatus.DialogProposed: - case PeerPushPaymentIncomingStatus.Done: - case PeerPushPaymentIncomingStatus.PendingMergeKycRequired: - case PeerPushPaymentIncomingStatus.PendingMerge: - case PeerPushPaymentIncomingStatus.PendingWithdrawing: - case PeerPushPaymentIncomingStatus.SuspendedMerge: - newStatus = PeerPushPaymentIncomingStatus.PendingMerge; + case PeerPushCreditStatus.DialogProposed: + case PeerPushCreditStatus.Done: + case PeerPushCreditStatus.PendingMergeKycRequired: + case PeerPushCreditStatus.PendingMerge: + case PeerPushCreditStatus.PendingWithdrawing: + case PeerPushCreditStatus.SuspendedMerge: + newStatus = PeerPushCreditStatus.PendingMerge; break; - case PeerPushPaymentIncomingStatus.SuspendedMergeKycRequired: - newStatus = PeerPushPaymentIncomingStatus.PendingMergeKycRequired; + case PeerPushCreditStatus.SuspendedMergeKycRequired: + newStatus = PeerPushCreditStatus.PendingMergeKycRequired; break; - case PeerPushPaymentIncomingStatus.SuspendedWithdrawing: + case PeerPushCreditStatus.SuspendedWithdrawing: // FIXME: resume underlying "internal-withdrawal" transaction. - newStatus = PeerPushPaymentIncomingStatus.PendingWithdrawing; + newStatus = PeerPushCreditStatus.PendingWithdrawing; break; - case PeerPushPaymentIncomingStatus.Aborted: + case PeerPushCreditStatus.Aborted: break; - case PeerPushPaymentIncomingStatus.Failed: + case PeerPushCreditStatus.Failed: break; default: assertUnreachable(pushCreditRec.status); @@ -857,7 +855,7 @@ export async function resumePeerPushCreditTransaction( const oldTxState = computePeerPushCreditTransactionState(pushCreditRec); pushCreditRec.status = newStatus; const newTxState = computePeerPushCreditTransactionState(pushCreditRec); - await tx.peerPushPaymentIncoming.put(pushCreditRec); + await tx.peerPushCredit.put(pushCreditRec); return { oldTxState, newTxState, @@ -873,50 +871,50 @@ export function computePeerPushCreditTransactionState( pushCreditRecord: PeerPushPaymentIncomingRecord, ): TransactionState { switch (pushCreditRecord.status) { - case PeerPushPaymentIncomingStatus.DialogProposed: + case PeerPushCreditStatus.DialogProposed: return { major: TransactionMajorState.Dialog, minor: TransactionMinorState.Proposed, }; - case PeerPushPaymentIncomingStatus.PendingMerge: + case PeerPushCreditStatus.PendingMerge: return { major: TransactionMajorState.Pending, minor: TransactionMinorState.Merge, }; - case PeerPushPaymentIncomingStatus.Done: + case PeerPushCreditStatus.Done: return { major: TransactionMajorState.Done, }; - case PeerPushPaymentIncomingStatus.PendingMergeKycRequired: + case PeerPushCreditStatus.PendingMergeKycRequired: return { major: TransactionMajorState.Pending, minor: TransactionMinorState.KycRequired, }; - case PeerPushPaymentIncomingStatus.PendingWithdrawing: + case PeerPushCreditStatus.PendingWithdrawing: return { major: TransactionMajorState.Pending, minor: TransactionMinorState.Withdraw, }; - case PeerPushPaymentIncomingStatus.SuspendedMerge: + case PeerPushCreditStatus.SuspendedMerge: return { major: TransactionMajorState.Suspended, minor: TransactionMinorState.Merge, }; - case PeerPushPaymentIncomingStatus.SuspendedMergeKycRequired: + case PeerPushCreditStatus.SuspendedMergeKycRequired: return { major: TransactionMajorState.Suspended, minor: TransactionMinorState.MergeKycRequired, }; - case PeerPushPaymentIncomingStatus.SuspendedWithdrawing: + case PeerPushCreditStatus.SuspendedWithdrawing: return { major: TransactionMajorState.Suspended, minor: TransactionMinorState.Withdraw, }; - case PeerPushPaymentIncomingStatus.Aborted: + case PeerPushCreditStatus.Aborted: return { major: TransactionMajorState.Aborted, }; - case PeerPushPaymentIncomingStatus.Failed: + case PeerPushCreditStatus.Failed: return { major: TransactionMajorState.Failed, }; @@ -929,25 +927,25 @@ export function computePeerPushCreditTransactionActions( pushCreditRecord: PeerPushPaymentIncomingRecord, ): TransactionAction[] { switch (pushCreditRecord.status) { - case PeerPushPaymentIncomingStatus.DialogProposed: + case PeerPushCreditStatus.DialogProposed: return [TransactionAction.Delete]; - case PeerPushPaymentIncomingStatus.PendingMerge: + case PeerPushCreditStatus.PendingMerge: return [TransactionAction.Abort, TransactionAction.Suspend]; - case PeerPushPaymentIncomingStatus.Done: + case PeerPushCreditStatus.Done: return [TransactionAction.Delete]; - case PeerPushPaymentIncomingStatus.PendingMergeKycRequired: + case PeerPushCreditStatus.PendingMergeKycRequired: return [TransactionAction.Abort, TransactionAction.Suspend]; - case PeerPushPaymentIncomingStatus.PendingWithdrawing: + case PeerPushCreditStatus.PendingWithdrawing: return [TransactionAction.Suspend, TransactionAction.Fail]; - case PeerPushPaymentIncomingStatus.SuspendedMerge: + case PeerPushCreditStatus.SuspendedMerge: return [TransactionAction.Resume, TransactionAction.Abort]; - case PeerPushPaymentIncomingStatus.SuspendedMergeKycRequired: + case PeerPushCreditStatus.SuspendedMergeKycRequired: return [TransactionAction.Resume, TransactionAction.Abort]; - case PeerPushPaymentIncomingStatus.SuspendedWithdrawing: + case PeerPushCreditStatus.SuspendedWithdrawing: return [TransactionAction.Resume, TransactionAction.Fail]; - case PeerPushPaymentIncomingStatus.Aborted: + case PeerPushCreditStatus.Aborted: return [TransactionAction.Delete]; - case PeerPushPaymentIncomingStatus.Failed: + case PeerPushCreditStatus.Failed: return [TransactionAction.Delete]; default: assertUnreachable(pushCreditRecord.status); diff --git a/packages/taler-wallet-core/src/operations/pay-peer-push-debit.ts b/packages/taler-wallet-core/src/operations/pay-peer-push-debit.ts index 2349e5c4a..e80ffc059 100644 --- a/packages/taler-wallet-core/src/operations/pay-peer-push-debit.ts +++ b/packages/taler-wallet-core/src/operations/pay-peer-push-debit.ts @@ -51,8 +51,8 @@ import { } from "@gnu-taler/taler-util/http"; import { EncryptContractRequest } from "../crypto/cryptoTypes.js"; import { - PeerPushPaymentInitiationRecord, - PeerPushPaymentInitiationStatus, + PeerPushDebitRecord, + PeerPushDebitStatus, RefreshOperationStatus, createRefreshGroup, } from "../index.js"; @@ -107,7 +107,7 @@ export async function checkPeerPushDebit( async function handlePurseCreationConflict( ws: InternalWalletState, - peerPushInitiation: PeerPushPaymentInitiationRecord, + peerPushInitiation: PeerPushDebitRecord, resp: HttpResponse, ): Promise<TaskRunResult> { const pursePub = peerPushInitiation.pursePub; @@ -152,17 +152,15 @@ async function handlePurseCreationConflict( } await ws.db - .mktx((x) => [x.peerPushPaymentInitiations]) + .mktx((x) => [x.peerPushDebit]) .runReadWrite(async (tx) => { - const myPpi = await tx.peerPushPaymentInitiations.get( - peerPushInitiation.pursePub, - ); + const myPpi = await tx.peerPushDebit.get(peerPushInitiation.pursePub); if (!myPpi) { return; } switch (myPpi.status) { - case PeerPushPaymentInitiationStatus.PendingCreatePurse: - case PeerPushPaymentInitiationStatus.SuspendedCreatePurse: { + case PeerPushDebitStatus.PendingCreatePurse: + case PeerPushDebitStatus.SuspendedCreatePurse: { const sel = coinSelRes.result; myPpi.coinSel = { coinPubs: sel.coins.map((x) => x.coinPub), @@ -173,19 +171,36 @@ async function handlePurseCreationConflict( default: return; } - await tx.peerPushPaymentInitiations.put(myPpi); + await tx.peerPushDebit.put(myPpi); }); return TaskRunResult.finished(); } async function processPeerPushDebitCreateReserve( ws: InternalWalletState, - peerPushInitiation: PeerPushPaymentInitiationRecord, + peerPushInitiation: PeerPushDebitRecord, ): Promise<TaskRunResult> { - logger.info("processing peer-push-debit pending(create-reserve)"); const pursePub = peerPushInitiation.pursePub; const purseExpiration = peerPushInitiation.purseExpiration; const hContractTerms = peerPushInitiation.contractTermsHash; + const transactionId = constructTransactionIdentifier({ + tag: TransactionType.PeerPushDebit, + pursePub: pursePub, + }); + + logger.trace(`processing ${transactionId} pending(create-reserve)`); + + const contractTermsRecord = await ws.db + .mktx((x) => [x.contractTerms]) + .runReadOnly(async (tx) => { + return tx.contractTerms.get(hContractTerms); + }); + + if (!contractTermsRecord) { + throw Error( + `db invariant failed, contract terms for ${transactionId} missing`, + ); + } const purseSigResp = await ws.cryptoApi.signPurseCreation({ hContractTerms, @@ -208,7 +223,7 @@ async function processPeerPushDebitCreateReserve( }); const encryptContractRequest: EncryptContractRequest = { - contractTerms: peerPushInitiation.contractTerms, + contractTerms: contractTermsRecord.contractTermsRaw, mergePriv: peerPushInitiation.mergePriv, pursePriv: peerPushInitiation.pursePriv, pursePub: peerPushInitiation.pursePub, @@ -284,8 +299,8 @@ async function processPeerPushDebitCreateReserve( } await transitionPeerPushDebitTransaction(ws, pursePub, { - stFrom: PeerPushPaymentInitiationStatus.PendingCreatePurse, - stTo: PeerPushPaymentInitiationStatus.PendingReady, + stFrom: PeerPushDebitStatus.PendingCreatePurse, + stTo: PeerPushDebitStatus.PendingReady, }); return TaskRunResult.finished(); @@ -293,7 +308,7 @@ async function processPeerPushDebitCreateReserve( async function processPeerPushDebitAbortingDeletePurse( ws: InternalWalletState, - peerPushInitiation: PeerPushPaymentInitiationRecord, + peerPushInitiation: PeerPushDebitRecord, ): Promise<TaskRunResult> { const { pursePub, pursePriv } = peerPushInitiation; const transactionId = constructTransactionIdentifier({ @@ -318,20 +333,18 @@ async function processPeerPushDebitAbortingDeletePurse( const transitionInfo = await ws.db .mktx((x) => [ - x.peerPushPaymentInitiations, + x.peerPushDebit, x.refreshGroups, x.denominations, x.coinAvailability, x.coins, ]) .runReadWrite(async (tx) => { - const ppiRec = await tx.peerPushPaymentInitiations.get(pursePub); + const ppiRec = await tx.peerPushDebit.get(pursePub); if (!ppiRec) { return undefined; } - if ( - ppiRec.status !== PeerPushPaymentInitiationStatus.AbortingDeletePurse - ) { + if (ppiRec.status !== PeerPushDebitStatus.AbortingDeletePurse) { return undefined; } const currency = Amounts.currencyOf(ppiRec.amount); @@ -352,9 +365,9 @@ async function processPeerPushDebitAbortingDeletePurse( coinPubs, RefreshReason.AbortPeerPushDebit, ); - ppiRec.status = PeerPushPaymentInitiationStatus.AbortingRefresh; + ppiRec.status = PeerPushDebitStatus.AbortingRefresh; ppiRec.abortRefreshGroupId = refresh.refreshGroupId; - await tx.peerPushPaymentInitiations.put(ppiRec); + await tx.peerPushDebit.put(ppiRec); const newTxState = computePeerPushDebitTransactionState(ppiRec); return { oldTxState, @@ -367,8 +380,8 @@ async function processPeerPushDebitAbortingDeletePurse( } interface SimpleTransition { - stFrom: PeerPushPaymentInitiationStatus; - stTo: PeerPushPaymentInitiationStatus; + stFrom: PeerPushDebitStatus; + stTo: PeerPushDebitStatus; } async function transitionPeerPushDebitTransaction( @@ -381,9 +394,9 @@ async function transitionPeerPushDebitTransaction( pursePub, }); const transitionInfo = await ws.db - .mktx((x) => [x.peerPushPaymentInitiations]) + .mktx((x) => [x.peerPushDebit]) .runReadWrite(async (tx) => { - const ppiRec = await tx.peerPushPaymentInitiations.get(pursePub); + const ppiRec = await tx.peerPushDebit.get(pursePub); if (!ppiRec) { return undefined; } @@ -392,7 +405,7 @@ async function transitionPeerPushDebitTransaction( } const oldTxState = computePeerPushDebitTransactionState(ppiRec); ppiRec.status = transitionSpec.stTo; - await tx.peerPushPaymentInitiations.put(ppiRec); + await tx.peerPushDebit.put(ppiRec); const newTxState = computePeerPushDebitTransactionState(ppiRec); return { oldTxState, @@ -404,7 +417,7 @@ async function transitionPeerPushDebitTransaction( async function processPeerPushDebitAbortingRefresh( ws: InternalWalletState, - peerPushInitiation: PeerPushPaymentInitiationRecord, + peerPushInitiation: PeerPushDebitRecord, ): Promise<TaskRunResult> { const pursePub = peerPushInitiation.pursePub; const abortRefreshGroupId = peerPushInitiation.abortRefreshGroupId; @@ -414,33 +427,33 @@ async function processPeerPushDebitAbortingRefresh( pursePub: peerPushInitiation.pursePub, }); const transitionInfo = await ws.db - .mktx((x) => [x.refreshGroups, x.peerPushPaymentInitiations]) + .mktx((x) => [x.refreshGroups, x.peerPushDebit]) .runReadWrite(async (tx) => { const refreshGroup = await tx.refreshGroups.get(abortRefreshGroupId); - let newOpState: PeerPushPaymentInitiationStatus | undefined; + let newOpState: PeerPushDebitStatus | undefined; if (!refreshGroup) { // Maybe it got manually deleted? Means that we should // just go into failed. logger.warn("no aborting refresh group found for deposit group"); - newOpState = PeerPushPaymentInitiationStatus.Failed; + newOpState = PeerPushDebitStatus.Failed; } else { if (refreshGroup.operationStatus === RefreshOperationStatus.Finished) { - newOpState = PeerPushPaymentInitiationStatus.Aborted; + newOpState = PeerPushDebitStatus.Aborted; } else if ( refreshGroup.operationStatus === RefreshOperationStatus.Failed ) { - newOpState = PeerPushPaymentInitiationStatus.Failed; + newOpState = PeerPushDebitStatus.Failed; } } if (newOpState) { - const newDg = await tx.peerPushPaymentInitiations.get(pursePub); + const newDg = await tx.peerPushDebit.get(pursePub); if (!newDg) { return; } const oldTxState = computePeerPushDebitTransactionState(newDg); newDg.status = newOpState; const newTxState = computePeerPushDebitTransactionState(newDg); - await tx.peerPushPaymentInitiations.put(newDg); + await tx.peerPushDebit.put(newDg); return { oldTxState, newTxState }; } return undefined; @@ -455,7 +468,7 @@ async function processPeerPushDebitAbortingRefresh( */ async function processPeerPushDebitReady( ws: InternalWalletState, - peerPushInitiation: PeerPushPaymentInitiationRecord, + peerPushInitiation: PeerPushDebitRecord, ): Promise<TaskRunResult> { logger.info("processing peer-push-debit pending(ready)"); const pursePub = peerPushInitiation.pursePub; @@ -488,8 +501,8 @@ async function processPeerPushDebitReady( ws, peerPushInitiation.pursePub, { - stFrom: PeerPushPaymentInitiationStatus.PendingReady, - stTo: PeerPushPaymentInitiationStatus.Done, + stFrom: PeerPushDebitStatus.PendingReady, + stTo: PeerPushDebitStatus.Done, }, ); return { @@ -501,8 +514,8 @@ async function processPeerPushDebitReady( ws, peerPushInitiation.pursePub, { - stFrom: PeerPushPaymentInitiationStatus.PendingReady, - stTo: PeerPushPaymentInitiationStatus.Expired, + stFrom: PeerPushDebitStatus.PendingReady, + stTo: PeerPushDebitStatus.Expired, }, ); return { @@ -528,9 +541,9 @@ export async function processPeerPushDebit( pursePub: string, ): Promise<TaskRunResult> { const peerPushInitiation = await ws.db - .mktx((x) => [x.peerPushPaymentInitiations]) + .mktx((x) => [x.peerPushDebit]) .runReadOnly(async (tx) => { - return tx.peerPushPaymentInitiations.get(pursePub); + return tx.peerPushDebit.get(pursePub); }); if (!peerPushInitiation) { throw Error("peer push payment not found"); @@ -550,13 +563,13 @@ export async function processPeerPushDebit( } switch (peerPushInitiation.status) { - case PeerPushPaymentInitiationStatus.PendingCreatePurse: + case PeerPushDebitStatus.PendingCreatePurse: return processPeerPushDebitCreateReserve(ws, peerPushInitiation); - case PeerPushPaymentInitiationStatus.PendingReady: + case PeerPushDebitStatus.PendingReady: return processPeerPushDebitReady(ws, peerPushInitiation); - case PeerPushPaymentInitiationStatus.AbortingDeletePurse: + case PeerPushDebitStatus.AbortingDeletePurse: return processPeerPushDebitAbortingDeletePurse(ws, peerPushInitiation); - case PeerPushPaymentInitiationStatus.AbortingRefresh: + case PeerPushDebitStatus.AbortingRefresh: return processPeerPushDebitAbortingRefresh(ws, peerPushInitiation); default: { const txState = computePeerPushDebitTransactionState(peerPushInitiation); @@ -626,7 +639,7 @@ export async function initiatePeerPushDebit( x.coinAvailability, x.denominations, x.refreshGroups, - x.peerPushPaymentInitiations, + x.peerPushDebit, ]) .runReadWrite(async (tx) => { // FIXME: Instead of directly doing a spendCoin here, @@ -645,7 +658,7 @@ export async function initiatePeerPushDebit( refreshReason: RefreshReason.PayPeerPush, }); - const ppi: PeerPushPaymentInitiationRecord = { + const ppi: PeerPushDebitRecord = { amount: Amounts.stringify(instructedAmount), contractPriv: contractKeyPair.priv, contractPub: contractKeyPair.pub, @@ -657,8 +670,7 @@ export async function initiatePeerPushDebit( pursePriv: pursePair.priv, pursePub: pursePair.pub, timestampCreated: TalerPreciseTimestamp.now(), - status: PeerPushPaymentInitiationStatus.PendingCreatePurse, - contractTerms: contractTerms, + status: PeerPushDebitStatus.PendingCreatePurse, contractEncNonce, coinSel: { coinPubs: sel.coins.map((x) => x.coinPub), @@ -667,7 +679,7 @@ export async function initiatePeerPushDebit( totalCost: Amounts.stringify(totalAmount), }; - await tx.peerPushPaymentInitiations.add(ppi); + await tx.peerPushDebit.add(ppi); await tx.contractTerms.put({ h: hContractTerms, @@ -701,32 +713,32 @@ export async function initiatePeerPushDebit( } export function computePeerPushDebitTransactionActions( - ppiRecord: PeerPushPaymentInitiationRecord, + ppiRecord: PeerPushDebitRecord, ): TransactionAction[] { switch (ppiRecord.status) { - case PeerPushPaymentInitiationStatus.PendingCreatePurse: + case PeerPushDebitStatus.PendingCreatePurse: return [TransactionAction.Abort, TransactionAction.Suspend]; - case PeerPushPaymentInitiationStatus.PendingReady: + case PeerPushDebitStatus.PendingReady: return [TransactionAction.Abort, TransactionAction.Suspend]; - case PeerPushPaymentInitiationStatus.Aborted: + case PeerPushDebitStatus.Aborted: return [TransactionAction.Delete]; - case PeerPushPaymentInitiationStatus.AbortingDeletePurse: + case PeerPushDebitStatus.AbortingDeletePurse: return [TransactionAction.Suspend, TransactionAction.Fail]; - case PeerPushPaymentInitiationStatus.AbortingRefresh: + case PeerPushDebitStatus.AbortingRefresh: return [TransactionAction.Suspend, TransactionAction.Fail]; - case PeerPushPaymentInitiationStatus.SuspendedAbortingDeletePurse: + case PeerPushDebitStatus.SuspendedAbortingDeletePurse: return [TransactionAction.Resume, TransactionAction.Fail]; - case PeerPushPaymentInitiationStatus.SuspendedAbortingRefresh: + case PeerPushDebitStatus.SuspendedAbortingRefresh: return [TransactionAction.Resume, TransactionAction.Fail]; - case PeerPushPaymentInitiationStatus.SuspendedCreatePurse: + case PeerPushDebitStatus.SuspendedCreatePurse: return [TransactionAction.Resume, TransactionAction.Abort]; - case PeerPushPaymentInitiationStatus.SuspendedReady: + case PeerPushDebitStatus.SuspendedReady: return [TransactionAction.Suspend, TransactionAction.Abort]; - case PeerPushPaymentInitiationStatus.Done: + case PeerPushDebitStatus.Done: return [TransactionAction.Delete]; - case PeerPushPaymentInitiationStatus.Expired: + case PeerPushDebitStatus.Expired: return [TransactionAction.Delete]; - case PeerPushPaymentInitiationStatus.Failed: + case PeerPushDebitStatus.Failed: return [TransactionAction.Delete]; } } @@ -745,32 +757,32 @@ export async function abortPeerPushDebitTransaction( }); stopLongpolling(ws, taskId); const transitionInfo = await ws.db - .mktx((x) => [x.peerPushPaymentInitiations]) + .mktx((x) => [x.peerPushDebit]) .runReadWrite(async (tx) => { - const pushDebitRec = await tx.peerPushPaymentInitiations.get(pursePub); + const pushDebitRec = await tx.peerPushDebit.get(pursePub); if (!pushDebitRec) { logger.warn(`peer push debit ${pursePub} not found`); return; } - let newStatus: PeerPushPaymentInitiationStatus | undefined = undefined; + let newStatus: PeerPushDebitStatus | undefined = undefined; switch (pushDebitRec.status) { - case PeerPushPaymentInitiationStatus.PendingReady: - case PeerPushPaymentInitiationStatus.SuspendedReady: - newStatus = PeerPushPaymentInitiationStatus.AbortingDeletePurse; + case PeerPushDebitStatus.PendingReady: + case PeerPushDebitStatus.SuspendedReady: + newStatus = PeerPushDebitStatus.AbortingDeletePurse; break; - case PeerPushPaymentInitiationStatus.SuspendedCreatePurse: - case PeerPushPaymentInitiationStatus.PendingCreatePurse: + case PeerPushDebitStatus.SuspendedCreatePurse: + case PeerPushDebitStatus.PendingCreatePurse: // Network request might already be in-flight! - newStatus = PeerPushPaymentInitiationStatus.AbortingDeletePurse; + newStatus = PeerPushDebitStatus.AbortingDeletePurse; break; - case PeerPushPaymentInitiationStatus.SuspendedAbortingRefresh: - case PeerPushPaymentInitiationStatus.SuspendedAbortingDeletePurse: - case PeerPushPaymentInitiationStatus.AbortingRefresh: - case PeerPushPaymentInitiationStatus.Done: - case PeerPushPaymentInitiationStatus.AbortingDeletePurse: - case PeerPushPaymentInitiationStatus.Aborted: - case PeerPushPaymentInitiationStatus.Expired: - case PeerPushPaymentInitiationStatus.Failed: + case PeerPushDebitStatus.SuspendedAbortingRefresh: + case PeerPushDebitStatus.SuspendedAbortingDeletePurse: + case PeerPushDebitStatus.AbortingRefresh: + case PeerPushDebitStatus.Done: + case PeerPushDebitStatus.AbortingDeletePurse: + case PeerPushDebitStatus.Aborted: + case PeerPushDebitStatus.Expired: + case PeerPushDebitStatus.Failed: // Do nothing break; default: @@ -780,7 +792,7 @@ export async function abortPeerPushDebitTransaction( const oldTxState = computePeerPushDebitTransactionState(pushDebitRec); pushDebitRec.status = newStatus; const newTxState = computePeerPushDebitTransactionState(pushDebitRec); - await tx.peerPushPaymentInitiations.put(pushDebitRec); + await tx.peerPushDebit.put(pushDebitRec); return { oldTxState, newTxState, @@ -805,32 +817,32 @@ export async function failPeerPushDebitTransaction( }); stopLongpolling(ws, taskId); const transitionInfo = await ws.db - .mktx((x) => [x.peerPushPaymentInitiations]) + .mktx((x) => [x.peerPushDebit]) .runReadWrite(async (tx) => { - const pushDebitRec = await tx.peerPushPaymentInitiations.get(pursePub); + const pushDebitRec = await tx.peerPushDebit.get(pursePub); if (!pushDebitRec) { logger.warn(`peer push debit ${pursePub} not found`); return; } - let newStatus: PeerPushPaymentInitiationStatus | undefined = undefined; + let newStatus: PeerPushDebitStatus | undefined = undefined; switch (pushDebitRec.status) { - case PeerPushPaymentInitiationStatus.AbortingRefresh: - case PeerPushPaymentInitiationStatus.SuspendedAbortingRefresh: + case PeerPushDebitStatus.AbortingRefresh: + case PeerPushDebitStatus.SuspendedAbortingRefresh: // FIXME: What to do about the refresh group? - newStatus = PeerPushPaymentInitiationStatus.Failed; + newStatus = PeerPushDebitStatus.Failed; break; - case PeerPushPaymentInitiationStatus.AbortingDeletePurse: - case PeerPushPaymentInitiationStatus.SuspendedAbortingDeletePurse: - case PeerPushPaymentInitiationStatus.PendingReady: - case PeerPushPaymentInitiationStatus.SuspendedReady: - case PeerPushPaymentInitiationStatus.SuspendedCreatePurse: - case PeerPushPaymentInitiationStatus.PendingCreatePurse: - newStatus = PeerPushPaymentInitiationStatus.Failed; + case PeerPushDebitStatus.AbortingDeletePurse: + case PeerPushDebitStatus.SuspendedAbortingDeletePurse: + case PeerPushDebitStatus.PendingReady: + case PeerPushDebitStatus.SuspendedReady: + case PeerPushDebitStatus.SuspendedCreatePurse: + case PeerPushDebitStatus.PendingCreatePurse: + newStatus = PeerPushDebitStatus.Failed; break; - case PeerPushPaymentInitiationStatus.Done: - case PeerPushPaymentInitiationStatus.Aborted: - case PeerPushPaymentInitiationStatus.Failed: - case PeerPushPaymentInitiationStatus.Expired: + case PeerPushDebitStatus.Done: + case PeerPushDebitStatus.Aborted: + case PeerPushDebitStatus.Failed: + case PeerPushDebitStatus.Expired: // Do nothing break; default: @@ -840,7 +852,7 @@ export async function failPeerPushDebitTransaction( const oldTxState = computePeerPushDebitTransactionState(pushDebitRec); pushDebitRec.status = newStatus; const newTxState = computePeerPushDebitTransactionState(pushDebitRec); - await tx.peerPushPaymentInitiations.put(pushDebitRec); + await tx.peerPushDebit.put(pushDebitRec); return { oldTxState, newTxState, @@ -865,36 +877,35 @@ export async function suspendPeerPushDebitTransaction( }); stopLongpolling(ws, taskId); const transitionInfo = await ws.db - .mktx((x) => [x.peerPushPaymentInitiations]) + .mktx((x) => [x.peerPushDebit]) .runReadWrite(async (tx) => { - const pushDebitRec = await tx.peerPushPaymentInitiations.get(pursePub); + const pushDebitRec = await tx.peerPushDebit.get(pursePub); if (!pushDebitRec) { logger.warn(`peer push debit ${pursePub} not found`); return; } - let newStatus: PeerPushPaymentInitiationStatus | undefined = undefined; + let newStatus: PeerPushDebitStatus | undefined = undefined; switch (pushDebitRec.status) { - case PeerPushPaymentInitiationStatus.PendingCreatePurse: - newStatus = PeerPushPaymentInitiationStatus.SuspendedCreatePurse; + case PeerPushDebitStatus.PendingCreatePurse: + newStatus = PeerPushDebitStatus.SuspendedCreatePurse; break; - case PeerPushPaymentInitiationStatus.AbortingRefresh: - newStatus = PeerPushPaymentInitiationStatus.SuspendedAbortingRefresh; + case PeerPushDebitStatus.AbortingRefresh: + newStatus = PeerPushDebitStatus.SuspendedAbortingRefresh; break; - case PeerPushPaymentInitiationStatus.AbortingDeletePurse: - newStatus = - PeerPushPaymentInitiationStatus.SuspendedAbortingDeletePurse; + case PeerPushDebitStatus.AbortingDeletePurse: + newStatus = PeerPushDebitStatus.SuspendedAbortingDeletePurse; break; - case PeerPushPaymentInitiationStatus.PendingReady: - newStatus = PeerPushPaymentInitiationStatus.SuspendedReady; + case PeerPushDebitStatus.PendingReady: + newStatus = PeerPushDebitStatus.SuspendedReady; break; - case PeerPushPaymentInitiationStatus.SuspendedAbortingDeletePurse: - case PeerPushPaymentInitiationStatus.SuspendedAbortingRefresh: - case PeerPushPaymentInitiationStatus.SuspendedReady: - case PeerPushPaymentInitiationStatus.SuspendedCreatePurse: - case PeerPushPaymentInitiationStatus.Done: - case PeerPushPaymentInitiationStatus.Aborted: - case PeerPushPaymentInitiationStatus.Failed: - case PeerPushPaymentInitiationStatus.Expired: + case PeerPushDebitStatus.SuspendedAbortingDeletePurse: + case PeerPushDebitStatus.SuspendedAbortingRefresh: + case PeerPushDebitStatus.SuspendedReady: + case PeerPushDebitStatus.SuspendedCreatePurse: + case PeerPushDebitStatus.Done: + case PeerPushDebitStatus.Aborted: + case PeerPushDebitStatus.Failed: + case PeerPushDebitStatus.Expired: // Do nothing break; default: @@ -904,7 +915,7 @@ export async function suspendPeerPushDebitTransaction( const oldTxState = computePeerPushDebitTransactionState(pushDebitRec); pushDebitRec.status = newStatus; const newTxState = computePeerPushDebitTransactionState(pushDebitRec); - await tx.peerPushPaymentInitiations.put(pushDebitRec); + await tx.peerPushDebit.put(pushDebitRec); return { oldTxState, newTxState, @@ -929,35 +940,35 @@ export async function resumePeerPushDebitTransaction( }); stopLongpolling(ws, taskId); const transitionInfo = await ws.db - .mktx((x) => [x.peerPushPaymentInitiations]) + .mktx((x) => [x.peerPushDebit]) .runReadWrite(async (tx) => { - const pushDebitRec = await tx.peerPushPaymentInitiations.get(pursePub); + const pushDebitRec = await tx.peerPushDebit.get(pursePub); if (!pushDebitRec) { logger.warn(`peer push debit ${pursePub} not found`); return; } - let newStatus: PeerPushPaymentInitiationStatus | undefined = undefined; + let newStatus: PeerPushDebitStatus | undefined = undefined; switch (pushDebitRec.status) { - case PeerPushPaymentInitiationStatus.SuspendedAbortingDeletePurse: - newStatus = PeerPushPaymentInitiationStatus.AbortingDeletePurse; + case PeerPushDebitStatus.SuspendedAbortingDeletePurse: + newStatus = PeerPushDebitStatus.AbortingDeletePurse; break; - case PeerPushPaymentInitiationStatus.SuspendedAbortingRefresh: - newStatus = PeerPushPaymentInitiationStatus.AbortingRefresh; + case PeerPushDebitStatus.SuspendedAbortingRefresh: + newStatus = PeerPushDebitStatus.AbortingRefresh; break; - case PeerPushPaymentInitiationStatus.SuspendedReady: - newStatus = PeerPushPaymentInitiationStatus.PendingReady; + case PeerPushDebitStatus.SuspendedReady: + newStatus = PeerPushDebitStatus.PendingReady; break; - case PeerPushPaymentInitiationStatus.SuspendedCreatePurse: - newStatus = PeerPushPaymentInitiationStatus.PendingCreatePurse; + case PeerPushDebitStatus.SuspendedCreatePurse: + newStatus = PeerPushDebitStatus.PendingCreatePurse; break; - case PeerPushPaymentInitiationStatus.PendingCreatePurse: - case PeerPushPaymentInitiationStatus.AbortingRefresh: - case PeerPushPaymentInitiationStatus.AbortingDeletePurse: - case PeerPushPaymentInitiationStatus.PendingReady: - case PeerPushPaymentInitiationStatus.Done: - case PeerPushPaymentInitiationStatus.Aborted: - case PeerPushPaymentInitiationStatus.Failed: - case PeerPushPaymentInitiationStatus.Expired: + case PeerPushDebitStatus.PendingCreatePurse: + case PeerPushDebitStatus.AbortingRefresh: + case PeerPushDebitStatus.AbortingDeletePurse: + case PeerPushDebitStatus.PendingReady: + case PeerPushDebitStatus.Done: + case PeerPushDebitStatus.Aborted: + case PeerPushDebitStatus.Failed: + case PeerPushDebitStatus.Expired: // Do nothing break; default: @@ -967,7 +978,7 @@ export async function resumePeerPushDebitTransaction( const oldTxState = computePeerPushDebitTransactionState(pushDebitRec); pushDebitRec.status = newStatus; const newTxState = computePeerPushDebitTransactionState(pushDebitRec); - await tx.peerPushPaymentInitiations.put(pushDebitRec); + await tx.peerPushDebit.put(pushDebitRec); return { oldTxState, newTxState, @@ -980,62 +991,62 @@ export async function resumePeerPushDebitTransaction( } export function computePeerPushDebitTransactionState( - ppiRecord: PeerPushPaymentInitiationRecord, + ppiRecord: PeerPushDebitRecord, ): TransactionState { switch (ppiRecord.status) { - case PeerPushPaymentInitiationStatus.PendingCreatePurse: + case PeerPushDebitStatus.PendingCreatePurse: return { major: TransactionMajorState.Pending, minor: TransactionMinorState.CreatePurse, }; - case PeerPushPaymentInitiationStatus.PendingReady: + case PeerPushDebitStatus.PendingReady: return { major: TransactionMajorState.Pending, minor: TransactionMinorState.Ready, }; - case PeerPushPaymentInitiationStatus.Aborted: + case PeerPushDebitStatus.Aborted: return { major: TransactionMajorState.Aborted, }; - case PeerPushPaymentInitiationStatus.AbortingDeletePurse: + case PeerPushDebitStatus.AbortingDeletePurse: return { major: TransactionMajorState.Aborting, minor: TransactionMinorState.DeletePurse, }; - case PeerPushPaymentInitiationStatus.AbortingRefresh: + case PeerPushDebitStatus.AbortingRefresh: return { major: TransactionMajorState.Aborting, minor: TransactionMinorState.Refresh, }; - case PeerPushPaymentInitiationStatus.SuspendedAbortingDeletePurse: + case PeerPushDebitStatus.SuspendedAbortingDeletePurse: return { major: TransactionMajorState.SuspendedAborting, minor: TransactionMinorState.DeletePurse, }; - case PeerPushPaymentInitiationStatus.SuspendedAbortingRefresh: + case PeerPushDebitStatus.SuspendedAbortingRefresh: return { major: TransactionMajorState.SuspendedAborting, minor: TransactionMinorState.Refresh, }; - case PeerPushPaymentInitiationStatus.SuspendedCreatePurse: + case PeerPushDebitStatus.SuspendedCreatePurse: return { major: TransactionMajorState.Suspended, minor: TransactionMinorState.CreatePurse, }; - case PeerPushPaymentInitiationStatus.SuspendedReady: + case PeerPushDebitStatus.SuspendedReady: return { major: TransactionMajorState.Suspended, minor: TransactionMinorState.Ready, }; - case PeerPushPaymentInitiationStatus.Done: + case PeerPushDebitStatus.Done: return { major: TransactionMajorState.Done, }; - case PeerPushPaymentInitiationStatus.Failed: + case PeerPushDebitStatus.Failed: return { major: TransactionMajorState.Failed, }; - case PeerPushPaymentInitiationStatus.Expired: + case PeerPushDebitStatus.Expired: return { major: TransactionMajorState.Expired, }; diff --git a/packages/taler-wallet-core/src/operations/pending.ts b/packages/taler-wallet-core/src/operations/pending.ts index e37e45c16..6115f848b 100644 --- a/packages/taler-wallet-core/src/operations/pending.ts +++ b/packages/taler-wallet-core/src/operations/pending.ts @@ -26,11 +26,10 @@ import { WalletStoresV1, BackupProviderStateTag, RefreshCoinStatus, - OperationStatusRange, - PeerPushPaymentInitiationStatus, + PeerPushDebitStatus, PeerPullDebitRecordStatus, - PeerPushPaymentIncomingStatus, - PeerPullPaymentInitiationStatus, + PeerPushCreditStatus, + PeerPullPaymentCreditStatus, WithdrawalGroupStatus, RewardRecordStatus, DepositOperationStatus, @@ -39,13 +38,15 @@ import { DepositGroupRecord, RewardRecord, PurchaseRecord, - PeerPullPaymentInitiationRecord, + PeerPullCreditRecord, PeerPullPaymentIncomingRecord, - PeerPushPaymentInitiationRecord, + PeerPushDebitRecord, PeerPushPaymentIncomingRecord, RefundGroupRecord, RefundGroupStatus, ExchangeEntryDbUpdateStatus, + RefreshOperationStatus, + DepositElementStatus, } from "../db.js"; import { PendingOperationsResponse, @@ -136,8 +137,8 @@ export async function iterRecordsForRefresh( let refreshGroups: RefreshGroupRecord[]; if (filter.onlyState === "nonfinal") { const keyRange = GlobalIDB.KeyRange.bound( - OperationStatusRange.ACTIVE_START, - OperationStatusRange.ACTIVE_END, + RefreshOperationStatus.Pending, + RefreshOperationStatus.Suspended, ); refreshGroups = await tx.refreshGroups.indexes.byStatus.getAll(keyRange); } else { @@ -277,8 +278,8 @@ async function gatherDepositPending( ): Promise<void> { await iterRecordsForDeposit(tx, { onlyState: "nonfinal" }, async (dg) => { let deposited = true; - for (const d of dg.depositedPerCoin) { - if (!d) { + for (const d of dg.statusPerCoin) { + if (d === DepositElementStatus.DepositPending) { deposited = false; } } @@ -470,28 +471,26 @@ async function gatherBackupPending( export async function iterRecordsForPeerPullInitiation( tx: GetReadOnlyAccess<{ - peerPullPaymentInitiations: typeof WalletStoresV1.peerPullPaymentInitiations; + peerPullCredit: typeof WalletStoresV1.peerPullCredit; }>, filter: TransactionRecordFilter, - f: (r: PeerPullPaymentInitiationRecord) => Promise<void>, + f: (r: PeerPullCreditRecord) => Promise<void>, ): Promise<void> { if (filter.onlyState === "nonfinal") { const keyRange = GlobalIDB.KeyRange.bound( - PeerPullPaymentInitiationStatus.PendingCreatePurse, - PeerPullPaymentInitiationStatus.AbortingDeletePurse, + PeerPullPaymentCreditStatus.PendingCreatePurse, + PeerPullPaymentCreditStatus.AbortingDeletePurse, ); - await tx.peerPullPaymentInitiations.indexes.byStatus - .iter(keyRange) - .forEachAsync(f); + await tx.peerPullCredit.indexes.byStatus.iter(keyRange).forEachAsync(f); } else { - await tx.peerPullPaymentInitiations.indexes.byStatus.iter().forEachAsync(f); + await tx.peerPullCredit.indexes.byStatus.iter().forEachAsync(f); } } async function gatherPeerPullInitiationPending( ws: InternalWalletState, tx: GetReadOnlyAccess<{ - peerPullPaymentInitiations: typeof WalletStoresV1.peerPullPaymentInitiations; + peerPullCredit: typeof WalletStoresV1.peerPullCredit; operationRetries: typeof WalletStoresV1.operationRetries; }>, now: AbsoluteTime, @@ -518,7 +517,7 @@ async function gatherPeerPullInitiationPending( export async function iterRecordsForPeerPullDebit( tx: GetReadOnlyAccess<{ - peerPullPaymentIncoming: typeof WalletStoresV1.peerPullPaymentIncoming; + peerPullDebit: typeof WalletStoresV1.peerPullDebit; }>, filter: TransactionRecordFilter, f: (r: PeerPullPaymentIncomingRecord) => Promise<void>, @@ -528,18 +527,16 @@ export async function iterRecordsForPeerPullDebit( PeerPullDebitRecordStatus.PendingDeposit, PeerPullDebitRecordStatus.AbortingRefresh, ); - await tx.peerPullPaymentIncoming.indexes.byStatus - .iter(keyRange) - .forEachAsync(f); + await tx.peerPullDebit.indexes.byStatus.iter(keyRange).forEachAsync(f); } else { - await tx.peerPullPaymentIncoming.indexes.byStatus.iter().forEachAsync(f); + await tx.peerPullDebit.indexes.byStatus.iter().forEachAsync(f); } } async function gatherPeerPullDebitPending( ws: InternalWalletState, tx: GetReadOnlyAccess<{ - peerPullPaymentIncoming: typeof WalletStoresV1.peerPullPaymentIncoming; + peerPullDebit: typeof WalletStoresV1.peerPullDebit; operationRetries: typeof WalletStoresV1.operationRetries; }>, now: AbsoluteTime, @@ -558,7 +555,7 @@ async function gatherPeerPullDebitPending( ...getPendingCommon(ws, opId, timestampDue), givesLifeness: true, retryInfo: retryRecord?.retryInfo, - peerPullPaymentIncomingId: pi.peerPullPaymentIncomingId, + peerPullDebitId: pi.peerPullDebitId, }); }, ); @@ -566,28 +563,26 @@ async function gatherPeerPullDebitPending( export async function iterRecordsForPeerPushInitiation( tx: GetReadOnlyAccess<{ - peerPushPaymentInitiations: typeof WalletStoresV1.peerPushPaymentInitiations; + peerPushDebit: typeof WalletStoresV1.peerPushDebit; }>, filter: TransactionRecordFilter, - f: (r: PeerPushPaymentInitiationRecord) => Promise<void>, + f: (r: PeerPushDebitRecord) => Promise<void>, ): Promise<void> { if (filter.onlyState === "nonfinal") { const keyRange = GlobalIDB.KeyRange.bound( - PeerPushPaymentInitiationStatus.PendingCreatePurse, - PeerPushPaymentInitiationStatus.AbortingRefresh, + PeerPushDebitStatus.PendingCreatePurse, + PeerPushDebitStatus.AbortingRefresh, ); - await tx.peerPushPaymentInitiations.indexes.byStatus - .iter(keyRange) - .forEachAsync(f); + await tx.peerPushDebit.indexes.byStatus.iter(keyRange).forEachAsync(f); } else { - await tx.peerPushPaymentInitiations.indexes.byStatus.iter().forEachAsync(f); + await tx.peerPushDebit.indexes.byStatus.iter().forEachAsync(f); } } async function gatherPeerPushInitiationPending( ws: InternalWalletState, tx: GetReadOnlyAccess<{ - peerPushPaymentInitiations: typeof WalletStoresV1.peerPushPaymentInitiations; + peerPushDebit: typeof WalletStoresV1.peerPushDebit; operationRetries: typeof WalletStoresV1.operationRetries; }>, now: AbsoluteTime, @@ -614,36 +609,34 @@ async function gatherPeerPushInitiationPending( export async function iterRecordsForPeerPushCredit( tx: GetReadOnlyAccess<{ - peerPushPaymentIncoming: typeof WalletStoresV1.peerPushPaymentIncoming; + peerPushCredit: typeof WalletStoresV1.peerPushCredit; }>, filter: TransactionRecordFilter, f: (r: PeerPushPaymentIncomingRecord) => Promise<void>, ): Promise<void> { if (filter.onlyState === "nonfinal") { const keyRange = GlobalIDB.KeyRange.bound( - PeerPushPaymentIncomingStatus.PendingMerge, - PeerPushPaymentIncomingStatus.PendingWithdrawing, + PeerPushCreditStatus.PendingMerge, + PeerPushCreditStatus.PendingWithdrawing, ); - await tx.peerPushPaymentIncoming.indexes.byStatus - .iter(keyRange) - .forEachAsync(f); + await tx.peerPushCredit.indexes.byStatus.iter(keyRange).forEachAsync(f); } else { - await tx.peerPushPaymentIncoming.indexes.byStatus.iter().forEachAsync(f); + await tx.peerPushCredit.indexes.byStatus.iter().forEachAsync(f); } } async function gatherPeerPushCreditPending( ws: InternalWalletState, tx: GetReadOnlyAccess<{ - peerPushPaymentIncoming: typeof WalletStoresV1.peerPushPaymentIncoming; + peerPushCredit: typeof WalletStoresV1.peerPushCredit; operationRetries: typeof WalletStoresV1.operationRetries; }>, now: AbsoluteTime, resp: PendingOperationsResponse, ): Promise<void> { const keyRange = GlobalIDB.KeyRange.bound( - PeerPushPaymentIncomingStatus.PendingMerge, - PeerPushPaymentIncomingStatus.PendingWithdrawing, + PeerPushCreditStatus.PendingMerge, + PeerPushCreditStatus.PendingWithdrawing, ); await iterRecordsForPeerPushCredit( tx, @@ -658,7 +651,7 @@ async function gatherPeerPushCreditPending( ...getPendingCommon(ws, opId, timestampDue), givesLifeness: true, retryInfo: retryRecord?.retryInfo, - peerPushPaymentIncomingId: pi.peerPushPaymentIncomingId, + peerPushCreditId: pi.peerPushCreditId, }); }, ); @@ -682,10 +675,10 @@ export async function getPendingOperations( x.depositGroups, x.recoupGroups, x.operationRetries, - x.peerPullPaymentInitiations, - x.peerPushPaymentInitiations, - x.peerPullPaymentIncoming, - x.peerPushPaymentIncoming, + x.peerPullCredit, + x.peerPushDebit, + x.peerPullDebit, + x.peerPushCredit, ]) .runReadWrite(async (tx) => { const resp: PendingOperationsResponse = { diff --git a/packages/taler-wallet-core/src/operations/refresh.ts b/packages/taler-wallet-core/src/operations/refresh.ts index fb356f0fc..75adbc860 100644 --- a/packages/taler-wallet-core/src/operations/refresh.ts +++ b/packages/taler-wallet-core/src/operations/refresh.ts @@ -76,7 +76,11 @@ import { RefreshReasonDetails, WalletStoresV1, } from "../db.js"; -import { isWithdrawableDenom, PendingTaskType } from "../index.js"; +import { + isWithdrawableDenom, + PendingTaskType, + RefreshSessionRecord, +} from "../index.js"; import { EXCHANGE_COINS_LOCK, InternalWalletState, @@ -130,11 +134,7 @@ export function getTotalRefreshCost( const resultingAmount = Amounts.add( Amounts.zeroOfCurrency(withdrawAmount.currency), ...withdrawDenoms.selectedDenoms.map( - (d) => - Amounts.mult( - DenominationRecord.getValue(denomMap[d.denomPubHash]), - d.count, - ).amount, + (d) => Amounts.mult(denomMap[d.denomPubHash].value, d.count).amount, ), ).amount; const totalCost = Amounts.sub(amountLeft, resultingAmount).amount; @@ -170,18 +170,23 @@ function updateGroupStatus(rg: RefreshGroupRecord): { final: boolean } { /** * Create a refresh session for one particular coin inside a refresh group. + * + * If the session already exists, return the existing one. + * + * If the session doesn't need to be created (refresh group gone or session already + * finished), return undefined. */ -async function refreshCreateSession( +async function provideRefreshSession( ws: InternalWalletState, refreshGroupId: string, coinIndex: number, -): Promise<void> { +): Promise<RefreshSessionRecord | undefined> { logger.trace( `creating refresh session for coin ${coinIndex} in refresh group ${refreshGroupId}`, ); const d = await ws.db - .mktx((x) => [x.refreshGroups, x.coins]) + .mktx((x) => [x.refreshGroups, x.coins, x.refreshSessions]) .runReadWrite(async (tx) => { const refreshGroup = await tx.refreshGroups.get(refreshGroupId); if (!refreshGroup) { @@ -192,21 +197,24 @@ async function refreshCreateSession( ) { return; } - const existingRefreshSession = - refreshGroup.refreshSessionPerCoin[coinIndex]; - if (existingRefreshSession) { - return; - } + const existingRefreshSession = await tx.refreshSessions.get([ + refreshGroupId, + coinIndex, + ]); const oldCoinPub = refreshGroup.oldCoinPubs[coinIndex]; const coin = await tx.coins.get(oldCoinPub); if (!coin) { throw Error("Can't refresh, coin not found"); } - return { refreshGroup, coin }; + return { refreshGroup, coin, existingRefreshSession }; }); if (!d) { - return; + return undefined; + } + + if (d.existingRefreshSession) { + return d.existingRefreshSession; } const { refreshGroup, coin } = d; @@ -288,17 +296,23 @@ async function refreshCreateSession( const sessionSecretSeed = encodeCrock(getRandomBytes(64)); // Store refresh session for this coin in the database. - await ws.db - .mktx((x) => [x.refreshGroups, x.coins]) + const newSession = await ws.db + .mktx((x) => [x.refreshGroups, x.coins, x.refreshSessions]) .runReadWrite(async (tx) => { const rg = await tx.refreshGroups.get(refreshGroupId); if (!rg) { return; } - if (rg.refreshSessionPerCoin[coinIndex]) { + const existingSession = await tx.refreshSessions.get([ + refreshGroupId, + coinIndex, + ]); + if (existingSession) { return; } - rg.refreshSessionPerCoin[coinIndex] = { + const newSession: RefreshSessionRecord = { + coinIndex, + refreshGroupId, norevealIndex: undefined, sessionSecretSeed: sessionSecretSeed, newDenoms: newCoinDenoms.selectedDenoms.map((x) => ({ @@ -307,11 +321,13 @@ async function refreshCreateSession( })), amountRefreshOutput: Amounts.stringify(newCoinDenoms.totalCoinValue), }; - await tx.refreshGroups.put(rg); + await tx.refreshSessions.put(newSession); + return newSession; }); logger.trace( `created refresh session for coin #${coinIndex} in ${refreshGroupId}`, ); + return newSession; } function getRefreshRequestTimeout(rg: RefreshGroupRecord): Duration { @@ -326,13 +342,16 @@ async function refreshMelt( coinIndex: number, ): Promise<void> { const d = await ws.db - .mktx((x) => [x.refreshGroups, x.coins, x.denominations]) + .mktx((x) => [x.refreshGroups, x.refreshSessions, x.coins, x.denominations]) .runReadWrite(async (tx) => { const refreshGroup = await tx.refreshGroups.get(refreshGroupId); if (!refreshGroup) { return; } - const refreshSession = refreshGroup.refreshSessionPerCoin[coinIndex]; + const refreshSession = await tx.refreshSessions.get([ + refreshGroupId, + coinIndex, + ]); if (!refreshSession) { return; } @@ -442,7 +461,12 @@ async function refreshMelt( if (resp.status === HttpStatusCode.NotFound) { const errDetails = await readUnexpectedResponseDetails(resp); const transitionInfo = await ws.db - .mktx((x) => [x.refreshGroups, x.coins, x.coinAvailability]) + .mktx((x) => [ + x.refreshGroups, + x.refreshSessions, + x.coins, + x.coinAvailability, + ]) .runReadWrite(async (tx) => { const rg = await tx.refreshGroups.get(refreshGroupId); if (!rg) { @@ -456,12 +480,22 @@ async function refreshMelt( } const oldTxState = computeRefreshTransactionState(rg); rg.statusPerCoin[coinIndex] = RefreshCoinStatus.Failed; - rg.lastErrorPerCoin[coinIndex] = errDetails; + const refreshSession = await tx.refreshSessions.get([ + refreshGroupId, + coinIndex, + ]); + if (!refreshSession) { + throw Error( + "db invariant failed: missing refresh session in database", + ); + } + refreshSession.lastError = errDetails; const updateRes = updateGroupStatus(rg); if (updateRes.final) { await makeCoinsVisible(ws, tx, transactionId); } await tx.refreshGroups.put(rg); + await tx.refreshSessions.put(refreshSession); const newTxState = computeRefreshTransactionState(rg); return { oldTxState, @@ -493,7 +527,7 @@ async function refreshMelt( refreshSession.norevealIndex = norevealIndex; await ws.db - .mktx((x) => [x.refreshGroups]) + .mktx((x) => [x.refreshGroups, x.refreshSessions]) .runReadWrite(async (tx) => { const rg = await tx.refreshGroups.get(refreshGroupId); if (!rg) { @@ -502,7 +536,7 @@ async function refreshMelt( if (rg.timestampFinished) { return; } - const rs = rg.refreshSessionPerCoin[coinIndex]; + const rs = await tx.refreshSessions.get([refreshGroupId, coinIndex]); if (!rs) { return; } @@ -510,7 +544,7 @@ async function refreshMelt( return; } rs.norevealIndex = norevealIndex; - await tx.refreshGroups.put(rg); + await tx.refreshSessions.put(rs); }); } @@ -581,13 +615,16 @@ async function refreshReveal( `doing refresh reveal for ${refreshGroupId} (old coin ${coinIndex})`, ); const d = await ws.db - .mktx((x) => [x.refreshGroups, x.coins, x.denominations]) + .mktx((x) => [x.refreshGroups, x.refreshSessions, x.coins, x.denominations]) .runReadOnly(async (tx) => { const refreshGroup = await tx.refreshGroups.get(refreshGroupId); if (!refreshGroup) { return; } - const refreshSession = refreshGroup.refreshSessionPerCoin[coinIndex]; + const refreshSession = await tx.refreshSessions.get([ + refreshGroupId, + coinIndex, + ]); if (!refreshSession) { return; } @@ -755,6 +792,7 @@ async function refreshReveal( x.denominations, x.coinAvailability, x.refreshGroups, + x.refreshSessions, ]) .runReadWrite(async (tx) => { const rg = await tx.refreshGroups.get(refreshGroupId); @@ -762,7 +800,7 @@ async function refreshReveal( logger.warn("no refresh session found"); return; } - const rs = rg.refreshSessionPerCoin[coinIndex]; + const rs = await tx.refreshSessions.get([refreshGroupId, coinIndex]); if (!rs) { return; } @@ -858,10 +896,15 @@ async function processRefreshSession( logger.trace( `processing refresh session for coin ${coinIndex} of group ${refreshGroupId}`, ); - let refreshGroup = await ws.db - .mktx((x) => [x.refreshGroups]) + let { refreshGroup, refreshSession } = await ws.db + .mktx((x) => [x.refreshGroups, x.refreshSessions]) .runReadOnly(async (tx) => { - return tx.refreshGroups.get(refreshGroupId); + const rg = await tx.refreshGroups.get(refreshGroupId); + const rs = await tx.refreshSessions.get([refreshGroupId, coinIndex]); + return { + refreshGroup: rg, + refreshSession: rs, + }; }); if (!refreshGroup) { return; @@ -869,18 +912,9 @@ async function processRefreshSession( if (refreshGroup.statusPerCoin[coinIndex] === RefreshCoinStatus.Finished) { return; } - if (!refreshGroup.refreshSessionPerCoin[coinIndex]) { - await refreshCreateSession(ws, refreshGroupId, coinIndex); - refreshGroup = await ws.db - .mktx((x) => [x.refreshGroups]) - .runReadOnly(async (tx) => { - return tx.refreshGroups.get(refreshGroupId); - }); - if (!refreshGroup) { - return; - } + if (!refreshSession) { + refreshSession = await provideRefreshSession(ws, refreshGroupId, coinIndex); } - const refreshSession = refreshGroup.refreshSessionPerCoin[coinIndex]; if (!refreshSession) { if (refreshGroup.statusPerCoin[coinIndex] !== RefreshCoinStatus.Finished) { throw Error( @@ -1058,13 +1092,11 @@ export async function createRefreshGroup( timestampFinished: undefined, statusPerCoin: oldCoinPubs.map(() => RefreshCoinStatus.Pending), oldCoinPubs: oldCoinPubs.map((x) => x.coinPub), - lastErrorPerCoin: {}, reasonDetails, reason, refreshGroupId, - refreshSessionPerCoin: oldCoinPubs.map(() => undefined), inputPerCoin: oldCoinPubs.map((x) => x.amount), - estimatedOutputPerCoin: estimatedOutputPerCoin.map((x) => + expectedOutputPerCoin: estimatedOutputPerCoin.map((x) => Amounts.stringify(x), ), timestampCreated: TalerPreciseTimestamp.now(), @@ -1164,11 +1196,7 @@ export async function autoRefresh( if (AbsoluteTime.isExpired(executeThreshold)) { refreshCoins.push({ coinPub: coin.coinPub, - amount: Amounts.stringify({ - value: denom.amountVal, - fraction: denom.amountFrac, - currency: denom.currency, - }), + amount: denom.value, }); } else { const checkThreshold = getAutoRefreshCheckThreshold(denom); diff --git a/packages/taler-wallet-core/src/operations/reward.ts b/packages/taler-wallet-core/src/operations/reward.ts index 6f9d3ce85..6ae021174 100644 --- a/packages/taler-wallet-core/src/operations/reward.ts +++ b/packages/taler-wallet-core/src/operations/reward.ts @@ -108,7 +108,7 @@ export function computeRewardTransactionStatus( major: TransactionMajorState.Dialog, minor: TransactionMinorState.Proposed, }; - case RewardRecordStatus.SuspendidPickup: + case RewardRecordStatus.SuspendedPickup: return { major: TransactionMajorState.Pending, minor: TransactionMinorState.Pickup, @@ -128,7 +128,7 @@ export function computeTipTransactionActions( return [TransactionAction.Delete]; case RewardRecordStatus.PendingPickup: return [TransactionAction.Suspend, TransactionAction.Fail]; - case RewardRecordStatus.SuspendidPickup: + case RewardRecordStatus.SuspendedPickup: return [TransactionAction.Resume, TransactionAction.Fail]; case RewardRecordStatus.DialogAccept: return [TransactionAction.Abort]; @@ -255,7 +255,7 @@ export async function processTip( case RewardRecordStatus.Aborted: case RewardRecordStatus.DialogAccept: case RewardRecordStatus.Done: - case RewardRecordStatus.SuspendidPickup: + case RewardRecordStatus.SuspendedPickup: return TaskRunResult.finished(); } @@ -496,12 +496,12 @@ export async function suspendRewardTransaction( let newStatus: RewardRecordStatus | undefined = undefined; switch (tipRec.status) { case RewardRecordStatus.Done: - case RewardRecordStatus.SuspendidPickup: + case RewardRecordStatus.SuspendedPickup: case RewardRecordStatus.Aborted: case RewardRecordStatus.DialogAccept: break; case RewardRecordStatus.PendingPickup: - newStatus = RewardRecordStatus.SuspendidPickup; + newStatus = RewardRecordStatus.SuspendedPickup; break; default: @@ -551,7 +551,7 @@ export async function resumeTipTransaction( case RewardRecordStatus.Aborted: case RewardRecordStatus.DialogAccept: break; - case RewardRecordStatus.SuspendidPickup: + case RewardRecordStatus.SuspendedPickup: newStatus = RewardRecordStatus.PendingPickup; break; default: @@ -608,7 +608,7 @@ export async function abortTipTransaction( case RewardRecordStatus.PendingPickup: case RewardRecordStatus.DialogAccept: break; - case RewardRecordStatus.SuspendidPickup: + case RewardRecordStatus.SuspendedPickup: newStatus = RewardRecordStatus.Aborted; break; default: diff --git a/packages/taler-wallet-core/src/operations/testing.ts b/packages/taler-wallet-core/src/operations/testing.ts index 1962c965c..f71d842c7 100644 --- a/packages/taler-wallet-core/src/operations/testing.ts +++ b/packages/taler-wallet-core/src/operations/testing.ts @@ -700,7 +700,7 @@ export async function runIntegrationTest2( }); await confirmPeerPushCredit(ws, { - peerPushPaymentIncomingId: peerPushCredit.peerPushPaymentIncomingId, + peerPushCreditId: peerPushCredit.peerPushCreditId, }); const peerPullInit = await initiatePeerPullPayment(ws, { @@ -723,7 +723,7 @@ export async function runIntegrationTest2( }); await confirmPeerPullDebit(ws, { - peerPullPaymentIncomingId: peerPullInc.peerPullPaymentIncomingId, + peerPullDebitId: peerPullInc.peerPullDebitId, }); await waitUntilDone(ws); diff --git a/packages/taler-wallet-core/src/operations/transactions.ts b/packages/taler-wallet-core/src/operations/transactions.ts index 7f5302b25..7bdb9af5b 100644 --- a/packages/taler-wallet-core/src/operations/transactions.ts +++ b/packages/taler-wallet-core/src/operations/transactions.ts @@ -50,10 +50,10 @@ import { OperationRetryRecord, PeerPullPaymentIncomingRecord, PeerPullDebitRecordStatus, - PeerPullPaymentInitiationRecord, + PeerPullCreditRecord, PeerPushPaymentIncomingRecord, - PeerPushPaymentIncomingStatus, - PeerPushPaymentInitiationRecord, + PeerPushCreditStatus, + PeerPushDebitRecord, PurchaseRecord, PurchaseStatus, RefreshGroupRecord, @@ -154,7 +154,18 @@ import { resumePeerPushDebitTransaction, abortPeerPushDebitTransaction, } from "./pay-peer-push-debit.js"; -import { iterRecordsForDeposit, iterRecordsForPeerPullDebit, iterRecordsForPeerPullInitiation, iterRecordsForPeerPushCredit, iterRecordsForPeerPushInitiation, iterRecordsForPurchase, iterRecordsForRefresh, iterRecordsForRefund, iterRecordsForReward, iterRecordsForWithdrawal } from "./pending.js"; +import { + iterRecordsForDeposit, + iterRecordsForPeerPullDebit, + iterRecordsForPeerPullInitiation, + iterRecordsForPeerPushCredit, + iterRecordsForPeerPushInitiation, + iterRecordsForPurchase, + iterRecordsForRefresh, + iterRecordsForRefund, + iterRecordsForReward, + iterRecordsForWithdrawal, +} from "./pending.js"; const logger = new Logger("taler-wallet-core:transactions.ts"); @@ -335,11 +346,9 @@ export async function getTransactionById( } case TransactionType.PeerPullDebit: { return await ws.db - .mktx((x) => [x.peerPullPaymentIncoming]) + .mktx((x) => [x.peerPullDebit]) .runReadWrite(async (tx) => { - const debit = await tx.peerPullPaymentIncoming.get( - parsedTx.peerPullPaymentIncomingId, - ); + const debit = await tx.peerPullDebit.get(parsedTx.peerPullDebitId); if (!debit) throw Error("not found"); return buildTransactionForPullPaymentDebit(debit); }); @@ -347,11 +356,9 @@ export async function getTransactionById( case TransactionType.PeerPushDebit: { return await ws.db - .mktx((x) => [x.peerPushPaymentInitiations, x.contractTerms]) + .mktx((x) => [x.peerPushDebit, x.contractTerms]) .runReadWrite(async (tx) => { - const debit = await tx.peerPushPaymentInitiations.get( - parsedTx.pursePub, - ); + const debit = await tx.peerPushDebit.get(parsedTx.pursePub); if (!debit) throw Error("not found"); const ct = await tx.contractTerms.get(debit.contractTermsHash); checkDbInvariant(!!ct); @@ -363,18 +370,16 @@ export async function getTransactionById( } case TransactionType.PeerPushCredit: { - const peerPushPaymentIncomingId = parsedTx.peerPushPaymentIncomingId; + const peerPushCreditId = parsedTx.peerPushCreditId; return await ws.db .mktx((x) => [ - x.peerPushPaymentIncoming, + x.peerPushCredit, x.contractTerms, x.withdrawalGroups, x.operationRetries, ]) .runReadWrite(async (tx) => { - const pushInc = await tx.peerPushPaymentIncoming.get( - peerPushPaymentIncomingId, - ); + const pushInc = await tx.peerPushCredit.get(peerPushCreditId); if (!pushInc) throw Error("not found"); const ct = await tx.contractTerms.get(pushInc.contractTermsHash); checkDbInvariant(!!ct); @@ -405,13 +410,13 @@ export async function getTransactionById( const pursePub = parsedTx.pursePub; return await ws.db .mktx((x) => [ - x.peerPullPaymentInitiations, + x.peerPullCredit, x.contractTerms, x.withdrawalGroups, x.operationRetries, ]) .runReadWrite(async (tx) => { - const pushInc = await tx.peerPullPaymentInitiations.get(pursePub); + const pushInc = await tx.peerPullCredit.get(pursePub); if (!pushInc) throw Error("not found"); const ct = await tx.contractTerms.get(pushInc.contractTermsHash); checkDbInvariant(!!ct); @@ -442,7 +447,7 @@ export async function getTransactionById( } function buildTransactionForPushPaymentDebit( - pi: PeerPushPaymentInitiationRecord, + pi: PeerPushDebitRecord, contractTerms: PeerContractTerms, ort?: OperationRetryRecord, ): Transaction { @@ -490,14 +495,14 @@ function buildTransactionForPullPaymentDebit( timestamp: pi.timestampCreated, transactionId: constructTransactionIdentifier({ tag: TransactionType.PeerPullDebit, - peerPullPaymentIncomingId: pi.peerPullPaymentIncomingId, + peerPullDebitId: pi.peerPullDebitId, }), ...(ort?.lastError ? { error: ort.lastError } : {}), }; } function buildTransactionForPeerPullCredit( - pullCredit: PeerPullPaymentInitiationRecord, + pullCredit: PeerPullCreditRecord, pullCreditOrt: OperationRetryRecord | undefined, peerContractTerms: PeerContractTerms, wsr: WithdrawalGroupRecord | undefined, @@ -532,8 +537,8 @@ function buildTransactionForPeerPullCredit( // Old transactions don't have it! timestamp: pullCredit.mergeTimestamp ?? TalerPreciseTimestamp.now(), info: { - expiration: wsr.wgInfo.contractTerms.purse_expiration, - summary: wsr.wgInfo.contractTerms.summary, + expiration: peerContractTerms.purse_expiration, + summary: peerContractTerms.summary, }, talerUri: stringifyPayPullUri({ exchangeBaseUrl: wsr.exchangeBaseUrl, @@ -600,13 +605,13 @@ function buildTransactionForPeerPushCredit( amountRaw: Amounts.stringify(wsr.instructedAmount), exchangeBaseUrl: wsr.exchangeBaseUrl, info: { - expiration: wsr.wgInfo.contractTerms.purse_expiration, - summary: wsr.wgInfo.contractTerms.summary, + expiration: peerContractTerms.purse_expiration, + summary: peerContractTerms.summary, }, timestamp: wsr.timestampStart, transactionId: constructTransactionIdentifier({ tag: TransactionType.PeerPushCredit, - peerPushPaymentIncomingId: pushInc.peerPushPaymentIncomingId, + peerPushCreditId: pushInc.peerPushCreditId, }), kycUrl: pushInc.kycUrl, ...(wsrOrt?.lastError ? { error: wsrOrt.lastError } : {}), @@ -629,7 +634,7 @@ function buildTransactionForPeerPushCredit( timestamp: pushInc.timestamp, transactionId: constructTransactionIdentifier({ tag: TransactionType.PeerPushCredit, - peerPushPaymentIncomingId: pushInc.peerPushPaymentIncomingId, + peerPushCreditId: pushInc.peerPushCreditId, }), ...(pushOrt?.lastError ? { error: pushOrt.lastError } : {}), }; @@ -654,7 +659,7 @@ function buildTransactionForBankIntegratedWithdraw( reservePub: wgRecord.reservePub, bankConfirmationUrl: wgRecord.wgInfo.bankInfo.confirmUrl, reserveIsReady: - wgRecord.status === WithdrawalGroupStatus.Finished || + wgRecord.status === WithdrawalGroupStatus.Done || wgRecord.status === WithdrawalGroupStatus.PendingReady, }, kycUrl: wgRecord.kycUrl, @@ -698,7 +703,7 @@ function buildTransactionForManualWithdraw( reservePub: withdrawalGroup.reservePub, exchangePaytoUris, reserveIsReady: - withdrawalGroup.status === WithdrawalGroupStatus.Finished || + withdrawalGroup.status === WithdrawalGroupStatus.Done || withdrawalGroup.status === WithdrawalGroupStatus.PendingReady, }, kycUrl: withdrawalGroup.kycUrl, @@ -755,7 +760,7 @@ function buildTransactionForRefresh( ).amount; const outputAmount = Amounts.sumOrZero( refreshGroupRecord.currency, - refreshGroupRecord.estimatedOutputPerCoin, + refreshGroupRecord.expectedOutputPerCoin, ).amount; return { type: TransactionType.Refresh, @@ -786,8 +791,8 @@ function buildTransactionForDeposit( ort?: OperationRetryRecord, ): Transaction { let deposited = true; - for (const d of dg.depositedPerCoin) { - if (!d) { + for (const d of dg.statusPerCoin) { + if (d == DepositElementStatus.DepositPending) { deposited = false; } } @@ -796,7 +801,7 @@ function buildTransactionForDeposit( type: TransactionType.Deposit, txState: computeDepositTransactionStatus(dg), txActions: computeDepositTransactionActions(dg), - amountRaw: Amounts.stringify(dg.effectiveDepositAmount), + amountRaw: Amounts.stringify(dg.counterpartyEffectiveDepositAmount), amountEffective: Amounts.stringify(dg.totalPayCost), timestamp: dg.timestampCreated, targetPaytoUri: dg.wire.payto_uri, @@ -807,11 +812,11 @@ function buildTransactionForDeposit( }), wireTransferProgress: (100 * - dg.transactionPerCoin.reduce( + dg.statusPerCoin.reduce( (prev, cur) => prev + (cur === DepositElementStatus.Wired ? 1 : 0), 0, )) / - dg.transactionPerCoin.length, + dg.statusPerCoin.length, depositGroupId: dg.depositGroupId, trackingState: Object.values(dg.trackingState ?? {}), deposited, @@ -879,7 +884,6 @@ async function buildTransactionForPurchase( const info: OrderShortInfo = { merchant: contractData.merchant, orderId: contractData.orderId, - products: contractData.products, summary: contractData.summary, summary_i18n: contractData.summaryI18n, contractTermsHash: contractData.contractTermsHash, @@ -944,10 +948,10 @@ export async function getTransactions( x.exchangeDetails, x.exchanges, x.operationRetries, - x.peerPullPaymentIncoming, - x.peerPushPaymentInitiations, - x.peerPushPaymentIncoming, - x.peerPullPaymentInitiations, + x.peerPullDebit, + x.peerPushDebit, + x.peerPushCredit, + x.peerPullCredit, x.planchets, x.purchases, x.contractTerms, @@ -985,7 +989,7 @@ export async function getTransactions( } if ( pi.status !== PeerPullDebitRecordStatus.PendingDeposit && - pi.status !== PeerPullDebitRecordStatus.DonePaid + pi.status !== PeerPullDebitRecordStatus.Done ) { return; } @@ -1004,7 +1008,7 @@ export async function getTransactions( if (shouldSkipSearch(transactionsRequest, [])) { return; } - if (pi.status === PeerPushPaymentIncomingStatus.DialogProposed) { + if (pi.status === PeerPushCreditStatus.DialogProposed) { // We don't report proposed push credit transactions, user needs // to scan URI again and confirm to see it. return; @@ -1033,7 +1037,7 @@ export async function getTransactions( ), ); }); - + await iterRecordsForPeerPullInitiation(tx, filter, async (pi) => { const currency = Amounts.currencyOf(pi.amount); if (shouldSkipCurrency(transactionsRequest, currency)) { @@ -1078,7 +1082,7 @@ export async function getTransactions( ); transactions.push(buildTransactionForRefund(refundGroup, contractData)); }); - + await iterRecordsForRefresh(tx, filter, async (rg) => { if (shouldSkipCurrency(transactionsRequest, rg.currency)) { return; @@ -1099,7 +1103,7 @@ export async function getTransactions( } }); - await iterRecordsForWithdrawal(tx, filter ,async (wsr) => { + await iterRecordsForWithdrawal(tx, filter, async (wsr) => { if ( shouldSkipCurrency( transactionsRequest, @@ -1268,9 +1272,9 @@ export async function getTransactions( export type ParsedTransactionIdentifier = | { tag: TransactionType.Deposit; depositGroupId: string } | { tag: TransactionType.Payment; proposalId: string } - | { tag: TransactionType.PeerPullDebit; peerPullPaymentIncomingId: string } + | { tag: TransactionType.PeerPullDebit; peerPullDebitId: string } | { tag: TransactionType.PeerPullCredit; pursePub: string } - | { tag: TransactionType.PeerPushCredit; peerPushPaymentIncomingId: string } + | { tag: TransactionType.PeerPushCredit; peerPushCreditId: string } | { tag: TransactionType.PeerPushDebit; pursePub: string } | { tag: TransactionType.Refresh; refreshGroupId: string } | { tag: TransactionType.Refund; refundGroupId: string } @@ -1289,9 +1293,9 @@ export function constructTransactionIdentifier( case TransactionType.PeerPullCredit: return `txn:${pTxId.tag}:${pTxId.pursePub}` as TransactionIdStr; case TransactionType.PeerPullDebit: - return `txn:${pTxId.tag}:${pTxId.peerPullPaymentIncomingId}` as TransactionIdStr; + return `txn:${pTxId.tag}:${pTxId.peerPullDebitId}` as TransactionIdStr; case TransactionType.PeerPushCredit: - return `txn:${pTxId.tag}:${pTxId.peerPushPaymentIncomingId}` as TransactionIdStr; + return `txn:${pTxId.tag}:${pTxId.peerPushCreditId}` as TransactionIdStr; case TransactionType.PeerPushDebit: return `txn:${pTxId.tag}:${pTxId.pursePub}` as TransactionIdStr; case TransactionType.Refresh: @@ -1337,12 +1341,12 @@ export function parseTransactionIdentifier( case TransactionType.PeerPullDebit: return { tag: TransactionType.PeerPullDebit, - peerPullPaymentIncomingId: rest[0], + peerPullDebitId: rest[0], }; case TransactionType.PeerPushCredit: return { tag: TransactionType.PeerPushCredit, - peerPushPaymentIncomingId: rest[0], + peerPushCreditId: rest[0], }; case TransactionType.PeerPushDebit: return { tag: TransactionType.PeerPushDebit, pursePub: rest[0] }; @@ -1455,7 +1459,7 @@ export async function retryTransaction( case TransactionType.PeerPullDebit: { const taskId = constructTaskIdentifier({ tag: PendingTaskType.PeerPullDebit, - peerPullPaymentIncomingId: parsedTx.peerPullPaymentIncomingId, + peerPullDebitId: parsedTx.peerPullDebitId, }); await resetPendingTaskTimeout(ws, taskId); stopLongpolling(ws, taskId); @@ -1464,7 +1468,7 @@ export async function retryTransaction( case TransactionType.PeerPushCredit: { const taskId = constructTaskIdentifier({ tag: PendingTaskType.PeerPushCredit, - peerPushPaymentIncomingId: parsedTx.peerPushPaymentIncomingId, + peerPushCreditId: parsedTx.peerPushCreditId, }); await resetPendingTaskTimeout(ws, taskId); stopLongpolling(ws, taskId); @@ -1522,10 +1526,10 @@ export async function suspendTransaction( await suspendPeerPushDebitTransaction(ws, tx.pursePub); break; case TransactionType.PeerPullDebit: - await suspendPeerPullDebitTransaction(ws, tx.peerPullPaymentIncomingId); + await suspendPeerPullDebitTransaction(ws, tx.peerPullDebitId); break; case TransactionType.PeerPushCredit: - await suspendPeerPushCreditTransaction(ws, tx.peerPushPaymentIncomingId); + await suspendPeerPushCreditTransaction(ws, tx.peerPushCreditId); break; case TransactionType.Refund: throw Error("refund transactions can't be suspended or resumed"); @@ -1568,10 +1572,10 @@ export async function failTransaction( await failPeerPullCreditTransaction(ws, tx.pursePub); return; case TransactionType.PeerPullDebit: - await failPeerPullDebitTransaction(ws, tx.peerPullPaymentIncomingId); + await failPeerPullDebitTransaction(ws, tx.peerPullDebitId); return; case TransactionType.PeerPushCredit: - await failPeerPushCreditTransaction(ws, tx.peerPushPaymentIncomingId); + await failPeerPushCreditTransaction(ws, tx.peerPushCreditId); return; case TransactionType.PeerPushDebit: await failPeerPushDebitTransaction(ws, tx.pursePub); @@ -1613,10 +1617,10 @@ export async function resumeTransaction( await resumePeerPushDebitTransaction(ws, tx.pursePub); break; case TransactionType.PeerPullDebit: - await resumePeerPullDebitTransaction(ws, tx.peerPullPaymentIncomingId); + await resumePeerPullDebitTransaction(ws, tx.peerPullDebitId); break; case TransactionType.PeerPushCredit: - await resumePeerPushCreditTransaction(ws, tx.peerPushPaymentIncomingId); + await resumePeerPushCreditTransaction(ws, tx.peerPushCreditId); break; case TransactionType.Refund: throw Error("refund transactions can't be suspended or resumed"); @@ -1641,17 +1645,11 @@ export async function deleteTransaction( switch (parsedTx.tag) { case TransactionType.PeerPushCredit: { - const peerPushPaymentIncomingId = parsedTx.peerPushPaymentIncomingId; + const peerPushCreditId = parsedTx.peerPushCreditId; await ws.db - .mktx((x) => [ - x.withdrawalGroups, - x.peerPushPaymentIncoming, - x.tombstones, - ]) + .mktx((x) => [x.withdrawalGroups, x.peerPushCredit, x.tombstones]) .runReadWrite(async (tx) => { - const pushInc = await tx.peerPushPaymentIncoming.get( - peerPushPaymentIncomingId, - ); + const pushInc = await tx.peerPushCredit.get(peerPushCreditId); if (!pushInc) { return; } @@ -1668,12 +1666,9 @@ export async function deleteTransaction( }); } } - await tx.peerPushPaymentIncoming.delete(peerPushPaymentIncomingId); + await tx.peerPushCredit.delete(peerPushCreditId); await tx.tombstones.put({ - id: - TombstoneTag.DeletePeerPushCredit + - ":" + - peerPushPaymentIncomingId, + id: TombstoneTag.DeletePeerPushCredit + ":" + peerPushCreditId, }); }); return; @@ -1682,13 +1677,9 @@ export async function deleteTransaction( case TransactionType.PeerPullCredit: { const pursePub = parsedTx.pursePub; await ws.db - .mktx((x) => [ - x.withdrawalGroups, - x.peerPullPaymentInitiations, - x.tombstones, - ]) + .mktx((x) => [x.withdrawalGroups, x.peerPullCredit, x.tombstones]) .runReadWrite(async (tx) => { - const pullIni = await tx.peerPullPaymentInitiations.get(pursePub); + const pullIni = await tx.peerPullCredit.get(pursePub); if (!pullIni) { return; } @@ -1705,7 +1696,7 @@ export async function deleteTransaction( }); } } - await tx.peerPullPaymentInitiations.delete(pursePub); + await tx.peerPullCredit.delete(pursePub); await tx.tombstones.put({ id: TombstoneTag.DeletePeerPullCredit + ":" + pursePub, }); @@ -1795,7 +1786,7 @@ export async function deleteTransaction( case TransactionType.Refund: { const refundGroupId = parsedTx.refundGroupId; await ws.db - .mktx((x) => [x.refundGroups, x.tombstones, x.refundItems]) + .mktx((x) => [x.refundGroups, x.tombstones]) .runReadWrite(async (tx) => { const refundRecord = await tx.refundGroups.get(refundGroupId); if (!refundRecord) { @@ -1809,15 +1800,13 @@ export async function deleteTransaction( } case TransactionType.PeerPullDebit: { - const peerPullPaymentIncomingId = parsedTx.peerPullPaymentIncomingId; + const peerPullDebitId = parsedTx.peerPullDebitId; await ws.db - .mktx((x) => [x.peerPullPaymentIncoming, x.tombstones]) + .mktx((x) => [x.peerPullDebit, x.tombstones]) .runReadWrite(async (tx) => { - const debit = await tx.peerPullPaymentIncoming.get( - peerPullPaymentIncomingId, - ); + const debit = await tx.peerPullDebit.get(peerPullDebitId); if (debit) { - await tx.peerPullPaymentIncoming.delete(peerPullPaymentIncomingId); + await tx.peerPullDebit.delete(peerPullDebitId); await tx.tombstones.put({ id: transactionId }); } }); @@ -1828,11 +1817,11 @@ export async function deleteTransaction( case TransactionType.PeerPushDebit: { const pursePub = parsedTx.pursePub; await ws.db - .mktx((x) => [x.peerPushPaymentInitiations, x.tombstones]) + .mktx((x) => [x.peerPushDebit, x.tombstones]) .runReadWrite(async (tx) => { - const debit = await tx.peerPushPaymentInitiations.get(pursePub); + const debit = await tx.peerPushDebit.get(pursePub); if (debit) { - await tx.peerPushPaymentInitiations.delete(pursePub); + await tx.peerPushDebit.delete(pursePub); await tx.tombstones.put({ id: transactionId }); } }); @@ -1875,10 +1864,10 @@ export async function abortTransaction( await abortPeerPullCreditTransaction(ws, txId.pursePub); break; case TransactionType.PeerPullDebit: - await abortPeerPullDebitTransaction(ws, txId.peerPullPaymentIncomingId); + await abortPeerPullDebitTransaction(ws, txId.peerPullDebitId); break; case TransactionType.PeerPushCredit: - await abortPeerPushCreditTransaction(ws, txId.peerPushPaymentIncomingId); + await abortPeerPushCreditTransaction(ws, txId.peerPushCreditId); break; case TransactionType.PeerPushDebit: await abortPeerPushDebitTransaction(ws, txId.pursePub); diff --git a/packages/taler-wallet-core/src/operations/withdraw.test.ts b/packages/taler-wallet-core/src/operations/withdraw.test.ts index 5a557b5de..2d9286610 100644 --- a/packages/taler-wallet-core/src/operations/withdraw.test.ts +++ b/packages/taler-wallet-core/src/operations/withdraw.test.ts @@ -78,8 +78,7 @@ test("withdrawal selection bug repro", (t) => { }, verificationStatus: DenominationVerificationStatus.Unverified, currency: "KUDOS", - amountFrac: 0, - amountVal: 1000, + value: "KUDOS:1000", listIssueDate: { t_s: 0 }, }, { @@ -133,8 +132,7 @@ test("withdrawal selection bug repro", (t) => { t_s: 1585229388, }, verificationStatus: DenominationVerificationStatus.Unverified, - amountFrac: 0, - amountVal: 10, + value: "KUDOS:10", currency: "KUDOS", listIssueDate: { t_s: 0 }, }, @@ -188,8 +186,7 @@ test("withdrawal selection bug repro", (t) => { t_s: 1585229388, }, verificationStatus: DenominationVerificationStatus.Unverified, - amountFrac: 0, - amountVal: 5, + value: "KUDOS:5", currency: "KUDOS", listIssueDate: { t_s: 0 }, }, @@ -244,8 +241,7 @@ test("withdrawal selection bug repro", (t) => { t_s: 1585229388, }, verificationStatus: DenominationVerificationStatus.Unverified, - amountFrac: 0, - amountVal: 1, + value: "KUDOS:1", currency: "KUDOS", listIssueDate: { t_s: 0 }, }, @@ -299,8 +295,11 @@ test("withdrawal selection bug repro", (t) => { t_s: 1585229388, }, verificationStatus: DenominationVerificationStatus.Unverified, - amountFrac: 10000000, - amountVal: 0, + value: Amounts.stringify({ + currency: "KUDOS", + fraction: 10000000, + value: 0, + }), currency: "KUDOS", listIssueDate: { t_s: 0 }, }, @@ -354,8 +353,7 @@ test("withdrawal selection bug repro", (t) => { t_s: 1585229388, }, verificationStatus: DenominationVerificationStatus.Unverified, - amountFrac: 0, - amountVal: 2, + value: "KUDOS:2", currency: "KUDOS", listIssueDate: { t_s: 0 }, }, diff --git a/packages/taler-wallet-core/src/operations/withdraw.ts b/packages/taler-wallet-core/src/operations/withdraw.ts index a3d95fb5c..bae348dc1 100644 --- a/packages/taler-wallet-core/src/operations/withdraw.ts +++ b/packages/taler-wallet-core/src/operations/withdraw.ts @@ -123,7 +123,6 @@ import { import { getExchangeDetails, getExchangePaytoUri, - getExchangeTrust, updateExchangeFromUrl, } from "./exchanges.js"; import { @@ -320,7 +319,7 @@ export async function abortWithdrawalTransaction( case WithdrawalGroupStatus.AbortingBank: // No transition needed, but not an error break; - case WithdrawalGroupStatus.Finished: + case WithdrawalGroupStatus.Done: case WithdrawalGroupStatus.FailedBankAborted: case WithdrawalGroupStatus.AbortedExchange: case WithdrawalGroupStatus.AbortedBank: @@ -400,7 +399,7 @@ export function computeWithdrawalTransactionStatus( return { major: TransactionMajorState.Aborted, }; - case WithdrawalGroupStatus.Finished: + case WithdrawalGroupStatus.Done: return { major: TransactionMajorState.Done, }; @@ -504,7 +503,7 @@ export function computeWithdrawalTransactionActions( switch (wgRecord.status) { case WithdrawalGroupStatus.FailedBankAborted: return [TransactionAction.Delete]; - case WithdrawalGroupStatus.Finished: + case WithdrawalGroupStatus.Done: return [TransactionAction.Delete]; case WithdrawalGroupStatus.PendingRegisteringBank: return [TransactionAction.Suspend, TransactionAction.Abort]; @@ -726,7 +725,9 @@ interface WithdrawalBatchResult { batchResp: ExchangeWithdrawBatchResponse; } enum AmlStatus { - normal= 0, pending = 1, fronzen = 2, + normal = 0, + pending = 1, + fronzen = 2, } async function handleKycRequired( @@ -773,7 +774,9 @@ async function handleKycRequired( const kycStatus = await kycStatusRes.json(); logger.info(`kyc status: ${j2s(kycStatus)}`); kycUrl = kycStatus.kyc_url; - } else if (kycStatusRes.status === HttpStatusCode.UnavailableForLegalReasons) { + } else if ( + kycStatusRes.status === HttpStatusCode.UnavailableForLegalReasons + ) { const kycStatus = await kycStatusRes.json(); logger.info(`aml status: ${j2s(kycStatus)}`); amlStatus = kycStatus.aml_status; @@ -809,11 +812,15 @@ async function handleKycRequired( requirementRow: uuidResp.requirement_row, }; wg2.kycUrl = kycUrl; - wg2.status = amlStatus === AmlStatus.normal || amlStatus === undefined ? WithdrawalGroupStatus.PendingKyc : - amlStatus === AmlStatus.pending ? WithdrawalGroupStatus.PendingAml : - amlStatus === AmlStatus.fronzen ? WithdrawalGroupStatus.SuspendedAml : - assertUnreachable(amlStatus); - + wg2.status = + amlStatus === AmlStatus.normal || amlStatus === undefined + ? WithdrawalGroupStatus.PendingKyc + : amlStatus === AmlStatus.pending + ? WithdrawalGroupStatus.PendingAml + : amlStatus === AmlStatus.fronzen + ? WithdrawalGroupStatus.SuspendedAml + : assertUnreachable(amlStatus); + await tx.withdrawalGroups.put(wg2); const newTxState = computeWithdrawalTransactionStatus(wg2); return { @@ -924,31 +931,31 @@ async function processPlanchetExchangeBatchRequest( // FIXME: handle individual error codes better! - const reqUrl = new URL( - `reserves/${withdrawalGroup.reservePub}/batch-withdraw`, - withdrawalGroup.exchangeBaseUrl, - ).href; + const reqUrl = new URL( + `reserves/${withdrawalGroup.reservePub}/batch-withdraw`, + withdrawalGroup.exchangeBaseUrl, + ).href; - try { - const resp = await ws.http.postJson(reqUrl, batchReq); - if (resp.status === HttpStatusCode.UnavailableForLegalReasons) { - await handleKycRequired(ws, withdrawalGroup, resp, 0, requestCoinIdxs); - } - const r = await readSuccessResponseJsonOrThrow( - resp, - codecForWithdrawBatchResponse(), - ); - return { - coinIdxs: requestCoinIdxs, - batchResp: r, - }; - } catch (e) { - await storeCoinError(e, requestCoinIdxs[0]); - return { - batchResp: { ev_sigs: [] }, - coinIdxs: [], - }; + try { + const resp = await ws.http.postJson(reqUrl, batchReq); + if (resp.status === HttpStatusCode.UnavailableForLegalReasons) { + await handleKycRequired(ws, withdrawalGroup, resp, 0, requestCoinIdxs); } + const r = await readSuccessResponseJsonOrThrow( + resp, + codecForWithdrawBatchResponse(), + ); + return { + coinIdxs: requestCoinIdxs, + batchResp: r, + }; + } catch (e) { + await storeCoinError(e, requestCoinIdxs[0]); + return { + batchResp: { ev_sigs: [] }, + coinIdxs: [], + }; + } } async function processPlanchetVerifyAndStoreCoin( @@ -1415,10 +1422,12 @@ async function processWithdrawalGroupPendingKyc( logger.info(`kyc status: ${j2s(kycStatus)}`); // FIXME: do we need to update the KYC url, or does it always stay constant? return { ready: false }; - } else if (kycStatusRes.status === HttpStatusCode.UnavailableForLegalReasons) { + } else if ( + kycStatusRes.status === HttpStatusCode.UnavailableForLegalReasons + ) { const kycStatus = await kycStatusRes.json(); logger.info(`aml status: ${j2s(kycStatus)}`); - return {ready : false} + return { ready: false }; } else { throw Error( `unexpected response from kyc-check (${kycStatusRes.status})`, @@ -1453,7 +1462,7 @@ async function processWithdrawalGroupPendingReady( return undefined; } const txStatusOld = computeWithdrawalTransactionStatus(wg); - wg.status = WithdrawalGroupStatus.Finished; + wg.status = WithdrawalGroupStatus.Done; wg.timestampFinish = TalerPreciseTimestamp.now(); const txStatusNew = computeWithdrawalTransactionStatus(wg); await tx.withdrawalGroups.put(wg); @@ -1551,7 +1560,7 @@ async function processWithdrawalGroupPendingReady( logger.info(`now withdrawn ${numFinished} of ${numTotalCoins} coins`); if (wg.timestampFinish === undefined && numFinished === numTotalCoins) { wg.timestampFinish = TalerPreciseTimestamp.now(); - wg.status = WithdrawalGroupStatus.Finished; + wg.status = WithdrawalGroupStatus.Done; await makeCoinsVisible(ws, tx, transactionId); } @@ -1643,7 +1652,7 @@ export async function processWithdrawalGroup( } break; } - case WithdrawalGroupStatus.Finished: + case WithdrawalGroupStatus.Done: case WithdrawalGroupStatus.FailedBankAborted: { // FIXME return TaskRunResult.pending(); @@ -1714,11 +1723,6 @@ export async function getExchangeWithdrawalInfo( exchangeWireAccounts.push(account.payto_uri); } - const { isTrusted, isAudited } = await ws.exchangeOps.getExchangeTrust( - ws, - exchange, - ); - let hasDenomWithAgeRestriction = false; logger.trace("computing earliest deposit expiration"); @@ -1797,8 +1801,6 @@ export async function getExchangeWithdrawalInfo( exchangePaytoUris: paytoUris, exchangeWireAccounts, exchangeVersion: exchangeDetails.protocolVersionRange || "unknown", - isAudited, - isTrusted, numOfferedDenoms: possibleDenoms.length, selectedDenoms, // FIXME: delete this field / replace by something we can display to the user @@ -1859,7 +1861,6 @@ export async function getWithdrawalDetailsForUri( .mktx((x) => [ x.exchanges, x.exchangeDetails, - x.exchangeTos, x.denominations, x.operationRetries, ]) @@ -2215,8 +2216,6 @@ export interface PrepareCreateWithdrawalGroupResult { withdrawalGroup: WithdrawalGroupRecord; transactionId: string; creationInfo?: { - isTrusted: boolean; - isAudited: boolean; amount: AmountJson; canonExchange: string; exchangeDetails: ExchangeDetailsRecord; @@ -2311,10 +2310,6 @@ export async function internalPrepareCreateWithdrawalGroup( logger.trace(exchangeDetails); throw Error("exchange not updated"); } - const { isAudited, isTrusted } = await getExchangeTrust( - ws, - exchangeInfo.exchange, - ); const transactionId = constructTransactionIdentifier({ tag: TransactionType.Withdrawal, withdrawalGroupId: withdrawalGroup.withdrawalGroupId, @@ -2324,8 +2319,6 @@ export async function internalPrepareCreateWithdrawalGroup( withdrawalGroup, transactionId, creationInfo: { - isAudited, - isTrusted, canonExchange, amount, exchangeDetails, @@ -2344,7 +2337,6 @@ export async function internalPerformCreateWithdrawalGroup( withdrawalGroups: typeof WalletStoresV1.withdrawalGroups; reserves: typeof WalletStoresV1.reserves; exchanges: typeof WalletStoresV1.exchanges; - exchangeTrust: typeof WalletStoresV1.exchangeTrust; }>, prep: PrepareCreateWithdrawalGroupResult, ): Promise<PerformCreateWithdrawalGroupResult> { @@ -2352,7 +2344,7 @@ export async function internalPerformCreateWithdrawalGroup( if (!prep.creationInfo) { return { withdrawalGroup, transitionInfo: undefined }; } - const { isAudited, isTrusted, amount, canonExchange, exchangeDetails } = + const { amount, canonExchange, exchangeDetails } = prep.creationInfo; await tx.withdrawalGroups.add(withdrawalGroup); @@ -2368,15 +2360,6 @@ export async function internalPerformCreateWithdrawalGroup( await tx.exchanges.put(exchange); } - if (!isAudited && !isTrusted) { - await tx.exchangeTrust.put({ - currency: amount.currency, - exchangeBaseUrl: canonExchange, - exchangeMasterPub: exchangeDetails.masterPublicKey, - uids: [encodeCrock(getRandomBytes(32))], - }); - } - const oldTxState = { major: TransactionMajorState.None, minor: undefined, @@ -2422,7 +2405,6 @@ export async function internalCreateWithdrawalGroup( x.reserves, x.exchanges, x.exchangeDetails, - x.exchangeTrust, ]) .runReadWrite(async (tx) => { return await internalPerformCreateWithdrawalGroup(ws, tx, prep); @@ -2568,7 +2550,6 @@ export async function createManualWithdrawal( x.withdrawalGroups, x.exchanges, x.exchangeDetails, - x.exchangeTrust, ]) .runReadOnly(async (tx) => { return await getFundingPaytoUris(tx, withdrawalGroup.withdrawalGroupId); diff --git a/packages/taler-wallet-core/src/pending-types.ts b/packages/taler-wallet-core/src/pending-types.ts index 82eb542a7..627888b4d 100644 --- a/packages/taler-wallet-core/src/pending-types.ts +++ b/packages/taler-wallet-core/src/pending-types.ts @@ -99,14 +99,14 @@ export interface PendingPeerPullInitiationTask { */ export interface PendingPeerPullDebitTask { type: PendingTaskType.PeerPullDebit; - peerPullPaymentIncomingId: string; + peerPullDebitId: string; } /** */ export interface PendingPeerPushCreditTask { type: PendingTaskType.PeerPushCredit; - peerPushPaymentIncomingId: string; + peerPushCreditId: string; } /** diff --git a/packages/taler-wallet-core/src/remote.ts b/packages/taler-wallet-core/src/remote.ts index 89348698e..164f7cfe9 100644 --- a/packages/taler-wallet-core/src/remote.ts +++ b/packages/taler-wallet-core/src/remote.ts @@ -65,6 +65,7 @@ export async function createRemoteWallet( const ctx: RemoteWallet = { makeCoreApiRequest(operation, payload) { const id = `req-${nextRequestId}`; + nextRequestId += 1; const req: CoreApiRequestEnvelope = { operation, id, diff --git a/packages/taler-wallet-core/src/util/coinSelection.ts b/packages/taler-wallet-core/src/util/coinSelection.ts index daba2ead5..ef2f85789 100644 --- a/packages/taler-wallet-core/src/util/coinSelection.ts +++ b/packages/taler-wallet-core/src/util/coinSelection.ts @@ -588,22 +588,22 @@ async function selectPayMerchantCandidates( for (const acc of exchangeDetails.wireInfo.accounts) { const pp = parsePaytoUri(acc.payto_uri); checkLogicInvariant(!!pp); - if (pp.targetType === req.wireMethod) { - // also check that wire method is supported now - const wireFeeStr = exchangeDetails.wireInfo.feesForType[ - req.wireMethod - ]?.find((x) => { - return AbsoluteTime.isBetween( - AbsoluteTime.now(), - AbsoluteTime.fromProtocolTimestamp(x.startStamp), - AbsoluteTime.fromProtocolTimestamp(x.endStamp), - ); - })?.wireFee; - if (wireFeeStr) { - wireMethodFee = wireFeeStr; - } - break; + if (pp.targetType !== req.wireMethod) { + continue; + } + const wireFeeStr = exchangeDetails.wireInfo.feesForType[ + req.wireMethod + ]?.find((x) => { + return AbsoluteTime.isBetween( + AbsoluteTime.now(), + AbsoluteTime.fromProtocolTimestamp(x.startStamp), + AbsoluteTime.fromProtocolTimestamp(x.endStamp), + ); + })?.wireFee; + if (wireFeeStr) { + wireMethodFee = wireFeeStr; } + break; } if (!wireMethodFee) { break; @@ -699,25 +699,17 @@ export function selectWithdrawalDenominations( let totalWithdrawCost = Amounts.zeroOfCurrency(amountAvailable.currency); denoms = denoms.filter((d) => isWithdrawableDenom(d, denomselAllowLate)); - denoms.sort((d1, d2) => - Amounts.cmp( - DenominationRecord.getValue(d2), - DenominationRecord.getValue(d1), - ), - ); + denoms.sort((d1, d2) => Amounts.cmp(d2.value, d1.value)); for (const d of denoms) { - const cost = Amounts.add( - DenominationRecord.getValue(d), - d.fees.feeWithdraw, - ).amount; + const cost = Amounts.add(d.value, d.fees.feeWithdraw).amount; const res = Amounts.divmod(remaining, cost); const count = res.quotient; remaining = Amounts.sub(remaining, Amounts.mult(cost, count).amount).amount; if (count > 0) { totalCoinValue = Amounts.add( totalCoinValue, - Amounts.mult(DenominationRecord.getValue(d), count).amount, + Amounts.mult(d.value, count).amount, ).amount; totalWithdrawCost = Amounts.add( totalWithdrawCost, @@ -766,30 +758,22 @@ export function selectForcedWithdrawalDenominations( let totalWithdrawCost = Amounts.zeroOfCurrency(amountAvailable.currency); denoms = denoms.filter((d) => isWithdrawableDenom(d, denomselAllowLate)); - denoms.sort((d1, d2) => - Amounts.cmp( - DenominationRecord.getValue(d2), - DenominationRecord.getValue(d1), - ), - ); + denoms.sort((d1, d2) => Amounts.cmp(d2.value, d1.value)); for (const fds of forcedDenomSel.denoms) { const count = fds.count; const denom = denoms.find((x) => { - return Amounts.cmp(DenominationRecord.getValue(x), fds.value) == 0; + return Amounts.cmp(x.value, fds.value) == 0; }); if (!denom) { throw Error( `unable to find denom for forced selection (value ${fds.value})`, ); } - const cost = Amounts.add( - DenominationRecord.getValue(denom), - denom.fees.feeWithdraw, - ).amount; + const cost = Amounts.add(denom.value, denom.fees.feeWithdraw).amount; totalCoinValue = Amounts.add( totalCoinValue, - Amounts.mult(DenominationRecord.getValue(denom), count).amount, + Amounts.mult(denom.value, count).amount, ).amount; totalWithdrawCost = Amounts.add( totalWithdrawCost, @@ -1002,7 +986,7 @@ export async function selectPeerCoins( x.coinAvailability, x.denominations, x.refreshGroups, - x.peerPushPaymentInitiations, + x.peerPushDebit, ]) .runReadWrite(async (tx) => { const exchanges = await tx.exchanges.iter().toArray(); diff --git a/packages/taler-wallet-core/src/util/instructedAmountConversion.ts b/packages/taler-wallet-core/src/util/instructedAmountConversion.ts index bd02e7b22..54c08eee4 100644 --- a/packages/taler-wallet-core/src/util/instructedAmountConversion.ts +++ b/packages/taler-wallet-core/src/util/instructedAmountConversion.ts @@ -321,7 +321,7 @@ function buildCoinInfoFromDenom( AbsoluteTime.fromProtocolTimestamp(denom.stampExpireDeposit), ), totalAvailable: total, - value: DenominationRecord.getValue(denom), + value: Amounts.parseOrThrow(denom.value), maxAge, }; } diff --git a/packages/taler-wallet-core/src/util/query.ts b/packages/taler-wallet-core/src/util/query.ts index 1c3ff6a2a..309c17a43 100644 --- a/packages/taler-wallet-core/src/util/query.ts +++ b/packages/taler-wallet-core/src/util/query.ts @@ -35,7 +35,7 @@ import { IDBKeyPath, IDBKeyRange, } from "@gnu-taler/idb-bridge"; -import { Logger, j2s } from "@gnu-taler/taler-util"; +import { Codec, Logger, j2s } from "@gnu-taler/taler-util"; const logger = new Logger("query.ts"); @@ -387,11 +387,11 @@ export interface StoreReadWriteAccessor<RecordType, IndexMap> { export interface StoreWithIndexes< StoreName extends string, - SD extends StoreDescriptor<unknown>, + RecordType, IndexMap, > { storeName: StoreName; - store: SD; + store: StoreDescriptor<RecordType>; indexMap: IndexMap; /** @@ -401,19 +401,13 @@ export interface StoreWithIndexes< mark: Symbol; } -export type GetRecordType<T> = T extends StoreDescriptor<infer X> ? X : unknown; - const storeWithIndexesSymbol = Symbol("StoreWithIndexesMark"); -export function describeStore< - StoreName extends string, - SD extends StoreDescriptor<unknown>, - IndexMap, ->( +export function describeStore<StoreName extends string, RecordType, IndexMap>( name: StoreName, - s: SD, + s: StoreDescriptor<RecordType>, m: IndexMap, -): StoreWithIndexes<StoreName, SD, IndexMap> { +): StoreWithIndexes<StoreName, RecordType, IndexMap> { return { storeName: name, store: s, @@ -422,13 +416,72 @@ export function describeStore< }; } +export function describeStoreV2< + StoreName extends string, + RecordType, + IndexMap extends { [x: string]: IndexDescriptor } = {}, +>(args: { + storeName: StoreName; + recordCodec: Codec<RecordType>; + keyPath?: IDBKeyPath | IDBKeyPath[]; + autoIncrement?: boolean; + /** + * Database version that this store was added in, or + * undefined if added in the first version. + */ + versionAdded?: number; + indexes?: IndexMap; +}): StoreWithIndexes<StoreName, RecordType, IndexMap> { + return { + storeName: args.storeName, + store: { + _dummy: undefined as any, + autoIncrement: args.autoIncrement, + keyPath: args.keyPath, + versionAdded: args.versionAdded, + }, + indexMap: args.indexes ?? ({} as IndexMap), + mark: storeWithIndexesSymbol, + }; +} + +type KeyPathComponents = string | number; + +/** + * Follow a key path (dot-separated) in an object. + */ +type DerefKeyPath<T, P> = P extends `${infer PX extends keyof T & + KeyPathComponents}` + ? T[PX] + : P extends `${infer P0 extends keyof T & KeyPathComponents}.${infer Rest}` + ? DerefKeyPath<T[P0], Rest> + : unknown; + +/** + * Return a path if it is a valid dot-separate path to an object. + * Otherwise, return "never". + */ +type ValidateKeyPath<T, P> = P extends `${infer PX extends keyof T & + KeyPathComponents}` + ? PX + : P extends `${infer P0 extends keyof T & KeyPathComponents}.${infer Rest}` + ? `${P0}.${ValidateKeyPath<T[P0], Rest>}` + : never; + +// function foo<T, P>( +// x: T, +// p: P extends ValidateKeyPath<T, P> ? P : never, +// ): void {} + +// foo({x: [0,1,2]}, "x.0"); + export type GetReadOnlyAccess<BoundStores> = { [P in keyof BoundStores]: BoundStores[P] extends StoreWithIndexes< - infer SN, - infer SD, - infer IM + infer StoreName, + infer RecordType, + infer IndexMap > - ? StoreReadOnlyAccessor<GetRecordType<SD>, IM> + ? StoreReadOnlyAccessor<RecordType, IndexMap> : unknown; }; @@ -446,11 +499,11 @@ export type DbReadOnlyTransaction< } ? { [P in Stores]: StoreMap[P] extends StoreWithIndexes< - infer SN, - infer SD, - infer IM + infer StoreName, + infer RecordType, + infer IndexMap > - ? StoreReadOnlyAccessor<GetRecordType<SD>, IM> + ? StoreReadOnlyAccessor<RecordType, IndexMap> : unknown; } : unknown; @@ -463,22 +516,22 @@ export type DbReadWriteTransaction< } ? { [P in Stores]: StoreMap[P] extends StoreWithIndexes< - infer SN, - infer SD, - infer IM + infer StoreName, + infer RecordType, + infer IndexMap > - ? StoreReadWriteAccessor<GetRecordType<SD>, IM> + ? StoreReadWriteAccessor<RecordType, IndexMap> : unknown; } : unknown; export type GetReadWriteAccess<BoundStores> = { [P in keyof BoundStores]: BoundStores[P] extends StoreWithIndexes< - infer SN, - infer SD, - infer IM + infer StoreName, + infer RecordType, + infer IndexMap > - ? StoreReadWriteAccessor<GetRecordType<SD>, IM> + ? StoreReadWriteAccessor<RecordType, IndexMap> : unknown; }; diff --git a/packages/taler-wallet-core/src/wallet-api-types.ts b/packages/taler-wallet-core/src/wallet-api-types.ts index 4d9d40c43..d89ad257a 100644 --- a/packages/taler-wallet-core/src/wallet-api-types.ts +++ b/packages/taler-wallet-core/src/wallet-api-types.ts @@ -119,8 +119,9 @@ import { CreateStoredBackupResponse, RecoverStoredBackupRequest, DeleteStoredBackupRequest, + TestingSetTimetravelRequest, } from "@gnu-taler/taler-util"; -import { AuditorTrustRecord, WalletContractData } from "./db.js"; +import { WalletContractData } from "./db.js"; import { AddBackupProviderRequest, AddBackupProviderResponse, @@ -215,6 +216,7 @@ export enum WalletApiOperation { ValidateIban = "validateIban", TestingWaitTransactionsFinal = "testingWaitTransactionsFinal", TestingWaitRefreshesFinal = "testingWaitRefreshesFinal", + TestingSetTimetravel = "testingSetTimetravel", GetScopedCurrencyInfo = "getScopedCurrencyInfo", ListStoredBackups = "listStoredBackups", CreateStoredBackup = "createStoredBackup", @@ -258,7 +260,6 @@ export interface WalletConfig { */ builtin: { exchanges: string[]; - auditors: AuditorTrustRecord[]; }; /** @@ -990,6 +991,15 @@ export type DumpCoinsOp = { }; /** + * Add an offset to the wallet's internal time. + */ +export type TestingSetTimetravelOp = { + op: WalletApiOperation.TestingSetTimetravel; + request: TestingSetTimetravelRequest; + response: EmptyObject; +}; + +/** * Wait until all transactions are in a final state. */ export type TestingWaitTransactionsFinal = { @@ -1111,6 +1121,7 @@ export type WalletOperations = { [WalletApiOperation.ValidateIban]: ValidateIbanOp; [WalletApiOperation.TestingWaitTransactionsFinal]: TestingWaitTransactionsFinal; [WalletApiOperation.TestingWaitRefreshesFinal]: TestingWaitRefreshesFinal; + [WalletApiOperation.TestingSetTimetravel]: TestingSetTimetravelOp; [WalletApiOperation.GetScopedCurrencyInfo]: GetScopedCurrencyInfoOp; [WalletApiOperation.CreateStoredBackup]: CreateStoredBackupsOp; [WalletApiOperation.ListStoredBackups]: ListStoredBackupsOp; diff --git a/packages/taler-wallet-core/src/wallet.ts b/packages/taler-wallet-core/src/wallet.ts index 626409dd6..1a60b148c 100644 --- a/packages/taler-wallet-core/src/wallet.ts +++ b/packages/taler-wallet-core/src/wallet.ts @@ -34,7 +34,6 @@ import { Duration, ExchangeDetailedResponse, ExchangeListItem, - ExchangeTosStatusDetails, ExchangesListResponse, FeeDescription, GetExchangeTosResult, @@ -121,11 +120,16 @@ import { GetCurrencyInfoResponse, codecForGetCurrencyInfoRequest, CreateStoredBackupResponse, + StoredBackupList, + codecForDeleteStoredBackupRequest, + DeleteStoredBackupRequest, + RecoverStoredBackupRequest, + codecForRecoverStoredBackupRequest, + codecForTestingSetTimetravelRequest, + setDangerousTimetravel, } from "@gnu-taler/taler-util"; -import { - HttpRequestLibrary, - readSuccessResponseJsonOrThrow, -} from "@gnu-taler/taler-util/http"; +import type { HttpRequestLibrary } from "@gnu-taler/taler-util/http"; +import { readSuccessResponseJsonOrThrow } from "@gnu-taler/taler-util/http"; import { TalerCryptoInterface } from "./crypto/cryptoImplementation.js"; import { CryptoDispatcher, @@ -194,11 +198,9 @@ import { downloadTosFromAcceptedFormat, getExchangeDetails, getExchangeRequestTimeout, - getExchangeTrust, provideExchangeRecordInTx, updateExchangeFromUrl, updateExchangeFromUrlHandler, - updateExchangeTermsOfService, } from "./operations/exchanges.js"; import { getMerchantInfo } from "./operations/merchants.js"; import { @@ -352,9 +354,9 @@ async function callOperationHandler( case PendingTaskType.PeerPullCredit: return await processPeerPullCredit(ws, pending.pursePub); case PendingTaskType.PeerPullDebit: - return await processPeerPullDebit(ws, pending.peerPullPaymentIncomingId); + return await processPeerPullDebit(ws, pending.peerPullDebitId); case PendingTaskType.PeerPushCredit: - return await processPeerPushCredit(ws, pending.peerPushPaymentIncomingId); + return await processPeerPushCredit(ws, pending.peerPushCreditId); default: return assertUnreachable(pending); } @@ -522,7 +524,7 @@ async function runTaskLoop( */ async function fillDefaults(ws: InternalWalletState): Promise<void> { await ws.db - .mktx((x) => [x.config, x.auditorTrust, x.exchanges, x.exchangeDetails]) + .mktx((x) => [x.config, x.exchanges, x.exchangeDetails]) .runReadWrite(async (tx) => { const appliedRec = await tx.config.get("currencyDefaultsApplied"); let alreadyApplied = appliedRec ? !!appliedRec.value : false; @@ -530,10 +532,6 @@ async function fillDefaults(ws: InternalWalletState): Promise<void> { logger.trace("defaults already applied"); return; } - logger.info("importing default exchanges and auditors"); - for (const c of ws.config.builtin.auditors) { - await tx.auditorTrust.put(c); - } for (const baseUrl of ws.config.builtin.exchanges) { await addPresetExchangeEntry(tx, baseUrl); const now = AbsoluteTime.now(); @@ -557,33 +555,6 @@ async function getExchangeTos( ): Promise<GetExchangeTosResult> { // FIXME: download ToS in acceptable format if passed! const { exchangeDetails } = await updateExchangeFromUrl(ws, exchangeBaseUrl); - const tosDetails = await ws.db - .mktx((x) => [x.exchangeTos]) - .runReadOnly(async (tx) => { - return await getExchangeTosStatusDetails(tx, exchangeDetails); - }); - const content = tosDetails.content; - const currentEtag = tosDetails.currentVersion; - const contentType = tosDetails.contentType; - if ( - content === undefined || - currentEtag === undefined || - contentType === undefined - ) { - throw Error("exchange is in invalid state"); - } - if ( - acceptedFormat && - acceptedFormat.findIndex((f) => f === contentType) !== -1 - ) { - return { - acceptedEtag: exchangeDetails.tosAccepted?.etag, - currentEtag, - content, - contentType, - tosStatus: getExchangeTosStatus(exchangeDetails), - }; - } const tosDownload = await downloadTosFromAcceptedFormat( ws, @@ -592,17 +563,15 @@ async function getExchangeTos( acceptedFormat, ); - if (tosDownload.tosContentType === contentType) { - return { - acceptedEtag: exchangeDetails.tosAccepted?.etag, - currentEtag, - content, - contentType, - tosStatus: getExchangeTosStatus(exchangeDetails), - }; - } - - await updateExchangeTermsOfService(ws, exchangeBaseUrl, tosDownload); + await ws.db + .mktx((x) => [x.exchanges, x.exchangeDetails]) + .runReadWrite(async (tx) => { + const d = await getExchangeDetails(tx, exchangeBaseUrl); + if (d) { + d.tosCurrentEtag = tosDownload.tosEtag; + await tx.exchangeDetails.put(d); + } + }); return { acceptedEtag: exchangeDetails.tosAccepted?.etag, @@ -683,32 +652,6 @@ async function forgetKnownBankAccounts( return; } -async function getExchangeTosStatusDetails( - tx: GetReadOnlyAccess<{ exchangeTos: typeof WalletStoresV1.exchangeTos }>, - exchangeDetails: ExchangeDetailsRecord, -): Promise<ExchangeTosStatusDetails> { - let exchangeTos = await tx.exchangeTos.get([ - exchangeDetails.exchangeBaseUrl, - exchangeDetails.tosCurrentEtag, - ]); - - if (!exchangeTos) { - exchangeTos = { - etag: "not-available", - termsOfServiceContentType: "text/plain", - termsOfServiceText: "terms of service unavailable", - exchangeBaseUrl: exchangeDetails.exchangeBaseUrl, - }; - } - - return { - acceptedVersion: exchangeDetails.tosAccepted?.etag, - content: exchangeTos.termsOfServiceText, - contentType: exchangeTos.termsOfServiceContentType, - currentVersion: exchangeTos.etag, - }; -} - async function getExchanges( ws: InternalWalletState, ): Promise<ExchangesListResponse> { @@ -717,7 +660,6 @@ async function getExchanges( .mktx((x) => [ x.exchanges, x.exchangeDetails, - x.exchangeTos, x.denominations, x.operationRetries, ]) @@ -742,12 +684,7 @@ async function getExchangeDetailedInfo( ): Promise<ExchangeDetailedResponse> { //TODO: should we use the forceUpdate parameter? const exchange = await ws.db - .mktx((x) => [ - x.exchanges, - x.exchangeTos, - x.exchangeDetails, - x.denominations, - ]) + .mktx((x) => [x.exchanges, x.exchangeDetails, x.denominations]) .runReadOnly(async (tx) => { const ex = await tx.exchanges.get(exchangeBaseurl); const dp = ex?.detailsPointer; @@ -769,8 +706,6 @@ async function getExchangeDetailedInfo( return; } - const tos = await getExchangeTosStatusDetails(tx, exchangeDetails); - const denominations: DenominationInfo[] = denominationRecords.map((x) => DenominationRecord.toDenomInfo(x), ); @@ -779,7 +714,6 @@ async function getExchangeDetailedInfo( info: { exchangeBaseUrl: ex.baseUrl, currency, - tos, paytoUris: exchangeDetails.wireInfo.accounts.map((x) => x.payto_uri), auditors: exchangeDetails.auditors, wireInfo: exchangeDetails.wireInfo, @@ -981,11 +915,7 @@ async function dumpCoins(ws: InternalWalletState): Promise<CoinDumpJson> { coin_pub: c.coinPub, denom_pub: denomInfo.denomPub, denom_pub_hash: c.denomPubHash, - denom_value: Amounts.stringify({ - value: denom.amountVal, - currency: denom.currency, - fraction: denom.amountFrac, - }), + denom_value: denom.value, exchange_base_url: c.exchangeBaseUrl, refresh_parent_coin_pub: refreshParentCoinPub, withdrawal_reserve_pub: withdrawalReservePub, @@ -1041,6 +971,57 @@ async function createStoredBackup( }; } +async function listStoredBackups( + ws: InternalWalletState, +): Promise<StoredBackupList> { + const storedBackups: StoredBackupList = { + storedBackups: [], + }; + const backupsDb = await openStoredBackupsDatabase(ws.idb); + await backupsDb.mktxAll().runReadWrite(async (tx) => { + await tx.backupMeta.iter().forEach((x) => { + storedBackups.storedBackups.push({ + name: x.name, + }); + }); + }); + return storedBackups; +} + +async function deleteStoredBackup( + ws: InternalWalletState, + req: DeleteStoredBackupRequest, +): Promise<void> { + const backupsDb = await openStoredBackupsDatabase(ws.idb); + await backupsDb.mktxAll().runReadWrite(async (tx) => { + await tx.backupData.delete(req.name); + await tx.backupMeta.delete(req.name); + }); +} + +async function recoverStoredBackup( + ws: InternalWalletState, + req: RecoverStoredBackupRequest, +): Promise<void> { + logger.info(`Recovering stored backup ${req.name}`); + const { name } = req; + const backupsDb = await openStoredBackupsDatabase(ws.idb); + const bd = await backupsDb.mktxAll().runReadWrite(async (tx) => { + const backupMeta = tx.backupMeta.get(name); + if (!backupMeta) { + throw Error("backup not found"); + } + const backupData = await tx.backupData.get(name); + if (!backupData) { + throw Error("no backup data (DB corrupt)"); + } + return backupData; + }); + logger.info(`backup found, now importing`); + await importDb(ws.db.idbHandle(), bd); + logger.info(`import done`); +} + /** * Implementation of the "wallet-core" API. */ @@ -1059,12 +1040,18 @@ async function dispatchRequestInternal<Op extends WalletApiOperation>( switch (operation) { case WalletApiOperation.CreateStoredBackup: return createStoredBackup(ws); - case WalletApiOperation.DeleteStoredBackup: + case WalletApiOperation.DeleteStoredBackup: { + const req = codecForDeleteStoredBackupRequest().decode(payload); + await deleteStoredBackup(ws, req); return {}; + } case WalletApiOperation.ListStoredBackups: + return listStoredBackups(ws); + case WalletApiOperation.RecoverStoredBackup: { + const req = codecForRecoverStoredBackupRequest().decode(payload); + await recoverStoredBackup(ws, req); return {}; - case WalletApiOperation.RecoverStoredBackup: - return {}; + } case WalletApiOperation.InitWallet: { logger.trace("initializing wallet"); ws.initCalled = true; @@ -1496,24 +1483,11 @@ async function dispatchRequestInternal<Op extends WalletApiOperation>( return {}; } case WalletApiOperation.ListCurrencies: { - return await ws.db - .mktx((x) => [x.auditorTrust, x.exchangeTrust]) - .runReadOnly(async (tx) => { - const trustedAuditors = await tx.auditorTrust.iter().toArray(); - const trustedExchanges = await tx.exchangeTrust.iter().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, - })), - }; - }); + // FIXME: Remove / change to scoped currency approach. + return { + trustedAuditors: [], + trustedExchanges: [], + }; } case WalletApiOperation.WithdrawFakebank: { const req = codecForWithdrawFakebankRequest().decode(payload); @@ -1612,6 +1586,12 @@ async function dispatchRequestInternal<Op extends WalletApiOperation>( return await waitUntilDone(ws); case WalletApiOperation.TestingWaitRefreshesFinal: return await waitUntilRefreshesDone(ws); + case WalletApiOperation.TestingSetTimetravel: { + const req = codecForTestingSetTimetravelRequest().decode(payload); + setDangerousTimetravel(req.offsetMs); + ws.workAvailable.trigger(); + return {}; + } // default: // assertUnreachable(operation); } @@ -1712,14 +1692,6 @@ export class Wallet { public static defaultConfig: Readonly<WalletConfig> = { builtin: { exchanges: ["https://exchange.demo.taler.net/"], - auditors: [ - { - currency: "KUDOS", - auditorPub: "BW9DC48PHQY4NH011SHHX36DZZ3Q22Y6X7FZ1VD1CMZ2PTFZ6PN0", - auditorBaseUrl: "https://auditor.demo.taler.net/", - uids: ["5P25XF8TVQP9AW6VYGY2KV47WT5Y3ZXFSJAA570GJPX5SVJXKBVG"], - }, - ], }, features: { allowHttp: false, @@ -1792,7 +1764,6 @@ class InternalWalletStateImpl implements InternalWalletState { exchangeOps: ExchangeOperations = { getExchangeDetails, - getExchangeTrust, updateExchangeFromUrl, }; @@ -1901,35 +1872,27 @@ class InternalWalletStateImpl implements InternalWalletState { return computeRefundTransactionState(rec); } case TransactionType.PeerPullCredit: - const rec = await tx.peerPullPaymentInitiations.get( - parsedTxId.pursePub, - ); + const rec = await tx.peerPullCredit.get(parsedTxId.pursePub); if (!rec) { return undefined; } return computePeerPullCreditTransactionState(rec); case TransactionType.PeerPullDebit: { - const rec = await tx.peerPullPaymentIncoming.get( - parsedTxId.peerPullPaymentIncomingId, - ); + const rec = await tx.peerPullDebit.get(parsedTxId.peerPullDebitId); if (!rec) { return undefined; } return computePeerPullDebitTransactionState(rec); } case TransactionType.PeerPushCredit: { - const rec = await tx.peerPushPaymentIncoming.get( - parsedTxId.peerPushPaymentIncomingId, - ); + const rec = await tx.peerPushCredit.get(parsedTxId.peerPushCreditId); if (!rec) { return undefined; } return computePeerPushCreditTransactionState(rec); } case TransactionType.PeerPushDebit: { - const rec = await tx.peerPushPaymentInitiations.get( - parsedTxId.pursePub, - ); + const rec = await tx.peerPushDebit.get(parsedTxId.pursePub); if (!rec) { return undefined; } |