From 2c52046f0bf358a5e07c53394b3b72d091356cce Mon Sep 17 00:00:00 2001 From: Florian Dold Date: Thu, 12 Mar 2020 00:44:28 +0530 Subject: full recoup, untested/unfinished first attempt --- src/types/dbTypes.ts | 123 +++++++++++++++++++++++++++++++++------------ src/types/history.ts | 14 +----- src/types/notifications.ts | 9 ++-- src/types/pending.ts | 6 +++ src/types/talerTypes.ts | 58 ++++++++++++++------- src/types/walletTypes.ts | 6 ++- 6 files changed, 145 insertions(+), 71 deletions(-) (limited to 'src/types') diff --git a/src/types/dbTypes.ts b/src/types/dbTypes.ts index c1d049179..56c1f82eb 100644 --- a/src/types/dbTypes.ts +++ b/src/types/dbTypes.ts @@ -33,10 +33,7 @@ import { } from "./talerTypes"; import { Index, Store } from "../util/query"; -import { - OperationError, - RefreshReason, -} from "./walletTypes"; +import { OperationError, RefreshReason } from "./walletTypes"; import { ReserveTransaction } from "./ReserveTransaction"; import { Timestamp, Duration, getTimestampNow } from "../util/time"; @@ -133,7 +130,6 @@ export function initRetryInfo( return info; } - /** * A reserve record as stored in the wallet's database. */ @@ -196,12 +192,6 @@ export interface ReserveRecord { */ amountInitiallyRequested: AmountJson; - /** - * We got some payback to this reserve. We'll cease to automatically - * withdraw money from it. - */ - hasPayback: boolean; - /** * Wire information (as payto URI) for the bank account that * transfered funds for this reserve. @@ -386,6 +376,8 @@ export interface DenominationRecord { /** * Did we verify the signature on the denomination? + * + * FIXME: Rename to "verificationStatus"? */ status: DenominationStatus; @@ -396,6 +388,13 @@ export interface DenominationRecord { */ isOffered: boolean; + /** + * Did the exchange revoke the denomination? + * When this field is set to true in the database, the same transaction + * should also mark all affected coins as revoked. + */ + isRevoked: boolean; + /** * Base URL of the exchange. */ @@ -577,7 +576,7 @@ export interface RefreshPlanchetRecord { /** * Status of a coin. */ -export enum CoinStatus { +export const enum CoinStatus { /** * Withdrawn and never shown to anybody. */ @@ -588,26 +587,47 @@ export enum CoinStatus { Dormant = "dormant", } -export enum CoinSource { +export const enum CoinSourceType { Withdraw = "withdraw", Refresh = "refresh", Tip = "tip", } +export interface WithdrawCoinSource { + type: CoinSourceType.Withdraw; + withdrawSessionId: string; + + /** + * Index of the coin in the withdrawal session. + */ + coinIndex: number; + + /** + * Reserve public key for the reserve we got this coin from. + */ + reservePub: string; +} + +export interface RefreshCoinSource { + type: CoinSourceType.Refresh; + oldCoinPub: string; +} + +export interface TipCoinSource { + type: CoinSourceType.Tip; +} + +export type CoinSource = WithdrawCoinSource | RefreshCoinSource | TipCoinSource; + /** * CoinRecord as stored in the "coins" data store * of the wallet database. */ export interface CoinRecord { /** - * Withdraw session ID, or "" (empty string) if withdrawn via refresh. + * Where did the coin come from? Used for recouping coins. */ - withdrawSessionId: string; - - /** - * Index of the coin in the withdrawal session. - */ - coinIndex: number; + coinSource: CoinSource; /** * Public key of the coin. @@ -658,12 +678,6 @@ export interface CoinRecord { */ blindingKey: string; - /** - * Reserve public key for the reserve we got this coin from, - * or zero when we got the coin from refresh. - */ - reservePub: string | undefined; - /** * Status of the coin. */ @@ -992,10 +1006,10 @@ export interface WireFee { /** * Record to store information about a refund event. - * + * * All information about a refund is stored with the purchase, * this event is just for the history. - * + * * The event is only present for completed refunds. */ export interface RefundEventRecord { @@ -1285,6 +1299,11 @@ export type WithdrawalSource = WithdrawalSourceTip | WithdrawalSourceReserve; export interface WithdrawalSessionRecord { withdrawSessionId: string; + /** + * Withdrawal source. Fields that don't apply to the respective + * withdrawal source type must be null (i.e. can't be absent), + * otherwise the IndexedDB indexing won't like us. + */ source: WithdrawalSource; exchangeBaseUrl: string; @@ -1343,6 +1362,46 @@ export interface BankWithdrawUriRecord { reservePub: string; } +/** + * Status of recoup operations that were grouped together. + * + * The remaining amount of involved coins should be set to zero + * in the same transaction that inserts the RecoupGroupRecord. + */ +export interface RecoupGroupRecord { + /** + * Unique identifier for the recoup group record. + */ + recoupGroupId: string; + + timestampStarted: Timestamp; + + timestampFinished: Timestamp | undefined; + + /** + * Public keys that identify the coins being recouped + * as part of this session. + * + * (Structured like this to enable multiEntry indexing in IndexedDB.) + */ + coinPubs: string[]; + + /** + * Array of flags to indicate whether the recoup finished on each individual coin. + */ + recoupFinishedPerCoin: boolean[]; + + /** + * Retry info. + */ + retryInfo: RetryInfo; + + /** + * Last error that occured, if any. + */ + lastError: OperationError | undefined; +} + export const enum ImportPayloadType { CoreSchema = "core-schema", } @@ -1398,11 +1457,6 @@ export namespace Stores { "denomPubIndex", "denomPub", ); - byWithdrawalWithIdx = new Index( - this, - "planchetsByWithdrawalWithIdxIndex", - ["withdrawSessionId", "coinIndex"], - ); } class ProposalsStore extends Store { @@ -1540,6 +1594,9 @@ export namespace Stores { export const refreshGroups = new Store("refreshGroups", { keyPath: "refreshGroupId", }); + export const recoupGroups = new Store("recoupGroups", { + keyPath: "recoupGroupId", + }); export const reserves = new ReservesStore(); export const purchases = new PurchasesStore(); export const tips = new TipsStore(); diff --git a/src/types/history.ts b/src/types/history.ts index 30fe8e529..f4a1d0631 100644 --- a/src/types/history.ts +++ b/src/types/history.ts @@ -348,19 +348,7 @@ export interface HistoryFundsDepositedToSelfEvent { * converted funds in these denominations to new funds. */ export interface HistoryFundsRecoupedEvent { - type: HistoryEventType.FundsDepositedToSelf; - - exchangeBaseUrl: string; - - /** - * Amount that the wallet managed to recover. - */ - amountRecouped: string; - - /** - * Amount that was lost due to fees. - */ - amountLost: string; + type: HistoryEventType.FundsRecouped; } /** diff --git a/src/types/notifications.ts b/src/types/notifications.ts index 30ede151c..34e98fe2c 100644 --- a/src/types/notifications.ts +++ b/src/types/notifications.ts @@ -26,8 +26,8 @@ export const enum NotificationType { ProposalAccepted = "proposal-accepted", ProposalDownloaded = "proposal-downloaded", RefundsSubmitted = "refunds-submitted", - PaybackStarted = "payback-started", - PaybackFinished = "payback-finished", + RecoupStarted = "payback-started", + RecoupFinished = "payback-finished", RefreshRevealed = "refresh-revealed", RefreshMelted = "refresh-melted", RefreshStarted = "refresh-started", @@ -44,6 +44,7 @@ export const enum NotificationType { RefundFinished = "refund-finished", ExchangeOperationError = "exchange-operation-error", RefreshOperationError = "refresh-operation-error", + RecoupOperationError = "refresh-operation-error", RefundApplyOperationError = "refund-apply-error", RefundStatusOperationError = "refund-status-error", ProposalOperationError = "proposal-error", @@ -82,11 +83,11 @@ export interface RefundsSubmittedNotification { } export interface PaybackStartedNotification { - type: NotificationType.PaybackStarted; + type: NotificationType.RecoupStarted; } export interface PaybackFinishedNotification { - type: NotificationType.PaybackFinished; + type: NotificationType.RecoupFinished; } export interface RefreshMeltedNotification { diff --git a/src/types/pending.ts b/src/types/pending.ts index b86c7797b..5d732c520 100644 --- a/src/types/pending.ts +++ b/src/types/pending.ts @@ -58,6 +58,7 @@ export type PendingOperationInfo = PendingOperationInfoCommon & | PendingTipChoiceOperation | PendingTipPickupOperation | PendingWithdrawOperation + | PendingRecoupOperation ); /** @@ -200,6 +201,11 @@ export interface PendingRefundApplyOperation { numRefundsDone: number; } +export interface PendingRecoupOperation { + type: PendingOperationType.Recoup; + recoupGroupId: string; +} + /** * Status of an ongoing withdrawal operation. */ diff --git a/src/types/talerTypes.ts b/src/types/talerTypes.ts index 10ee83743..e65c82383 100644 --- a/src/types/talerTypes.ts +++ b/src/types/talerTypes.ts @@ -38,7 +38,12 @@ import { codecForBoolean, makeCodecForMap, } from "../util/codec"; -import { Timestamp, codecForTimestamp, Duration, codecForDuration } from "../util/time"; +import { + Timestamp, + codecForTimestamp, + Duration, + codecForDuration, +} from "../util/time"; /** * Denomination as found in the /keys response from the exchange. @@ -141,7 +146,7 @@ export class Auditor { /** * Request that we send to the exchange to get a payback. */ -export interface PaybackRequest { +export interface RecoupRequest { /** * Denomination public key of the coin we want to get * paid back. @@ -168,6 +173,11 @@ export interface PaybackRequest { * Signature made by the coin, authorizing the payback. */ coin_sig: string; + + /** + * Was the coin refreshed (and thus the recoup should go to the old coin)? + */ + refreshed: boolean; } /** @@ -175,9 +185,15 @@ export interface PaybackRequest { */ export class RecoupConfirmation { /** - * public key of the reserve that will receive the payback. + * Public key of the reserve that will receive the payback. */ - reserve_pub: string; + reserve_pub?: string; + + /** + * Public key of the old coin that will receive the recoup, + * provided if refreshed was true. + */ + old_coin_pub?: string; /** * How much will the exchange pay back (needed by wallet in @@ -575,7 +591,7 @@ export class TipResponse { * Element of the payback list that the * exchange gives us in /keys. */ -export class Payback { +export class Recoup { /** * The hash of the denomination public key for which the payback is offered. */ @@ -607,9 +623,9 @@ export class ExchangeKeysJson { list_issue_date: Timestamp; /** - * List of paybacks for compromised denominations. + * List of revoked denominations. */ - payback?: Payback[]; + recoup?: Recoup[]; /** * Short-lived signing keys used to sign online @@ -764,7 +780,10 @@ export const codecForAuditor = () => makeCodecForObject() .property("auditor_pub", codecForString) .property("auditor_url", codecForString) - .property("denomination_keys", makeCodecForList(codecForAuditorDenomSig())) + .property( + "denomination_keys", + makeCodecForList(codecForAuditorDenomSig()), + ) .build("Auditor"), ); @@ -779,7 +798,7 @@ export const codecForExchangeHandle = () => export const codecForAuditorHandle = () => typecheckedCodec( makeCodecForObject() - .property("name", codecForString) + .property("name", codecForString) .property("master_pub", codecForString) .property("url", codecForString) .build("AuditorHandle"), @@ -851,9 +870,9 @@ export const codecForTipResponse = () => .build("TipResponse"), ); -export const codecForPayback = () => - typecheckedCodec( - makeCodecForObject() +export const codecForRecoup = () => + typecheckedCodec( + makeCodecForObject() .property("h_denom_pub", codecForString) .build("Payback"), ); @@ -865,13 +884,12 @@ export const codecForExchangeKeysJson = () => .property("master_public_key", codecForString) .property("auditors", makeCodecForList(codecForAuditor())) .property("list_issue_date", codecForTimestamp) - .property("payback", makeCodecOptional(makeCodecForList(codecForPayback()))) + .property("recoup", makeCodecOptional(makeCodecForList(codecForRecoup()))) .property("signkeys", codecForAny) .property("version", codecForString) .build("KeysJson"), ); - export const codecForWireFeesJson = () => typecheckedCodec( makeCodecForObject() @@ -895,7 +913,10 @@ export const codecForExchangeWireJson = () => typecheckedCodec( makeCodecForObject() .property("accounts", makeCodecForList(codecForAccountInfo())) - .property("fees", makeCodecForMap(makeCodecForList(codecForWireFeesJson()))) + .property( + "fees", + makeCodecForMap(makeCodecForList(codecForWireFeesJson())), + ) .build("ExchangeWireJson"), ); @@ -919,13 +940,12 @@ export const codecForCheckPaymentResponse = () => .build("CheckPaymentResponse"), ); - export const codecForWithdrawOperationStatusResponse = () => typecheckedCodec( makeCodecForObject() .property("selection_done", codecForBoolean) .property("transfer_done", codecForBoolean) - .property("amount",codecForString) + .property("amount", codecForString) .property("sender_wire", makeCodecOptional(codecForString)) .property("suggested_exchange", makeCodecOptional(codecForString)) .property("confirm_transfer_url", makeCodecOptional(codecForString)) @@ -945,11 +965,11 @@ export const codecForTipPickupGetResponse = () => .build("TipPickupGetResponse"), ); - export const codecForRecoupConfirmation = () => typecheckedCodec( makeCodecForObject() - .property("reserve_pub", codecForString) + .property("reserve_pub", makeCodecOptional(codecForString)) + .property("old_coin_pub", makeCodecOptional(codecForString)) .property("amount", codecForString) .property("timestamp", codecForTimestamp) .property("exchange_sig", codecForString) diff --git a/src/types/walletTypes.ts b/src/types/walletTypes.ts index 9887474c3..c6473a9b7 100644 --- a/src/types/walletTypes.ts +++ b/src/types/walletTypes.ts @@ -1,6 +1,6 @@ /* - This file is part of TALER - (C) 2015-2017 GNUnet e.V. and INRIA + This file is part of GNU Taler + (C) 2015-2020 Taler Systems SA 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 @@ -20,6 +20,8 @@ * These types are defined in a separate file make tree shaking easier, since * some components use these types (via RPC) but do not depend on the wallet * code directly. + * + * @author Florian Dold */ /** -- cgit v1.2.3