aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFlorian Dold <florian@dold.me>2022-09-21 20:46:45 +0200
committerFlorian Dold <florian@dold.me>2022-09-21 22:50:42 +0200
commit7d6bcd42ea9efced6200cf94924aa38bed2dbb02 (patch)
tree54b2077512c7d098e558193392a7ac92a6d74d63
parent5d31803c92ac085d50ab0942a6cf657a6cd9cc4b (diff)
downloadwallet-core-7d6bcd42ea9efced6200cf94924aa38bed2dbb02.tar.xz
wallet-core: use numeric status field to allow range queries
-rw-r--r--packages/taler-wallet-core/src/db.ts33
-rw-r--r--packages/taler-wallet-core/src/operations/backup/export.ts4
-rw-r--r--packages/taler-wallet-core/src/operations/backup/import.ts11
-rw-r--r--packages/taler-wallet-core/src/operations/peer-to-peer.ts6
-rw-r--r--packages/taler-wallet-core/src/operations/pending.ts9
-rw-r--r--packages/taler-wallet-core/src/operations/recoup.ts4
-rw-r--r--packages/taler-wallet-core/src/operations/transactions.ts2
-rw-r--r--packages/taler-wallet-core/src/operations/withdraw.ts89
-rw-r--r--packages/taler-wallet-core/src/util/invariants.ts7
-rw-r--r--packages/taler-wallet-core/src/util/query.ts2
10 files changed, 86 insertions, 81 deletions
diff --git a/packages/taler-wallet-core/src/db.ts b/packages/taler-wallet-core/src/db.ts
index 078060297..8ed4fe85e 100644
--- a/packages/taler-wallet-core/src/db.ts
+++ b/packages/taler-wallet-core/src/db.ts
@@ -62,6 +62,8 @@ import { Event, IDBDatabase } from "@gnu-taler/idb-bridge";
* will have an index.
* - Amounts are stored as strings, except when they are needed for
* indexing.
+ * - Every record that has a corresponding transaction item must have
+ * an index for a mandatory timestamp field.
* - Optional fields should be avoided, use "T | undefined" instead.
*
* @author Florian Dold <dold@taler.net>
@@ -94,38 +96,45 @@ export const CURRENT_DB_CONFIG_KEY = "currentMainDbName";
*/
export const WALLET_DB_MINOR_VERSION = 1;
+export namespace OperationStatusRange {
+ export const ACTIVE_START = 10;
+ export const ACTIVE_END = 29;
+ export const DORMANT_START = 40;
+ export const DORMANT_END = 59;
+}
+
/**
* Status of a withdrawal.
*/
-export enum ReserveRecordStatus {
+export enum WithdrawalGroupStatus {
/**
* Reserve must be registered with the bank.
*/
- RegisteringBank = "registering-bank",
+ RegisteringBank = OperationStatusRange.ACTIVE_START,
/**
* 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).
*/
- WaitConfirmBank = "wait-confirm-bank",
+ WaitConfirmBank = OperationStatusRange.ACTIVE_START + 1,
/**
* Querying reserve status with the exchange.
*/
- QueryingStatus = "querying-status",
+ QueryingStatus = OperationStatusRange.ACTIVE_START + 2,
/**
* The corresponding withdraw record has been created.
* No further processing is done, unless explicitly requested
* by the user.
*/
- Dormant = "dormant",
+ Finished = OperationStatusRange.DORMANT_START,
/**
* The bank aborted the withdrawal.
*/
- BankAborted = "bank-aborted",
+ BankAborted = OperationStatusRange.DORMANT_START + 1,
}
/**
@@ -1355,19 +1364,11 @@ export interface WithdrawalGroupRecord {
timestampFinish?: TalerProtocolTimestamp;
/**
- * Operation status of the withdrawal group.
- * Used for indexing in the database.
- *
- * FIXME: Redundant with reserveStatus
- */
- operationStatus: OperationStatus;
-
- /**
* Current status of the reserve.
*
* FIXME: Wrong name!
*/
- reserveStatus: ReserveRecordStatus;
+ status: WithdrawalGroupStatus;
/**
* Amount that was sent by the user to fund the reserve.
@@ -1947,7 +1948,7 @@ export const WalletStoresV1 = {
}),
{
byReservePub: describeIndex("byReservePub", "reservePub"),
- byStatus: describeIndex("byStatus", "operationStatus"),
+ byStatus: describeIndex("byStatus", "status"),
byTalerWithdrawUri: describeIndex(
"byTalerWithdrawUri",
"wgInfo.bankInfo.talerWithdrawUri",
diff --git a/packages/taler-wallet-core/src/operations/backup/export.ts b/packages/taler-wallet-core/src/operations/backup/export.ts
index b39e6dc27..c8454a62f 100644
--- a/packages/taler-wallet-core/src/operations/backup/export.ts
+++ b/packages/taler-wallet-core/src/operations/backup/export.ts
@@ -71,6 +71,7 @@ import {
RefreshCoinStatus,
RefundState,
WALLET_BACKUP_STATE_KEY,
+ WithdrawalGroupStatus,
WithdrawalRecordType,
} from "../../db.js";
import { InternalWalletState } from "../../internal-wallet-state.js";
@@ -167,8 +168,9 @@ export async function exportBackup(
instructed_amount: Amounts.stringify(wg.instructedAmount),
reserve_priv: wg.reservePriv,
restrict_age: wg.restrictAge,
+ // FIXME: proper status conversion!
operation_status:
- wg.operationStatus == OperationStatus.Finished
+ wg.status == WithdrawalGroupStatus.Finished
? BackupOperationStatus.Finished
: BackupOperationStatus.Pending,
selected_denoms_uid: wg.denomSelUid,
diff --git a/packages/taler-wallet-core/src/operations/backup/import.ts b/packages/taler-wallet-core/src/operations/backup/import.ts
index 20c7316c1..3a92273df 100644
--- a/packages/taler-wallet-core/src/operations/backup/import.ts
+++ b/packages/taler-wallet-core/src/operations/backup/import.ts
@@ -52,7 +52,7 @@ import {
RefreshSessionRecord,
RefundState,
ReserveBankInfo,
- ReserveRecordStatus,
+ WithdrawalGroupStatus,
WalletContractData,
WalletRefundItem,
WalletStoresV1,
@@ -531,9 +531,6 @@ export async function importBackup(
exchangeBaseUrl: backupWg.exchange_base_url,
instructedAmount: Amounts.parseOrThrow(backupWg.instructed_amount),
secretSeed: backupWg.secret_seed,
- operationStatus: backupWg.timestamp_finish
- ? OperationStatus.Finished
- : OperationStatus.Pending,
denomsSel: await getDenomSelStateFromBackup(
tx,
backupWg.exchange_base_url,
@@ -545,9 +542,9 @@ export async function importBackup(
),
reservePriv: backupWg.reserve_priv,
reservePub,
- reserveStatus: backupWg.timestamp_finish
- ? ReserveRecordStatus.Dormant
- : ReserveRecordStatus.QueryingStatus, // FIXME!
+ status: backupWg.timestamp_finish
+ ? WithdrawalGroupStatus.Finished
+ : WithdrawalGroupStatus.QueryingStatus, // FIXME!
timestampStart: backupWg.timestamp_created,
wgInfo,
restrictAge: backupWg.restrict_age,
diff --git a/packages/taler-wallet-core/src/operations/peer-to-peer.ts b/packages/taler-wallet-core/src/operations/peer-to-peer.ts
index 48d422e0b..d30cb294d 100644
--- a/packages/taler-wallet-core/src/operations/peer-to-peer.ts
+++ b/packages/taler-wallet-core/src/operations/peer-to-peer.ts
@@ -65,7 +65,7 @@ import {
import {
CoinStatus,
MergeReserveInfo,
- ReserveRecordStatus,
+ WithdrawalGroupStatus,
WalletStoresV1,
WithdrawalRecordType,
} from "../db.js";
@@ -544,7 +544,7 @@ export async function acceptPeerPushPayment(
contractTerms: peerInc.contractTerms,
},
exchangeBaseUrl: peerInc.exchangeBaseUrl,
- reserveStatus: ReserveRecordStatus.QueryingStatus,
+ reserveStatus: WithdrawalGroupStatus.QueryingStatus,
reserveKeyPair: {
priv: mergeReserveInfo.reservePriv,
pub: mergeReserveInfo.reservePub,
@@ -828,7 +828,7 @@ export async function initiatePeerRequestForPay(
contractPriv: econtractResp.contractPriv,
},
exchangeBaseUrl: req.exchangeBaseUrl,
- reserveStatus: ReserveRecordStatus.QueryingStatus,
+ reserveStatus: WithdrawalGroupStatus.QueryingStatus,
reserveKeyPair: {
priv: mergeReserveInfo.reservePriv,
pub: mergeReserveInfo.reservePub,
diff --git a/packages/taler-wallet-core/src/operations/pending.ts b/packages/taler-wallet-core/src/operations/pending.ts
index 9ba532ab7..18e8ec83b 100644
--- a/packages/taler-wallet-core/src/operations/pending.ts
+++ b/packages/taler-wallet-core/src/operations/pending.ts
@@ -28,6 +28,9 @@ import {
BackupProviderStateTag,
RefreshCoinStatus,
OperationStatus,
+ WithdrawalGroupRecord,
+ WithdrawalGroupStatus,
+ OperationStatusRange,
} from "../db.js";
import {
PendingOperationsResponse,
@@ -38,6 +41,7 @@ import { InternalWalletState } from "../internal-wallet-state.js";
import { GetReadOnlyAccess } from "../util/query.js";
import { RetryTags } from "../util/retries.js";
import { Wallet } from "../wallet.js";
+import { GlobalIDB } from "@gnu-taler/idb-bridge";
async function gatherExchangePending(
tx: GetReadOnlyAccess<{
@@ -120,7 +124,10 @@ async function gatherWithdrawalPending(
resp: PendingOperationsResponse,
): Promise<void> {
const wsrs = await tx.withdrawalGroups.indexes.byStatus.getAll(
- OperationStatus.Pending,
+ GlobalIDB.KeyRange.bound(
+ OperationStatusRange.ACTIVE_START,
+ OperationStatusRange.ACTIVE_END,
+ ),
);
for (const wsr of wsrs) {
if (wsr.timestampFinish) {
diff --git a/packages/taler-wallet-core/src/operations/recoup.ts b/packages/taler-wallet-core/src/operations/recoup.ts
index 119119035..6d899b947 100644
--- a/packages/taler-wallet-core/src/operations/recoup.ts
+++ b/packages/taler-wallet-core/src/operations/recoup.ts
@@ -44,7 +44,7 @@ import {
CoinStatus,
RecoupGroupRecord,
RefreshCoinSource,
- ReserveRecordStatus,
+ WithdrawalGroupStatus,
WalletStoresV1,
WithdrawalRecordType,
WithdrawCoinSource,
@@ -382,7 +382,7 @@ export async function processRecoupGroupHandler(
await internalCreateWithdrawalGroup(ws, {
amount: Amounts.parseOrThrow(result.balance),
exchangeBaseUrl: recoupGroup.exchangeBaseUrl,
- reserveStatus: ReserveRecordStatus.QueryingStatus,
+ reserveStatus: WithdrawalGroupStatus.QueryingStatus,
reserveKeyPair: {
pub: reservePub,
priv: reservePrivMap[reservePub],
diff --git a/packages/taler-wallet-core/src/operations/transactions.ts b/packages/taler-wallet-core/src/operations/transactions.ts
index 19f6aee64..be1233d2c 100644
--- a/packages/taler-wallet-core/src/operations/transactions.ts
+++ b/packages/taler-wallet-core/src/operations/transactions.ts
@@ -31,11 +31,9 @@ import {
TalerProtocolTimestamp,
Transaction,
TransactionByIdRequest,
- TransactionRefund,
TransactionsRequest,
TransactionsResponse,
TransactionType,
- WithdrawalDetails,
WithdrawalType,
} from "@gnu-taler/taler-util";
import {
diff --git a/packages/taler-wallet-core/src/operations/withdraw.ts b/packages/taler-wallet-core/src/operations/withdraw.ts
index ad9875400..ce910363f 100644
--- a/packages/taler-wallet-core/src/operations/withdraw.ts
+++ b/packages/taler-wallet-core/src/operations/withdraw.ts
@@ -71,7 +71,7 @@ import {
ExchangeRecord,
OperationStatus,
PlanchetRecord,
- ReserveRecordStatus,
+ WithdrawalGroupStatus,
WalletStoresV1,
WgInfo,
WithdrawalGroupRecord,
@@ -91,7 +91,11 @@ import {
readSuccessResponseJsonOrThrow,
throwUnexpectedRequestError,
} from "../util/http.js";
-import { checkDbInvariant, checkLogicInvariant } from "../util/invariants.js";
+import {
+ checkDbInvariant,
+ checkLogicInvariant,
+ InvariantViolatedError,
+} from "../util/invariants.js";
import { DbAccess, GetReadOnlyAccess } from "../util/query.js";
import {
OperationAttemptResult,
@@ -962,7 +966,7 @@ async function queryReserve(
withdrawalGroupId,
});
checkDbInvariant(!!withdrawalGroup);
- if (withdrawalGroup.reserveStatus !== ReserveRecordStatus.QueryingStatus) {
+ if (withdrawalGroup.status !== WithdrawalGroupStatus.QueryingStatus) {
return { ready: true };
}
const reservePub = withdrawalGroup.reservePub;
@@ -1010,7 +1014,7 @@ async function queryReserve(
logger.warn(`withdrawal group ${withdrawalGroupId} not found`);
return;
}
- wg.reserveStatus = ReserveRecordStatus.Dormant;
+ wg.status = WithdrawalGroupStatus.Finished;
await tx.withdrawalGroups.put(wg);
});
@@ -1039,13 +1043,13 @@ export async function processWithdrawalGroup(
throw Error(`withdrawal group ${withdrawalGroupId} not found`);
}
- switch (withdrawalGroup.reserveStatus) {
- case ReserveRecordStatus.RegisteringBank:
+ switch (withdrawalGroup.status) {
+ case WithdrawalGroupStatus.RegisteringBank:
await processReserveBankStatus(ws, withdrawalGroupId);
return await processWithdrawalGroup(ws, withdrawalGroupId, {
forceNow: true,
});
- case ReserveRecordStatus.QueryingStatus: {
+ case WithdrawalGroupStatus.QueryingStatus: {
const res = await queryReserve(ws, withdrawalGroupId);
if (res.ready) {
return await processWithdrawalGroup(ws, withdrawalGroupId, {
@@ -1057,7 +1061,7 @@ export async function processWithdrawalGroup(
result: undefined,
};
}
- case ReserveRecordStatus.WaitConfirmBank: {
+ case WithdrawalGroupStatus.WaitConfirmBank: {
const res = await processReserveBankStatus(ws, withdrawalGroupId);
switch (res.status) {
case BankStatusResultCode.Aborted:
@@ -1075,23 +1079,20 @@ export async function processWithdrawalGroup(
}
break;
}
- case ReserveRecordStatus.BankAborted: {
+ case WithdrawalGroupStatus.BankAborted: {
// FIXME
return {
type: OperationAttemptResultType.Pending,
result: undefined,
};
}
- case ReserveRecordStatus.Dormant:
+ case WithdrawalGroupStatus.Finished:
// We can try to withdraw, nothing needs to be done with the reserve.
break;
default:
- logger.warn(
- "unknown reserve record status:",
- withdrawalGroup.reserveStatus,
+ throw new InvariantViolatedError(
+ `unknown reserve record status: ${withdrawalGroup.status}`,
);
- assertUnreachable(withdrawalGroup.reserveStatus);
- break;
}
await ws.exchangeOps.updateExchangeFromUrl(
@@ -1108,7 +1109,7 @@ export async function processWithdrawalGroup(
if (!wg) {
return;
}
- wg.operationStatus = OperationStatus.Finished;
+ wg.status = WithdrawalGroupStatus.Finished;
wg.timestampFinish = TalerProtocolTimestamp.now();
await tx.withdrawalGroups.put(wg);
});
@@ -1192,7 +1193,7 @@ export async function processWithdrawalGroup(
if (wg.timestampFinish === undefined && numFinished === numTotalCoins) {
finishedForFirstTime = true;
wg.timestampFinish = TalerProtocolTimestamp.now();
- wg.operationStatus = OperationStatus.Finished;
+ wg.status = WithdrawalGroupStatus.Finished;
}
await tx.withdrawalGroups.put(wg);
@@ -1508,9 +1509,9 @@ async function registerReserveWithBank(
.runReadOnly(async (tx) => {
return await tx.withdrawalGroups.get(withdrawalGroupId);
});
- switch (withdrawalGroup?.reserveStatus) {
- case ReserveRecordStatus.WaitConfirmBank:
- case ReserveRecordStatus.RegisteringBank:
+ switch (withdrawalGroup?.status) {
+ case WithdrawalGroupStatus.WaitConfirmBank:
+ case WithdrawalGroupStatus.RegisteringBank:
break;
default:
return;
@@ -1544,9 +1545,9 @@ async function registerReserveWithBank(
if (!r) {
return;
}
- switch (r.reserveStatus) {
- case ReserveRecordStatus.RegisteringBank:
- case ReserveRecordStatus.WaitConfirmBank:
+ switch (r.status) {
+ case WithdrawalGroupStatus.RegisteringBank:
+ case WithdrawalGroupStatus.WaitConfirmBank:
break;
default:
return;
@@ -1557,8 +1558,7 @@ async function registerReserveWithBank(
r.wgInfo.bankInfo.timestampReserveInfoPosted = AbsoluteTime.toTimestamp(
AbsoluteTime.now(),
);
- r.reserveStatus = ReserveRecordStatus.WaitConfirmBank;
- r.operationStatus = OperationStatus.Pending;
+ r.status = WithdrawalGroupStatus.WaitConfirmBank;
await tx.withdrawalGroups.put(r);
});
ws.notify({ type: NotificationType.ReserveRegisteredWithBank });
@@ -1575,9 +1575,9 @@ async function processReserveBankStatus(
const withdrawalGroup = await getWithdrawalGroupRecordTx(ws.db, {
withdrawalGroupId,
});
- switch (withdrawalGroup?.reserveStatus) {
- case ReserveRecordStatus.WaitConfirmBank:
- case ReserveRecordStatus.RegisteringBank:
+ switch (withdrawalGroup?.status) {
+ case WithdrawalGroupStatus.WaitConfirmBank:
+ case WithdrawalGroupStatus.RegisteringBank:
break;
default:
return {
@@ -1616,9 +1616,9 @@ async function processReserveBankStatus(
if (!r) {
return;
}
- switch (r.reserveStatus) {
- case ReserveRecordStatus.RegisteringBank:
- case ReserveRecordStatus.WaitConfirmBank:
+ switch (r.status) {
+ case WithdrawalGroupStatus.RegisteringBank:
+ case WithdrawalGroupStatus.WaitConfirmBank:
break;
default:
return;
@@ -1628,8 +1628,7 @@ async function processReserveBankStatus(
}
const now = AbsoluteTime.toTimestamp(AbsoluteTime.now());
r.wgInfo.bankInfo.timestampBankConfirmed = now;
- r.reserveStatus = ReserveRecordStatus.BankAborted;
- r.operationStatus = OperationStatus.Finished;
+ r.status = WithdrawalGroupStatus.BankAborted;
await tx.withdrawalGroups.put(r);
});
return {
@@ -1644,7 +1643,7 @@ async function processReserveBankStatus(
}
// FIXME: Why do we do this?!
- if (withdrawalGroup.reserveStatus === ReserveRecordStatus.RegisteringBank) {
+ if (withdrawalGroup.status === WithdrawalGroupStatus.RegisteringBank) {
await registerReserveWithBank(ws, withdrawalGroupId);
return await processReserveBankStatus(ws, withdrawalGroupId);
}
@@ -1657,9 +1656,9 @@ async function processReserveBankStatus(
return;
}
// Re-check reserve status within transaction
- switch (r.reserveStatus) {
- case ReserveRecordStatus.RegisteringBank:
- case ReserveRecordStatus.WaitConfirmBank:
+ switch (r.status) {
+ case WithdrawalGroupStatus.RegisteringBank:
+ case WithdrawalGroupStatus.WaitConfirmBank:
break;
default:
return;
@@ -1671,8 +1670,7 @@ async function processReserveBankStatus(
logger.info("withdrawal: transfer confirmed by bank.");
const now = AbsoluteTime.toTimestamp(AbsoluteTime.now());
r.wgInfo.bankInfo.timestampBankConfirmed = now;
- r.reserveStatus = ReserveRecordStatus.QueryingStatus;
- r.operationStatus = OperationStatus.Pending;
+ r.status = WithdrawalGroupStatus.QueryingStatus;
} else {
logger.info("withdrawal: transfer not yet confirmed by bank");
r.wgInfo.bankInfo.confirmUrl = status.confirm_transfer_url;
@@ -1689,7 +1687,7 @@ async function processReserveBankStatus(
export async function internalCreateWithdrawalGroup(
ws: InternalWalletState,
args: {
- reserveStatus: ReserveRecordStatus;
+ reserveStatus: WithdrawalGroupStatus;
amount: AmountJson;
exchangeBaseUrl: string;
forcedDenomSel?: ForcedDenomSel;
@@ -1728,12 +1726,11 @@ export async function internalCreateWithdrawalGroup(
exchangeBaseUrl: canonExchange,
instructedAmount: amount,
timestampStart: now,
- operationStatus: OperationStatus.Pending,
rawWithdrawalAmount: initialDenomSel.totalWithdrawCost,
secretSeed,
reservePriv: reserveKeyPair.priv,
reservePub: reserveKeyPair.pub,
- reserveStatus: args.reserveStatus,
+ status: args.reserveStatus,
withdrawalGroupId,
restrictAge: args.restrictAge,
senderWire: undefined,
@@ -1839,7 +1836,7 @@ export async function acceptWithdrawalFromUri(
},
restrictAge: req.restrictAge,
forcedDenomSel: req.forcedDenomSel,
- reserveStatus: ReserveRecordStatus.RegisteringBank,
+ reserveStatus: WithdrawalGroupStatus.RegisteringBank,
});
const withdrawalGroupId = withdrawalGroup.withdrawalGroupId;
@@ -1850,9 +1847,7 @@ export async function acceptWithdrawalFromUri(
const processedWithdrawalGroup = await getWithdrawalGroupRecordTx(ws.db, {
withdrawalGroupId,
});
- if (
- processedWithdrawalGroup?.reserveStatus === ReserveRecordStatus.BankAborted
- ) {
+ if (processedWithdrawalGroup?.status === WithdrawalGroupStatus.BankAborted) {
throw TalerError.fromDetail(
TalerErrorCode.WALLET_WITHDRAWAL_OPERATION_ABORTED_BY_BANK,
{},
@@ -1898,7 +1893,7 @@ export async function createManualWithdrawal(
exchangeBaseUrl: req.exchangeBaseUrl,
forcedDenomSel: req.forcedDenomSel,
restrictAge: req.restrictAge,
- reserveStatus: ReserveRecordStatus.QueryingStatus,
+ reserveStatus: WithdrawalGroupStatus.QueryingStatus,
});
const withdrawalGroupId = withdrawalGroup.withdrawalGroupId;
diff --git a/packages/taler-wallet-core/src/util/invariants.ts b/packages/taler-wallet-core/src/util/invariants.ts
index b788d044e..3598d857c 100644
--- a/packages/taler-wallet-core/src/util/invariants.ts
+++ b/packages/taler-wallet-core/src/util/invariants.ts
@@ -14,6 +14,13 @@
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
+export class InvariantViolatedError extends Error {
+ constructor(message?: string) {
+ super(message);
+ Object.setPrototypeOf(this, InvariantViolatedError.prototype);
+ }
+}
+
/**
* Helpers for invariants.
*/
diff --git a/packages/taler-wallet-core/src/util/query.ts b/packages/taler-wallet-core/src/util/query.ts
index d1aae6fd6..3d4ff79cb 100644
--- a/packages/taler-wallet-core/src/util/query.ts
+++ b/packages/taler-wallet-core/src/util/query.ts
@@ -36,8 +36,6 @@ import {
IDBKeyRange,
} from "@gnu-taler/idb-bridge";
import { Logger } from "@gnu-taler/taler-util";
-import { performanceNow } from "./timer.js";
-import { access } from "fs";
const logger = new Logger("query.ts");