gnunet-svn
[Top][All Lists]
Advanced

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

[taler-exchange] branch master updated: -fixing missing '/' at end of pa


From: gnunet
Subject: [taler-exchange] branch master updated: -fixing missing '/' at end of paths in Debian package, implement coin history logic
Date: Thu, 05 Oct 2023 23:00:44 +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 35873463 -fixing missing '/' at end of paths in Debian package, 
implement coin history logic
35873463 is described below

commit 35873463b830a16a00f167a2ac9e146d0396bbb2
Author: Christian Grothoff <grothoff@gnunet.org>
AuthorDate: Thu Oct 5 23:00:15 2023 +0200

    -fixing missing '/' at end of paths in Debian package, implement coin 
history logic
---
 debian/etc-libtalerexchange/taler/taler.conf   |  10 +-
 src/auditor/taler-helper-auditor-aggregation.c |  20 +-
 src/auditor/taler-helper-auditor-coins.c       |  19 +-
 src/exchange/taler-exchange-httpd_coins_get.c  |   8 +
 src/exchangedb/pg_get_coin_transactions.c      |  18 +-
 src/exchangedb/pg_get_coin_transactions.h      |   4 +
 src/exchangedb/test_exchangedb.c               |   8 +
 src/include/taler_exchange_service.h           | 318 ++++++++++++
 src/include/taler_exchangedb_plugin.h          |  17 +-
 src/lib/Makefile.am                            |   1 +
 src/lib/exchange_api_coins_history.c           | 671 +++++++++++--------------
 11 files changed, 698 insertions(+), 396 deletions(-)

diff --git a/debian/etc-libtalerexchange/taler/taler.conf 
b/debian/etc-libtalerexchange/taler/taler.conf
index 1c86ccc3..2cf81565 100644
--- a/debian/etc-libtalerexchange/taler/taler.conf
+++ b/debian/etc-libtalerexchange/taler/taler.conf
@@ -35,11 +35,11 @@
 
 [paths]
 
-TALER_HOME = /var/lib/taler
-TALER_RUNTIME_DIR = /run/taler
-TALER_CACHE_HOME = /var/cache/taler
-TALER_CONFIG_HOME = /etc/taler
-TALER_DATA_HOME = /var/lib/taler
+TALER_HOME = /var/lib/taler/
+TALER_RUNTIME_DIR = /run/taler/
+TALER_CACHE_HOME = /var/cache/taler/
+TALER_CONFIG_HOME = /etc/taler/
+TALER_DATA_HOME = /var/lib/taler/
 
 
 # Inline configurations from all Taler components.
diff --git a/src/auditor/taler-helper-auditor-aggregation.c 
b/src/auditor/taler-helper-auditor-aggregation.c
index 8075e100..fe95dfe3 100644
--- a/src/auditor/taler-helper-auditor-aggregation.c
+++ b/src/auditor/taler-helper-auditor-aggregation.c
@@ -783,12 +783,20 @@ wire_transfer_information_cb (
   /* TODO: could use 'start' mechanism to only fetch transactions
      we did not yet process, instead of going over them
      again and again.*/
-  qs = TALER_ARL_edb->get_coin_transactions (TALER_ARL_edb->cls,
-                                             coin_pub,
-                                             0,
-                                             0,
-                                             &etag_out,
-                                             &tl);
+
+  {
+    struct TALER_Amount balance;
+    struct TALER_DenominationHashP h_denom_pub;
+
+    qs = TALER_ARL_edb->get_coin_transactions (TALER_ARL_edb->cls,
+                                               coin_pub,
+                                               0,
+                                               0,
+                                               &etag_out,
+                                               &balance,
+                                               &h_denom_pub,
+                                               &tl);
+  }
   if ( (qs < 0) ||
        (NULL == tl) )
   {
diff --git a/src/auditor/taler-helper-auditor-coins.c 
b/src/auditor/taler-helper-auditor-coins.c
index f873fa3c..bc1598b0 100644
--- a/src/auditor/taler-helper-auditor-coins.c
+++ b/src/auditor/taler-helper-auditor-coins.c
@@ -440,12 +440,19 @@ check_coin_history (const struct 
TALER_CoinSpendPublicKeyP *coin_pub,
   /* TODO: could use 'etag' mechanism to only fetch transactions
      we did not yet process, instead of going over them
      again and again. */
-  qs = TALER_ARL_edb->get_coin_transactions (TALER_ARL_edb->cls,
-                                             coin_pub,
-                                             0,
-                                             0,
-                                             &etag_out,
-                                             &tl);
+  {
+    struct TALER_Amount balance;
+    struct TALER_DenominationHashP h_denom_pub;
+
+    qs = TALER_ARL_edb->get_coin_transactions (TALER_ARL_edb->cls,
+                                               coin_pub,
+                                               0,
+                                               0,
+                                               &etag_out,
+                                               &balance,
+                                               &h_denom_pub,
+                                               &tl);
+  }
   if (0 >= qs)
     return qs;
   GNUNET_assert (GNUNET_OK ==
diff --git a/src/exchange/taler-exchange-httpd_coins_get.c 
b/src/exchange/taler-exchange-httpd_coins_get.c
index 655a4c54..cd453275 100644
--- a/src/exchange/taler-exchange-httpd_coins_get.c
+++ b/src/exchange/taler-exchange-httpd_coins_get.c
@@ -544,6 +544,8 @@ TEH_handler_coins_get (struct TEH_RequestContext *rc,
   char etagp[24];
   struct MHD_Response *resp;
   unsigned int http_status;
+  struct TALER_DenominationHashP h_denom_pub;
+  struct TALER_Amount balance;
 
   TALER_MHD_parse_request_number (rc->connection,
                                   "start",
@@ -612,6 +614,8 @@ TEH_handler_coins_get (struct TEH_RequestContext *rc,
                                             start_off,
                                             etag_in,
                                             &etag_out,
+                                            &balance,
+                                            &h_denom_pub,
                                             &tl);
     switch (qs)
     {
@@ -675,6 +679,10 @@ TEH_handler_coins_get (struct TEH_RequestContext *rc,
                                          "Failed to compile coin history");
     }
     resp = TALER_MHD_MAKE_JSON_PACK (
+      GNUNET_JSON_pack_data_auto ("h_denom_pub",
+                                  &h_denom_pub),
+      TALER_JSON_pack_amount ("balance",
+                              &balance),
       GNUNET_JSON_pack_array_steal ("history",
                                     history));
     http_status = MHD_HTTP_OK;
diff --git a/src/exchangedb/pg_get_coin_transactions.c 
b/src/exchangedb/pg_get_coin_transactions.c
index e5d3b9b0..640f93ec 100644
--- a/src/exchangedb/pg_get_coin_transactions.c
+++ b/src/exchangedb/pg_get_coin_transactions.c
@@ -810,6 +810,8 @@ TEH_PG_get_coin_transactions (
   uint64_t start_off,
   uint64_t etag_in,
   uint64_t *etag_out,
+  struct TALER_Amount *balance,
+  struct TALER_DenominationHashP *h_denom_pub,
   struct TALER_EXCHANGEDB_TransactionList **tlp)
 {
   struct PostgresClosure *pg = cls;
@@ -833,10 +835,16 @@ TEH_PG_get_coin_transactions (
               "Getting transactions for coin %s\n",
               TALER_B2S (coin_pub));
   PREPARE (pg,
-           "get_coin_history_etag",
+           "get_coin_history_etag_balance",
            "SELECT"
-           " coin_history_serial_id"
-           " FROM coin_history"
+           " ch.coin_history_serial_id"
+           ",kc.remaining"
+           ",denom.denom_pub_hash"
+           " FROM coin_history ch"
+           " JOIN known_coins kc"
+           "   USING (coin_pub)"
+           " JOIN denominations denom"
+           "   USING (denominations_serial)"
            " WHERE coin_pub=$1"
            " ORDER BY coin_history_serial_id DESC"
            " LIMIT 1;");
@@ -1045,6 +1053,10 @@ TEH_PG_get_coin_transactions (
     struct GNUNET_PQ_ResultSpec rs[] = {
       GNUNET_PQ_result_spec_uint64 ("coin_history_serial_id",
                                     &end),
+      GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash",
+                                            h_denom_pub),
+      TALER_PQ_RESULT_SPEC_AMOUNT ("remaining",
+                                   balance),
       GNUNET_PQ_result_spec_end
     };
 
diff --git a/src/exchangedb/pg_get_coin_transactions.h 
b/src/exchangedb/pg_get_coin_transactions.h
index c19df387..46e32e09 100644
--- a/src/exchangedb/pg_get_coin_transactions.h
+++ b/src/exchangedb/pg_get_coin_transactions.h
@@ -39,6 +39,8 @@
  * @param etag_in up to this offset the client already has a response, do not
  *                   return anything unless @a etag_out will be larger
  * @param[out] etag_out set to the latest history offset known for this @a 
coin_pub
+ * @param[out] balance set to current balance of the coin
+ * @param[out] h_denom_pub set to denomination public key of the coin
  * @param[out] tlp set to list of transactions, set to NULL if coin has no
  *             transaction history past @a start_off or if @a etag_in is equal
  *             to the value written to @a etag_out.
@@ -51,6 +53,8 @@ TEH_PG_get_coin_transactions (
   uint64_t start_off,
   uint64_t etag_in,
   uint64_t *etag_out,
+  struct TALER_Amount *balance,
+  struct TALER_DenominationHashP *h_denom_pub,
   struct TALER_EXCHANGEDB_TransactionList **tlp);
 
 
diff --git a/src/exchangedb/test_exchangedb.c b/src/exchangedb/test_exchangedb.c
index f0948991..eeaaffad 100644
--- a/src/exchangedb/test_exchangedb.c
+++ b/src/exchangedb/test_exchangedb.c
@@ -1724,12 +1724,16 @@ run (void *cls)
       struct TALER_EXCHANGEDB_TransactionList *tl;
       enum GNUNET_DB_QueryStatus qs;
       uint64_t etag;
+      struct TALER_Amount balance;
+      struct TALER_DenominationHashP h_denom_pub;
 
       qs = plugin->get_coin_transactions (plugin->cls,
                                           &refresh.coin.coin_pub,
                                           0,
                                           0,
                                           &etag,
+                                          &balance,
+                                          &h_denom_pub,
                                           &tl);
       FAILIF (0 >= qs);
       FAILIF (NULL == tl);
@@ -1985,12 +1989,16 @@ run (void *cls)
   FAILIF (1 != auditor_row_cnt);
   {
     uint64_t etag = 0;
+    struct TALER_Amount balance;
+    struct TALER_DenominationHashP h_denom_pub;
 
     qs = plugin->get_coin_transactions (plugin->cls,
                                         &refund.coin.coin_pub,
                                         0,
                                         0,
                                         &etag,
+                                        &balance,
+                                        &h_denom_pub,
                                         &tl);
   }
   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs);
diff --git a/src/include/taler_exchange_service.h 
b/src/include/taler_exchange_service.h
index 662700d4..e0da5ed1 100644
--- a/src/include/taler_exchange_service.h
+++ b/src/include/taler_exchange_service.h
@@ -1576,6 +1576,324 @@ TALER_EXCHANGE_csr_withdraw_cancel (
   struct TALER_EXCHANGE_CsRWithdrawHandle *csrh);
 
 
+/* ********************* GET /coins/$COIN_PUB *********************** */
+
+/**
+ * Ways how a coin's balance may change.
+ */
+enum TALER_EXCHANGE_CoinTransactionType
+{
+
+  /**
+   * Reserved for uninitialized / none.
+   */
+  TALER_EXCHANGE_CTT_NONE,
+
+  /**
+   * Deposit into a contract.
+   */
+  TALER_EXCHANGE_CTT_DEPOSIT,
+
+  /**
+   * Spent on melt.
+   */
+  TALER_EXCHANGE_CTT_MELT,
+
+  /**
+   * Refunded by merchant.
+   */
+  TALER_EXCHANGE_CTT_REFUND,
+
+  /**
+   * Debited in recoup (to reserve) operation.
+   */
+  TALER_EXCHANGE_CTT_RECOUP,
+
+  /**
+   * Debited in recoup-and-refresh operation.
+   */
+  TALER_EXCHANGE_CTT_RECOUP_REFRESH,
+
+  /**
+   * Credited in recoup-refresh.
+   */
+  TALER_EXCHANGE_CTT_OLD_COIN_RECOUP,
+
+  /**
+   * Deposited into purse.
+   */
+  TALER_EXCHANGE_CTT_PURSE_DEPOSIT,
+
+  /**
+   * Refund from purse.
+   */
+  TALER_EXCHANGE_CTT_PURSE_REFUND,
+
+  /**
+   * Reserve open payment operation.
+   */
+  TALER_EXCHANGE_CTT_RESERVE_OPEN_DEPOSIT
+
+};
+
+
+/**
+ * @brief Entry in the coin's transaction history.
+ */
+struct TALER_EXCHANGE_CoinHistoryEntry
+{
+
+  /**
+   * Type of the transaction.
+   */
+  enum TALER_EXCHANGE_CoinTransactionType type;
+
+  /**
+   * Amount transferred (in or out).
+   */
+  struct TALER_Amount amount;
+
+  /**
+   * Details depending on @e type.
+   */
+  union
+  {
+
+    struct
+    {
+      struct TALER_MerchantWireHashP h_wire;
+      struct TALER_PrivateContractHashP h_contract_terms;
+      struct TALER_ExtensionPolicyHashP h_policy;
+      bool no_h_policy;
+      struct GNUNET_HashCode wallet_data_hash;
+      bool no_wallet_data_hash;
+      struct GNUNET_TIME_Timestamp wallet_timestamp;
+      struct TALER_MerchantPublicKeyP merchant_pub;
+      struct GNUNET_TIME_Timestamp refund_deadline;
+      struct TALER_CoinSpendSignatureP sig;
+      struct TALER_AgeCommitmentHash hac;
+      bool no_hac;
+      struct TALER_Amount deposit_fee;
+    } deposit;
+
+    struct
+    {
+      struct TALER_CoinSpendSignatureP sig;
+      struct TALER_RefreshCommitmentP rc;
+      struct TALER_AgeCommitmentHash h_age_commitment;
+      bool no_hac;
+      struct TALER_Amount melt_fee;
+    } melt;
+
+    struct
+    {
+      struct TALER_PrivateContractHashP h_contract_terms;
+      struct TALER_MerchantPublicKeyP merchant_pub;
+      struct TALER_MerchantSignatureP sig;
+      struct TALER_Amount refund_fee;
+      struct TALER_Amount sig_amount;
+      uint64_t rtransaction_id;
+    } refund;
+
+    struct
+    {
+      struct TALER_ReservePublicKeyP reserve_pub;
+      struct GNUNET_TIME_Timestamp timestamp;
+      union TALER_DenominationBlindingKeyP coin_bks;
+      struct TALER_ExchangePublicKeyP exchange_pub;
+      struct TALER_ExchangeSignatureP exchange_sig;
+      struct TALER_CoinSpendSignatureP coin_sig;
+    } recoup;
+
+    struct
+    {
+      struct TALER_CoinSpendPublicKeyP old_coin_pub;
+      union TALER_DenominationBlindingKeyP coin_bks;
+      struct GNUNET_TIME_Timestamp timestamp;
+      struct TALER_ExchangePublicKeyP exchange_pub;
+      struct TALER_ExchangeSignatureP exchange_sig;
+      struct TALER_CoinSpendSignatureP coin_sig;
+    } recoup_refresh;
+
+    struct
+    {
+      struct TALER_ExchangePublicKeyP exchange_pub;
+      struct TALER_ExchangeSignatureP exchange_sig;
+      struct TALER_CoinSpendPublicKeyP new_coin_pub;
+      struct GNUNET_TIME_Timestamp timestamp;
+    } old_coin_recoup;
+
+    struct
+    {
+      struct TALER_PurseContractPublicKeyP purse_pub;
+      struct TALER_CoinSpendSignatureP coin_sig;
+      const char *exchange_base_url;
+      bool refunded;
+      struct TALER_AgeCommitmentHash phac;
+    } purse_deposit;
+
+    struct
+    {
+      struct TALER_PurseContractPublicKeyP purse_pub;
+      struct TALER_Amount refund_fee;
+      struct TALER_ExchangePublicKeyP exchange_pub;
+      struct TALER_ExchangeSignatureP exchange_sig;
+    } purse_refund;
+
+    struct
+    {
+      struct TALER_ReserveSignatureP reserve_sig;
+      struct TALER_CoinSpendSignatureP coin_sig;
+    } reserve_open_deposit;
+
+  } details;
+
+};
+
+
+/**
+ * @brief A /coins/$RID/history Handle
+ */
+struct TALER_EXCHANGE_CoinsHistoryHandle;
+
+
+/**
+ * Parses and verifies a coin's transaction history as
+ * returned by the exchange.  Note that in case of
+ * incremental histories, the client must first combine
+ * the incremental histories into one complete history.
+ *
+ * @param keys /keys data of the exchange
+ * @param dk denomination key of the coin
+ * @param history JSON array with the coin's history
+ * @param coin_pub public key of the coin
+ * @param currency currency of the coin
+ * @param[out] total_in set to total amount credited to the coin in @a history
+ * @param[out] total_out set to total amount debited to the coin in @a history
+ * @param rlen length of the @a rhistory array
+ * @param[out] rhistory array where to write the parsed @a history
+ * @return #GNUNET_OK if @a history is valid,
+ *         #GNUNET_SYSERR if not
+ */
+enum GNUNET_GenericReturnValue
+TALER_EXCHANGE_parse_coin_history (
+  const struct TALER_EXCHANGE_Keys *keys,
+  const struct TALER_EXCHANGE_DenomPublicKey *dk,
+  const json_t *history,
+  const struct TALER_CoinSpendPublicKeyP *coin_pub,
+  struct TALER_Amount *total_in,
+  struct TALER_Amount *total_out,
+  unsigned int rlen,
+  struct TALER_EXCHANGE_CoinHistoryEntry rhistory[static rlen]);
+
+
+/**
+ * Verify that @a coin_sig does NOT appear in the @a history of a coin's
+ * transactions and thus whatever transaction is authorized by @a coin_sig is
+ * a conflict with @a proof.
+ *
+ * @param history coin history to check
+ * @param coin_sig signature that must not be in @a history
+ * @return #GNUNET_OK if @a coin_sig is not in @a history
+ */
+enum GNUNET_GenericReturnValue
+TALER_EXCHANGE_check_coin_signature_conflict (
+  const json_t *history,
+  const struct TALER_CoinSpendSignatureP *coin_sig);
+
+
+/**
+ * Response to a GET /coins/$COIN_PUB/history request.
+ */
+struct TALER_EXCHANGE_CoinHistory
+{
+  /**
+   * High-level HTTP response details.
+   */
+  struct TALER_EXCHANGE_HttpResponse hr;
+
+  /**
+   * Details depending on @e hr.http_status.
+   */
+  union
+  {
+
+    /**
+     * Information returned on success, if
+     * @e hr.http_status is #MHD_HTTP_OK
+     */
+    struct
+    {
+
+      /**
+       * Coin transaction history (possibly partial).
+       * Not yet validated, combine with other already
+       * known history data for this coin and then use
+       * #TALER_EXCHANGE_parse_coin_history() to validate
+       * the complete history and obtain it in binary
+       * format.
+       */
+      const json_t *history;
+
+      /**
+       * The hash of the coin denomination's public key
+       */
+      struct TALER_DenominationHashP h_denom_pub;
+
+      /**
+       * Coin balance.
+       */
+      struct TALER_Amount balance;
+
+    } ok;
+
+  } details;
+
+};
+
+typedef void
+(*TALER_EXCHANGE_CoinsHistoryCallback)(
+  void *cls,
+  const struct TALER_EXCHANGE_CoinHistory *ch);
+
+/**
+ * Parses and verifies a coin's transaction history as
+ * returned by the exchange. Note that a client may
+ * have to combine multiple partial coin histories
+ * into one coherent history before calling this function.
+ *
+ * @param keys /keys data of the exchange
+ * @param dk denomination key of the coin
+ * @param history JSON array with the coin's full history
+ * @param coin_pub public key of the coin
+ * @param currency currency of the coin
+ * @param[out] total_in set to total amount credited to the coin in @a history
+ * @param[out] total_out set to total amount debited to the coin in @a history
+ * @param len length of the @a rhistory
+ * @param[out] rhistory where to write the parsed @a history
+ * @return #GNUNET_OK if @a history is valid,
+ *         #GNUNET_SYSERR if not
+ */
+struct TALER_EXCHANGE_CoinsHistoryHandle *
+TALER_EXCHANGE_coins_history (
+  struct GNUNET_CURL_Context *ctx,
+  const char *url,
+  const struct TALER_CoinSpendPrivateKeyP *coin_priv,
+  uint64_t start_off,
+  TALER_EXCHANGE_CoinsHistoryCallback cb,
+  void *cb_cls);
+
+
+/**
+ * Cancel #TALER_EXCHANGE_coins_history() operation.
+ *
+ * @param[in] rsh operation to chancel
+ */
+void
+TALER_EXCHANGE_coins_history_cancel (
+  struct TALER_EXCHANGE_CoinsHistoryHandle *rsh);
+
+
 /* ********************* GET /reserves/$RESERVE_PUB *********************** */
 
 /**
diff --git a/src/include/taler_exchangedb_plugin.h 
b/src/include/taler_exchangedb_plugin.h
index 484b1155..f0f4d6aa 100644
--- a/src/include/taler_exchangedb_plugin.h
+++ b/src/include/taler_exchangedb_plugin.h
@@ -4672,18 +4672,23 @@ struct TALER_EXCHANGEDB_Plugin
    * @param etag_in up to this offset the client already has a response, do not
    *                   return anything unless @a etag_out will be larger
    * @param[out] etag_out set to the latest history offset known for this @a 
coin_pub
+   * @param[out] balance set to current balance of the coin
+   * @param[out] h_denom_pub set to denomination public key of the coin
    * @param[out] tlp set to list of transactions, set to NULL if coin has no
    *             transaction history past @a start_off or if @a etag_in is 
equal
    *             to the value written to @a etag_out.
    * @return database transaction status
    */
   enum GNUNET_DB_QueryStatus
-  (*get_coin_transactions)(void *cls,
-                           const struct TALER_CoinSpendPublicKeyP *coin_pub,
-                           uint64_t start_off,
-                           uint64_t etag_in,
-                           uint64_t *etag_out,
-                           struct TALER_EXCHANGEDB_TransactionList **tlp);
+  (*get_coin_transactions)(
+    void *cls,
+    const struct TALER_CoinSpendPublicKeyP *coin_pub,
+    uint64_t start_off,
+    uint64_t etag_in,
+    uint64_t *etag_out,
+    struct TALER_Amount *balance,
+    struct TALER_DenominationHashP *h_denom_pub,
+    struct TALER_EXCHANGEDB_TransactionList **tlp);
 
 
   /**
diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am
index ff5d3b80..12f991d8 100644
--- a/src/lib/Makefile.am
+++ b/src/lib/Makefile.am
@@ -29,6 +29,7 @@ libtalerexchange_la_SOURCES = \
   exchange_api_batch_withdraw.c \
   exchange_api_batch_withdraw2.c \
   exchange_api_curl_defaults.c exchange_api_curl_defaults.h \
+  exchange_api_coins_history.c \
   exchange_api_common.c exchange_api_common.h \
   exchange_api_contracts_get.c \
   exchange_api_csr_melt.c \
diff --git a/src/lib/exchange_api_coins_history.c 
b/src/lib/exchange_api_coins_history.c
index ae718c45..b74e1a9b 100644
--- a/src/lib/exchange_api_coins_history.c
+++ b/src/lib/exchange_api_coins_history.c
@@ -40,11 +40,6 @@
 struct TALER_EXCHANGE_CoinsHistoryHandle
 {
 
-  /**
-   * The keys of the exchange this request handle will use
-   */
-  struct TALER_EXCHANGE_Keys *keys;
-
   /**
    * The url for this request.
    */
@@ -69,7 +64,7 @@ struct TALER_EXCHANGE_CoinsHistoryHandle
   /**
    * Public key of the coin we are querying.
    */
-  struct TALER_CoinPublicKeyP coin_pub;
+  struct TALER_CoinSpendPublicKeyP coin_pub;
 
   /**
    * Closure for @a cb.
@@ -85,6 +80,11 @@ struct TALER_EXCHANGE_CoinsHistoryHandle
 struct CoinHistoryParseContext
 {
 
+  /**
+   * Keys of the exchange.
+   */
+  struct TALER_EXCHANGE_Keys *keys;
+
   /**
    * Denomination of the coin.
    */
@@ -98,12 +98,12 @@ struct CoinHistoryParseContext
   /**
    * Where to sum up total refunds.
    */
-  struct TALER_Amount rtotal;
+  struct TALER_Amount *total_in;
 
   /**
    * Total amount encountered.
    */
-  struct TALER_Amount *total;
+  struct TALER_Amount *total_out;
 
 };
 
@@ -113,6 +113,7 @@ struct CoinHistoryParseContext
  * the coin's history entries.
  *
  * @param[in,out] pc overall context
+ * @param[out] rh where to write the history entry
  * @param amount main amount of this operation
  * @param transaction JSON details for the operation
  * @return #GNUNET_SYSERR on error,
@@ -120,6 +121,7 @@ struct CoinHistoryParseContext
  */
 typedef enum GNUNET_GenericReturnValue
 (*CoinCheckHelper)(struct CoinHistoryParseContext *pc,
+                   struct TALER_EXCHANGE_CoinHistoryEntry *rh,
                    const struct TALER_Amount *amount,
                    json_t *transaction);
 
@@ -135,54 +137,43 @@ typedef enum GNUNET_GenericReturnValue
  */
 static enum GNUNET_GenericReturnValue
 help_deposit (struct CoinHistoryParseContext *pc,
+              struct TALER_EXCHANGE_CoinHistoryEntry *rh,
               const struct TALER_Amount *amount,
               json_t *transaction)
 {
-  struct TALER_MerchantWireHashP h_wire;
-  struct TALER_PrivateContractHashP h_contract_terms;
-  struct TALER_ExtensionPolicyHashP h_policy;
-  bool no_h_policy;
-  struct GNUNET_HashCode wallet_data_hash;
-  bool no_wallet_data_hash;
-  struct GNUNET_TIME_Timestamp wallet_timestamp;
-  struct TALER_MerchantPublicKeyP merchant_pub;
-  struct GNUNET_TIME_Timestamp refund_deadline = {0};
-  struct TALER_CoinSpendSignatureP sig;
-  struct TALER_AgeCommitmentHash hac;
-  bool no_hac;
-  struct TALER_Amount deposit_fee;
   struct GNUNET_JSON_Specification spec[] = {
     GNUNET_JSON_spec_fixed_auto ("coin_sig",
-                                 &sig),
+                                 &rh->details.deposit.sig),
     GNUNET_JSON_spec_fixed_auto ("h_contract_terms",
-                                 &h_contract_terms),
+                                 &rh->details.deposit.h_contract_terms),
     GNUNET_JSON_spec_mark_optional (
       GNUNET_JSON_spec_fixed_auto ("wallet_data_hash",
-                                   &wallet_data_hash),
-      &no_wallet_data_hash),
+                                   &rh->details.deposit.wallet_data_hash),
+      &rh->details.deposit.no_wallet_data_hash),
     GNUNET_JSON_spec_fixed_auto ("h_wire",
-                                 &h_wire),
+                                 &rh->details.deposit.h_wire),
     GNUNET_JSON_spec_mark_optional (
       GNUNET_JSON_spec_fixed_auto ("h_age_commitment",
-                                   &hac),
-      &no_hac),
+                                   &rh->details.deposit.hac),
+      &rh->details.deposit.no_hac),
     GNUNET_JSON_spec_mark_optional (
       GNUNET_JSON_spec_fixed_auto ("h_policy",
-                                   &h_policy),
-      &no_h_policy),
+                                   &rh->details.deposit.h_policy),
+      &rh->details.deposit.no_h_policy),
     GNUNET_JSON_spec_timestamp ("timestamp",
-                                &wallet_timestamp),
+                                &rh->details.deposit.wallet_timestamp),
     GNUNET_JSON_spec_mark_optional (
       GNUNET_JSON_spec_timestamp ("refund_deadline",
-                                  &refund_deadline),
+                                  &rh->details.deposit.refund_deadline),
       NULL),
     TALER_JSON_spec_amount_any ("deposit_fee",
-                                &deposit_fee),
+                                &rh->details.deposit.deposit_fee),
     GNUNET_JSON_spec_fixed_auto ("merchant_pub",
-                                 &merchant_pub),
+                                 &rh->details.deposit.merchant_pub),
     GNUNET_JSON_spec_end ()
   };
 
+  rh->details.deposit.refund_deadline = GNUNET_TIME_UNIT_ZERO_TS;
   if (GNUNET_OK !=
       GNUNET_JSON_parse (transaction,
                          spec,
@@ -194,28 +185,34 @@ help_deposit (struct CoinHistoryParseContext *pc,
   if (GNUNET_OK !=
       TALER_wallet_deposit_verify (
         amount,
-        &deposit_fee,
-        &h_wire,
-        &h_contract_terms,
-        no_wallet_data_hash ? NULL : &wallet_data_hash,
-        no_hac ? NULL : &hac,
-        no_h_policy ? NULL : &h_policy,
+        &rh->details.deposit.deposit_fee,
+        &rh->details.deposit.h_wire,
+        &rh->details.deposit.h_contract_terms,
+        rh->details.deposit.no_wallet_data_hash
+        ? NULL
+        : &rh->details.deposit.wallet_data_hash,
+        rh->details.deposit.no_hac
+        ? NULL
+        : &rh->details.deposit.hac,
+        rh->details.deposit.no_h_policy
+        ? NULL
+        : &rh->details.deposit.h_policy,
         &pc->dk->h_key,
-        wallet_timestamp,
-        &merchant_pub,
-        refund_deadline,
+        rh->details.deposit.wallet_timestamp,
+        &rh->details.deposit.merchant_pub,
+        rh->details.deposit.refund_deadline,
         pc->coin_pub,
-        &sig))
+        &rh->details.deposit.sig))
   {
     GNUNET_break_op (0);
     return GNUNET_SYSERR;
   }
   /* check that deposit fee matches our expectations from /keys! */
   if ( (GNUNET_YES !=
-        TALER_amount_cmp_currency (&deposit_fee,
+        TALER_amount_cmp_currency (&rh->details.deposit.deposit_fee,
                                    &pc->dk->fees.deposit)) ||
        (0 !=
-        TALER_amount_cmp (&deposit_fee,
+        TALER_amount_cmp (&rh->details.deposit.deposit_fee,
                           &pc->dk->fees.deposit)) )
   {
     GNUNET_break_op (0);
@@ -236,25 +233,21 @@ help_deposit (struct CoinHistoryParseContext *pc,
  */
 static enum GNUNET_GenericReturnValue
 help_melt (struct CoinHistoryParseContext *pc,
+           struct TALER_EXCHANGE_CoinHistoryEntry *rh,
            const struct TALER_Amount *amount,
            json_t *transaction)
 {
-  struct TALER_CoinSpendSignatureP sig;
-  struct TALER_RefreshCommitmentP rc;
-  struct TALER_AgeCommitmentHash h_age_commitment;
-  bool no_hac;
-  struct TALER_Amount melt_fee;
   struct GNUNET_JSON_Specification spec[] = {
     GNUNET_JSON_spec_fixed_auto ("coin_sig",
-                                 &sig),
+                                 &rh->details.melt.sig),
     GNUNET_JSON_spec_fixed_auto ("rc",
-                                 &rc),
+                                 &rh->details.melt.rc),
     GNUNET_JSON_spec_mark_optional (
       GNUNET_JSON_spec_fixed_auto ("h_age_commitment",
-                                   &h_age_commitment),
-      &no_hac),
+                                   &rh->details.melt.h_age_commitment),
+      &rh->details.melt.no_hac),
     TALER_JSON_spec_amount_any ("melt_fee",
-                                &melt_fee),
+                                &rh->details.melt.melt_fee),
     GNUNET_JSON_spec_end ()
   };
 
@@ -269,10 +262,10 @@ help_melt (struct CoinHistoryParseContext *pc,
 
   /* check that melt fee matches our expectations from /keys! */
   if ( (GNUNET_YES !=
-        TALER_amount_cmp_currency (&melt_fee,
+        TALER_amount_cmp_currency (&rh->details.melt.melt_fee,
                                    &pc->dk->fees.refresh)) ||
        (0 !=
-        TALER_amount_cmp (&melt_fee,
+        TALER_amount_cmp (&rh->details.melt.melt_fee,
                           &pc->dk->fees.refresh)) )
   {
     GNUNET_break_op (0);
@@ -281,14 +274,14 @@ help_melt (struct CoinHistoryParseContext *pc,
   if (GNUNET_OK !=
       TALER_wallet_melt_verify (
         amount,
-        &melt_fee,
-        &rc,
+        &rh->details.melt.melt_fee,
+        &rh->details.melt.rc,
         &pc->dk->h_key,
-        no_hac
+        rh->details.melt.no_hac
         ? NULL
-        : &h_age_commitment,
+        : &rh->details.melt.h_age_commitment,
         pc->coin_pub,
-        &sig))
+        &rh->details.melt.sig))
   {
     GNUNET_break_op (0);
     return GNUNET_SYSERR;
@@ -308,26 +301,21 @@ help_melt (struct CoinHistoryParseContext *pc,
  */
 static enum GNUNET_GenericReturnValue
 help_refund (struct CoinHistoryParseContext *pc,
+             struct TALER_EXCHANGE_CoinHistoryEntry *rh,
              const struct TALER_Amount *amount,
              json_t *transaction)
 {
-  struct TALER_PrivateContractHashP h_contract_terms;
-  struct TALER_MerchantPublicKeyP merchant_pub;
-  struct TALER_MerchantSignatureP sig;
-  struct TALER_Amount refund_fee;
-  struct TALER_Amount sig_amount;
-  uint64_t rtransaction_id;
   struct GNUNET_JSON_Specification spec[] = {
     TALER_JSON_spec_amount_any ("refund_fee",
-                                &refund_fee),
+                                &rh->details.refund.refund_fee),
     GNUNET_JSON_spec_fixed_auto ("merchant_sig",
-                                 &sig),
+                                 &rh->details.refund.sig),
     GNUNET_JSON_spec_fixed_auto ("h_contract_terms",
-                                 &h_contract_terms),
+                                 &rh->details.refund.h_contract_terms),
     GNUNET_JSON_spec_fixed_auto ("merchant_pub",
-                                 &merchant_pub),
+                                 &rh->details.refund.merchant_pub),
     GNUNET_JSON_spec_uint64 ("rtransaction_id",
-                             &rtransaction_id),
+                             &rh->details.refund.rtransaction_id),
     GNUNET_JSON_spec_end ()
   };
 
@@ -340,8 +328,8 @@ help_refund (struct CoinHistoryParseContext *pc,
     return GNUNET_SYSERR;
   }
   if (0 >
-      TALER_amount_add (&sig_amount,
-                        &refund_fee,
+      TALER_amount_add (&rh->details.refund.sig_amount,
+                        &rh->details.refund.refund_fee,
                         amount))
   {
     GNUNET_break_op (0);
@@ -349,11 +337,11 @@ help_refund (struct CoinHistoryParseContext *pc,
   }
   if (GNUNET_OK !=
       TALER_merchant_refund_verify (pc->coin_pub,
-                                    &h_contract_terms,
-                                    rtransaction_id,
-                                    &sig_amount,
-                                    &merchant_pub,
-                                    &sig))
+                                    &rh->details.refund.h_contract_terms,
+                                    rh->details.refund.rtransaction_id,
+                                    &rh->details.refund.sig_amount,
+                                    &rh->details.refund.merchant_pub,
+                                    &rh->details.refund.sig))
   {
     GNUNET_break_op (0);
     return GNUNET_SYSERR;
@@ -368,10 +356,10 @@ help_refund (struct CoinHistoryParseContext *pc,
 
   /* check that refund fee matches our expectations from /keys! */
   if ( (GNUNET_YES !=
-        TALER_amount_cmp_currency (&refund_fee,
+        TALER_amount_cmp_currency (&rh->details.refund.refund_fee,
                                    &pc->dk->fees.refund)) ||
        (0 !=
-        TALER_amount_cmp (&refund_fee,
+        TALER_amount_cmp (&rh->details.refund.refund_fee,
                           &pc->dk->fees.refund)) )
   {
     GNUNET_break_op (0);
@@ -392,28 +380,23 @@ help_refund (struct CoinHistoryParseContext *pc,
  */
 static enum GNUNET_GenericReturnValue
 help_recoup (struct CoinHistoryParseContext *pc,
+             struct TALER_EXCHANGE_CoinHistoryEntry *rh,
              const struct TALER_Amount *amount,
              json_t *transaction)
 {
-  struct TALER_ReservePublicKeyP reserve_pub;
-  struct GNUNET_TIME_Timestamp timestamp;
-  union TALER_DenominationBlindingKeyP coin_bks;
-  struct TALER_ExchangePublicKeyP exchange_pub;
-  struct TALER_ExchangeSignatureP exchange_sig;
-  struct TALER_CoinSpendSignatureP coin_sig;
   struct GNUNET_JSON_Specification spec[] = {
     GNUNET_JSON_spec_fixed_auto ("exchange_sig",
-                                 &exchange_sig),
+                                 &rh->details.recoup.exchange_sig),
     GNUNET_JSON_spec_fixed_auto ("exchange_pub",
-                                 &exchange_pub),
+                                 &rh->details.recoup.exchange_pub),
     GNUNET_JSON_spec_fixed_auto ("reserve_pub",
-                                 &reserve_pub),
+                                 &rh->details.recoup.reserve_pub),
     GNUNET_JSON_spec_fixed_auto ("coin_sig",
-                                 &coin_sig),
+                                 &rh->details.recoup.coin_sig),
     GNUNET_JSON_spec_fixed_auto ("coin_blind",
-                                 &coin_bks),
+                                 &rh->details.recoup.coin_bks),
     GNUNET_JSON_spec_timestamp ("timestamp",
-                                &timestamp),
+                                &rh->details.recoup.timestamp),
     GNUNET_JSON_spec_end ()
   };
 
@@ -427,21 +410,21 @@ help_recoup (struct CoinHistoryParseContext *pc,
   }
   if (GNUNET_OK !=
       TALER_exchange_online_confirm_recoup_verify (
-        timestamp,
+        rh->details.recoup.timestamp,
         amount,
         pc->coin_pub,
-        &reserve_pub,
-        &exchange_pub,
-        &exchange_sig))
+        &rh->details.recoup.reserve_pub,
+        &rh->details.recoup.exchange_pub,
+        &rh->details.recoup.exchange_sig))
   {
     GNUNET_break_op (0);
     return GNUNET_SYSERR;
   }
   if (GNUNET_OK !=
       TALER_wallet_recoup_verify (&pc->dk->h_key,
-                                  &coin_bks,
+                                  &rh->details.recoup.coin_bks,
                                   pc->coin_pub,
-                                  &coin_sig))
+                                  &rh->details.recoup.coin_sig))
   {
     GNUNET_break_op (0);
     return GNUNET_SYSERR;
@@ -452,6 +435,8 @@ help_recoup (struct CoinHistoryParseContext *pc,
 
 /**
  * Handle recoup-refresh entry in the coin's history.
+ * This is the coin that was subjected to a recoup,
+ * the value being credited to the old coin.
  *
  * @param[in,out] pc overall context
  * @param amount main amount of this operation
@@ -461,30 +446,23 @@ help_recoup (struct CoinHistoryParseContext *pc,
  */
 static enum GNUNET_GenericReturnValue
 help_recoup_refresh (struct CoinHistoryParseContext *pc,
+                     struct TALER_EXCHANGE_CoinHistoryEntry *rh,
                      const struct TALER_Amount *amount,
                      json_t *transaction)
 {
-  /* This is the coin that was subjected to a recoup,
-       the value being credited to the old coin. */
-  struct TALER_CoinSpendPublicKeyP old_coin_pub;
-  union TALER_DenominationBlindingKeyP coin_bks;
-  struct GNUNET_TIME_Timestamp timestamp;
-  struct TALER_ExchangePublicKeyP exchange_pub;
-  struct TALER_ExchangeSignatureP exchange_sig;
-  struct TALER_CoinSpendSignatureP coin_sig;
   struct GNUNET_JSON_Specification spec[] = {
     GNUNET_JSON_spec_fixed_auto ("exchange_sig",
-                                 &exchange_sig),
+                                 &rh->details.recoup_refresh.exchange_sig),
     GNUNET_JSON_spec_fixed_auto ("exchange_pub",
-                                 &exchange_pub),
+                                 &rh->details.recoup_refresh.exchange_pub),
     GNUNET_JSON_spec_fixed_auto ("coin_sig",
-                                 &coin_sig),
+                                 &rh->details.recoup_refresh.coin_sig),
     GNUNET_JSON_spec_fixed_auto ("old_coin_pub",
-                                 &old_coin_pub),
+                                 &rh->details.recoup_refresh.old_coin_pub),
     GNUNET_JSON_spec_fixed_auto ("coin_blind",
-                                 &coin_bks),
+                                 &rh->details.recoup_refresh.coin_bks),
     GNUNET_JSON_spec_timestamp ("timestamp",
-                                &timestamp),
+                                &rh->details.recoup_refresh.timestamp),
     GNUNET_JSON_spec_end ()
   };
 
@@ -498,21 +476,21 @@ help_recoup_refresh (struct CoinHistoryParseContext *pc,
   }
   if (GNUNET_OK !=
       TALER_exchange_online_confirm_recoup_refresh_verify (
-        timestamp,
+        rh->details.recoup_refresh.timestamp,
         amount,
         pc->coin_pub,
-        &old_coin_pub,
-        &exchange_pub,
-        &exchange_sig))
+        &rh->details.recoup_refresh.old_coin_pub,
+        &rh->details.recoup_refresh.exchange_pub,
+        &rh->details.recoup_refresh.exchange_sig))
   {
     GNUNET_break_op (0);
     return GNUNET_SYSERR;
   }
   if (GNUNET_OK !=
       TALER_wallet_recoup_verify (&pc->dk->h_key,
-                                  &coin_bks,
+                                  &rh->details.recoup_refresh.coin_bks,
                                   pc->coin_pub,
-                                  &coin_sig))
+                                  &rh->details.recoup_refresh.coin_sig))
   {
     GNUNET_break_op (0);
     return GNUNET_SYSERR;
@@ -523,6 +501,8 @@ help_recoup_refresh (struct CoinHistoryParseContext *pc,
 
 /**
  * Handle old coin recoup entry in the coin's history.
+ * This is the coin that was credited in a recoup,
+ * the value being credited to the this coin.
  *
  * @param[in,out] pc overall context
  * @param amount main amount of this operation
@@ -532,24 +512,19 @@ help_recoup_refresh (struct CoinHistoryParseContext *pc,
  */
 static enum GNUNET_GenericReturnValue
 help_old_coin_recoup (struct CoinHistoryParseContext *pc,
+                      struct TALER_EXCHANGE_CoinHistoryEntry *rh,
                       const struct TALER_Amount *amount,
                       json_t *transaction)
 {
-  /* This is the coin that was credited in a recoup,
-       the value being credited to the this coin. */
-  struct TALER_ExchangePublicKeyP exchange_pub;
-  struct TALER_ExchangeSignatureP exchange_sig;
-  struct TALER_CoinSpendPublicKeyP new_coin_pub;
-  struct GNUNET_TIME_Timestamp timestamp;
   struct GNUNET_JSON_Specification spec[] = {
     GNUNET_JSON_spec_fixed_auto ("exchange_sig",
-                                 &exchange_sig),
+                                 &rh->details.old_coin_recoup.exchange_sig),
     GNUNET_JSON_spec_fixed_auto ("exchange_pub",
-                                 &exchange_pub),
+                                 &rh->details.old_coin_recoup.exchange_pub),
     GNUNET_JSON_spec_fixed_auto ("coin_pub",
-                                 &new_coin_pub),
+                                 &rh->details.old_coin_recoup.new_coin_pub),
     GNUNET_JSON_spec_timestamp ("timestamp",
-                                &timestamp),
+                                &rh->details.old_coin_recoup.timestamp),
     GNUNET_JSON_spec_end ()
   };
 
@@ -563,12 +538,12 @@ help_old_coin_recoup (struct CoinHistoryParseContext *pc,
   }
   if (GNUNET_OK !=
       TALER_exchange_online_confirm_recoup_refresh_verify (
-        timestamp,
+        rh->details.old_coin_recoup.timestamp,
         amount,
-        &new_coin_pub,
+        &rh->details.old_coin_recoup.new_coin_pub,
         pc->coin_pub,
-        &exchange_pub,
-        &exchange_sig))
+        &rh->details.old_coin_recoup.exchange_pub,
+        &rh->details.old_coin_recoup.exchange_sig))
   {
     GNUNET_break_op (0);
     return GNUNET_SYSERR;
@@ -588,31 +563,23 @@ help_old_coin_recoup (struct CoinHistoryParseContext *pc,
  */
 static enum GNUNET_GenericReturnValue
 help_purse_deposit (struct CoinHistoryParseContext *pc,
+                    struct TALER_EXCHANGE_CoinHistoryEntry *rh,
                     const struct TALER_Amount *amount,
                     json_t *transaction)
 {
-  struct TALER_PurseContractPublicKeyP purse_pub;
-  struct TALER_CoinSpendSignatureP coin_sig;
-  const char *exchange_base_url;
-  bool refunded;
-  struct TALER_AgeCommitmentHash phac = { 0 };
   struct GNUNET_JSON_Specification spec[] = {
     GNUNET_JSON_spec_fixed_auto ("purse_pub",
-                                 &purse_pub),
+                                 &rh->details.purse_deposit.purse_pub),
     GNUNET_JSON_spec_fixed_auto ("coin_sig",
-                                 &coin_sig),
+                                 &rh->details.purse_deposit.coin_sig),
     GNUNET_JSON_spec_mark_optional (
       GNUNET_JSON_spec_fixed_auto ("h_age_commitment",
-                                   &coin_sig),
+                                   &rh->details.purse_deposit.phac),
       NULL),
     GNUNET_JSON_spec_string ("exchange_base_url",
-                             &exchange_base_url),
-    GNUNET_JSON_spec_mark_optional (
-      GNUNET_JSON_spec_fixed_auto ("h_age_commitment",
-                                   &phac),
-      NULL),
+                             &rh->details.purse_deposit.exchange_base_url),
     GNUNET_JSON_spec_bool ("refunded",
-                           &refunded),
+                           &rh->details.purse_deposit.refunded),
     GNUNET_JSON_spec_end ()
   };
 
@@ -626,23 +593,23 @@ help_purse_deposit (struct CoinHistoryParseContext *pc,
   }
   if (GNUNET_OK !=
       TALER_wallet_purse_deposit_verify (
-        exchange_base_url,
-        &purse_pub,
+        rh->details.purse_deposit.exchange_base_url,
+        &rh->details.purse_deposit.purse_pub,
         amount,
         &pc->dk->h_key,
-        &phac,
+        &rh->details.purse_deposit.phac,
         pc->coin_pub,
-        &coin_sig))
+        &rh->details.purse_deposit.coin_sig))
   {
     GNUNET_break_op (0);
     return GNUNET_SYSERR;
   }
-  if (refunded)
+  if (rh->details.purse_deposit.refunded)
   {
     /* We wave the deposit fee. */
     if (0 >
-        TALER_amount_add (&pc->rtotal,
-                          &pc->rtotal,
+        TALER_amount_add (pc->total_in,
+                          pc->total_in,
                           &pc->dk->fees.deposit))
     {
       /* overflow in refund history? inconceivable! Bad exchange! */
@@ -665,22 +632,19 @@ help_purse_deposit (struct CoinHistoryParseContext *pc,
  */
 static enum GNUNET_GenericReturnValue
 help_purse_refund (struct CoinHistoryParseContext *pc,
+                   struct TALER_EXCHANGE_CoinHistoryEntry *rh,
                    const struct TALER_Amount *amount,
                    json_t *transaction)
 {
-  struct TALER_PurseContractPublicKeyP purse_pub;
-  struct TALER_Amount refund_fee;
-  struct TALER_ExchangePublicKeyP exchange_pub;
-  struct TALER_ExchangeSignatureP exchange_sig;
   struct GNUNET_JSON_Specification spec[] = {
     TALER_JSON_spec_amount_any ("refund_fee",
-                                &refund_fee),
+                                &rh->details.purse_refund.refund_fee),
     GNUNET_JSON_spec_fixed_auto ("purse_pub",
-                                 &purse_pub),
+                                 &rh->details.purse_refund.purse_pub),
     GNUNET_JSON_spec_fixed_auto ("exchange_sig",
-                                 &exchange_sig),
+                                 &rh->details.purse_refund.exchange_sig),
     GNUNET_JSON_spec_fixed_auto ("exchange_pub",
-                                 &exchange_pub),
+                                 &rh->details.purse_refund.exchange_pub),
     GNUNET_JSON_spec_end ()
   };
 
@@ -695,20 +659,20 @@ help_purse_refund (struct CoinHistoryParseContext *pc,
   if (GNUNET_OK !=
       TALER_exchange_online_purse_refund_verify (
         amount,
-        &refund_fee,
+        &rh->details.purse_refund.refund_fee,
         pc->coin_pub,
-        &purse_pub,
-        &exchange_pub,
-        &exchange_sig))
+        &rh->details.purse_refund.purse_pub,
+        &rh->details.purse_refund.exchange_pub,
+        &rh->details.purse_refund.exchange_sig))
   {
     GNUNET_break_op (0);
     return GNUNET_SYSERR;
   }
   if ( (GNUNET_YES !=
-        TALER_amount_cmp_currency (&refund_fee,
+        TALER_amount_cmp_currency (&rh->details.purse_refund.refund_fee,
                                    &pc->dk->fees.refund)) ||
        (0 !=
-        TALER_amount_cmp (&refund_fee,
+        TALER_amount_cmp (&rh->details.purse_refund.refund_fee,
                           &pc->dk->fees.refund)) )
   {
     GNUNET_break_op (0);
@@ -729,16 +693,15 @@ help_purse_refund (struct CoinHistoryParseContext *pc,
  */
 static enum GNUNET_GenericReturnValue
 help_reserve_open_deposit (struct CoinHistoryParseContext *pc,
+                           struct TALER_EXCHANGE_CoinHistoryEntry *rh,
                            const struct TALER_Amount *amount,
                            json_t *transaction)
 {
-  struct TALER_ReserveSignatureP reserve_sig;
-  struct TALER_CoinSpendSignatureP coin_sig;
   struct GNUNET_JSON_Specification spec[] = {
     GNUNET_JSON_spec_fixed_auto ("reserve_sig",
-                                 &reserve_sig),
+                                 
&rh->details.reserve_open_deposit.reserve_sig),
     GNUNET_JSON_spec_fixed_auto ("coin_sig",
-                                 &coin_sig),
+                                 &rh->details.reserve_open_deposit.coin_sig),
     GNUNET_JSON_spec_end ()
   };
 
@@ -753,9 +716,9 @@ help_reserve_open_deposit (struct CoinHistoryParseContext 
*pc,
   if (GNUNET_OK !=
       TALER_wallet_reserve_open_deposit_verify (
         amount,
-        &reserve_sig,
+        &rh->details.reserve_open_deposit.reserve_sig,
         pc->coin_pub,
-        &coin_sig))
+        &rh->details.reserve_open_deposit.coin_sig))
   {
     GNUNET_break_op (0);
     return GNUNET_SYSERR;
@@ -764,47 +727,57 @@ help_reserve_open_deposit (struct CoinHistoryParseContext 
*pc,
 }
 
 
-/**
- * Convenience function.  Verifies a coin's transaction history as
- * returned by the exchange.
- *
- * FIXME: add support for partial histories!
- * NOTE: this API will thus still change!
- *
- * @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] 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
- */
-static enum GNUNET_GenericReturnValue
-verify_coin_history (
+enum GNUNET_GenericReturnValue
+TALER_EXCHANGE_parse_coin_history (
+  const struct TALER_EXCHANGE_Keys *keys,
   const struct TALER_EXCHANGE_DenomPublicKey *dk,
-  const struct TALER_CoinSpendPublicKeyP *coin_pub,
   const json_t *history,
-  struct TALER_Amount *total)
+  const struct TALER_CoinSpendPublicKeyP *coin_pub,
+  struct TALER_Amount *total_in,
+  struct TALER_Amount *total_out,
+  unsigned int rlen,
+  struct TALER_EXCHANGE_CoinHistoryEntry rhistory[static rlen])
 {
-  const char *currency = dk->value.currency;
   const struct
   {
     const char *type;
     CoinCheckHelper helper;
+    enum TALER_EXCHANGE_CoinTransactionType ctt;
   } map[] = {
-    { "DEPOSIT", &help_deposit },
-    { "MELT", &help_melt },
-    { "REFUND", &help_refund },
-    { "RECOUP", &help_recoup },
-    { "RECOUP-REFRESH", &help_recoup_refresh },
-    { "OLD-COIN-RECOUP", &help_old_coin_recoup },
-    { "PURSE-DEPOSIT", &help_purse_deposit },
-    { "PURSE-REFUND", &help_purse_refund },
-    { "RESERVE-OPEN-DEPOSIT", &help_reserve_open_deposit },
-    { NULL, NULL }
+    { "DEPOSIT",
+      &help_deposit,
+      TALER_EXCHANGE_CTT_DEPOSIT },
+    { "MELT",
+      &help_melt,
+      TALER_EXCHANGE_CTT_MELT },
+    { "REFUND",
+      &help_refund,
+      TALER_EXCHANGE_CTT_REFUND },
+    { "RECOUP",
+      &help_recoup,
+      TALER_EXCHANGE_CTT_RECOUP },
+    { "RECOUP-REFRESH",
+      &help_recoup_refresh,
+      TALER_EXCHANGE_CTT_RECOUP_REFRESH },
+    { "OLD-COIN-RECOUP",
+      &help_old_coin_recoup,
+      TALER_EXCHANGE_CTT_OLD_COIN_RECOUP },
+    { "PURSE-DEPOSIT",
+      &help_purse_deposit,
+      TALER_EXCHANGE_CTT_PURSE_DEPOSIT },
+    { "PURSE-REFUND",
+      &help_purse_refund,
+      TALER_EXCHANGE_CTT_PURSE_REFUND },
+    { "RESERVE-OPEN-DEPOSIT",
+      &help_reserve_open_deposit,
+      TALER_EXCHANGE_CTT_RESERVE_OPEN_DEPOSIT },
+    { NULL, NULL, TALER_EXCHANGE_CTT_NONE }
   };
   struct CoinHistoryParseContext pc = {
     .dk = dk,
     .coin_pub = coin_pub,
-    .total = total
+    .total_out = total_out,
+    .total_in = total_in
   };
   size_t len;
 
@@ -819,16 +792,16 @@ verify_coin_history (
     GNUNET_break_op (0);
     return GNUNET_SYSERR;
   }
+  *total_in = dk->value;
   GNUNET_assert (GNUNET_OK ==
-                 TALER_amount_set_zero (currency,
-                                        total));
-  GNUNET_assert (GNUNET_OK ==
-                 TALER_amount_set_zero (currency,
-                                        &pc.rtotal));
+                 TALER_amount_set_zero (total_in->currency,
+                                        total_out));
   for (size_t off = 0; off<len; off++)
   {
+    struct TALER_EXCHANGE_CoinHistoryEntry *rh = &rhistory[off];
+    json_t *transaction = json_array_get (history,
+                                          off);
     enum GNUNET_GenericReturnValue add;
-    json_t *transaction;
     struct TALER_Amount amount;
     const char *type;
     struct GNUNET_JSON_Specification spec_glob[] = {
@@ -839,8 +812,6 @@ verify_coin_history (
       GNUNET_JSON_spec_end ()
     };
 
-    transaction = json_array_get (history,
-                                  off);
     if (GNUNET_OK !=
         GNUNET_JSON_parse (transaction,
                            spec_glob,
@@ -851,7 +822,7 @@ verify_coin_history (
     }
     if (GNUNET_YES !=
         TALER_amount_cmp_currency (&amount,
-                                   &pc.rtotal))
+                                   total_in))
     {
       GNUNET_break_op (0);
       return GNUNET_SYSERR;
@@ -866,7 +837,9 @@ verify_coin_history (
       if (0 == strcasecmp (type,
                            map[i].type))
       {
+        rh->type = map[i].ctt;
         add = map[i].helper (&pc,
+                             rh,
                              &amount,
                              transaction);
         break;
@@ -876,16 +849,17 @@ verify_coin_history (
     {
     case GNUNET_SYSERR:
       /* entry type not supported, new version on server? */
+      rh->type = TALER_EXCHANGE_CTT_NONE;
       GNUNET_break_op (0);
       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                   "Unexpected type `%s' in response\n",
                   type);
       return GNUNET_SYSERR;
     case GNUNET_YES:
-      /* This amount should be added to the total */
+      /* This amount should be debited from the coin */
       if (0 >
-          TALER_amount_add (total,
-                            total,
+          TALER_amount_add (total_out,
+                            total_out,
                             &amount))
       {
         /* overflow in history already!? inconceivable! Bad exchange! */
@@ -894,15 +868,10 @@ verify_coin_history (
       }
       break;
     case GNUNET_NO:
-      /* This amount should be subtracted from the total.
-
-         However, for the implementation, we first *add* up all of
-         these negative amounts, as we might get refunds before
-         deposits from a semi-evil exchange.  Then, at the end, we do
-         the subtraction by calculating "total = total - rtotal" */
+      /* This amount should be credited to the coin. */
       if (0 >
-          TALER_amount_add (&pc.rtotal,
-                            &pc.rtotal,
+          TALER_amount_add (total_in,
+                            total_in,
                             &amount))
       {
         /* overflow in refund history? inconceivable! Bad exchange! */
@@ -912,110 +881,6 @@ verify_coin_history (
       break;
     } /* end of switch(add) */
   }
-  /* Finally, subtract 'rtotal' from total to handle the subtractions */
-  if (0 >
-      TALER_amount_subtract (total,
-                             total,
-                             &pc.rtotal))
-  {
-    /* underflow in history? inconceivable! Bad exchange! */
-    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
- */
-// FIXME: should be used...
-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;
-}
-
-
-/**
- * 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 required balance required on the coin for the operation
- * @return #GNUNET_OK if @a proof holds
- */
-// FIXME: should be used!
-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_Amount *required)
-{
-  enum TALER_ErrorCode ec;
-
-  ec = TALER_JSON_get_error_code (proof);
-  switch (ec)
-  {
-  case TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS:
-    /* Nothing to check anymore here, proof needs to be
-       checked in the GET /coins/$COIN_PUB handler */
-    break;
-  case TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_DENOMINATION_KEY:
-    // FIXME: write check!
-    break;
-  default:
-    GNUNET_break_op (0);
-    return GNUNET_SYSERR;
-  }
   return GNUNET_OK;
 }
 
@@ -1032,8 +897,6 @@ static enum GNUNET_GenericReturnValue
 handle_coins_history_ok (struct TALER_EXCHANGE_CoinsHistoryHandle *rsh,
                          const json_t *j)
 {
-  const json_t *history;
-  unsigned int len;
   struct TALER_EXCHANGE_CoinHistory rs = {
     .hr.reply = j,
     .hr.http_status = MHD_HTTP_OK
@@ -1041,8 +904,10 @@ handle_coins_history_ok (struct 
TALER_EXCHANGE_CoinsHistoryHandle *rsh,
   struct GNUNET_JSON_Specification spec[] = {
     TALER_JSON_spec_amount_any ("balance",
                                 &rs.details.ok.balance),
+    GNUNET_JSON_spec_fixed_auto ("h_denom_pub",
+                                 &rs.details.ok.h_denom_pub),
     GNUNET_JSON_spec_array_const ("history",
-                                  &history),
+                                  &rs.details.ok.history),
     GNUNET_JSON_spec_end ()
   };
 
@@ -1055,39 +920,13 @@ handle_coins_history_ok (struct 
TALER_EXCHANGE_CoinsHistoryHandle *rsh,
     GNUNET_break_op (0);
     return GNUNET_SYSERR;
   }
-  len = json_array_size (history);
+  if (NULL != rsh->cb)
   {
-    struct TALER_EXCHANGE_CoinHistoryEntry *rhistory;
-
-    rhistory = GNUNET_new_array (len,
-                                 struct TALER_EXCHANGE_CoinHistoryEntry);
-    if (GNUNET_OK !=
-        parse_coin_history (rsh->keys,
-                            history,
-                            &rsh->coin_pub,
-                            rs.details.ok.balance.currency,
-                            &rs.details.ok.total_in,
-                            &rs.details.ok.total_out,
-                            len,
-                            rhistory))
-    {
-      GNUNET_break_op (0);
-      free_coin_history (len,
-                         rhistory);
-      GNUNET_JSON_parse_free (spec);
-      return GNUNET_SYSERR;
-    }
-    if (NULL != rsh->cb)
-    {
-      rs.details.ok.history = rhistory;
-      rs.details.ok.history_len = len;
-      rsh->cb (rsh->cb_cls,
-               &rs);
-      rsh->cb = NULL;
-    }
-    free_coin_history (len,
-                       rhistory);
+    rsh->cb (rsh->cb_cls,
+             &rs);
+    rsh->cb = NULL;
   }
+  GNUNET_JSON_parse_free (spec);
   return GNUNET_OK;
 }
 
@@ -1178,15 +1017,14 @@ struct TALER_EXCHANGE_CoinsHistoryHandle *
 TALER_EXCHANGE_coins_history (
   struct GNUNET_CURL_Context *ctx,
   const char *url,
-  struct TALER_EXCHANGE_Keys *keys,
-  const struct TALER_CoinPrivateKeyP *coin_priv,
+  const struct TALER_CoinSpendPrivateKeyP *coin_priv,
   uint64_t start_off,
   TALER_EXCHANGE_CoinsHistoryCallback cb,
   void *cb_cls)
 {
   struct TALER_EXCHANGE_CoinsHistoryHandle *rsh;
   CURL *eh;
-  char arg_str[sizeof (struct TALER_CoinPublicKeyP) * 2 + 64];
+  char arg_str[sizeof (struct TALER_CoinSpendPublicKeyP) * 2 + 64];
   struct curl_slist *job_headers;
 
   rsh = GNUNET_new (struct TALER_EXCHANGE_CoinsHistoryHandle);
@@ -1195,7 +1033,7 @@ TALER_EXCHANGE_coins_history (
   GNUNET_CRYPTO_eddsa_key_get_public (&coin_priv->eddsa_priv,
                                       &rsh->coin_pub.eddsa_pub);
   {
-    char pub_str[sizeof (struct TALER_CoinPublicKeyP) * 2];
+    char pub_str[sizeof (struct TALER_CoinSpendPublicKeyP) * 2];
     char *end;
 
     end = GNUNET_STRINGS_data_to_string (
@@ -1234,7 +1072,7 @@ TALER_EXCHANGE_coins_history (
   }
 
   {
-    struct TALER_CoinSignatureP coin_sig;
+    struct TALER_CoinSpendSignatureP coin_sig;
     char *sig_hdr;
     char *hdr;
 
@@ -1260,7 +1098,6 @@ TALER_EXCHANGE_coins_history (
     }
   }
 
-  rsh->keys = TALER_EXCHANGE_keys_incref (keys);
   rsh->job = GNUNET_CURL_job_add2 (ctx,
                                    eh,
                                    job_headers,
@@ -1282,9 +1119,103 @@ TALER_EXCHANGE_coins_history_cancel (
   }
   TALER_curl_easy_post_finished (&rsh->post_ctx);
   GNUNET_free (rsh->url);
-  TALER_EXCHANGE_keys_decref (rsh->keys);
   GNUNET_free (rsh);
 }
 
 
+/**
+ * Verify that @a coin_sig does NOT appear in the @a history of a coin's
+ * transactions and thus whatever transaction is authorized by @a coin_sig is
+ * a conflict with @a proof.
+ *
+ * @param history coin history to check
+ * @param coin_sig signature that must not be in @a history
+ * @return #GNUNET_OK if @a coin_sig is not in @a history
+ */
+enum GNUNET_GenericReturnValue
+TALER_EXCHANGE_check_coin_signature_conflict (
+  const json_t *history,
+  const struct TALER_CoinSpendSignatureP *coin_sig)
+{
+  size_t off;
+  json_t *entry;
+
+  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;
+}
+
+
+#if FIXME_IMPLEMENT
+/**
+ * FIXME-Oec: we need some specific routines that show
+ * that certain coin operations are indeed in conflict,
+ * for example that the coin is of a different denomination
+ * or different age restrictions.
+ * This relates to unimplemented error handling for
+ * coins in the exchange!
+ *
+ * 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 required balance required on the coin for the operation
+ * @return #GNUNET_OK if @a proof holds
+ */
+// FIXME: should be properly defined and implemented!
+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_Amount *required)
+{
+  enum TALER_ErrorCode ec;
+
+  ec = TALER_JSON_get_error_code (proof);
+  switch (ec)
+  {
+  case TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS:
+    /* Nothing to check anymore here, proof needs to be
+       checked in the GET /coins/$COIN_PUB handler */
+    break;
+  case TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_DENOMINATION_KEY:
+    // FIXME: write check!
+    break;
+  default:
+    GNUNET_break_op (0);
+    return GNUNET_SYSERR;
+  }
+  return GNUNET_OK;
+}
+
+
+#endif
+
+
 /* end of exchange_api_coins_history.c */

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