aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFlorian Dold <florian@dold.me>2022-10-15 21:26:36 +0200
committerFlorian Dold <florian@dold.me>2022-10-15 21:26:36 +0200
commitfbb7dd9e7e7fe4cf0611f5827f0bd250634dc29f (patch)
treeb6fb1fc37c67f72e8d2333d76fd1588931d261cb
parentd98d49aa58d59e6d428e5d024ba3f6ea0352ae2a (diff)
wallet-core, wallet-cli: add status to exchange list, add detail query to CLI
-rw-r--r--packages/taler-util/src/wallet-types.ts18
-rw-r--r--packages/taler-wallet-cli/src/index.ts19
-rw-r--r--packages/taler-wallet-core/src/db.ts57
-rw-r--r--packages/taler-wallet-core/src/operations/common.ts35
-rw-r--r--packages/taler-wallet-core/src/operations/exchanges.ts2
-rw-r--r--packages/taler-wallet-core/src/operations/withdraw.ts14
-rw-r--r--packages/taler-wallet-core/src/util/retries.ts1
-rw-r--r--packages/taler-wallet-core/src/wallet-api-types.ts12
-rw-r--r--packages/taler-wallet-core/src/wallet.ts34
-rw-r--r--packages/taler-wallet-webextension/src/wallet/DestinationSelection.tsx6
-rw-r--r--packages/taler-wallet-webextension/src/wallet/ManualWithdrawPage.tsx2
-rw-r--r--packages/taler-wallet-webextension/src/wallet/Settings.tsx8
12 files changed, 133 insertions, 75 deletions
diff --git a/packages/taler-util/src/wallet-types.ts b/packages/taler-util/src/wallet-types.ts
index aba1b1185..7e538b2d9 100644
--- a/packages/taler-util/src/wallet-types.ts
+++ b/packages/taler-util/src/wallet-types.ts
@@ -905,13 +905,27 @@ export enum ExchangeTosStatus {
Accepted = "accepted",
Changed = "changed",
NotFound = "not-found",
+ Unknown = "unknown",
}
+export enum ExchangeEntryStatus {
+ Unknown = "unknown",
+ Outdated = "outdated",
+ Ok = "ok",
+}
+
+// FIXME: This should probably include some error status.
export interface ExchangeListItem {
exchangeBaseUrl: string;
- currency: string;
+ currency: string | undefined;
paytoUris: string[];
tosStatus: ExchangeTosStatus;
+ exchangeStatus: ExchangeEntryStatus;
+ /**
+ * Permanently added to the wallet, as opposed to just
+ * temporarily queried.
+ */
+ permanent: boolean;
}
const codecForAuditorDenomSig = (): Codec<AuditorDenomSig> =>
@@ -984,6 +998,8 @@ export const codecForExchangeListItem = (): Codec<ExchangeListItem> =>
.property("exchangeBaseUrl", codecForString())
.property("paytoUris", codecForList(codecForString()))
.property("tosStatus", codecForAny())
+ .property("exchangeStatus", codecForAny())
+ .property("permanent", codecForBoolean())
.build("ExchangeListItem");
export const codecForExchangesListResponse = (): Codec<ExchangesListResponse> =>
diff --git a/packages/taler-wallet-cli/src/index.ts b/packages/taler-wallet-cli/src/index.ts
index b3abbac6f..249198e3c 100644
--- a/packages/taler-wallet-cli/src/index.ts
+++ b/packages/taler-wallet-cli/src/index.ts
@@ -550,6 +550,25 @@ exchangesCli
});
exchangesCli
+ .subcommand("exchangesShowCmd", "show", {
+ help: "Show exchange details",
+ })
+ .requiredArgument("url", clk.STRING, {
+ help: "Base URL of the exchange.",
+ })
+ .action(async (args) => {
+ await withWallet(args, async (wallet) => {
+ const resp = await wallet.client.call(
+ WalletApiOperation.GetExchangeDetailedInfo,
+ {
+ exchangeBaseUrl: args.exchangesShowCmd.url,
+ },
+ );
+ console.log(JSON.stringify(resp, undefined, 2));
+ });
+ });
+
+exchangesCli
.subcommand("exchangesAddCmd", "add", {
help: "Add an exchange by base URL.",
})
diff --git a/packages/taler-wallet-core/src/db.ts b/packages/taler-wallet-core/src/db.ts
index 4b031ace3..59980621f 100644
--- a/packages/taler-wallet-core/src/db.ts
+++ b/packages/taler-wallet-core/src/db.ts
@@ -17,45 +17,42 @@
/**
* Imports.
*/
+import { Event, IDBDatabase } from "@gnu-taler/idb-bridge";
import {
- describeStore,
- describeContents,
- describeIndex,
-} from "./util/query.js";
-import {
+ AgeCommitmentProof,
AmountJson,
AmountString,
- ExchangeAuditor,
- CoinDepositPermission,
+ CoinEnvelope,
+ CoinRefreshRequest,
+ CoinStatus,
ContractTerms,
+ DenominationInfo,
DenominationPubKey,
- ExchangeSignKeyJson,
+ DenomSelectionState,
+ EddsaPublicKeyString,
+ EddsaSignatureString,
+ ExchangeAuditor,
+ ExchangeGlobalFees,
InternationalizedString,
+ Location,
MerchantInfo,
+ PayCoinSelection,
+ PeerContractTerms,
Product,
RefreshReason,
TalerErrorDetail,
- UnblindedSignature,
- CoinEnvelope,
- TalerProtocolTimestamp,
TalerProtocolDuration,
- AgeCommitmentProof,
- PayCoinSelection,
- PeerContractTerms,
- Location,
- WireInfo,
- DenominationInfo,
- GlobalFees,
- ExchangeGlobalFees,
- DenomSelectionState,
+ TalerProtocolTimestamp,
TransactionIdStr,
- CoinRefreshRequest,
- CoinStatus,
- EddsaPublicKeyString,
- EddsaSignatureString,
+ UnblindedSignature,
+ WireInfo,
} from "@gnu-taler/taler-util";
+import {
+ describeContents,
+ describeIndex,
+ describeStore,
+} from "./util/query.js";
import { RetryInfo, RetryTags } from "./util/retries.js";
-import { Event, IDBDatabase } from "@gnu-taler/idb-bridge";
/**
* This file contains the database schema of the Taler wallet together
@@ -354,8 +351,6 @@ export interface DenominationRecord {
* Was this denomination still offered by the exchange the last time
* we checked?
* Only false when the exchange redacts a previously published denomination.
- *
- * FIXME: Consider rolling this and isRevoked into some bitfield?
*/
isOffered: boolean;
@@ -526,6 +521,8 @@ export interface ExchangeRecord {
* Should usually not change. Only changes when the
* exchange advertises a different master public key and/or
* currency.
+ *
+ * FIXME: Use a rowId here?
*/
detailsPointer: ExchangeDetailsPointer | undefined;
@@ -1168,8 +1165,6 @@ export interface PurchaseRecord {
/**
* Timestamp of the first time that sending a payment to the merchant
* for this purchase was successful.
- *
- * FIXME: Does this need to be a timestamp, doesn't boolean suffice?
*/
timestampFirstSuccessfulPay: TalerProtocolTimestamp | undefined;
@@ -1369,6 +1364,8 @@ export interface WithdrawalGroupRecord {
/**
* Wire information (as payto URI) for the bank account that
* transferred funds for this reserve.
+ *
+ * FIXME: Doesn't this belong to the bankAccounts object store?
*/
senderWire?: string;
@@ -1604,7 +1601,7 @@ export interface GhostDepositGroupRecord {
export interface TombstoneRecord {
/**
- * Tombstone ID, with the syntax "<type>:<key>".
+ * Tombstone ID, with the syntax "tmb:<type>:<key>".
*/
id: string;
}
diff --git a/packages/taler-wallet-core/src/operations/common.ts b/packages/taler-wallet-core/src/operations/common.ts
index ee7a1b46e..95441be2b 100644
--- a/packages/taler-wallet-core/src/operations/common.ts
+++ b/packages/taler-wallet-core/src/operations/common.ts
@@ -22,6 +22,8 @@ import {
Amounts,
CoinRefreshRequest,
CoinStatus,
+ ExchangeEntryStatus,
+ ExchangeListItem,
ExchangeTosStatus,
j2s,
Logger,
@@ -32,7 +34,12 @@ import {
TransactionIdStr,
TransactionType,
} from "@gnu-taler/taler-util";
-import { WalletStoresV1, CoinRecord, ExchangeDetailsRecord } from "../db.js";
+import {
+ WalletStoresV1,
+ CoinRecord,
+ ExchangeDetailsRecord,
+ ExchangeRecord,
+} from "../db.js";
import { makeErrorDetail, TalerError } from "../errors.js";
import { InternalWalletState } from "../internal-wallet-state.js";
import { checkDbInvariant, checkLogicInvariant } from "../util/invariants.js";
@@ -320,3 +327,29 @@ export function getExchangeTosStatus(
}
return ExchangeTosStatus.Changed;
}
+
+export function makeExchangeListItem(
+ r: ExchangeRecord,
+ exchangeDetails: ExchangeDetailsRecord | undefined,
+): ExchangeListItem {
+ if (!exchangeDetails) {
+ return {
+ exchangeBaseUrl: r.baseUrl,
+ currency: undefined,
+ tosStatus: ExchangeTosStatus.Unknown,
+ paytoUris: [],
+ exchangeStatus: ExchangeEntryStatus.Unknown,
+ permanent: r.permanent,
+ };
+ }
+ let exchangeStatus;
+ exchangeStatus = ExchangeEntryStatus.Ok;
+ return {
+ exchangeBaseUrl: r.baseUrl,
+ currency: exchangeDetails.currency,
+ tosStatus: getExchangeTosStatus(exchangeDetails),
+ paytoUris: exchangeDetails.wireInfo.accounts.map((x) => x.payto_uri),
+ exchangeStatus,
+ permanent: r.permanent,
+ };
+}
diff --git a/packages/taler-wallet-core/src/operations/exchanges.ts b/packages/taler-wallet-core/src/operations/exchanges.ts
index e89364ad1..142bedd83 100644
--- a/packages/taler-wallet-core/src/operations/exchanges.ts
+++ b/packages/taler-wallet-core/src/operations/exchanges.ts
@@ -705,8 +705,6 @@ export async function updateExchangeFromUrlHandler(
};
}
await tx.exchanges.put(r);
- logger.info(`existing details ${j2s(existingDetails)}`);
- logger.info(`inserting new details ${j2s(newDetails)}`);
const drRowId = await tx.exchangeDetails.put(newDetails);
checkDbInvariant(typeof drRowId.key === "number");
diff --git a/packages/taler-wallet-core/src/operations/withdraw.ts b/packages/taler-wallet-core/src/operations/withdraw.ts
index 1113fb87a..59863101a 100644
--- a/packages/taler-wallet-core/src/operations/withdraw.ts
+++ b/packages/taler-wallet-core/src/operations/withdraw.ts
@@ -85,6 +85,7 @@ import { InternalWalletState } from "../internal-wallet-state.js";
import {
getExchangeTosStatus,
makeCoinAvailable,
+ makeExchangeListItem,
runOperationWithErrorReporting,
} from "../operations/common.js";
import { walletCoreDebugFlags } from "../util/debugFlags.js";
@@ -1367,18 +1368,7 @@ export async function getWithdrawalDetailsForUri(
.iter(r.baseUrl)
.toArray();
if (exchangeDetails && denominations) {
- const tosRecord = await tx.exchangeTos.get([
- exchangeDetails.exchangeBaseUrl,
- exchangeDetails.tosCurrentEtag,
- ]);
- exchanges.push({
- exchangeBaseUrl: exchangeDetails.exchangeBaseUrl,
- currency: exchangeDetails.currency,
- paytoUris: exchangeDetails.wireInfo.accounts.map(
- (x) => x.payto_uri,
- ),
- tosStatus: getExchangeTosStatus(exchangeDetails),
- });
+ exchanges.push(makeExchangeListItem(r, exchangeDetails));
}
}
});
diff --git a/packages/taler-wallet-core/src/util/retries.ts b/packages/taler-wallet-core/src/util/retries.ts
index 697d6531e..3597a4311 100644
--- a/packages/taler-wallet-core/src/util/retries.ts
+++ b/packages/taler-wallet-core/src/util/retries.ts
@@ -49,7 +49,6 @@ export enum OperationAttemptResultType {
Longpoll = "longpoll",
}
-// FIXME: not part of DB!
export type OperationAttemptResult<TSuccess = unknown, TPending = unknown> =
| OperationAttemptFinishedResult<TSuccess>
| OperationAttemptErrorResult
diff --git a/packages/taler-wallet-core/src/wallet-api-types.ts b/packages/taler-wallet-core/src/wallet-api-types.ts
index 63d960f81..3841bd8d3 100644
--- a/packages/taler-wallet-core/src/wallet-api-types.ts
+++ b/packages/taler-wallet-core/src/wallet-api-types.ts
@@ -48,6 +48,7 @@ import {
CreateDepositGroupRequest,
CreateDepositGroupResponse,
DeleteTransactionRequest,
+ ExchangeDetailedResponse,
ExchangesListResponse,
ForceRefreshRequest,
GetExchangeTosRequest,
@@ -109,6 +110,7 @@ export enum WalletApiOperation {
ApplyRefund = "applyRefund",
AcceptBankIntegratedWithdrawal = "acceptBankIntegratedWithdrawal",
GetExchangeTos = "getExchangeTos",
+ GetExchangeDetailedInfo = "getExchangeDetailedInfo",
RetryPendingNow = "retryPendingNow",
AbortFailedPayWithRefund = "abortFailedPayWithRefund",
ConfirmPay = "confirmPay",
@@ -334,6 +336,15 @@ export type GetExchangeTosOp = {
};
/**
+ * Get the current terms of a service of an exchange.
+ */
+export type GetExchangeDetailedInfoOp = {
+ op: WalletApiOperation.GetExchangeDetailedInfo;
+ request: AddExchangeRequest;
+ response: ExchangeDetailedResponse;
+};
+
+/**
* List currencies known to the wallet.
*/
export type ListCurrenciesOp = {
@@ -661,6 +672,7 @@ export type WalletOperations = {
[WalletApiOperation.AddExchange]: AddExchangeOp;
[WalletApiOperation.SetExchangeTosAccepted]: SetExchangeTosAcceptedOp;
[WalletApiOperation.GetExchangeTos]: GetExchangeTosOp;
+ [WalletApiOperation.GetExchangeDetailedInfo]: GetExchangeDetailedInfoOp;
[WalletApiOperation.TrackDepositGroup]: TrackDepositGroupOp;
[WalletApiOperation.CreateDepositGroup]: CreateDepositGroupOp;
[WalletApiOperation.SetWalletDeviceId]: SetWalletDeviceIdOp;
diff --git a/packages/taler-wallet-core/src/wallet.ts b/packages/taler-wallet-core/src/wallet.ts
index 3c7194059..7839f3dab 100644
--- a/packages/taler-wallet-core/src/wallet.ts
+++ b/packages/taler-wallet-core/src/wallet.ts
@@ -97,6 +97,8 @@ import {
ExchangeTosStatusDetails,
CoinRefreshRequest,
CoinStatus,
+ ExchangeEntryStatus,
+ ExchangeTosStatus,
} from "@gnu-taler/taler-util";
import { TalerCryptoInterface } from "./crypto/cryptoImplementation.js";
import {
@@ -146,7 +148,11 @@ import {
} from "./operations/backup/index.js";
import { setWalletDeviceId } from "./operations/backup/state.js";
import { getBalances } from "./operations/balance.js";
-import { getExchangeTosStatus, runOperationWithErrorReporting } from "./operations/common.js";
+import {
+ getExchangeTosStatus,
+ makeExchangeListItem,
+ runOperationWithErrorReporting,
+} from "./operations/common.js";
import {
createDepositGroup,
getFeeForDeposit,
@@ -645,32 +651,8 @@ async function getExchanges(
.runReadOnly(async (tx) => {
const exchangeRecords = await tx.exchanges.iter().toArray();
for (const r of exchangeRecords) {
- const dp = r.detailsPointer;
- if (!dp) {
- continue;
- }
- const { currency } = dp;
const exchangeDetails = await getExchangeDetails(tx, r.baseUrl);
- if (!exchangeDetails) {
- continue;
- }
-
- const denominations = await tx.denominations.indexes.byExchangeBaseUrl
- .iter(r.baseUrl)
- .toArray();
-
- if (!denominations) {
- continue;
- }
-
- const tos = await getExchangeTosStatusDetails(tx, exchangeDetails);
-
- exchanges.push({
- exchangeBaseUrl: r.baseUrl,
- currency,
- tosStatus: getExchangeTosStatus(exchangeDetails),
- paytoUris: exchangeDetails.wireInfo.accounts.map((x) => x.payto_uri),
- });
+ exchanges.push(makeExchangeListItem(r, exchangeDetails));
}
});
return { exchanges };
diff --git a/packages/taler-wallet-webextension/src/wallet/DestinationSelection.tsx b/packages/taler-wallet-webextension/src/wallet/DestinationSelection.tsx
index 3143aafa1..94e6ab442 100644
--- a/packages/taler-wallet-webextension/src/wallet/DestinationSelection.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/DestinationSelection.tsx
@@ -185,7 +185,11 @@ export function SelectCurrency({
);
}
const list: Record<string, string> = {};
- hook.response.exchanges.forEach((e) => (list[e.currency] = e.currency));
+ hook.response.exchanges.forEach((e) => {
+ if (e.currency) {
+ list[e.currency] = e.currency;
+ }
+ });
list[""] = "Select a currency";
return <SelectCurrencyView onChange={onChange} list={list} />;
}
diff --git a/packages/taler-wallet-webextension/src/wallet/ManualWithdrawPage.tsx b/packages/taler-wallet-webextension/src/wallet/ManualWithdrawPage.tsx
index a292914fb..3714ae538 100644
--- a/packages/taler-wallet-webextension/src/wallet/ManualWithdrawPage.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/ManualWithdrawPage.tsx
@@ -112,7 +112,7 @@ export function ManualWithdrawPage({ amount, onCancel }: Props): VNode {
const exchangeList = state.response.exchanges.reduce(
(p, c) => ({
...p,
- [c.exchangeBaseUrl]: c.currency,
+ [c.exchangeBaseUrl]: c.currency || "??",
}),
{} as Record<string, string>,
);
diff --git a/packages/taler-wallet-webextension/src/wallet/Settings.tsx b/packages/taler-wallet-webextension/src/wallet/Settings.tsx
index 8412c4a12..2ff9f15f5 100644
--- a/packages/taler-wallet-webextension/src/wallet/Settings.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/Settings.tsx
@@ -204,6 +204,14 @@ export function SettingsView({
<i18n.Translate>not accepted</i18n.Translate>
</DestructiveText>
);
+ case ExchangeTosStatus.Unknown:
+ return (
+ <DestructiveText>
+ <i18n.Translate>
+ unknown (exchange status should be updated)
+ </i18n.Translate>
+ </DestructiveText>
+ );
}
}
return (