gnunet-svn
[Top][All Lists]
Advanced

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

[taler-exchange] 01/03: avoid extra transaction to fetch balance if rese


From: gnunet
Subject: [taler-exchange] 01/03: avoid extra transaction to fetch balance if reserve is out of funds, remove legacy /withdraw endpoint
Date: Mon, 16 Oct 2023 08:49:56 +0200

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

grothoff pushed a commit to branch master
in repository exchange.

commit 917dd4d70ff2f38d475146b387e649a669996f10
Author: Christian Grothoff <grothoff@gnunet.org>
AuthorDate: Sun Oct 15 23:30:51 2023 +0200

    avoid extra transaction to fetch balance if reserve is out of funds, remove 
legacy /withdraw endpoint
---
 src/exchange/Makefile.am                           |   3 +-
 src/exchange/taler-exchange-httpd.c                |   5 -
 src/exchange/taler-exchange-httpd_age-withdraw.c   |  15 +-
 src/exchange/taler-exchange-httpd_batch-withdraw.c |   5 +-
 src/exchange/taler-exchange-httpd_reserves_open.c  |   3 +
 src/exchange/taler-exchange-httpd_responses.c      |  19 +-
 src/exchange/taler-exchange-httpd_responses.h      |   2 +
 src/exchange/taler-exchange-httpd_withdraw.c       | 700 ---------------------
 src/exchange/taler-exchange-httpd_withdraw.h       |  47 --
 src/exchangedb/exchange_do_age_withdraw.sql        |  10 +-
 src/exchangedb/exchange_do_batch_withdraw.sql      |  24 +-
 src/exchangedb/exchange_do_reserve_open.sql        |  32 +-
 src/exchangedb/pg_do_age_withdraw.c                |   8 +-
 src/exchangedb/pg_do_age_withdraw.h                |   2 +
 src/exchangedb/pg_do_batch_withdraw.c              |   4 +
 src/exchangedb/pg_do_batch_withdraw.h              |   2 +
 src/exchangedb/pg_do_reserve_open.c                |  23 +-
 src/exchangedb/pg_do_reserve_open.h                |   2 +
 src/exchangedb/pg_do_withdraw.c                    |   2 +
 src/include/taler_exchange_service.h               | 138 +---
 src/include/taler_exchangedb_plugin.h              |   6 +
 src/lib/Makefile.am                                |   4 +-
 src/lib/exchange_api_age_withdraw.c                |  42 +-
 src/lib/exchange_api_batch_withdraw2.c             |  45 +-
 src/lib/exchange_api_withdraw.c                    | 364 -----------
 src/lib/exchange_api_withdraw2.c                   | 389 ------------
 src/testing/testing_api_cmd_withdraw.c             |  19 +-
 27 files changed, 175 insertions(+), 1740 deletions(-)

diff --git a/src/exchange/Makefile.am b/src/exchange/Makefile.am
index 86829edd..12ea34e1 100644
--- a/src/exchange/Makefile.am
+++ b/src/exchange/Makefile.am
@@ -182,8 +182,7 @@ taler_exchange_httpd_SOURCES = \
   taler-exchange-httpd_reserves_purse.c taler-exchange-httpd_reserves_purse.h \
   taler-exchange-httpd_responses.c taler-exchange-httpd_responses.h \
   taler-exchange-httpd_terms.c taler-exchange-httpd_terms.h \
-  taler-exchange-httpd_transfers_get.c taler-exchange-httpd_transfers_get.h \
-  taler-exchange-httpd_withdraw.c taler-exchange-httpd_withdraw.h
+  taler-exchange-httpd_transfers_get.c taler-exchange-httpd_transfers_get.h 
 
 taler_exchange_httpd_LDADD = \
   $(LIBGCRYPT_LIBS) \
diff --git a/src/exchange/taler-exchange-httpd.c 
b/src/exchange/taler-exchange-httpd.c
index 5bc118e3..46d5833a 100644
--- a/src/exchange/taler-exchange-httpd.c
+++ b/src/exchange/taler-exchange-httpd.c
@@ -69,7 +69,6 @@
 #include "taler-exchange-httpd_reserves_purse.h"
 #include "taler-exchange-httpd_terms.h"
 #include "taler-exchange-httpd_transfers_get.h"
-#include "taler-exchange-httpd_withdraw.h"
 #include "taler_exchangedb_lib.h"
 #include "taler_exchangedb_plugin.h"
 #include "taler_extensions.h"
@@ -746,10 +745,6 @@ handle_post_reserves (struct TEH_RequestContext *rc,
       .op = "age-withdraw",
       .handler = &TEH_handler_age_withdraw
     },
-    {
-      .op = "withdraw",
-      .handler = &TEH_handler_withdraw
-    },
     {
       .op = "purse",
       .handler = &TEH_handler_reserves_purse
diff --git a/src/exchange/taler-exchange-httpd_age-withdraw.c 
b/src/exchange/taler-exchange-httpd_age-withdraw.c
index 47cff626..4a7d6b1a 100644
--- a/src/exchange/taler-exchange-httpd_age-withdraw.c
+++ b/src/exchange/taler-exchange-httpd_age-withdraw.c
@@ -399,8 +399,8 @@ denomination_is_valid (
  * @param[out] denom_serials On success, will be filled with the serial-id's 
of the denomination keys.  Caller must deallocate.
  * @param[out] amount_with_fee On success, will contain the committed amount 
including fees
  * @param[out] result In the error cases, a response will be queued with MHD 
and this will be the result.
- * @return GNUNET_OK if the denominations are valid and support age-restriction
- *   GNUNET_SYSERR otherwise
+ * @return #GNUNET_OK if the denominations are valid and support 
age-restriction
+ *   #GNUNET_SYSERR otherwise
  */
 static enum GNUNET_GenericReturnValue
 are_denominations_valid (
@@ -737,12 +737,14 @@ age_withdraw_transaction (void *cls,
     bool conflict = false;
     uint16_t allowed_maximum_age = 0;
     uint32_t reserve_birthday = 0;
+    struct TALER_Amount reserve_balance;
 
     qs = TEH_plugin->do_age_withdraw (TEH_plugin->cls,
                                       &awc->commitment,
                                       awc->now,
                                       &found,
                                       &balance_ok,
+                                      &reserve_balance,
                                       &age_ok,
                                       &allowed_maximum_age,
                                       &reserve_birthday,
@@ -755,14 +757,14 @@ age_withdraw_transaction (void *cls,
                                             "do_age_withdraw");
       return qs;
     }
-    else if (! found)
+    if (! found)
     {
       *mhd_ret = TALER_MHD_reply_with_ec (connection,
                                           
TALER_EC_EXCHANGE_GENERIC_RESERVE_UNKNOWN,
                                           NULL);
       return GNUNET_DB_STATUS_HARD_ERROR;
     }
-    else if (! age_ok)
+    if (! age_ok)
     {
       enum TALER_ErrorCode ec =
         TALER_EC_EXCHANGE_AGE_WITHDRAW_MAXIMUM_AGE_TOO_LARGE;
@@ -779,19 +781,20 @@ age_withdraw_transaction (void *cls,
 
       return GNUNET_DB_STATUS_HARD_ERROR;
     }
-    else if (! balance_ok)
+    if (! balance_ok)
     {
       TEH_plugin->rollback (TEH_plugin->cls);
 
       *mhd_ret = TEH_RESPONSE_reply_reserve_insufficient_balance (
         connection,
         TALER_EC_EXCHANGE_AGE_WITHDRAW_INSUFFICIENT_FUNDS,
+        &reserve_balance,
         &awc->commitment.amount_with_fee,
         &awc->commitment.reserve_pub);
 
       return GNUNET_DB_STATUS_HARD_ERROR;
     }
-    else if (conflict)
+    if (conflict)
     {
       /* do_age_withdraw signaled a conflict, so there MUST be an entry
        * in the DB.  Put that into the response */
diff --git a/src/exchange/taler-exchange-httpd_batch-withdraw.c 
b/src/exchange/taler-exchange-httpd_batch-withdraw.c
index 38a7f43c..fe210876 100644
--- a/src/exchange/taler-exchange-httpd_batch-withdraw.c
+++ b/src/exchange/taler-exchange-httpd_batch-withdraw.c
@@ -312,6 +312,7 @@ batch_withdraw_transaction (void *cls,
   bool balance_ok = false;
   bool age_ok = false;
   uint16_t allowed_maximum_age = 0;
+  struct TALER_Amount reserve_balance;
   char *kyc_required;
   struct TALER_PaytoHashP reserve_h_payto;
 
@@ -476,6 +477,7 @@ batch_withdraw_transaction (void *cls,
                                       TEH_age_restriction_enabled,
                                       &found,
                                       &balance_ok,
+                                      &reserve_balance,
                                       &age_ok,
                                       &allowed_maximum_age,
                                       &ruuid);
@@ -521,6 +523,7 @@ batch_withdraw_transaction (void *cls,
     *mhd_ret = TEH_RESPONSE_reply_reserve_insufficient_balance (
       connection,
       TALER_EC_EXCHANGE_WITHDRAW_INSUFFICIENT_FUNDS,
+      &reserve_balance,
       &wc->batch_total,
       wc->reserve_pub);
     return GNUNET_DB_STATUS_HARD_ERROR;
@@ -553,7 +556,7 @@ batch_withdraw_transaction (void *cls,
         *mhd_ret = TALER_MHD_reply_with_error (connection,
                                                MHD_HTTP_INTERNAL_SERVER_ERROR,
                                                
TALER_EC_GENERIC_DB_FETCH_FAILED,
-                                               "do_withdraw");
+                                               "do_batch_withdraw_insert");
       return qs;
     }
     if (denom_unknown)
diff --git a/src/exchange/taler-exchange-httpd_reserves_open.c 
b/src/exchange/taler-exchange-httpd_reserves_open.c
index 50487990..5aadc9e4 100644
--- a/src/exchange/taler-exchange-httpd_reserves_open.c
+++ b/src/exchange/taler-exchange-httpd_reserves_open.c
@@ -188,6 +188,7 @@ reserve_open_transaction (void *cls,
 {
   struct ReserveOpenContext *rsc = cls;
   enum GNUNET_DB_QueryStatus qs;
+  struct TALER_Amount reserve_balance;
 
   for (unsigned int i = 0; i<rsc->payments_len; i++)
   {
@@ -258,6 +259,7 @@ reserve_open_transaction (void *cls,
                                     &rsc->gf->fees.account,
                                     /* outputs */
                                     &rsc->no_funds,
+                                    &reserve_balance,
                                     &rsc->open_cost,
                                     &rsc->reserve_expiration);
   switch (qs)
@@ -289,6 +291,7 @@ reserve_open_transaction (void *cls,
       = TEH_RESPONSE_reply_reserve_insufficient_balance (
           connection,
           TALER_EC_EXCHANGE_RESERVES_OPEN_INSUFFICIENT_FUNDS,
+          &reserve_balance,
           &rsc->reserve_payment,
           rsc->reserve_pub);
     return GNUNET_DB_STATUS_HARD_ERROR;
diff --git a/src/exchange/taler-exchange-httpd_responses.c 
b/src/exchange/taler-exchange-httpd_responses.c
index 2d5d8dce..322da387 100644
--- a/src/exchange/taler-exchange-httpd_responses.c
+++ b/src/exchange/taler-exchange-httpd_responses.c
@@ -181,31 +181,16 @@ MHD_RESULT
 TEH_RESPONSE_reply_reserve_insufficient_balance (
   struct MHD_Connection *connection,
   enum TALER_ErrorCode ec,
+  const struct TALER_Amount *reserve_balance,
   const struct TALER_Amount *balance_required,
   const struct TALER_ReservePublicKeyP *reserve_pub)
 {
-  struct TALER_Amount balance;
-  enum GNUNET_DB_QueryStatus qs;
-
-  // FIXME: pass balance as argument to this function,
-  // instead of getting it in another transaction!
-  qs = TEH_plugin->get_reserve_balance (TEH_plugin->cls,
-                                        reserve_pub,
-                                        &balance);
-  if (qs < 0)
-  {
-    GNUNET_break (0);
-    return TALER_MHD_reply_with_error (connection,
-                                       MHD_HTTP_INTERNAL_SERVER_ERROR,
-                                       TALER_EC_GENERIC_DB_FETCH_FAILED,
-                                       "reserve balance");
-  }
   return TALER_MHD_REPLY_JSON_PACK (
     connection,
     MHD_HTTP_CONFLICT,
     TALER_JSON_pack_ec (ec),
     TALER_JSON_pack_amount ("balance",
-                            &balance),
+                            reserve_balance),
     TALER_JSON_pack_amount ("requested_amount",
                             balance_required));
 }
diff --git a/src/exchange/taler-exchange-httpd_responses.h 
b/src/exchange/taler-exchange-httpd_responses.h
index 877e8878..8adf1136 100644
--- a/src/exchange/taler-exchange-httpd_responses.h
+++ b/src/exchange/taler-exchange-httpd_responses.h
@@ -53,6 +53,7 @@ TEH_RESPONSE_reply_unknown_denom_pub_hash (
  *
  * @param connection connection to the client
  * @param ec specific error code to return with the reserve history
+ * @param reserve_balance balance remaining in the reserve
  * @param balance_required the balance required for the operation
  * @param reserve_pub the reserve with insufficient balance
  * @return MHD result code
@@ -61,6 +62,7 @@ MHD_RESULT
 TEH_RESPONSE_reply_reserve_insufficient_balance (
   struct MHD_Connection *connection,
   enum TALER_ErrorCode ec,
+  const struct TALER_Amount *reserve_balance,
   const struct TALER_Amount *balance_required,
   const struct TALER_ReservePublicKeyP *reserve_pub);
 
diff --git a/src/exchange/taler-exchange-httpd_withdraw.c 
b/src/exchange/taler-exchange-httpd_withdraw.c
deleted file mode 100644
index 07fcc846..00000000
--- a/src/exchange/taler-exchange-httpd_withdraw.c
+++ /dev/null
@@ -1,700 +0,0 @@
-/*
-  This file is part of TALER
-  Copyright (C) 2014-2023 Taler Systems SA
-
-  TALER is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Affero General Public License as
-  published by the Free Software Foundation; either version 3,
-  or (at your option) any later version.
-
-  TALER is distributed in the hope that it will be useful,
-  but WITHOUT ANY WARRANTY; without even the implied warranty
-  of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-  See the GNU Affero General Public License for more details.
-
-  You should have received a copy of the GNU Affero General
-  Public License along with TALER; see the file COPYING.  If not,
-  see <http://www.gnu.org/licenses/>
-*/
-/**
- * @file taler-exchange-httpd_withdraw.c
- * @brief Handle /reserves/$RESERVE_PUB/withdraw requests
- * @author Florian Dold
- * @author Benedikt Mueller
- * @author Christian Grothoff
- */
-#include "platform.h"
-#include <gnunet/gnunet_util_lib.h>
-#include <jansson.h>
-#include "taler-exchange-httpd.h"
-#include "taler_json_lib.h"
-#include "taler_kyclogic_lib.h"
-#include "taler_mhd_lib.h"
-#include "taler-exchange-httpd_withdraw.h"
-#include "taler-exchange-httpd_responses.h"
-#include "taler-exchange-httpd_keys.h"
-
-
-/**
- * Context for #withdraw_transaction.
- */
-struct WithdrawContext
-{
-
-  /**
-   * Hash of the (blinded) message to be signed by the Exchange.
-   */
-  struct TALER_BlindedCoinHashP h_coin_envelope;
-
-  /**
-   * Blinded planchet.
-   */
-  struct TALER_BlindedPlanchet blinded_planchet;
-
-  /**
-   * Set to the resulting signed coin data to be returned to the client.
-   */
-  struct TALER_EXCHANGEDB_CollectableBlindcoin collectable;
-
-  /**
-   * KYC status for the operation.
-   */
-  struct TALER_EXCHANGEDB_KycStatus kyc;
-
-  /**
-   * Hash of the payto-URI representing the account
-   * from which the money was put into the reserve.
-   */
-  struct TALER_PaytoHashP h_account_payto;
-
-  /**
-   * Current time for the DB transaction.
-   */
-  struct GNUNET_TIME_Timestamp now;
-
-  /**
-   * AML decision, #TALER_AML_NORMAL if we may proceed.
-   */
-  enum TALER_AmlDecisionState aml_decision;
-
-};
-
-
-/**
- * Function called to iterate over KYC-relevant
- * transaction amounts for a particular time range.
- * Called within a database transaction, so must
- * not start a new one.
- *
- * @param cls closure, identifies the event type and
- *        account to iterate over events for
- * @param limit maximum time-range for which events
- *        should be fetched (timestamp in the past)
- * @param cb function to call on each event found,
- *        events must be returned in reverse chronological
- *        order
- * @param cb_cls closure for @a cb
- */
-static void
-withdraw_amount_cb (void *cls,
-                    struct GNUNET_TIME_Absolute limit,
-                    TALER_EXCHANGEDB_KycAmountCallback cb,
-                    void *cb_cls)
-{
-  struct WithdrawContext *wc = cls;
-  enum GNUNET_DB_QueryStatus qs;
-
-  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-              "Signaling amount %s for KYC check\n",
-              TALER_amount2s (&wc->collectable.amount_with_fee));
-  if (GNUNET_OK !=
-      cb (cb_cls,
-          &wc->collectable.amount_with_fee,
-          wc->now.abs_time))
-    return;
-  qs = TEH_plugin->select_withdraw_amounts_for_kyc_check (
-    TEH_plugin->cls,
-    &wc->h_account_payto,
-    limit,
-    cb,
-    cb_cls);
-  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-              "Got %d additional transactions for this withdrawal and limit 
%llu\n",
-              qs,
-              (unsigned long long) limit.abs_value_us);
-  GNUNET_break (qs >= 0);
-}
-
-
-/**
- * Function called on each @a amount that was found to
- * be relevant for the AML check as it was merged into
- * the reserve.
- *
- * @param cls `struct TALER_Amount *` to total up the amounts
- * @param amount encountered transaction amount
- * @param date when was the amount encountered
- * @return #GNUNET_OK to continue to iterate,
- *         #GNUNET_NO to abort iteration
- *         #GNUNET_SYSERR on internal error (also abort itaration)
- */
-static enum GNUNET_GenericReturnValue
-aml_amount_cb (
-  void *cls,
-  const struct TALER_Amount *amount,
-  struct GNUNET_TIME_Absolute date)
-{
-  struct TALER_Amount *total = cls;
-
-  GNUNET_assert (0 <=
-                 TALER_amount_add (total,
-                                   total,
-                                   amount));
-  return GNUNET_OK;
-}
-
-
-/**
- * Function implementing withdraw transaction.  Runs the
- * transaction logic; IF it returns a non-error code, the transaction
- * logic MUST NOT queue a MHD response.  IF it returns an hard error,
- * the transaction logic MUST queue a MHD response and set @a mhd_ret.
- * IF it returns the soft error code, the function MAY be called again
- * to retry and MUST not queue a MHD response.
- *
- * Note that "wc->collectable.sig" is set before entering this function as we
- * signed before entering the transaction.
- *
- * @param cls a `struct WithdrawContext *`
- * @param connection MHD request which triggered the transaction
- * @param[out] mhd_ret set to MHD response status for @a connection,
- *             if transaction failed (!)
- * @return transaction status
- */
-static enum GNUNET_DB_QueryStatus
-withdraw_transaction (void *cls,
-                      struct MHD_Connection *connection,
-                      MHD_RESULT *mhd_ret)
-{
-  struct WithdrawContext *wc = cls;
-  enum GNUNET_DB_QueryStatus qs;
-  bool found = false;
-  bool balance_ok = false;
-  bool nonce_ok = false;
-  bool age_ok = false;
-  uint16_t allowed_maximum_age = 0;
-  uint64_t ruuid;
-  const struct TALER_CsNonce *nonce;
-  const struct TALER_BlindedPlanchet *bp;
-  struct TALER_PaytoHashP reserve_h_payto;
-
-  wc->now = GNUNET_TIME_timestamp_get ();
-  /* Do AML check: compute total merged amount and check
-     against applicable AML threshold */
-  {
-    char *reserve_payto;
-
-    reserve_payto = TALER_reserve_make_payto (TEH_base_url,
-                                              &wc->collectable.reserve_pub);
-    TALER_payto_hash (reserve_payto,
-                      &reserve_h_payto);
-    GNUNET_free (reserve_payto);
-  }
-  {
-    struct TALER_Amount merge_amount;
-    struct TALER_Amount threshold;
-    struct GNUNET_TIME_Absolute now_minus_one_month;
-
-    now_minus_one_month
-      = GNUNET_TIME_absolute_subtract (wc->now.abs_time,
-                                       GNUNET_TIME_UNIT_MONTHS);
-    GNUNET_assert (GNUNET_OK ==
-                   TALER_amount_set_zero (TEH_currency,
-                                          &merge_amount));
-    qs = TEH_plugin->select_merge_amounts_for_kyc_check (TEH_plugin->cls,
-                                                         &reserve_h_payto,
-                                                         now_minus_one_month,
-                                                         &aml_amount_cb,
-                                                         &merge_amount);
-    if (qs < 0)
-    {
-      GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
-      if (GNUNET_DB_STATUS_HARD_ERROR == qs)
-        *mhd_ret = TALER_MHD_reply_with_error (connection,
-                                               MHD_HTTP_INTERNAL_SERVER_ERROR,
-                                               
TALER_EC_GENERIC_DB_FETCH_FAILED,
-                                               
"select_merge_amounts_for_kyc_check");
-      return qs;
-    }
-    qs = TEH_plugin->select_aml_threshold (TEH_plugin->cls,
-                                           &reserve_h_payto,
-                                           &wc->aml_decision,
-                                           &wc->kyc,
-                                           &threshold);
-    if (qs < 0)
-    {
-      GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
-      if (GNUNET_DB_STATUS_HARD_ERROR == qs)
-        *mhd_ret = TALER_MHD_reply_with_error (connection,
-                                               MHD_HTTP_INTERNAL_SERVER_ERROR,
-                                               
TALER_EC_GENERIC_DB_FETCH_FAILED,
-                                               "select_aml_threshold");
-      return qs;
-    }
-    if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
-    {
-      threshold = TEH_aml_threshold; /* use default */
-      wc->aml_decision = TALER_AML_NORMAL;
-    }
-
-    switch (wc->aml_decision)
-    {
-    case TALER_AML_NORMAL:
-      if (0 >= TALER_amount_cmp (&merge_amount,
-                                 &threshold))
-      {
-        /* merge_amount <= threshold, continue withdraw below */
-        break;
-      }
-      wc->aml_decision = TALER_AML_PENDING;
-      qs = TEH_plugin->trigger_aml_process (TEH_plugin->cls,
-                                            &reserve_h_payto,
-                                            &merge_amount);
-      if (qs <= 0)
-      {
-        GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
-        if (GNUNET_DB_STATUS_HARD_ERROR == qs)
-          *mhd_ret = TALER_MHD_reply_with_error (connection,
-                                                 
MHD_HTTP_INTERNAL_SERVER_ERROR,
-                                                 
TALER_EC_GENERIC_DB_STORE_FAILED,
-                                                 "trigger_aml_process");
-        return qs;
-      }
-      return qs;
-    case TALER_AML_PENDING:
-      GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                  "AML already pending, doing nothing\n");
-      return qs;
-    case TALER_AML_FROZEN:
-      GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                  "Account frozen, doing nothing\n");
-      return qs;
-    }
-  }
-
-  /* Check if the money came from a wire transfer */
-  qs = TEH_plugin->reserves_get_origin (TEH_plugin->cls,
-                                        &wc->collectable.reserve_pub,
-                                        &wc->h_account_payto);
-  if (qs < 0)
-    return qs;
-  /* If no results, reserve was created by merge, in which case no KYC check
-     is required as the merge already did that. */
-  if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
-  {
-    char *kyc_required;
-
-    qs = TALER_KYCLOGIC_kyc_test_required (
-      TALER_KYCLOGIC_KYC_TRIGGER_WITHDRAW,
-      &wc->h_account_payto,
-      TEH_plugin->select_satisfied_kyc_processes,
-      TEH_plugin->cls,
-      &withdraw_amount_cb,
-      wc,
-      &kyc_required);
-    if (qs < 0)
-    {
-      if (GNUNET_DB_STATUS_HARD_ERROR == qs)
-      {
-        GNUNET_break (0);
-        *mhd_ret = TALER_MHD_reply_with_error (connection,
-                                               MHD_HTTP_INTERNAL_SERVER_ERROR,
-                                               
TALER_EC_GENERIC_DB_FETCH_FAILED,
-                                               "kyc_test_required");
-      }
-      return qs;
-    }
-    if (NULL != kyc_required)
-    {
-      /* insert KYC requirement into DB! */
-      wc->kyc.ok = false;
-      qs = TEH_plugin->insert_kyc_requirement_for_account (
-        TEH_plugin->cls,
-        kyc_required,
-        &wc->h_account_payto,
-        &wc->collectable.reserve_pub,
-        &wc->kyc.requirement_row);
-      GNUNET_free (kyc_required);
-      if (GNUNET_DB_STATUS_HARD_ERROR == qs)
-      {
-        GNUNET_break (0);
-        *mhd_ret = TALER_MHD_reply_with_error (connection,
-                                               MHD_HTTP_INTERNAL_SERVER_ERROR,
-                                               
TALER_EC_GENERIC_DB_STORE_FAILED,
-                                               
"insert_kyc_requirement_for_account");
-      }
-      return qs;
-    }
-  }
-  wc->kyc.ok = true;
-  bp = &wc->blinded_planchet;
-  nonce = (TALER_DENOMINATION_CS == bp->cipher)
-    ? &bp->details.cs_blinded_planchet.nonce
-    : NULL;
-  qs = TEH_plugin->do_withdraw (TEH_plugin->cls,
-                                nonce,
-                                &wc->collectable,
-                                wc->now,
-                                TEH_age_restriction_enabled,
-                                &found,
-                                &balance_ok,
-                                &nonce_ok,
-                                &age_ok,
-                                &allowed_maximum_age,
-                                &ruuid);
-  if (0 > qs)
-  {
-    if (GNUNET_DB_STATUS_HARD_ERROR == qs)
-    {
-      GNUNET_break (0);
-      *mhd_ret = TALER_MHD_reply_with_error (connection,
-                                             MHD_HTTP_INTERNAL_SERVER_ERROR,
-                                             TALER_EC_GENERIC_DB_FETCH_FAILED,
-                                             "do_withdraw");
-    }
-    return qs;
-  }
-  if (! found)
-  {
-    *mhd_ret = TALER_MHD_reply_with_error (connection,
-                                           MHD_HTTP_NOT_FOUND,
-                                           
TALER_EC_EXCHANGE_GENERIC_RESERVE_UNKNOWN,
-                                           NULL);
-    return GNUNET_DB_STATUS_HARD_ERROR;
-  }
-  if (! age_ok)
-  {
-    /* We respond with the lowest age in the corresponding age group
-     * of the required age */
-    uint16_t lowest_age = TALER_get_lowest_age (
-      &TEH_age_restriction_config.mask,
-      allowed_maximum_age);
-
-    TEH_plugin->rollback (TEH_plugin->cls);
-    *mhd_ret = TEH_RESPONSE_reply_reserve_age_restriction_required (
-      connection,
-      lowest_age);
-    return GNUNET_DB_STATUS_HARD_ERROR;
-  }
-  if (! balance_ok)
-  {
-    TEH_plugin->rollback (TEH_plugin->cls);
-    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-                "Balance insufficient for /withdraw\n");
-    *mhd_ret = TEH_RESPONSE_reply_reserve_insufficient_balance (
-      connection,
-      TALER_EC_EXCHANGE_WITHDRAW_INSUFFICIENT_FUNDS,
-      &wc->collectable.amount_with_fee,
-      &wc->collectable.reserve_pub);
-    return GNUNET_DB_STATUS_HARD_ERROR;
-  }
-  if (! nonce_ok)
-  {
-    TEH_plugin->rollback (TEH_plugin->cls);
-    *mhd_ret = TALER_MHD_reply_with_error (connection,
-                                           MHD_HTTP_CONFLICT,
-                                           
TALER_EC_EXCHANGE_WITHDRAW_NONCE_REUSE,
-                                           NULL);
-    return GNUNET_DB_STATUS_HARD_ERROR;
-  }
-  if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
-    TEH_METRICS_num_success[TEH_MT_SUCCESS_WITHDRAW]++;
-  return qs;
-}
-
-
-/**
- * Check if the @a rc is replayed and we already have an
- * answer. If so, replay the existing answer and return the
- * HTTP response.
- *
- * @param rc request context
- * @param[in,out] wc parsed request data
- * @param[out] mret HTTP status, set if we return true
- * @return true if the request is idempotent with an existing request
- *    false if we did not find the request in the DB and did not set @a mret
- */
-static bool
-check_request_idempotent (struct TEH_RequestContext *rc,
-                          struct WithdrawContext *wc,
-                          MHD_RESULT *mret)
-{
-  enum GNUNET_DB_QueryStatus qs;
-
-  qs = TEH_plugin->get_withdraw_info (TEH_plugin->cls,
-                                      &wc->h_coin_envelope,
-                                      &wc->collectable);
-  if (0 > qs)
-  {
-    GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
-    if (GNUNET_DB_STATUS_HARD_ERROR == qs)
-      *mret = TALER_MHD_reply_with_error (rc->connection,
-                                          MHD_HTTP_INTERNAL_SERVER_ERROR,
-                                          TALER_EC_GENERIC_DB_FETCH_FAILED,
-                                          "get_withdraw_info");
-    return true; /* well, kind-of */
-  }
-  if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
-    return false;
-  /* generate idempotent reply */
-  TEH_METRICS_num_requests[TEH_MT_REQUEST_IDEMPOTENT_WITHDRAW]++;
-  *mret = TALER_MHD_REPLY_JSON_PACK (
-    rc->connection,
-    MHD_HTTP_OK,
-    TALER_JSON_pack_blinded_denom_sig ("ev_sig",
-                                       &wc->collectable.sig));
-  TALER_blinded_denom_sig_free (&wc->collectable.sig);
-  return true;
-}
-
-
-MHD_RESULT
-TEH_handler_withdraw (struct TEH_RequestContext *rc,
-                      const struct TALER_ReservePublicKeyP *reserve_pub,
-                      const json_t *root)
-{
-  struct WithdrawContext wc;
-  struct GNUNET_JSON_Specification spec[] = {
-    GNUNET_JSON_spec_fixed_auto ("reserve_sig",
-                                 &wc.collectable.reserve_sig),
-    GNUNET_JSON_spec_fixed_auto ("denom_pub_hash",
-                                 &wc.collectable.denom_pub_hash),
-    TALER_JSON_spec_blinded_planchet ("coin_ev",
-                                      &wc.blinded_planchet),
-    GNUNET_JSON_spec_end ()
-  };
-  enum TALER_ErrorCode ec;
-  struct TEH_DenominationKey *dk;
-
-  memset (&wc,
-          0,
-          sizeof (wc));
-  wc.collectable.reserve_pub = *reserve_pub;
-  {
-    enum GNUNET_GenericReturnValue res;
-
-    res = TALER_MHD_parse_json_data (rc->connection,
-                                     root,
-                                     spec);
-    if (GNUNET_OK != res)
-      return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
-  }
-  {
-    MHD_RESULT mret;
-    struct TEH_KeyStateHandle *ksh;
-
-    ksh = TEH_keys_get_state ();
-    if (NULL == ksh)
-    {
-      if (! check_request_idempotent (rc,
-                                      &wc,
-                                      &mret))
-      {
-        GNUNET_JSON_parse_free (spec);
-        return TALER_MHD_reply_with_error (rc->connection,
-                                           MHD_HTTP_INTERNAL_SERVER_ERROR,
-                                           
TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING,
-                                           NULL);
-      }
-      GNUNET_JSON_parse_free (spec);
-      return mret;
-    }
-
-    dk = TEH_keys_denomination_by_hash_from_state (
-      ksh,
-      &wc.collectable.denom_pub_hash,
-      NULL,
-      NULL);
-
-    if (NULL == dk)
-    {
-      if (! check_request_idempotent (rc,
-                                      &wc,
-                                      &mret))
-      {
-        GNUNET_JSON_parse_free (spec);
-        return TEH_RESPONSE_reply_unknown_denom_pub_hash (
-          rc->connection,
-          &wc.collectable.denom_pub_hash);
-      }
-      GNUNET_JSON_parse_free (spec);
-      return mret;
-    }
-    if (GNUNET_TIME_absolute_is_past (dk->meta.expire_withdraw.abs_time))
-    {
-      /* This denomination is past the expiration time for withdraws */
-      if (! check_request_idempotent (rc,
-                                      &wc,
-                                      &mret))
-      {
-        GNUNET_JSON_parse_free (spec);
-        return TEH_RESPONSE_reply_expired_denom_pub_hash (
-          rc->connection,
-          &wc.collectable.denom_pub_hash,
-          TALER_EC_EXCHANGE_GENERIC_DENOMINATION_EXPIRED,
-          "WITHDRAW");
-      }
-      GNUNET_JSON_parse_free (spec);
-      return mret;
-    }
-    if (GNUNET_TIME_absolute_is_future (dk->meta.start.abs_time))
-    {
-      /* This denomination is not yet valid, no need to check
-         for idempotency! */
-      GNUNET_JSON_parse_free (spec);
-      return TEH_RESPONSE_reply_expired_denom_pub_hash (
-        rc->connection,
-        &wc.collectable.denom_pub_hash,
-        TALER_EC_EXCHANGE_GENERIC_DENOMINATION_VALIDITY_IN_FUTURE,
-        "WITHDRAW");
-    }
-    if (dk->recoup_possible)
-    {
-      /* This denomination has been revoked */
-      if (! check_request_idempotent (rc,
-                                      &wc,
-                                      &mret))
-      {
-        GNUNET_JSON_parse_free (spec);
-        return TEH_RESPONSE_reply_expired_denom_pub_hash (
-          rc->connection,
-          &wc.collectable.denom_pub_hash,
-          TALER_EC_EXCHANGE_GENERIC_DENOMINATION_REVOKED,
-          "WITHDRAW");
-      }
-      GNUNET_JSON_parse_free (spec);
-      return mret;
-    }
-    if (dk->denom_pub.cipher != wc.blinded_planchet.cipher)
-    {
-      /* denomination cipher and blinded planchet cipher not the same */
-      GNUNET_JSON_parse_free (spec);
-      return TALER_MHD_reply_with_error (rc->connection,
-                                         MHD_HTTP_BAD_REQUEST,
-                                         
TALER_EC_EXCHANGE_GENERIC_CIPHER_MISMATCH,
-                                         NULL);
-    }
-  }
-
-  if (0 >
-      TALER_amount_add (&wc.collectable.amount_with_fee,
-                        &dk->meta.value,
-                        &dk->meta.fees.withdraw))
-  {
-    GNUNET_JSON_parse_free (spec);
-    return TALER_MHD_reply_with_error (rc->connection,
-                                       MHD_HTTP_INTERNAL_SERVER_ERROR,
-                                       
TALER_EC_EXCHANGE_WITHDRAW_AMOUNT_FEE_OVERFLOW,
-                                       NULL);
-  }
-
-  if (GNUNET_OK !=
-      TALER_coin_ev_hash (&wc.blinded_planchet,
-                          &wc.collectable.denom_pub_hash,
-                          &wc.collectable.h_coin_envelope))
-  {
-    GNUNET_break (0);
-    GNUNET_JSON_parse_free (spec);
-    return TALER_MHD_reply_with_error (rc->connection,
-                                       MHD_HTTP_INTERNAL_SERVER_ERROR,
-                                       
TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
-                                       NULL);
-  }
-
-  TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++;
-  if (GNUNET_OK !=
-      TALER_wallet_withdraw_verify (&wc.collectable.denom_pub_hash,
-                                    &wc.collectable.amount_with_fee,
-                                    &wc.collectable.h_coin_envelope,
-                                    &wc.collectable.reserve_pub,
-                                    &wc.collectable.reserve_sig))
-  {
-    GNUNET_break_op (0);
-    GNUNET_JSON_parse_free (spec);
-    return TALER_MHD_reply_with_error (rc->connection,
-                                       MHD_HTTP_FORBIDDEN,
-                                       
TALER_EC_EXCHANGE_WITHDRAW_RESERVE_SIGNATURE_INVALID,
-                                       NULL);
-  }
-
-  {
-    struct TEH_CoinSignData csd = {
-      .h_denom_pub = &wc.collectable.denom_pub_hash,
-      .bp = &wc.blinded_planchet
-    };
-
-    /* Sign before transaction! */
-    ec = TEH_keys_denomination_sign (
-      &csd,
-      false,
-      &wc.collectable.sig);
-  }
-  if (TALER_EC_NONE != ec)
-  {
-    GNUNET_break (0);
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Failed to sign coin: %d\n",
-                ec);
-    GNUNET_JSON_parse_free (spec);
-    return TALER_MHD_reply_with_ec (rc->connection,
-                                    ec,
-                                    NULL);
-  }
-
-  /* run transaction */
-  {
-    MHD_RESULT mhd_ret;
-
-    if (GNUNET_OK !=
-        TEH_DB_run_transaction (rc->connection,
-                                "run withdraw",
-                                TEH_MT_REQUEST_WITHDRAW,
-                                &mhd_ret,
-                                &withdraw_transaction,
-                                &wc))
-    {
-      /* Even if #withdraw_transaction() failed, it may have created a 
signature
-         (or we might have done it optimistically above). */
-      TALER_blinded_denom_sig_free (&wc.collectable.sig);
-      GNUNET_JSON_parse_free (spec);
-      return mhd_ret;
-    }
-  }
-
-  /* Clean up and send back final response */
-  GNUNET_JSON_parse_free (spec);
-
-  if (! wc.kyc.ok)
-    return TEH_RESPONSE_reply_kyc_required (rc->connection,
-                                            &wc.h_account_payto,
-                                            &wc.kyc);
-
-  if (TALER_AML_NORMAL != wc.aml_decision)
-    return TEH_RESPONSE_reply_aml_blocked (rc->connection,
-                                           wc.aml_decision);
-
-  {
-    MHD_RESULT ret;
-
-    ret = TALER_MHD_REPLY_JSON_PACK (
-      rc->connection,
-      MHD_HTTP_OK,
-      TALER_JSON_pack_blinded_denom_sig ("ev_sig",
-                                         &wc.collectable.sig));
-    TALER_blinded_denom_sig_free (&wc.collectable.sig);
-    return ret;
-  }
-}
-
-
-/* end of taler-exchange-httpd_withdraw.c */
diff --git a/src/exchange/taler-exchange-httpd_withdraw.h 
b/src/exchange/taler-exchange-httpd_withdraw.h
deleted file mode 100644
index 2ec76bb9..00000000
--- a/src/exchange/taler-exchange-httpd_withdraw.h
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
-  This file is part of TALER
-  Copyright (C) 2014-2021 Taler Systems SA
-
-  TALER is free software; you can redistribute it and/or modify it under the
-  terms of the GNU Affero General Public License as published by the Free 
Software
-  Foundation; either version 3, or (at your option) any later version.
-
-  TALER is distributed in the hope that it will be useful, but WITHOUT ANY
-  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
-  A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more 
details.
-
-  You should have received a copy of the GNU Affero General Public License 
along with
-  TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
-*/
-/**
- * @file taler-exchange-httpd_withdraw.h
- * @brief Handle /reserve/withdraw requests
- * @author Florian Dold
- * @author Benedikt Mueller
- * @author Christian Grothoff
- */
-#ifndef TALER_EXCHANGE_HTTPD_WITHDRAW_H
-#define TALER_EXCHANGE_HTTPD_WITHDRAW_H
-
-#include <microhttpd.h>
-#include "taler-exchange-httpd.h"
-
-
-/**
- * Handle a "/reserves/$RESERVE_PUB/withdraw" request.  Parses the requested 
"denom_pub" which
- * specifies the key/value of the coin to be withdrawn, and checks that the
- * signature "reserve_sig" makes this a valid withdrawal request from the
- * specified reserve.  If so, the envelope with the blinded coin "coin_ev" is
- * passed down to execute the withdrawal operation.
- *
- * @param rc request context
- * @param root uploaded JSON data
- * @param reserve_pub public key of the reserve
- * @return MHD result code
-  */
-MHD_RESULT
-TEH_handler_withdraw (struct TEH_RequestContext *rc,
-                      const struct TALER_ReservePublicKeyP *reserve_pub,
-                      const json_t *root);
-
-#endif
diff --git a/src/exchangedb/exchange_do_age_withdraw.sql 
b/src/exchangedb/exchange_do_age_withdraw.sql
index 184a3e49..c696bd80 100644
--- a/src/exchangedb/exchange_do_age_withdraw.sql
+++ b/src/exchangedb/exchange_do_age_withdraw.sql
@@ -29,6 +29,7 @@ CREATE OR REPLACE FUNCTION exchange_do_age_withdraw(
   IN denom_sigs BYTEA[],
   OUT reserve_found BOOLEAN,
   OUT balance_ok BOOLEAN,
+  OUT reserve_balance taler_amount,
   OUT age_ok BOOLEAN,
   OUT required_age INT2, -- in years ϵ [0,1..)
   OUT reserve_birthday INT4,
@@ -38,7 +39,7 @@ AS $$
 DECLARE
   reserve RECORD;
   difference RECORD;
-  balance  taler_amount;
+  balance taler_amount;
   not_before date;
   earliest_date date;
 BEGIN
@@ -48,6 +49,7 @@ BEGIN
 --         reserves_in by reserve_pub (SELECT)
 --         wire_targets by wire_target_h_payto
 
+-- FIXME-Oec: never select-*!
 SELECT *
   INTO reserve
   FROM exchange.reserves
@@ -59,6 +61,8 @@ THEN
   age_ok = FALSE;
   required_age=-1;
   conflict=FALSE;
+  reserve_balance.val = 0;
+  reserve_balance.frac = 0;
   balance_ok=FALSE;
   RETURN;
 END IF;
@@ -66,7 +70,7 @@ END IF;
 reserve_found = TRUE;
 conflict=FALSE;  -- not really yet determined
 
-balance = reserve.current_balance;
+reserve_balance = reserve.current_balance;
 reserve_birthday = reserve.birthday;
 
 -- Check age requirements
@@ -99,7 +103,7 @@ required_age=0;
 -- Check reserve balance is sufficient.
 SELECT *
 INTO difference
-FROM amount_left_minus_right(balance
+FROM amount_left_minus_right(reserve_balance
                             ,amount_with_fee);
 
 balance_ok = difference.ok;
diff --git a/src/exchangedb/exchange_do_batch_withdraw.sql 
b/src/exchangedb/exchange_do_batch_withdraw.sql
index be279ab7..a27f2348 100644
--- a/src/exchangedb/exchange_do_batch_withdraw.sql
+++ b/src/exchangedb/exchange_do_batch_withdraw.sql
@@ -24,6 +24,7 @@ CREATE OR REPLACE FUNCTION exchange_do_batch_withdraw(
   IN do_age_check BOOLEAN,
   OUT reserve_found BOOLEAN,
   OUT balance_ok BOOLEAN,
+  OUT reserve_balance taler_amount,
   OUT age_ok BOOLEAN,
   OUT allowed_maximum_age INT2, -- in years
   OUT ruuid INT8)
@@ -40,7 +41,7 @@ BEGIN
 --         reserves_in by reserve_pub (SELECT)
 --         wire_targets by wire_target_h_payto
 
-
+-- FIXME-Oec: do not use select-*!
 SELECT *
   INTO reserve
   FROM exchange.reserves
@@ -51,13 +52,15 @@ THEN
   -- reserve unknown
   reserve_found=FALSE;
   balance_ok=FALSE;
+  reserve_balance.frac = 0;
+  reserve_balance.val = 0;
   age_ok=FALSE;
   allowed_maximum_age=0;
   ruuid=2;
   RETURN;
 END IF;
 reserve_found=TRUE;
-
+reserve_balance = reserve.current_balance;
 ruuid = reserve.reserve_uuid;
 
 -- Check if age requirements are present
@@ -79,24 +82,23 @@ ELSE
   RETURN;
 END IF;
 
-balance = reserve.current_balance;
 
 -- Check reserve balance is sufficient.
-IF (balance.val > amount.val)
+IF (reserve_balance.val > amount.val)
 THEN
-  IF (balance.frac >= amount.frac)
+  IF (reserve_balance.frac >= amount.frac)
   THEN
-    balance.val=balance.val - amount.val;
-    balance.frac=balance.frac - amount.frac;
+    balance.val=reserve_balance.val - amount.val;
+    balance.frac=reserve_balance.frac - amount.frac;
   ELSE
-    balance.val=balance.val - amount.val - 1;
-    balance.frac=balance.frac + 100000000 - amount.frac;
+    balance.val=reserve_balance.val - amount.val - 1;
+    balance.frac=reserve_balance.frac + 100000000 - amount.frac;
   END IF;
 ELSE
-  IF (balance.val = amount.val) AND (balance.frac >= amount.frac)
+  IF (reserve_balance.val = amount.val) AND (reserve_balance.frac >= 
amount.frac)
   THEN
     balance.val=0;
-    balance.frac=balance.frac - amount.frac;
+    balance.frac=reserve_balance.frac - amount.frac;
   ELSE
     balance_ok=FALSE;
     RETURN;
diff --git a/src/exchangedb/exchange_do_reserve_open.sql 
b/src/exchangedb/exchange_do_reserve_open.sql
index 7aca78b8..5d36d1af 100644
--- a/src/exchangedb/exchange_do_reserve_open.sql
+++ b/src/exchangedb/exchange_do_reserve_open.sql
@@ -27,7 +27,9 @@ CREATE OR REPLACE FUNCTION exchange_do_reserve_open(
   IN in_open_fee taler_amount,
   OUT out_open_cost taler_amount,
   OUT out_final_expiration INT8,
-  OUT out_no_funds BOOLEAN)
+  OUT out_no_reserve BOOLEAN,
+  OUT out_no_funds BOOLEAN,
+  OUT out_reserve_balance taler_amount)
 LANGUAGE plpgsql
 AS $$
 DECLARE
@@ -41,6 +43,7 @@ DECLARE
   reserve RECORD;
 BEGIN
 
+-- FIXME: do not use SELECT-*
 -- FIXME: use SELECT FOR UPDATE?
 SELECT *
   INTO reserve
@@ -49,12 +52,19 @@ SELECT *
 
 IF NOT FOUND
 THEN
-  -- FIXME: do we need to set a 'not found'?
   RAISE NOTICE 'reserve not found';
+  out_no_reserve = TRUE;
+  out_no_funds = TRUE;
+  out_reserve_balance.val = 0;
+  out_reserve_balance.frac = 0;
+  out_open_cost.val = 0;
+  out_open_cost.frac = 0;
+  out_final_expiration = 0;
   RETURN;
 END IF;
 
-my_balance = reserve.current_balance;
+out_no_reserve = FALSE;
+out_reserve_balance = reserve.current_balance;
 
 -- Do not allow expiration time to start in the past already
 IF (reserve.expiration_date < in_now)
@@ -143,21 +153,21 @@ THEN
 END IF;
 
 -- Check reserve balance is sufficient.
-IF (my_balance.val > in_reserve_payment.val)
+IF (out_reserve_balance.val > in_reserve_payment.val)
 THEN
-  IF (my_balance.frac >= in_reserve_payment.frac)
+  IF (out_reserve_balance.frac >= in_reserve_payment.frac)
   THEN
-    my_balance.val=my_balance.val - in_reserve_payment.val;
-    my_balance.frac=my_balance.frac - in_reserve_payment.frac;
+    my_balance.val=out_reserve_balance.val - in_reserve_payment.val;
+    my_balance.frac=out_reserve_balance.frac - in_reserve_payment.frac;
   ELSE
-    my_balance.val=my_balance.val - in_reserve_payment.val - 1;
-    my_balance.frac=my_balance.frac + 100000000 - in_reserve_payment.frac;
+    my_balance.val=out_reserve_balance.val - in_reserve_payment.val - 1;
+    my_balance.frac=out_reserve_balance.frac + 100000000 - 
in_reserve_payment.frac;
   END IF;
 ELSE
-  IF (my_balance.val = in_reserve_payment.val) AND (my_balance.frac >= 
in_reserve_payment.frac)
+  IF (out_reserve_balance.val = in_reserve_payment.val) AND 
(out_reserve_balance.frac >= in_reserve_payment.frac)
   THEN
     my_balance.val=0;
-    my_balance.frac=my_balance.frac - in_reserve_payment.frac;
+    my_balance.frac=out_reserve_balance.frac - in_reserve_payment.frac;
   ELSE
     out_final_expiration = reserve.expiration_date;
     out_open_cost.val = my_cost.val;
diff --git a/src/exchangedb/pg_do_age_withdraw.c 
b/src/exchangedb/pg_do_age_withdraw.c
index 93e15716..99584098 100644
--- a/src/exchangedb/pg_do_age_withdraw.c
+++ b/src/exchangedb/pg_do_age_withdraw.c
@@ -36,6 +36,7 @@ TEH_PG_do_age_withdraw (
   struct GNUNET_TIME_Timestamp now,
   bool *found,
   bool *balance_ok,
+  struct TALER_Amount *reserve_balance,
   bool *age_ok,
   uint16_t *required_age,
   uint32_t *reserve_birthday,
@@ -69,6 +70,8 @@ TEH_PG_do_age_withdraw (
                                 found),
     GNUNET_PQ_result_spec_bool ("balance_ok",
                                 balance_ok),
+    TALER_PQ_RESULT_SPEC_AMOUNT ("reserve_balance",
+                                 reserve_balance),
     GNUNET_PQ_result_spec_bool ("age_ok",
                                 age_ok),
     GNUNET_PQ_result_spec_uint16 ("required_age",
@@ -83,15 +86,12 @@ TEH_PG_do_age_withdraw (
   gc = GNUNET_TIME_absolute_to_timestamp (
     GNUNET_TIME_absolute_add (now.abs_time,
                               pg->legal_reserve_expiration_time));
-
-
-  /* Used in #postgres_do_age_withdraw() to
-        update the reserve balance and check its status */
   PREPARE (pg,
            "call_age_withdraw",
            "SELECT "
            " reserve_found"
            ",balance_ok"
+           ",reserve_balance"
            ",age_ok"
            ",required_age"
            ",reserve_birthday"
diff --git a/src/exchangedb/pg_do_age_withdraw.h 
b/src/exchangedb/pg_do_age_withdraw.h
index 71376022..fb435a30 100644
--- a/src/exchangedb/pg_do_age_withdraw.h
+++ b/src/exchangedb/pg_do_age_withdraw.h
@@ -34,6 +34,7 @@
  * @param now current time (rounded)
  * @param[out] found set to true if the reserve was found
  * @param[out] balance_ok set to true if the balance was sufficient
+ * @param[out] reserve_balance set to the original reserve balance (at the 
start of this transaction)
  * @param[out] age_ok set to true if no age requirements are present on the 
reserve
  * @param[out] required_age if @e age_ok is false, set to the maximum allowed 
age when withdrawing from this reserve
  * @param[out] reserve_birthday if @e age_ok is false, set to the birthday of 
the reserve
@@ -47,6 +48,7 @@ TEH_PG_do_age_withdraw (
   const struct GNUNET_TIME_Timestamp now,
   bool *found,
   bool *balance_ok,
+  struct TALER_Amount *reserve_balance,
   bool *age_ok,
   uint16_t *required_age,
   uint32_t *reserve_birthday,
diff --git a/src/exchangedb/pg_do_batch_withdraw.c 
b/src/exchangedb/pg_do_batch_withdraw.c
index f89f3277..f5571ddb 100644
--- a/src/exchangedb/pg_do_batch_withdraw.c
+++ b/src/exchangedb/pg_do_batch_withdraw.c
@@ -36,6 +36,7 @@ TEH_PG_do_batch_withdraw (
   bool do_age_check,
   bool *found,
   bool *balance_ok,
+  struct TALER_Amount *reserve_balance,
   bool *age_ok,
   uint16_t *allowed_maximum_age,
   uint64_t *ruuid)
@@ -56,6 +57,8 @@ TEH_PG_do_batch_withdraw (
                                 found),
     GNUNET_PQ_result_spec_bool ("balance_ok",
                                 balance_ok),
+    TALER_PQ_RESULT_SPEC_AMOUNT ("reserve_balance",
+                                 reserve_balance),
     GNUNET_PQ_result_spec_bool ("age_ok",
                                 age_ok),
     GNUNET_PQ_result_spec_uint16 ("allowed_maximum_age",
@@ -73,6 +76,7 @@ TEH_PG_do_batch_withdraw (
            "SELECT "
            " reserve_found"
            ",balance_ok"
+           ",reserve_balance"
            ",age_ok"
            ",allowed_maximum_age"
            ",ruuid"
diff --git a/src/exchangedb/pg_do_batch_withdraw.h 
b/src/exchangedb/pg_do_batch_withdraw.h
index d0b86574..486f8d1b 100644
--- a/src/exchangedb/pg_do_batch_withdraw.h
+++ b/src/exchangedb/pg_do_batch_withdraw.h
@@ -36,6 +36,7 @@
  * @param age_check_required if true, fail if age requirements are set on the 
reserve
  * @param[out] found set to true if the reserve was found
  * @param[out] balance_ok set to true if the balance was sufficient
+ * @param[out] reserve_balance set to the original reserve balance (at the 
start of this transaction)
  * @param[out] age_ok set to true if no age requirements are present on the 
reserve
  * @param[out] allowed_maximum_age if @e age_ok is false, set to the maximum 
allowed age when withdrawing from this reserve (client needs to call 
age-withdraw)
  * @param[out] ruuid set to the reserve's UUID (reserves table row)
@@ -50,6 +51,7 @@ TEH_PG_do_batch_withdraw (
   bool age_check_required,
   bool *found,
   bool *balance_ok,
+  struct TALER_Amount *reserve_balance,
   bool *age_ok,
   uint16_t *allowed_maximum_age,
   uint64_t *ruuid);
diff --git a/src/exchangedb/pg_do_reserve_open.c 
b/src/exchangedb/pg_do_reserve_open.c
index e82a3e9c..b15c96dd 100644
--- a/src/exchangedb/pg_do_reserve_open.c
+++ b/src/exchangedb/pg_do_reserve_open.c
@@ -38,6 +38,7 @@ TEH_PG_do_reserve_open (
   struct GNUNET_TIME_Timestamp now,
   const struct TALER_Amount *open_fee,
   bool *no_funds,
+  struct TALER_Amount *reserve_balance,
   struct TALER_Amount *open_cost,
   struct GNUNET_TIME_Timestamp *final_expiration)
 {
@@ -60,16 +61,23 @@ TEH_PG_do_reserve_open (
                                  open_fee),
     GNUNET_PQ_query_param_end
   };
+  bool no_reserve = true;
   struct GNUNET_PQ_ResultSpec rs[] = {
     TALER_PQ_result_spec_amount ("out_open_cost",
                                  pg->currency,
                                  open_cost),
+    TALER_PQ_result_spec_amount ("out_reserve_balance",
+                                 pg->currency,
+                                 reserve_balance),
     GNUNET_PQ_result_spec_timestamp ("out_final_expiration",
                                      final_expiration),
+    GNUNET_PQ_result_spec_bool ("out_no_reserve",
+                                &no_reserve),
     GNUNET_PQ_result_spec_bool ("out_no_funds",
                                 no_funds),
     GNUNET_PQ_result_spec_end
   };
+  enum GNUNET_DB_QueryStatus qs;
 
   PREPARE (pg,
            "do_reserve_open",
@@ -77,10 +85,17 @@ TEH_PG_do_reserve_open (
            " out_open_cost"
            ",out_final_expiration"
            ",out_no_funds"
+           ",out_no_reserve"
+           ",out_reserve_balance"
            " FROM exchange_do_reserve_open"
            " ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10);");
-  return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
-                                                   "do_reserve_open",
-                                                   params,
-                                                   rs);
+  qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
+                                                 "do_reserve_open",
+                                                 params,
+                                                 rs);
+  if (qs <= 0)
+    return qs;
+  if (no_reserve)
+    return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
+  return qs;
 }
diff --git a/src/exchangedb/pg_do_reserve_open.h 
b/src/exchangedb/pg_do_reserve_open.h
index acf2d67e..432f3f66 100644
--- a/src/exchangedb/pg_do_reserve_open.h
+++ b/src/exchangedb/pg_do_reserve_open.h
@@ -39,6 +39,7 @@
  * @param now when did we the client initiate the action
  * @param open_fee annual fee to be charged for the open operation by the 
exchange
  * @param[out] no_funds set to true if reserve balance is insufficient
+ * @param[out] reserve_balance set to the original reserve balance (at the 
start of this transaction)
  * @param[out] open_cost set to the actual cost
  * @param[out] final_expiration when will the reserve expire now
  * @return transaction status code
@@ -55,6 +56,7 @@ TEH_PG_do_reserve_open (
   struct GNUNET_TIME_Timestamp now,
   const struct TALER_Amount *open_fee,
   bool *no_funds,
+  struct TALER_Amount *reserve_balance,
   struct TALER_Amount *open_cost,
   struct GNUNET_TIME_Timestamp *final_expiration);
 
diff --git a/src/exchangedb/pg_do_withdraw.c b/src/exchangedb/pg_do_withdraw.c
index e32afcda..cb511b10 100644
--- a/src/exchangedb/pg_do_withdraw.c
+++ b/src/exchangedb/pg_do_withdraw.c
@@ -26,6 +26,8 @@
 #include "pg_helper.h"
 
 
+// FIXME: this function is currently only used in tests.
+// Replace by batch withdraw function in tests as well!
 enum GNUNET_DB_QueryStatus
 TEH_PG_do_withdraw (
   void *cls,
diff --git a/src/include/taler_exchange_service.h 
b/src/include/taler_exchange_service.h
index 30c966d7..75252449 100644
--- a/src/include/taler_exchange_service.h
+++ b/src/include/taler_exchange_service.h
@@ -2449,15 +2449,6 @@ TALER_EXCHANGE_reserves_history_cancel (
   struct TALER_EXCHANGE_ReservesHistoryHandle *rsh);
 
 
-/* ********************* POST /reserves/$RESERVE_PUB/withdraw 
*********************** */
-
-
-/**
- * @brief A /reserves/$RESERVE_PUB/withdraw Handle
- */
-struct TALER_EXCHANGE_WithdrawHandle;
-
-
 /**
  * Information input into the withdraw process per coin.
  */
@@ -2511,119 +2502,6 @@ struct TALER_EXCHANGE_PrivateCoinDetails
 };
 
 
-/**
- * Details about a response for a withdraw request.
- */
-struct TALER_EXCHANGE_WithdrawResponse
-{
-  /**
-   * HTTP response data.
-   */
-  struct TALER_EXCHANGE_HttpResponse hr;
-
-  /**
-   * Details about the response.
-   */
-  union
-  {
-    /**
-     * Details if the status is #MHD_HTTP_OK.
-     */
-    struct TALER_EXCHANGE_PrivateCoinDetails ok;
-
-    /**
-     * Details if the status is #MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS.
-     */
-    struct
-    {
-      /**
-       * Requirement row that the merchant should use
-       * to check for its KYC status.
-       */
-      uint64_t requirement_row;
-
-      /**
-       * Hash of the payto-URI of the account to KYC;
-       */
-      struct TALER_PaytoHashP h_payto;
-
-    } unavailable_for_legal_reasons;
-
-    /**
-     * Details if the status is #MHD_HTTP_CONFLICT.
-     */
-    struct
-    {
-      /* TODO: returning full details is not implemented */
-    } conflict;
-
-    /**
-     * Details if the status is #MHD_HTTP_GONE.
-     */
-    struct
-    {
-      /* TODO: returning full details is not implemented */
-    } gone;
-
-  } details;
-};
-
-
-/**
- * Callbacks of this type are used to serve the result of submitting a
- * withdraw request to a exchange.
- *
- * @param cls closure
- * @param wr response details
- */
-typedef void
-(*TALER_EXCHANGE_WithdrawCallback) (
-  void *cls,
-  const struct TALER_EXCHANGE_WithdrawResponse *wr);
-
-
-/**
- * Withdraw a coin from the exchange using a /reserves/$RESERVE_PUB/withdraw
- * request.  This API is typically used by a wallet to withdraw from a
- * reserve.
- *
- * Note that to ensure that no money is lost in case of hardware
- * failures, the caller must have committed (most of) the arguments to
- * disk before calling, and be ready to repeat the request with the
- * same arguments in case of failures.
- *
- * @param curl_ctx The curl context to use
- * @param exchange_url The base-URL of the exchange
- * @param keys The /keys material from the exchange
- * @param reserve_priv private key of the reserve to withdraw from
- * @param wci inputs that determine the planchet
- * @param res_cb the callback to call when the final result for this request 
is available
- * @param res_cb_cls closure for @a res_cb
- * @return NULL
- *         if the inputs are invalid (i.e. denomination key not with this 
exchange).
- *         In this case, the callback is not called.
- */
-struct TALER_EXCHANGE_WithdrawHandle *
-TALER_EXCHANGE_withdraw (
-  struct GNUNET_CURL_Context *curl_ctx,
-  const char *exchange_url,
-  struct TALER_EXCHANGE_Keys *keys,
-  const struct TALER_ReservePrivateKeyP *reserve_priv,
-  const struct TALER_EXCHANGE_WithdrawCoinInput *wci,
-  TALER_EXCHANGE_WithdrawCallback res_cb,
-  void *res_cb_cls);
-
-
-/**
- * Cancel a withdraw status request.  This function cannot be used
- * on a request handle if a response is already served for it.
- *
- * @param wh the withdraw handle
- */
-void
-TALER_EXCHANGE_withdraw_cancel (struct TALER_EXCHANGE_WithdrawHandle *wh);
-
-
 /**
  * @brief A /reserves/$RESERVE_PUB/batch-withdraw Handle
  */
@@ -2883,6 +2761,21 @@ struct TALER_EXCHANGE_BatchWithdraw2Response
       unsigned int blind_sigs_length;
 
     } ok;
+
+    struct
+    {
+      /**
+       * Hash of the payto-URI of the account to KYC;
+       */
+      struct TALER_PaytoHashP h_payto;
+
+      /**
+       * ID identifying the KYC requirement to withdraw.
+       */
+      uint64_t kyc_requirement_id;
+
+    } unavailable_for_legal_reasons;
+
   } details;
 
 };
@@ -3084,6 +2977,7 @@ struct TALER_EXCHANGE_AgeWithdrawResponse
   } details;
 };
 
+
 typedef void
 (*TALER_EXCHANGE_AgeWithdrawCallback)(
   void *cls,
diff --git a/src/include/taler_exchangedb_plugin.h 
b/src/include/taler_exchangedb_plugin.h
index f0f4d6aa..20dca495 100644
--- a/src/include/taler_exchangedb_plugin.h
+++ b/src/include/taler_exchangedb_plugin.h
@@ -3930,6 +3930,7 @@ struct TALER_EXCHANGEDB_Plugin
    * @param do_age_check if set, the batch-withdrawal can only succeed when 
the reserve has no age restriction (birthday) set.
    * @param[out] found set to true if the reserve was found
    * @param[out] balance_ok set to true if the balance was sufficient
+   * @param[out] reserve_balance set to original balance of the reserve
    * @param[out] age_ok set to true if no age requirements were defined on the 
reserve or @e do_age_check was false
    * @param[out] allowed_maximum_age when @e age_ok is false, set to the 
allowed maximum age for withdrawal from the reserve.  The client MUST then use 
the age-withdraw endpoint
    * @param[out] ruuid set to the reserve's UUID (reserves table row)
@@ -3944,6 +3945,7 @@ struct TALER_EXCHANGEDB_Plugin
     bool do_age_check,
     bool *found,
     bool *balance_ok,
+    struct TALER_Amount *reserve_balance,
     bool *age_ok,
     uint16_t *allowed_maximum_age,
     uint64_t *ruuid);
@@ -4001,6 +4003,7 @@ struct TALER_EXCHANGEDB_Plugin
    * @param commitment corresponding commitment for the age-withdraw
    * @param[out] found set to true if the reserve was found
    * @param[out] balance_ok set to true if the balance was sufficient
+   * @param[out] reserve_balance set to original balance of the reserve
    * @param[out] age_ok set to true if age requirements were met
    * @param[out] allowed_maximum_age if @e age_ok is FALSE, this is set to the 
allowed maximum age
    * @param[out] reserve_birthday if @e age_ok is FALSE, this is set to the 
reserve's birthday
@@ -4013,6 +4016,7 @@ struct TALER_EXCHANGEDB_Plugin
     struct GNUNET_TIME_Timestamp now,
     bool *found,
     bool *balance_ok,
+    struct TALER_Amount *reserve_balance,
     bool *age_ok,
     uint16_t *allowed_maximum_age,
     uint32_t *reserve_birthday,
@@ -4923,6 +4927,7 @@ struct TALER_EXCHANGEDB_Plugin
    * @param now when did we the client initiate the action
    * @param open_fee annual fee to be charged for the open operation by the 
exchange
    * @param[out] no_funds set to true if reserve balance is insufficient
+   * @param[out] reserve_balance set to original balance of the reserve
    * @param[out] open_cost set to the actual cost
    * @param[out] final_expiration when will the reserve expire now
    * @return transaction status code
@@ -4938,6 +4943,7 @@ struct TALER_EXCHANGEDB_Plugin
                      struct GNUNET_TIME_Timestamp now,
                      const struct TALER_Amount *open_fee,
                      bool *no_funds,
+                     struct TALER_Amount *reserve_balance,
                      struct TALER_Amount *open_cost,
                      struct GNUNET_TIME_Timestamp *final_expiration);
 
diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am
index 12f991d8..230dfba2 100644
--- a/src/lib/Makefile.am
+++ b/src/lib/Makefile.am
@@ -75,9 +75,7 @@ libtalerexchange_la_SOURCES = \
   exchange_api_reserves_history.c \
   exchange_api_reserves_open.c \
   exchange_api_stefan.c \
-  exchange_api_transfers_get.c \
-  exchange_api_withdraw.c \
-  exchange_api_withdraw2.c
+  exchange_api_transfers_get.c 
 libtalerexchange_la_LIBADD = \
   libtalerauditor.la \
   $(top_builddir)/src/json/libtalerjson.la \
diff --git a/src/lib/exchange_api_age_withdraw.c 
b/src/lib/exchange_api_age_withdraw.c
index 4092c5c2..ea9c0371 100644
--- a/src/lib/exchange_api_age_withdraw.c
+++ b/src/lib/exchange_api_age_withdraw.c
@@ -396,27 +396,6 @@ handle_reserve_age_withdraw_blinded_finished (
     GNUNET_assert (NULL == awbh->callback);
     TALER_EXCHANGE_age_withdraw_blinded_cancel (awbh);
     return;
-  case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS:
-    /* only validate reply is well-formed */
-    {
-      uint64_t ptu;
-      struct GNUNET_JSON_Specification spec[] = {
-        GNUNET_JSON_spec_uint64 ("legitimization_uuid",
-                                 &ptu),
-        GNUNET_JSON_spec_end ()
-      };
-
-      if (GNUNET_OK !=
-          GNUNET_JSON_parse (j_response,
-                             spec,
-                             NULL, NULL))
-      {
-        GNUNET_break_op (0);
-        awbr.hr.http_status = 0;
-        awbr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
-        break;
-      }
-    }
   case MHD_HTTP_BAD_REQUEST:
     /* This should never happen, either us or the exchange is buggy
        (or API version conflict); just pass JSON reply to the application */
@@ -452,6 +431,27 @@ handle_reserve_age_withdraw_blinded_finished (
     awbr.hr.ec = TALER_JSON_get_error_code (j_response);
     awbr.hr.hint = TALER_JSON_get_error_hint (j_response);
     break;
+  case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS:
+    /* only validate reply is well-formed */
+    {
+      uint64_t ptu;
+      struct GNUNET_JSON_Specification spec[] = {
+        GNUNET_JSON_spec_uint64 ("requirement_row",
+                                 &ptu),
+        GNUNET_JSON_spec_end ()
+      };
+
+      if (GNUNET_OK !=
+          GNUNET_JSON_parse (j_response,
+                             spec,
+                             NULL, NULL))
+      {
+        GNUNET_break_op (0);
+        awbr.hr.http_status = 0;
+        awbr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
+        break;
+      }
+    }
   case MHD_HTTP_INTERNAL_SERVER_ERROR:
     /* Server had an internal issue; we should retry, but this API
        leaves this to the application */
diff --git a/src/lib/exchange_api_batch_withdraw2.c 
b/src/lib/exchange_api_batch_withdraw2.c
index 12c6aeff..b6f77319 100644
--- a/src/lib/exchange_api_batch_withdraw2.c
+++ b/src/lib/exchange_api_batch_withdraw2.c
@@ -193,28 +193,6 @@ handle_reserve_batch_withdraw_finished (void *cls,
     GNUNET_assert (NULL == wh->cb);
     TALER_EXCHANGE_batch_withdraw2_cancel (wh);
     return;
-  case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS:
-    /* only validate reply is well-formed */
-    {
-      uint64_t ptu;
-      struct GNUNET_JSON_Specification spec[] = {
-        GNUNET_JSON_spec_uint64 ("legitimization_uuid",
-                                 &ptu),
-        GNUNET_JSON_spec_end ()
-      };
-
-      if (GNUNET_OK !=
-          GNUNET_JSON_parse (j,
-                             spec,
-                             NULL, NULL))
-      {
-        GNUNET_break_op (0);
-        bwr.hr.http_status = 0;
-        bwr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
-        break;
-      }
-    }
-    break;
   case MHD_HTTP_BAD_REQUEST:
     /* This should never happen, either us or the exchange is buggy
        (or API version conflict); just pass JSON reply to the application */
@@ -249,6 +227,29 @@ handle_reserve_batch_withdraw_finished (void *cls,
     bwr.hr.ec = TALER_JSON_get_error_code (j);
     bwr.hr.hint = TALER_JSON_get_error_hint (j);
     break;
+  case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS:
+    {
+      struct GNUNET_JSON_Specification spec[] = {
+        GNUNET_JSON_spec_fixed_auto (
+          "h_payto",
+          &bwr.details.unavailable_for_legal_reasons.h_payto),
+        GNUNET_JSON_spec_uint64 ("requirement_row",
+                                 &bwr.details.unavailable_for_legal_reasons.
+                                 kyc_requirement_id),
+        GNUNET_JSON_spec_end ()
+      };
+
+      if (GNUNET_OK !=
+          GNUNET_JSON_parse (j,
+                             spec,
+                             NULL, NULL))
+      {
+        GNUNET_break_op (0);
+        bwr.hr.http_status = 0;
+        bwr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
+        break;
+      }
+    }
   case MHD_HTTP_INTERNAL_SERVER_ERROR:
     /* Server had an internal issue; we should retry, but this API
        leaves this to the application */
diff --git a/src/lib/exchange_api_withdraw.c b/src/lib/exchange_api_withdraw.c
deleted file mode 100644
index 87218989..00000000
--- a/src/lib/exchange_api_withdraw.c
+++ /dev/null
@@ -1,364 +0,0 @@
-/*
-  This file is part of TALER
-  Copyright (C) 2014-2022 Taler Systems SA
-
-  TALER is free software; you can redistribute it and/or modify it under the
-  terms of the GNU General Public License as published by the Free Software
-  Foundation; either version 3, or (at your option) any later version.
-
-  TALER is distributed in the hope that it will be useful, but WITHOUT ANY
-  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
-  A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
-
-  You should have received a copy of the GNU General Public License along with
-  TALER; see the file COPYING.  If not, see
-  <http://www.gnu.org/licenses/>
-*/
-/**
- * @file lib/exchange_api_withdraw.c
- * @brief Implementation of /reserves/$RESERVE_PUB/withdraw requests with 
blinding/unblinding
- * @author Christian Grothoff
- */
-#include "platform.h"
-#include <jansson.h>
-#include <microhttpd.h> /* just for HTTP status codes */
-#include <gnunet/gnunet_util_lib.h>
-#include <gnunet/gnunet_json_lib.h>
-#include <gnunet/gnunet_curl_lib.h>
-#include "taler_exchange_service.h"
-#include "taler_json_lib.h"
-#include "exchange_api_handle.h"
-#include "taler_signatures.h"
-#include "exchange_api_curl_defaults.h"
-
-
-/**
- * @brief A Withdraw Handle
- */
-struct TALER_EXCHANGE_WithdrawHandle
-{
-
-  /**
-   * The curl context to use
-   */
-  struct GNUNET_CURL_Context *curl_ctx;
-
-  /**
-   * The base-URL to the exchange
-   */
-  const char *exchange_url;
-
-  /**
-   * The /keys material from the exchange
-   */
-  struct TALER_EXCHANGE_Keys *keys;
-
-  /**
-   * Handle for the actual (internal) withdraw operation.
-   */
-  struct TALER_EXCHANGE_Withdraw2Handle *wh2;
-
-  /**
-   * Function to call with the result.
-   */
-  TALER_EXCHANGE_WithdrawCallback cb;
-
-  /**
-   * Closure for @a cb.
-   */
-  void *cb_cls;
-
-  /**
-   * Reserve private key.
-   */
-  const struct TALER_ReservePrivateKeyP *reserve_priv;
-
-  /**
-   * Seed of the planchet.
-   */
-  struct TALER_PlanchetMasterSecretP ps;
-
-  /**
-   *  blinding secret
-   */
-  union TALER_DenominationBlindingKeyP bks;
-
-  /**
-   * Private key of the coin we are withdrawing.
-   */
-  struct TALER_CoinSpendPrivateKeyP priv;
-
-  /**
-   * Details of the planchet.
-   */
-  struct TALER_PlanchetDetail pd;
-
-  /**
-   * Values of the @cipher selected
-   */
-  struct TALER_ExchangeWithdrawValues alg_values;
-
-  /**
-   * Hash of the age commitment for this coin, if applicable. Maybe NULL
-   */
-  const struct TALER_AgeCommitmentHash *ach;
-
-  /**
-   * Denomination key we are withdrawing.
-   */
-  struct TALER_EXCHANGE_DenomPublicKey pk;
-
-  /**
-   * Hash of the public key of the coin we are signing.
-   */
-  struct TALER_CoinPubHashP c_hash;
-
-  /**
-   * Handler for the CS R request (only used for TALER_DENOMINATION_CS 
denominations)
-   */
-  struct TALER_EXCHANGE_CsRWithdrawHandle *csrh;
-
-};
-
-
-/**
- * Function called when we're done processing the
- * HTTP /reserves/$RESERVE_PUB/withdraw request.
- *
- * @param cls the `struct TALER_EXCHANGE_WithdrawHandle`
- * @param w2r response data
- */
-static void
-handle_reserve_withdraw_finished (
-  void *cls,
-  const struct TALER_EXCHANGE_Withdraw2Response *w2r)
-{
-  struct TALER_EXCHANGE_WithdrawHandle *wh = cls;
-  struct TALER_EXCHANGE_WithdrawResponse wr = {
-    .hr = w2r->hr
-  };
-
-  wh->wh2 = NULL;
-  switch (w2r->hr.http_status)
-  {
-  case MHD_HTTP_OK:
-    {
-      struct TALER_FreshCoin fc;
-
-      if (GNUNET_OK !=
-          TALER_planchet_to_coin (&wh->pk.key,
-                                  &w2r->details.ok.blind_sig,
-                                  &wh->bks,
-                                  &wh->priv,
-                                  wh->ach,
-                                  &wh->c_hash,
-                                  &wh->alg_values,
-                                  &fc))
-      {
-        wr.hr.http_status = 0;
-        wr.hr.ec = TALER_EC_EXCHANGE_WITHDRAW_UNBLIND_FAILURE;
-        break;
-      }
-      wr.details.ok.coin_priv = wh->priv;
-      wr.details.ok.bks = wh->bks;
-      wr.details.ok.sig = fc.sig;
-      wr.details.ok.exchange_vals = wh->alg_values;
-      break;
-    }
-  case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS:
-    {
-      struct GNUNET_JSON_Specification spec[] = {
-        GNUNET_JSON_spec_fixed_auto (
-          "h_payto",
-          &wr.details.unavailable_for_legal_reasons.h_payto),
-        GNUNET_JSON_spec_uint64 (
-          "requirement_row",
-          &wr.details.unavailable_for_legal_reasons.requirement_row),
-        GNUNET_JSON_spec_end ()
-      };
-
-      if (GNUNET_OK !=
-          GNUNET_JSON_parse (w2r->hr.reply,
-                             spec,
-                             NULL, NULL))
-      {
-        GNUNET_break_op (0);
-        wr.hr.http_status = 0;
-        wr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
-        break;
-      }
-    }
-    break;
-  default:
-    break;
-  }
-  wh->cb (wh->cb_cls,
-          &wr);
-  if (MHD_HTTP_OK == w2r->hr.http_status)
-    TALER_denom_sig_free (&wr.details.ok.sig);
-  TALER_EXCHANGE_withdraw_cancel (wh);
-}
-
-
-/**
- * Function called when stage 1 of CS withdraw is finished (request r_pub's)
- *
- * @param cls the `struct TALER_EXCHANGE_WithdrawHandle`
- * @param csrr replies from the /csr-withdraw request
- */
-static void
-withdraw_cs_stage_two_callback (
-  void *cls,
-  const struct TALER_EXCHANGE_CsRWithdrawResponse *csrr)
-{
-  struct TALER_EXCHANGE_WithdrawHandle *wh = cls;
-  struct TALER_EXCHANGE_WithdrawResponse wr = {
-    .hr = csrr->hr
-  };
-
-  wh->csrh = NULL;
-  GNUNET_assert (TALER_DENOMINATION_CS == wh->pk.key.cipher);
-  switch (csrr->hr.http_status)
-  {
-  case MHD_HTTP_OK:
-    wh->alg_values = csrr->details.ok.alg_values;
-    TALER_planchet_setup_coin_priv (&wh->ps,
-                                    &wh->alg_values,
-                                    &wh->priv);
-    TALER_planchet_blinding_secret_create (&wh->ps,
-                                           &wh->alg_values,
-                                           &wh->bks);
-    /* This initializes the 2nd half of the
-       wh->pd.blinded_planchet! */
-    if (GNUNET_OK !=
-        TALER_planchet_prepare (&wh->pk.key,
-                                &wh->alg_values,
-                                &wh->bks,
-                                &wh->priv,
-                                wh->ach,
-                                &wh->c_hash,
-                                &wh->pd))
-    {
-      GNUNET_break (0);
-      break;
-    }
-    wh->wh2 = TALER_EXCHANGE_withdraw2 (wh->curl_ctx,
-                                        wh->exchange_url,
-                                        wh->keys,
-                                        &wh->pd,
-                                        wh->reserve_priv,
-                                        &handle_reserve_withdraw_finished,
-                                        wh);
-    return;
-  default:
-    break;
-  }
-  wh->cb (wh->cb_cls,
-          &wr);
-  TALER_EXCHANGE_withdraw_cancel (wh);
-}
-
-
-struct TALER_EXCHANGE_WithdrawHandle *
-TALER_EXCHANGE_withdraw (
-  struct GNUNET_CURL_Context *curl_ctx,
-  const char *exchange_url,
-  struct TALER_EXCHANGE_Keys *keys,
-  const struct TALER_ReservePrivateKeyP *reserve_priv,
-  const struct TALER_EXCHANGE_WithdrawCoinInput *wci,
-  TALER_EXCHANGE_WithdrawCallback res_cb,
-  void *res_cb_cls)
-{
-  struct TALER_EXCHANGE_WithdrawHandle *wh;
-
-  wh = GNUNET_new (struct TALER_EXCHANGE_WithdrawHandle);
-  wh->keys = TALER_EXCHANGE_keys_incref (keys);
-  wh->exchange_url = exchange_url;
-  wh->curl_ctx = curl_ctx;
-  wh->cb = res_cb;
-  wh->cb_cls = res_cb_cls;
-  wh->reserve_priv = reserve_priv;
-  wh->ps = *wci->ps;
-  wh->ach = wci->ach;
-  wh->pk = *wci->pk;
-  TALER_denom_pub_deep_copy (&wh->pk.key,
-                             &wci->pk->key);
-
-  switch (wci->pk->key.cipher)
-  {
-  case TALER_DENOMINATION_RSA:
-    {
-      wh->alg_values.cipher = TALER_DENOMINATION_RSA;
-      TALER_planchet_setup_coin_priv (&wh->ps,
-                                      &wh->alg_values,
-                                      &wh->priv);
-      TALER_planchet_blinding_secret_create (&wh->ps,
-                                             &wh->alg_values,
-                                             &wh->bks);
-      if (GNUNET_OK !=
-          TALER_planchet_prepare (&wh->pk.key,
-                                  &wh->alg_values,
-                                  &wh->bks,
-                                  &wh->priv,
-                                  wh->ach,
-                                  &wh->c_hash,
-                                  &wh->pd))
-      {
-        GNUNET_break (0);
-        GNUNET_free (wh);
-        return NULL;
-      }
-      wh->wh2 = TALER_EXCHANGE_withdraw2 (curl_ctx,
-                                          exchange_url,
-                                          keys,
-                                          &wh->pd,
-                                          wh->reserve_priv,
-                                          &handle_reserve_withdraw_finished,
-                                          wh);
-      break;
-    }
-  case TALER_DENOMINATION_CS:
-    {
-      TALER_cs_withdraw_nonce_derive (
-        &wh->ps,
-        &wh->pd.blinded_planchet.details.cs_blinded_planchet.nonce);
-      /* Note that we only initialize the first half
-         of the blinded_planchet here; the other part
-         will be done after the /csr-withdraw request! */
-      wh->pd.blinded_planchet.cipher = TALER_DENOMINATION_CS;
-      wh->csrh = TALER_EXCHANGE_csr_withdraw (
-        curl_ctx,
-        exchange_url,
-        &wh->pk,
-        &wh->pd.blinded_planchet.details.cs_blinded_planchet.nonce,
-        &withdraw_cs_stage_two_callback,
-        wh);
-      break;
-    }
-  default:
-    GNUNET_break (0);
-    GNUNET_free (wh);
-    return NULL;
-  }
-  return wh;
-}
-
-
-void
-TALER_EXCHANGE_withdraw_cancel (struct TALER_EXCHANGE_WithdrawHandle *wh)
-{
-  TALER_blinded_planchet_free (&wh->pd.blinded_planchet);
-  if (NULL != wh->csrh)
-  {
-    TALER_EXCHANGE_csr_withdraw_cancel (wh->csrh);
-    wh->csrh = NULL;
-  }
-  if (NULL != wh->wh2)
-  {
-    TALER_EXCHANGE_withdraw2_cancel (wh->wh2);
-    wh->wh2 = NULL;
-  }
-  TALER_EXCHANGE_keys_decref (wh->keys);
-  TALER_denom_pub_free (&wh->pk.key);
-  GNUNET_free (wh);
-}
diff --git a/src/lib/exchange_api_withdraw2.c b/src/lib/exchange_api_withdraw2.c
deleted file mode 100644
index 53a5934d..00000000
--- a/src/lib/exchange_api_withdraw2.c
+++ /dev/null
@@ -1,389 +0,0 @@
-/*
-  This file is part of TALER
-  Copyright (C) 2014-2023 Taler Systems SA
-
-  TALER is free software; you can redistribute it and/or modify it under the
-  terms of the GNU General Public License as published by the Free Software
-  Foundation; either version 3, or (at your option) any later version.
-
-  TALER is distributed in the hope that it will be useful, but WITHOUT ANY
-  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
-  A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
-
-  You should have received a copy of the GNU General Public License along with
-  TALER; see the file COPYING.  If not, see
-  <http://www.gnu.org/licenses/>
-*/
-/**
- * @file lib/exchange_api_withdraw2.c
- * @brief Implementation of /reserves/$RESERVE_PUB/withdraw requests without 
blinding/unblinding
- * @author Christian Grothoff
- */
-#include "platform.h"
-#include <jansson.h>
-#include <microhttpd.h> /* just for HTTP status codes */
-#include <gnunet/gnunet_util_lib.h>
-#include <gnunet/gnunet_json_lib.h>
-#include <gnunet/gnunet_curl_lib.h>
-#include "taler_exchange_service.h"
-#include "taler_json_lib.h"
-#include "exchange_api_handle.h"
-#include "taler_signatures.h"
-#include "exchange_api_curl_defaults.h"
-
-
-/**
- * @brief A Withdraw Handle
- */
-struct TALER_EXCHANGE_Withdraw2Handle
-{
-
-  /**
-   * The /keys material from the exchange
-   */
-  struct TALER_EXCHANGE_Keys *keys;
-
-  /**
-   * The url for this request.
-   */
-  char *url;
-
-  /**
-   * Handle for the request.
-   */
-  struct GNUNET_CURL_Job *job;
-
-  /**
-   * Function to call with the result.
-   */
-  TALER_EXCHANGE_Withdraw2Callback cb;
-
-  /**
-   * Closure for @a cb.
-   */
-  void *cb_cls;
-
-  /**
-   * Context for #TEH_curl_easy_post(). Keeps the data that must
-   * persist for Curl to make the upload.
-   */
-  struct TALER_CURL_PostContext post_ctx;
-
-  /**
-   * Total amount requested (value plus withdraw fee).
-   */
-  struct TALER_Amount requested_amount;
-
-  /**
-   * Public key of the reserve we are withdrawing from.
-   */
-  struct TALER_ReservePublicKeyP reserve_pub;
-
-};
-
-
-/**
- * We got a 200 OK response for the /reserves/$RESERVE_PUB/withdraw operation.
- * Extract the coin's signature and return it to the caller.  The signature we
- * get from the exchange is for the blinded value.  Thus, we first must
- * unblind it and then should verify its validity against our coin's hash.
- *
- * If everything checks out, we return the unblinded signature
- * to the application via the callback.
- *
- * @param wh operation handle
- * @param json reply from the exchange
- * @return #GNUNET_OK on success, #GNUNET_SYSERR on errors
- */
-static enum GNUNET_GenericReturnValue
-reserve_withdraw_ok (struct TALER_EXCHANGE_Withdraw2Handle *wh,
-                     const json_t *json)
-{
-  struct TALER_EXCHANGE_Withdraw2Response w2r = {
-    .hr.reply = json,
-    .hr.http_status = MHD_HTTP_OK
-  };
-  struct GNUNET_JSON_Specification spec[] = {
-    TALER_JSON_spec_blinded_denom_sig ("ev_sig",
-                                       &w2r.details.ok.blind_sig),
-    GNUNET_JSON_spec_end ()
-  };
-
-  if (GNUNET_OK !=
-      GNUNET_JSON_parse (json,
-                         spec,
-                         NULL, NULL))
-  {
-    GNUNET_break_op (0);
-    return GNUNET_SYSERR;
-  }
-
-  /* signature is valid, return it to the application */
-  wh->cb (wh->cb_cls,
-          &w2r);
-  /* make sure callback isn't called again after return */
-  wh->cb = NULL;
-  GNUNET_JSON_parse_free (spec);
-  return GNUNET_OK;
-}
-
-
-/**
- * Function called when we're done processing the
- * HTTP /reserves/$RESERVE_PUB/withdraw request.
- *
- * @param cls the `struct TALER_EXCHANGE_WithdrawHandle`
- * @param response_code HTTP response code, 0 on error
- * @param response parsed JSON result, NULL on error
- */
-static void
-handle_reserve_withdraw_finished (void *cls,
-                                  long response_code,
-                                  const void *response)
-{
-  struct TALER_EXCHANGE_Withdraw2Handle *wh = cls;
-  const json_t *j = response;
-  struct TALER_EXCHANGE_Withdraw2Response w2r = {
-    .hr.reply = j,
-    .hr.http_status = (unsigned int) response_code
-  };
-
-  wh->job = NULL;
-  switch (response_code)
-  {
-  case 0:
-    w2r.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
-    break;
-  case MHD_HTTP_OK:
-    if (GNUNET_OK !=
-        reserve_withdraw_ok (wh,
-                             j))
-    {
-      GNUNET_break_op (0);
-      w2r.hr.http_status = 0;
-      w2r.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
-      break;
-    }
-    GNUNET_assert (NULL == wh->cb);
-    TALER_EXCHANGE_withdraw2_cancel (wh);
-    return;
-  case MHD_HTTP_BAD_REQUEST:
-    /* This should never happen, either us or the exchange is buggy
-       (or API version conflict); just pass JSON reply to the application */
-    w2r.hr.ec = TALER_JSON_get_error_code (j);
-    w2r.hr.hint = TALER_JSON_get_error_hint (j);
-    break;
-  case MHD_HTTP_FORBIDDEN:
-    GNUNET_break_op (0);
-    /* Nothing really to verify, exchange says one of the signatures is
-       invalid; as we checked them, this should never happen, we
-       should pass the JSON reply to the application */
-    w2r.hr.ec = TALER_JSON_get_error_code (j);
-    w2r.hr.hint = TALER_JSON_get_error_hint (j);
-    break;
-  case MHD_HTTP_NOT_FOUND:
-    /* Nothing really to verify, the exchange basically just says
-       that it doesn't know this reserve.  Can happen if we
-       query before the wire transfer went through.
-       We should simply pass the JSON reply to the application. */
-    w2r.hr.ec = TALER_JSON_get_error_code (j);
-    w2r.hr.hint = TALER_JSON_get_error_hint (j);
-    break;
-  case MHD_HTTP_CONFLICT:
-    w2r.hr.ec = TALER_JSON_get_error_code (j);
-    w2r.hr.hint = TALER_JSON_get_error_hint (j);
-    break;
-  case MHD_HTTP_GONE:
-    /* could happen if denomination was revoked */
-    /* Note: one might want to check /keys for revocation
-       signature here, alas tricky in case our /keys
-       is outdated => left to clients */
-    w2r.hr.ec = TALER_JSON_get_error_code (j);
-    w2r.hr.hint = TALER_JSON_get_error_hint (j);
-    break;
-  case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS:
-    /* only validate reply is well-formed */
-    {
-      uint64_t ptu;
-      struct GNUNET_JSON_Specification spec[] = {
-        GNUNET_JSON_spec_uint64 ("requirement_row",
-                                 &ptu),
-        GNUNET_JSON_spec_end ()
-      };
-
-      if (GNUNET_OK !=
-          GNUNET_JSON_parse (j,
-                             spec,
-                             NULL, NULL))
-      {
-        GNUNET_break_op (0);
-        w2r.hr.http_status = 0;
-        w2r.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
-        break;
-      }
-    }
-    break;
-  case MHD_HTTP_INTERNAL_SERVER_ERROR:
-    /* Server had an internal issue; we should retry, but this API
-       leaves this to the application */
-    w2r.hr.ec = TALER_JSON_get_error_code (j);
-    w2r.hr.hint = TALER_JSON_get_error_hint (j);
-    break;
-  default:
-    /* unexpected response code */
-    GNUNET_break_op (0);
-    w2r.hr.ec = TALER_JSON_get_error_code (j);
-    w2r.hr.hint = TALER_JSON_get_error_hint (j);
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Unexpected response code %u/%d for exchange withdraw\n",
-                (unsigned int) response_code,
-                (int) w2r.hr.ec);
-    break;
-  }
-  if (NULL != wh->cb)
-  {
-    wh->cb (wh->cb_cls,
-            &w2r);
-    wh->cb = NULL;
-  }
-  TALER_EXCHANGE_withdraw2_cancel (wh);
-}
-
-
-struct TALER_EXCHANGE_Withdraw2Handle *
-TALER_EXCHANGE_withdraw2 (
-  struct GNUNET_CURL_Context *curl_ctx,
-  const char *exchange_url,
-  struct TALER_EXCHANGE_Keys *keys,
-  const struct TALER_PlanchetDetail *pd,
-  const struct TALER_ReservePrivateKeyP *reserve_priv,
-  TALER_EXCHANGE_Withdraw2Callback res_cb,
-  void *res_cb_cls)
-{
-  struct TALER_EXCHANGE_Withdraw2Handle *wh;
-  const struct TALER_EXCHANGE_DenomPublicKey *dk;
-  struct TALER_ReserveSignatureP reserve_sig;
-  char arg_str[sizeof (struct TALER_ReservePublicKeyP) * 2 + 32];
-  struct TALER_BlindedCoinHashP bch;
-
-  GNUNET_assert (NULL != keys);
-  dk = TALER_EXCHANGE_get_denomination_key_by_hash (keys,
-                                                    &pd->denom_pub_hash);
-  if (NULL == dk)
-  {
-    GNUNET_break (0);
-    return NULL;
-  }
-  wh = GNUNET_new (struct TALER_EXCHANGE_Withdraw2Handle);
-  wh->keys = TALER_EXCHANGE_keys_incref (keys);
-  wh->cb = res_cb;
-  wh->cb_cls = res_cb_cls;
-  /* Compute how much we expected to charge to the reserve */
-  if (0 >
-      TALER_amount_add (&wh->requested_amount,
-                        &dk->value,
-                        &dk->fees.withdraw))
-  {
-    /* Overflow here? Very strange, our CPU must be fried... */
-    GNUNET_break (0);
-    GNUNET_free (wh);
-    return NULL;
-  }
-
-  GNUNET_CRYPTO_eddsa_key_get_public (&reserve_priv->eddsa_priv,
-                                      &wh->reserve_pub.eddsa_pub);
-
-  {
-    char pub_str[sizeof (struct TALER_ReservePublicKeyP) * 2];
-    char *end;
-
-    end = GNUNET_STRINGS_data_to_string (
-      &wh->reserve_pub,
-      sizeof (struct TALER_ReservePublicKeyP),
-      pub_str,
-      sizeof (pub_str));
-    *end = '\0';
-    GNUNET_snprintf (arg_str,
-                     sizeof (arg_str),
-                     "reserves/%s/withdraw",
-                     pub_str);
-  }
-
-  if (GNUNET_OK !=
-      TALER_coin_ev_hash (&pd->blinded_planchet,
-                          &pd->denom_pub_hash,
-                          &bch))
-  {
-    GNUNET_break (0);
-    GNUNET_free (wh);
-    return NULL;
-  }
-
-  TALER_wallet_withdraw_sign (&pd->denom_pub_hash,
-                              &wh->requested_amount,
-                              &bch,
-                              reserve_priv,
-                              &reserve_sig);
-  {
-    json_t *withdraw_obj = GNUNET_JSON_PACK (
-      GNUNET_JSON_pack_data_auto ("denom_pub_hash",
-                                  &pd->denom_pub_hash),
-      TALER_JSON_pack_blinded_planchet ("coin_ev",
-                                        &pd->blinded_planchet),
-      GNUNET_JSON_pack_data_auto ("reserve_sig",
-                                  &reserve_sig));
-    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                "Attempting to withdraw from reserve %s\n",
-                TALER_B2S (&wh->reserve_pub));
-    wh->url = TALER_url_join (exchange_url,
-                              arg_str,
-                              NULL);
-    if (NULL == wh->url)
-    {
-      json_decref (withdraw_obj);
-      GNUNET_free (wh);
-      return NULL;
-    }
-    {
-      CURL *eh;
-
-      eh = TALER_EXCHANGE_curl_easy_get_ (wh->url);
-      if ( (NULL == eh) ||
-           (GNUNET_OK !=
-            TALER_curl_easy_post (&wh->post_ctx,
-                                  eh,
-                                  withdraw_obj)) )
-      {
-        GNUNET_break (0);
-        if (NULL != eh)
-          curl_easy_cleanup (eh);
-        json_decref (withdraw_obj);
-        GNUNET_free (wh->url);
-        GNUNET_free (wh);
-        return NULL;
-      }
-      json_decref (withdraw_obj);
-      wh->job = GNUNET_CURL_job_add2 (curl_ctx,
-                                      eh,
-                                      wh->post_ctx.headers,
-                                      &handle_reserve_withdraw_finished,
-                                      wh);
-    }
-  }
-  return wh;
-}
-
-
-void
-TALER_EXCHANGE_withdraw2_cancel (struct TALER_EXCHANGE_Withdraw2Handle *wh)
-{
-  if (NULL != wh->job)
-  {
-    GNUNET_CURL_job_cancel (wh->job);
-    wh->job = NULL;
-  }
-  GNUNET_free (wh->url);
-  TALER_curl_easy_post_finished (&wh->post_ctx);
-  TALER_EXCHANGE_keys_decref (wh->keys);
-  GNUNET_free (wh);
-}
diff --git a/src/testing/testing_api_cmd_withdraw.c 
b/src/testing/testing_api_cmd_withdraw.c
index c45e29ff..8a88f60f 100644
--- a/src/testing/testing_api_cmd_withdraw.c
+++ b/src/testing/testing_api_cmd_withdraw.c
@@ -158,7 +158,7 @@ struct WithdrawState
   /**
    * Withdraw handle (while operation is running).
    */
-  struct TALER_EXCHANGE_WithdrawHandle *wsh;
+  struct TALER_EXCHANGE_BatchWithdrawHandle *wsh;
 
   /**
    * Task scheduled to try later.
@@ -242,7 +242,7 @@ do_retry (void *cls)
  */
 static void
 reserve_withdraw_cb (void *cls,
-                     const struct TALER_EXCHANGE_WithdrawResponse *wr)
+                     const struct TALER_EXCHANGE_BatchWithdrawResponse *wr)
 {
   struct WithdrawState *ws = cls;
   struct TALER_TESTING_Interpreter *is = ws->is;
@@ -292,11 +292,12 @@ reserve_withdraw_cb (void *cls,
   switch (wr->hr.http_status)
   {
   case MHD_HTTP_OK:
+    GNUNET_assert (1 == wr->details.ok.num_coins);
     TALER_denom_sig_deep_copy (&ws->sig,
-                               &wr->details.ok.sig);
-    ws->coin_priv = wr->details.ok.coin_priv;
-    ws->bks = wr->details.ok.bks;
-    ws->exchange_vals = wr->details.ok.exchange_vals;
+                               &wr->details.ok.coins[0].sig);
+    ws->coin_priv = wr->details.ok.coins[0].coin_priv;
+    ws->bks = wr->details.ok.coins[0].bks;
+    ws->exchange_vals = wr->details.ok.coins[0].exchange_vals;
     if (0 != ws->total_backoff.rel_value_us)
     {
       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
@@ -442,11 +443,13 @@ withdraw_run (void *cls,
       .ps = &ws->ps,
       .ach = 0 < ws->age ? &ws->h_age_commitment : NULL
     };
-    ws->wsh = TALER_EXCHANGE_withdraw (
+
+    ws->wsh = TALER_EXCHANGE_batch_withdraw (
       TALER_TESTING_interpreter_get_context (is),
       TALER_TESTING_get_exchange_url (is),
       TALER_TESTING_get_keys (is),
       rp,
+      1,
       &wci,
       &reserve_withdraw_cb,
       ws);
@@ -477,7 +480,7 @@ withdraw_cleanup (void *cls,
   {
     TALER_TESTING_command_incomplete (ws->is,
                                       cmd->label);
-    TALER_EXCHANGE_withdraw_cancel (ws->wsh);
+    TALER_EXCHANGE_batch_withdraw_cancel (ws->wsh);
     ws->wsh = NULL;
   }
   if (NULL != ws->retry_task)

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