gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[taler-wallet-core] 01/02: towards new recoup API


From: gnunet
Subject: [taler-wallet-core] 01/02: towards new recoup API
Date: Tue, 11 Jan 2022 22:16:01 +0100

This is an automated email from the git hooks/post-receive script.

dold pushed a commit to branch master
in repository wallet-core.

commit a05e891d6e1468fdd99f710301e286857a46aea3
Author: Florian Dold <florian@dold.me>
AuthorDate: Tue Jan 11 12:48:32 2022 +0100

    towards new recoup API
---
 packages/taler-util/src/talerCrypto.ts             |   1 +
 packages/taler-util/src/talerTypes.ts              | 310 ++++++++++++---------
 packages/taler-wallet-cli/src/harness/harness.ts   |   8 +-
 packages/taler-wallet-cli/src/index.ts             |   8 +-
 .../taler-wallet-core/src/crypto/cryptoTypes.ts    |  34 ++-
 .../src/crypto/workers/cryptoApi.ts                |  22 +-
 .../src/crypto/workers/cryptoImplementation.ts     |  73 +++--
 .../taler-wallet-core/src/operations/exchanges.ts  |   4 +-
 .../taler-wallet-core/src/operations/recoup.ts     |  37 ++-
 .../taler-wallet-core/src/operations/reserves.ts   |  10 +-
 10 files changed, 326 insertions(+), 181 deletions(-)

diff --git a/packages/taler-util/src/talerCrypto.ts 
b/packages/taler-util/src/talerCrypto.ts
index d96c2323..5f1c2e8c 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 e7d8a8c1..5f72e08c 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 040bd5a6..35162065 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 b57e73a1..22a2d855 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 9b72dfbe..5351815a 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 e88b64c3..29c2553a 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 9e2dc18f..b366fa9e 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 98703181..2975c860 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 8a4c2242..559513d4 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 5a9fbb40..75d517d6 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,

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]