gnunet-svn
[Top][All Lists]
Advanced

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

[taler-wallet-core] branch master updated: wallet-core: implement batch


From: gnunet
Subject: [taler-wallet-core] branch master updated: wallet-core: implement batch withdrawal
Date: Tue, 03 May 2022 17:53:39 +0200

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

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

The following commit(s) were added to refs/heads/master by this push:
     new f16d2e52 wallet-core: implement batch withdrawal
f16d2e52 is described below

commit f16d2e52d51b931d18abd9d87568be681339350f
Author: Florian Dold <florian@dold.me>
AuthorDate: Tue May 3 17:53:32 2022 +0200

    wallet-core: implement batch withdrawal
---
 packages/taler-util/src/talerTypes.ts              |   9 ++
 packages/taler-wallet-cli/src/index.ts             |  13 ++
 .../taler-wallet-core/src/internal-wallet-state.ts |   2 +
 .../taler-wallet-core/src/operations/withdraw.ts   | 147 ++++++++++++++++++---
 packages/taler-wallet-core/src/wallet.ts           |   6 +
 5 files changed, 162 insertions(+), 15 deletions(-)

diff --git a/packages/taler-util/src/talerTypes.ts 
b/packages/taler-util/src/talerTypes.ts
index abac1cd1..ffc1f516 100644
--- a/packages/taler-util/src/talerTypes.ts
+++ b/packages/taler-util/src/talerTypes.ts
@@ -904,6 +904,10 @@ export class WithdrawResponse {
   ev_sig: BlindedDenominationSignature;
 }
 
+export class WithdrawBatchResponse {
+  ev_sigs: WithdrawResponse[];
+}
+
 /**
  * Easy to process format for the public data of coins
  * managed by the wallet.
@@ -1452,6 +1456,11 @@ export const codecForWithdrawResponse = (): 
Codec<WithdrawResponse> =>
     .property("ev_sig", codecForBlindedDenominationSignature())
     .build("WithdrawResponse");
 
+export const codecForWithdrawBatchResponse = (): Codec<WithdrawBatchResponse> 
=>
+  buildCodecForObject<WithdrawBatchResponse>()
+    .property("ev_sigs", codecForList(codecForWithdrawResponse()))
+    .build("WithdrawBatchResponse");
+
 export const codecForMerchantPayResponse = (): Codec<MerchantPayResponse> =>
   buildCodecForObject<MerchantPayResponse>()
     .property("sig", codecForString())
diff --git a/packages/taler-wallet-cli/src/index.ts 
b/packages/taler-wallet-cli/src/index.ts
index 7cff0df8..a4c99902 100644
--- a/packages/taler-wallet-cli/src/index.ts
+++ b/packages/taler-wallet-cli/src/index.ts
@@ -195,6 +195,14 @@ export const walletCli = clk
 
 type WalletCliArgsType = clk.GetArgType<typeof walletCli>;
 
+function checkEnvFlag(name: string): boolean {
+  const val = process.env[name];
+  if (val == "1") {
+    return true;
+  }
+  return false;
+}
+
 async function withWallet<T>(
   walletCliArgs: WalletCliArgsType,
   f: (w: { client: WalletCoreApiClient; ws: Wallet }) => Promise<T>,
@@ -208,6 +216,11 @@ async function withWallet<T>(
     persistentStoragePath: dbPath,
     httpLib: myHttpLib,
   });
+
+  if (checkEnvFlag("TALER_WALLET_BATCH_WITHDRAWAL")) {
+    wallet.setBatchWithdrawal(true);
+  }
+
   applyVerbose(walletCliArgs.wallet.verbose);
   try {
     const w = {
diff --git a/packages/taler-wallet-core/src/internal-wallet-state.ts 
b/packages/taler-wallet-core/src/internal-wallet-state.ts
index bfd006d3..7074128b 100644
--- a/packages/taler-wallet-core/src/internal-wallet-state.ts
+++ b/packages/taler-wallet-core/src/internal-wallet-state.ts
@@ -215,6 +215,8 @@ export interface InternalWalletState {
 
   insecureTrustExchange: boolean;
 
+  batchWithdrawal: boolean;
+
   /**
    * Asynchronous condition to interrupt the sleep of the
    * retry loop.
diff --git a/packages/taler-wallet-core/src/operations/withdraw.ts 
b/packages/taler-wallet-core/src/operations/withdraw.ts
index 94f8e20b..2edc3ed9 100644
--- a/packages/taler-wallet-core/src/operations/withdraw.ts
+++ b/packages/taler-wallet-core/src/operations/withdraw.ts
@@ -24,6 +24,7 @@ import {
   AmountString,
   BankWithdrawDetails,
   codecForTalerConfigResponse,
+  codecForWithdrawBatchResponse,
   codecForWithdrawOperationStatusResponse,
   codecForWithdrawResponse,
   DenomKeyType,
@@ -42,6 +43,7 @@ import {
   UnblindedSignature,
   URL,
   VersionMatchResult,
+  WithdrawBatchResponse,
   WithdrawResponse,
   WithdrawUriInfoResponse,
 } from "@gnu-taler/taler-util";
@@ -70,11 +72,7 @@ import {
   readSuccessResponseJsonOrThrow,
 } from "../util/http.js";
 import { checkDbInvariant, checkLogicInvariant } from "../util/invariants.js";
-import {
-  resetRetryInfo,
-  RetryInfo,
-  updateRetryInfoTimeout,
-} from "../util/retries.js";
+import { resetRetryInfo, RetryInfo } from "../util/retries.js";
 import {
   WALLET_BANK_INTEGRATION_PROTOCOL_VERSION,
   WALLET_EXCHANGE_PROTOCOL_VERSION,
@@ -585,6 +583,108 @@ async function processPlanchetExchangeRequest(
   }
 }
 
+/**
+ * Send the withdrawal request for a generated planchet to the exchange.
+ *
+ * The verification of the response is done asynchronously to enable 
parallelism.
+ */
+async function processPlanchetExchangeBatchRequest(
+  ws: InternalWalletState,
+  withdrawalGroup: WithdrawalGroupRecord,
+): Promise<WithdrawBatchResponse | undefined> {
+  logger.info(
+    `processing planchet exchange batch request 
${withdrawalGroup.withdrawalGroupId}`,
+  );
+  const numTotalCoins = withdrawalGroup.denomsSel.selectedDenoms
+    .map((x) => x.count)
+    .reduce((a, b) => a + b);
+  const d = await ws.db
+    .mktx((x) => ({
+      withdrawalGroups: x.withdrawalGroups,
+      planchets: x.planchets,
+      exchanges: x.exchanges,
+      denominations: x.denominations,
+    }))
+    .runReadOnly(async (tx) => {
+      const reqBody: { planchets: ExchangeWithdrawRequest[] } = {
+        planchets: [],
+      };
+      const exchange = await tx.exchanges.get(withdrawalGroup.exchangeBaseUrl);
+      if (!exchange) {
+        logger.error("db inconsistent: exchange for planchet not found");
+        return;
+      }
+
+      for (let coinIdx = 0; coinIdx < numTotalCoins; coinIdx++) {
+        let planchet = await tx.planchets.indexes.byGroupAndIndex.get([
+          withdrawalGroup.withdrawalGroupId,
+          coinIdx,
+        ]);
+        if (!planchet) {
+          return;
+        }
+        if (planchet.withdrawalDone) {
+          logger.warn("processPlanchet: planchet already withdrawn");
+          return;
+        }
+        const denom = await ws.getDenomInfo(
+          ws,
+          tx,
+          withdrawalGroup.exchangeBaseUrl,
+          planchet.denomPubHash,
+        );
+
+        if (!denom) {
+          logger.error("db inconsistent: denom for planchet not found");
+          return;
+        }
+
+        const planchetReq: ExchangeWithdrawRequest = {
+          denom_pub_hash: planchet.denomPubHash,
+          reserve_sig: planchet.withdrawSig,
+          coin_ev: planchet.coinEv,
+        };
+        reqBody.planchets.push(planchetReq);
+      }
+      return reqBody;
+    });
+
+  if (!d) {
+    return;
+  }
+
+  const reqUrl = new URL(
+    `reserves/${withdrawalGroup.reservePub}/batch-withdraw`,
+    withdrawalGroup.exchangeBaseUrl,
+  ).href;
+
+  try {
+    const resp = await ws.http.postJson(reqUrl, d);
+    const r = await readSuccessResponseJsonOrThrow(
+      resp,
+      codecForWithdrawBatchResponse(),
+    );
+    return r;
+  } catch (e) {
+    const errDetail = getErrorDetailFromException(e);
+    logger.trace("withdrawal batch request failed", e);
+    logger.trace(e);
+    await ws.db
+      .mktx((x) => ({ withdrawalGroups: x.withdrawalGroups }))
+      .runReadWrite(async (tx) => {
+        let wg = await tx.withdrawalGroups.get(
+          withdrawalGroup.withdrawalGroupId,
+        );
+        if (!wg) {
+          return;
+        }
+        wg.lastError = errDetail;
+        await tx.withdrawalGroups.put(wg);
+      });
+    return;
+  }
+}
+
 async function processPlanchetVerifyAndStoreCoin(
   ws: InternalWalletState,
   withdrawalGroup: WithdrawalGroupRecord,
@@ -931,18 +1031,35 @@ async function processWithdrawGroupImpl(
 
   work = [];
 
-  for (let coinIdx = 0; coinIdx < numTotalCoins; coinIdx++) {
-    const resp = await processPlanchetExchangeRequest(
-      ws,
-      withdrawalGroup,
-      coinIdx,
-    );
+  if (ws.batchWithdrawal) {
+    const resp = await processPlanchetExchangeBatchRequest(ws, 
withdrawalGroup);
     if (!resp) {
-      continue;
+      return;
+    }
+    for (let coinIdx = 0; coinIdx < numTotalCoins; coinIdx++) {
+      work.push(
+        processPlanchetVerifyAndStoreCoin(
+          ws,
+          withdrawalGroup,
+          coinIdx,
+          resp.ev_sigs[coinIdx],
+        ),
+      );
+    }
+  } else {
+    for (let coinIdx = 0; coinIdx < numTotalCoins; coinIdx++) {
+      const resp = await processPlanchetExchangeRequest(
+        ws,
+        withdrawalGroup,
+        coinIdx,
+      );
+      if (!resp) {
+        continue;
+      }
+      work.push(
+        processPlanchetVerifyAndStoreCoin(ws, withdrawalGroup, coinIdx, resp),
+      );
     }
-    work.push(
-      processPlanchetVerifyAndStoreCoin(ws, withdrawalGroup, coinIdx, resp),
-    );
   }
 
   await Promise.all(work);
diff --git a/packages/taler-wallet-core/src/wallet.ts 
b/packages/taler-wallet-core/src/wallet.ts
index 7c917c41..fb61ae0d 100644
--- a/packages/taler-wallet-core/src/wallet.ts
+++ b/packages/taler-wallet-core/src/wallet.ts
@@ -1101,6 +1101,10 @@ export class Wallet {
     this.ws.insecureTrustExchange = true;
   }
 
+  setBatchWithdrawal(enable: boolean): void {
+    this.ws.batchWithdrawal = enable;
+  }
+
   static async create(
     db: DbAccess<typeof WalletStoresV1>,
     http: HttpRequestLibrary,
@@ -1158,6 +1162,8 @@ class InternalWalletStateImpl implements 
InternalWalletState {
 
   insecureTrustExchange = false;
 
+  batchWithdrawal = false;
+
   readonly timerGroup: TimerGroup;
   latch = new AsyncCondition();
   stopped = false;

-- 
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]