aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/taler-harness/src/integrationtests/test-revocation.ts4
-rw-r--r--packages/taler-harness/src/integrationtests/test-wallet-refresh-errors.ts4
-rw-r--r--packages/taler-harness/src/integrationtests/test-wallettesting.ts6
-rw-r--r--packages/taler-util/src/wallet-types.ts46
-rw-r--r--packages/taler-wallet-cli/src/index.ts23
-rw-r--r--packages/taler-wallet-core/src/common.ts16
-rw-r--r--packages/taler-wallet-core/src/db.ts9
-rw-r--r--packages/taler-wallet-core/src/deposits.ts7
-rw-r--r--packages/taler-wallet-core/src/exchanges.ts5
-rw-r--r--packages/taler-wallet-core/src/pay-merchant.ts20
-rw-r--r--packages/taler-wallet-core/src/pay-peer-pull-debit.ts5
-rw-r--r--packages/taler-wallet-core/src/pay-peer-push-debit.ts14
-rw-r--r--packages/taler-wallet-core/src/recoup.ts5
-rw-r--r--packages/taler-wallet-core/src/refresh.ts29
-rw-r--r--packages/taler-wallet-core/src/testing.ts8
-rw-r--r--packages/taler-wallet-core/src/wallet.ts20
-rw-r--r--packages/taler-wallet-webextension/src/wallet/DeveloperPage.tsx20
17 files changed, 175 insertions, 66 deletions
diff --git a/packages/taler-harness/src/integrationtests/test-revocation.ts b/packages/taler-harness/src/integrationtests/test-revocation.ts
index 65aa86f98..8714a3769 100644
--- a/packages/taler-harness/src/integrationtests/test-revocation.ts
+++ b/packages/taler-harness/src/integrationtests/test-revocation.ts
@@ -51,7 +51,7 @@ async function revokeAllWalletCoins(req: {
console.log(coinDump);
const usedDenomHashes = new Set<string>();
for (const coin of coinDump.coins) {
- usedDenomHashes.add(coin.denom_pub_hash);
+ usedDenomHashes.add(coin.denomPubHash);
}
for (const x of usedDenomHashes.values()) {
await exchange.revokeDenomination(x);
@@ -239,7 +239,7 @@ export async function runRevocationTest(t: GlobalTestState) {
const coinDump = await walletClient.call(WalletApiOperation.DumpCoins, {});
console.log(coinDump);
- const coinPubList = coinDump.coins.map((x) => x.coin_pub);
+ const coinPubList = coinDump.coins.map((x) => x.coinPub);
await walletClient.call(WalletApiOperation.ForceRefresh, {
refreshCoinSpecs: coinPubList.map((x) => ({ coinPub: x })),
});
diff --git a/packages/taler-harness/src/integrationtests/test-wallet-refresh-errors.ts b/packages/taler-harness/src/integrationtests/test-wallet-refresh-errors.ts
index 0f1efd35e..7b101bc18 100644
--- a/packages/taler-harness/src/integrationtests/test-wallet-refresh-errors.ts
+++ b/packages/taler-harness/src/integrationtests/test-wallet-refresh-errors.ts
@@ -80,7 +80,7 @@ export async function runWalletRefreshErrorsTest(t: GlobalTestState) {
await walletClient.call(WalletApiOperation.ForceRefresh, {
refreshCoinSpecs: [
{
- coinPub: coinDump.coins[0].coin_pub,
+ coinPub: coinDump.coins[0].coinPub,
amount: "TESTKUDOS:3" as AmountString,
},
],
@@ -95,7 +95,7 @@ export async function runWalletRefreshErrorsTest(t: GlobalTestState) {
await walletClient.call(WalletApiOperation.ForceRefresh, {
refreshCoinSpecs: [
{
- coinPub: coinDump.coins[0].coin_pub,
+ coinPub: coinDump.coins[0].coinPub,
amount: "TESTKUDOS:3" as AmountString,
},
],
diff --git a/packages/taler-harness/src/integrationtests/test-wallettesting.ts b/packages/taler-harness/src/integrationtests/test-wallettesting.ts
index 001081532..bc5ed1004 100644
--- a/packages/taler-harness/src/integrationtests/test-wallettesting.ts
+++ b/packages/taler-harness/src/integrationtests/test-wallettesting.ts
@@ -191,10 +191,10 @@ export async function runWallettestingTest(t: GlobalTestState) {
{
for (const c of coinDump.coins) {
if (
- c.coin_status === CoinStatus.Fresh &&
- 0 === Amounts.cmp(c.denom_value, "TESTKUDOS:8")
+ c.coinStatus === CoinStatus.Fresh &&
+ 0 === Amounts.cmp(c.denomValue, "TESTKUDOS:8")
) {
- susp = c.coin_pub;
+ susp = c.coinPub;
}
}
}
diff --git a/packages/taler-util/src/wallet-types.ts b/packages/taler-util/src/wallet-types.ts
index 099e5c060..360d8b183 100644
--- a/packages/taler-util/src/wallet-types.ts
+++ b/packages/taler-util/src/wallet-types.ts
@@ -627,6 +627,32 @@ export enum CoinStatus {
Dormant = "dormant",
}
+export type WalletCoinHistoryItem =
+ | {
+ type: "withdraw";
+ transactionId: TransactionIdStr;
+ }
+ | {
+ type: "spend";
+ transactionId: TransactionIdStr;
+ amount: AmountString;
+ }
+ | {
+ type: "refresh";
+ transactionId: TransactionIdStr;
+ amount: AmountString;
+ }
+ | {
+ type: "recoup";
+ transactionId: TransactionIdStr;
+ amount: AmountString;
+ }
+ | {
+ type: "refund";
+ transactionId: TransactionIdStr;
+ amount: AmountString;
+ };
+
/**
* Easy to process format for the public data of coins
* managed by the wallet.
@@ -636,38 +662,42 @@ export interface CoinDumpJson {
/**
* The coin's denomination's public key.
*/
- denom_pub: DenominationPubKey;
+ denomPub: DenominationPubKey;
/**
* Hash of denom_pub.
*/
- denom_pub_hash: string;
+ denomPubHash: string;
/**
* Value of the denomination (without any fees).
*/
- denom_value: string;
+ denomValue: string;
/**
* Public key of the coin.
*/
- coin_pub: string;
+ coinPub: string;
/**
* Base URL of the exchange for the coin.
*/
- exchange_base_url: string;
+ exchangeBaseUrl: string;
/**
* Public key of the parent coin.
* Only present if this coin was obtained via refreshing.
*/
- refresh_parent_coin_pub: string | undefined;
+ refreshParentCoinPub: string | undefined;
/**
* Public key of the reserve for this coin.
* Only present if this coin was obtained via refreshing.
*/
- withdrawal_reserve_pub: string | undefined;
- coin_status: CoinStatus;
+ withdrawalReservePub: string | undefined;
+ /**
+ * Status of the coin.
+ */
+ coinStatus: CoinStatus;
/**
* Information about the age restriction
*/
ageCommitmentProof: AgeCommitmentProof | undefined;
+ history: WalletCoinHistoryItem[];
}>;
}
diff --git a/packages/taler-wallet-cli/src/index.ts b/packages/taler-wallet-cli/src/index.ts
index 5bde7db01..252390733 100644
--- a/packages/taler-wallet-cli/src/index.ts
+++ b/packages/taler-wallet-cli/src/index.ts
@@ -1695,10 +1695,25 @@ advancedCli
await withWallet(args, { lazyTaskLoop: true }, async (wallet) => {
const coins = await wallet.client.call(WalletApiOperation.DumpCoins, {});
for (const coin of coins.coins) {
- console.log(`coin ${coin.coin_pub}`);
- console.log(` exchange ${coin.exchange_base_url}`);
- console.log(` denomPubHash ${coin.denom_pub_hash}`);
- console.log(` status ${coin.coin_status}`);
+ console.log(`coin ${coin.coinPub}`);
+ console.log(` exchange ${coin.exchangeBaseUrl}`);
+ console.log(` denomPubHash ${coin.denomPubHash}`);
+ console.log(` status ${coin.coinStatus}`);
+ if (coin.history.length > 0) {
+ console.log(` history`);
+ for (const hi of coin.history) {
+ switch (hi.type) {
+ case "spend":
+ console.log(` spend ${hi.transactionId} ${hi.amount}`);
+ break;
+ case "refresh":
+ console.log(` refresh ${hi.transactionId} ${hi.amount}`);
+ break;
+ default:
+ console.log(` unknown (${hi.type})`);
+ }
+ }
+ }
}
});
});
diff --git a/packages/taler-wallet-core/src/common.ts b/packages/taler-wallet-core/src/common.ts
index 9f63c74d5..83f50c8e3 100644
--- a/packages/taler-wallet-core/src/common.ts
+++ b/packages/taler-wallet-core/src/common.ts
@@ -46,6 +46,7 @@ import {
} from "@gnu-taler/taler-util";
import {
BackupProviderRecord,
+ CoinHistoryRecord,
CoinRecord,
DbPreciseTimestamp,
DepositGroupRecord,
@@ -221,6 +222,21 @@ export async function spendCoins(
coinAvailability.visibleCoinCount--;
}
}
+ let histEntry: CoinHistoryRecord | undefined = await tx.coinHistory.get(
+ coin.coinPub,
+ );
+ if (!histEntry) {
+ histEntry = {
+ coinPub: coin.coinPub,
+ history: [],
+ };
+ }
+ histEntry.history.push({
+ type: "spend",
+ transactionId: csi.transactionId,
+ amount: Amounts.stringify(contrib),
+ });
+ await tx.coinHistory.put(histEntry);
await tx.coins.put(coin);
await tx.coinAvailability.put(coinAvailability);
}
diff --git a/packages/taler-wallet-core/src/db.ts b/packages/taler-wallet-core/src/db.ts
index 7c2380e2d..3438cbdc7 100644
--- a/packages/taler-wallet-core/src/db.ts
+++ b/packages/taler-wallet-core/src/db.ts
@@ -905,7 +905,12 @@ export interface CoinRecord {
ageCommitmentProof: AgeCommitmentProof | undefined;
}
-export type WalletCoinHistoryItem =
+/**
+ * History item for a coin.
+ *
+ * DB-specific format,
+ */
+export type DbWalletCoinHistoryItem =
| {
type: "withdraw";
transactionId: TransactionIdStr;
@@ -944,7 +949,7 @@ export interface CoinHistoryRecord {
* We store this as an array in the object store, as the coin history
* is pretty much always very small.
*/
- history: WalletCoinHistoryItem[];
+ history: DbWalletCoinHistoryItem[];
}
export enum RefreshCoinStatus {
diff --git a/packages/taler-wallet-core/src/deposits.ts b/packages/taler-wallet-core/src/deposits.ts
index e3d0b997e..6394fdc78 100644
--- a/packages/taler-wallet-core/src/deposits.ts
+++ b/packages/taler-wallet-core/src/deposits.ts
@@ -518,12 +518,13 @@ async function refundDepositGroup(
const res = await wex.db.runReadWriteTx(
{
storeNames: [
+ "coinAvailability",
+ "coinHistory",
+ "coins",
+ "denominations",
"depositGroups",
"refreshGroups",
"refreshSessions",
- "coins",
- "denominations",
- "coinAvailability",
],
},
async (tx) => {
diff --git a/packages/taler-wallet-core/src/exchanges.ts b/packages/taler-wallet-core/src/exchanges.ts
index 3bec30587..4043d3498 100644
--- a/packages/taler-wallet-core/src/exchanges.ts
+++ b/packages/taler-wallet-core/src/exchanges.ts
@@ -1693,12 +1693,13 @@ export async function updateExchangeFromUrlHandler(
await wex.db.runReadWriteTx(
{
storeNames: [
+ "coinAvailability",
+ "coinHistory",
"coins",
"denominations",
- "coinAvailability",
+ "exchanges",
"refreshGroups",
"refreshSessions",
- "exchanges",
],
},
async (tx) => {
diff --git a/packages/taler-wallet-core/src/pay-merchant.ts b/packages/taler-wallet-core/src/pay-merchant.ts
index 118a338cc..28fb204dd 100644
--- a/packages/taler-wallet-core/src/pay-merchant.ts
+++ b/packages/taler-wallet-core/src/pay-merchant.ts
@@ -282,13 +282,14 @@ export class PayMerchantTransactionContext implements TransactionContext {
const transitionInfo = await wex.db.runReadWriteTx(
{
storeNames: [
- "purchases",
- "refreshGroups",
- "refreshSessions",
- "denominations",
"coinAvailability",
+ "coinHistory",
"coins",
+ "denominations",
"operationRetries",
+ "purchases",
+ "refreshGroups",
+ "refreshSessions",
],
},
async (tx) => {
@@ -3407,16 +3408,17 @@ async function storeRefunds(
const result = await wex.db.runReadWriteTx(
{
storeNames: [
+ "coinAvailability",
+ "coinHistory",
+ "coins",
"coins",
"denominations",
- "purchases",
- "refundItems",
- "refundGroups",
"denominations",
- "coins",
- "coinAvailability",
+ "purchases",
"refreshGroups",
"refreshSessions",
+ "refundGroups",
+ "refundItems",
],
},
async (tx) => {
diff --git a/packages/taler-wallet-core/src/pay-peer-pull-debit.ts b/packages/taler-wallet-core/src/pay-peer-pull-debit.ts
index bc154a45d..a1c6b46ce 100644
--- a/packages/taler-wallet-core/src/pay-peer-pull-debit.ts
+++ b/packages/taler-wallet-core/src/pay-peer-pull-debit.ts
@@ -237,11 +237,12 @@ export class PeerPullDebitTransactionContext implements TransactionContext {
{
extraStores: [
"coinAvailability",
+ "coinAvailability",
+ "coinHistory",
+ "coins",
"denominations",
"refreshGroups",
"refreshSessions",
- "coins",
- "coinAvailability",
],
},
async (pi, tx) => {
diff --git a/packages/taler-wallet-core/src/pay-peer-push-debit.ts b/packages/taler-wallet-core/src/pay-peer-push-debit.ts
index 92d4b98ba..6603cc4f3 100644
--- a/packages/taler-wallet-core/src/pay-peer-push-debit.ts
+++ b/packages/taler-wallet-core/src/pay-peer-push-debit.ts
@@ -733,12 +733,13 @@ async function processPeerPushDebitAbortingDeletePurse(
const transitionInfo = await wex.db.runReadWriteTx(
{
storeNames: [
+ "coinAvailability",
+ "coinHistory",
+ "coins",
+ "denominations",
"peerPushDebit",
"refreshGroups",
"refreshSessions",
- "denominations",
- "coinAvailability",
- "coins",
],
},
async (tx) => {
@@ -974,12 +975,13 @@ async function processPeerPushDebitReady(
const transitionInfo = await wex.db.runReadWriteTx(
{
storeNames: [
+ "coinAvailability",
+ "coinHistory",
+ "coins",
+ "denominations",
"peerPushDebit",
"refreshGroups",
"refreshSessions",
- "denominations",
- "coinAvailability",
- "coins",
],
},
async (tx) => {
diff --git a/packages/taler-wallet-core/src/recoup.ts b/packages/taler-wallet-core/src/recoup.ts
index 9adc5e2ad..56c819bf8 100644
--- a/packages/taler-wallet-core/src/recoup.ts
+++ b/packages/taler-wallet-core/src/recoup.ts
@@ -392,12 +392,13 @@ export async function processRecoupGroup(
await wex.db.runReadWriteTx(
{
storeNames: [
- "recoupGroups",
"coinAvailability",
+ "coinHistory",
+ "coins",
"denominations",
+ "recoupGroups",
"refreshGroups",
"refreshSessions",
- "coins",
],
},
async (tx) => {
diff --git a/packages/taler-wallet-core/src/refresh.ts b/packages/taler-wallet-core/src/refresh.ts
index 01970f8bc..9a5058a4d 100644
--- a/packages/taler-wallet-core/src/refresh.ts
+++ b/packages/taler-wallet-core/src/refresh.ts
@@ -92,6 +92,7 @@ import {
import { CryptoApiStoppedError } from "./crypto/workers/crypto-dispatcher.js";
import {
CoinAvailabilityRecord,
+ CoinHistoryRecord,
CoinRecord,
CoinSourceType,
DenominationRecord,
@@ -1546,7 +1547,13 @@ export async function calculateRefreshOutput(
async function applyRefreshToOldCoins(
wex: WalletExecutionContext,
tx: WalletDbReadWriteTransaction<
- ["denominations", "coins", "refreshGroups", "coinAvailability"]
+ [
+ "denominations",
+ "coins",
+ "coinHistory",
+ "refreshGroups",
+ "coinAvailability",
+ ]
>,
oldCoinPubs: CoinRefreshRequest[],
refreshGroupId: string,
@@ -1604,6 +1611,24 @@ async function applyRefreshToOldCoins(
default:
assertUnreachable(coin.status);
}
+ let histEntry: CoinHistoryRecord | undefined = await tx.coinHistory.get(
+ coin.coinPub,
+ );
+ if (!histEntry) {
+ histEntry = {
+ coinPub: coin.coinPub,
+ history: [],
+ };
+ }
+ histEntry.history.push({
+ type: "refresh",
+ transactionId: constructTransactionIdentifier({
+ tag: TransactionType.Refresh,
+ refreshGroupId,
+ }),
+ amount: Amounts.stringify(ocp.amount),
+ });
+ await tx.coinHistory.put(histEntry);
await tx.coins.put(coin);
}
}
@@ -1628,6 +1653,7 @@ export async function createRefreshGroup(
[
"denominations",
"coins",
+ "coinHistory",
"refreshGroups",
"refreshSessions",
"coinAvailability",
@@ -1785,6 +1811,7 @@ export async function forceRefresh(
"refreshSessions",
"denominations",
"coins",
+ "coinHistory",
],
},
async (tx) => {
diff --git a/packages/taler-wallet-core/src/testing.ts b/packages/taler-wallet-core/src/testing.ts
index 057ac50cd..6435595cb 100644
--- a/packages/taler-wallet-core/src/testing.ts
+++ b/packages/taler-wallet-core/src/testing.ts
@@ -58,7 +58,10 @@ import {
import { getBalances } from "./balance.js";
import { genericWaitForState } from "./common.js";
import { createDepositGroup } from "./deposits.js";
-import { fetchFreshExchange } from "./exchanges.js";
+import {
+ acceptExchangeTermsOfService,
+ fetchFreshExchange,
+} from "./exchanges.js";
import {
confirmPay,
preparePayForUri,
@@ -122,6 +125,9 @@ export async function withdrawTestBalance(
amount,
);
+ await fetchFreshExchange(wex, req.exchangeBaseUrl);
+ await acceptExchangeTermsOfService(wex, req.exchangeBaseUrl);
+
const acceptResp = await acceptWithdrawalFromUri(wex, {
talerWithdrawUri: wresp.taler_withdraw_uri,
selectedExchange: exchangeBaseUrl,
diff --git a/packages/taler-wallet-core/src/wallet.ts b/packages/taler-wallet-core/src/wallet.ts
index bc2011883..a58e3aff0 100644
--- a/packages/taler-wallet-core/src/wallet.ts
+++ b/packages/taler-wallet-core/src/wallet.ts
@@ -516,7 +516,7 @@ async function dumpCoins(wex: WalletExecutionContext): Promise<CoinDumpJson> {
const coinsJson: CoinDumpJson = { coins: [] };
logger.info("dumping coins");
await wex.db.runReadOnlyTx(
- { storeNames: ["coins", "denominations"] },
+ { storeNames: ["coins", "coinHistory", "denominations"] },
async (tx) => {
const coins = await tx.coins.iter().toArray();
for (const c of coins) {
@@ -547,16 +547,18 @@ async function dumpCoins(wex: WalletExecutionContext): Promise<CoinDumpJson> {
logger.warn("no denomination found for coin");
continue;
}
+ const historyRec = await tx.coinHistory.get(c.coinPub);
coinsJson.coins.push({
- coin_pub: c.coinPub,
- denom_pub: denomInfo.denomPub,
- denom_pub_hash: c.denomPubHash,
- denom_value: denom.value,
- exchange_base_url: c.exchangeBaseUrl,
- refresh_parent_coin_pub: refreshParentCoinPub,
- withdrawal_reserve_pub: withdrawalReservePub,
- coin_status: c.status,
+ coinPub: c.coinPub,
+ denomPub: denomInfo.denomPub,
+ denomPubHash: c.denomPubHash,
+ denomValue: denom.value,
+ exchangeBaseUrl: c.exchangeBaseUrl,
+ refreshParentCoinPub: refreshParentCoinPub,
+ withdrawalReservePub: withdrawalReservePub,
+ coinStatus: c.status,
ageCommitmentProof: c.ageCommitmentProof,
+ history: historyRec ? historyRec.history : [],
});
}
},
diff --git a/packages/taler-wallet-webextension/src/wallet/DeveloperPage.tsx b/packages/taler-wallet-webextension/src/wallet/DeveloperPage.tsx
index 8f23c0685..9feb03714 100644
--- a/packages/taler-wallet-webextension/src/wallet/DeveloperPage.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/DeveloperPage.tsx
@@ -22,7 +22,7 @@ import {
LogLevel,
NotificationType,
ScopeType,
- stringifyWithdrawExchange
+ stringifyWithdrawExchange,
} from "@gnu-taler/taler-util";
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import { useTranslationContext } from "@gnu-taler/web-util/browser";
@@ -112,21 +112,21 @@ export function DeveloperPage(): VNode {
const currencies: { [ex: string]: string } = {};
const money_by_exchange = coins.reduce(
(prev, cur) => {
- const denom = Amounts.parseOrThrow(cur.denom_value);
- if (!prev[cur.exchange_base_url]) {
- prev[cur.exchange_base_url] = [];
- currencies[cur.exchange_base_url] = denom.currency;
+ const denom = Amounts.parseOrThrow(cur.denomValue);
+ if (!prev[cur.exchangeBaseUrl]) {
+ prev[cur.exchangeBaseUrl] = [];
+ currencies[cur.exchangeBaseUrl] = denom.currency;
}
- prev[cur.exchange_base_url].push({
+ prev[cur.exchangeBaseUrl].push({
// ageKeysCount: cur.ageCommitmentProof?.proof.privateKeys.length,
denom_value: denom.value,
denom_fraction: denom.fraction,
// remain_value: parseFloat(
// Amounts.stringifyValue(Amounts.parseOrThrow(cur.remaining_value)),
// ),
- status: cur.coin_status,
- from_refresh: cur.refresh_parent_coin_pub !== undefined,
- id: cur.coin_pub,
+ status: cur.coinStatus,
+ from_refresh: cur.refreshParentCoinPub !== undefined,
+ id: cur.coinPub,
});
return prev;
},
@@ -351,7 +351,7 @@ export function DeveloperPage(): VNode {
<a
href={new URL(`/keys`, e.exchangeBaseUrl).href}
target="_blank"
- rel="noreferrer"
+ rel="noreferrer"
>
{e.exchangeBaseUrl}
</a>