gnunet-svn
[Top][All Lists]
Advanced

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

[taler-exchange] 04/04: return new global fees from /keys


From: gnunet
Subject: [taler-exchange] 04/04: return new global fees from /keys
Date: Sun, 20 Mar 2022 09:44:56 +0100

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

grothoff pushed a commit to branch master
in repository exchange.

commit dee45bf02284716d5dea18e94193d74e64f7e5bf
Author: Christian Grothoff <grothoff@gnunet.org>
AuthorDate: Sun Mar 20 09:44:42 2022 +0100

    return new global fees from /keys
---
 src/exchange/taler-exchange-httpd_keys.c           | 140 +++++++-
 .../taler-exchange-httpd_management_extensions.c   |   6 +-
 .../taler-exchange-httpd_management_global_fees.c  |   3 +-
 src/exchangedb/plugin_exchangedb_postgres.c        | 174 +++++++++-
 src/include/taler_exchange_service.h               |  59 ++++
 src/include/taler_exchangedb_plugin.h              |   2 +-
 src/include/taler_json_lib.h                       |  24 ++
 src/lib/exchange_api_handle.c                      | 111 ++++++-
 src/lib/exchange_api_reserves_history.c            | 368 +++++++++++++++++++++
 src/lib/exchange_api_reserves_status.c             | 364 ++++++++++++++++++++
 10 files changed, 1231 insertions(+), 20 deletions(-)

diff --git a/src/exchange/taler-exchange-httpd_keys.c 
b/src/exchange/taler-exchange-httpd_keys.c
index 95278ab8..1012a8c0 100644
--- a/src/exchange/taler-exchange-httpd_keys.c
+++ b/src/exchange/taler-exchange-httpd_keys.c
@@ -57,7 +57,7 @@
  * #TALER_PROTOCOL_CURRENT and #TALER_PROTOCOL_AGE in
  * exchange_api_handle.c!
  */
-#define EXCHANGE_PROTOCOL_VERSION "12:0:0"
+#define EXCHANGE_PROTOCOL_VERSION "13:0:1"
 
 
 /**
@@ -280,6 +280,31 @@ struct SigningKey
 };
 
 
+/**
+ * Set of global fees (and options) for a time range.
+ */
+struct GlobalFee
+{
+  /**
+   * Kept in a DLL.
+   */
+  struct GlobalFee *next;
+
+  /**
+   * Kept in a DLL.
+   */
+  struct GlobalFee *prev;
+
+  struct GNUNET_TIME_Timestamp start_date;
+  struct GNUNET_TIME_Timestamp end_date;
+  struct GNUNET_TIME_Relative purse_timeout;
+  struct GNUNET_TIME_Relative kyc_timeout;
+  struct GNUNET_TIME_Relative history_expiration;
+  struct TALER_MasterSignatureP master_sig;
+  struct TALER_GlobalFeeSet fees;
+  uint32_t purse_account_limit;
+};
+
 struct TEH_KeyStateHandle
 {
 
@@ -296,12 +321,28 @@ struct TEH_KeyStateHandle
    */
   struct GNUNET_CONTAINER_MultiPeerMap *signkey_map;
 
+  /**
+   * Head of DLL of our global fees.
+   */
+  struct GlobalFee *gf_head;
+
+  /**
+   * Tail of DLL of our global fees.
+   */
+  struct GlobalFee *gf_tail;
+
   /**
    * json array with the auditors of this exchange. Contains exactly
    * the information needed for the "auditors" field of the /keys response.
    */
   json_t *auditors;
 
+  /**
+   * json array with the global fees of this exchange. Contains exactly
+   * the information needed for the "global_fees" field of the /keys response.
+   */
+  json_t *global_fees;
+
   /**
    * Sorted array of responses to /keys (MUST be sorted by cherry-picking 
date) of
    * length @e krd_array_length;
@@ -548,7 +589,7 @@ suspend_request (struct MHD_Connection *connection)
  * @param value a `struct TEH_DenominationKey`
  * @return #GNUNET_OK
  */
-static int
+static enum GNUNET_GenericReturnValue
 check_dk (void *cls,
           const struct GNUNET_HashCode *hc,
           void *value)
@@ -1174,7 +1215,16 @@ static void
 destroy_key_state (struct TEH_KeyStateHandle *ksh,
                    bool free_helper)
 {
+  struct GlobalFee *gf;
+
   clear_response_cache (ksh);
+  while (NULL != (gf = ksh->gf_head))
+  {
+    GNUNET_CONTAINER_DLL_remove (ksh->gf_head,
+                                 ksh->gf_tail,
+                                 gf);
+    GNUNET_free (gf);
+  }
   GNUNET_CONTAINER_multihashmap_iterate (ksh->denomkey_map,
                                          &clear_denomination_cb,
                                          ksh);
@@ -1185,6 +1235,8 @@ destroy_key_state (struct TEH_KeyStateHandle *ksh,
   GNUNET_CONTAINER_multipeermap_destroy (ksh->signkey_map);
   json_decref (ksh->auditors);
   ksh->auditors = NULL;
+  json_decref (ksh->global_fees);
+  ksh->global_fees = NULL;
   if (free_helper)
   {
     destroy_key_helpers (ksh->helpers);
@@ -1817,6 +1869,8 @@ create_krd (struct TEH_KeyStateHandle *ksh,
                                    denoms),
     GNUNET_JSON_pack_array_incref ("auditors",
                                    ksh->auditors),
+    GNUNET_JSON_pack_array_incref ("global_fees",
+                                   ksh->global_fees),
     GNUNET_JSON_pack_timestamp ("list_issue_date",
                                 last_cpd),
     GNUNET_JSON_pack_data_auto ("eddsa_pub",
@@ -1825,7 +1879,7 @@ create_krd (struct TEH_KeyStateHandle *ksh,
                                 &exchange_sig));
   GNUNET_assert (NULL != keys);
 
-  // Set wallet limit if KYC is configured
+  /* Set wallet limit if KYC is configured */
   if ( (TEH_KYC_NONE != TEH_kyc_config.mode) &&
        (GNUNET_OK ==
         TALER_amount_is_valid (&TEH_kyc_config.wallet_balance_limit)) )
@@ -1839,7 +1893,7 @@ create_krd (struct TEH_KeyStateHandle *ksh,
           &TEH_kyc_config.wallet_balance_limit)));
   }
 
-  // Signal support for the configured, enabled extensions.
+  /* Signal support for the configured, enabled extensions. */
   {
     json_t *extensions = json_object ();
     bool has_extensions = false;
@@ -2201,6 +2255,70 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh)
 }
 
 
+/**
+ * Called with information about global fees.
+ *
+ * @param cls `struct TEH_KeyStateHandle *` we are building
+ * @param fees the global fees we charge
+ * @param purse_timeout when do purses time out
+ * @param kyc_timeout when do reserves without KYC time out
+ * @param history_expiration how long are account histories preserved
+ * @param purse_account_limit how many purses are free per account
+ * @param start_date from when are these fees valid (start date)
+ * @param end_date until when are these fees valid (end date, exclusive)
+ * @param master_sig master key signature affirming that this is the correct
+ *                   fee (of purpose #TALER_SIGNATURE_MASTER_GLOBAL_FEES)
+ */
+static void
+global_fee_info_cb (
+  void *cls,
+  const struct TALER_GlobalFeeSet *fees,
+  struct GNUNET_TIME_Relative purse_timeout,
+  struct GNUNET_TIME_Relative kyc_timeout,
+  struct GNUNET_TIME_Relative history_expiration,
+  uint32_t purse_account_limit,
+  struct GNUNET_TIME_Timestamp start_date,
+  struct GNUNET_TIME_Timestamp end_date,
+  const struct TALER_MasterSignatureP *master_sig)
+{
+  struct TEH_KeyStateHandle *ksh = cls;
+  struct GlobalFee *gf;
+
+  gf = GNUNET_new (struct GlobalFee);
+  gf->start_date = start_date;
+  gf->end_date = end_date;
+  gf->fees = *fees;
+  gf->purse_timeout = purse_timeout;
+  gf->kyc_timeout = kyc_timeout;
+  gf->history_expiration = history_expiration;
+  gf->purse_account_limit = purse_account_limit;
+  gf->master_sig = *master_sig;
+  GNUNET_CONTAINER_DLL_insert (ksh->gf_head,
+                               ksh->gf_tail,
+                               gf);
+  GNUNET_assert (
+    0 ==
+    json_array_append_new (
+      ksh->global_fees,
+      GNUNET_JSON_PACK (
+        GNUNET_JSON_pack_timestamp ("start_date",
+                                    start_date),
+        GNUNET_JSON_pack_timestamp ("end_date",
+                                    end_date),
+        TALER_JSON_PACK_GLOBAL_FEES (fees),
+        GNUNET_JSON_pack_time_rel ("history_expiration",
+                                   history_expiration),
+        GNUNET_JSON_pack_time_rel ("account_kyc_timeout",
+                                   kyc_timeout),
+        GNUNET_JSON_pack_time_rel ("purse_timeout",
+                                   purse_timeout),
+        GNUNET_JSON_pack_uint64 ("purse_account_limit",
+                                 purse_account_limit),
+        GNUNET_JSON_pack_data_auto ("master_sig",
+                                    master_sig))));
+}
+
+
 /**
  * Create a key state.
  *
@@ -2246,6 +2364,20 @@ build_key_state (struct HelperState *hs,
   /* NOTE: fetches master-signed signkeys, but ALSO those that were revoked! */
   GNUNET_break (GNUNET_OK ==
                 TEH_plugin->preflight (TEH_plugin->cls));
+  if (NULL != ksh->global_fees)
+    json_decref (ksh->global_fees);
+  ksh->global_fees = json_array ();
+  qs = TEH_plugin->get_global_fees (TEH_plugin->cls,
+                                    &global_fee_info_cb,
+                                    ksh);
+  if (qs < 0)
+  {
+    GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs);
+    GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR != qs);
+    destroy_key_state (ksh,
+                       true);
+    return NULL;
+  }
   qs = TEH_plugin->iterate_denominations (TEH_plugin->cls,
                                           &denomination_info_cb,
                                           ksh);
diff --git a/src/exchange/taler-exchange-httpd_management_extensions.c 
b/src/exchange/taler-exchange-httpd_management_extensions.c
index 109e863d..ce151e2e 100644
--- a/src/exchange/taler-exchange-httpd_management_extensions.c
+++ b/src/exchange/taler-exchange-httpd_management_extensions.c
@@ -126,6 +126,11 @@ set_extensions (void *cls,
         .size = htons (sizeof (ev)),
         .type = htons (TALER_DBEVENT_EXCHANGE_EXTENSIONS_UPDATED)
       };
+
+      // FIXME-Oec: bug: convert type to NBO first!
+      // FIXME-Oec: bug: sizeof enum is ill-defined...
+      // FIXME-Oec: bug: don't see /keys listening to the event
+      // FIXME-Oec: why is   TEH_keys_update_states (); not enough?
       TEH_plugin->event_notify (TEH_plugin->cls,
                                 &ev,
                                 type,
@@ -294,7 +299,6 @@ TEH_handler_management_post_extensions (
     NULL,
     0);
 
-
 CLEANUP:
   for (unsigned int i = 0; i < sec.num_extensions; i++)
   {
diff --git a/src/exchange/taler-exchange-httpd_management_global_fees.c 
b/src/exchange/taler-exchange-httpd_management_global_fees.c
index eb3aa599..37bb40d9 100644
--- a/src/exchange/taler-exchange-httpd_management_global_fees.c
+++ b/src/exchange/taler-exchange-httpd_management_global_fees.c
@@ -27,6 +27,7 @@
 #include "taler_json_lib.h"
 #include "taler_mhd_lib.h"
 #include "taler_signatures.h"
+#include "taler-exchange-httpd_keys.h"
 #include "taler-exchange-httpd_management.h"
 #include "taler-exchange-httpd_responses.h"
 
@@ -256,7 +257,7 @@ TEH_handler_management_post_global_fees (
     if (GNUNET_SYSERR == res)
       return ret;
   }
-  //  TEH_global_update_state (); // FIXME: trigger!
+  TEH_keys_update_states ();
   return TALER_MHD_reply_static (
     connection,
     MHD_HTTP_NO_CONTENT,
diff --git a/src/exchangedb/plugin_exchangedb_postgres.c 
b/src/exchangedb/plugin_exchangedb_postgres.c
index 2a955e15..bb6f46f5 100644
--- a/src/exchangedb/plugin_exchangedb_postgres.c
+++ b/src/exchangedb/plugin_exchangedb_postgres.c
@@ -1385,6 +1385,28 @@ prepare_statements (struct PostgresClosure *pg)
       " WHERE start_date <= $1"
       "   AND end_date > $1;",
       1),
+    /* Used in #postgres_get_global_fees() */
+    GNUNET_PQ_make_prepare (
+      "get_global_fees",
+      "SELECT "
+      " start_date"
+      ",end_date"
+      ",history_fee_val"
+      ",history_fee_frac"
+      ",kyc_fee_val"
+      ",kyc_fee_frac"
+      ",account_fee_val"
+      ",account_fee_frac"
+      ",purse_fee_val"
+      ",purse_fee_frac"
+      ",purse_timeout"
+      ",kyc_timeout"
+      ",history_expiration"
+      ",purse_account_limit"
+      ",master_sig"
+      " FROM global_fee"
+      " WHERE start_date >= $1",
+      1),
     /* Used in #postgres_insert_wire_fee */
     GNUNET_PQ_make_prepare (
       "insert_wire_fee",
@@ -7818,6 +7840,142 @@ postgres_get_global_fee (void *cls,
 }
 
 
+/**
+ * Closure for #global_fees_cb().
+ */
+struct GlobalFeeContext
+{
+  /**
+   * Function to call for each global fee block.
+   */
+  TALER_EXCHANGEDB_GlobalFeeCallback cb;
+
+  /**
+   * Closure to give to @e rec.
+   */
+  void *cb_cls;
+
+  /**
+   * Plugin context.
+   */
+  struct PostgresClosure *pg;
+
+  /**
+   * Set to #GNUNET_SYSERR on error.
+   */
+  enum GNUNET_GenericReturnValue status;
+};
+
+
+/**
+ * Function to be called with the results of a SELECT statement
+ * that has returned @a num_results results.
+ *
+ * @param cls closure
+ * @param result the postgres result
+ * @param num_results the number of results in @a result
+ */
+static void
+global_fees_cb (void *cls,
+                PGresult *result,
+                unsigned int num_results)
+{
+  struct GlobalFeeContext *gctx = cls;
+  struct PostgresClosure *pg = gctx->pg;
+
+  for (unsigned int i = 0; i<num_results; i++)
+  {
+    struct TALER_GlobalFeeSet fees;
+    struct GNUNET_TIME_Relative purse_timeout;
+    struct GNUNET_TIME_Relative kyc_timeout;
+    struct GNUNET_TIME_Relative history_expiration;
+    uint32_t purse_account_limit;
+    struct GNUNET_TIME_Timestamp start_date;
+    struct GNUNET_TIME_Timestamp end_date;
+    struct TALER_MasterSignatureP master_sig;
+    struct GNUNET_PQ_ResultSpec rs[] = {
+      GNUNET_PQ_result_spec_timestamp ("start_date",
+                                       &start_date),
+      GNUNET_PQ_result_spec_timestamp ("end_date",
+                                       &end_date),
+      TALER_PQ_RESULT_SPEC_AMOUNT ("history_fee",
+                                   &fees.history),
+      TALER_PQ_RESULT_SPEC_AMOUNT ("kyc_fee",
+                                   &fees.kyc),
+      TALER_PQ_RESULT_SPEC_AMOUNT ("account_fee",
+                                   &fees.account),
+      TALER_PQ_RESULT_SPEC_AMOUNT ("purse_fee",
+                                   &fees.purse),
+      GNUNET_PQ_result_spec_relative_time ("purse_timeout",
+                                           &purse_timeout),
+      GNUNET_PQ_result_spec_relative_time ("kyc_timeout",
+                                           &kyc_timeout),
+      GNUNET_PQ_result_spec_relative_time ("history_expiration",
+                                           &history_expiration),
+      GNUNET_PQ_result_spec_uint32 ("purse_account_limit",
+                                    &purse_account_limit),
+      GNUNET_PQ_result_spec_auto_from_type ("master_sig",
+                                            &master_sig),
+      GNUNET_PQ_result_spec_end
+    };
+    if (GNUNET_OK !=
+        GNUNET_PQ_extract_result (result,
+                                  rs,
+                                  i))
+    {
+      GNUNET_break (0);
+      gctx->status = GNUNET_SYSERR;
+      break;
+    }
+    gctx->cb (gctx->cb_cls,
+              &fees,
+              purse_timeout,
+              kyc_timeout,
+              history_expiration,
+              purse_account_limit,
+              start_date,
+              end_date,
+              &master_sig);
+    GNUNET_PQ_cleanup_result (rs);
+  }
+}
+
+
+/**
+ * Obtain global fees from database.
+ *
+ * @param cls closure
+ * @param cb function to call on each fee entry
+ * @param cb_cls closure for @a cb
+ * @return status of the transaction
+ */
+static enum GNUNET_DB_QueryStatus
+postgres_get_global_fees (void *cls,
+                          TALER_EXCHANGEDB_GlobalFeeCallback cb,
+                          void *cb_cls)
+{
+  struct PostgresClosure *pg = cls;
+  struct GNUNET_TIME_Timestamp date
+    = GNUNET_TIME_timestamp_get ();
+  struct GNUNET_PQ_QueryParam params[] = {
+    GNUNET_PQ_query_param_timestamp (&date),
+    GNUNET_PQ_query_param_end
+  };
+  struct GlobalFeeContext gctx = {
+    .cb = cb,
+    .cb_cls = cb_cls,
+    .pg = pg,
+    .status = GNUNET_OK
+  };
+
+  return GNUNET_PQ_eval_prepared_multi_select (pg->conn,
+                                               "get_global_fees",
+                                               params,
+                                               &global_fees_cb,
+                                               &gctx);
+}
+
+
 /**
  * Insert wire transfer fee into database.
  *
@@ -8034,7 +8192,7 @@ struct ExpiredReserveContext
   /**
    * Set to #GNUNET_SYSERR on error.
    */
-  int status;
+  enum GNUNET_GenericReturnValue status;
 };
 
 
@@ -8053,7 +8211,7 @@ reserve_expired_cb (void *cls,
 {
   struct ExpiredReserveContext *erc = cls;
   struct PostgresClosure *pg = erc->pg;
-  int ret;
+  enum GNUNET_GenericReturnValue ret;
 
   ret = GNUNET_OK;
   for (unsigned int i = 0; i<num_results; i++)
@@ -8117,13 +8275,14 @@ postgres_get_expired_reserves (void *cls,
     GNUNET_PQ_query_param_timestamp (&now),
     GNUNET_PQ_query_param_end
   };
-  struct ExpiredReserveContext ectx;
+  struct ExpiredReserveContext ectx = {
+    .rec = rec,
+    .rec_cls = rec_cls,
+    .pg = pg,
+    .status = GNUNET_OK
+  };
   enum GNUNET_DB_QueryStatus qs;
 
-  ectx.rec = rec;
-  ectx.rec_cls = rec_cls;
-  ectx.pg = pg;
-  ectx.status = GNUNET_OK;
   qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
                                              "get_expired_reserves",
                                              params,
@@ -12371,6 +12530,7 @@ libtaler_plugin_exchangedb_postgres_init (void *cls)
   plugin->insert_global_fee = &postgres_insert_global_fee;
   plugin->get_wire_fee = &postgres_get_wire_fee;
   plugin->get_global_fee = &postgres_get_global_fee;
+  plugin->get_global_fees = &postgres_get_global_fees;
   plugin->get_expired_reserves = &postgres_get_expired_reserves;
   plugin->insert_reserve_closed = &postgres_insert_reserve_closed;
   plugin->wire_prepare_data_insert = &postgres_wire_prepare_data_insert;
diff --git a/src/include/taler_exchange_service.h 
b/src/include/taler_exchange_service.h
index 48272a6b..56940669 100644
--- a/src/include/taler_exchange_service.h
+++ b/src/include/taler_exchange_service.h
@@ -203,6 +203,55 @@ struct TALER_EXCHANGE_AuditorInformation
 };
 
 
+/**
+ * Global fees and options of an exchange for a given time period.
+ */
+struct TALER_EXCHANGE_GlobalFee
+{
+
+  /**
+   * Signature affirming all of the data.
+   */
+  struct TALER_MasterSignatureP master_sig;
+
+  /**
+   * Starting time of the validity period (inclusive).
+   */
+  struct GNUNET_TIME_Timestamp start_date;
+
+  /**
+   * End time of the validity period (exclusive).
+   */
+  struct GNUNET_TIME_Timestamp end_date;
+
+  /**
+   * Unmerged purses will be timed out after at most this time.
+   */
+  struct GNUNET_TIME_Relative purse_timeout;
+
+  /**
+   * Accounts without KYC will be closed after this time.
+   */
+  struct GNUNET_TIME_Relative kyc_timeout;
+
+  /**
+   * Account history is limited to this timeframe.
+   */
+  struct GNUNET_TIME_Relative history_expiration;
+
+  /**
+   * Fees that apply globally, independent of denomination
+   * and wire method.
+   */
+  struct TALER_GlobalFeeSet fees;
+
+  /**
+   * Number of free purses per account.
+   */
+  uint32_t purse_account_limit;
+};
+
+
 /**
  * @brief Information about keys from the exchange.
  */
@@ -229,6 +278,11 @@ struct TALER_EXCHANGE_Keys
    */
   struct TALER_EXCHANGE_AuditorInformation *auditors;
 
+  /**
+   * Array with the global fees of the exchange.
+   */
+  struct TALER_EXCHANGE_GlobalFee *global_fees;
+
   /**
    * Supported Taler protocol version by the exchange.
    * String in the format current:revision:age using the
@@ -272,6 +326,11 @@ struct TALER_EXCHANGE_Keys
    */
   struct TALER_AgeMask age_mask;
 
+  /**
+   * Length of the @e global_fees array.
+   */
+  unsigned int num_global_fees;
+
   /**
    * Length of the @e sign_keys array (number of valid entries).
    */
diff --git a/src/include/taler_exchangedb_plugin.h 
b/src/include/taler_exchangedb_plugin.h
index 7696b607..fc909a1b 100644
--- a/src/include/taler_exchangedb_plugin.h
+++ b/src/include/taler_exchangedb_plugin.h
@@ -3968,7 +3968,7 @@ struct TALER_EXCHANGEDB_Plugin
    * Obtain information about the global fee structure of the exchange.
    *
    * @param cls closure
-   * @param cb function to call on each account
+   * @param cb function to call on each fee entry
    * @param cb_cls closure for @a cb
    * @return transaction status code
    */
diff --git a/src/include/taler_json_lib.h b/src/include/taler_json_lib.h
index 6238c07d..b4f99900 100644
--- a/src/include/taler_json_lib.h
+++ b/src/include/taler_json_lib.h
@@ -279,6 +279,30 @@ TALER_JSON_spec_amount_any_nbo (const char *name,
   TALER_JSON_pack_amount (pfx "_refund", &(dfs)->refund)
 
 
+/**
+ * Generate specification to parse all global fees.
+ *
+ * @param currency which currency to expect
+ * @param[out] gfs a `struct TALER_GlobalFeeSet` to initialize
+ */
+#define TALER_JSON_SPEC_GLOBAL_FEES(currency,gfs) \
+  TALER_JSON_spec_amount ("kyc_fee", (currency), &(gfs)->kyc), \
+  TALER_JSON_spec_amount ("history_fee", (currency), &(gfs)->history),   \
+  TALER_JSON_spec_amount ("account_fee", (currency), &(gfs)->account),   \
+  TALER_JSON_spec_amount ("purse_fee", (currency), &(gfs)->purse)
+
+/**
+ * Macro to pack all of the global fees.
+ *
+ * @param gfs a `struct TALER_GlobalFeeSet` to pack
+ */
+#define TALER_JSON_PACK_GLOBAL_FEES(gfs) \
+  TALER_JSON_pack_amount ("kyc_fee", &(gfs)->kyc),   \
+  TALER_JSON_pack_amount ("history_fee", &(gfs)->history),     \
+  TALER_JSON_pack_amount ("account_fee", &(gfs)->account),     \
+  TALER_JSON_pack_amount ("purse_fee", &(gfs)->purse)
+
+
 /**
  * Generate line in parser specification for denomination public key.
  *
diff --git a/src/lib/exchange_api_handle.c b/src/lib/exchange_api_handle.c
index a9713a45..f7e87791 100644
--- a/src/lib/exchange_api_handle.c
+++ b/src/lib/exchange_api_handle.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 General Public License as published
@@ -40,7 +40,7 @@
  * Which version of the Taler protocol is implemented
  * by this library?  Used to determine compatibility.
  */
-#define EXCHANGE_PROTOCOL_CURRENT 12
+#define EXCHANGE_PROTOCOL_CURRENT 13
 
 /**
  * How many versions are we backwards compatible with?
@@ -255,7 +255,7 @@ free_keys_request (struct KeysRequest *kr)
  */
 static enum GNUNET_GenericReturnValue
 parse_json_signkey (struct TALER_EXCHANGE_SigningPublicKey *sign_key,
-                    int check_sigs,
+                    bool check_sigs,
                     json_t *sign_key_obj,
                     const struct TALER_MasterPublicKeyP *master_key)
 {
@@ -317,7 +317,7 @@ parse_json_signkey (struct TALER_EXCHANGE_SigningPublicKey 
*sign_key,
 static enum GNUNET_GenericReturnValue
 parse_json_denomkey (const char *currency,
                      struct TALER_EXCHANGE_DenomPublicKey *denom_key,
-                     int check_sigs,
+                     bool check_sigs,
                      json_t *denom_key_obj,
                      struct TALER_MasterPublicKeyP *master_key,
                      struct GNUNET_HashContext *hash_context)
@@ -394,7 +394,7 @@ EXITIF_exit:
  */
 static enum GNUNET_GenericReturnValue
 parse_json_auditor (struct TALER_EXCHANGE_AuditorInformation *auditor,
-                    int check_sigs,
+                    bool check_sigs,
                     json_t *auditor_obj,
                     const struct TALER_EXCHANGE_Keys *key_data)
 {
@@ -504,6 +504,79 @@ parse_json_auditor (struct 
TALER_EXCHANGE_AuditorInformation *auditor,
 }
 
 
+/**
+ * Parse a exchange's global fee information encoded in JSON.
+ *
+ * @param[out] gf where to return the result
+ * @param check_sigs should we check signatures
+ * @param[in] fee_obj json to parse
+ * @param key_data already parsed information about the exchange
+ * @return #GNUNET_OK if all is fine, #GNUNET_SYSERR if the signature is
+ *        invalid or the json malformed.
+ */
+static enum GNUNET_GenericReturnValue
+parse_global_fee (struct TALER_EXCHANGE_GlobalFee *gf,
+                  bool check_sigs,
+                  json_t *fee_obj,
+                  const struct TALER_EXCHANGE_Keys *key_data)
+{
+  struct GNUNET_JSON_Specification spec[] = {
+    GNUNET_JSON_spec_timestamp ("start_time",
+                                &gf->start_date),
+    GNUNET_JSON_spec_timestamp ("end_time",
+                                &gf->end_date),
+    GNUNET_JSON_spec_relative_time ("purse_timeout",
+                                    &gf->purse_timeout),
+    GNUNET_JSON_spec_relative_time ("kyc_timeout",
+                                    &gf->kyc_timeout),
+    GNUNET_JSON_spec_relative_time ("history_expiration",
+                                    &gf->history_expiration),
+    GNUNET_JSON_spec_uint32 ("purse_account_limit",
+                             &gf->purse_account_limit),
+    TALER_JSON_SPEC_GLOBAL_FEES (key_data->currency,
+                                 &gf->fees),
+    GNUNET_JSON_spec_fixed_auto ("master_sig",
+                                 &gf->master_sig),
+    GNUNET_JSON_spec_end ()
+  };
+
+  if (GNUNET_OK !=
+      GNUNET_JSON_parse (fee_obj,
+                         spec,
+                         NULL, NULL))
+  {
+    GNUNET_break_op (0);
+#if DEBUG
+    json_dumpf (fee_obj,
+                stderr,
+                JSON_INDENT (2));
+#endif
+    return GNUNET_SYSERR;
+  }
+  if (check_sigs)
+  {
+    if (GNUNET_OK !=
+        TALER_exchange_offline_global_fee_verify (
+          gf->start_date,
+          gf->end_date,
+          &gf->fees,
+          gf->purse_timeout,
+          gf->kyc_timeout,
+          gf->history_expiration,
+          gf->purse_account_limit,
+          &key_data->master_pub,
+          &gf->master_sig))
+    {
+      GNUNET_break_op (0);
+      GNUNET_JSON_parse_free (spec);
+      return GNUNET_SYSERR;
+    }
+  }
+  GNUNET_JSON_parse_free (spec);
+  return GNUNET_OK;
+}
+
+
 /**
  * Function called with information about the auditor.  Marks an
  * auditor as 'up'.
@@ -691,7 +764,7 @@ decode_keys_json (const json_t *resp_obj,
               stderr,
               JSON_INDENT (2));
 #endif
-  /* check the version */
+  /* check the version first */
   {
     const char *ver;
     unsigned int age;
@@ -762,6 +835,32 @@ decode_keys_json (const json_t *resp_obj,
     hash_context_restricted = GNUNET_CRYPTO_hash_context_start ();
   }
 
+  /* parse the global fees */
+  {
+    json_t *global_fees;
+    json_t *global_fee;
+    unsigned int index;
+
+    EXITIF (NULL == (global_fees =
+                       json_object_get (resp_obj,
+                                        "global_fees")));
+    EXITIF (! json_is_array (global_fees));
+    if (0 != (key_data->num_global_fees =
+                json_array_size (global_fees)))
+    {
+      key_data->global_fees
+        = GNUNET_new_array (key_data->num_global_fees,
+                            struct TALER_EXCHANGE_GlobalFee);
+      json_array_foreach (global_fees, index, global_fee) {
+        EXITIF (GNUNET_SYSERR ==
+                parse_global_fee (&key_data->global_fees[index],
+                                  check_sig,
+                                  global_fee,
+                                  key_data));
+      }
+    }
+  }
+
   /* parse the signing keys */
   {
     json_t *sign_keys_array;
diff --git a/src/lib/exchange_api_reserves_history.c 
b/src/lib/exchange_api_reserves_history.c
new file mode 100644
index 00000000..f7191b2a
--- /dev/null
+++ b/src/lib/exchange_api_reserves_history.c
@@ -0,0 +1,368 @@
+/*
+  This file is part of TALER
+  Copyright (C) 2014-2022 Taler Systems SA
+
+  TALER is free software; you can redistribute it and/or modify it under the
+  terms of the GNU General Public License as published by the Free Software
+  Foundation; either version 3, or (at your option) any later version.
+
+  TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+  A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License along with
+  TALER; see the file COPYING.  If not, see
+  <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file lib/exchange_api_reserves_history.c
+ * @brief Implementation of the POST /reserves/$RESERVE_PUB/history requests
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <jansson.h>
+#include <microhttpd.h> /* just for HTTP history codes */
+#include <gnunet/gnunet_util_lib.h>
+#include <gnunet/gnunet_json_lib.h>
+#include <gnunet/gnunet_curl_lib.h>
+#include "taler_exchange_service.h"
+#include "taler_json_lib.h"
+#include "exchange_api_handle.h"
+#include "taler_signatures.h"
+#include "exchange_api_curl_defaults.h"
+
+
+/**
+ * @brief A /reserves/$RID/history Handle
+ */
+struct TALER_EXCHANGE_ReservesHistoryHandle
+{
+
+  /**
+   * The connection to exchange this request handle will use
+   */
+  struct TALER_EXCHANGE_Handle *exchange;
+
+  /**
+   * The url for this request.
+   */
+  char *url;
+
+  /**
+   * Handle for the request.
+   */
+  struct GNUNET_CURL_Job *job;
+
+  /**
+   * Context for #TEH_curl_easy_post(). Keeps the data that must
+   * persist for Curl to make the upload.
+   */
+  struct TALER_CURL_PostContext post_ctx;
+
+  /**
+   * Function to call with the result.
+   */
+  TALER_EXCHANGE_ReservesHistoryCallback cb;
+
+  /**
+   * Public key of the reserve we are querying.
+   */
+  struct TALER_ReservePublicKeyP reserve_pub;
+
+  /**
+   * Closure for @a cb.
+   */
+  void *cb_cls;
+
+};
+
+
+/**
+ * We received an #MHD_HTTP_OK history code. Handle the JSON
+ * response.
+ *
+ * @param rgh handle of the request
+ * @param j JSON response
+ * @return #GNUNET_OK on success
+ */
+static enum GNUNET_GenericReturnValue
+handle_reserves_history_ok (struct TALER_EXCHANGE_ReservesHistoryHandle *rgh,
+                            const json_t *j)
+{
+  json_t *history;
+  unsigned int len;
+  bool kyc_ok;
+  bool kyc_required;
+  struct TALER_Amount balance;
+  struct TALER_Amount balance_from_history;
+  struct GNUNET_JSON_Specification spec[] = {
+    TALER_JSON_spec_amount_any ("balance",
+                                &balance),
+    GNUNET_JSON_spec_bool ("kyc_passed",
+                           &kyc_ok),
+    GNUNET_JSON_spec_bool ("kyc_required",
+                           &kyc_required),
+    GNUNET_JSON_spec_json ("history",
+                           &history),
+    GNUNET_JSON_spec_end ()
+  };
+  struct TALER_EXCHANGE_HttpResponse hr = {
+    .reply = j,
+    .http_history = MHD_HTTP_OK
+  };
+
+  if (GNUNET_OK !=
+      GNUNET_JSON_parse (j,
+                         spec,
+                         NULL,
+                         NULL))
+  {
+    GNUNET_break_op (0);
+    return GNUNET_SYSERR;
+  }
+  len = json_array_size (history);
+  {
+    struct TALER_EXCHANGE_ReserveHistory *rhistory;
+
+    rhistory = GNUNET_new_array (len,
+                                 struct TALER_EXCHANGE_ReserveHistory);
+    if (GNUNET_OK !=
+        TALER_EXCHANGE_parse_reserve_history (rgh->exchange,
+                                              history,
+                                              &rgh->reserve_pub,
+                                              balance.currency,
+                                              &balance_from_history,
+                                              len,
+                                              rhistory))
+    {
+      GNUNET_break_op (0);
+      TALER_EXCHANGE_free_reserve_history (rhistory,
+                                           len);
+      GNUNET_JSON_parse_free (spec);
+      return GNUNET_SYSERR;
+    }
+    if (0 !=
+        TALER_amount_cmp (&balance_from_history,
+                          &balance))
+    {
+      /* exchange cannot add up balances!? */
+      GNUNET_break_op (0);
+      TALER_EXCHANGE_free_reserve_history (rhistory,
+                                           len);
+      GNUNET_JSON_parse_free (spec);
+      return GNUNET_SYSERR;
+    }
+    if (NULL != rgh->cb)
+    {
+      rgh->cb (rgh->cb_cls,
+               &hr,
+               &balance,
+               len,
+               rhistory);
+      rgh->cb = NULL;
+    }
+    TALER_EXCHANGE_free_reserve_history (rhistory,
+                                         len);
+  }
+  GNUNET_JSON_parse_free (spec);
+  return GNUNET_OK;
+}
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP /reserves/$RID/history request.
+ *
+ * @param cls the `struct TALER_EXCHANGE_ReservesHistoryHandle`
+ * @param response_code HTTP response code, 0 on error
+ * @param response parsed JSON result, NULL on error
+ */
+static void
+handle_reserves_history_finished (void *cls,
+                                  long response_code,
+                                  const void *response)
+{
+  struct TALER_EXCHANGE_ReservesHistoryHandle *rgh = cls;
+  const json_t *j = response;
+  struct TALER_EXCHANGE_HttpResponse hr = {
+    .reply = j,
+    .http_history = (unsigned int) response_code
+  };
+
+  rgh->job = NULL;
+  switch (response_code)
+  {
+  case 0:
+    hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+    break;
+  case MHD_HTTP_OK:
+    if (GNUNET_OK !=
+        handle_reserves_history_ok (rgh,
+                                    j))
+    {
+      hr.http_history = 0;
+      hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
+    }
+    break;
+  case MHD_HTTP_BAD_REQUEST:
+    /* This should never happen, either us or the exchange is buggy
+       (or API version conflict); just pass JSON reply to the application */
+    GNUNET_break (0);
+    hr.ec = TALER_JSON_history_error_code (j);
+    hr.hint = TALER_JSON_history_error_hint (j);
+    break;
+  case MHD_HTTP_FORBIDDEN:
+    /* This should never happen, either us or the exchange is buggy
+       (or API version conflict); just pass JSON reply to the application */
+    GNUNET_break (0);
+    hr.ec = TALER_JSON_history_error_code (j);
+    hr.hint = TALER_JSON_history_error_hint (j);
+    break;
+  case MHD_HTTP_NOT_FOUND:
+    /* Nothing really to verify, this should never
+       happen, we should pass the JSON reply to the application */
+    hr.ec = TALER_JSON_history_error_code (j);
+    hr.hint = TALER_JSON_history_error_hint (j);
+    break;
+  case MHD_HTTP_INTERNAL_SERVER_ERROR:
+    /* Server had an internal issue; we should retry, but this API
+       leaves this to the application */
+    hr.ec = TALER_JSON_history_error_code (j);
+    hr.hint = TALER_JSON_history_error_hint (j);
+    break;
+  default:
+    /* unexpected response code */
+    GNUNET_break_op (0);
+    hr.ec = TALER_JSON_history_error_code (j);
+    hr.hint = TALER_JSON_history_error_hint (j);
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Unexpected response code %u/%d for reserves history\n",
+                (unsigned int) response_code,
+                (int) hr.ec);
+    break;
+  }
+  if (NULL != rgh->cb)
+  {
+    rgh->cb (rgh->cb_cls,
+             &hr,
+             NULL,
+             0,
+             NULL);
+    rgh->cb = NULL;
+  }
+  TALER_EXCHANGE_reserves_history_cancel (rgh);
+}
+
+
+struct TALER_EXCHANGE_ReservesHistoryHandle *
+TALER_EXCHANGE_reserves_history (
+  struct TALER_EXCHANGE_Handle *exchange,
+  const struct TALER_ReservePrivateKeyP *reserve_priv,
+  TALER_EXCHANGE_ReservesHistoryCallback cb,
+  void *cb_cls)
+{
+  struct TALER_EXCHANGE_ReservesHistoryHandle *rgh;
+  struct GNUNET_CURL_Context *ctx;
+  CURL *eh;
+  char arg_str[sizeof (struct TALER_ReservePublicKeyP) * 2 + 32];
+  const struct TALER_Amount *history_fee;
+  const struct TALER_EXCHANGE_Keys *keys;
+  struct GNUNET_TIME_Timestamp ts
+    = GNUNET_TIME_timestamp_get ();
+
+  if (GNUNET_YES !=
+      TEAH_handle_is_ready (exchange))
+  {
+    GNUNET_break (0);
+    return NULL;
+  }
+  keys = TALER_EXCHANGE_get_keys (exchange);
+  // FIXME: extract history_fee from keys!
+  history_fee = FIXME;
+  rgh = GNUNET_new (struct TALER_EXCHANGE_ReservesHistoryHandle);
+  rgh->exchange = exchange;
+  rgh->cb = cb;
+  rgh->cb_cls = cb_cls;
+  GNUNET_CRYPTO_eddsa_key_get_public (&reserve_priv->eddsa_priv,
+                                      &rgh->reserve_pub.eddsa_pub);
+  {
+    char pub_str[sizeof (struct TALER_ReservePublicKeyP) * 2];
+    char *end;
+
+    end = GNUNET_STRINGS_data_to_string (
+      &rgh->reserve_pub,
+      sizeof (rgh->reserve_pub),
+      pub_str,
+      sizeof (pub_str));
+    *end = '\0';
+    GNUNET_snprintf (arg_str,
+                     sizeof (arg_str),
+                     "/reserves/%s/history",
+                     pub_str);
+  }
+  rgh->url = TEAH_path_to_url (exchange,
+                               arg_str);
+  if (NULL == rgh->url)
+  {
+    GNUNET_free (rgh);
+    return NULL;
+  }
+  eh = TALER_EXCHANGE_curl_easy_history_ (rgh->url);
+  if (NULL == eh)
+  {
+    GNUNET_break (0);
+    GNUNET_free (rgh->url);
+    GNUNET_free (rgh);
+    return NULL;
+  }
+  TALER_wallet_reserve_history_sign (ts,
+                                     history_fee,
+                                     reserve_priv,
+                                     &reserve_sig);
+  {
+    json_t *history_obj = GNUNET_JSON_PACK (
+      GNUNET_JSON_pack_timestamp ("request_timestamp",
+                                  &ts),
+      GNUNET_JSON_pack_data_auto ("reserve_sig",
+                                  &reserve_sig));
+
+    if (GNUNET_OK !=
+        TALER_curl_easy_post (&rgh->post_ctx,
+                              eh,
+                              history_obj))
+      )
+      {
+        GNUNET_break (0);
+        curl_easy_cleanup (eh);
+        json_decref (history_obj);
+        GNUNET_free (rgh->url);
+        GNUNET_free (rgh);
+        return NULL;
+      }
+      json_decref (history_obj);
+  }
+  ctx = TEAH_handle_to_context (exchange);
+  rgh->job = GNUNET_CURL_job_add (ctx,
+                                  eh,
+                                  &handle_reserves_history_finished,
+                                  rgh);
+  return rgh;
+}
+
+
+void
+TALER_EXCHANGE_reserves_history_cancel (
+  struct TALER_EXCHANGE_ReservesHistoryHandle *rgh)
+{
+  if (NULL != rgh->job)
+  {
+    GNUNET_CURL_job_cancel (rgh->job);
+    rgh->job = NULL;
+  }
+  TALER_curl_easy_post_finished (&rgh->post_ctx);
+  GNUNET_free (rgh->url);
+  GNUNET_free (rgh);
+}
+
+
+/* end of exchange_api_reserves_history.c */
diff --git a/src/lib/exchange_api_reserves_status.c 
b/src/lib/exchange_api_reserves_status.c
new file mode 100644
index 00000000..2758a3a2
--- /dev/null
+++ b/src/lib/exchange_api_reserves_status.c
@@ -0,0 +1,364 @@
+/*
+  This file is part of TALER
+  Copyright (C) 2014-2022 Taler Systems SA
+
+  TALER is free software; you can redistribute it and/or modify it under the
+  terms of the GNU General Public License as published by the Free Software
+  Foundation; either version 3, or (at your option) any later version.
+
+  TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+  A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License along with
+  TALER; see the file COPYING.  If not, see
+  <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file lib/exchange_api_reserves_status.c
+ * @brief Implementation of the POST /reserves/$RESERVE_PUB/status requests
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <jansson.h>
+#include <microhttpd.h> /* just for HTTP status codes */
+#include <gnunet/gnunet_util_lib.h>
+#include <gnunet/gnunet_json_lib.h>
+#include <gnunet/gnunet_curl_lib.h>
+#include "taler_exchange_service.h"
+#include "taler_json_lib.h"
+#include "exchange_api_handle.h"
+#include "taler_signatures.h"
+#include "exchange_api_curl_defaults.h"
+
+
+/**
+ * @brief A /reserves/$RID/status Handle
+ */
+struct TALER_EXCHANGE_ReservesStatusHandle
+{
+
+  /**
+   * The connection to exchange this request handle will use
+   */
+  struct TALER_EXCHANGE_Handle *exchange;
+
+  /**
+   * The url for this request.
+   */
+  char *url;
+
+  /**
+   * Handle for the request.
+   */
+  struct GNUNET_CURL_Job *job;
+
+  /**
+   * Context for #TEH_curl_easy_post(). Keeps the data that must
+   * persist for Curl to make the upload.
+   */
+  struct TALER_CURL_PostContext post_ctx;
+
+  /**
+   * Function to call with the result.
+   */
+  TALER_EXCHANGE_ReservesStatusCallback cb;
+
+  /**
+   * Public key of the reserve we are querying.
+   */
+  struct TALER_ReservePublicKeyP reserve_pub;
+
+  /**
+   * Closure for @a cb.
+   */
+  void *cb_cls;
+
+};
+
+
+/**
+ * We received an #MHD_HTTP_OK status code. Handle the JSON
+ * response.
+ *
+ * @param rgh handle of the request
+ * @param j JSON response
+ * @return #GNUNET_OK on success
+ */
+static enum GNUNET_GenericReturnValue
+handle_reserves_status_ok (struct TALER_EXCHANGE_ReservesStatusHandle *rgh,
+                           const json_t *j)
+{
+  json_t *history;
+  unsigned int len;
+  bool kyc_ok;
+  bool kyc_required;
+  struct TALER_Amount balance;
+  struct TALER_Amount balance_from_history;
+  struct GNUNET_JSON_Specification spec[] = {
+    TALER_JSON_spec_amount_any ("balance",
+                                &balance),
+    GNUNET_JSON_spec_bool ("kyc_passed",
+                           &kyc_ok),
+    GNUNET_JSON_spec_bool ("kyc_required",
+                           &kyc_required),
+    GNUNET_JSON_spec_json ("history",
+                           &history),
+    GNUNET_JSON_spec_end ()
+  };
+  struct TALER_EXCHANGE_HttpResponse hr = {
+    .reply = j,
+    .http_status = MHD_HTTP_OK
+  };
+
+  if (GNUNET_OK !=
+      GNUNET_JSON_parse (j,
+                         spec,
+                         NULL,
+                         NULL))
+  {
+    GNUNET_break_op (0);
+    return GNUNET_SYSERR;
+  }
+  len = json_array_size (history);
+  {
+    struct TALER_EXCHANGE_ReserveHistory *rhistory;
+
+    rhistory = GNUNET_new_array (len,
+                                 struct TALER_EXCHANGE_ReserveHistory);
+    if (GNUNET_OK !=
+        TALER_EXCHANGE_parse_reserve_history (rgh->exchange,
+                                              history,
+                                              &rgh->reserve_pub,
+                                              balance.currency,
+                                              &balance_from_history,
+                                              len,
+                                              rhistory))
+    {
+      GNUNET_break_op (0);
+      TALER_EXCHANGE_free_reserve_history (rhistory,
+                                           len);
+      GNUNET_JSON_parse_free (spec);
+      return GNUNET_SYSERR;
+    }
+    // FIXME: status history is allowed to be
+    // partial, so this is NOT ok...
+    if (0 !=
+        TALER_amount_cmp (&balance_from_history,
+                          &balance))
+    {
+      /* exchange cannot add up balances!? */
+      GNUNET_break_op (0);
+      TALER_EXCHANGE_free_reserve_history (rhistory,
+                                           len);
+      GNUNET_JSON_parse_free (spec);
+      return GNUNET_SYSERR;
+    }
+    if (NULL != rgh->cb)
+    {
+      rgh->cb (rgh->cb_cls,
+               &hr,
+               &balance,
+               len,
+               rhistory);
+      rgh->cb = NULL;
+    }
+    TALER_EXCHANGE_free_reserve_history (rhistory,
+                                         len);
+  }
+  GNUNET_JSON_parse_free (spec);
+  return GNUNET_OK;
+}
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP /reserves/$RID/status request.
+ *
+ * @param cls the `struct TALER_EXCHANGE_ReservesStatusHandle`
+ * @param response_code HTTP response code, 0 on error
+ * @param response parsed JSON result, NULL on error
+ */
+static void
+handle_reserves_status_finished (void *cls,
+                                 long response_code,
+                                 const void *response)
+{
+  struct TALER_EXCHANGE_ReservesStatusHandle *rgh = cls;
+  const json_t *j = response;
+  struct TALER_EXCHANGE_HttpResponse hr = {
+    .reply = j,
+    .http_status = (unsigned int) response_code
+  };
+
+  rgh->job = NULL;
+  switch (response_code)
+  {
+  case 0:
+    hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+    break;
+  case MHD_HTTP_OK:
+    if (GNUNET_OK !=
+        handle_reserves_status_ok (rgh,
+                                   j))
+    {
+      hr.http_status = 0;
+      hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
+    }
+    break;
+  case MHD_HTTP_BAD_REQUEST:
+    /* This should never happen, either us or the exchange is buggy
+       (or API version conflict); just pass JSON reply to the application */
+    GNUNET_break (0);
+    hr.ec = TALER_JSON_status_error_code (j);
+    hr.hint = TALER_JSON_status_error_hint (j);
+    break;
+  case MHD_HTTP_FORBIDDEN:
+    /* This should never happen, either us or the exchange is buggy
+       (or API version conflict); just pass JSON reply to the application */
+    GNUNET_break (0);
+    hr.ec = TALER_JSON_status_error_code (j);
+    hr.hint = TALER_JSON_status_error_hint (j);
+    break;
+  case MHD_HTTP_NOT_FOUND:
+    /* Nothing really to verify, this should never
+       happen, we should pass the JSON reply to the application */
+    hr.ec = TALER_JSON_status_error_code (j);
+    hr.hint = TALER_JSON_status_error_hint (j);
+    break;
+  case MHD_HTTP_INTERNAL_SERVER_ERROR:
+    /* Server had an internal issue; we should retry, but this API
+       leaves this to the application */
+    hr.ec = TALER_JSON_status_error_code (j);
+    hr.hint = TALER_JSON_status_error_hint (j);
+    break;
+  default:
+    /* unexpected response code */
+    GNUNET_break_op (0);
+    hr.ec = TALER_JSON_status_error_code (j);
+    hr.hint = TALER_JSON_status_error_hint (j);
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Unexpected response code %u/%d for reserves status\n",
+                (unsigned int) response_code,
+                (int) hr.ec);
+    break;
+  }
+  if (NULL != rgh->cb)
+  {
+    rgh->cb (rgh->cb_cls,
+             &hr,
+             NULL,
+             0,
+             NULL);
+    rgh->cb = NULL;
+  }
+  TALER_EXCHANGE_reserves_status_cancel (rgh);
+}
+
+
+struct TALER_EXCHANGE_ReservesStatusHandle *
+TALER_EXCHANGE_reserves_status (
+  struct TALER_EXCHANGE_Handle *exchange,
+  const struct TALER_ReservePrivateKeyP *reserve_priv,
+  TALER_EXCHANGE_ReservesStatusCallback cb,
+  void *cb_cls)
+{
+  struct TALER_EXCHANGE_ReservesStatusHandle *rgh;
+  struct GNUNET_CURL_Context *ctx;
+  CURL *eh;
+  char arg_str[sizeof (struct TALER_ReservePublicKeyP) * 2 + 32];
+  struct GNUNET_TIME_Timestamp ts
+    = GNUNET_TIME_timestamp_get ();
+
+  if (GNUNET_YES !=
+      TEAH_handle_is_ready (exchange))
+  {
+    GNUNET_break (0);
+    return NULL;
+  }
+  rgh = GNUNET_new (struct TALER_EXCHANGE_ReservesStatusHandle);
+  rgh->exchange = exchange;
+  rgh->cb = cb;
+  rgh->cb_cls = cb_cls;
+  GNUNET_CRYPTO_eddsa_key_get_public (&reserve_priv->eddsa_priv,
+                                      &rgh->reserve_pub.eddsa_pub);
+  {
+    char pub_str[sizeof (struct TALER_ReservePublicKeyP) * 2];
+    char *end;
+
+    end = GNUNET_STRINGS_data_to_string (
+      &rgh->reserve_pub,
+      sizeof (rgh->reserve_pub),
+      pub_str,
+      sizeof (pub_str));
+    *end = '\0';
+    GNUNET_snprintf (arg_str,
+                     sizeof (arg_str),
+                     "/reserves/%s/status",
+                     pub_str);
+  }
+  rgh->url = TEAH_path_to_url (exchange,
+                               arg_str);
+  if (NULL == rgh->url)
+  {
+    GNUNET_free (rgh);
+    return NULL;
+  }
+  eh = TALER_EXCHANGE_curl_easy_status_ (rgh->url);
+  if (NULL == eh)
+  {
+    GNUNET_break (0);
+    GNUNET_free (rgh->url);
+    GNUNET_free (rgh);
+    return NULL;
+  }
+  TALER_wallet_reserve_status_sign (ts,
+                                    reserve_priv,
+                                    &reserve_sig);
+  {
+    json_t *status_obj = GNUNET_JSON_PACK (
+      GNUNET_JSON_pack_timestamp ("request_timestamp",
+                                  &ts),
+      GNUNET_JSON_pack_data_auto ("reserve_sig",
+                                  &reserve_sig));
+
+    if (GNUNET_OK !=
+        TALER_curl_easy_post (&rgh->post_ctx,
+                              eh,
+                              status_obj))
+      )
+      {
+        GNUNET_break (0);
+        curl_easy_cleanup (eh);
+        json_decref (status_obj);
+        GNUNET_free (rgh->url);
+        GNUNET_free (rgh);
+        return NULL;
+      }
+      json_decref (status_obj);
+  }
+  ctx = TEAH_handle_to_context (exchange);
+  rgh->job = GNUNET_CURL_job_add (ctx,
+                                  eh,
+                                  &handle_reserves_status_finished,
+                                  rgh);
+  return rgh;
+}
+
+
+void
+TALER_EXCHANGE_reserves_status_cancel (
+  struct TALER_EXCHANGE_ReservesStatusHandle *rgh)
+{
+  if (NULL != rgh->job)
+  {
+    GNUNET_CURL_job_cancel (rgh->job);
+    rgh->job = NULL;
+  }
+  TALER_curl_easy_post_finished (&rgh->post_ctx);
+  GNUNET_free (rgh->url);
+  GNUNET_free (rgh);
+}
+
+
+/* end of exchange_api_reserves_status.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]