gnunet-svn
[Top][All Lists]
Advanced

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

[taler-exchange] branch master updated: complete P2P/W2W conflict handli


From: gnunet
Subject: [taler-exchange] branch master updated: complete P2P/W2W conflict handling, deduplicate code across handlers
Date: Sun, 05 Jun 2022 14:07:29 +0200

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

grothoff pushed a commit to branch master
in repository exchange.

The following commit(s) were added to refs/heads/master by this push:
     new b9963f75 complete P2P/W2W conflict handling, deduplicate code across 
handlers
b9963f75 is described below

commit b9963f75255e416ca79b2f5c3081bde4ba78fab0
Author: Christian Grothoff <christian@grothoff.org>
AuthorDate: Sun Jun 5 14:07:23 2022 +0200

    complete P2P/W2W conflict handling, deduplicate code across handlers
---
 src/exchange/taler-exchange-httpd_batch-withdraw.c |  15 +
 src/exchange/taler-exchange-httpd_db.c             |   4 +
 src/exchange/taler-exchange-httpd_deposit.c        |   5 +-
 src/exchange/taler-exchange-httpd_melt.c           |   1 +
 src/exchange/taler-exchange-httpd_purses_create.c  |   1 +
 src/exchange/taler-exchange-httpd_purses_deposit.c |   1 +
 src/exchange/taler-exchange-httpd_purses_merge.c   |   3 +-
 src/exchange/taler-exchange-httpd_recoup-refresh.c |   1 +
 src/exchange/taler-exchange-httpd_recoup.c         |   1 +
 src/exchange/taler-exchange-httpd_refund.c         |  14 +-
 src/exchange/taler-exchange-httpd_responses.c      |  82 +++--
 src/exchange/taler-exchange-httpd_responses.h      |   2 +
 src/exchangedb/plugin_exchangedb_postgres.c        |   3 +-
 src/include/taler_exchange_service.h               |   6 +-
 src/include/taler_exchangedb_plugin.h              |   2 +-
 src/lib/exchange_api_common.c                      | 368 +++++++++++++++++++--
 src/lib/exchange_api_common.h                      | 123 ++++++-
 src/lib/exchange_api_deposit.c                     | 101 ++----
 src/lib/exchange_api_melt.c                        | 187 ++---------
 src/lib/exchange_api_purse_create_with_deposit.c   | 203 ++++++++++--
 src/lib/exchange_api_purse_deposit.c               | 172 +++-------
 src/lib/exchange_api_recoup.c                      | 106 ++----
 src/lib/exchange_api_recoup_refresh.c              | 117 +++----
 src/lib/exchange_api_refund.c                      |  32 +-
 src/testing/testing_api_cmd_deposit.c              |   2 +-
 src/testing/testing_api_cmd_purse_deposit.c        |   2 +-
 26 files changed, 896 insertions(+), 658 deletions(-)

diff --git a/src/exchange/taler-exchange-httpd_batch-withdraw.c 
b/src/exchange/taler-exchange-httpd_batch-withdraw.c
index d1311f8a..52f42036 100644
--- a/src/exchange/taler-exchange-httpd_batch-withdraw.c
+++ b/src/exchange/taler-exchange-httpd_batch-withdraw.c
@@ -456,6 +456,21 @@ parse_planchets (const struct TEH_RequestContext *rc,
         return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
     }
     pc->collectable.reserve_pub = *wc->reserve_pub;
+    for (unsigned int k = 0; k<i; k++)
+    {
+      const struct PlanchetContext *kpc = &wc->planchets[k];
+
+      if (0 ==
+          TALER_blinded_planchet_cmp (&kpc->blinded_planchet,
+                                      &pc->blinded_planchet))
+      {
+        GNUNET_break_op (0);
+        return TALER_MHD_reply_with_error (rc->connection,
+                                           MHD_HTTP_BAD_REQUEST,
+                                           
TALER_EC_GENERIC_PARAMETER_MALFORMED,
+                                           "duplicate planchet");
+      }
+    }
   }
 
   ksh = TEH_keys_get_state ();
diff --git a/src/exchange/taler-exchange-httpd_db.c 
b/src/exchange/taler-exchange-httpd_db.c
index 53b935ba..f0c55139 100644
--- a/src/exchange/taler-exchange-httpd_db.c
+++ b/src/exchange/taler-exchange-httpd_db.c
@@ -61,15 +61,19 @@ TEH_make_coin_known (const struct TALER_CoinPublicInfo 
*coin,
                                     NULL);
     return GNUNET_DB_STATUS_HARD_ERROR;
   case TALER_EXCHANGEDB_CKS_DENOM_CONFLICT:
+    /* FIXME-Oec: insufficient_funds != denom conflict! */
     *mhd_ret = TEH_RESPONSE_reply_coin_insufficient_funds (
       connection,
       TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_DENOMINATION_KEY,
+      &h_denom_pub,
       &coin->coin_pub);
     return GNUNET_DB_STATUS_HARD_ERROR;
   case TALER_EXCHANGEDB_CKS_AGE_CONFLICT:
+    /* FIXME-Oec: insufficient_funds != Age conflict! */
     *mhd_ret = TEH_RESPONSE_reply_coin_insufficient_funds (
       connection,
       TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_AGE_HASH,
+      &h_denom_pub,
       &coin->coin_pub);
     return GNUNET_DB_STATUS_HARD_ERROR;
   }
diff --git a/src/exchange/taler-exchange-httpd_deposit.c 
b/src/exchange/taler-exchange-httpd_deposit.c
index 7ca56e10..f5e18507 100644
--- a/src/exchange/taler-exchange-httpd_deposit.c
+++ b/src/exchange/taler-exchange-httpd_deposit.c
@@ -1,6 +1,6 @@
 /*
   This file is part of TALER
-  Copyright (C) 2014-2021 Taler Systems SA
+  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 Affero General Public License as published by the Free 
Software
@@ -178,10 +178,12 @@ deposit_transaction (void *cls,
   }
   if (in_conflict)
   {
+    /* FIXME: conficting contract != insufficient funds */
     *mhd_ret
       = TEH_RESPONSE_reply_coin_insufficient_funds (
           connection,
           TALER_EC_EXCHANGE_DEPOSIT_CONFLICTING_CONTRACT,
+          &dc->deposit->coin.denom_pub_hash,
           &dc->deposit->coin.coin_pub);
     return GNUNET_DB_STATUS_HARD_ERROR;
   }
@@ -191,6 +193,7 @@ deposit_transaction (void *cls,
       = TEH_RESPONSE_reply_coin_insufficient_funds (
           connection,
           TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS,
+          &dc->deposit->coin.denom_pub_hash,
           &dc->deposit->coin.coin_pub);
     return GNUNET_DB_STATUS_HARD_ERROR;
   }
diff --git a/src/exchange/taler-exchange-httpd_melt.c 
b/src/exchange/taler-exchange-httpd_melt.c
index 3d6f05c0..c6a8cc62 100644
--- a/src/exchange/taler-exchange-httpd_melt.c
+++ b/src/exchange/taler-exchange-httpd_melt.c
@@ -196,6 +196,7 @@ melt_transaction (void *cls,
       = TEH_RESPONSE_reply_coin_insufficient_funds (
           connection,
           TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS,
+          &rmc->refresh_session.coin.denom_pub_hash,
           &rmc->refresh_session.coin.coin_pub);
     return GNUNET_DB_STATUS_HARD_ERROR;
   }
diff --git a/src/exchange/taler-exchange-httpd_purses_create.c 
b/src/exchange/taler-exchange-httpd_purses_create.c
index 112a24dc..d6c942c6 100644
--- a/src/exchange/taler-exchange-httpd_purses_create.c
+++ b/src/exchange/taler-exchange-httpd_purses_create.c
@@ -319,6 +319,7 @@ create_transaction (void *cls,
         = TEH_RESPONSE_reply_coin_insufficient_funds (
             connection,
             TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS,
+            &coin->cpi.denom_pub_hash,
             &coin->cpi.coin_pub);
       return GNUNET_DB_STATUS_HARD_ERROR;
     }
diff --git a/src/exchange/taler-exchange-httpd_purses_deposit.c 
b/src/exchange/taler-exchange-httpd_purses_deposit.c
index 051df85c..45d0c6f7 100644
--- a/src/exchange/taler-exchange-httpd_purses_deposit.c
+++ b/src/exchange/taler-exchange-httpd_purses_deposit.c
@@ -229,6 +229,7 @@ deposit_transaction (void *cls,
         = TEH_RESPONSE_reply_coin_insufficient_funds (
             connection,
             TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS,
+            &coin->cpi.denom_pub_hash,
             &coin->cpi.coin_pub);
       return GNUNET_DB_STATUS_HARD_ERROR;
     }
diff --git a/src/exchange/taler-exchange-httpd_purses_merge.c 
b/src/exchange/taler-exchange-httpd_purses_merge.c
index d87fb16d..5f09f198 100644
--- a/src/exchange/taler-exchange-httpd_purses_merge.c
+++ b/src/exchange/taler-exchange-httpd_purses_merge.c
@@ -325,14 +325,13 @@ merge_transaction (void *cls,
       GNUNET_JSON_pack_data_auto ("merge_sig",
                                   &merge_sig),
       GNUNET_JSON_pack_allow_null (
-        GNUNET_JSON_pack_string ("partner_base_url",
+        GNUNET_JSON_pack_string ("partner_url",
                                  partner_url)),
       GNUNET_JSON_pack_data_auto ("reserve_pub",
                                   &reserve_pub));
     GNUNET_free (partner_url);
     return GNUNET_DB_STATUS_HARD_ERROR;
   }
-  // FIXME: if ! kyc check, return 451!
   return qs;
 }
 
diff --git a/src/exchange/taler-exchange-httpd_recoup-refresh.c 
b/src/exchange/taler-exchange-httpd_recoup-refresh.c
index a00eeba4..79e99950 100644
--- a/src/exchange/taler-exchange-httpd_recoup-refresh.c
+++ b/src/exchange/taler-exchange-httpd_recoup-refresh.c
@@ -146,6 +146,7 @@ recoup_refresh_transaction (void *cls,
     *mhd_ret = TEH_RESPONSE_reply_coin_insufficient_funds (
       connection,
       TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS,
+      &pc->coin->denom_pub_hash,
       &pc->coin->coin_pub);
     return GNUNET_DB_STATUS_HARD_ERROR;
   }
diff --git a/src/exchange/taler-exchange-httpd_recoup.c 
b/src/exchange/taler-exchange-httpd_recoup.c
index 6bda8af9..bf17d9c2 100644
--- a/src/exchange/taler-exchange-httpd_recoup.c
+++ b/src/exchange/taler-exchange-httpd_recoup.c
@@ -149,6 +149,7 @@ recoup_transaction (void *cls,
     *mhd_ret = TEH_RESPONSE_reply_coin_insufficient_funds (
       connection,
       TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS,
+      &pc->coin->denom_pub_hash,
       &pc->coin->coin_pub);
     return GNUNET_DB_STATUS_HARD_ERROR;
   }
diff --git a/src/exchange/taler-exchange-httpd_refund.c 
b/src/exchange/taler-exchange-httpd_refund.c
index 3718fded..33ead7c6 100644
--- a/src/exchange/taler-exchange-httpd_refund.c
+++ b/src/exchange/taler-exchange-httpd_refund.c
@@ -158,10 +158,10 @@ refund_transaction (void *cls,
   }
   if (conflict)
   {
-    TEH_plugin->rollback (TEH_plugin->cls);
     *mhd_ret = TEH_RESPONSE_reply_coin_insufficient_funds (
       connection,
       TALER_EC_EXCHANGE_REFUND_INCONSISTENT_AMOUNT,
+      &refund->coin.denom_pub_hash,
       &refund->coin.coin_pub);
     return GNUNET_DB_STATUS_HARD_ERROR;
   }
@@ -175,10 +175,10 @@ refund_transaction (void *cls,
   }
   if (! refund_ok)
   {
-    TEH_plugin->rollback (TEH_plugin->cls);
     *mhd_ret = TEH_RESPONSE_reply_coin_insufficient_funds (
       connection,
       TALER_EC_EXCHANGE_REFUND_CONFLICT_DEPOSIT_INSUFFICIENT,
+      &refund->coin.denom_pub_hash,
       &refund->coin.coin_pub);
     return GNUNET_DB_STATUS_HARD_ERROR;
   }
@@ -200,7 +200,6 @@ static MHD_RESULT
 verify_and_execute_refund (struct MHD_Connection *connection,
                            struct TALER_EXCHANGEDB_Refund *refund)
 {
-  struct TALER_DenominationHashP denom_hash;
   struct RefundContext rctx = {
     .refund = refund
   };
@@ -228,15 +227,16 @@ verify_and_execute_refund (struct MHD_Connection 
*connection,
     qs = TEH_plugin->get_coin_denomination (TEH_plugin->cls,
                                             &refund->coin.coin_pub,
                                             &rctx.known_coin_id,
-                                            &denom_hash);
+                                            &refund->coin.denom_pub_hash);
     if (0 > qs)
     {
       MHD_RESULT res;
       char *dhs;
 
       GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs);
-      dhs = GNUNET_STRINGS_data_to_string_alloc (&denom_hash,
-                                                 sizeof (denom_hash));
+      dhs = GNUNET_STRINGS_data_to_string_alloc (
+        &refund->coin.denom_pub_hash,
+        sizeof (refund->coin.denom_pub_hash));
       res = TALER_MHD_reply_with_error (connection,
                                         MHD_HTTP_NOT_FOUND,
                                         
TALER_EC_EXCHANGE_REFUND_COIN_NOT_FOUND,
@@ -251,7 +251,7 @@ verify_and_execute_refund (struct MHD_Connection 
*connection,
     struct TEH_DenominationKey *dk;
     MHD_RESULT mret;
 
-    dk = TEH_keys_denomination_by_hash (&denom_hash,
+    dk = TEH_keys_denomination_by_hash (&refund->coin.denom_pub_hash,
                                         connection,
                                         &mret);
     if (NULL == dk)
diff --git a/src/exchange/taler-exchange-httpd_responses.c 
b/src/exchange/taler-exchange-httpd_responses.c
index 11cc0b93..e5309f9e 100644
--- a/src/exchange/taler-exchange-httpd_responses.c
+++ b/src/exchange/taler-exchange-httpd_responses.c
@@ -70,18 +70,19 @@ TEH_RESPONSE_compile_transaction_history (
         /* internal sanity check before we hand out a bogus sig... */
         TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++;
         if (GNUNET_OK !=
-            TALER_wallet_deposit_verify (&deposit->amount_with_fee,
-                                         &deposit->deposit_fee,
-                                         &h_wire,
-                                         &deposit->h_contract_terms,
-                                         &deposit->h_age_commitment,
-                                         NULL /* h_extensions! */,
-                                         &deposit->h_denom_pub,
-                                         deposit->timestamp,
-                                         &deposit->merchant_pub,
-                                         deposit->refund_deadline,
-                                         coin_pub,
-                                         &deposit->csig))
+            TALER_wallet_deposit_verify (
+              &deposit->amount_with_fee,
+              &deposit->deposit_fee,
+              &h_wire,
+              &deposit->h_contract_terms,
+              &deposit->h_age_commitment,
+              NULL /* h_extensions! */,
+              &deposit->h_denom_pub,
+              deposit->timestamp,
+              &deposit->merchant_pub,
+              deposit->refund_deadline,
+              coin_pub,
+              &deposit->csig))
         {
           GNUNET_break (0);
           json_decref (history);
@@ -109,8 +110,6 @@ TEH_RESPONSE_compile_transaction_history (
                                             &deposit->h_contract_terms),
                 GNUNET_JSON_pack_data_auto ("h_wire",
                                             &h_wire),
-                GNUNET_JSON_pack_data_auto ("h_denom_pub",
-                                            &deposit->h_denom_pub),
                 GNUNET_JSON_pack_allow_null (
                   deposit->no_age_commitment ?
                   GNUNET_JSON_pack_string (
@@ -135,13 +134,14 @@ TEH_RESPONSE_compile_transaction_history (
 #if ENABLE_SANITY_CHECKS
         TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++;
         if (GNUNET_OK !=
-            TALER_wallet_melt_verify (&melt->amount_with_fee,
-                                      &melt->melt_fee,
-                                      &melt->rc,
-                                      &melt->h_denom_pub,
-                                      &melt->h_age_commitment,
-                                      coin_pub,
-                                      &melt->coin_sig))
+            TALER_wallet_melt_verify (
+              &melt->amount_with_fee,
+              &melt->melt_fee,
+              &melt->rc,
+              &melt->h_denom_pub,
+              &melt->h_age_commitment,
+              coin_pub,
+              &melt->coin_sig))
         {
           GNUNET_break (0);
           json_decref (history);
@@ -166,8 +166,6 @@ TEH_RESPONSE_compile_transaction_history (
                                         &melt->melt_fee),
                 GNUNET_JSON_pack_data_auto ("rc",
                                             &melt->rc),
-                GNUNET_JSON_pack_data_auto ("h_denom_pub",
-                                            &melt->h_denom_pub),
                 GNUNET_JSON_pack_allow_null (
                   GNUNET_JSON_pack_data_auto ("h_age_commitment",
                                               phac)),
@@ -189,12 +187,13 @@ TEH_RESPONSE_compile_transaction_history (
 #if ENABLE_SANITY_CHECKS
         TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++;
         if (GNUNET_OK !=
-            TALER_merchant_refund_verify (coin_pub,
-                                          &refund->h_contract_terms,
-                                          refund->rtransaction_id,
-                                          &refund->refund_amount,
-                                          &refund->merchant_pub,
-                                          &refund->merchant_sig))
+            TALER_merchant_refund_verify (
+              coin_pub,
+              &refund->h_contract_terms,
+              refund->rtransaction_id,
+              &refund->refund_amount,
+              &refund->merchant_pub,
+              &refund->merchant_sig))
         {
           GNUNET_break (0);
           json_decref (history);
@@ -319,8 +318,6 @@ TEH_RESPONSE_compile_transaction_history (
                                             &epub),
                 GNUNET_JSON_pack_data_auto ("reserve_pub",
                                             &recoup->reserve_pub),
-                GNUNET_JSON_pack_data_auto ("h_denom_pub",
-                                            &recoup->h_denom_pub),
                 GNUNET_JSON_pack_data_auto ("coin_sig",
                                             &recoup->coin_sig),
                 GNUNET_JSON_pack_data_auto ("coin_blind",
@@ -376,8 +373,6 @@ TEH_RESPONSE_compile_transaction_history (
                                             &epub),
                 GNUNET_JSON_pack_data_auto ("old_coin_pub",
                                             &pr->old_coin_pub),
-                GNUNET_JSON_pack_data_auto ("h_denom_pub",
-                                            &pr->coin.denom_pub_hash),
                 GNUNET_JSON_pack_data_auto ("coin_sig",
                                             &pr->coin_sig),
                 GNUNET_JSON_pack_data_auto ("coin_blind",
@@ -558,18 +553,31 @@ MHD_RESULT
 TEH_RESPONSE_reply_coin_insufficient_funds (
   struct MHD_Connection *connection,
   enum TALER_ErrorCode ec,
+  const struct TALER_DenominationHashP *h_denom_pub,
   const struct TALER_CoinSpendPublicKeyP *coin_pub)
 {
   struct TALER_EXCHANGEDB_TransactionList *tl;
   enum GNUNET_DB_QueryStatus qs;
   json_t *history;
 
-  // FIXME: maybe start read-committed transaction here?
-  // => check all callers (that they aborted already!)
+  TEH_plugin->rollback (TEH_plugin->cls);
+  // FIXME: maybe start read-only transaction here?
+  if (GNUNET_OK !=
+      TEH_plugin->start_read_committed (TEH_plugin->cls,
+                                        "get_coin_transactions"))
+  {
+    return TALER_MHD_reply_with_error (
+      connection,
+      MHD_HTTP_INTERNAL_SERVER_ERROR,
+      TALER_EC_GENERIC_DB_START_FAILED,
+      NULL);
+  }
+  // FIXME: simplify, 3rd arg is always 'true' now?
   qs = TEH_plugin->get_coin_transactions (TEH_plugin->cls,
                                           coin_pub,
-                                          GNUNET_NO,
+                                          true,
                                           &tl);
+  TEH_plugin->rollback (TEH_plugin->cls);
   if (0 > qs)
   {
     return TALER_MHD_reply_with_error (
@@ -597,6 +605,8 @@ TEH_RESPONSE_reply_coin_insufficient_funds (
     TALER_JSON_pack_ec (ec),
     GNUNET_JSON_pack_data_auto ("coin_pub",
                                 coin_pub),
+    GNUNET_JSON_pack_data_auto ("h_denom_pub",
+                                h_denom_pub),
     GNUNET_JSON_pack_array_steal ("history",
                                   history));
 }
diff --git a/src/exchange/taler-exchange-httpd_responses.h 
b/src/exchange/taler-exchange-httpd_responses.h
index 2c4ac018..dde4c2c8 100644
--- a/src/exchange/taler-exchange-httpd_responses.h
+++ b/src/exchange/taler-exchange-httpd_responses.h
@@ -113,6 +113,7 @@ TEH_RESPONSE_reply_invalid_denom_cipher_for_operation (
  *
  * @param connection connection to the client
  * @param ec error code to return
+ * @param h_denom_pub hash of the denomination of the coin
  * @param coin_pub public key of the coin
  * @return MHD result code
  */
@@ -120,6 +121,7 @@ MHD_RESULT
 TEH_RESPONSE_reply_coin_insufficient_funds (
   struct MHD_Connection *connection,
   enum TALER_ErrorCode ec,
+  const struct TALER_DenominationHashP *h_denom_pub,
   const struct TALER_CoinSpendPublicKeyP *coin_pub);
 
 
diff --git a/src/exchangedb/plugin_exchangedb_postgres.c 
b/src/exchangedb/plugin_exchangedb_postgres.c
index b259e351..4ff51ebc 100644
--- a/src/exchangedb/plugin_exchangedb_postgres.c
+++ b/src/exchangedb/plugin_exchangedb_postgres.c
@@ -8403,7 +8403,6 @@ add_coin_melt (void *cls,
         chc->failed = true;
         return;
       }
-
     }
     tl = GNUNET_new (struct TALER_EXCHANGEDB_TransactionList);
     tl->next = chc->head;
@@ -8704,7 +8703,7 @@ static enum GNUNET_DB_QueryStatus
 postgres_get_coin_transactions (
   void *cls,
   const struct TALER_CoinSpendPublicKeyP *coin_pub,
-  int include_recoup,
+  bool include_recoup,
   struct TALER_EXCHANGEDB_TransactionList **tlp)
 {
   struct PostgresClosure *pg = cls;
diff --git a/src/include/taler_exchange_service.h 
b/src/include/taler_exchange_service.h
index 7e44698f..03223edc 100644
--- a/src/include/taler_exchange_service.h
+++ b/src/include/taler_exchange_service.h
@@ -2978,21 +2978,17 @@ TALER_EXCHANGE_deposits_get_cancel (
  * Convenience function.  Verifies a coin's transaction history as
  * returned by the exchange.
  *
- * @param dk fee structure for the coin, NULL to skip verifying fees
- * @param currency expected currency for the coin
+ * @param dk fee structure for the coin
  * @param coin_pub public key of the coin
  * @param history history of the coin in json encoding
- * @param[out] h_denom_pub set to the hash of the coin's denomination (if 
available)
  * @param[out] total how much of the coin has been spent according to @a 
history
  * @return #GNUNET_OK if @a history is valid, #GNUNET_SYSERR if not
  */
 enum GNUNET_GenericReturnValue
 TALER_EXCHANGE_verify_coin_history (
   const struct TALER_EXCHANGE_DenomPublicKey *dk,
-  const char *currency,
   const struct TALER_CoinSpendPublicKeyP *coin_pub,
   json_t *history,
-  struct TALER_DenominationHashP *h_denom_pub,
   struct TALER_Amount *total);
 
 
diff --git a/src/include/taler_exchangedb_plugin.h 
b/src/include/taler_exchangedb_plugin.h
index 15f5661d..a79acd20 100644
--- a/src/include/taler_exchangedb_plugin.h
+++ b/src/include/taler_exchangedb_plugin.h
@@ -3620,7 +3620,7 @@ struct TALER_EXCHANGEDB_Plugin
   enum GNUNET_DB_QueryStatus
   (*get_coin_transactions)(void *cls,
                            const struct TALER_CoinSpendPublicKeyP *coin_pub,
-                           int include_recoup,
+                           bool include_recoup,
                            struct TALER_EXCHANGEDB_TransactionList **tlp);
 
 
diff --git a/src/lib/exchange_api_common.c b/src/lib/exchange_api_common.c
index 567239ee..59f5bab8 100644
--- a/src/lib/exchange_api_common.c
+++ b/src/lib/exchange_api_common.c
@@ -22,6 +22,7 @@
 #include "platform.h"
 #include "taler_json_lib.h"
 #include <gnunet/gnunet_curl_lib.h>
+#include "exchange_api_common.h"
 #include "exchange_api_handle.h"
 #include "taler_signatures.h"
 
@@ -684,11 +685,6 @@ struct CoinHistoryParseContext
    */
   const struct TALER_CoinSpendPublicKeyP *coin_pub;
 
-  /**
-   * Hash of @e dk, set from parsing.
-   */
-  struct TALER_DenominationHashP *h_denom_pub;
-
   /**
    * Where to sum up total refunds.
    */
@@ -749,8 +745,6 @@ help_deposit (struct CoinHistoryParseContext *pc,
                                  &h_contract_terms),
     GNUNET_JSON_spec_fixed_auto ("h_wire",
                                  &h_wire),
-    GNUNET_JSON_spec_fixed_auto ("h_denom_pub",
-                                 pc->h_denom_pub),
     GNUNET_JSON_spec_mark_optional (
       GNUNET_JSON_spec_fixed_auto ("h_age_commitment",
                                    &hac),
@@ -784,7 +778,7 @@ help_deposit (struct CoinHistoryParseContext *pc,
         &h_contract_terms,
         no_hac ? NULL : &hac,
         NULL /* h_extensions! */,
-        pc->h_denom_pub,
+        &pc->dk->h_key,
         wallet_timestamp,
         &merchant_pub,
         refund_deadline,
@@ -836,8 +830,6 @@ help_melt (struct CoinHistoryParseContext *pc,
                                  &sig),
     GNUNET_JSON_spec_fixed_auto ("rc",
                                  &rc),
-    GNUNET_JSON_spec_fixed_auto ("h_denom_pub",
-                                 pc->h_denom_pub),
     GNUNET_JSON_spec_mark_optional (
       GNUNET_JSON_spec_fixed_auto ("h_age_commitment",
                                    &h_age_commitment),
@@ -876,7 +868,7 @@ help_melt (struct CoinHistoryParseContext *pc,
         amount,
         &melt_fee,
         &rc,
-        pc->h_denom_pub,
+        &pc->dk->h_key,
         no_hac
         ? NULL
         : &h_age_commitment,
@@ -1008,8 +1000,6 @@ help_recoup (struct CoinHistoryParseContext *pc,
                                  &coin_sig),
     GNUNET_JSON_spec_fixed_auto ("coin_blind",
                                  &coin_bks),
-    GNUNET_JSON_spec_fixed_auto ("h_denom_pub",
-                                 pc->h_denom_pub),
     GNUNET_JSON_spec_timestamp ("timestamp",
                                 &timestamp),
     GNUNET_JSON_spec_end ()
@@ -1036,7 +1026,7 @@ help_recoup (struct CoinHistoryParseContext *pc,
     return GNUNET_SYSERR;
   }
   if (GNUNET_OK !=
-      TALER_wallet_recoup_verify (pc->h_denom_pub,
+      TALER_wallet_recoup_verify (&pc->dk->h_key,
                                   &coin_bks,
                                   pc->coin_pub,
                                   &coin_sig))
@@ -1081,8 +1071,6 @@ help_recoup_refresh (struct CoinHistoryParseContext *pc,
                                  &old_coin_pub),
     GNUNET_JSON_spec_fixed_auto ("coin_blind",
                                  &coin_bks),
-    GNUNET_JSON_spec_fixed_auto ("h_denom_pub",
-                                 pc->h_denom_pub),
     GNUNET_JSON_spec_timestamp ("timestamp",
                                 &timestamp),
     GNUNET_JSON_spec_end ()
@@ -1109,7 +1097,7 @@ help_recoup_refresh (struct CoinHistoryParseContext *pc,
     return GNUNET_SYSERR;
   }
   if (GNUNET_OK !=
-      TALER_wallet_recoup_verify (pc->h_denom_pub,
+      TALER_wallet_recoup_verify (&pc->dk->h_key,
                                   &coin_bks,
                                   pc->coin_pub,
                                   &coin_sig))
@@ -1250,12 +1238,11 @@ help_purse_deposit (struct CoinHistoryParseContext *pc,
 enum GNUNET_GenericReturnValue
 TALER_EXCHANGE_verify_coin_history (
   const struct TALER_EXCHANGE_DenomPublicKey *dk,
-  const char *currency,
   const struct TALER_CoinSpendPublicKeyP *coin_pub,
   json_t *history,
-  struct TALER_DenominationHashP *h_denom_pub,
   struct TALER_Amount *total)
 {
+  const char *currency = dk->value.currency;
   const struct
   {
     const char *type;
@@ -1273,8 +1260,7 @@ TALER_EXCHANGE_verify_coin_history (
   struct CoinHistoryParseContext pc = {
     .dk = dk,
     .coin_pub = coin_pub,
-    .total = total,
-    .h_denom_pub = h_denom_pub
+    .total = total
   };
   size_t len;
 
@@ -1528,11 +1514,10 @@ TALER_EXCHANGE_check_purse_merge_conflict_ (
 {
   struct TALER_PurseMergeSignatureP merge_sig;
   struct GNUNET_TIME_Timestamp merge_timestamp;
-  const char *partner_url = exchange_url;
+  const char *partner_url = NULL;
   struct TALER_ReservePublicKeyP reserve_pub;
   struct GNUNET_JSON_Specification spec[] = {
     GNUNET_JSON_spec_mark_optional (
-      // FIXME: partner_url or partner_base_url?
       GNUNET_JSON_spec_string ("partner_url",
                                &partner_url),
       NULL),
@@ -1554,6 +1539,8 @@ TALER_EXCHANGE_check_purse_merge_conflict_ (
     GNUNET_break_op (0);
     return GNUNET_SYSERR;
   }
+  if (NULL == partner_url)
+    partner_url = exchange_url;
   payto_uri = make_payto (partner_url,
                           &reserve_pub);
   if (GNUNET_OK !=
@@ -1581,18 +1568,55 @@ TALER_EXCHANGE_check_purse_merge_conflict_ (
 }
 
 
-/**
- * Check proof of a contract conflict.
- *
- * DESIGN-FIXME: this 'proof' doesn't really proof a conflict!
- *
- * @param ccontract_sig conflicting signature (must
- *        not match the signature from the proof)
- * @param purse_pub public key of the purse
- * @param exchange_url the base URL of this exchange
- * @param proof the proof to check
- * @return #GNUNET_OK if the @a proof is OK for @a purse_pub and conflicts 
with @a purse_sig
- */
+enum GNUNET_GenericReturnValue
+TALER_EXCHANGE_check_purse_coin_conflict_ (
+  const struct TALER_PurseContractPublicKeyP *purse_pub,
+  const char *exchange_url,
+  const json_t *proof,
+  struct TALER_CoinSpendPublicKeyP *coin_pub,
+  struct TALER_CoinSpendSignatureP *coin_sig)
+{
+  const char *partner_url = NULL;
+  struct TALER_Amount amount;
+  struct GNUNET_JSON_Specification spec[] = {
+    GNUNET_JSON_spec_fixed_auto ("coin_sig",
+                                 coin_sig),
+    GNUNET_JSON_spec_fixed_auto ("coin_pub",
+                                 coin_pub),
+    GNUNET_JSON_spec_mark_optional (
+      GNUNET_JSON_spec_string ("partner_url",
+                               &partner_url),
+      NULL),
+    TALER_JSON_spec_amount_any ("amount",
+                                &amount),
+    GNUNET_JSON_spec_end ()
+  };
+
+  if (GNUNET_OK !=
+      GNUNET_JSON_parse (proof,
+                         spec,
+                         NULL, NULL))
+  {
+    GNUNET_break_op (0);
+    return GNUNET_SYSERR;
+  }
+  if (NULL == partner_url)
+    partner_url = exchange_url;
+  if (GNUNET_OK !=
+      TALER_wallet_purse_deposit_verify (
+        partner_url,
+        purse_pub,
+        &amount,
+        coin_pub,
+        coin_sig))
+  {
+    GNUNET_break_op (0);
+    return GNUNET_SYSERR;
+  }
+  return GNUNET_OK;
+}
+
+
 enum GNUNET_GenericReturnValue
 TALER_EXCHANGE_check_purse_econtract_conflict_ (
   const struct TALER_PurseContractSignatureP *ccontract_sig,
@@ -1642,4 +1666,278 @@ TALER_EXCHANGE_check_purse_econtract_conflict_ (
 }
 
 
+enum GNUNET_GenericReturnValue
+TALER_EXCHANGE_check_coin_amount_conflict_ (
+  const struct TALER_EXCHANGE_Keys *keys,
+  const json_t *proof,
+  struct TALER_CoinSpendPublicKeyP *coin_pub,
+  struct TALER_Amount *remaining)
+{
+  json_t *history;
+  struct TALER_Amount total;
+  struct TALER_DenominationHashP h_denom_pub;
+  const struct TALER_EXCHANGE_DenomPublicKey *dki;
+  struct GNUNET_JSON_Specification spec[] = {
+    GNUNET_JSON_spec_fixed_auto ("coin_pub",
+                                 coin_pub),
+    GNUNET_JSON_spec_fixed_auto ("h_denom_pub",
+                                 &h_denom_pub),
+    GNUNET_JSON_spec_json ("history",
+                           &history),
+    GNUNET_JSON_spec_end ()
+  };
+
+  if (GNUNET_OK !=
+      GNUNET_JSON_parse (proof,
+                         spec,
+                         NULL, NULL))
+  {
+    GNUNET_break_op (0);
+    return GNUNET_SYSERR;
+  }
+  dki = TALER_EXCHANGE_get_denomination_key_by_hash (
+    keys,
+    &h_denom_pub);
+  if (NULL == dki)
+  {
+    GNUNET_break_op (0);
+    return GNUNET_SYSERR;
+  }
+  if (GNUNET_OK !=
+      TALER_EXCHANGE_verify_coin_history (dki,
+                                          coin_pub,
+                                          history,
+                                          &total))
+  {
+    GNUNET_break_op (0);
+    json_decref (history);
+    return GNUNET_SYSERR;
+  }
+  json_decref (history);
+  if (0 >
+      TALER_amount_subtract (remaining,
+                             &dki->value,
+                             &total))
+  {
+    /* Strange 'proof': coin was double-spent
+       before our transaction?! */
+    GNUNET_break_op (0);
+    return GNUNET_SYSERR;
+  }
+  return GNUNET_OK;
+}
+
+
+/**
+ * Verify that @a coin_sig does NOT appear in
+ * the history of @a proof and thus whatever transaction
+ * is authorized by @a coin_sig is a conflict with
+ * @a proof.
+ *
+ * @param proof a proof to check
+ * @param coin_sig signature that must not be in @a proof
+ * @return #GNUNET_OK if @a coin_sig is not in @a proof
+ */
+enum GNUNET_GenericReturnValue
+TALER_EXCHANGE_check_coin_signature_conflict_ (
+  const json_t *proof,
+  const struct TALER_CoinSpendSignatureP *coin_sig)
+{
+  json_t *history;
+  size_t off;
+  json_t *entry;
+
+  history = json_object_get (proof,
+                             "history");
+  if (NULL == history)
+  {
+    GNUNET_break (0);
+    return GNUNET_SYSERR;
+  }
+  json_array_foreach (history, off, entry)
+  {
+    struct TALER_CoinSpendSignatureP cs;
+    struct GNUNET_JSON_Specification spec[] = {
+      GNUNET_JSON_spec_fixed_auto ("coin_sig",
+                                   &cs),
+      GNUNET_JSON_spec_end ()
+    };
+
+    if (GNUNET_OK !=
+        GNUNET_JSON_parse (entry,
+                           spec,
+                           NULL, NULL))
+      continue; /* entry without coin signature */
+    if (0 ==
+        GNUNET_memcmp (&cs,
+                       coin_sig))
+    {
+      GNUNET_break_op (0);
+      return GNUNET_SYSERR;
+    }
+  }
+  return GNUNET_OK;
+}
+
+
+enum GNUNET_GenericReturnValue
+TALER_EXCHANGE_check_coin_denomination_conflict_ (
+  const json_t *proof,
+  const struct TALER_DenominationHashP *ch_denom_pub)
+{
+  struct TALER_DenominationHashP h_denom_pub;
+  struct GNUNET_JSON_Specification spec[] = {
+    GNUNET_JSON_spec_fixed_auto ("h_denom_pub",
+                                 &h_denom_pub),
+    GNUNET_JSON_spec_end ()
+  };
+
+  if (GNUNET_OK !=
+      GNUNET_JSON_parse (proof,
+                         spec,
+                         NULL, NULL))
+  {
+    GNUNET_break_op (0);
+    return GNUNET_SYSERR;
+  }
+  if (0 ==
+      GNUNET_memcmp (ch_denom_pub,
+                     &h_denom_pub))
+  {
+    GNUNET_break_op (0);
+    return GNUNET_OK;
+  }
+  /* indeed, proof with different denomination key provided */
+  return GNUNET_OK;
+}
+
+
+enum GNUNET_GenericReturnValue
+TALER_EXCHANGE_check_coin_conflict_ (
+  const struct TALER_EXCHANGE_Keys *keys,
+  const json_t *proof,
+  const struct TALER_EXCHANGE_DenomPublicKey *dk,
+  const struct TALER_CoinSpendPublicKeyP *coin_pub,
+  const struct TALER_CoinSpendSignatureP *coin_sig,
+  const struct TALER_Amount *required)
+{
+  enum TALER_ErrorCode ec;
+
+  ec = TALER_JSON_get_error_code (proof);
+  switch (ec)
+  {
+  case TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS:
+    {
+      struct TALER_Amount left;
+      struct TALER_CoinSpendPublicKeyP pcoin_pub;
+
+      if (GNUNET_OK !=
+          TALER_EXCHANGE_check_coin_amount_conflict_ (
+            keys,
+            proof,
+            &pcoin_pub,
+            &left))
+      {
+        GNUNET_break_op (0);
+        return GNUNET_SYSERR;
+      }
+      if (0 !=
+          GNUNET_memcmp (&pcoin_pub,
+                         coin_pub))
+      {
+        /* conflict is for a different coin! */
+        GNUNET_break_op (0);
+        return GNUNET_SYSERR;
+      }
+      if (-1 !=
+          TALER_amount_cmp (&left,
+                            required))
+      {
+        /* Balance was sufficient after all; recoup MAY have still been 
possible */
+        GNUNET_break_op (0);
+        return GNUNET_SYSERR;
+      }
+      if (GNUNET_OK !=
+          TALER_EXCHANGE_check_coin_signature_conflict_ (
+            proof,
+            coin_sig))
+      {
+        /* Not a conflicting transaction: ours is included! */
+        GNUNET_break_op (0);
+        return GNUNET_SYSERR;
+      }
+      break;
+    }
+  case TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_DENOMINATION_KEY:
+    {
+      struct TALER_Amount left;
+      struct TALER_CoinSpendPublicKeyP pcoin_pub;
+
+      if (GNUNET_OK !=
+          TALER_EXCHANGE_check_coin_amount_conflict_ (
+            keys,
+            proof,
+            &pcoin_pub,
+            &left))
+      {
+        GNUNET_break_op (0);
+        return GNUNET_SYSERR;
+      }
+      if (0 !=
+          GNUNET_memcmp (&pcoin_pub,
+                         coin_pub))
+      {
+        /* conflict is for a different coin! */
+        GNUNET_break_op (0);
+        return GNUNET_SYSERR;
+      }
+      if (GNUNET_OK !=
+          TALER_EXCHANGE_check_coin_denomination_conflict_ (
+            proof,
+            &dk->h_key))
+      {
+        /* Eh, same denomination, hence no conflict */
+        GNUNET_break_op (0);
+        return GNUNET_SYSERR;
+      }
+      break;
+    }
+  default:
+    GNUNET_break_op (0);
+    return GNUNET_SYSERR;
+  }
+  return GNUNET_OK;
+}
+
+
+enum GNUNET_GenericReturnValue
+TALER_EXCHANGE_get_min_denomination_ (
+  const struct TALER_EXCHANGE_Keys *keys,
+  struct TALER_Amount *min)
+{
+  bool have_min = false;
+  for (unsigned int i = 0; i<keys->num_denom_keys; i++)
+  {
+    const struct TALER_EXCHANGE_DenomPublicKey *dk = &keys->denom_keys[i];
+
+    if (! have_min)
+    {
+      *min = dk->value;
+      have_min = true;
+      continue;
+    }
+    if (1 != TALER_amount_cmp (min,
+                               &dk->value))
+      continue;
+    *min = dk->value;
+  }
+  if (! have_min)
+  {
+    GNUNET_break (0);
+    return GNUNET_SYSERR;
+  }
+  return GNUNET_OK;
+}
+
+
 /* end of exchange_api_common.c */
diff --git a/src/lib/exchange_api_common.h b/src/lib/exchange_api_common.h
index 9c6d4502..9cbdf547 100644
--- a/src/lib/exchange_api_common.h
+++ b/src/lib/exchange_api_common.h
@@ -23,6 +23,7 @@
 #define EXCHANGE_API_COMMON_H
 
 #include "taler_json_lib.h"
+#include "taler_exchange_service.h"
 
 
 /**
@@ -33,7 +34,7 @@
  * @param purse_pub the public key (must match
  *        the signature from the proof)
  * @param proof the proof to check
- * @return #GNUNET_OK if the @a proof is OK for @a purse_pub and conflicts 
with @a purse_sig
+ * @return #GNUNET_OK if the @a proof is OK for @a purse_pub and conflicts 
with @a cpurse_sig
  */
 enum GNUNET_GenericReturnValue
 TALER_EXCHANGE_check_purse_create_conflict_ (
@@ -51,7 +52,7 @@ TALER_EXCHANGE_check_purse_create_conflict_ (
  *        the signature from the proof)
  * @param exchange_url the base URL of this exchange
  * @param proof the proof to check
- * @return #GNUNET_OK if the @a proof is OK for @a purse_pub and conflicts 
with @a purse_sig
+ * @return #GNUNET_OK if the @a proof is OK for @a purse_pub and @a merge_pub 
and conflicts with @a cmerge_sig
  */
 enum GNUNET_GenericReturnValue
 TALER_EXCHANGE_check_purse_merge_conflict_ (
@@ -63,16 +64,38 @@ TALER_EXCHANGE_check_purse_merge_conflict_ (
 
 
 /**
- * Check proof of a contract conflict.
+ * Check @a proof that claims this coin was spend
+ * differently on the same purse already. Note that
+ * the caller must still check that @a coin_pub is
+ * in the list of coins that were used, and that
+ * @a coin_sig is different from the signature the
+ * caller used.
  *
- * DESIGN-FIXME: this 'proof' doesn't really proof a conflict!
+ * @param purse_pub the public key of the purse
+ * @param exchange_url base URL of our exchange
+ * @param proof the proof to check
+ * @param[out] coin_pub set to the conflicting coin
+ * @param[out] coin_sig set to the conflicting signature
+ * @return #GNUNET_OK if the @a proof is OK for @a purse_pub and showing that 
@a coin_pub was spent using @a coin_sig.
+ */
+enum GNUNET_GenericReturnValue
+TALER_EXCHANGE_check_purse_coin_conflict_ (
+  const struct TALER_PurseContractPublicKeyP *purse_pub,
+  const char *exchange_url,
+  const json_t *proof,
+  struct TALER_CoinSpendPublicKeyP *coin_pub,
+  struct TALER_CoinSpendSignatureP *coin_sig);
+
+
+/**
+ * Check proof of a contract conflict.
  *
  * @param ccontract_sig conflicting signature (must
  *        not match the signature from the proof)
  * @param purse_pub public key of the purse
  * @param exchange_url the base URL of this exchange
  * @param proof the proof to check
- * @return #GNUNET_OK if the @a proof is OK for @a purse_pub and conflicts 
with @a purse_sig
+ * @return #GNUNET_OK if the @a proof is OK for @a purse_pub and conflicts 
with @a ccontract_sig
  */
 enum GNUNET_GenericReturnValue
 TALER_EXCHANGE_check_purse_econtract_conflict_ (
@@ -81,4 +104,94 @@ TALER_EXCHANGE_check_purse_econtract_conflict_ (
   const json_t *proof);
 
 
+/**
+ * Check proof of a coin spend value conflict.
+ *
+ * @param keys exchange /keys structure
+ * @param proof the proof to check
+ * @param[out] coin_pub set to the public key of the
+ *        coin that is claimed to have an insufficient
+ *        balance
+ * @param[out] remaining set to the remaining balance
+ *        of the coin as provided by the proof
+ * @return #GNUNET_OK if the @a proof is OK for @a purse_pub demonstrating 
that @a coin_pub has only @a remaining balance.
+ */
+enum GNUNET_GenericReturnValue
+TALER_EXCHANGE_check_coin_amount_conflict_ (
+  const struct TALER_EXCHANGE_Keys *keys,
+  const json_t *proof,
+  struct TALER_CoinSpendPublicKeyP *coin_pub,
+  struct TALER_Amount *remaining);
+
+
+/**
+ * Verify that @a proof contains a coin history
+ * that demonstrates that @a coin_pub was previously
+ * used with a denomination key that is different
+ * from @a ch_denom_pub.  Note that the coin history
+ * MUST have been checked before using
+ * #TALER_EXCHANGE_check_coin_amount_conflict_().
+ *
+ * @param proof a proof to check
+ * @param ch_denom_pub hash of the conflicting denomination
+ * @return #GNUNET_OK if @a ch_denom_pub differs from the
+ *         denomination hash given by the history of the coin
+ */
+enum GNUNET_GenericReturnValue
+TALER_EXCHANGE_check_coin_denomination_conflict_ (
+  const json_t *proof,
+  const struct TALER_DenominationHashP *ch_denom_pub);
+
+
+/**
+ * Verify that @a coin_sig does NOT appear in
+ * the history of @a proof and thus whatever transaction
+ * is authorized by @a coin_sig is a conflict with
+ * @a proof.
+ *
+ * @param proof a proof to check
+ * @param coin_sig signature that must not be in @a proof
+ * @return #GNUNET_OK if @a coin_sig is not in @a proof
+ */
+enum GNUNET_GenericReturnValue
+TALER_EXCHANGE_check_coin_signature_conflict_ (
+  const json_t *proof,
+  const struct TALER_CoinSpendSignatureP *coin_sig);
+
+
+/**
+ * Check that the provided @a proof indeeds indicates
+ * a conflict for @a coin_pub.
+ *
+ * @param keys exchange keys
+ * @param proof provided conflict proof
+ * @param dk denomination of @a coin_pub that the client
+ *           used
+ * @param coin_pub public key of the coin
+ * @param coin_sig signature over operation that conflicted
+ * @param required balance required on the coin for the operation
+ * @return #GNUNET_OK if @a proof holds
+ */
+enum GNUNET_GenericReturnValue
+TALER_EXCHANGE_check_coin_conflict_ (
+  const struct TALER_EXCHANGE_Keys *keys,
+  const json_t *proof,
+  const struct TALER_EXCHANGE_DenomPublicKey *dk,
+  const struct TALER_CoinSpendPublicKeyP *coin_pub,
+  const struct TALER_CoinSpendSignatureP *coin_sig,
+  const struct TALER_Amount *required);
+
+
+/**
+ * Find the smallest denomination amount in @e keys.
+ *
+ * @param keys keys to search
+ * @param[out] min set to the smallest amount
+ * @return #GNUNET_SYSERR if there are no denominations in @a keys
+ */
+enum GNUNET_GenericReturnValue
+TALER_EXCHANGE_get_min_denomination_ (
+  const struct TALER_EXCHANGE_Keys *keys,
+  struct TALER_Amount *min);
+
 #endif
diff --git a/src/lib/exchange_api_deposit.c b/src/lib/exchange_api_deposit.c
index 67f595bf..58b1c3a4 100644
--- a/src/lib/exchange_api_deposit.c
+++ b/src/lib/exchange_api_deposit.c
@@ -29,6 +29,7 @@
 #include "taler_json_lib.h"
 #include "taler_auditor_service.h"
 #include "taler_exchange_service.h"
+#include "exchange_api_common.h"
 #include "exchange_api_handle.h"
 #include "taler_signatures.h"
 #include "exchange_api_curl_defaults.h"
@@ -128,6 +129,11 @@ struct TALER_EXCHANGE_DepositHandle
    */
   struct TALER_CoinSpendPublicKeyP coin_pub;
 
+  /**
+   * Our signature for the deposit operation.
+   */
+  struct TALER_CoinSpendSignatureP coin_sig;
+
   /**
    * The Merchant's public key.  Allows the merchant to later refund
    * the transaction or to inquire about the wire transfer identifier.
@@ -226,77 +232,6 @@ auditor_cb (void *cls,
 }
 
 
-/**
- * Verify that the signatures on the "403 FORBIDDEN" response from the
- * exchange demonstrating customer double-spending are valid.
- *
- * @param dh deposit handle
- * @param json json reply with the signature(s) and transaction history
- * @return #GNUNET_OK if the signature(s) is valid, #GNUNET_SYSERR if not
- */
-static enum GNUNET_GenericReturnValue
-verify_deposit_signature_conflict (
-  const struct TALER_EXCHANGE_DepositHandle *dh,
-  const json_t *json)
-{
-  json_t *history;
-  struct TALER_Amount total;
-  enum TALER_ErrorCode ec;
-  struct TALER_DenominationHashP h_denom_pub;
-
-  memset (&h_denom_pub,
-          0,
-          sizeof (h_denom_pub));
-  history = json_object_get (json,
-                             "history");
-  if (GNUNET_OK !=
-      TALER_EXCHANGE_verify_coin_history (&dh->dki,
-                                          dh->dki.value.currency,
-                                          &dh->coin_pub,
-                                          history,
-                                          &h_denom_pub,
-                                          &total))
-  {
-    GNUNET_break_op (0);
-    return GNUNET_SYSERR;
-  }
-  ec = TALER_JSON_get_error_code (json);
-  switch (ec)
-  {
-  case TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS:
-    if (0 >
-        TALER_amount_add (&total,
-                          &total,
-                          &dh->amount_with_fee))
-    {
-      /* clearly not OK if our transaction would have caused
-         the overflow... */
-      return GNUNET_OK;
-    }
-
-    if (0 >= TALER_amount_cmp (&total,
-                               &dh->dki.value))
-    {
-      /* transaction should have still fit */
-      GNUNET_break (0);
-      return GNUNET_SYSERR;
-    }
-    /* everything OK, proof of double-spending was provided */
-    return GNUNET_OK;
-  case TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_DENOMINATION_KEY:
-    if (0 != GNUNET_memcmp (&dh->dki.h_key,
-                            &h_denom_pub))
-      return GNUNET_OK; /* indeed, proof with different denomination key 
provided */
-    /* invalid proof provided */
-    return GNUNET_SYSERR;
-  default:
-    /* unexpected error code */
-    GNUNET_break_op (0);
-    return GNUNET_SYSERR;
-  }
-}
-
-
 /**
  * Function called when we're done processing the
  * HTTP /deposit request.
@@ -316,8 +251,10 @@ handle_deposit_finished (void *cls,
     .hr.reply = j,
     .hr.http_status = (unsigned int) response_code
   };
+  const struct TALER_EXCHANGE_Keys *keys;
 
   dh->job = NULL;
+  keys = TALER_EXCHANGE_get_keys (dh->exchange);
   switch (response_code)
   {
   case 0:
@@ -409,19 +346,21 @@ handle_deposit_finished (void *cls,
        happen, we should pass the JSON reply to the application */
     break;
   case MHD_HTTP_CONFLICT:
-    /* Double spending; check signatures on transaction history */
+    dr.hr.ec = TALER_JSON_get_error_code (j);
+    dr.hr.hint = TALER_JSON_get_error_hint (j);
     if (GNUNET_OK !=
-        verify_deposit_signature_conflict (dh,
-                                           j))
+        TALER_EXCHANGE_check_coin_conflict_ (
+          keys,
+          j,
+          &dh->dki,
+          &dh->coin_pub,
+          &dh->coin_sig,
+          &dh->amount_with_fee))
     {
       GNUNET_break_op (0);
       dr.hr.http_status = 0;
-      dr.hr.ec = TALER_EC_EXCHANGE_DEPOSIT_INVALID_SIGNATURE_BY_EXCHANGE;
-    }
-    else
-    {
-      dr.hr.ec = TALER_JSON_get_error_code (j);
-      dr.hr.hint = TALER_JSON_get_error_hint (j);
+      dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
+      break;
     }
     break;
   case MHD_HTTP_GONE:
@@ -692,6 +631,8 @@ TALER_EXCHANGE_deposit (
   dh->exchange = exchange;
   dh->cb = cb;
   dh->cb_cls = cb_cls;
+  dh->coin_sig = *coin_sig;
+  dh->coin_pub = *coin_pub;
   dh->url = TEAH_path_to_url (exchange,
                               arg_str);
   if (NULL == dh->url)
diff --git a/src/lib/exchange_api_melt.c b/src/lib/exchange_api_melt.c
index 80c75970..ff720d2f 100644
--- a/src/lib/exchange_api_melt.c
+++ b/src/lib/exchange_api_melt.c
@@ -27,6 +27,7 @@
 #include <gnunet/gnunet_curl_lib.h>
 #include "taler_json_lib.h"
 #include "taler_exchange_service.h"
+#include "exchange_api_common.h"
 #include "exchange_api_handle.h"
 #include "taler_signatures.h"
 #include "exchange_api_curl_defaults.h"
@@ -101,6 +102,11 @@ struct TALER_EXCHANGE_MeltHandle
    */
   struct TALER_CoinSpendPublicKeyP coin_pub;
 
+  /**
+   * Signature affirming the melt.
+   */
+  struct TALER_CoinSpendSignatureP coin_sig;
+
   /**
    * @brief Public information about the coin's denomination key
    */
@@ -183,143 +189,6 @@ verify_melt_signature_ok (struct 
TALER_EXCHANGE_MeltHandle *mh,
 }
 
 
-/**
- * Verify that the signatures on the "409 CONFLICT" response from the
- * exchange demonstrating customer denomination key differences
- * resulting from coin private key reuse are valid.
- *
- * @param mh melt handle
- * @param json json reply with the signature(s) and transaction history
- * @return #GNUNET_OK if the signature(s) is valid, #GNUNET_SYSERR if not
- */
-static enum GNUNET_GenericReturnValue
-verify_melt_signature_denom_conflict (struct TALER_EXCHANGE_MeltHandle *mh,
-                                      const json_t *json)
-
-{
-  json_t *history;
-  struct TALER_Amount total;
-  struct TALER_DenominationHashP h_denom_pub;
-
-  memset (&h_denom_pub,
-          0,
-          sizeof (h_denom_pub));
-  history = json_object_get (json,
-                             "history");
-  if (GNUNET_OK !=
-      TALER_EXCHANGE_verify_coin_history (mh->dki,
-                                          mh->dki->value.currency,
-                                          &mh->coin_pub,
-                                          history,
-                                          &h_denom_pub,
-                                          &total))
-  {
-    GNUNET_break_op (0);
-    return GNUNET_SYSERR;
-  }
-  if (0 != GNUNET_memcmp (&mh->dki->h_key,
-                          &h_denom_pub))
-    return GNUNET_OK; /* indeed, proof with different denomination key 
provided */
-  /* invalid proof provided */
-  return GNUNET_SYSERR;
-}
-
-
-/**
- * Verify that the signatures on the "409 CONFLICT" response from the
- * exchange demonstrating customer double-spending are valid.
- *
- * @param mh melt handle
- * @param json json reply with the signature(s) and transaction history
- * @return #GNUNET_OK if the signature(s) is valid, #GNUNET_SYSERR if not
- */
-static enum GNUNET_GenericReturnValue
-verify_melt_signature_spend_conflict (struct TALER_EXCHANGE_MeltHandle *mh,
-                                      const json_t *json)
-{
-  json_t *history;
-  struct TALER_Amount total;
-  struct GNUNET_JSON_Specification spec[] = {
-    GNUNET_JSON_spec_json ("history",
-                           &history),
-    GNUNET_JSON_spec_end ()
-  };
-  const struct MeltedCoin *mc;
-  enum TALER_ErrorCode ec;
-  struct TALER_DenominationHashP h_denom_pub;
-
-  /* parse JSON reply */
-  if (GNUNET_OK !=
-      GNUNET_JSON_parse (json,
-                         spec,
-                         NULL, NULL))
-  {
-    GNUNET_break_op (0);
-    return GNUNET_SYSERR;
-  }
-
-  /* Find out which coin was deemed problematic by the exchange */
-  mc = &mh->md.melted_coin;
-  /* verify coin history */
-  memset (&h_denom_pub,
-          0,
-          sizeof (h_denom_pub));
-  history = json_object_get (json,
-                             "history");
-  if (GNUNET_OK !=
-      TALER_EXCHANGE_verify_coin_history (mh->dki,
-                                          mc->original_value.currency,
-                                          &mh->coin_pub,
-                                          history,
-                                          &h_denom_pub,
-                                          &total))
-  {
-    GNUNET_break_op (0);
-    json_decref (history);
-    return GNUNET_SYSERR;
-  }
-  json_decref (history);
-
-  ec = TALER_JSON_get_error_code (json);
-  switch (ec)
-  {
-  case TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS:
-    /* check if melt operation was really too expensive given history */
-    if (0 >
-        TALER_amount_add (&total,
-                          &total,
-                          &mc->melt_amount_with_fee))
-    {
-      /* clearly not OK if our transaction would have caused
-         the overflow... */
-      return GNUNET_OK;
-    }
-
-    if (0 >= TALER_amount_cmp (&total,
-                               &mc->original_value))
-    {
-      /* transaction should have still fit */
-      GNUNET_break (0);
-      return GNUNET_SYSERR;
-    }
-
-    /* everything OK, valid proof of double-spending was provided */
-    return GNUNET_OK;
-  case TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_DENOMINATION_KEY:
-    if (0 != GNUNET_memcmp (&mh->dki->h_key,
-                            &h_denom_pub))
-      return GNUNET_OK; /* indeed, proof with different denomination key 
provided */
-    /* invalid proof provided */
-    GNUNET_break_op (0);
-    return GNUNET_SYSERR;
-  default:
-    /* unexpected error code */
-    GNUNET_break_op (0);
-    return GNUNET_SYSERR;
-  }
-}
-
-
 /**
  * Function called when we're done processing the
  * HTTP /coins/$COIN_PUB/melt request.
@@ -339,8 +208,10 @@ handle_melt_finished (void *cls,
     .hr.reply = j,
     .hr.http_status = (unsigned int) response_code
   };
+  const struct TALER_EXCHANGE_Keys *keys;
 
   mh->job = NULL;
+  keys = TALER_EXCHANGE_get_keys (mh->exchange);
   switch (response_code)
   {
   case 0:
@@ -372,36 +243,19 @@ handle_melt_finished (void *cls,
     break;
   case MHD_HTTP_CONFLICT:
     mr.hr.ec = TALER_JSON_get_error_code (j);
-    switch (mr.hr.ec)
+    mr.hr.hint = TALER_JSON_get_error_hint (j);
+    if (GNUNET_OK !=
+        TALER_EXCHANGE_check_coin_conflict_ (
+          keys,
+          j,
+          mh->dki,
+          &mh->coin_pub,
+          &mh->coin_sig,
+          &mh->md.melted_coin.melt_amount_with_fee))
     {
-    case TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS:
-      /* Double spending; check signatures on transaction history */
-      if (GNUNET_OK !=
-          verify_melt_signature_spend_conflict (mh,
-                                                j))
-      {
-        GNUNET_break_op (0);
-        mr.hr.http_status = 0;
-        mr.hr.ec = TALER_EC_EXCHANGE_MELT_INVALID_SIGNATURE_BY_EXCHANGE;
-        mr.hr.hint = TALER_JSON_get_error_hint (j);
-      }
-      break;
-    case TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_DENOMINATION_KEY:
-      if (GNUNET_OK !=
-          verify_melt_signature_denom_conflict (mh,
-                                                j))
-      {
-        GNUNET_break_op (0);
-        mr.hr.http_status = 0;
-        mr.hr.ec = TALER_EC_EXCHANGE_MELT_INVALID_SIGNATURE_BY_EXCHANGE;
-        mr.hr.hint = TALER_JSON_get_error_hint (j);
-      }
-      break;
-    default:
       GNUNET_break_op (0);
       mr.hr.http_status = 0;
-      mr.hr.ec = TALER_EC_EXCHANGE_MELT_INVALID_SIGNATURE_BY_EXCHANGE;
-      mr.hr.hint = TALER_JSON_get_error_hint (j);
+      mr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
       break;
     }
     break;
@@ -456,7 +310,6 @@ start_melt (struct TALER_EXCHANGE_MeltHandle *mh)
   json_t *melt_obj;
   CURL *eh;
   struct GNUNET_CURL_Context *ctx;
-  struct TALER_CoinSpendSignatureP confirm_sig;
   char arg_str[sizeof (struct TALER_CoinSpendPublicKeyP) * 2 + 32];
   struct TALER_DenominationHashP h_denom_pub;
   struct TALER_ExchangeWithdrawValues alg_values[mh->rd->fresh_pks_len];
@@ -480,7 +333,7 @@ start_melt (struct TALER_EXCHANGE_MeltHandle *mh)
                           &h_denom_pub,
                           mh->md.melted_coin.h_age_commitment,
                           &mh->md.melted_coin.coin_priv,
-                          &confirm_sig);
+                          &mh->coin_sig);
   GNUNET_CRYPTO_eddsa_key_get_public (&mh->md.melted_coin.coin_priv.eddsa_priv,
                                       &mh->coin_pub.eddsa_pub);
   melt_obj = GNUNET_JSON_PACK (
@@ -489,7 +342,7 @@ start_melt (struct TALER_EXCHANGE_MeltHandle *mh)
     TALER_JSON_pack_denom_sig ("denom_sig",
                                &mh->md.melted_coin.sig),
     GNUNET_JSON_pack_data_auto ("confirm_sig",
-                                &confirm_sig),
+                                &mh->coin_sig),
     TALER_JSON_pack_amount ("value_with_fee",
                             &mh->md.melted_coin.melt_amount_with_fee),
     GNUNET_JSON_pack_data_auto ("rc",
diff --git a/src/lib/exchange_api_purse_create_with_deposit.c 
b/src/lib/exchange_api_purse_create_with_deposit.c
index f21b7d31..f2d80b94 100644
--- a/src/lib/exchange_api_purse_create_with_deposit.c
+++ b/src/lib/exchange_api_purse_create_with_deposit.c
@@ -34,6 +34,33 @@
 #include "exchange_api_curl_defaults.h"
 
 
+/**
+ * Information we track per deposited coin.
+ */
+struct Deposit
+{
+  /**
+   * Coin's public key.
+   */
+  struct TALER_CoinSpendPublicKeyP coin_pub;
+
+  /**
+   * Signature made with the coin.
+   */
+  struct TALER_CoinSpendSignatureP coin_sig;
+
+  /**
+   * Coin's denomination.
+   */
+  struct TALER_DenominationHashP h_denom_pub;
+
+  /**
+   * How much did we say the coin contributed.
+   */
+  struct TALER_Amount contribution;
+};
+
+
 /**
  * @brief A purse create with deposit handle
  */
@@ -106,6 +133,16 @@ struct TALER_EXCHANGE_PurseCreateDepositHandle
    */
   struct GNUNET_TIME_Timestamp purse_expiration;
 
+  /**
+   * Array of @e num_deposit deposits.
+   */
+  struct Deposit *deposits;
+
+  /**
+   * How many deposits did we make?
+   */
+  unsigned int num_deposits;
+
 };
 
 
@@ -128,8 +165,10 @@ handle_purse_create_deposit_finished (void *cls,
     .hr.reply = j,
     .hr.http_status = (unsigned int) response_code
   };
+  const struct TALER_EXCHANGE_Keys *keys;
 
   pch->job = NULL;
+  keys = TALER_EXCHANGE_get_keys (pch->exchange);
   switch (response_code)
   {
   case 0:
@@ -232,43 +271,151 @@ handle_purse_create_deposit_finished (void *cls,
         break;
       case TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS:
         {
-#if FIXME
-          struct TALER_Amount total;
-          struct TALER_DenominationHashP h_denom_pub;
-          const struct TALER_EXCHANGE_DenomPublicKey *dk = NULL;
+          struct TALER_Amount left;
+          struct TALER_CoinSpendPublicKeyP pcoin_pub;
+          bool found = false;
 
-          // FIXME: parse coin_pub
-          // FIXME: lookup dk for that coin from our deposits array!
-
-          if (NULL == dk)
+          if (GNUNET_OK !=
+              TALER_EXCHANGE_check_coin_amount_conflict_ (
+                keys,
+                j,
+                &pcoin_pub,
+                &left))
           {
-            /* not one of our coins */
             GNUNET_break_op (0);
             dr.hr.http_status = 0;
             dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
             break;
           }
+          for (unsigned int i = 0; i<pch->num_deposits; i++)
+          {
+            struct Deposit *deposit = &pch->deposits[i];
+
+            if (0 != GNUNET_memcmp (&pcoin_pub,
+                                    &deposit->coin_pub))
+              continue;
+            if (-1 !=
+                TALER_amount_cmp (&left,
+                                  &deposit->contribution))
+            {
+              /* Balance was sufficient after all; recoup MAY have still been 
possible */
+              GNUNET_break_op (0);
+              continue;
+            }
+            if (GNUNET_OK !=
+                TALER_EXCHANGE_check_coin_signature_conflict_ (
+                  j,
+                  &deposit->coin_sig))
+            {
+              GNUNET_break_op (0);
+              continue;
+            }
+            found = true;
+            break;
+          }
+          if (! found)
+          {
+            /* conflict is for a different coin! */
+            GNUNET_break_op (0);
+            dr.hr.http_status = 0;
+            dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
+            break;
+          }
+          break;
+        }
+      case TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_DENOMINATION_KEY:
+        {
+          struct TALER_Amount left;
+          struct TALER_CoinSpendPublicKeyP pcoin_pub;
+          bool found = false;
+
           if (GNUNET_OK !=
-              TALER_EXCHANGE_verify_coin_history (dk,
-                                                  pch->exchange->currency,
-                                                  &coin_pub,
-                                                  history,
-                                                  &h_denom_pub,
-                                                  &total))
+              TALER_EXCHANGE_check_coin_amount_conflict_ (
+                keys,
+                j,
+                &pcoin_pub,
+                &left))
           {
             GNUNET_break_op (0);
             dr.hr.http_status = 0;
             dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
             break;
           }
-          // FIXME: check total is too high for the request...
-#endif
+          for (unsigned int i = 0; i<pch->num_deposits; i++)
+          {
+            struct Deposit *deposit = &pch->deposits[i];
+
+            if (0 !=
+                GNUNET_memcmp (&pcoin_pub,
+                               &deposit->coin_pub))
+              continue;
+            if (GNUNET_OK !=
+                TALER_EXCHANGE_check_coin_denomination_conflict_ (
+                  j,
+                  &deposit->h_denom_pub))
+            {
+              /* Eh, same denomination, hence no conflict */
+              GNUNET_break_op (0);
+              continue;
+            }
+            found = true;
+          }
+          if (! found)
+          {
+            /* conflict is for a different coin! */
+            GNUNET_break_op (0);
+            dr.hr.http_status = 0;
+            dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
+            break;
+          }
+          /* meta data conflict is real! */
           break;
         }
       case TALER_EC_EXCHANGE_PURSE_DEPOSIT_CONFLICTING_META_DATA:
         {
-          // FIXME!
-          break;
+          struct TALER_CoinSpendPublicKeyP coin_pub;
+          struct TALER_CoinSpendSignatureP coin_sig;
+          bool found = false;
+
+          if (GNUNET_OK !=
+              TALER_EXCHANGE_check_purse_coin_conflict_ (
+                &pch->purse_pub,
+                pch->exchange->url,
+                j,
+                &coin_pub,
+                &coin_sig))
+          {
+            GNUNET_break_op (0);
+            dr.hr.http_status = 0;
+            dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
+            break;
+          }
+          for (unsigned int i = 0; i<pch->num_deposits; i++)
+          {
+            struct Deposit *deposit = &pch->deposits[i];
+
+            if (0 !=
+                GNUNET_memcmp (&coin_pub,
+                               &deposit->coin_pub))
+              continue;
+            if (0 ==
+                GNUNET_memcmp (&coin_sig,
+                               &deposit->coin_sig))
+            {
+              GNUNET_break_op (0);
+              continue;
+            }
+            found = true;
+            break;
+          }
+          if (! found)
+          {
+            /* conflict is for a different coin! */
+            GNUNET_break_op (0);
+            dr.hr.http_status = 0;
+            dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
+            break;
+          }
         }
       case TALER_EC_EXCHANGE_PURSE_ECONTRACT_CONFLICTING_META_DATA:
         if (GNUNET_OK !=
@@ -408,6 +555,9 @@ TALER_EXCHANGE_purse_create_with_deposit (
     GNUNET_free (pch);
     return NULL;
   }
+  pch->num_deposits = num_deposits;
+  pch->deposits = GNUNET_new_array (num_deposits,
+                                    struct Deposit);
   deposit_arr = json_array ();
   GNUNET_assert (NULL != deposit_arr);
   url = TEAH_path_to_url (exchange,
@@ -418,9 +568,8 @@ TALER_EXCHANGE_purse_create_with_deposit (
   for (unsigned int i = 0; i<num_deposits; i++)
   {
     const struct TALER_EXCHANGE_PurseDeposit *deposit = &deposits[i];
+    struct Deposit *d = &pch->deposits[i];
     json_t *jdeposit;
-    struct TALER_CoinSpendSignatureP coin_sig;
-    struct TALER_CoinSpendPublicKeyP coin_pub;
 #if FIXME_OEC
     struct TALER_AgeCommitmentHash agh;
     struct TALER_AgeCommitmentHash *aghp = NULL;
@@ -441,14 +590,16 @@ TALER_EXCHANGE_purse_create_with_deposit (
       return NULL;
     }
 #endif
+    d->contribution = deposit->amount;
+    d->h_denom_pub = deposit->h_denom_pub;
     GNUNET_CRYPTO_eddsa_key_get_public (&deposit->coin_priv.eddsa_priv,
-                                        &coin_pub.eddsa_pub);
+                                        &d->coin_pub.eddsa_pub);
     TALER_wallet_purse_deposit_sign (
       url,
       &pch->purse_pub,
       &deposit->amount,
       &deposit->coin_priv,
-      &coin_sig);
+      &d->coin_sig);
     jdeposit = GNUNET_JSON_PACK (
 #if FIXME_OEC
       GNUNET_JSON_pack_allow_null (
@@ -465,9 +616,9 @@ TALER_EXCHANGE_purse_create_with_deposit (
       TALER_JSON_pack_denom_sig ("ub_sig",
                                  &deposit->denom_sig),
       GNUNET_JSON_pack_data_auto ("coin_sig",
-                                  &coin_sig),
+                                  &d->coin_sig),
       GNUNET_JSON_pack_data_auto ("coin_pub",
-                                  &coin_pub));
+                                  &d->coin_pub));
     GNUNET_assert (0 ==
                    json_array_append_new (deposit_arr,
                                           jdeposit));
@@ -529,6 +680,7 @@ TALER_EXCHANGE_purse_create_with_deposit (
       curl_easy_cleanup (eh);
     json_decref (create_obj);
     GNUNET_free (pch->econtract.econtract);
+    GNUNET_free (pch->deposits);
     GNUNET_free (pch->url);
     GNUNET_free (pch);
     return NULL;
@@ -558,6 +710,7 @@ TALER_EXCHANGE_purse_create_with_deposit_cancel (
   }
   GNUNET_free (pch->econtract.econtract);
   GNUNET_free (pch->url);
+  GNUNET_free (pch->deposits);
   TALER_curl_easy_post_finished (&pch->ctx);
   GNUNET_free (pch);
 }
diff --git a/src/lib/exchange_api_purse_deposit.c 
b/src/lib/exchange_api_purse_deposit.c
index 2027ca0d..6946419d 100644
--- a/src/lib/exchange_api_purse_deposit.c
+++ b/src/lib/exchange_api_purse_deposit.c
@@ -28,6 +28,7 @@
 #include <gnunet/gnunet_curl_lib.h>
 #include "taler_json_lib.h"
 #include "taler_exchange_service.h"
+#include "exchange_api_common.h"
 #include "exchange_api_handle.h"
 #include "taler_signatures.h"
 #include "exchange_api_curl_defaults.h"
@@ -43,6 +44,11 @@ struct Coin
    */
   struct TALER_CoinSpendPublicKeyP coin_pub;
 
+  /**
+   * Signature made with the coin.
+   */
+  struct TALER_CoinSpendSignatureP coin_sig;
+
   /**
    * Coin's denomination.
    */
@@ -226,30 +232,17 @@ handle_purse_deposit_finished (void *cls,
     {
     case TALER_EC_EXCHANGE_PURSE_DEPOSIT_CONFLICTING_META_DATA:
       {
-        const char *partner_url = NULL;
         struct TALER_CoinSpendPublicKeyP coin_pub;
         struct TALER_CoinSpendSignatureP coin_sig;
-        struct TALER_Amount amount;
-        struct GNUNET_JSON_Specification spec[] = {
-          GNUNET_JSON_spec_fixed_auto ("coin_sig",
-                                       &coin_sig),
-          GNUNET_JSON_spec_fixed_auto ("coin_pub",
-                                       &coin_pub),
-          GNUNET_JSON_spec_mark_optional (
-            GNUNET_JSON_spec_string ("partner_url",
-                                     &partner_url),
-            NULL),
-          TALER_JSON_spec_amount ("amount",
-                                  keys->currency,
-                                  &amount),
-          GNUNET_JSON_spec_end ()
-        };
         bool found = false;
 
         if (GNUNET_OK !=
-            GNUNET_JSON_parse (j,
-                               spec,
-                               NULL, NULL))
+            TALER_EXCHANGE_check_purse_coin_conflict_ (
+              &pch->purse_pub,
+              pch->base_url,
+              j,
+              &coin_pub,
+              &coin_sig))
         {
           GNUNET_break_op (0);
           dr.hr.http_status = 0;
@@ -257,29 +250,21 @@ handle_purse_deposit_finished (void *cls,
           break;
         }
         for (unsigned int i = 0; i<pch->num_deposits; i++)
+        {
           if (0 == GNUNET_memcmp (&coin_pub,
                                   &pch->coins[i].coin_pub))
           {
+            if (0 == GNUNET_memcmp (&coin_sig,
+                                    &pch->coins[i].coin_sig))
+            {
+              /* identical signature => not a conflict */
+              continue;
+            }
             found = true;
             break;
           }
-        if (! found)
-        {
-          /* proof is about a coin we did not even deposit */
-          GNUNET_break_op (0);
-          dr.hr.http_status = 0;
-          dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
-          break;
         }
-        if (NULL == partner_url)
-          partner_url = pch->base_url;
-        if (GNUNET_OK !=
-            TALER_wallet_purse_deposit_verify (
-              partner_url,
-              &pch->purse_pub,
-              &amount,
-              &coin_pub,
-              &coin_sig))
+        if (! found)
         {
           GNUNET_break_op (0);
           dr.hr.http_status = 0;
@@ -291,27 +276,18 @@ handle_purse_deposit_finished (void *cls,
       }
     case TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS:
       {
-        json_t *history;
-        struct TALER_Amount total;
-        struct TALER_DenominationHashP h_denom_pub;
-        const struct TALER_EXCHANGE_DenomPublicKey *dki;
         struct TALER_CoinSpendPublicKeyP coin_pub;
-        struct GNUNET_JSON_Specification spec[] = {
-          GNUNET_JSON_spec_fixed_auto ("coin_pub",
-                                       &coin_pub),
-          GNUNET_JSON_spec_json ("history",
-                                 &history),
-          GNUNET_JSON_spec_end ()
-        };
+        struct TALER_Amount remaining;
         bool found = false;
         const struct Coin *my_coin;
 
         if (GNUNET_OK !=
-            GNUNET_JSON_parse (j,
-                               spec,
-                               NULL, NULL))
+            TALER_EXCHANGE_check_coin_amount_conflict_ (
+              keys,
+              j,
+              &coin_pub,
+              &remaining))
         {
-          GNUNET_break_op (0);
           dr.hr.http_status = 0;
           dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
           break;
@@ -334,45 +310,22 @@ handle_purse_deposit_finished (void *cls,
           dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
           break;
         }
-        dki = TALER_EXCHANGE_get_denomination_key_by_hash (
-          keys,
-          &my_coin->h_denom_pub);
-        if (NULL == dki)
-        {
-          dr.hr.http_status = 0;
-          dr.hr.ec = TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN;
-          GNUNET_break_op (0);
-          break;
-        }
-        if (GNUNET_OK !=
-            TALER_EXCHANGE_verify_coin_history (dki,
-                                                dki->value.currency,
-                                                &coin_pub,
-                                                history,
-                                                &h_denom_pub,
-                                                &total))
+        if (1 == TALER_amount_cmp (&remaining,
+                                   &my_coin->contribution))
         {
+          /* transaction should have still fit */
           GNUNET_break_op (0);
           dr.hr.http_status = 0;
           dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
-          json_decref (history);
           break;
         }
-        json_decref (history);
-        if (0 >
-            TALER_amount_add (&total,
-                              &total,
-                              &my_coin->contribution))
-        {
-          /* clearly not OK if our transaction would have caused
-             the overflow... */
-          break;
-        }
-        if (0 >= TALER_amount_cmp (&total,
-                                   &dki->value))
+        if (GNUNET_OK !=
+            TALER_EXCHANGE_check_coin_signature_conflict_ (
+              j,
+              &my_coin->coin_sig))
         {
-          /* transaction should have still fit */
-          GNUNET_break (0);
+          /* THIS transaction must not be in the conflicting history */
+          GNUNET_break_op (0);
           dr.hr.http_status = 0;
           dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
           break;
@@ -382,27 +335,18 @@ handle_purse_deposit_finished (void *cls,
       }
     case TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_DENOMINATION_KEY:
       {
-        json_t *history;
-        struct TALER_Amount total;
-        struct TALER_DenominationHashP h_denom_pub;
-        const struct Coin *my_coin;
-        const struct TALER_EXCHANGE_DenomPublicKey *dki;
         struct TALER_CoinSpendPublicKeyP coin_pub;
-        struct GNUNET_JSON_Specification spec[] = {
-          GNUNET_JSON_spec_fixed_auto ("coin_pub",
-                                       &coin_pub),
-          GNUNET_JSON_spec_json ("history",
-                                 &history),
-          GNUNET_JSON_spec_end ()
-        };
+        struct TALER_Amount remaining;
         bool found = false;
+        const struct Coin *my_coin;
 
         if (GNUNET_OK !=
-            GNUNET_JSON_parse (j,
-                               spec,
-                               NULL, NULL))
+            TALER_EXCHANGE_check_coin_amount_conflict_ (
+              keys,
+              j,
+              &coin_pub,
+              &remaining))
         {
-          GNUNET_break_op (0);
           dr.hr.http_status = 0;
           dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
           break;
@@ -425,31 +369,12 @@ handle_purse_deposit_finished (void *cls,
           dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
           break;
         }
-        dki = TALER_EXCHANGE_get_denomination_key_by_hash (
-          keys,
-          &my_coin->h_denom_pub);
-        memset (&h_denom_pub,
-                0,
-                sizeof (h_denom_pub));
         if (GNUNET_OK !=
-            TALER_EXCHANGE_verify_coin_history (dki,
-                                                dki->value.currency,
-                                                &coin_pub,
-                                                history,
-                                                &h_denom_pub,
-                                                &total))
-        {
-          GNUNET_break_op (0);
-          dr.hr.http_status = 0;
-          dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
-          json_decref (history);
-          break;
-        }
-        json_decref (history);
-        if (0 == GNUNET_memcmp (&dki->h_key,
-                                &h_denom_pub))
+            TALER_EXCHANGE_check_coin_denomination_conflict_ (
+              j,
+              &my_coin->h_denom_pub))
         {
-          /* sorry, this proves nothing */
+          /* no conflicting denomination detected */
           GNUNET_break_op (0);
           dr.hr.http_status = 0;
           dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
@@ -562,7 +487,6 @@ TALER_EXCHANGE_purse_deposit (
     const struct TALER_EXCHANGE_PurseDeposit *deposit = &deposits[i];
     struct Coin *coin = &pch->coins[i];
     json_t *jdeposit;
-    struct TALER_CoinSpendSignatureP coin_sig;
 #if FIXME_OEC
     struct TALER_AgeCommitmentHash agh;
     struct TALER_AgeCommitmentHash *aghp = NULL;
@@ -593,7 +517,7 @@ TALER_EXCHANGE_purse_deposit (
       &pch->purse_pub,
       &deposit->amount,
       &deposit->coin_priv,
-      &coin_sig);
+      &coin->coin_sig);
     jdeposit = GNUNET_JSON_PACK (
 #if FIXME_OEC
       GNUNET_JSON_pack_allow_null (
@@ -612,7 +536,7 @@ TALER_EXCHANGE_purse_deposit (
       GNUNET_JSON_pack_data_auto ("coin_pub",
                                   &coin->coin_pub),
       GNUNET_JSON_pack_data_auto ("coin_sig",
-                                  &coin_sig));
+                                  &coin->coin_sig));
     GNUNET_assert (0 ==
                    json_array_append_new (deposit_arr,
                                           jdeposit));
diff --git a/src/lib/exchange_api_recoup.c b/src/lib/exchange_api_recoup.c
index 5c197e2f..49fb6fd5 100644
--- a/src/lib/exchange_api_recoup.c
+++ b/src/lib/exchange_api_recoup.c
@@ -1,6 +1,6 @@
 /*
   This file is part of TALER
-  Copyright (C) 2017-2021 Taler Systems SA
+  Copyright (C) 2017-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
@@ -27,6 +27,7 @@
 #include <gnunet/gnunet_curl_lib.h>
 #include "taler_json_lib.h"
 #include "taler_exchange_service.h"
+#include "exchange_api_common.h"
 #include "exchange_api_handle.h"
 #include "taler_signatures.h"
 #include "exchange_api_curl_defaults.h"
@@ -59,6 +60,11 @@ struct TALER_EXCHANGE_RecoupHandle
    */
   struct TALER_EXCHANGE_DenomPublicKey pk;
 
+  /**
+   * Our signature requesting the recoup.
+   */
+  struct TALER_CoinSpendSignatureP coin_sig;
+
   /**
    * Handle for the request.
    */
@@ -139,8 +145,10 @@ handle_recoup_finished (void *cls,
     .reply = j,
     .http_status = (unsigned int) response_code
   };
+  const struct TALER_EXCHANGE_Keys *keys;
 
   ph->job = NULL;
+  keys = TALER_EXCHANGE_get_keys (ph->exchange);
   switch (response_code)
   {
   case 0:
@@ -166,76 +174,34 @@ handle_recoup_finished (void *cls,
     break;
   case MHD_HTTP_CONFLICT:
     {
-      /* Insufficient funds, proof attached */
-      json_t *history;
-      struct TALER_Amount total;
-      struct TALER_DenominationHashP h_denom_pub;
-      const struct TALER_EXCHANGE_DenomPublicKey *dki;
-      enum TALER_ErrorCode ec;
-
-      dki = &ph->pk;
-      history = json_object_get (j,
-                                 "history");
+      struct TALER_Amount min_key;
+
+      hr.ec = TALER_JSON_get_error_code (j);
+      hr.hint = TALER_JSON_get_error_hint (j);
       if (GNUNET_OK !=
-          TALER_EXCHANGE_verify_coin_history (dki,
-                                              dki->fees.deposit.currency,
-                                              &ph->coin_pub,
-                                              history,
-                                              &h_denom_pub,
-                                              &total))
+          TALER_EXCHANGE_get_min_denomination_ (keys,
+                                                &min_key))
       {
-        GNUNET_break_op (0);
-        hr.http_status = 0;
+        GNUNET_break (0);
         hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
+        hr.http_status = 0;
+        break;
       }
-      else
-      {
-        hr.ec = TALER_JSON_get_error_code (j);
-        hr.hint = TALER_JSON_get_error_hint (j);
-      }
-      ec = TALER_JSON_get_error_code (j);
-      switch (ec)
+      if (GNUNET_OK !=
+          TALER_EXCHANGE_check_coin_conflict_ (
+            keys,
+            j,
+            &ph->pk,
+            &ph->coin_pub,
+            &ph->coin_sig,
+            &min_key))
       {
-      case TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS:
-        if (0 > TALER_amount_cmp (&total,
-                                  &dki->value))
-        {
-          /* recoup MAY have still been possible */
-          /* FIXME: This code may falsely complain, as we do not
-             know that the smallest denomination offered by the
-             exchange is here. We should look at the key
-             structure of ph->exchange, and find the smallest
-             _currently withdrawable_ denomination and check
-             if the value remaining would suffice... */
-          GNUNET_break_op (0);
-          hr.http_status = 0;
-          hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
-          break;
-        }
-        break;
-      case TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_DENOMINATION_KEY:
-        if (0 == GNUNET_memcmp (&ph->pk.h_key,
-                                &h_denom_pub))
-        {
-          /* invalid proof provided */
-          GNUNET_break_op (0);
-          hr.http_status = 0;
-          hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
-          break;
-        }
-        /* valid error from exchange */
-        break;
-      default:
-        GNUNET_break_op (0);
-        hr.http_status = 0;
+        GNUNET_break (0);
         hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
+        hr.http_status = 0;
         break;
       }
-      ph->cb (ph->cb_cls,
-              &hr,
-              NULL);
-      TALER_EXCHANGE_recoup_cancel (ph);
-      return;
+      break;
     }
   case MHD_HTTP_FORBIDDEN:
     /* Nothing really to verify, exchange says one of the signatures is
@@ -291,8 +257,6 @@ TALER_EXCHANGE_recoup (struct TALER_EXCHANGE_Handle 
*exchange,
 {
   struct TALER_EXCHANGE_RecoupHandle *ph;
   struct GNUNET_CURL_Context *ctx;
-  struct TALER_CoinSpendSignatureP coin_sig;
-  struct TALER_CoinSpendPublicKeyP coin_pub;
   struct TALER_DenominationHashP h_denom_pub;
   json_t *recoup_obj;
   CURL *eh;
@@ -302,7 +266,7 @@ TALER_EXCHANGE_recoup (struct TALER_EXCHANGE_Handle 
*exchange,
 
   GNUNET_assert (GNUNET_YES ==
                  TEAH_handle_is_ready (exchange));
-
+  ph = GNUNET_new (struct TALER_EXCHANGE_RecoupHandle);
   TALER_planchet_setup_coin_priv (ps,
                                   exchange_vals,
                                   &coin_priv);
@@ -310,13 +274,13 @@ TALER_EXCHANGE_recoup (struct TALER_EXCHANGE_Handle 
*exchange,
                                          exchange_vals,
                                          &bks);
   GNUNET_CRYPTO_eddsa_key_get_public (&coin_priv.eddsa_priv,
-                                      &coin_pub.eddsa_pub);
+                                      &ph->coin_pub.eddsa_pub);
   TALER_denom_pub_hash (&pk->key,
                         &h_denom_pub);
   TALER_wallet_recoup_sign (&h_denom_pub,
                             &bks,
                             &coin_priv,
-                            &coin_sig);
+                            &ph->coin_sig);
   recoup_obj = GNUNET_JSON_PACK (
     GNUNET_JSON_pack_data_auto ("denom_pub_hash",
                                 &h_denom_pub),
@@ -325,7 +289,7 @@ TALER_EXCHANGE_recoup (struct TALER_EXCHANGE_Handle 
*exchange,
     TALER_JSON_pack_exchange_withdraw_values ("ewv",
                                               exchange_vals),
     GNUNET_JSON_pack_data_auto ("coin_sig",
-                                &coin_sig),
+                                &ph->coin_sig),
     GNUNET_JSON_pack_data_auto ("coin_blind_key_secret",
                                 &bks));
   if (TALER_DENOMINATION_CS == denom_sig->cipher)
@@ -352,7 +316,7 @@ TALER_EXCHANGE_recoup (struct TALER_EXCHANGE_Handle 
*exchange,
     char *end;
 
     end = GNUNET_STRINGS_data_to_string (
-      &coin_pub,
+      &ph->coin_pub,
       sizeof (struct TALER_CoinSpendPublicKeyP),
       pub_str,
       sizeof (pub_str));
@@ -363,8 +327,6 @@ TALER_EXCHANGE_recoup (struct TALER_EXCHANGE_Handle 
*exchange,
                      pub_str);
   }
 
-  ph = GNUNET_new (struct TALER_EXCHANGE_RecoupHandle);
-  ph->coin_pub = coin_pub;
   ph->exchange = exchange;
   ph->pk = *pk;
   memset (&ph->pk.key,
diff --git a/src/lib/exchange_api_recoup_refresh.c 
b/src/lib/exchange_api_recoup_refresh.c
index 8ae8f976..d551df74 100644
--- a/src/lib/exchange_api_recoup_refresh.c
+++ b/src/lib/exchange_api_recoup_refresh.c
@@ -27,6 +27,7 @@
 #include <gnunet/gnunet_curl_lib.h>
 #include "taler_json_lib.h"
 #include "taler_exchange_service.h"
+#include "exchange_api_common.h"
 #include "exchange_api_handle.h"
 #include "taler_signatures.h"
 #include "exchange_api_curl_defaults.h"
@@ -79,6 +80,11 @@ struct TALER_EXCHANGE_RecoupRefreshHandle
    */
   struct TALER_CoinSpendPublicKeyP coin_pub;
 
+  /**
+   * Signature affirming the recoup-refresh operation.
+   */
+  struct TALER_CoinSpendSignatureP coin_sig;
+
 };
 
 
@@ -140,8 +146,10 @@ handle_recoup_refresh_finished (void *cls,
     .reply = j,
     .http_status = (unsigned int) response_code
   };
+  const struct TALER_EXCHANGE_Keys *keys;
 
   ph->job = NULL;
+  keys = TALER_EXCHANGE_get_keys (ph->exchange);
   switch (response_code)
   {
   case 0:
@@ -180,76 +188,34 @@ handle_recoup_refresh_finished (void *cls,
     break;
   case MHD_HTTP_CONFLICT:
     {
-      /* Insufficient funds, proof attached */
-      json_t *history;
-      struct TALER_Amount total;
-      struct TALER_DenominationHashP h_denom_pub;
-      const struct TALER_EXCHANGE_DenomPublicKey *dki;
-      enum TALER_ErrorCode ec;
-
-      dki = &ph->pk;
-      history = json_object_get (j,
-                                 "history");
+      struct TALER_Amount min_key;
+
+      hr.ec = TALER_JSON_get_error_code (j);
+      hr.hint = TALER_JSON_get_error_hint (j);
       if (GNUNET_OK !=
-          TALER_EXCHANGE_verify_coin_history (dki,
-                                              dki->fees.deposit.currency,
-                                              &ph->coin_pub,
-                                              history,
-                                              &h_denom_pub,
-                                              &total))
+          TALER_EXCHANGE_get_min_denomination_ (keys,
+                                                &min_key))
       {
-        GNUNET_break_op (0);
-        hr.http_status = 0;
+        GNUNET_break (0);
         hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
+        hr.http_status = 0;
+        break;
       }
-      else
-      {
-        hr.ec = TALER_JSON_get_error_code (j);
-        hr.hint = TALER_JSON_get_error_hint (j);
-      }
-      ec = TALER_JSON_get_error_code (j);
-      switch (ec)
+      if (GNUNET_OK !=
+          TALER_EXCHANGE_check_coin_conflict_ (
+            keys,
+            j,
+            &ph->pk,
+            &ph->coin_pub,
+            &ph->coin_sig,
+            &min_key))
       {
-      case TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS:
-        if (0 > TALER_amount_cmp (&total,
-                                  &dki->value))
-        {
-          /* recoup MAY have still been possible */
-          /* FIXME: This code may falsely complain, as we do not
-             know that the smallest denomination offered by the
-             exchange is here. We should look at the key
-             structure of ph->exchange, and find the smallest
-             _currently withdrawable_ denomination and check
-             if the value remaining would suffice... */
-          GNUNET_break_op (0);
-          hr.http_status = 0;
-          hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
-          break;
-        }
-        break;
-      case TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_DENOMINATION_KEY:
-        if (0 == GNUNET_memcmp (&ph->pk.h_key,
-                                &h_denom_pub))
-        {
-          /* invalid proof provided */
-          GNUNET_break_op (0);
-          hr.http_status = 0;
-          hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
-          break;
-        }
-        /* valid error from exchange */
-        break;
-      default:
-        GNUNET_break_op (0);
-        hr.http_status = 0;
+        GNUNET_break (0);
         hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
+        hr.http_status = 0;
         break;
       }
-      ph->cb (ph->cb_cls,
-              &hr,
-              NULL);
-      TALER_EXCHANGE_recoup_refresh_cancel (ph);
-      return;
+      break;
     }
   case MHD_HTTP_GONE:
     /* Kind of normal: the money was already sent to the merchant
@@ -295,8 +261,6 @@ TALER_EXCHANGE_recoup_refresh (
 {
   struct TALER_EXCHANGE_RecoupRefreshHandle *ph;
   struct GNUNET_CURL_Context *ctx;
-  struct TALER_CoinSpendSignatureP coin_sig;
-  struct TALER_CoinSpendPublicKeyP coin_pub;
   struct TALER_DenominationHashP h_denom_pub;
   json_t *recoup_obj;
   CURL *eh;
@@ -307,6 +271,14 @@ TALER_EXCHANGE_recoup_refresh (
   GNUNET_assert (NULL != recoup_cb);
   GNUNET_assert (GNUNET_YES ==
                  TEAH_handle_is_ready (exchange));
+  ph = GNUNET_new (struct TALER_EXCHANGE_RecoupRefreshHandle);
+  ph->exchange = exchange;
+  ph->pk = *pk;
+  memset (&ph->pk.key,
+          0,
+          sizeof (ph->pk.key)); /* zero out, as lifetime cannot be warranted */
+  ph->cb = recoup_cb;
+  ph->cb_cls = recoup_cb_cls;
   TALER_planchet_setup_coin_priv (ps,
                                   exchange_vals,
                                   &coin_priv);
@@ -314,13 +286,13 @@ TALER_EXCHANGE_recoup_refresh (
                                          exchange_vals,
                                          &bks);
   GNUNET_CRYPTO_eddsa_key_get_public (&coin_priv.eddsa_priv,
-                                      &coin_pub.eddsa_pub);
+                                      &ph->coin_pub.eddsa_pub);
   TALER_denom_pub_hash (&pk->key,
                         &h_denom_pub);
   TALER_wallet_recoup_refresh_sign (&h_denom_pub,
                                     &bks,
                                     &coin_priv,
-                                    &coin_sig);
+                                    &ph->coin_sig);
   recoup_obj = GNUNET_JSON_PACK (
     GNUNET_JSON_pack_data_auto ("denom_pub_hash",
                                 &h_denom_pub),
@@ -329,7 +301,7 @@ TALER_EXCHANGE_recoup_refresh (
     TALER_JSON_pack_exchange_withdraw_values ("ewv",
                                               exchange_vals),
     GNUNET_JSON_pack_data_auto ("coin_sig",
-                                &coin_sig),
+                                &ph->coin_sig),
     GNUNET_JSON_pack_data_auto ("coin_blind_key_secret",
                                 &bks));
 
@@ -358,7 +330,7 @@ TALER_EXCHANGE_recoup_refresh (
     char *end;
 
     end = GNUNET_STRINGS_data_to_string (
-      &coin_pub,
+      &ph->coin_pub,
       sizeof (struct TALER_CoinSpendPublicKeyP),
       pub_str,
       sizeof (pub_str));
@@ -369,15 +341,6 @@ TALER_EXCHANGE_recoup_refresh (
                      pub_str);
   }
 
-  ph = GNUNET_new (struct TALER_EXCHANGE_RecoupRefreshHandle);
-  ph->coin_pub = coin_pub;
-  ph->exchange = exchange;
-  ph->pk = *pk;
-  memset (&ph->pk.key,
-          0,
-          sizeof (ph->pk.key)); /* zero out, as lifetime cannot be warranted */
-  ph->cb = recoup_cb;
-  ph->cb_cls = recoup_cb_cls;
   ph->url = TEAH_path_to_url (exchange,
                               arg_str);
   if (NULL == ph->url)
diff --git a/src/lib/exchange_api_refund.c b/src/lib/exchange_api_refund.c
index 004661b0..09a21883 100644
--- a/src/lib/exchange_api_refund.c
+++ b/src/lib/exchange_api_refund.c
@@ -173,9 +173,12 @@ verify_conflict_history_ok (struct 
TALER_EXCHANGE_RefundHandle *rh,
                             const json_t *json)
 {
   json_t *history;
+  struct TALER_DenominationHashP h_denom_pub;
   struct GNUNET_JSON_Specification spec[] = {
     GNUNET_JSON_spec_json ("history",
                            &history),
+    GNUNET_JSON_spec_fixed_auto ("h_denom_pub",
+                                 &h_denom_pub),
     GNUNET_JSON_spec_end ()
   };
   size_t len;
@@ -234,7 +237,6 @@ verify_conflict_history_ok (struct 
TALER_EXCHANGE_RefundHandle *rh,
       struct TALER_AgeCommitmentHash h_age_commitment;
       bool no_hac;
       // struct TALER_ExtensionContractHashP h_extensions; // FIXME!
-      struct TALER_DenominationHashP h_denom_pub;
       struct GNUNET_TIME_Timestamp wallet_timestamp;
       struct TALER_MerchantPublicKeyP merchant_pub;
       struct GNUNET_TIME_Timestamp refund_deadline;
@@ -246,8 +248,6 @@ verify_conflict_history_ok (struct 
TALER_EXCHANGE_RefundHandle *rh,
                                      &h_contract_terms),
         GNUNET_JSON_spec_fixed_auto ("h_wire",
                                      &h_wire),
-        GNUNET_JSON_spec_fixed_auto ("h_denom_pub",
-                                     &h_denom_pub),
         GNUNET_JSON_spec_mark_optional (
           GNUNET_JSON_spec_fixed_auto ("h_age_commitment",
                                        &h_age_commitment),
@@ -429,24 +429,22 @@ verify_conflict_history_ok (struct 
TALER_EXCHANGE_RefundHandle *rh,
     }
   }
 
+  if (have_refund)
   {
-    if (have_refund)
+    if (0 >
+        TALER_amount_add (&rtotal,
+                          &rtotal,
+                          &rh->refund_amount))
     {
-      if (0 >
-          TALER_amount_add (&rtotal,
-                            &rtotal,
-                            &rh->refund_amount))
-      {
-        GNUNET_break (0);
-        GNUNET_JSON_parse_free (spec);
-        return GNUNET_SYSERR;
-      }
-    }
-    else
-    {
-      rtotal = rh->refund_amount;
+      GNUNET_break (0);
+      GNUNET_JSON_parse_free (spec);
+      return GNUNET_SYSERR;
     }
   }
+  else
+  {
+    rtotal = rh->refund_amount;
+  }
   if (-1 == TALER_amount_cmp (&dtotal,
                               &rtotal))
   {
diff --git a/src/testing/testing_api_cmd_deposit.c 
b/src/testing/testing_api_cmd_deposit.c
index 2f55a318..4edc6f21 100644
--- a/src/testing/testing_api_cmd_deposit.c
+++ b/src/testing/testing_api_cmd_deposit.c
@@ -257,7 +257,7 @@ deposit_cb (void *cls,
                 __LINE__);
     json_dumpf (dr->hr.reply,
                 stderr,
-                0);
+                JSON_INDENT (2));
     TALER_TESTING_interpreter_fail (ds->is);
     return;
   }
diff --git a/src/testing/testing_api_cmd_purse_deposit.c 
b/src/testing/testing_api_cmd_purse_deposit.c
index 0fa0998d..862163c6 100644
--- a/src/testing/testing_api_cmd_purse_deposit.c
+++ b/src/testing/testing_api_cmd_purse_deposit.c
@@ -145,7 +145,7 @@ deposit_cb (void *cls,
                 __LINE__);
     json_dumpf (dr->hr.reply,
                 stderr,
-                0);
+                JSON_INDENT (2));
     TALER_TESTING_interpreter_fail (ds->is);
     return;
   }

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