aboutsummaryrefslogtreecommitdiff
path: root/packages
diff options
context:
space:
mode:
authorFlorian Dold <florian@dold.me>2022-01-11 12:48:32 +0100
committerFlorian Dold <florian@dold.me>2022-01-11 14:48:02 +0100
commita05e891d6e1468fdd99f710301e286857a46aea3 (patch)
tree5ea22817eb2cec888ff52a80dcd3a9986bd768fa /packages
parentfb22009ec4799a624f00c228fbd7435b44c1cbac (diff)
towards new recoup API
Diffstat (limited to 'packages')
-rw-r--r--packages/taler-util/src/talerCrypto.ts1
-rw-r--r--packages/taler-util/src/talerTypes.ts310
-rw-r--r--packages/taler-wallet-cli/src/harness/harness.ts8
-rw-r--r--packages/taler-wallet-cli/src/index.ts8
-rw-r--r--packages/taler-wallet-core/src/crypto/cryptoTypes.ts34
-rw-r--r--packages/taler-wallet-core/src/crypto/workers/cryptoApi.ts22
-rw-r--r--packages/taler-wallet-core/src/crypto/workers/cryptoImplementation.ts73
-rw-r--r--packages/taler-wallet-core/src/operations/exchanges.ts4
-rw-r--r--packages/taler-wallet-core/src/operations/recoup.ts37
-rw-r--r--packages/taler-wallet-core/src/operations/reserves.ts10
10 files changed, 326 insertions, 181 deletions
diff --git a/packages/taler-util/src/talerCrypto.ts b/packages/taler-util/src/talerCrypto.ts
index d96c23236..5f1c2e8cc 100644
--- a/packages/taler-util/src/talerCrypto.ts
+++ b/packages/taler-util/src/talerCrypto.ts
@@ -479,6 +479,7 @@ export enum TalerSignaturePurpose {
MERCHANT_CONTRACT = 1101,
WALLET_COIN_RECOUP = 1203,
WALLET_COIN_LINK = 1204,
+ WALLET_COIN_RECOUP_REFRESH = 1206,
EXCHANGE_CONFIRM_RECOUP = 1039,
EXCHANGE_CONFIRM_RECOUP_REFRESH = 1041,
ANASTASIS_POLICY_UPLOAD = 1400,
diff --git a/packages/taler-util/src/talerTypes.ts b/packages/taler-util/src/talerTypes.ts
index e7d8a8c18..5f72e08ce 100644
--- a/packages/taler-util/src/talerTypes.ts
+++ b/packages/taler-util/src/talerTypes.ts
@@ -46,7 +46,7 @@ import {
Duration,
codecForDuration,
} from "./time.js";
-import { codecForAmountString } from "./amounts.js";
+import { Amounts, codecForAmountString } from "./amounts.js";
/**
* Denomination as found in the /keys response from the exchange.
@@ -149,9 +149,6 @@ export class ExchangeAuditor {
denomination_keys: AuditorDenomSig[];
}
-/**
- * Request that we send to the exchange to get a payback.
- */
export interface RecoupRequest {
/**
* Hashed enomination public key of the coin we want to get
@@ -161,31 +158,57 @@ export interface RecoupRequest {
/**
* Signature over the coin public key by the denomination.
- *
+ *
* The string variant is for the legacy exchange protocol.
*/
denom_sig: UnblindedSignature | string;
/**
- * Coin public key of the coin we want to refund.
+ * Blinding key that was used during withdraw,
+ * used to prove that we were actually withdrawing the coin.
*/
- coin_pub: string;
+ coin_blind_key_secret: string;
/**
- * Blinding key that was used during withdraw,
- * used to prove that we were actually withdrawing the coin.
+ * Signature of TALER_RecoupRequestPS created with the coin's private key.
+ */
+ coin_sig: string;
+
+ /**
+ * Amount being recouped.
+ */
+ amount: AmountString;
+}
+
+export interface RecoupRefreshRequest {
+ /**
+ * Hashed enomination public key of the coin we want to get
+ * paid back.
+ */
+ denom_pub_hash: string;
+
+ /**
+ * Signature over the coin public key by the denomination.
+ *
+ * The string variant is for the legacy exchange protocol.
+ */
+ denom_sig: UnblindedSignature | string;
+
+ /**
+ * Coin's blinding factor.
*/
coin_blind_key_secret: string;
/**
- * Signature made by the coin, authorizing the payback.
+ * Signature of TALER_RecoupRefreshRequestPS created with
+ * the coin's private key.
*/
coin_sig: string;
/**
- * Was the coin refreshed (and thus the recoup should go to the old coin)?
+ * Amount being recouped.
*/
- refreshed: boolean;
+ amount: AmountString;
}
/**
@@ -1131,10 +1154,11 @@ export const codecForLegacyRsaDenominationPubKey = () =>
.property("rsa_public_key", codecForString())
.build("LegacyRsaDenominationPubKey");
-export const codecForBankWithdrawalOperationPostResponse = (): Codec<BankWithdrawalOperationPostResponse> =>
- buildCodecForObject<BankWithdrawalOperationPostResponse>()
- .property("transfer_done", codecForBoolean())
- .build("BankWithdrawalOperationPostResponse");
+export const codecForBankWithdrawalOperationPostResponse =
+ (): Codec<BankWithdrawalOperationPostResponse> =>
+ buildCodecForObject<BankWithdrawalOperationPostResponse>()
+ .property("transfer_done", codecForBoolean())
+ .build("BankWithdrawalOperationPostResponse");
export type AmountString = string;
export type Base32String = string;
@@ -1213,8 +1237,8 @@ export const codecForTax = (): Codec<Tax> =>
.property("tax", codecForString())
.build("Tax");
-export const codecForInternationalizedString = (): Codec<InternationalizedString> =>
- codecForMap(codecForString());
+export const codecForInternationalizedString =
+ (): Codec<InternationalizedString> => codecForMap(codecForString());
export const codecForProduct = (): Codec<Product> =>
buildCodecForObject<Product>()
@@ -1262,30 +1286,33 @@ export const codecForContractTerms = (): Codec<ContractTerms> =>
.property("extra", codecForAny())
.build("ContractTerms");
-export const codecForMerchantRefundPermission = (): Codec<MerchantAbortPayRefundDetails> =>
- buildCodecForObject<MerchantAbortPayRefundDetails>()
- .property("refund_amount", codecForAmountString())
- .property("refund_fee", codecForAmountString())
- .property("coin_pub", codecForString())
- .property("rtransaction_id", codecForNumber())
- .property("exchange_http_status", codecForNumber())
- .property("exchange_code", codecOptional(codecForNumber()))
- .property("exchange_reply", codecOptional(codecForAny()))
- .property("exchange_sig", codecOptional(codecForString()))
- .property("exchange_pub", codecOptional(codecForString()))
- .build("MerchantRefundPermission");
-
-export const codecForMerchantRefundResponse = (): Codec<MerchantRefundResponse> =>
- buildCodecForObject<MerchantRefundResponse>()
- .property("merchant_pub", codecForString())
- .property("h_contract_terms", codecForString())
- .property("refunds", codecForList(codecForMerchantRefundPermission()))
- .build("MerchantRefundResponse");
-
-export const codecForMerchantBlindSigWrapperV1 = (): Codec<MerchantBlindSigWrapperV1> =>
- buildCodecForObject<MerchantBlindSigWrapperV1>()
- .property("blind_sig", codecForString())
- .build("BlindSigWrapper");
+export const codecForMerchantRefundPermission =
+ (): Codec<MerchantAbortPayRefundDetails> =>
+ buildCodecForObject<MerchantAbortPayRefundDetails>()
+ .property("refund_amount", codecForAmountString())
+ .property("refund_fee", codecForAmountString())
+ .property("coin_pub", codecForString())
+ .property("rtransaction_id", codecForNumber())
+ .property("exchange_http_status", codecForNumber())
+ .property("exchange_code", codecOptional(codecForNumber()))
+ .property("exchange_reply", codecOptional(codecForAny()))
+ .property("exchange_sig", codecOptional(codecForString()))
+ .property("exchange_pub", codecOptional(codecForString()))
+ .build("MerchantRefundPermission");
+
+export const codecForMerchantRefundResponse =
+ (): Codec<MerchantRefundResponse> =>
+ buildCodecForObject<MerchantRefundResponse>()
+ .property("merchant_pub", codecForString())
+ .property("h_contract_terms", codecForString())
+ .property("refunds", codecForList(codecForMerchantRefundPermission()))
+ .build("MerchantRefundResponse");
+
+export const codecForMerchantBlindSigWrapperV1 =
+ (): Codec<MerchantBlindSigWrapperV1> =>
+ buildCodecForObject<MerchantBlindSigWrapperV1>()
+ .property("blind_sig", codecForString())
+ .build("BlindSigWrapper");
export const codecForMerchantTipResponseV1 = (): Codec<MerchantTipResponseV1> =>
buildCodecForObject<MerchantTipResponseV1>()
@@ -1365,17 +1392,18 @@ export const codecForCheckPaymentResponse = (): Codec<CheckPaymentResponse> =>
.property("contract_url", codecOptional(codecForString()))
.build("CheckPaymentResponse");
-export const codecForWithdrawOperationStatusResponse = (): Codec<WithdrawOperationStatusResponse> =>
- buildCodecForObject<WithdrawOperationStatusResponse>()
- .property("selection_done", codecForBoolean())
- .property("transfer_done", codecForBoolean())
- .property("aborted", codecForBoolean())
- .property("amount", codecForString())
- .property("sender_wire", codecOptional(codecForString()))
- .property("suggested_exchange", codecOptional(codecForString()))
- .property("confirm_transfer_url", codecOptional(codecForString()))
- .property("wire_types", codecForList(codecForString()))
- .build("WithdrawOperationStatusResponse");
+export const codecForWithdrawOperationStatusResponse =
+ (): Codec<WithdrawOperationStatusResponse> =>
+ buildCodecForObject<WithdrawOperationStatusResponse>()
+ .property("selection_done", codecForBoolean())
+ .property("transfer_done", codecForBoolean())
+ .property("aborted", codecForBoolean())
+ .property("amount", codecForString())
+ .property("sender_wire", codecOptional(codecForString()))
+ .property("suggested_exchange", codecOptional(codecForString()))
+ .property("confirm_transfer_url", codecOptional(codecForString()))
+ .property("wire_types", codecForList(codecForString()))
+ .build("WithdrawOperationStatusResponse");
export const codecForTipPickupGetResponse = (): Codec<TipPickupGetResponse> =>
buildCodecForObject<TipPickupGetResponse>()
@@ -1419,60 +1447,67 @@ export const codecForExchangeRevealItem = (): Codec<ExchangeRevealItem> =>
)
.build("ExchangeRevealItem");
-export const codecForExchangeRevealResponse = (): Codec<ExchangeRevealResponse> =>
- buildCodecForObject<ExchangeRevealResponse>()
- .property("ev_sigs", codecForList(codecForExchangeRevealItem()))
- .build("ExchangeRevealResponse");
-
-export const codecForMerchantCoinRefundSuccessStatus = (): Codec<MerchantCoinRefundSuccessStatus> =>
- buildCodecForObject<MerchantCoinRefundSuccessStatus>()
- .property("type", codecForConstString("success"))
- .property("coin_pub", codecForString())
- .property("exchange_status", codecForConstNumber(200))
- .property("exchange_sig", codecForString())
- .property("rtransaction_id", codecForNumber())
- .property("refund_amount", codecForString())
- .property("exchange_pub", codecForString())
- .property("execution_time", codecForTimestamp)
- .build("MerchantCoinRefundSuccessStatus");
-
-export const codecForMerchantCoinRefundFailureStatus = (): Codec<MerchantCoinRefundFailureStatus> =>
- buildCodecForObject<MerchantCoinRefundFailureStatus>()
- .property("type", codecForConstString("failure"))
- .property("coin_pub", codecForString())
- .property("exchange_status", codecForNumber())
- .property("rtransaction_id", codecForNumber())
- .property("refund_amount", codecForString())
- .property("exchange_code", codecOptional(codecForNumber()))
- .property("exchange_reply", codecOptional(codecForAny()))
- .property("execution_time", codecForTimestamp)
- .build("MerchantCoinRefundFailureStatus");
-
-export const codecForMerchantCoinRefundStatus = (): Codec<MerchantCoinRefundStatus> =>
- buildCodecForUnion<MerchantCoinRefundStatus>()
- .discriminateOn("type")
- .alternative("success", codecForMerchantCoinRefundSuccessStatus())
- .alternative("failure", codecForMerchantCoinRefundFailureStatus())
- .build("MerchantCoinRefundStatus");
-
-export const codecForMerchantOrderStatusPaid = (): Codec<MerchantOrderStatusPaid> =>
- buildCodecForObject<MerchantOrderStatusPaid>()
- .property("refund_amount", codecForString())
- .property("refunded", codecForBoolean())
- .build("MerchantOrderStatusPaid");
-
-export const codecForMerchantOrderRefundPickupResponse = (): Codec<MerchantOrderRefundResponse> =>
- buildCodecForObject<MerchantOrderRefundResponse>()
- .property("merchant_pub", codecForString())
- .property("refund_amount", codecForString())
- .property("refunds", codecForList(codecForMerchantCoinRefundStatus()))
- .build("MerchantOrderRefundPickupResponse");
-
-export const codecForMerchantOrderStatusUnpaid = (): Codec<MerchantOrderStatusUnpaid> =>
- buildCodecForObject<MerchantOrderStatusUnpaid>()
- .property("taler_pay_uri", codecForString())
- .property("already_paid_order_id", codecOptional(codecForString()))
- .build("MerchantOrderStatusUnpaid");
+export const codecForExchangeRevealResponse =
+ (): Codec<ExchangeRevealResponse> =>
+ buildCodecForObject<ExchangeRevealResponse>()
+ .property("ev_sigs", codecForList(codecForExchangeRevealItem()))
+ .build("ExchangeRevealResponse");
+
+export const codecForMerchantCoinRefundSuccessStatus =
+ (): Codec<MerchantCoinRefundSuccessStatus> =>
+ buildCodecForObject<MerchantCoinRefundSuccessStatus>()
+ .property("type", codecForConstString("success"))
+ .property("coin_pub", codecForString())
+ .property("exchange_status", codecForConstNumber(200))
+ .property("exchange_sig", codecForString())
+ .property("rtransaction_id", codecForNumber())
+ .property("refund_amount", codecForString())
+ .property("exchange_pub", codecForString())
+ .property("execution_time", codecForTimestamp)
+ .build("MerchantCoinRefundSuccessStatus");
+
+export const codecForMerchantCoinRefundFailureStatus =
+ (): Codec<MerchantCoinRefundFailureStatus> =>
+ buildCodecForObject<MerchantCoinRefundFailureStatus>()
+ .property("type", codecForConstString("failure"))
+ .property("coin_pub", codecForString())
+ .property("exchange_status", codecForNumber())
+ .property("rtransaction_id", codecForNumber())
+ .property("refund_amount", codecForString())
+ .property("exchange_code", codecOptional(codecForNumber()))
+ .property("exchange_reply", codecOptional(codecForAny()))
+ .property("execution_time", codecForTimestamp)
+ .build("MerchantCoinRefundFailureStatus");
+
+export const codecForMerchantCoinRefundStatus =
+ (): Codec<MerchantCoinRefundStatus> =>
+ buildCodecForUnion<MerchantCoinRefundStatus>()
+ .discriminateOn("type")
+ .alternative("success", codecForMerchantCoinRefundSuccessStatus())
+ .alternative("failure", codecForMerchantCoinRefundFailureStatus())
+ .build("MerchantCoinRefundStatus");
+
+export const codecForMerchantOrderStatusPaid =
+ (): Codec<MerchantOrderStatusPaid> =>
+ buildCodecForObject<MerchantOrderStatusPaid>()
+ .property("refund_amount", codecForString())
+ .property("refunded", codecForBoolean())
+ .build("MerchantOrderStatusPaid");
+
+export const codecForMerchantOrderRefundPickupResponse =
+ (): Codec<MerchantOrderRefundResponse> =>
+ buildCodecForObject<MerchantOrderRefundResponse>()
+ .property("merchant_pub", codecForString())
+ .property("refund_amount", codecForString())
+ .property("refunds", codecForList(codecForMerchantCoinRefundStatus()))
+ .build("MerchantOrderRefundPickupResponse");
+
+export const codecForMerchantOrderStatusUnpaid =
+ (): Codec<MerchantOrderStatusUnpaid> =>
+ buildCodecForObject<MerchantOrderStatusUnpaid>()
+ .property("taler_pay_uri", codecForString())
+ .property("already_paid_order_id", codecOptional(codecForString()))
+ .build("MerchantOrderStatusUnpaid");
export interface AbortRequest {
// hash of the order's contract terms (this is used to authenticate the
@@ -1550,28 +1585,31 @@ export interface MerchantAbortPayRefundSuccessStatus {
exchange_pub: string;
}
-export const codecForMerchantAbortPayRefundSuccessStatus = (): Codec<MerchantAbortPayRefundSuccessStatus> =>
- buildCodecForObject<MerchantAbortPayRefundSuccessStatus>()
- .property("exchange_pub", codecForString())
- .property("exchange_sig", codecForString())
- .property("exchange_status", codecForConstNumber(200))
- .property("type", codecForConstString("success"))
- .build("MerchantAbortPayRefundSuccessStatus");
-
-export const codecForMerchantAbortPayRefundFailureStatus = (): Codec<MerchantAbortPayRefundFailureStatus> =>
- buildCodecForObject<MerchantAbortPayRefundFailureStatus>()
- .property("exchange_code", codecForNumber())
- .property("exchange_reply", codecForAny())
- .property("exchange_status", codecForNumber())
- .property("type", codecForConstString("failure"))
- .build("MerchantAbortPayRefundFailureStatus");
-
-export const codecForMerchantAbortPayRefundStatus = (): Codec<MerchantAbortPayRefundStatus> =>
- buildCodecForUnion<MerchantAbortPayRefundStatus>()
- .discriminateOn("type")
- .alternative("success", codecForMerchantAbortPayRefundSuccessStatus())
- .alternative("failure", codecForMerchantAbortPayRefundFailureStatus())
- .build("MerchantAbortPayRefundStatus");
+export const codecForMerchantAbortPayRefundSuccessStatus =
+ (): Codec<MerchantAbortPayRefundSuccessStatus> =>
+ buildCodecForObject<MerchantAbortPayRefundSuccessStatus>()
+ .property("exchange_pub", codecForString())
+ .property("exchange_sig", codecForString())
+ .property("exchange_status", codecForConstNumber(200))
+ .property("type", codecForConstString("success"))
+ .build("MerchantAbortPayRefundSuccessStatus");
+
+export const codecForMerchantAbortPayRefundFailureStatus =
+ (): Codec<MerchantAbortPayRefundFailureStatus> =>
+ buildCodecForObject<MerchantAbortPayRefundFailureStatus>()
+ .property("exchange_code", codecForNumber())
+ .property("exchange_reply", codecForAny())
+ .property("exchange_status", codecForNumber())
+ .property("type", codecForConstString("failure"))
+ .build("MerchantAbortPayRefundFailureStatus");
+
+export const codecForMerchantAbortPayRefundStatus =
+ (): Codec<MerchantAbortPayRefundStatus> =>
+ buildCodecForUnion<MerchantAbortPayRefundStatus>()
+ .discriminateOn("type")
+ .alternative("success", codecForMerchantAbortPayRefundSuccessStatus())
+ .alternative("failure", codecForMerchantAbortPayRefundFailureStatus())
+ .build("MerchantAbortPayRefundStatus");
export interface TalerConfigResponse {
name: string;
@@ -1614,13 +1652,13 @@ export interface MerchantConfigResponse {
version: string;
}
-export const codecForMerchantConfigResponse = (): Codec<MerchantConfigResponse> =>
- buildCodecForObject<MerchantConfigResponse>()
- .property("currency", codecForString())
- .property("name", codecForString())
- .property("version", codecForString())
- .build("MerchantConfigResponse");
-
+export const codecForMerchantConfigResponse =
+ (): Codec<MerchantConfigResponse> =>
+ buildCodecForObject<MerchantConfigResponse>()
+ .property("currency", codecForString())
+ .property("name", codecForString())
+ .property("version", codecForString())
+ .build("MerchantConfigResponse");
export enum ExchangeProtocolVersion {
V9 = 9,
diff --git a/packages/taler-wallet-cli/src/harness/harness.ts b/packages/taler-wallet-cli/src/harness/harness.ts
index 040bd5a6f..35162065d 100644
--- a/packages/taler-wallet-cli/src/harness/harness.ts
+++ b/packages/taler-wallet-cli/src/harness/harness.ts
@@ -2031,9 +2031,9 @@ export class WalletCli {
`wallet-${self.name}`,
`taler-wallet-cli ${
self.timetravelArg ?? ""
- } --no-throttle --wallet-db '${self.dbfile}' api '${op}' ${shellWrap(
- JSON.stringify(payload),
- )}`,
+ } --no-throttle -LTRACE --wallet-db '${
+ self.dbfile
+ }' api '${op}' ${shellWrap(JSON.stringify(payload))}`,
);
console.log("--- wallet core response ---");
console.log(resp);
@@ -2080,6 +2080,7 @@ export class WalletCli {
[
"--no-throttle",
...this.timetravelArgArr,
+ "-LTRACE",
"--wallet-db",
this.dbfile,
"run-until-done",
@@ -2095,6 +2096,7 @@ export class WalletCli {
"taler-wallet-cli",
[
"--no-throttle",
+ "-LTRACE",
...this.timetravelArgArr,
"--wallet-db",
this.dbfile,
diff --git a/packages/taler-wallet-cli/src/index.ts b/packages/taler-wallet-cli/src/index.ts
index b57e73a1c..22a2d8552 100644
--- a/packages/taler-wallet-cli/src/index.ts
+++ b/packages/taler-wallet-cli/src/index.ts
@@ -249,6 +249,7 @@ walletCli
.action(async (args) => {
await withWallet(args, async (wallet) => {
let requestJson;
+ logger.info(`handling 'api' request (${args.api.operation})`);
try {
requestJson = JSON.parse(args.api.request);
} catch (e) {
@@ -293,12 +294,6 @@ walletCli
});
});
-async function asyncSleep(milliSeconds: number): Promise<void> {
- return new Promise<void>((resolve, reject) => {
- setTimeout(() => resolve(), milliSeconds);
- });
-}
-
walletCli
.subcommand("runPendingOpt", "run-pending", {
help: "Run pending operations.",
@@ -330,6 +325,7 @@ walletCli
.maybeOption("maxRetries", ["--max-retries"], clk.INT)
.action(async (args) => {
await withWallet(args, async (wallet) => {
+ logger.info("running until pending operations are finished");
await wallet.ws.runTaskLoop({
maxRetries: args.finishPendingOpt.maxRetries,
stopWhenDone: true,
diff --git a/packages/taler-wallet-core/src/crypto/cryptoTypes.ts b/packages/taler-wallet-core/src/crypto/cryptoTypes.ts
index 9b72dfbe2..5351815a7 100644
--- a/packages/taler-wallet-core/src/crypto/cryptoTypes.ts
+++ b/packages/taler-wallet-core/src/crypto/cryptoTypes.ts
@@ -27,7 +27,13 @@
/**
* Imports.
*/
-import { AmountJson, DenominationPubKey, ExchangeProtocolVersion } from "@gnu-taler/taler-util";
+import {
+ AmountJson,
+ AmountString,
+ DenominationPubKey,
+ ExchangeProtocolVersion,
+ UnblindedSignature,
+} from "@gnu-taler/taler-util";
export interface RefreshNewDenomInfo {
count: number;
@@ -140,3 +146,29 @@ export interface SignTrackTransactionRequest {
merchantPriv: string;
merchantPub: string;
}
+
+/**
+ * Request to create a recoup request payload.
+ */
+export interface CreateRecoupReqRequest {
+ coinPub: string;
+ coinPriv: string;
+ blindingKey: string;
+ denomPub: DenominationPubKey;
+ denomPubHash: string;
+ denomSig: UnblindedSignature;
+ recoupAmount: AmountJson;
+}
+
+/**
+ * Request to create a recoup-refresh request payload.
+ */
+export interface CreateRecoupRefreshReqRequest {
+ coinPub: string;
+ coinPriv: string;
+ blindingKey: string;
+ denomPub: DenominationPubKey;
+ denomPubHash: string;
+ denomSig: UnblindedSignature;
+ recoupAmount: AmountJson;
+}
diff --git a/packages/taler-wallet-core/src/crypto/workers/cryptoApi.ts b/packages/taler-wallet-core/src/crypto/workers/cryptoApi.ts
index e88b64c3c..29c2553a5 100644
--- a/packages/taler-wallet-core/src/crypto/workers/cryptoApi.ts
+++ b/packages/taler-wallet-core/src/crypto/workers/cryptoApi.ts
@@ -26,7 +26,11 @@ import { CoinRecord, DenominationRecord, WireFee } from "../../db.js";
import { CryptoWorker } from "./cryptoWorkerInterface.js";
-import { RecoupRequest, CoinDepositPermission } from "@gnu-taler/taler-util";
+import {
+ CoinDepositPermission,
+ RecoupRefreshRequest,
+ RecoupRequest,
+} from "@gnu-taler/taler-util";
import {
BenchmarkResult,
@@ -39,6 +43,8 @@ import {
import * as timer from "../../util/timer.js";
import { Logger } from "@gnu-taler/taler-util";
import {
+ CreateRecoupRefreshReqRequest,
+ CreateRecoupReqRequest,
DerivedRefreshSession,
DerivedTipPlanchet,
DeriveRefreshSessionRequest,
@@ -421,8 +427,18 @@ export class CryptoApi {
);
}
- createRecoupRequest(coin: CoinRecord): Promise<RecoupRequest> {
- return this.doRpc<RecoupRequest>("createRecoupRequest", 1, coin);
+ createRecoupRequest(req: CreateRecoupReqRequest): Promise<RecoupRequest> {
+ return this.doRpc<RecoupRequest>("createRecoupRequest", 1, req);
+ }
+
+ createRecoupRefreshRequest(
+ req: CreateRecoupRefreshReqRequest,
+ ): Promise<RecoupRefreshRequest> {
+ return this.doRpc<RecoupRefreshRequest>(
+ "createRecoupRefreshRequest",
+ 1,
+ req,
+ );
}
deriveRefreshSession(
diff --git a/packages/taler-wallet-core/src/crypto/workers/cryptoImplementation.ts b/packages/taler-wallet-core/src/crypto/workers/cryptoImplementation.ts
index 9e2dc18f3..b366fa9ec 100644
--- a/packages/taler-wallet-core/src/crypto/workers/cryptoImplementation.ts
+++ b/packages/taler-wallet-core/src/crypto/workers/cryptoImplementation.ts
@@ -25,12 +25,7 @@
*/
// FIXME: Crypto should not use DB Types!
-import {
- CoinRecord,
- DenominationRecord,
- WireFee,
- CoinSourceType,
-} from "../../db.js";
+import { DenominationRecord, WireFee } from "../../db.js";
import {
buildSigPS,
@@ -39,6 +34,7 @@ import {
ExchangeProtocolVersion,
FreshCoin,
hashDenomPub,
+ RecoupRefreshRequest,
RecoupRequest,
RefreshPlanchetInfo,
TalerSignaturePurpose,
@@ -78,6 +74,8 @@ import { Timestamp, timestampTruncateToSecond } from "@gnu-taler/taler-util";
import { Logger } from "@gnu-taler/taler-util";
import {
+ CreateRecoupRefreshReqRequest,
+ CreateRecoupReqRequest,
DerivedRefreshSession,
DerivedTipPlanchet,
DeriveRefreshSessionRequest,
@@ -261,33 +259,64 @@ export class CryptoImplementation {
/**
* Create and sign a message to recoup a coin.
*/
- createRecoupRequest(coin: CoinRecord): RecoupRequest {
+ createRecoupRequest(req: CreateRecoupReqRequest): RecoupRequest {
const p = buildSigPS(TalerSignaturePurpose.WALLET_COIN_RECOUP)
- .put(decodeCrock(coin.coinPub))
- .put(decodeCrock(coin.denomPubHash))
- .put(decodeCrock(coin.blindingKey))
+ .put(decodeCrock(req.denomPubHash))
+ .put(decodeCrock(req.blindingKey))
+ .put(amountToBuffer(Amounts.jsonifyAmount(req.recoupAmount)))
.build();
- const coinPriv = decodeCrock(coin.coinPriv);
+ const coinPriv = decodeCrock(req.coinPriv);
const coinSig = eddsaSign(p, coinPriv);
- if (coin.denomPub.cipher === DenomKeyType.LegacyRsa) {
+ if (req.denomPub.cipher === DenomKeyType.LegacyRsa) {
const paybackRequest: RecoupRequest = {
- coin_blind_key_secret: coin.blindingKey,
- coin_pub: coin.coinPub,
+ coin_blind_key_secret: req.blindingKey,
coin_sig: encodeCrock(coinSig),
- denom_pub_hash: coin.denomPubHash,
- denom_sig: coin.denomSig.rsa_signature,
- refreshed: coin.coinSource.type === CoinSourceType.Refresh,
+ denom_pub_hash: req.denomPubHash,
+ denom_sig: req.denomSig.rsa_signature,
+ amount: Amounts.stringify(req.recoupAmount),
};
return paybackRequest;
} else {
const paybackRequest: RecoupRequest = {
- coin_blind_key_secret: coin.blindingKey,
- coin_pub: coin.coinPub,
+ coin_blind_key_secret: req.blindingKey,
+ coin_sig: encodeCrock(coinSig),
+ denom_pub_hash: req.denomPubHash,
+ denom_sig: req.denomSig,
+ amount: Amounts.stringify(req.recoupAmount),
+ };
+ return paybackRequest;
+ }
+ }
+
+ /**
+ * Create and sign a message to recoup a coin.
+ */
+ createRecoupRefreshRequest(req: CreateRecoupRefreshReqRequest): RecoupRefreshRequest {
+ const p = buildSigPS(TalerSignaturePurpose.WALLET_COIN_RECOUP_REFRESH)
+ .put(decodeCrock(req.denomPubHash))
+ .put(decodeCrock(req.blindingKey))
+ .put(amountToBuffer(Amounts.jsonifyAmount(req.recoupAmount)))
+ .build();
+
+ const coinPriv = decodeCrock(req.coinPriv);
+ const coinSig = eddsaSign(p, coinPriv);
+ if (req.denomPub.cipher === DenomKeyType.LegacyRsa) {
+ const paybackRequest: RecoupRefreshRequest = {
+ coin_blind_key_secret: req.blindingKey,
+ coin_sig: encodeCrock(coinSig),
+ denom_pub_hash: req.denomPubHash,
+ denom_sig: req.denomSig.rsa_signature,
+ amount: Amounts.stringify(req.recoupAmount),
+ };
+ return paybackRequest;
+ } else {
+ const paybackRequest: RecoupRefreshRequest = {
+ coin_blind_key_secret: req.blindingKey,
coin_sig: encodeCrock(coinSig),
- denom_pub_hash: coin.denomPubHash,
- denom_sig: coin.denomSig,
- refreshed: coin.coinSource.type === CoinSourceType.Refresh,
+ denom_pub_hash: req.denomPubHash,
+ denom_sig: req.denomSig,
+ amount: Amounts.stringify(req.recoupAmount),
};
return paybackRequest;
}
diff --git a/packages/taler-wallet-core/src/operations/exchanges.ts b/packages/taler-wallet-core/src/operations/exchanges.ts
index 987031810..2975c860f 100644
--- a/packages/taler-wallet-core/src/operations/exchanges.ts
+++ b/packages/taler-wallet-core/src/operations/exchanges.ts
@@ -651,7 +651,7 @@ async function updateExchangeFromUrlImpl(
logger.trace("denom already revoked");
continue;
}
- logger.trace("revoking denom", recoupInfo.h_denom_pub);
+ logger.info("revoking denom", recoupInfo.h_denom_pub);
oldDenom.isRevoked = true;
await tx.denominations.put(oldDenom);
const affectedCoins = await tx.coins.indexes.byDenomPubHash
@@ -662,7 +662,7 @@ async function updateExchangeFromUrlImpl(
}
}
if (newlyRevokedCoinPubs.length != 0) {
- logger.trace("recouping coins", newlyRevokedCoinPubs);
+ logger.info("recouping coins", newlyRevokedCoinPubs);
recoupGroupId = await ws.recoupOps.createRecoupGroup(
ws,
tx,
diff --git a/packages/taler-wallet-core/src/operations/recoup.ts b/packages/taler-wallet-core/src/operations/recoup.ts
index 8a4c2242a..559513d44 100644
--- a/packages/taler-wallet-core/src/operations/recoup.ts
+++ b/packages/taler-wallet-core/src/operations/recoup.ts
@@ -28,6 +28,7 @@ import {
Amounts,
codecForRecoupConfirmation,
getTimestampNow,
+ j2s,
NotificationType,
RefreshReason,
TalerErrorDetails,
@@ -107,7 +108,7 @@ async function putGroupAsFinished(
}
}
if (allFinished) {
- logger.trace("all recoups of recoup group are finished");
+ logger.info("all recoups of recoup group are finished");
recoupGroup.timestampFinished = getTimestampNow();
recoupGroup.retryInfo = initRetryInfo();
recoupGroup.lastError = undefined;
@@ -178,8 +179,17 @@ async function recoupWithdrawCoin(
type: NotificationType.RecoupStarted,
});
- const recoupRequest = await ws.cryptoApi.createRecoupRequest(coin);
+ const recoupRequest = await ws.cryptoApi.createRecoupRequest({
+ blindingKey: coin.blindingKey,
+ coinPriv: coin.coinPriv,
+ coinPub: coin.coinPub,
+ denomPub: coin.denomPub,
+ denomPubHash: coin.denomPubHash,
+ denomSig: coin.denomSig,
+ recoupAmount: coin.currentAmount,
+ });
const reqUrl = new URL(`/coins/${coin.coinPub}/recoup`, coin.exchangeBaseUrl);
+ logger.trace(`requesting recoup via ${reqUrl.href}`);
const resp = await ws.http.postJson(reqUrl.href, recoupRequest, {
timeout: getReserveRequestTimeout(reserve),
});
@@ -188,6 +198,8 @@ async function recoupWithdrawCoin(
codecForRecoupConfirmation(),
);
+ logger.trace(`got recoup confirmation ${j2s(recoupConfirmation)}`);
+
if (recoupConfirmation.reserve_pub !== reservePub) {
throw Error(`Coin's reserve doesn't match reserve on recoup`);
}
@@ -249,7 +261,15 @@ async function recoupRefreshCoin(
type: NotificationType.RecoupStarted,
});
- const recoupRequest = await ws.cryptoApi.createRecoupRequest(coin);
+ const recoupRequest = await ws.cryptoApi.createRecoupRefreshRequest({
+ blindingKey: coin.blindingKey,
+ coinPriv: coin.coinPriv,
+ coinPub: coin.coinPub,
+ denomPub: coin.denomPub,
+ denomPubHash: coin.denomPubHash,
+ denomSig: coin.denomSig,
+ recoupAmount: coin.currentAmount,
+ });
const reqUrl = new URL(`/coins/${coin.coinPub}/recoup`, coin.exchangeBaseUrl);
logger.trace(`making recoup request for ${coin.coinPub}`);
@@ -359,9 +379,14 @@ async function processRecoupGroupImpl(
logger.trace("recoup group finished");
return;
}
- const ps = recoupGroup.coinPubs.map((x, i) =>
- processRecoup(ws, recoupGroupId, i),
- );
+ const ps = recoupGroup.coinPubs.map(async (x, i) => {
+ try {
+ processRecoup(ws, recoupGroupId, i);
+ } catch (e) {
+ logger.warn(`processRecoup failed: ${e}`);
+ throw e;
+ }
+ });
await Promise.all(ps);
const reserveSet = new Set<string>();
diff --git a/packages/taler-wallet-core/src/operations/reserves.ts b/packages/taler-wallet-core/src/operations/reserves.ts
index 5a9fbb405..75d517d68 100644
--- a/packages/taler-wallet-core/src/operations/reserves.ts
+++ b/packages/taler-wallet-core/src/operations/reserves.ts
@@ -30,6 +30,7 @@ import {
encodeCrock,
getRandomBytes,
getTimestampNow,
+ j2s,
Logger,
NotificationType,
randomBytes,
@@ -538,6 +539,7 @@ async function updateReserve(
resp,
codecForReserveStatus(),
);
+
if (result.isError) {
if (
resp.status === 404 &&
@@ -555,6 +557,8 @@ async function updateReserve(
}
}
+ logger.trace(`got reserve status ${j2s(result.response)}`);
+
const reserveInfo = result.response;
const balance = Amounts.parseOrThrow(reserveInfo.balance);
const currency = balance.currency;
@@ -635,8 +639,10 @@ async function updateReserve(
}
}
- const remainingAmount = Amounts.sub(amountReservePlus, amountReserveMinus)
- .amount;
+ const remainingAmount = Amounts.sub(
+ amountReservePlus,
+ amountReserveMinus,
+ ).amount;
const denomSelInfo = selectWithdrawalDenominations(
remainingAmount,
denoms,