summaryrefslogtreecommitdiff
path: root/packages/taler-wallet-core/src/operations
diff options
context:
space:
mode:
authorFlorian Dold <florian.dold@gmail.com>2020-09-08 19:27:08 +0530
committerFlorian Dold <florian.dold@gmail.com>2020-09-08 19:27:08 +0530
commitb9e43e652e606706a81f39d4f888477580de79b0 (patch)
treed6840162173a9c97414c3662a7ad43bf7349f1d6 /packages/taler-wallet-core/src/operations
parentb063382d25d1ed8572ebe2f52bf54247379300d5 (diff)
downloadwallet-core-b9e43e652e606706a81f39d4f888477580de79b0.tar.gz
wallet-core-b9e43e652e606706a81f39d4f888477580de79b0.tar.bz2
wallet-core-b9e43e652e606706a81f39d4f888477580de79b0.zip
fix tipping and adjust DB
Diffstat (limited to 'packages/taler-wallet-core/src/operations')
-rw-r--r--packages/taler-wallet-core/src/operations/pending.ts1
-rw-r--r--packages/taler-wallet-core/src/operations/reserves.ts5
-rw-r--r--packages/taler-wallet-core/src/operations/tip.ts131
-rw-r--r--packages/taler-wallet-core/src/operations/transactions.ts98
-rw-r--r--packages/taler-wallet-core/src/operations/withdraw.ts9
5 files changed, 118 insertions, 126 deletions
diff --git a/packages/taler-wallet-core/src/operations/pending.ts b/packages/taler-wallet-core/src/operations/pending.ts
index 91a55c705..7dda1214d 100644
--- a/packages/taler-wallet-core/src/operations/pending.ts
+++ b/packages/taler-wallet-core/src/operations/pending.ts
@@ -286,7 +286,6 @@ async function gatherWithdrawalPending(
givesLifeness: true,
numCoinsTotal,
numCoinsWithdrawn,
- source: wsr.source,
withdrawalGroupId: wsr.withdrawalGroupId,
lastError: wsr.lastError,
retryInfo: wsr.retryInfo,
diff --git a/packages/taler-wallet-core/src/operations/reserves.ts b/packages/taler-wallet-core/src/operations/reserves.ts
index 69942fe94..b4fa3b23e 100644
--- a/packages/taler-wallet-core/src/operations/reserves.ts
+++ b/packages/taler-wallet-core/src/operations/reserves.ts
@@ -818,10 +818,7 @@ async function depleteReserve(
const withdrawalRecord: WithdrawalGroupRecord = {
withdrawalGroupId: withdrawalGroupId,
exchangeBaseUrl: newReserve.exchangeBaseUrl,
- source: {
- type: WithdrawalSourceType.Reserve,
- reservePub: newReserve.reservePub,
- },
+ reservePub: newReserve.reservePub,
rawWithdrawalAmount: withdrawAmount,
timestampStart: getTimestampNow(),
retryInfo: initRetryInfo(),
diff --git a/packages/taler-wallet-core/src/operations/tip.ts b/packages/taler-wallet-core/src/operations/tip.ts
index 6fe374bf0..6ccd262b0 100644
--- a/packages/taler-wallet-core/src/operations/tip.ts
+++ b/packages/taler-wallet-core/src/operations/tip.ts
@@ -31,6 +31,9 @@ import {
updateRetryInfoTimeout,
WithdrawalSourceType,
TipPlanchet,
+ CoinRecord,
+ CoinSourceType,
+ CoinStatus,
} from "../types/dbTypes";
import {
getExchangeWithdrawalInfo,
@@ -40,13 +43,14 @@ import {
} from "./withdraw";
import { updateExchangeFromUrl } from "./exchanges";
import { getRandomBytes, encodeCrock } from "../crypto/talerCrypto";
-import { guardOperationException } from "./errors";
+import { guardOperationException, makeErrorDetails } from "./errors";
import { NotificationType } from "../types/notifications";
import { getTimestampNow } from "../util/time";
import { readSuccessResponseJsonOrThrow } from "../util/http";
import { URL } from "../util/url";
import { Logger } from "../util/logging";
import { checkDbInvariant } from "../util/invariants";
+import { TalerErrorCode } from "../TalerErrorCode";
const logger = new Logger("operations/tip.ts");
@@ -99,7 +103,7 @@ export async function prepareTip(
walletTipId: walletTipId,
acceptedTimestamp: undefined,
rejectedTimestamp: undefined,
- amount,
+ tipAmountRaw: amount,
deadline: tipPickupStatus.expiration,
exchangeUrl: tipPickupStatus.exchange_url,
merchantBaseUrl: res.merchantBaseUrl,
@@ -109,10 +113,10 @@ export async function prepareTip(
response: undefined,
createdTimestamp: getTimestampNow(),
merchantTipId: res.merchantTipId,
- totalFees: Amounts.add(
+ tipAmountEffective: Amounts.sub(amount, Amounts.add(
withdrawDetails.overhead,
withdrawDetails.withdrawFee,
- ).amount,
+ ).amount).amount,
retryInfo: initRetryInfo(),
lastError: undefined,
denomsSel: denomSelectionInfoToState(selectedDenoms),
@@ -122,10 +126,10 @@ export async function prepareTip(
const tipStatus: PrepareTipResult = {
accepted: !!tipRecord && !!tipRecord.acceptedTimestamp,
- amount: Amounts.stringify(tipPickupStatus.tip_amount),
+ tipAmountRaw: Amounts.stringify(tipPickupStatus.tip_amount),
exchangeBaseUrl: tipPickupStatus.exchange_url,
expirationTimestamp: tipPickupStatus.expiration,
- totalFees: Amounts.stringify(tipRecord.totalFees),
+ tipAmountEffective: Amounts.stringify(tipRecord.tipAmountEffective),
walletTipId: tipRecord.walletTipId,
};
@@ -182,13 +186,13 @@ async function resetTipRetry(
async function processTipImpl(
ws: InternalWalletState,
- tipId: string,
+ walletTipId: string,
forceNow: boolean,
): Promise<void> {
if (forceNow) {
- await resetTipRetry(ws, tipId);
+ await resetTipRetry(ws, walletTipId);
}
- let tipRecord = await ws.db.get(Stores.tips, tipId);
+ let tipRecord = await ws.db.get(Stores.tips, walletTipId);
if (!tipRecord) {
return;
}
@@ -216,7 +220,7 @@ async function processTipImpl(
planchets.push(r);
}
}
- await ws.db.mutate(Stores.tips, tipId, (r) => {
+ await ws.db.mutate(Stores.tips, walletTipId, (r) => {
if (!r.planchets) {
r.planchets = planchets;
}
@@ -224,7 +228,7 @@ async function processTipImpl(
});
}
- tipRecord = await ws.db.get(Stores.tips, tipId);
+ tipRecord = await ws.db.get(Stores.tips, walletTipId);
checkDbInvariant(!!tipRecord, "tip record should be in database");
checkDbInvariant(!!tipRecord.planchets, "tip record should have planchets");
@@ -246,55 +250,68 @@ async function processTipImpl(
codecForTipResponse(),
);
- if (response.reserve_sigs.length !== tipRecord.planchets.length) {
+ if (response.blind_sigs.length !== tipRecord.planchets.length) {
throw Error("number of tip responses does not match requested planchets");
}
- const withdrawalGroupId = encodeCrock(getRandomBytes(32));
- const planchets: PlanchetRecord[] = [];
-
- for (let i = 0; i < tipRecord.planchets.length; i++) {
- const tipPlanchet = tipRecord.planchets[i];
- const coinEvHash = await ws.cryptoApi.hashEncoded(tipPlanchet.coinEv);
- const planchet: PlanchetRecord = {
- blindingKey: tipPlanchet.blindingKey,
- coinEv: tipPlanchet.coinEv,
- coinPriv: tipPlanchet.coinPriv,
- coinPub: tipPlanchet.coinPub,
- coinValue: tipPlanchet.coinValue,
- denomPub: tipPlanchet.denomPub,
- denomPubHash: tipPlanchet.denomPubHash,
- reservePub: response.reserve_pub,
- withdrawSig: response.reserve_sigs[i].reserve_sig,
- isFromTip: true,
- coinEvHash,
- coinIdx: i,
- withdrawalDone: false,
- withdrawalGroupId: withdrawalGroupId,
- lastError: undefined,
- };
- planchets.push(planchet);
- }
+ const newCoinRecords: CoinRecord[] = [];
- const withdrawalGroup: WithdrawalGroupRecord = {
- exchangeBaseUrl: tipRecord.exchangeUrl,
- source: {
- type: WithdrawalSourceType.Tip,
- tipId: tipRecord.walletTipId,
- },
- timestampStart: getTimestampNow(),
- withdrawalGroupId: withdrawalGroupId,
- rawWithdrawalAmount: tipRecord.amount,
- retryInfo: initRetryInfo(),
- timestampFinish: undefined,
- lastError: undefined,
- denomsSel: tipRecord.denomsSel,
- };
+ for (let i = 0; i < response.blind_sigs.length; i++) {
+ const blindedSig = response.blind_sigs[i].blind_sig;
+
+ const planchet = tipRecord.planchets[i];
+
+ const denomSig = await ws.cryptoApi.rsaUnblind(
+ blindedSig,
+ planchet.blindingKey,
+ planchet.denomPub,
+ );
+
+ const isValid = await ws.cryptoApi.rsaVerify(
+ planchet.coinPub,
+ denomSig,
+ planchet.denomPub,
+ );
+
+ if (!isValid) {
+ await ws.db.runWithWriteTransaction([Stores.planchets], async (tx) => {
+ const tipRecord = await tx.get(Stores.tips, walletTipId);
+ if (!tipRecord) {
+ return;
+ }
+ tipRecord.lastError = makeErrorDetails(
+ TalerErrorCode.WALLET_TIPPING_COIN_SIGNATURE_INVALID,
+ "invalid signature from the exchange (via merchant tip) after unblinding",
+ {},
+ );
+ await tx.put(Stores.tips, tipRecord);
+ });
+ return;
+ }
+
+ newCoinRecords.push({
+ blindingKey: planchet.blindingKey,
+ coinPriv: planchet.coinPriv,
+ coinPub: planchet.coinPub,
+ coinSource: {
+ type: CoinSourceType.Tip,
+ coinIndex: i,
+ walletTipId: walletTipId,
+ },
+ currentAmount: planchet.coinValue,
+ denomPub: planchet.denomPub,
+ denomPubHash: planchet.denomPubHash,
+ denomSig: denomSig,
+ exchangeBaseUrl: tipRecord.exchangeUrl,
+ status: CoinStatus.Fresh,
+ suspended: false,
+ });
+ }
await ws.db.runWithWriteTransaction(
- [Stores.tips, Stores.withdrawalGroups],
+ [Stores.coins, Stores.tips, Stores.withdrawalGroups],
async (tx) => {
- const tr = await tx.get(Stores.tips, tipId);
+ const tr = await tx.get(Stores.tips, walletTipId);
if (!tr) {
return;
}
@@ -303,16 +320,12 @@ async function processTipImpl(
}
tr.pickedUp = true;
tr.retryInfo = initRetryInfo(false);
-
await tx.put(Stores.tips, tr);
- await tx.put(Stores.withdrawalGroups, withdrawalGroup);
- for (const p of planchets) {
- await tx.put(Stores.planchets, p);
+ for (const cr of newCoinRecords) {
+ await tx.put(Stores.coins, cr);
}
},
);
-
- await processWithdrawGroup(ws, withdrawalGroupId);
}
export async function acceptTip(
diff --git a/packages/taler-wallet-core/src/operations/transactions.ts b/packages/taler-wallet-core/src/operations/transactions.ts
index 7a3228422..b5f77a190 100644
--- a/packages/taler-wallet-core/src/operations/transactions.ts
+++ b/packages/taler-wallet-core/src/operations/transactions.ts
@@ -116,63 +116,49 @@ export async function getTransactions(
return;
}
- switch (wsr.source.type) {
- case WithdrawalSourceType.Reserve:
- {
- const r = await tx.get(Stores.reserves, wsr.source.reservePub);
- if (!r) {
- break;
- }
- let amountRaw: AmountJson | undefined = undefined;
- if (wsr.withdrawalGroupId === r.initialWithdrawalGroupId) {
- amountRaw = r.instructedAmount;
- } else {
- amountRaw = wsr.denomsSel.totalWithdrawCost;
- }
- let withdrawalDetails: WithdrawalDetails;
- if (r.bankInfo) {
- withdrawalDetails = {
- type: WithdrawalType.TalerBankIntegrationApi,
- confirmed: true,
- bankConfirmationUrl: r.bankInfo.confirmUrl,
- };
- } else {
- const exchange = await tx.get(
- Stores.exchanges,
- r.exchangeBaseUrl,
- );
- if (!exchange) {
- // FIXME: report somehow
- break;
- }
- withdrawalDetails = {
- type: WithdrawalType.ManualTransfer,
- exchangePaytoUris:
- exchange.wireInfo?.accounts.map((x) => x.payto_uri) ?? [],
- };
- }
- transactions.push({
- type: TransactionType.Withdrawal,
- amountEffective: Amounts.stringify(
- wsr.denomsSel.totalCoinValue,
- ),
- amountRaw: Amounts.stringify(amountRaw),
- withdrawalDetails,
- exchangeBaseUrl: wsr.exchangeBaseUrl,
- pending: !wsr.timestampFinish,
- timestamp: wsr.timestampStart,
- transactionId: makeEventId(
- TransactionType.Withdrawal,
- wsr.withdrawalGroupId,
- ),
- ...(wsr.lastError ? { error: wsr.lastError } : {}),
- });
- }
- break;
- default:
- // Tips are reported via their own event
- break;
+ const r = await tx.get(Stores.reserves, wsr.reservePub);
+ if (!r) {
+ return;
+ }
+ let amountRaw: AmountJson | undefined = undefined;
+ if (wsr.withdrawalGroupId === r.initialWithdrawalGroupId) {
+ amountRaw = r.instructedAmount;
+ } else {
+ amountRaw = wsr.denomsSel.totalWithdrawCost;
+ }
+ let withdrawalDetails: WithdrawalDetails;
+ if (r.bankInfo) {
+ withdrawalDetails = {
+ type: WithdrawalType.TalerBankIntegrationApi,
+ confirmed: true,
+ bankConfirmationUrl: r.bankInfo.confirmUrl,
+ };
+ } else {
+ const exchange = await tx.get(Stores.exchanges, r.exchangeBaseUrl);
+ if (!exchange) {
+ // FIXME: report somehow
+ return;
+ }
+ withdrawalDetails = {
+ type: WithdrawalType.ManualTransfer,
+ exchangePaytoUris:
+ exchange.wireInfo?.accounts.map((x) => x.payto_uri) ?? [],
+ };
}
+ transactions.push({
+ type: TransactionType.Withdrawal,
+ amountEffective: Amounts.stringify(wsr.denomsSel.totalCoinValue),
+ amountRaw: Amounts.stringify(amountRaw),
+ withdrawalDetails,
+ exchangeBaseUrl: wsr.exchangeBaseUrl,
+ pending: !wsr.timestampFinish,
+ timestamp: wsr.timestampStart,
+ transactionId: makeEventId(
+ TransactionType.Withdrawal,
+ wsr.withdrawalGroupId,
+ ),
+ ...(wsr.lastError ? { error: wsr.lastError } : {}),
+ });
});
// Report pending withdrawals based on reserves that
diff --git a/packages/taler-wallet-core/src/operations/withdraw.ts b/packages/taler-wallet-core/src/operations/withdraw.ts
index 4070e39f4..eec92ba29 100644
--- a/packages/taler-wallet-core/src/operations/withdraw.ts
+++ b/packages/taler-wallet-core/src/operations/withdraw.ts
@@ -242,12 +242,9 @@ async function processPlanchetGenerate(
if (!denom) {
throw Error("invariant violated");
}
- if (withdrawalGroup.source.type != WithdrawalSourceType.Reserve) {
- throw Error("invariant violated");
- }
const reserve = await ws.db.get(
Stores.reserves,
- withdrawalGroup.source.reservePub,
+ withdrawalGroup.reservePub,
);
if (!reserve) {
throw Error("invariant violated");
@@ -420,7 +417,7 @@ async function processPlanchetVerifyAndStoreCoin(
if (!isValid) {
await ws.db.runWithWriteTransaction([Stores.planchets], async (tx) => {
- let planchet = await ws.db.getIndexed(Stores.planchets.byGroupAndIndex, [
+ let planchet = await tx.getIndexed(Stores.planchets.byGroupAndIndex, [
withdrawalGroupId,
coinIdx,
]);
@@ -700,7 +697,7 @@ async function processWithdrawGroupImpl(
if (finishedForFirstTime) {
ws.notify({
type: NotificationType.WithdrawGroupFinished,
- withdrawalSource: withdrawalGroup.source,
+ reservePub: withdrawalGroup.reservePub,
});
}
}